diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/TrigT1CaloByteStream/ITrigT1CaloDataAccess.h b/Trigger/TrigT1/TrigT1CaloByteStream/TrigT1CaloByteStream/ITrigT1CaloDataAccess.h
new file mode 100644
index 0000000000000000000000000000000000000000..cc4900fe76be5e6cda0b6d4cde6924b2c419d9cd
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/TrigT1CaloByteStream/ITrigT1CaloDataAccess.h
@@ -0,0 +1,48 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_ITRIGT1CALODATAACCESS_H
+#define TRIGT1CALOBYTESTREAM_ITRIGT1CALODATAACCESS_H
+
+#include "GaudiKernel/IAlgTool.h"
+#include "GaudiKernel/IInterface.h"
+
+#include "DataModel/DataVector.h"
+
+namespace LVL1 {
+  class TriggerTower;
+  class JEMEtSums;
+  class JetElement;
+  class JetElementKey;
+}
+
+namespace LVL1BS {
+
+static const InterfaceID IID_ITrigT1CaloDataAccess("LVL1BS::ITrigT1CaloDataAccess", 1, 0);
+
+class ITrigT1CaloDataAccess : virtual public IAlgTool {
+
+ public:
+   static const InterfaceID& interfaceID();
+
+   virtual StatusCode loadCollection(
+                      DataVector<LVL1::TriggerTower>::const_iterator& beg,
+                      DataVector<LVL1::TriggerTower>::const_iterator& end,
+		      double etaMin, double etaMax,
+		      double phiMin, double phiMax, const bool full) = 0;
+   virtual StatusCode loadCollection(
+                      DataVector<LVL1::JetElement>::const_iterator& beg,
+                      DataVector<LVL1::JetElement>::const_iterator& end)=0;
+
+      
+};
+
+inline const InterfaceID& ITrigT1CaloDataAccess::interfaceID()
+{ 
+  return IID_ITrigT1CaloDataAccess;
+}
+
+} // end of namespace
+
+#endif 
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/cmt/requirements b/Trigger/TrigT1/TrigT1CaloByteStream/cmt/requirements
new file mode 100755
index 0000000000000000000000000000000000000000..f4ac237a64717189bcd26ce580051b676b1f814d
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/cmt/requirements
@@ -0,0 +1,34 @@
+package TrigT1CaloByteStream
+
+use AtlasPolicy          AtlasPolicy-*
+use DataModel            DataModel-*            Control
+use GaudiInterface       GaudiInterface-*       External
+
+author Peter Faulkner <P.J.W.Faulkner@bham.ac.uk> 
+
+# use this line to exclude test algorithms
+library TrigT1CaloByteStream *.cxx components/*.cxx
+# use this line to include test algorithms
+#library TrigT1CaloByteStream *.cxx ../test/*.cxx
+apply_pattern component_library
+
+apply_pattern declare_joboptions files="*.py"
+
+private
+use AthenaBaseComps      AthenaBaseComps-*      Control
+use ByteStreamCnvSvcBase ByteStreamCnvSvcBase-* Event 
+use ByteStreamData       ByteStreamData-*       Event
+use DataCollection       DataCollection-*       External
+use StoreGate            StoreGate-*            Control
+use SGTools              SGTools-*              Control
+use EventInfo            EventInfo-*            Event
+
+use TrigT1CaloEvent      TrigT1CaloEvent-*      Trigger/TrigT1
+use TrigT1CaloUtils      TrigT1CaloUtils-*      Trigger/TrigT1
+use TrigT1CaloMappingToolInterfaces TrigT1CaloMappingToolInterfaces-* Trigger/TrigT1
+
+# Only needed for test algorithms
+#use TrigT1Interfaces     TrigT1Interfaces-*     Trigger/TrigT1
+#use StoreGate            StoreGate-*            Control
+
+macro_append DOXYGEN_FILE_PATTERNS    " *.icc"
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/doc/mainpage.h b/Trigger/TrigT1/TrigT1CaloByteStream/doc/mainpage.h
new file mode 100755
index 0000000000000000000000000000000000000000..05edfc912aff585e904bb4a92146e7f7f882e513
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/doc/mainpage.h
@@ -0,0 +1,49 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+@mainpage TrigT1CaloByteStream Package
+
+@section intro Introduction
+This package contains offline bytestream conversion code for the
+Level-1 Calorimeter Trigger sub-detectors.<p>
+
+The code is based on the following documents/sources:<p>
+
+ATLAS Level-1 Calorimeter Trigger-Read-out Driver, version 1.09h<br>
+Level-1 Calorimeter Trigger: Cable Mappings and Crate Layouts from Analogue
+Inputs to Processors, version 1.6<br>
+Steve Hillier's online decoder, mainly L1CaloBsDecoderTemplate.h as of
+November 2005.<br>
+Private communications from Steve Hillier.<br>
+ATLAS L1Calo Pre-processor compressed Slink data formats, D.P.C.Sankey,
+Version 1.1, October 17, 2006.<br>
+ATLAS Level-1 Calorimeter Trigger: Jet/Energy Processor Module,
+Project Specification, version 1.0<br>
+ATLAS Level-1 Calorimeter Trigger: Cluster Processor Module,
+Project Specification, version 2.03<br>
+ATLAS Calorimeter First Level Trigger: Common Merger Module,
+Project Specification, version 1.7.8<p>
+
+Implemented so far: PPM, CPM, JEM, CMM-CP, CMM-Jet, CMM-Energy DAQ;
+                    CPM RoI, JEM Jet RoI, CMM-Jet-Et RoI, CMM-Energy RoI.
+
+@author Peter Faulkner
+
+@ref used_TrigT1CaloByteStream
+@ref requirements_TrigT1CaloByteStream
+
+*/
+
+/**
+@page used_TrigT1CaloByteStream Used Packages
+@htmlinclude used_packages.html
+*/
+
+/**
+@page requirements_TrigT1CaloByteStream Requirements
+@include requirements
+*/
+
+
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/share/ReadLVL1CaloBSRun1_jobOptions.py b/Trigger/TrigT1/TrigT1CaloByteStream/share/ReadLVL1CaloBSRun1_jobOptions.py
new file mode 100755
index 0000000000000000000000000000000000000000..4a20894417f283a7d9641eb6f73de2df624daf9e
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/share/ReadLVL1CaloBSRun1_jobOptions.py
@@ -0,0 +1,70 @@
+# Bytestream to TrigT1Calo objects conversions for data with Run 1 formats
+include.block("TrigT1CaloByteStream/ReadLVL1CaloBSRun1_jobOptions.py")
+
+# the following include is needed to load correctly the trigger towers on/off, TT/Cells maps
+include( "CaloConditions/CaloConditions_jobOptions.py" )
+# To setup correctly the LArCablingService when doLAr is off in the top option.
+from RecExConfig.RecFlags import rec
+if not rec.doLArg():
+    include( "LArConditionsCommon/LArIdMap_comm_jobOptions.py" )
+    include( "LArIdCnv/LArIdCnv_joboptions.py" )
+
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__CpByteStreamV1Tool
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__CpmRoiByteStreamV1Tool
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__JepByteStreamV1Tool
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__JepRoiByteStreamV1Tool
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__PpmByteStreamTool
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__RodHeaderByteStreamTool
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__L1CaloErrorByteStreamTool
+ToolSvc = Service("ToolSvc")
+ToolSvc += LVL1BS__CpByteStreamV1Tool("CpByteStreamV1Tool")
+ToolSvc += LVL1BS__CpmRoiByteStreamV1Tool("CpmRoiByteStreamV1Tool")
+ToolSvc += LVL1BS__JepByteStreamV1Tool("JepByteStreamV1Tool")
+ToolSvc += LVL1BS__JepRoiByteStreamV1Tool("JepRoiByteStreamV1Tool")
+ToolSvc += LVL1BS__PpmByteStreamTool("PpmByteStreamTool",
+           PpmMappingTool="LVL1::PpmCoolOrBuiltinMappingTool/PpmCoolOrBuiltinMappingTool")
+ToolSvc += LVL1BS__RodHeaderByteStreamTool("RodHeaderByteStreamTool")
+ToolSvc += LVL1BS__L1CaloErrorByteStreamTool("L1CaloErrorByteStreamTool")
+
+ByteStreamAddressProviderSvc = Service( "ByteStreamAddressProviderSvc" )
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CPMTower>/CPMTowers" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CPMTower>/CPMTowersOverlap" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CPMHits>/CPMHits" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CMMCPHits>/CMMCPHits" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CPMRoI>/CPMRoIsRoIB" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CPMRoI>/CPMRoIs" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::JetElement>/JetElements" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::JetElement>/JetElementsOverlap" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::JEMHits>/JEMHits" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::JEMEtSums>/JEMEtSums" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CMMJetHits>/CMMJetHits" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CMMEtSums>/CMMEtSums" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::JEMRoI>/JEMRoIs" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::JEMRoI>/JEMRoIsRoIB" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "LVL1::CMMRoI/CMMRoIs" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "LVL1::CMMRoI/CMMRoIsRoIB" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::TriggerTower>/TriggerTowers" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::TriggerTower>/TriggerTowersSpare" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::TriggerTower>/TriggerTowersMuon" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeaders" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersPP" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersCP" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersCPRoI" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersJEP" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersJEPRoI" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersCPRoIB" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersJEPRoIB" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "std::vector<unsigned int>/L1CaloUnpackingErrors" ]
+
+#from AthenaCommon.AlgSequence import AlgSequence
+#topSequence = AlgSequence()
+#from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__CpmTesterV1
+#topSequence += LVL1BS__CpmTesterV1("CpmTesterV1")
+#from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__CpmTesterV2
+#topSequence += LVL1BS__CpmTesterV2("CpmTesterV2")
+#topSequence += LVL1BS__CpmTesterV2("CpmTesterV2", CMXCPTobPrint=2, CMXCPHitsPrint=2)
+#from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__JemTesterV1
+#topSequence += LVL1BS__JemTesterV1("JemTesterV1")
+#from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__JemTesterV2
+#topSequence += LVL1BS__JemTesterV2("JemTesterV2")
+#topSequence += LVL1BS__JemTesterV2("JemTesterV2", CMXJetTobPrint=1)
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/share/ReadLVL1CaloBSRun2_jobOptions.py b/Trigger/TrigT1/TrigT1CaloByteStream/share/ReadLVL1CaloBSRun2_jobOptions.py
new file mode 100755
index 0000000000000000000000000000000000000000..603cfbe429f187bafdcdf214328acae46981675f
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/share/ReadLVL1CaloBSRun2_jobOptions.py
@@ -0,0 +1,70 @@
+# Bytestream to TrigT1Calo objects conversions for data with Run 2 formats
+include.block("TrigT1CaloByteStream/ReadLVL1CaloBSRun2_jobOptions.py")
+
+# the following include is needed to load correctly the trigger towers on/off, TT/Cells maps
+include( "CaloConditions/CaloConditions_jobOptions.py" )
+# To setup correctly the LArCablingService when doLAr is off in the top option.
+from RecExConfig.RecFlags import rec
+if not rec.doLArg():
+    include( "LArConditionsCommon/LArIdMap_comm_jobOptions.py" )
+    include( "LArIdCnv/LArIdCnv_joboptions.py" )
+
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__CpByteStreamV2Tool
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__CpmRoiByteStreamV2Tool
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__JepByteStreamV2Tool
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__JepRoiByteStreamV2Tool
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__PpmByteStreamTool
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__RodHeaderByteStreamTool
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__L1CaloErrorByteStreamTool
+ToolSvc = Service("ToolSvc")
+ToolSvc += LVL1BS__CpByteStreamV2Tool("CpByteStreamV2Tool")
+ToolSvc += LVL1BS__CpmRoiByteStreamV2Tool("CpmRoiByteStreamV2Tool")
+ToolSvc += LVL1BS__JepByteStreamV2Tool("JepByteStreamV2Tool")
+ToolSvc += LVL1BS__JepRoiByteStreamV2Tool("JepRoiByteStreamV2Tool")
+ToolSvc += LVL1BS__PpmByteStreamTool("PpmByteStreamTool",
+           PpmMappingTool="LVL1::PpmCoolOrBuiltinMappingTool/PpmCoolOrBuiltinMappingTool")
+ToolSvc += LVL1BS__RodHeaderByteStreamTool("RodHeaderByteStreamTool")
+ToolSvc += LVL1BS__L1CaloErrorByteStreamTool("L1CaloErrorByteStreamTool")
+
+ByteStreamAddressProviderSvc = Service( "ByteStreamAddressProviderSvc" )
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CPMTower>/CPMTowers" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CPMTower>/CPMTowersOverlap" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CMXCPTob>/CMXCPTobs" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CMXCPHits>/CMXCPHits" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CPMTobRoI>/CPMTobRoIs" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CPMTobRoI>/CPMTobRoIsRoIB" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::JetElement>/JetElements" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::JetElement>/JetElementsOverlap" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::JEMEtSums>/JEMEtSums" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CMXJetTob>/CMXJetTobs" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CMXJetHits>/CMXJetHits" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CMXEtSums>/CMXEtSums" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::JEMTobRoI>/JEMTobRoIs" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::JEMTobRoI>/JEMTobRoIsRoIB" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "LVL1::CMXRoI/CMXRoIs" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "LVL1::CMXRoI/CMXRoIsRoIB" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::TriggerTower>/TriggerTowers" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::TriggerTower>/TriggerTowersSpare" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::TriggerTower>/TriggerTowersMuon" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeaders" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersPP" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersCP" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersCPRoI" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersJEP" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersJEPRoI" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersCPRoIB" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersJEPRoIB" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "std::vector<unsigned int>/L1CaloUnpackingErrors" ]
+
+#from AthenaCommon.AlgSequence import AlgSequence
+#topSequence = AlgSequence()
+#from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__CpmTesterV1
+#topSequence += LVL1BS__CpmTesterV1("CpmTesterV1")
+#from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__CpmTesterV2
+#topSequence += LVL1BS__CpmTesterV2("CpmTesterV2")
+#topSequence += LVL1BS__CpmTesterV2("CpmTesterV2", CMXCPTobPrint=2, CMXCPHitsPrint=2)
+#from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__JemTesterV1
+#topSequence += LVL1BS__JemTesterV1("JemTesterV1")
+#from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__JemTesterV2
+#topSequence += LVL1BS__JemTesterV2("JemTesterV2")
+#topSequence += LVL1BS__JemTesterV2("JemTesterV2", CMXJetTobPrint=1)
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/share/ReadLVL1CaloBS_jobOptions.py b/Trigger/TrigT1/TrigT1CaloByteStream/share/ReadLVL1CaloBS_jobOptions.py
new file mode 100755
index 0000000000000000000000000000000000000000..7f41cf3026c364c192c3d4fdc53ac56dd9481970
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/share/ReadLVL1CaloBS_jobOptions.py
@@ -0,0 +1,17 @@
+# Bytestream to TrigT1Calo objects conversions
+include.block("TrigT1CaloByteStream/ReadLVL1CaloBS_jobOptions.py")
+
+from AthenaCommon.AthenaCommonFlags import athenaCommonFlags
+from AthenaCommon.GlobalFlags       import globalflags
+from RecExConfig.AutoConfiguration  import GetRunNumber
+# Need something better eventually
+if athenaCommonFlags.isOnline:
+    run1 = False
+elif globalflags.DataSource() == "data":
+    run1 = (GetRunNumber() < 230000)
+else:  # MC
+    run1 = False
+if run1:
+    include ("TrigT1CaloByteStream/ReadLVL1CaloBSRun1_jobOptions.py")
+else:
+    include ("TrigT1CaloByteStream/ReadLVL1CaloBSRun2_jobOptions.py")
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/share/WriteLVL1CaloBSRun1_jobOptions.py b/Trigger/TrigT1/TrigT1CaloByteStream/share/WriteLVL1CaloBSRun1_jobOptions.py
new file mode 100755
index 0000000000000000000000000000000000000000..39dff520825ef25ac8da3c7b10550293d9a997c1
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/share/WriteLVL1CaloBSRun1_jobOptions.py
@@ -0,0 +1,29 @@
+# TrigT1Calo object containers to bytestream conversion
+include.block("TrigT1CaloByteStream/WriteLVL1CaloBSRun1_jobOptions.py")
+
+# the following include is needed to load correctly the trigger towers on/off, TT/Cells maps
+include( "CaloConditions/CaloConditions_jobOptions.py" )
+# To setup correctly the LArCablingService when doLAr is off in the top option.
+if not rec.doLArg():
+    include( "LArConditionsCommon/LArIdMap_comm_jobOptions.py" )
+    include( "LArIdCnv/LArIdCnv_joboptions.py" )
+
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__PpmByteStreamTool
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__CpByteStreamV1Tool
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__CpmRoiByteStreamV1Tool
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__JepByteStreamV1Tool
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__JepRoiByteStreamV1Tool
+ToolSvc = Service("ToolSvc")
+ToolSvc += LVL1BS__PpmByteStreamTool("PpmByteStreamTool",
+           PpmMappingTool="LVL1::PpmCoolOrBuiltinMappingTool/PpmCoolOrBuiltinMappingTool")
+ToolSvc += LVL1BS__CpByteStreamV1Tool("CpByteStreamV1Tool")
+ToolSvc += LVL1BS__CpmRoiByteStreamV1Tool("CpmRoiByteStreamV1Tool")
+ToolSvc += LVL1BS__JepByteStreamV1Tool("JepByteStreamV1Tool")
+ToolSvc += LVL1BS__JepRoiByteStreamV1Tool("JepRoiByteStreamV1Tool")
+
+StreamBS = AthenaOutputStream( "StreamBS" )
+StreamBS.ItemList += [ "6207#*" ]       # TriggerTower
+StreamBS.ItemList += [ "216508938#*" ]  # CPMRoI
+StreamBS.ItemList += [ "1270847938#*" ] # CPBSCollectionV1
+StreamBS.ItemList += [ "1243139661#*" ] # JEPBSCollectionV1
+StreamBS.ItemList += [ "1316106214#*" ] # JEPRoIBSCollectionV1
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/share/WriteLVL1CaloBSRun2_jobOptions.py b/Trigger/TrigT1/TrigT1CaloByteStream/share/WriteLVL1CaloBSRun2_jobOptions.py
new file mode 100755
index 0000000000000000000000000000000000000000..1d662e98f45f17a3f5152e377bee906759b94f58
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/share/WriteLVL1CaloBSRun2_jobOptions.py
@@ -0,0 +1,29 @@
+# TrigT1Calo object containers to bytestream conversion
+include.block("TrigT1CaloByteStream/WriteLVL1CaloBSRun2_jobOptions.py")
+
+# the following include is needed to load correctly the trigger towers on/off, TT/Cells maps
+include( "CaloConditions/CaloConditions_jobOptions.py" )
+# To setup correctly the LArCablingService when doLAr is off in the top option.
+if not rec.doLArg():
+    include( "LArConditionsCommon/LArIdMap_comm_jobOptions.py" )
+    include( "LArIdCnv/LArIdCnv_joboptions.py" )
+
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__PpmByteStreamTool
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__CpByteStreamV2Tool
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__CpmRoiByteStreamV2Tool
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__JepByteStreamV2Tool
+from TrigT1CaloByteStream.TrigT1CaloByteStreamConf import LVL1BS__JepRoiByteStreamV2Tool
+ToolSvc = Service("ToolSvc")
+ToolSvc += LVL1BS__PpmByteStreamTool("PpmByteStreamTool",
+           PpmMappingTool="LVL1::PpmCoolOrBuiltinMappingTool/PpmCoolOrBuiltinMappingTool")
+ToolSvc += LVL1BS__CpByteStreamV2Tool("CpByteStreamV2Tool")
+ToolSvc += LVL1BS__CpmRoiByteStreamV2Tool("CpmRoiByteStreamV2Tool")
+ToolSvc += LVL1BS__JepByteStreamV2Tool("JepByteStreamV2Tool")
+ToolSvc += LVL1BS__JepRoiByteStreamV2Tool("JepRoiByteStreamV2Tool")
+
+StreamBS = AthenaOutputStream( "StreamBS" )
+StreamBS.ItemList += [ "6207#*" ]        # TriggerTower
+StreamBS.ItemList += [ "1270847937#*" ]  # CPBSCollectionV2
+StreamBS.ItemList += [ "80981142#*" ]    # CPMTobRoI
+StreamBS.ItemList += [ "1243139662#*" ]  # JEPBSCollectionV2
+StreamBS.ItemList += [ "1316106213#*" ]  # JEPRoIBSCollectionV2
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/share/WriteLVL1CaloBS_jobOptions.py b/Trigger/TrigT1/TrigT1CaloByteStream/share/WriteLVL1CaloBS_jobOptions.py
new file mode 100755
index 0000000000000000000000000000000000000000..9fca7d37f122f884db96296416021a6723a7bfef
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/share/WriteLVL1CaloBS_jobOptions.py
@@ -0,0 +1,18 @@
+# TrigT1Calo object containers to bytestream conversion
+include.block("TrigT1CaloByteStream/WriteLVL1CaloBS_jobOptions.py")
+
+from AthenaCommon.AthenaCommonFlags import athenaCommonFlags
+from AthenaCommon.GlobalFlags       import globalflags
+from RecExConfig.AutoConfiguration  import GetRunNumber
+# Need something better eventually
+if athenaCommonFlags.isOnline:
+    run1 = False
+elif globalflags.DataSource() == "data":
+    run1 = (GetRunNumber() < 230000)
+else:  # MC
+    run1 = False
+if run1:
+    include ("TrigT1CaloByteStream/WriteLVL1CaloBSRun1_jobOptions.py")
+else:
+    include ("TrigT1CaloByteStream/WriteLVL1CaloBSRun2_jobOptions.py")
+    #include ("TrigT1CaloByteStream/WriteLVL1CaloBSRun1Run2_jobOptions.py")
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/share/jobOfragment_ReadLVL1CaloBS.py b/Trigger/TrigT1/TrigT1CaloByteStream/share/jobOfragment_ReadLVL1CaloBS.py
new file mode 100755
index 0000000000000000000000000000000000000000..c07ee30aa54381a871a86067833f85e57180059d
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/share/jobOfragment_ReadLVL1CaloBS.py
@@ -0,0 +1,27 @@
+# Bytestream to TrigT1Calo objects conversions
+ByteStreamAddressProviderSvc = Service( "ByteStreamAddressProviderSvc" )
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::TriggerTower>/TriggerTowers" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CPMTower>/CPMTowers" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CPMTower>/CPMTowersOverlap" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CPMHits>/CPMHits" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CMMCPHits>/CMMCPHits" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CPMRoI>/CPMRoIs" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CPMRoI>/CPMRoIsRoIB" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::JetElement>/JetElements" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::JetElement>/JetElementsOverlap" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::JEMHits>/JEMHits" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::JEMEtSums>/JEMEtSums" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CMMJetHits>/CMMJetHits" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::CMMEtSums>/CMMEtSums" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::JEMRoI>/JEMRoIs" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::JEMRoI>/JEMRoIsRoIB" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "LVL1::CMMRoI/CMMRoIs" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "LVL1::CMMRoI/CMMRoIsRoIB" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeaders" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersPP" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersCP" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersCPRoI" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersJEP" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersJEPRoI" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersCPRoIB" ]
+ByteStreamAddressProviderSvc.TypeNames += [ "DataVector<LVL1::RODHeader>/RODHeadersJEPRoIB" ]
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmCpSubBlock.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmCpSubBlock.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..2a9cfe2155c83f9b4ca8edc268746c6ff140cbb9
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmCpSubBlock.cxx
@@ -0,0 +1,255 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "CmmCpSubBlock.h"
+
+namespace LVL1BS {
+
+// Constant definitions
+
+const int      CmmCpSubBlock::s_wordLength;
+
+const int      CmmCpSubBlock::s_threshBit;
+const int      CmmCpSubBlock::s_threshErrorBit;
+const int      CmmCpSubBlock::s_sourceIdBit;
+const int      CmmCpSubBlock::s_dataWordIdBit;
+const int      CmmCpSubBlock::s_dataWordId;
+const int      CmmCpSubBlock::s_maxHits;
+const uint32_t CmmCpSubBlock::s_threshMask;
+const uint32_t CmmCpSubBlock::s_errorMask;
+const uint32_t CmmCpSubBlock::s_sourceIdMask;
+
+const int      CmmCpSubBlock::s_hitsBits;
+const int      CmmCpSubBlock::s_hitsErrorBits;
+const int      CmmCpSubBlock::s_bunchCrossingBits;
+const int      CmmCpSubBlock::s_paddingBits;
+const int      CmmCpSubBlock::s_fifoOverflowPin;
+
+
+CmmCpSubBlock::CmmCpSubBlock()
+{
+}
+
+CmmCpSubBlock::~CmmCpSubBlock()
+{
+}
+
+// Clear all data
+
+void CmmCpSubBlock::clear()
+{
+  L1CaloSubBlock::clear();
+  m_hitsData.clear();
+}
+
+// Return hit counts for given CPM or source ID
+
+unsigned int CmmCpSubBlock::hits(const int slice, int source) const
+{
+  --source;
+  unsigned int hits = 0;
+  if (slice >= 0 && slice < timeslices() &&
+      source >= 0 && source < s_maxHits  && !m_hitsData.empty()) {
+    hits = (m_hitsData[index(slice, source)] >> s_threshBit) & s_threshMask;
+  }
+  return hits;
+}
+
+// Return hit error for given CPM or source ID
+
+int CmmCpSubBlock::hitsError(const int slice, int source) const
+{
+  --source;
+  int error = 0;
+  if (slice >= 0 && slice < timeslices() &&
+      source >= 0 && source < s_maxHits  && !m_hitsData.empty()) {
+    error = (m_hitsData[index(slice, source)] >> s_threshErrorBit)
+                                                               & s_errorMask;
+  }
+  return error;
+}
+
+// Store hit counts for given CPM or source ID
+
+void CmmCpSubBlock::setHits(const int slice, int source,
+                            const unsigned int hits, const int error)
+{
+  --source;
+  if (slice >= 0 && slice < timeslices() &&
+      source >= 0 && source < s_maxHits  && (hits || error)) {
+    resize();
+    uint32_t word = m_hitsData[index(slice, source)];
+    word |= (hits & s_threshMask)     << s_threshBit;
+    word |= (error & s_errorMask)     << s_threshErrorBit;
+    word |= (source & s_sourceIdMask) << s_sourceIdBit;
+    word |= (s_dataWordId)            << s_dataWordIdBit;
+    m_hitsData[index(slice, source)] = word;
+  }
+}
+
+// Packing/Unpacking routines
+
+bool CmmCpSubBlock::pack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = packNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = packUncompressed();
+	  break;
+        default:
+	  break;
+      }
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
+
+bool CmmCpSubBlock::unpack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = unpackNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = unpackUncompressed();
+	  break;
+        default:
+	  setUnpackErrorCode(UNPACK_FORMAT);
+	  break;
+      }
+      break;
+    default:
+      setUnpackErrorCode(UNPACK_VERSION);
+      break;
+  }
+  return rc;
+}
+
+// Return data index appropriate to format
+
+int CmmCpSubBlock::index(const int slice, const int source) const
+{
+  int ix = source;
+  if (format() == NEUTRAL) ix += slice * s_maxHits;
+  return ix;
+}
+
+// Resize the hits vector according to format
+
+void CmmCpSubBlock::resize()
+{
+  if (m_hitsData.empty()) {
+    int size = s_maxHits;
+    if (format() == NEUTRAL) size *= timeslices();
+    m_hitsData.resize(size);
+  }
+}
+
+// Pack neutral data
+
+bool CmmCpSubBlock::packNeutral()
+{
+  resize();
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    for (int source = 1; source < MAX_SOURCE_ID; ++source) {
+      const int pin = source - 1;
+      // CPM hits; remote(3), local and total hits; parity error
+      packerNeutral(pin, hits(slice, source), s_hitsBits);
+      packerNeutral(pin, hitsError(slice, source), s_hitsErrorBits);
+      // Bunch crossing number; Fifo overflow
+      if (pin < s_bunchCrossingBits) {
+        packerNeutral(pin, bunchCrossing() >> pin, 1);
+      } else if (pin == s_fifoOverflowPin) {
+	packerNeutral(pin, daqOverflow(), 1);
+      } else packerNeutral(pin, 0, 1);
+      // Padding
+      packerNeutral(pin, 0, s_paddingBits);
+      // G-Link parity
+      packerNeutralParity(pin);
+    }
+  }
+  return true;
+}
+
+// Pack uncompressed data
+
+bool CmmCpSubBlock::packUncompressed()
+{
+  std::vector<uint32_t>::const_iterator pos;
+  for (pos = m_hitsData.begin(); pos != m_hitsData.end(); ++pos) {
+    if (*pos) packer(*pos, s_wordLength);
+  }
+  packerFlush();
+  return true;
+}
+
+// Unpack neutral data
+
+bool CmmCpSubBlock::unpackNeutral()
+{
+  resize();
+  int bunchCrossing = 0;
+  int overflow = 0;
+  int parity = 0;
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    for (int source = 1; source < MAX_SOURCE_ID; ++source) {
+      const int pin = source - 1;
+      // CPM hits; remote(3), local and total hits; parity error
+      const unsigned int hits = unpackerNeutral(pin, s_hitsBits);
+      const int error = unpackerNeutral(pin, s_hitsErrorBits);
+      setHits(slice, source, hits, error);
+      // Bunch crossing number; Fifo overflow
+      if (pin < s_bunchCrossingBits) {
+        bunchCrossing |= unpackerNeutral(pin, 1) << pin;
+      } else if (pin == s_fifoOverflowPin) {
+        overflow |= unpackerNeutral(pin, 1);
+      } else unpackerNeutral(pin, 1);
+      // Padding
+      unpackerNeutral(pin, s_paddingBits);
+      // G-Link parity errors
+      parity |= unpackerNeutralParityError(pin);
+    }
+  }
+  setBunchCrossing(bunchCrossing);
+  setDaqOverflow(overflow);
+  setGlinkParity(parity);
+
+  const bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  return rc;
+}
+
+// Unpack uncompressed data
+
+bool CmmCpSubBlock::unpackUncompressed()
+{
+  resize();
+  unpackerInit();
+  uint32_t word = unpacker(s_wordLength);
+  while (unpackerSuccess()) {
+    const int source = sourceId(word);
+    if (source < s_maxHits && m_hitsData[source] == 0) m_hitsData[source] = word;
+    else {
+      setUnpackErrorCode(UNPACK_SOURCE_ID);
+      return false;
+    }
+    word = unpacker(s_wordLength);
+  }
+  return true;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmCpSubBlock.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmCpSubBlock.h
new file mode 100755
index 0000000000000000000000000000000000000000..ab7645f54c1d284f05cb333586007ddae752485e
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmCpSubBlock.h
@@ -0,0 +1,95 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CMMCPSUBBLOCK_H
+#define TRIGT1CALOBYTESTREAM_CMMCPSUBBLOCK_H
+
+#include <stdint.h>
+#include <vector>
+
+#include "CmmSubBlock.h"
+
+namespace LVL1BS {
+
+/** Sub-Block class for CMM-CP data.
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version 1.06d
+ *
+ *  @author Peter Faulkner
+ */
+
+class CmmCpSubBlock : public CmmSubBlock {
+
+ public:
+   //  Note - because CPMs are numbered 1-14 these are one more
+   //  than the stored source ID and corresponding G-Link pin.
+   enum SourceId { REMOTE_0 = 15, REMOTE_1 = 16, REMOTE_2 = 17,
+                   LOCAL = 18, TOTAL = 19, MAX_SOURCE_ID };
+
+   CmmCpSubBlock();
+   ~CmmCpSubBlock();
+
+   /// Clear all data
+   void clear();
+
+   /// Return hit counts for given CPM or source ID
+   unsigned int hits(int slice, int source) const;
+   /// Return hit error for given CPM or source ID
+   int hitsError(int slice, int source)     const;
+
+   /// Store hit counts for given CPM or source ID
+   void setHits(int slice, int source, unsigned int hits, int error);
+
+   /// Pack data
+   bool pack();
+   /// Unpack data
+   bool unpack();
+
+ private:
+   /// Data word length
+   static const int      s_wordLength      = 32;
+   //  Jet hit counts bit positions and masks
+   static const int      s_threshBit       = 0;
+   static const int      s_threshErrorBit  = 24;
+   static const int      s_sourceIdBit     = 25;
+   static const int      s_dataWordIdBit   = 30;
+   static const int      s_dataWordId      = 0;
+   static const int      s_maxHits         = 19;
+   static const uint32_t s_threshMask      = 0xffffff;
+   static const uint32_t s_errorMask       = 0x1;
+   static const uint32_t s_sourceIdMask    = 0x1f;
+   //  Neutral format
+   static const int      s_hitsBits          = 24;
+   static const int      s_hitsErrorBits     = 1;
+   static const int      s_bunchCrossingBits = 12;
+   static const int      s_paddingBits       = 8;
+   static const int      s_fifoOverflowPin   = 13;
+
+   int  sourceId(uint32_t word)      const;
+   int  index(int slice, int source) const;
+   void resize();
+
+   /// Pack neutral data
+   bool packNeutral();
+   /// Pack uncompressed data
+   bool packUncompressed();
+   /// Unpack neutral data
+   bool unpackNeutral();
+   /// Unpack uncompressed data
+   bool unpackUncompressed();
+
+   /// CPM hits and sums data
+   std::vector<uint32_t> m_hitsData;
+
+};
+
+inline int CmmCpSubBlock::sourceId(const uint32_t word) const
+{
+  return (word >> s_sourceIdBit) & s_sourceIdMask;
+}
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmEnergySubBlock.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmEnergySubBlock.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..0019e28681d53438712cb9de343932f710525209
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmEnergySubBlock.cxx
@@ -0,0 +1,533 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include <utility>
+
+#include "CmmEnergySubBlock.h"
+
+namespace LVL1BS {
+
+// Constant definitions
+
+const int      CmmEnergySubBlock::s_wordLength;
+
+const int      CmmEnergySubBlock::s_exBit;
+const int      CmmEnergySubBlock::s_eyBit;
+const int      CmmEnergySubBlock::s_etBit;
+const int      CmmEnergySubBlock::s_jemErrorBit;
+const int      CmmEnergySubBlock::s_errorBit;
+const int      CmmEnergySubBlock::s_etMissBit;
+const int      CmmEnergySubBlock::s_etHitsBit;
+const int      CmmEnergySubBlock::s_etMissSigBit;
+const int      CmmEnergySubBlock::s_sourceIdBit;
+const int      CmmEnergySubBlock::s_dataWordIdBit;
+const int      CmmEnergySubBlock::s_dataWordId;
+const int      CmmEnergySubBlock::s_maxSums;
+const int      CmmEnergySubBlock::s_maxJems;
+const uint32_t CmmEnergySubBlock::s_exMask;
+const uint32_t CmmEnergySubBlock::s_eyMask;
+const uint32_t CmmEnergySubBlock::s_etMask;
+const uint32_t CmmEnergySubBlock::s_errorMask;
+const uint32_t CmmEnergySubBlock::s_remoteErrorMask;
+const uint32_t CmmEnergySubBlock::s_etMissMask;
+const uint32_t CmmEnergySubBlock::s_etHitsMask;
+const uint32_t CmmEnergySubBlock::s_etHitsMaskV1;
+const uint32_t CmmEnergySubBlock::s_etMissSigMask;
+const uint32_t CmmEnergySubBlock::s_sumsMask;
+const uint32_t CmmEnergySubBlock::s_sourceIdMask;
+
+const int      CmmEnergySubBlock::s_jemSumBits;
+const int      CmmEnergySubBlock::s_sumBits;
+const int      CmmEnergySubBlock::s_bunchCrossingBits;
+const int      CmmEnergySubBlock::s_paddingBits;
+const int      CmmEnergySubBlock::s_fifoOverflowPin;
+
+
+CmmEnergySubBlock::CmmEnergySubBlock()
+{
+}
+
+CmmEnergySubBlock::~CmmEnergySubBlock()
+{
+}
+
+// Clear all data
+
+void CmmEnergySubBlock::clear()
+{
+  L1CaloSubBlock::clear();
+  m_sumsData.clear();
+}
+
+// Return Ex subsum for given JEM or source ID
+
+unsigned int CmmEnergySubBlock::ex(const int slice, const int source) const
+{
+  unsigned int ex = 0;
+  if (slice >= 0 && slice < timeslices() && !m_sumsData.empty()) {
+    if (source >= 0 && source < s_maxJems) {
+      ex = (m_sumsData[index(slice, source)] >> s_exBit) & s_exMask;
+    } else if (source == REMOTE || source == LOCAL || source == TOTAL) {
+      ex = m_sumsData[index(slice, source)] & s_sumsMask;
+    }
+  }
+  return ex;
+}
+
+// Return Ey subsum for given JEM or source ID
+
+unsigned int CmmEnergySubBlock::ey(const int slice, const int source) const
+{
+  unsigned int ey = 0;
+  if (slice >= 0 && slice < timeslices() && !m_sumsData.empty()) {
+    if (source >= 0 && source < s_maxJems) {
+      ey = (m_sumsData[index(slice, source)] >> s_eyBit) & s_eyMask;
+    } else if (source == REMOTE || source == LOCAL || source == TOTAL) {
+      ey = m_sumsData[index(slice, source+1)] & s_sumsMask;
+    }
+  }
+  return ey;
+}
+
+// Return Et subsum for given JEM or source ID
+
+unsigned int CmmEnergySubBlock::et(const int slice, const int source) const
+{
+  unsigned int et = 0;
+  if (slice >= 0 && slice < timeslices() && !m_sumsData.empty()) {
+    if (source >= 0 && source < s_maxJems) {
+      et = (m_sumsData[index(slice, source)] >> s_etBit) & s_etMask;
+    } else if (source == REMOTE || source == LOCAL || source == TOTAL) {
+      et = m_sumsData[index(slice, source+2)] & s_sumsMask;
+    }
+  }
+  return et;
+}
+
+// Return Ex subsum error for given JEM or source ID
+
+int CmmEnergySubBlock::exError(const int slice, const int source) const
+{
+  int error = 0;
+  if (slice >= 0 && slice < timeslices() && !m_sumsData.empty()) {
+    if (source >= 0 && source < s_maxJems) {
+      error = (m_sumsData[index(slice, source)] >> s_jemErrorBit) & s_errorMask;
+    } else if (source == REMOTE) {
+      error = (m_sumsData[index(slice, source)] >> s_errorBit)
+                                                         & s_remoteErrorMask;
+    } else if (source == LOCAL || source == TOTAL) {
+      error = (m_sumsData[index(slice, source)] >> s_errorBit) & s_errorMask;
+    }
+  }
+  return error;
+}
+
+// Return Ey subsum error for given JEM or source ID
+
+int CmmEnergySubBlock::eyError(const int slice, const int source) const
+{
+  int error = 0;
+  if (slice >= 0 && slice < timeslices() && !m_sumsData.empty()) {
+    if (source >= 0 && source < s_maxJems) {
+      error = (m_sumsData[index(slice, source)] >> s_jemErrorBit) & s_errorMask;
+    } else if (source == REMOTE) {
+      error = (m_sumsData[index(slice, source+1)] >> s_errorBit)
+                                                           & s_remoteErrorMask;
+    } else if (source == LOCAL || source == TOTAL) {
+      error = (m_sumsData[index(slice, source+1)] >> s_errorBit) & s_errorMask;
+    }
+  }
+  return error;
+}
+
+// Return Et subsum error for given JEM or source ID
+
+int CmmEnergySubBlock::etError(const int slice, const int source) const
+{
+  int error = 0;
+  if (slice >= 0 && slice < timeslices() && !m_sumsData.empty()) {
+    if (source >= 0 && source < s_maxJems) {
+      error = (m_sumsData[index(slice, source)] >> s_jemErrorBit) & s_errorMask;
+    } else if (source == REMOTE) {
+      error = (m_sumsData[index(slice, source+2)] >> s_errorBit)
+                                                           & s_remoteErrorMask;
+    } else if (source == LOCAL || source == TOTAL) {
+      error = (m_sumsData[index(slice, source+2)] >> s_errorBit) & s_errorMask;
+    }
+  }
+  return error;
+}
+
+// Return Missing-ET Hits map
+
+unsigned int CmmEnergySubBlock::missingEtHits(const int slice) const
+{
+  unsigned int map = 0;
+  if (slice >= 0 && slice < timeslices() && !m_sumsData.empty()) {
+    map = (m_sumsData[index(slice, TOTAL+1)] >> s_etMissBit) & s_etMissMask;
+  }
+  return map;
+}
+
+// Return Sum-ET Hits map
+
+unsigned int CmmEnergySubBlock::sumEtHits(const int slice) const
+{
+  unsigned int map = 0;
+  if (slice >= 0 && slice < timeslices() && !m_sumsData.empty()) {
+    unsigned int mask = (version() > 1) ? s_etHitsMask : s_etHitsMaskV1;
+    map = (m_sumsData[index(slice, TOTAL+2)] >> s_etHitsBit) & mask;
+  }
+  return map;
+}
+
+// Return Missing-ET-Sig Hits map
+
+unsigned int CmmEnergySubBlock::missingEtSigHits(const int slice) const
+{
+  unsigned int map = 0;
+  if (version() > 1 && slice >= 0 && slice < timeslices()
+                                  && !m_sumsData.empty()) {
+    map = (m_sumsData[index(slice, TOTAL)] >> s_etMissSigBit) & s_etMissSigMask;
+  }
+  return map;
+}
+
+// Store energy subsums and errors for given JEM or source ID
+
+void CmmEnergySubBlock::setSubsums(const int slice, const int source,
+                                   const unsigned int ex, const unsigned int ey,
+				   const unsigned int et, const int exError,
+				   const int eyError, const int etError)
+{
+  if (slice >= 0 && slice < timeslices() &&
+      source >= 0 && source < s_maxSums  &&
+      (ex || ey || et || exError || eyError || etError)) {
+    resize();
+    if (source < s_maxJems) {
+      uint32_t word = 0;
+      word |= (ex      & s_exMask)       << s_exBit;
+      word |= (ey      & s_eyMask)       << s_eyBit;
+      word |= (et      & s_etMask)       << s_etBit;
+      word |= (exError & s_errorMask)    << s_jemErrorBit;
+      word |= (eyError & s_errorMask)    << s_jemErrorBit;
+      word |= (etError & s_errorMask)    << s_jemErrorBit;
+      word |= (source  & s_sourceIdMask) << s_sourceIdBit;
+      word |=  s_dataWordId              << s_dataWordIdBit;
+      m_sumsData[index(slice, source)] = word;
+    } else {
+      unsigned int sum  = ex;
+      int          err  = exError;
+      uint32_t     mask = s_errorMask;
+      if (source == REMOTE) mask = s_remoteErrorMask;
+      for (int i = 0; i < 3; ++i) {
+        if (sum || err) {
+          uint32_t word = m_sumsData[index(slice, source+i)];
+          word |=  sum        & s_sumsMask;
+	  word |= (err        & mask)           << s_errorBit;
+          word |= ((source+i) & s_sourceIdMask) << s_sourceIdBit;
+          word |=  s_dataWordId                 << s_dataWordIdBit;
+          m_sumsData[index(slice, source+i)] = word;
+        }
+	if (i == 0) {
+	  sum = ey;
+	  err = eyError;
+        } else {
+	  sum = et;
+	  err = etError;
+        }
+      }
+    }
+  }
+}
+
+// Store Missing-ET Hits map
+
+void CmmEnergySubBlock::setMissingEtHits(const int slice,
+                                         const unsigned int map)
+{
+  if (slice >= 0 && slice < timeslices() && map) {
+    resize();
+    const int source = TOTAL + 1;
+    uint32_t word = m_sumsData[index(slice, source)];
+    word |= (map & s_etMissMask)      << s_etMissBit;
+    word |= (source & s_sourceIdMask) << s_sourceIdBit;
+    word |=  s_dataWordId             << s_dataWordIdBit;
+    m_sumsData[index(slice, source)] = word;
+  }
+}
+
+// Store Sum-Et Hits map
+
+void CmmEnergySubBlock::setSumEtHits(const int slice, const unsigned int map)
+{
+  if (slice >= 0 && slice < timeslices() && map) {
+    resize();
+    const int source = TOTAL + 2;
+    const unsigned int mask = (version() > 1) ? s_etHitsMask : s_etHitsMaskV1;
+    uint32_t word = m_sumsData[index(slice, source)];
+    word |= (map & mask)              << s_etHitsBit;
+    word |= (source & s_sourceIdMask) << s_sourceIdBit;
+    word |=  s_dataWordId             << s_dataWordIdBit;
+    m_sumsData[index(slice, source)] = word;
+  }
+}
+
+// Store Missing-ET-Sig Hits map
+
+void CmmEnergySubBlock::setMissingEtSigHits(const int slice,
+                                            const unsigned int map)
+{
+  if (version() > 1 && slice >= 0 && slice < timeslices() && map) {
+    resize();
+    const int source = TOTAL;
+    uint32_t word = m_sumsData[index(slice, source)];
+    word |= (map & s_etMissSigMask)   << s_etMissSigBit;
+    word |= (source & s_sourceIdMask) << s_sourceIdBit;
+    word |=  s_dataWordId             << s_dataWordIdBit;
+    m_sumsData[index(slice, source)] = word;
+  }
+}
+
+// Packing/Unpacking routines
+
+bool CmmEnergySubBlock::pack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 2:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = packNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = packUncompressed();
+	  break;
+        default:
+	  break;
+      }
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
+
+bool CmmEnergySubBlock::unpack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+    case 2:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = unpackNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = unpackUncompressed();
+	  break;
+        default:
+	  setUnpackErrorCode(UNPACK_FORMAT);
+	  break;
+      }
+      break;
+    default:
+      setUnpackErrorCode(UNPACK_VERSION);
+      break;
+  }
+  return rc;
+}
+
+// Return data index appropriate to format
+
+int CmmEnergySubBlock::index(const int slice, const int source) const
+{
+  int ix = source;
+  if (format() == NEUTRAL) ix += slice * s_maxSums;
+  return ix;
+}
+
+// Resize the sums vector according to format
+
+void CmmEnergySubBlock::resize()
+{
+  if (m_sumsData.empty()) {
+    int size = s_maxSums;
+    if (format() == NEUTRAL) size *= timeslices();
+    m_sumsData.resize(size);
+  }
+}
+
+// Pack neutral data
+
+bool CmmEnergySubBlock::packNeutral()
+{
+  resize();
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    for (int pin = 0; pin < s_maxJems; ++pin) {
+      // JEM energy sums (jem == pin); parity error
+      packerNeutral(pin, ex(slice, pin), s_jemSumBits);
+      packerNeutral(pin, ey(slice, pin), s_jemSumBits);
+      packerNeutral(pin, et(slice, pin), s_jemSumBits);
+      packerNeutral(pin, etError(slice, pin), 1);
+      // Bunch crossing number; Fifo overflow
+      if (pin < s_bunchCrossingBits) {
+        packerNeutral(pin, bunchCrossing() >> pin, 1);
+        // Padding
+        packerNeutral(pin, 0, s_paddingBits);
+      } else if (pin == s_fifoOverflowPin) {
+        packerNeutral(pin, daqOverflow(), 1);
+      } else packerNeutral(pin, 0, 1);
+    }
+    int pin = s_bunchCrossingBits - 1;
+    // Missing-ET-Sig Hits Map
+    packerNeutral(pin, missingEtSigHits(slice), s_paddingBits);
+    // Total Et
+    packerNeutral(++pin, et(slice, TOTAL), s_paddingBits);
+    packerNeutral(++pin, et(slice, TOTAL) >> s_paddingBits, s_paddingBits-1);
+    packerNeutral(pin, etError(slice, TOTAL), 1);
+    // Sum-Et Hits Map
+    packerNeutral(++pin, sumEtHits(slice), s_paddingBits);
+    // Missing-ET Hits Map
+    packerNeutral(++pin, missingEtHits(slice), s_paddingBits);
+    // Remote Ex, Ey
+    packerNeutral(++pin, ex(slice, REMOTE), s_sumBits);
+    packerNeutral(pin, exError(slice, REMOTE), 2);
+    packerNeutral(pin, ey(slice, REMOTE), s_sumBits);
+    packerNeutral(pin, eyError(slice, REMOTE), 2);
+    // Local Ex, Ey
+    packerNeutral(++pin, ex(slice, LOCAL), s_sumBits);
+    packerNeutral(pin, exError(slice, LOCAL), 2);
+    packerNeutral(pin, ey(slice, LOCAL), s_sumBits);
+    packerNeutral(pin, eyError(slice, LOCAL), 2);
+    // Total Ex, Ey
+    packerNeutral(++pin, ex(slice, TOTAL), s_sumBits);
+    packerNeutral(pin, exError(slice, TOTAL), 2);
+    packerNeutral(pin, ey(slice, TOTAL), s_sumBits);
+    packerNeutral(pin, eyError(slice, TOTAL), 2);
+    // Remote and Local Et
+    packerNeutral(++pin, et(slice, REMOTE), s_sumBits-1);
+    packerNeutral(pin, etError(slice, REMOTE), 3);
+    packerNeutral(pin, et(slice, LOCAL), s_sumBits-1);
+    packerNeutral(pin, etError(slice, LOCAL), 3);
+    // G-Link parity errors
+    for (int p = 0; p <= pin; ++p) packerNeutralParity(p);
+  }
+  return true;
+}
+
+// Pack uncompressed data
+
+bool CmmEnergySubBlock::packUncompressed()
+{
+  std::vector<uint32_t>::const_iterator pos;
+  for (pos = m_sumsData.begin(); pos != m_sumsData.end(); ++pos) {
+    if (*pos) packer(*pos, s_wordLength);
+  }
+  packerFlush();
+  return true;
+}
+
+// Unpack neutral data
+
+bool CmmEnergySubBlock::unpackNeutral()
+{
+  resize();
+  int bunchCrossing = 0;
+  int overflow = 0;
+  int parity = 0;
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    for (int pin = 0; pin < s_maxJems; ++pin) {
+      // JEM energy sums (jem == pin); parity error
+      const unsigned int ex = unpackerNeutral(pin, s_jemSumBits);
+      const unsigned int ey = unpackerNeutral(pin, s_jemSumBits);
+      const unsigned int et = unpackerNeutral(pin, s_jemSumBits);
+      const int er = unpackerNeutral(pin, 1);
+      setSubsums(slice, pin, ex, ey, et, er, er, er);
+      // Bunch crossing number; Fifo overflow
+      if (pin < s_bunchCrossingBits) {
+        bunchCrossing |= unpackerNeutral(pin, 1) << pin;
+        // Padding
+        unpackerNeutral(pin, s_paddingBits);
+      } else if (pin == s_fifoOverflowPin) {
+        overflow |= unpackerNeutral(pin, 1);
+      } else unpackerNeutral(pin, 1);
+    }
+    int pin = s_bunchCrossingBits - 1;
+    // Missing-ET-Sig Hits Map
+    setMissingEtSigHits(slice, unpackerNeutral(pin, s_paddingBits));
+    // Total Et
+    unsigned int etTot = unpackerNeutral(++pin, s_paddingBits);
+    etTot |= unpackerNeutral(++pin, s_paddingBits-1) << s_paddingBits;
+    const int etErrTot = unpackerNeutral(pin, 1);
+    // Sum-Et Hits Map + padding
+    setSumEtHits(slice, unpackerNeutral(++pin, s_paddingBits) & s_etHitsMask);
+    // Missing-ET Hits Map
+    setMissingEtHits(slice, unpackerNeutral(++pin, s_paddingBits));
+    // Remote Ex, Ey
+    const unsigned int exRem = unpackerNeutral(++pin, s_sumBits);
+    const int exErrRem = unpackerNeutral(pin, 2);
+    const unsigned int eyRem = unpackerNeutral(pin, s_sumBits);
+    const int eyErrRem = unpackerNeutral(pin, 2);
+    // Local Ex, Ey
+    const unsigned int exLoc = unpackerNeutral(++pin, s_sumBits);
+    const int exErrLoc = unpackerNeutral(pin, 1);
+    unpackerNeutral(pin, 1);
+    const unsigned int eyLoc = unpackerNeutral(pin, s_sumBits);
+    const int eyErrLoc = unpackerNeutral(pin, 1);
+    unpackerNeutral(pin, 1);
+    // Total Ex, Ey
+    const unsigned int exTot = unpackerNeutral(++pin, s_sumBits);
+    const int exErrTot = unpackerNeutral(pin, 1);
+    unpackerNeutral(pin, 1);
+    const unsigned int eyTot = unpackerNeutral(pin, s_sumBits);
+    const int eyErrTot = unpackerNeutral(pin, 1);
+    unpackerNeutral(pin, 1);
+    // Remote and Local Et
+    const unsigned int etRem = unpackerNeutral(++pin, s_sumBits-1);
+    const int etErrRem = unpackerNeutral(pin, 2);
+    unpackerNeutral(pin, 1);
+    const unsigned int etLoc = unpackerNeutral(pin, s_sumBits-1);
+    const int etErrLoc = unpackerNeutral(pin, 1);
+    unpackerNeutral(pin, 2);
+    setSubsums(slice, REMOTE, exRem, eyRem, etRem,
+                              exErrRem, eyErrRem, etErrRem);
+    setSubsums(slice, LOCAL,  exLoc, eyLoc, etLoc,
+                              exErrLoc, eyErrLoc, etErrLoc);
+    setSubsums(slice, TOTAL,  exTot, eyTot, etTot,
+                              exErrTot, eyErrTot, etErrTot);
+    // G-Link parity errors
+    for (int p = 0; p <= pin; ++p) parity |= unpackerNeutralParityError(p);
+  }
+  setBunchCrossing(bunchCrossing);
+  setDaqOverflow(overflow);
+  setGlinkParity(parity);
+
+  const bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  return rc;
+}
+
+// Unpack uncompressed data
+
+bool CmmEnergySubBlock::unpackUncompressed()
+{
+  resize();
+  unpackerInit();
+  uint32_t word = unpacker(s_wordLength);
+  while (unpackerSuccess()) {
+    const int source = sourceId(word);
+    if (source < s_maxSums && m_sumsData[source] == 0) m_sumsData[source] = word;
+    else {
+      setUnpackErrorCode(UNPACK_SOURCE_ID);
+      return false;
+    }
+    word = unpacker(s_wordLength);
+  }
+  return true;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmEnergySubBlock.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmEnergySubBlock.h
new file mode 100755
index 0000000000000000000000000000000000000000..521a606f687549df06d0fdf535883b914d2eb799
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmEnergySubBlock.h
@@ -0,0 +1,129 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CMMENERGYSUBBLOCK_H
+#define TRIGT1CALOBYTESTREAM_CMMENERGYSUBBLOCK_H
+
+#include <stdint.h>
+#include <vector>
+
+#include "CmmSubBlock.h"
+
+namespace LVL1BS {
+
+/** Sub-Block class for CMM-Energy data.
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version 1.06d
+ *
+ *  @author Peter Faulkner
+ */
+
+class CmmEnergySubBlock : public CmmSubBlock {
+
+ public:
+   enum SourceId { REMOTE = 20, LOCAL = 23, TOTAL = 26, MAX_SOURCE_ID };
+
+   CmmEnergySubBlock();
+   ~CmmEnergySubBlock();
+
+   /// Clear all data
+   void clear();
+
+   /// Return Ex subsum for given JEM or source ID
+   unsigned int  ex(int slice, int source) const;
+   /// Return Ey subsum for given JEM or source ID
+   unsigned int  ey(int slice, int source) const;
+   /// Return Et subsum for given JEM or source ID
+   unsigned int  et(int slice, int source) const;
+   /// Return Ex subsum error for given JEM or source ID
+   int  exError(int slice, int source) const;
+   /// Return Ey subsum error for given JEM or source ID
+   int  eyError(int slice, int source) const;
+   /// Return Et subsum error for given JEM or source ID
+   int  etError(int slice, int source) const;
+   /// Return Missing-ET Hits map
+   unsigned int  missingEtHits(int slice) const;
+   /// Return Sum-Et Hits map
+   unsigned int  sumEtHits(int slice) const;
+   /// Return Missing-ET-Sig Hits map
+   unsigned int  missingEtSigHits(int slice) const;
+
+   /// Store energy subsums and errors for given JEM or source ID
+   void setSubsums(int slice, int source, unsigned int ex,
+	           unsigned int ey, unsigned int et,
+                   int exError, int eyError, int etError);
+   /// Store Missing-ET Hits map
+   void setMissingEtHits(int slice, unsigned int map);
+   /// Store Sum-Et Hits map
+   void setSumEtHits(int slice, unsigned int map);
+   /// Store Missing-ET-Sig Hits map
+   void setMissingEtSigHits(int slice, unsigned int map);
+
+   /// Pack data
+   bool pack();
+   /// Unpack data
+   bool unpack();
+
+ private:
+   /// Data word length
+   static const int      s_wordLength      = 32;
+   //  Energy subsums bit positions and masks
+   static const int      s_exBit           = 0;
+   static const int      s_eyBit           = 8;
+   static const int      s_etBit           = 16;
+   static const int      s_jemErrorBit     = 24;
+   static const int      s_errorBit        = 15;
+   static const int      s_etMissBit       = 16;
+   static const int      s_etHitsBit       = 16;
+   static const int      s_etMissSigBit    = 16;
+   static const int      s_sourceIdBit     = 25;
+   static const int      s_dataWordIdBit   = 30;
+   static const int      s_dataWordId      = 0;
+   static const int      s_maxSums         = 29;
+   static const int      s_maxJems         = 16;
+   static const uint32_t s_exMask          = 0xff;
+   static const uint32_t s_eyMask          = 0xff;
+   static const uint32_t s_etMask          = 0xff;
+   static const uint32_t s_errorMask       = 0x1;
+   static const uint32_t s_remoteErrorMask = 0x3;
+   static const uint32_t s_etMissMask      = 0xff;
+   static const uint32_t s_etHitsMask      = 0xff;
+   static const uint32_t s_etHitsMaskV1    = 0xf;
+   static const uint32_t s_etMissSigMask   = 0xff;
+   static const uint32_t s_sumsMask        = 0x7fff;
+   static const uint32_t s_sourceIdMask    = 0x1f;
+   //  Neutral format
+   static const int      s_jemSumBits        = 8;
+   static const int      s_sumBits           = 15;
+   static const int      s_bunchCrossingBits = 12;
+   static const int      s_paddingBits       = 8;
+   static const int      s_fifoOverflowPin   = 15;
+
+   int  sourceId(uint32_t word)      const;
+   int  index(int slice, int source) const;
+   void resize();
+
+   /// Pack neutral data
+   bool packNeutral();
+   /// Pack uncompressed data
+   bool packUncompressed();
+   /// Unpack neutral data
+   bool unpackNeutral();
+   /// Unpack uncompressed data
+   bool unpackUncompressed();
+
+   /// Energy subsums data
+   std::vector<uint32_t> m_sumsData;
+
+};
+
+inline int CmmEnergySubBlock::sourceId(const uint32_t word) const
+{
+  return (word >> s_sourceIdBit) & s_sourceIdMask;
+}
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmJetSubBlock.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmJetSubBlock.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..37867c93e0fe46058115049cde7c322c9f52eced
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmJetSubBlock.cxx
@@ -0,0 +1,325 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "CmmJetSubBlock.h"
+
+namespace LVL1BS {
+
+// Constant definitions
+
+const int      CmmJetSubBlock::s_wordLength;
+
+const int      CmmJetSubBlock::s_threshBit;
+const int      CmmJetSubBlock::s_fwdErrorBit;
+const int      CmmJetSubBlock::s_etMapBit;
+const int      CmmJetSubBlock::s_threshErrorBit;
+const int      CmmJetSubBlock::s_sourceIdBit;
+const int      CmmJetSubBlock::s_dataWordIdBit;
+const int      CmmJetSubBlock::s_dataWordId;
+const int      CmmJetSubBlock::s_maxHits;
+const uint32_t CmmJetSubBlock::s_threshMask;
+const uint32_t CmmJetSubBlock::s_fwdMask;
+const uint32_t CmmJetSubBlock::s_etMapMask;
+const uint32_t CmmJetSubBlock::s_errorMask;
+const uint32_t CmmJetSubBlock::s_sourceIdMask;
+
+const int      CmmJetSubBlock::s_jetHitsBits;
+const int      CmmJetSubBlock::s_jetHitsErrorBits;
+const int      CmmJetSubBlock::s_fwdHitsBits;
+const int      CmmJetSubBlock::s_bunchCrossingBits;
+const int      CmmJetSubBlock::s_paddingBits;
+const int      CmmJetSubBlock::s_rightBit;
+const int      CmmJetSubBlock::s_fifoOverflowPin;
+
+
+CmmJetSubBlock::CmmJetSubBlock()
+{
+}
+
+CmmJetSubBlock::~CmmJetSubBlock()
+{
+}
+
+// Clear all data
+
+void CmmJetSubBlock::clear()
+{
+  L1CaloSubBlock::clear();
+  m_hitsData.clear();
+}
+
+// Return jet hit counts for given jem or source ID
+
+unsigned int CmmJetSubBlock::jetHits(const int slice, const int source) const
+{
+  unsigned int hits = 0;
+  if (slice >= 0 && slice < timeslices() &&
+      source >= 0 && source < s_maxHits  && !m_hitsData.empty()) {
+    if (source <= TOTAL_MAIN) {
+      hits = (m_hitsData[index(slice, source)] >> s_threshBit) & s_threshMask;
+    } else if (source >= REMOTE_FORWARD) {
+      hits = (m_hitsData[index(slice, source)] >> s_threshBit) & s_fwdMask;
+    }
+  }
+  return hits;
+}
+
+// Return jet hit error for given jem or source ID
+
+int CmmJetSubBlock::jetHitsError(const int slice, const int source) const
+{
+  int error = 0;
+  if (slice >= 0 && slice < timeslices() &&
+      source >= 0 && source < s_maxHits  && !m_hitsData.empty()) {
+    if (source <= TOTAL_MAIN) {
+      error = (m_hitsData[index(slice, source)] >> s_threshErrorBit)
+                                                                  & s_errorMask;
+    } else if (source == REMOTE_FORWARD) {
+      error = (m_hitsData[index(slice, source)] >> s_fwdErrorBit) & s_errorMask;
+    }
+  }
+  return error;
+}
+
+// Return jet ET map
+
+unsigned int CmmJetSubBlock::jetEtMap(const int slice) const
+{
+  unsigned int map = 0;
+  if (slice >= 0 && slice < timeslices() && !m_hitsData.empty()) {
+    map = (m_hitsData[index(slice, TOTAL_FORWARD)] >> s_etMapBit) & s_etMapMask;
+  }
+  return map;
+}
+
+// Store jet hit counts and error for given jem or source ID
+
+void CmmJetSubBlock::setJetHits(const int slice, const int source,
+                                const unsigned int hits, const int error)
+{
+  if (slice >= 0 && slice < timeslices() &&
+      source >= 0 && source < s_maxHits  && (hits || error)) {
+    resize();
+    uint32_t word = m_hitsData[index(slice, source)];
+    if (source <= TOTAL_MAIN) {
+      word |= (hits & s_threshMask)     << s_threshBit;
+      word |= (error & s_errorMask)     << s_threshErrorBit;
+    } else {
+      word |= (hits & s_fwdMask)        << s_threshBit;
+      word |= (error & s_errorMask)     << s_fwdErrorBit;
+    }
+    word   |= (source & s_sourceIdMask) << s_sourceIdBit;
+    word   |= (s_dataWordId)            << s_dataWordIdBit;
+    m_hitsData[index(slice, source)] = word;
+  }
+}
+
+// Store jet ET map
+
+void CmmJetSubBlock::setJetEtMap(const int slice, const unsigned int map)
+{
+  if (slice >= 0 && slice < timeslices() && map) {
+    resize();
+    uint32_t word = m_hitsData[index(slice, TOTAL_FORWARD)];
+    word |= (map & s_etMapMask)              << s_etMapBit;
+    word |= (TOTAL_FORWARD & s_sourceIdMask) << s_sourceIdBit;
+    word |= (s_dataWordId)                   << s_dataWordIdBit;
+    m_hitsData[index(slice, TOTAL_FORWARD)] = word;
+  }
+}
+
+// Packing/Unpacking routines
+
+bool CmmJetSubBlock::pack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = packNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = packUncompressed();
+	  break;
+        default:
+	  break;
+      }
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
+
+bool CmmJetSubBlock::unpack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = unpackNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = unpackUncompressed();
+	  break;
+        default:
+	  setUnpackErrorCode(UNPACK_FORMAT);
+	  break;
+      }
+      break;
+    default:
+      setUnpackErrorCode(UNPACK_VERSION);
+      break;
+  }
+  return rc;
+}
+
+// Return data index appropriate to format
+
+int CmmJetSubBlock::index(const int slice, const int source) const
+{
+  int ix = source;
+  if (format() == NEUTRAL) ix += slice * s_maxHits;
+  return ix;
+}
+
+// Resize the hits vector according to format
+
+void CmmJetSubBlock::resize()
+{
+  if (m_hitsData.empty()) {
+    int size = s_maxHits;
+    if (format() == NEUTRAL) size *= timeslices();
+    m_hitsData.resize(size);
+  }
+}
+
+// Pack neutral data
+
+bool CmmJetSubBlock::packNeutral()
+{
+  resize();
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    for (int pin = 0; pin <= TOTAL_MAIN; ++pin) {
+      // Jem hits; remote, local and total main hits; parity error
+      packerNeutral(pin, jetHits(slice, pin), s_jetHitsBits);
+      packerNeutral(pin, jetHitsError(slice, pin), s_jetHitsErrorBits);
+      // Bunch crossing number; Fifo overflow
+      if (pin < s_bunchCrossingBits) {
+        packerNeutral(pin, bunchCrossing() >> pin, 1);
+      } else if (pin == s_fifoOverflowPin) {
+        packerNeutral(pin, daqOverflow(), 1);
+      } else packerNeutral(pin, 0, 1);
+      // Padding
+      if (pin < REMOTE_MAIN) packerNeutral(pin, 0, s_paddingBits);
+    }
+    // ET Map + padding
+    packerNeutral(REMOTE_MAIN, jetEtMap(slice), s_paddingBits);
+    // Total forward (left + right)
+    packerNeutral(LOCAL_MAIN, jetHits(slice, TOTAL_FORWARD), s_paddingBits);
+    packerNeutral(TOTAL_MAIN, jetHits(slice, TOTAL_FORWARD) >> s_rightBit,
+                                                             s_paddingBits);
+    // Remote + Local forward
+    const int lastpin = TOTAL_MAIN + 1;
+    packerNeutral(lastpin, jetHits(slice, REMOTE_FORWARD), s_fwdHitsBits);
+    packerNeutral(lastpin, jetHitsError(slice, REMOTE_FORWARD),
+                                                      s_jetHitsErrorBits);
+    packerNeutral(lastpin, 0, 1);
+    packerNeutral(lastpin, jetHits(slice, LOCAL_FORWARD),  s_fwdHitsBits);
+    // G-Link parity
+    for (int pin = 0; pin <= lastpin; ++pin) packerNeutralParity(pin);
+  }
+  return true;
+}
+
+// Pack uncompressed data
+
+bool CmmJetSubBlock::packUncompressed()
+{
+  std::vector<uint32_t>::const_iterator pos;
+  for (pos = m_hitsData.begin(); pos != m_hitsData.end(); ++pos) {
+    if (*pos) packer(*pos, s_wordLength);
+  }
+  packerFlush();
+  return true;
+}
+
+// Unpack neutral data
+
+bool CmmJetSubBlock::unpackNeutral()
+{
+  resize();
+  int bunchCrossing = 0;
+  int overflow = 0;
+  int parity = 0;
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    unsigned int hits = 0;
+    int error = 0;
+    for (int pin = 0; pin <= TOTAL_MAIN; ++pin) {
+      // Jem hits; remote, local and total main hits; parity error
+      hits  = unpackerNeutral(pin, s_jetHitsBits);
+      error = unpackerNeutral(pin, s_jetHitsErrorBits);
+      setJetHits(slice, pin, hits, error);
+      // Bunch crossing number; Fifo overflow
+      if (pin < s_bunchCrossingBits) {
+        bunchCrossing |= unpackerNeutral(pin, 1) << pin;
+      } else if (pin == s_fifoOverflowPin) {
+        overflow |= unpackerNeutral(pin, 1);
+      } else unpackerNeutral(pin, 1);
+      // Padding
+      if (pin < REMOTE_MAIN) unpackerNeutral(pin, s_paddingBits);
+    }
+    // ET Map + padding
+    setJetEtMap(slice, unpackerNeutral(REMOTE_MAIN, s_paddingBits));
+    // Total forward (left + right)
+    hits  = unpackerNeutral(LOCAL_MAIN, s_paddingBits);
+    hits |= unpackerNeutral(TOTAL_MAIN, s_paddingBits) << s_rightBit;
+    setJetHits(slice, TOTAL_FORWARD, hits, 0);
+    // Remote + Local forward
+    const int lastpin = TOTAL_MAIN + 1;
+    hits  = unpackerNeutral(lastpin, s_fwdHitsBits);
+    error = unpackerNeutral(lastpin, s_jetHitsErrorBits);
+    setJetHits(slice, REMOTE_FORWARD, hits, error);
+    unpackerNeutral(lastpin, 1);
+    setJetHits(slice, LOCAL_FORWARD,
+                      unpackerNeutral(lastpin, s_fwdHitsBits), 0);
+    // G-Link parity errors
+    for (int pin = 0; pin <= lastpin; ++pin) {
+      parity |= unpackerNeutralParityError(pin);
+    }
+  }
+  setBunchCrossing(bunchCrossing);
+  setDaqOverflow(overflow);
+  setGlinkParity(parity);
+
+  const bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  return rc;
+}
+
+// Unpack uncompressed data
+
+bool CmmJetSubBlock::unpackUncompressed()
+{
+  resize();
+  unpackerInit();
+  uint32_t word = unpacker(s_wordLength);
+  while (unpackerSuccess()) {
+    const int source = sourceId(word);
+    if (source < s_maxHits && m_hitsData[source] == 0) m_hitsData[source] = word;
+    else {
+      setUnpackErrorCode(UNPACK_SOURCE_ID);
+      return false;
+    }
+    word = unpacker(s_wordLength);
+  }
+  return true;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmJetSubBlock.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmJetSubBlock.h
new file mode 100755
index 0000000000000000000000000000000000000000..75a00b3cecad5dc73f51b55388241959be4dc671
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmJetSubBlock.h
@@ -0,0 +1,105 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CMMJETSUBBLOCK_H
+#define TRIGT1CALOBYTESTREAM_CMMJETSUBBLOCK_H
+
+#include <stdint.h>
+#include <vector>
+
+#include "CmmSubBlock.h"
+
+namespace LVL1BS {
+
+/** Sub-Block class for CMM-Jet data.
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version 1.06d
+ *
+ *  @author Peter Faulkner
+ */
+
+class CmmJetSubBlock : public CmmSubBlock {
+
+ public:
+   enum SourceId { REMOTE_MAIN = 16, LOCAL_MAIN = 17, TOTAL_MAIN = 18,
+                 REMOTE_FORWARD = 20, LOCAL_FORWARD = 21, TOTAL_FORWARD = 22,
+		 MAX_SOURCE_ID };
+
+   CmmJetSubBlock();
+   ~CmmJetSubBlock();
+
+   /// Clear all data
+   void clear();
+
+   /// Return jet hit counts for given jem or source ID
+   unsigned int jetHits(int slice, int source) const;
+   /// Return jet hit error for given jem or source ID
+   int jetHitsError(int slice, int source)     const;
+   /// Return jet ET map
+   unsigned int jetEtMap(int slice)            const;
+
+   /// Store jet hit counts and error for given jem or source ID
+   void setJetHits(int slice, int source, unsigned int hits,
+	                                           int error);
+   /// Store jet ET map
+   void setJetEtMap(int slice, unsigned int map);
+
+   /// Pack data
+   bool pack();
+   /// Unpack data
+   bool unpack();
+
+ private:
+   /// Data word length
+   static const int      s_wordLength      = 32;
+   //  Jet hit counts bit positions and masks
+   static const int      s_threshBit       = 0;
+   static const int      s_fwdErrorBit     = 16;
+   static const int      s_etMapBit        = 17;
+   static const int      s_threshErrorBit  = 24;
+   static const int      s_sourceIdBit     = 25;
+   static const int      s_dataWordIdBit   = 30;
+   static const int      s_dataWordId      = 0;
+   static const int      s_maxHits         = 23;
+   static const uint32_t s_threshMask      = 0xffffff;
+   static const uint32_t s_fwdMask         = 0xffff;
+   static const uint32_t s_etMapMask       = 0xf;
+   static const uint32_t s_errorMask       = 0x1;
+   static const uint32_t s_sourceIdMask    = 0x1f;
+   //  Neutral format
+   static const int      s_jetHitsBits       = 24;
+   static const int      s_jetHitsErrorBits  = 1;
+   static const int      s_fwdHitsBits       = 16;
+   static const int      s_bunchCrossingBits = 12;
+   static const int      s_paddingBits       = 8;
+   static const int      s_rightBit          = 8;
+   static const int      s_fifoOverflowPin   = 15;
+
+   int  sourceId(uint32_t word)      const;
+   int  index(int slice, int source) const;
+   void resize();
+
+   /// Pack neutral data
+   bool packNeutral();
+   /// Pack uncompressed data
+   bool packUncompressed();
+   /// Unpack neutral data
+   bool unpackNeutral();
+   /// Unpack uncompressed data
+   bool unpackUncompressed();
+
+   /// JEM hits and sums data
+   std::vector<uint32_t> m_hitsData;
+
+};
+
+inline int CmmJetSubBlock::sourceId(const uint32_t word) const
+{
+  return (word >> s_sourceIdBit) & s_sourceIdMask;
+}
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmSubBlock.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmSubBlock.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..48dd020e451e71fea3dc44fe4f4b748e275a3859
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmSubBlock.cxx
@@ -0,0 +1,89 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "CmmSubBlock.h"
+
+namespace LVL1BS {
+
+// Static constant definitions
+
+const int      CmmSubBlock::s_wordIdVal;
+
+const int      CmmSubBlock::s_cmmSummingBit;
+const int      CmmSubBlock::s_cmmFirmwareBit;
+const int      CmmSubBlock::s_cmmPositionBit;
+const uint32_t CmmSubBlock::s_cmmSummingMask;
+const uint32_t CmmSubBlock::s_cmmFirmwareMask;
+const uint32_t CmmSubBlock::s_cmmPositionMask;
+
+const int      CmmSubBlock::s_glinkBitsPerSlice;
+
+CmmSubBlock::CmmSubBlock()
+{
+}
+
+CmmSubBlock::~CmmSubBlock()
+{
+}
+
+// Store CMM header
+
+void CmmSubBlock::setCmmHeader(const int version, const int format,
+                               const int slice, const int crate,
+                               const int summing, const int firmware,
+			       const int position, const int timeslices)
+{
+  int module = 0;
+  module |= (summing  & s_cmmSummingMask)  << s_cmmSummingBit;
+  module |= (firmware & s_cmmFirmwareMask) << s_cmmFirmwareBit;
+  module |= (position & s_cmmPositionMask) << s_cmmPositionBit;
+  setHeader(s_wordIdVal, version, format, slice, crate, module, 0,
+                                                           timeslices);
+}
+
+// Return number of timeslices
+
+int CmmSubBlock::timeslices() const
+{
+  int slices = slices1();
+  if (slices == 0 && format() == NEUTRAL) {
+    slices = dataWords() / s_glinkBitsPerSlice;
+  }
+  if (slices == 0) slices = 1;
+  return slices;
+}
+
+// Static function to determine CMM type
+
+CmmSubBlock::CmmFirmwareCode CmmSubBlock::cmmType(const uint32_t word)
+{
+  CmmFirmwareCode type;
+  const int module = L1CaloSubBlock::module(word);
+  const int code   = (module >> s_cmmFirmwareBit) & s_cmmFirmwareMask;
+  switch (code) {
+    case CMM_CP:
+      type = CMM_CP;
+      break;
+    case CMM_JET:
+      type = CMM_JET;
+      break;
+    case CMM_ENERGY:
+      type = CMM_ENERGY;
+      break;
+    default:
+      type = CMM_UNKNOWN;
+      break;
+  }
+  return type;
+}
+
+// Static function to determine if header word corresponds to CMM block
+
+bool CmmSubBlock::cmmBlock(const uint32_t word)
+{
+  return L1CaloSubBlock::wordId(word) == s_wordIdVal;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmSubBlock.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmSubBlock.h
new file mode 100755
index 0000000000000000000000000000000000000000..71c009c5dfb1f9bc50fc4f54888bf820900f73cb
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmmSubBlock.h
@@ -0,0 +1,80 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CMMSUBBLOCK_H
+#define TRIGT1CALOBYTESTREAM_CMMSUBBLOCK_H
+
+#include <stdint.h>
+
+#include "L1CaloSubBlock.h"
+
+namespace LVL1BS {
+
+/** Sub-Block class for CMM data.
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version 1.06d
+ *
+ *  @author Peter Faulkner
+ */
+
+class CmmSubBlock : public L1CaloSubBlock {
+
+ public:
+   enum CmmFirmwareCode { CMM_CP = 0, CMM_JET = 1, CMM_ENERGY = 2,
+                          CMM_UNKNOWN = 3 };
+   enum CmmSummingCode  { CRATE = 0, SYSTEM = 1 };
+   enum CmmPositions    { LEFT = 0, RIGHT = 1 };
+
+   CmmSubBlock();
+   ~CmmSubBlock();
+
+   /// Store CMM header
+   void setCmmHeader(int version, int format, int slice, int crate,
+                     int summing, int firmware, int position, int timeslices);
+
+   //   Return CMM specific header data
+   int cmmSumming()  const;
+   int cmmFirmware() const;
+   int cmmPosition() const;
+   int timeslices()  const;
+
+   /// CMM differentiation (CMM_CP, CMM_JET, or CMM_ENERGY)
+   static CmmFirmwareCode cmmType(uint32_t word);
+   /// Determine if header word corresponds to CMM
+   static bool cmmBlock(uint32_t word);
+
+ private:
+   /// CMM header word ID
+   static const int      s_wordIdVal        = 0xe;
+   //  CMM fields packed in module field
+   static const int      s_cmmSummingBit    = 3;
+   static const int      s_cmmFirmwareBit   = 1;
+   static const int      s_cmmPositionBit   = 0;
+   static const uint32_t s_cmmSummingMask   = 0x1;
+   static const uint32_t s_cmmFirmwareMask  = 0x3;
+   static const uint32_t s_cmmPositionMask  = 0x1;
+   /// Needed for neutral format
+   static const int      s_glinkBitsPerSlice = 35;
+
+};
+
+inline int CmmSubBlock::cmmSumming() const
+{
+  return (module() >> s_cmmSummingBit) & s_cmmSummingMask;
+}
+
+inline int CmmSubBlock::cmmFirmware() const
+{
+  return (module() >> s_cmmFirmwareBit) & s_cmmFirmwareMask;
+}
+
+inline int CmmSubBlock::cmmPosition() const
+{
+  return (module() >> s_cmmPositionBit) & s_cmmPositionMask;
+}
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxCpSubBlock.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxCpSubBlock.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..5ccd5ce3954c09f50cb44cab8443b250dc649608
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxCpSubBlock.cxx
@@ -0,0 +1,618 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "CmxCpSubBlock.h"
+
+namespace LVL1BS {
+
+// Constant definitions
+
+const int      CmxCpSubBlock::s_wordLength;
+
+const int      CmxCpSubBlock::s_tobEnergyBit;
+const int      CmxCpSubBlock::s_tobIsolationBit;
+const int      CmxCpSubBlock::s_tobErrorBit;
+const int      CmxCpSubBlock::s_tobOverflowBit;
+const int      CmxCpSubBlock::s_tobCoordBit;
+const int      CmxCpSubBlock::s_tobChipBit;
+const int      CmxCpSubBlock::s_tobCpmBit;
+const int      CmxCpSubBlock::s_tobWordId;
+const uint32_t CmxCpSubBlock::s_tobEnergyMask;
+const uint32_t CmxCpSubBlock::s_tobIsolationMask;
+const uint32_t CmxCpSubBlock::s_tobErrorMask;
+const uint32_t CmxCpSubBlock::s_tobCoordMask;
+const uint32_t CmxCpSubBlock::s_tobChipMask;
+const uint32_t CmxCpSubBlock::s_tobCpmMask;
+
+const int      CmxCpSubBlock::s_threshBit;
+const int      CmxCpSubBlock::s_threshErrorBit;
+const int      CmxCpSubBlock::s_hlFlagBit;
+const int      CmxCpSubBlock::s_sourceIdBit;
+const int      CmxCpSubBlock::s_dataWordIdBit;
+const int      CmxCpSubBlock::s_threshWordId;
+const uint32_t CmxCpSubBlock::s_threshMask;
+const uint32_t CmxCpSubBlock::s_errorMask;
+const uint32_t CmxCpSubBlock::s_hlFlagMask;
+const uint32_t CmxCpSubBlock::s_sourceIdMask;
+const uint32_t CmxCpSubBlock::s_dataWordIdMask;
+
+const int      CmxCpSubBlock::s_presenceBits;
+const int      CmxCpSubBlock::s_coordBits;
+const int      CmxCpSubBlock::s_isolationBits;
+const int      CmxCpSubBlock::s_parityErrorBits;
+const int      CmxCpSubBlock::s_parityErrorMask;
+const int      CmxCpSubBlock::s_energyBits;
+const int      CmxCpSubBlock::s_hitsBits;
+const int      CmxCpSubBlock::s_hitsErrorBits;
+const int      CmxCpSubBlock::s_roiOverflowBits;
+const int      CmxCpSubBlock::s_paddingBits;
+const int      CmxCpSubBlock::s_bunchCrossingBits;
+const int      CmxCpSubBlock::s_fifoOverflowBits;
+const int      CmxCpSubBlock::s_topoChecksumBits;
+const int      CmxCpSubBlock::s_topoMapBits;
+const int      CmxCpSubBlock::s_topoCountsBits;
+const int      CmxCpSubBlock::s_topoPaddingBits;
+const int      CmxCpSubBlock::s_glinkPins;
+const int      CmxCpSubBlock::s_modules;
+const int      CmxCpSubBlock::s_tobsPerModule;
+const int      CmxCpSubBlock::s_muxPhases;
+
+
+CmxCpSubBlock::CmxCpSubBlock()
+{
+}
+
+CmxCpSubBlock::~CmxCpSubBlock()
+{
+}
+
+// Clear all data
+
+void CmxCpSubBlock::clear()
+{
+  L1CaloSubBlock::clear();
+  m_tobData.clear();
+  m_hitsData.clear();
+  m_presenceMaps.clear();
+  m_overflow.clear();
+}
+
+// Return presence map for given CPM
+
+unsigned int CmxCpSubBlock::presenceMap(const int slice,
+                                        const int cpm) const
+{
+  unsigned int map = 0;
+  const unsigned int ix = mapIndex(slice, cpm);
+  if (ix < m_presenceMaps.size()) map = m_presenceMaps[ix];
+  return map;
+}
+
+// Return chip for given cpm and tob
+
+int CmxCpSubBlock::chip(const int slice, const int cpm, const int tob) const
+{
+  int ch = 0;
+  const unsigned int ix = tobIndex(slice, cpm, tob);
+  if (ix < m_tobData.size()) {
+    ch = (m_tobData[ix] >> s_tobChipBit) & s_tobChipMask;
+  }
+  return ch;
+}
+
+// Return Local coordinate for given cpm and tob
+
+int CmxCpSubBlock::localCoord(const int slice, const int cpm, const int tob) const
+{
+  int coord = 0;
+  const unsigned int ix = tobIndex(slice, cpm, tob);
+  if (ix < m_tobData.size()) {
+    coord = (m_tobData[ix] >> s_tobCoordBit) & s_tobCoordMask;
+  }
+  return coord;
+}
+
+// Return isolation for given cpm and tob
+
+int CmxCpSubBlock::isolation(const int slice, const int cpm, const int tob) const
+{
+  int isol = 0;
+  const unsigned int ix = tobIndex(slice, cpm, tob);
+  if (ix < m_tobData.size()) {
+    isol = (m_tobData[ix] >> s_tobIsolationBit) & s_tobIsolationMask;
+  }
+  return isol;
+}
+
+// Return energy for given cpm and tob
+
+int CmxCpSubBlock::energy(const int slice, const int cpm, const int tob) const
+{
+  int et = 0;
+  const unsigned int ix = tobIndex(slice, cpm, tob);
+  if (ix < m_tobData.size()) {
+    et = (m_tobData[ix] >> s_tobEnergyBit) & s_tobEnergyMask;
+  }
+  return et;
+}
+
+// Return error bits for given cpm and tob
+
+int  CmxCpSubBlock::tobError(int slice, int cpm, int tob) const
+{
+  int error = 0;
+  const unsigned int ix = tobIndex(slice, cpm, tob);
+  if (ix < m_tobData.size()) {
+    error = (m_tobData[ix] >> s_tobErrorBit) & s_tobErrorMask;
+  }
+  return error;
+}
+
+// Return hit/topo counts for given source ID and HL flag
+
+unsigned int CmxCpSubBlock::hits(const int slice, const int source,
+                                                  const int flag) const
+{
+  unsigned int hits = 0;
+  const unsigned int ix = hitIndex(slice, source, flag);
+  if (ix < m_hitsData.size()) {
+    hits = (m_hitsData[ix] >> s_threshBit) & s_threshMask;
+  }
+  return hits;
+}
+
+// Return hit error for given source ID and HL flag
+
+int CmxCpSubBlock::hitsError(const int slice, const int source,
+                                              const int flag) const
+{
+  int error = 0;
+  const unsigned int ix = hitIndex(slice, source, flag);
+  if (ix < m_hitsData.size()) {
+    error = (m_hitsData[ix] >> s_threshErrorBit) & s_errorMask;
+  }
+  return error;
+}
+
+// Return RoI overflow for given source ID
+
+int CmxCpSubBlock::roiOverflow(const int slice, const int source) const
+{
+  int overflow = 0;
+  const unsigned int ix = ovfIndex(slice, source);
+  if (ix < m_overflow.size()) overflow = m_overflow[ix];
+  return overflow;
+}
+
+// Store presence map
+
+void CmxCpSubBlock::setPresenceMap(const int slice, const int cpm,
+                                                    const unsigned int map)
+{
+  resize();
+  if (map) {
+    const unsigned int ix = mapIndex(slice, cpm);
+    if (ix < m_presenceMaps.size()) m_presenceMaps[ix] = map;
+  }
+}
+
+// Store TOB (RoI) data for given CPM, chip, local coord
+
+void CmxCpSubBlock::setTob(const int slice, const int cpm, const int chip,
+                           const int loc, const int energy, const int isol,
+			   const int error)
+{
+  resize();
+  if (energy || isol || error) {
+    uint32_t word = 0;
+    word |= (energy & s_tobEnergyMask)    << s_tobEnergyBit;
+    word |= (isol   & s_tobIsolationMask) << s_tobIsolationBit;
+    word |= (error  & s_tobErrorMask)     << s_tobErrorBit;
+    word |= (loc    & s_tobCoordMask)     << s_tobCoordBit;
+    word |= (chip   & s_tobChipMask)      << s_tobChipBit;
+    word |= (cpm    & s_tobCpmMask)       << s_tobCpmBit;
+    word |= (s_tobWordId)                 << s_dataWordIdBit;
+    // Order by chip == presence bit                                          // <<== CHECK
+    for (int tob = 0; tob < s_tobsPerModule; ++tob) {
+      const unsigned int ix = tobIndex(slice, cpm, tob);
+      if (m_tobData[ix] == 0) {
+        m_tobData[ix] = word;
+	break;
+      } else {
+        const int chipOld = (m_tobData[ix]>>s_tobChipBit)&s_tobChipMask;
+	if (chip < chipOld) {
+          for (int i = s_tobsPerModule-tob-1; i > 0; --i) {
+	    m_tobData[ix + i] = m_tobData[ix + i - 1];
+          }
+	  m_tobData[ix] = word;
+	  break;
+        }
+      }
+    }
+  }
+}
+
+// Store hit counts for given source ID and HL flag
+
+void CmxCpSubBlock::setHits(const int slice, const int source, const int flag,
+                            const unsigned int hits, const int error)
+{
+  resize();
+  const unsigned int ix = hitIndex(slice, source, flag);
+  if (ix < m_hitsData.size() && (hits || error)) {
+    uint32_t word = m_hitsData[ix];
+    word |= (hits & s_threshMask)     << s_threshBit;
+    word |= (error & s_errorMask)     << s_threshErrorBit;
+    word |= (flag & s_hlFlagMask)     << s_hlFlagBit;
+    word |= (source & s_sourceIdMask) << s_sourceIdBit;
+    word |= (s_threshWordId)          << s_dataWordIdBit;
+    m_hitsData[ix] = word;
+  }
+}
+
+// Store RoI overflow for given source ID
+
+void CmxCpSubBlock::setRoiOverflow(const int slice, const int source,
+                                                    const int overflow)
+{
+  resize();
+  if (overflow) {
+    const unsigned int ix = ovfIndex(slice, source);
+    if (ix < m_overflow.size()) m_overflow[ix] = overflow;
+  }
+}
+
+// Packing/Unpacking routines
+
+bool CmxCpSubBlock::pack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 2:                                                  // <<== CHECK
+      switch (format()) {
+        case NEUTRAL:
+	  rc = packNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = packUncompressed();
+	  break;
+        default:
+	  break;
+      }
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
+
+bool CmxCpSubBlock::unpack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 2:                                                  // <<== CHECK
+      switch (format()) {
+        case NEUTRAL:
+	  rc = unpackNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = unpackUncompressed();
+	  break;
+        default:
+	  setUnpackErrorCode(UNPACK_FORMAT);
+	  break;
+      }
+      break;
+    default:
+      setUnpackErrorCode(UNPACK_VERSION);
+      break;
+  }
+  return rc;
+}
+
+// Return presence map index appropriate to format
+
+unsigned int CmxCpSubBlock::mapIndex(const int slice,
+                                     const int cpm) const
+{
+  unsigned int ix = cpm - 1;
+  if (format() == NEUTRAL) ix += slice * s_modules;
+  return ix;
+}
+
+// Return tob data index appropriate to format
+
+unsigned int CmxCpSubBlock::tobIndex(const int slice, const int cpm,
+                                             const int tob) const
+{
+  unsigned int ix = (cpm - 1) * s_tobsPerModule + tob;
+  if (format() == NEUTRAL) ix += slice * s_modules * s_tobsPerModule;
+  return ix;
+}
+
+// Return hits data index appropriate to format
+
+unsigned int CmxCpSubBlock::hitIndex(const int slice, const int source,
+                                             const int flag) const
+{
+  unsigned int ix = (source<<1)|flag;
+  if (format() == NEUTRAL) ix += slice * 2 * MAX_SOURCE_ID;
+  return ix;
+}
+
+// Return overflow index appropriate to format
+
+unsigned int CmxCpSubBlock::ovfIndex(const int slice, const int source) const
+{
+  unsigned int ix = source;
+  if (format() == NEUTRAL) ix += slice * MAX_SOURCE_ID;
+  return ix;
+}
+
+// Resize the data vectors according to format
+
+void CmxCpSubBlock::resize()
+{
+  if (m_tobData.empty()) {
+    int size1 = s_modules * s_tobsPerModule;
+    int size2 = 2 * MAX_SOURCE_ID;
+    int size3 = s_modules;
+    int size4 = MAX_SOURCE_ID;
+    if (format() == NEUTRAL) {
+      size1 *= timeslices();
+      size2 *= timeslices();
+      size3 *= timeslices();
+      size4 *= timeslices();
+    }
+    m_tobData.resize(size1);
+    m_hitsData.resize(size2);
+    m_presenceMaps.resize(size3);
+    m_overflow.resize(size4);
+  }
+}
+
+// Pack neutral data
+
+bool CmxCpSubBlock::packNeutral()
+{
+  resize();
+  std::vector<int> locVec(s_tobsPerModule);
+  std::vector<int> isolVec(s_tobsPerModule);
+  std::vector<int> parityVec(s_muxPhases);
+  std::vector<int> energyVec(s_tobsPerModule);
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    for (int pin = 0; pin < s_glinkPins; ++pin) {
+      if (pin < s_modules) { // TOB data
+	const int cpm = pin + 1;
+        // Presence map
+        packerNeutral(pin, presenceMap(slice, cpm), s_presenceBits);
+	// Get tob data for this cpm
+	locVec.clear();
+	isolVec.clear();
+	parityVec.clear();
+	energyVec.clear();
+	int parityMerge = 0;
+	for (int tob = 0; tob < s_tobsPerModule; ++tob) {
+	  locVec.push_back(localCoord(slice, cpm, tob));
+	  isolVec.push_back(isolation(slice, cpm, tob));
+	  energyVec.push_back(energy(slice, cpm, tob));
+	  if (tob == 0) {
+	    const int err = tobError(slice, cpm, tob);
+	    parityMerge = err&s_parityErrorMask;
+	    for (int mux = 0; mux < s_muxPhases; ++mux) {
+	      parityVec.push_back((err>>(mux+s_parityErrorBits))&s_parityErrorMask);
+	    }
+          }
+	}
+	// And pack
+	int locCount = 0;
+	int isolCount = 0;
+	int parityCount = 0;
+        int energyCount = 0;
+        for (int tob = 0; tob < s_tobsPerModule-2; ++tob) {
+          // Local coordinates (2 bit)
+	  packerNeutral(pin, locVec[locCount++], s_coordBits);
+	  // isolation
+	  packerNeutral(pin, isolVec[isolCount++], s_isolationBits);
+	  // backplane parity error
+	  packerNeutral(pin, parityVec[parityCount++], s_parityErrorBits);
+	  // energy
+	  packerNeutral(pin, energyVec[energyCount++], s_energyBits);
+	  if (tob < s_tobsPerModule-3) {
+	    packerNeutral(pin, energyVec[energyCount++], s_energyBits);
+          } else {
+	    packerNeutral(pin, locVec[locCount++], s_coordBits);
+	    packerNeutral(pin, isolVec[isolCount++], s_isolationBits);
+	    packerNeutral(pin, parityMerge, s_parityErrorBits);
+	    packerNeutral(pin, locVec[locCount++], s_coordBits);
+	    packerNeutral(pin, isolVec[isolCount++], s_isolationBits);
+	    packerNeutral(pin, parityVec[parityCount++], s_parityErrorBits);
+	  }
+        }
+      } else { // Hits and Topo data
+        if (pin < s_glinkPins-1) {
+          // Remote(3), local and total hits; parity error
+          const int source = pin - s_modules;
+          packerNeutral(pin, hits(slice, source, 0), s_hitsBits);
+          packerNeutral(pin, hitsError(slice, source, 0), s_hitsErrorBits);
+          packerNeutral(pin, hits(slice, source, 1), s_hitsBits);
+          packerNeutral(pin, hitsError(slice, source, 1), s_hitsErrorBits);
+          packerNeutral(pin, roiOverflow(slice, source), s_hitsErrorBits);
+	  packerNeutral(pin, 0, s_paddingBits);
+        } else {
+          // Bunch crossing number, Fifo overflow and Topo data
+          packerNeutral(pin, bunchCrossing(), s_bunchCrossingBits);
+	  packerNeutral(pin, daqOverflow(), s_fifoOverflowBits);
+	  packerNeutral(pin, hits(slice, TOPO_CHECKSUM, 0), s_topoChecksumBits);
+	  packerNeutral(pin, hits(slice, TOPO_OCCUPANCY_MAP, 0), s_topoMapBits);
+	  packerNeutral(pin, hits(slice, TOPO_OCCUPANCY_COUNTS, 0), s_topoCountsBits);
+	  packerNeutral(pin, hits(slice, TOPO_OCCUPANCY_COUNTS, 1), s_topoCountsBits);
+	  packerNeutral(pin, 0, s_topoPaddingBits);
+        }
+      }
+      // G-Link parity
+      packerNeutralParity(pin);
+    }
+  }
+  return true;
+}
+
+// Pack uncompressed data
+
+bool CmxCpSubBlock::packUncompressed()
+{
+  std::vector<uint32_t>::const_iterator pos;
+  for (pos = m_tobData.begin(); pos != m_tobData.end(); ++pos) {
+    if (*pos) packer(*pos, s_wordLength);
+  }
+  for (pos = m_hitsData.begin(); pos != m_hitsData.end(); ++pos) {
+    if (*pos) packer(*pos, s_wordLength);
+  }
+  packerFlush();
+  return true;
+}
+
+// Unpack neutral data
+
+bool CmxCpSubBlock::unpackNeutral()
+{
+  resize();
+  int bunchCrossing = 0;
+  int fifoOverflow = 0;
+  int glinkParity = 0;
+  std::vector<int> locVec(s_tobsPerModule);
+  std::vector<int> isolVec(s_tobsPerModule);
+  std::vector<int> parityVec(s_muxPhases);
+  std::vector<int> energyVec(s_tobsPerModule);
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    for (int pin = 0; pin < s_glinkPins; ++pin) {
+      if (pin < s_modules) { // TOB data
+        // Presence map
+	const unsigned int map = unpackerNeutral(pin, s_presenceBits);
+	locVec.clear();
+	isolVec.clear();
+	parityVec.clear();
+	energyVec.clear();
+	int parityMerge = 0;
+	for (int tob = 0; tob < s_tobsPerModule-2; ++tob) {
+	  // Local coordinates (2 bit)
+	  locVec.push_back(unpackerNeutral(pin, s_coordBits));
+	  // isolation
+	  isolVec.push_back(unpackerNeutral(pin, s_isolationBits));
+	  // backplane parity error
+	  parityVec.push_back(unpackerNeutral(pin, s_parityErrorBits));
+	  // energy
+	  energyVec.push_back(unpackerNeutral(pin, s_energyBits));
+	  if (tob < s_tobsPerModule-3) {
+	    energyVec.push_back(unpackerNeutral(pin, s_energyBits));
+          } else {
+	    locVec.push_back(unpackerNeutral(pin, s_coordBits));
+	    isolVec.push_back(unpackerNeutral(pin, s_isolationBits));
+	    parityMerge = unpackerNeutral(pin, s_parityErrorBits);
+	    locVec.push_back(unpackerNeutral(pin, s_coordBits));
+	    isolVec.push_back(unpackerNeutral(pin, s_isolationBits));
+	    parityVec.push_back(unpackerNeutral(pin, s_parityErrorBits));
+          }
+        }
+	int ntobs = 0;
+	for (int bit = 0; bit < s_presenceBits; ++bit) ntobs += (map>>bit)&1;
+	int error = parityMerge;
+	for (int mux = 0; mux < s_muxPhases; ++mux) {
+	  error |= (parityVec[mux]<<(mux+s_parityErrorBits));
+	}
+	if (ntobs > s_tobsPerModule) error |= (1<<(s_muxPhases+s_parityErrorBits)); // overflow
+	int tob = 0;
+	const int cpm = pin + 1;
+	for (int chip = 0; chip < s_presenceBits && tob < s_tobsPerModule; ++chip) {  // <<== CHECK - assuming bit==chip
+	  if ((map>>chip)&1) {
+	    setTob(slice, cpm, chip, locVec[tob], energyVec[tob], isolVec[tob], error);
+	    ++tob;
+          }
+        }
+        setPresenceMap(slice, cpm, map);
+      } else {  // Hits and Topo data
+        if (pin < s_glinkPins-1) {
+	  // Remote(3), local and total hits; parity error
+	  const int source = pin - s_modules;
+	  unsigned int hits = unpackerNeutral(pin, s_hitsBits);
+	  int error = unpackerNeutral(pin, s_hitsErrorBits);
+	  setHits(slice, source, 0, hits, error);
+	  hits = unpackerNeutral(pin, s_hitsBits);
+	  error = unpackerNeutral(pin, s_hitsErrorBits);
+	  setHits(slice, source, 1, hits, error);
+	  error = unpackerNeutral(pin, s_hitsErrorBits);
+	  setRoiOverflow(slice, source, error);
+	  unpackerNeutral(pin, s_paddingBits);
+        } else {
+	  // Bunch crossing number, Fifo overflow and Topo data
+	  bunchCrossing = unpackerNeutral(pin, s_bunchCrossingBits);
+	  fifoOverflow |= unpackerNeutral(pin, s_fifoOverflowBits);
+	  unsigned int hits = unpackerNeutral(pin, s_topoChecksumBits);
+	  int error = 0;
+	  setHits(slice, TOPO_CHECKSUM, 0, hits, error);
+	  hits = unpackerNeutral(pin, s_topoMapBits);
+	  setHits(slice, TOPO_OCCUPANCY_MAP, 0, hits, error);
+	  hits = unpackerNeutral(pin, s_topoCountsBits);
+	  setHits(slice, TOPO_OCCUPANCY_COUNTS, 0, hits, error);
+	  hits = unpackerNeutral(pin, s_topoCountsBits);
+	  setHits(slice, TOPO_OCCUPANCY_COUNTS, 1, hits, error);
+	  unpackerNeutral(pin, s_topoPaddingBits);
+        }
+      }
+      // G-Link parity errors
+      glinkParity |= unpackerNeutralParityError(pin);
+    }
+  }
+  setBunchCrossing(bunchCrossing);
+  setDaqOverflow(fifoOverflow);
+  setGlinkParity(glinkParity);
+
+  const bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  return rc;
+}
+
+// Unpack uncompressed data
+
+bool CmxCpSubBlock::unpackUncompressed()
+{
+  resize();
+  const int maxHits = m_hitsData.size();
+  m_cpmTobCount.assign(s_modules, 0);
+  unpackerInit();
+  uint32_t word = unpacker(s_wordLength);
+  while (unpackerSuccess()) {
+    const int id = dataWordId(word);
+    if (id == s_tobWordId) {  // TOB data
+      const int index = cpm(word)-1;
+      const int count = m_cpmTobCount[index];
+      const int index2 = index*s_tobsPerModule + count;
+      if (count < s_tobsPerModule) {
+        m_tobData[index2] = word;
+        ++m_cpmTobCount[index];
+      } else {
+        setUnpackErrorCode(UNPACK_EXCESS_TOBS);             // New code.  Check consequences
+	return false;
+      }
+    } else if (id == s_threshWordId) {  // Hits and Topo data
+      const int index = (sourceId(word)<<1) | hlFlag(word);
+      if (index < maxHits && m_hitsData[index] == 0) {
+        m_hitsData[index] = word;
+      } else {
+        setUnpackErrorCode(UNPACK_SOURCE_ID);
+	return false;
+      }
+    } else {
+      setUnpackErrorCode(UNPACK_DATA_ID);                // New code
+      return false;
+    }
+    word = unpacker(s_wordLength);
+  }
+  return true;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxCpSubBlock.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxCpSubBlock.h
new file mode 100755
index 0000000000000000000000000000000000000000..87b886a94d9f498af3c6041f8c3fe625daee9c34
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxCpSubBlock.h
@@ -0,0 +1,178 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CMXCPSUBBLOCK_H
+#define TRIGT1CALOBYTESTREAM_CMXCPSUBBLOCK_H
+
+#include <stdint.h>
+#include <vector>
+
+#include "CmxSubBlock.h"
+
+namespace LVL1BS {
+
+/** Sub-Block class for CMX-CP data post LS1.
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version X.xxx                                                    <<== CHECK
+ *
+ *  @author Peter Faulkner
+ */
+
+class CmxCpSubBlock : public CmxSubBlock {
+
+ public:
+
+   /// Sources of threshold sums
+   enum SourceId { REMOTE_0, REMOTE_1, REMOTE_2, LOCAL, TOTAL,
+                   TOPO_CHECKSUM, TOPO_OCCUPANCY_MAP, TOPO_OCCUPANCY_COUNTS,
+		   MAX_SOURCE_ID };
+
+   CmxCpSubBlock();
+   ~CmxCpSubBlock();
+
+   /// Clear all data
+   void clear();
+
+   /// Return presence map for given CPM
+   unsigned int presenceMap(int slice, int cpm)       const;
+   /// Return chip for given cpm and tob
+   int chip(int slice, int cpm, int tob)              const;
+   /// Return Local coordinate for given cpm and tob
+   int localCoord(int slice, int cpm, int tob)        const;
+   /// Return isolation for given cpm and tob
+   int isolation(int slice, int cpm, int tob)         const;
+   /// Return energy for given cpm and tob
+   int energy(int slice, int cpm, int tob)            const;
+   /// Return error bits for given cpm and tob
+   int tobError(int slice, int cpm, int tob)          const;
+   /// Return hit/topo counts for given source ID and HL flag
+   unsigned int hits(int slice, int source, int flag) const;
+   /// Return hit error for given source ID and HL flag
+   int hitsError(int slice, int source, int flag)     const;
+   /// Return RoI overflow for given source ID
+   int roiOverflow(int slice, int source)             const;
+
+   /// Store presence map
+   void setPresenceMap(int slice, int cpm, unsigned int map);
+   /// Store TOB (RoI) data for given CPM, chip, local coord
+   void setTob(int slice, int cpm, int chip, int loc,            //  NB chip and loc 4 and 2 bits here, as in diagram
+               int energy, int isol, int error);
+   /// Store hit counts for given source ID and HL flag
+   void setHits(int slice, int source, int flag, unsigned int hits, int error);
+   /// Store RoI overflow for given source ID
+   void setRoiOverflow(int slice, int source, int overflow);
+
+   /// Pack data
+   bool pack();
+   /// Unpack data
+   bool unpack();
+
+ private:
+   /// Data word length
+   static const int      s_wordLength       = 32;
+   //  TOB bit positions and masks
+   static const int      s_tobEnergyBit     = 0;
+   static const int      s_tobIsolationBit  = 8;
+   static const int      s_tobErrorBit      = 13;
+   static const int      s_tobOverflowBit   = 18;
+   static const int      s_tobCoordBit      = 19;
+   static const int      s_tobChipBit       = 21;
+   static const int      s_tobCpmBit        = 25;
+   static const int      s_tobWordId        = 0;
+   static const uint32_t s_tobEnergyMask    = 0xff;
+   static const uint32_t s_tobIsolationMask = 0x1f;
+   static const uint32_t s_tobErrorMask     = 0x3f;  // includes RoI overflow
+   static const uint32_t s_tobCoordMask     = 0x3;
+   static const uint32_t s_tobChipMask      = 0xf;
+   static const uint32_t s_tobCpmMask       = 0xf;
+   //  EM/Tau hit and topo counts bit positions and masks
+   static const int      s_threshBit        = 0;
+   static const int      s_threshErrorBit   = 24;
+   static const int      s_hlFlagBit        = 25;
+   static const int      s_sourceIdBit      = 26;
+   static const int      s_dataWordIdBit    = 29;
+   static const int      s_threshWordId     = 1;
+   static const uint32_t s_threshMask       = 0xffffff;
+   static const uint32_t s_errorMask        = 0x1;
+   static const uint32_t s_hlFlagMask       = 0x1;
+   static const uint32_t s_sourceIdMask     = 0x7;
+   static const uint32_t s_dataWordIdMask   = 0x7;
+   //  Neutral format
+   static const int      s_presenceBits      = 16;
+   static const int      s_coordBits         = 2;
+   static const int      s_isolationBits     = 5;
+   static const int      s_parityErrorBits   = 1;
+   static const int      s_parityErrorMask   = 0x1;
+   static const int      s_energyBits        = 8;
+   static const int      s_hitsBits          = 24;
+   static const int      s_hitsErrorBits     = 1;
+   static const int      s_roiOverflowBits   = 1;
+   static const int      s_paddingBits       = 45;
+   static const int      s_bunchCrossingBits = 12;
+   static const int      s_fifoOverflowBits  = 1;
+   static const int      s_topoChecksumBits  = 16;
+   static const int      s_topoMapBits       = 14;
+   static const int      s_topoCountsBits    = 21;
+   static const int      s_topoPaddingBits   = 11;
+   static const int      s_glinkPins         = 20;
+   static const int      s_modules           = 14;
+   static const int      s_tobsPerModule     = 5;
+   static const int      s_muxPhases         = 4;
+
+   int dataWordId(uint32_t word) const;
+   int sourceId(uint32_t word)   const;
+   int cpm(uint32_t word)        const;
+   int hlFlag(uint32_t word)     const;
+   unsigned int mapIndex(int slice, int cpm)              const;
+   unsigned int tobIndex(int slice, int cpm, int tob)     const;
+   unsigned int hitIndex(int slice, int source, int flag) const;
+   unsigned int ovfIndex(int slice, int source)           const;
+   void resize();
+
+   /// Pack neutral data
+   bool packNeutral();
+   /// Pack uncompressed data
+   bool packUncompressed();
+   /// Unpack neutral data
+   bool unpackNeutral();
+   /// Unpack uncompressed data
+   bool unpackUncompressed();
+
+   /// TOB data
+   std::vector<uint32_t> m_tobData;
+   /// Hits and topo data
+   std::vector<uint32_t> m_hitsData;
+   /// Presence maps
+   std::vector<unsigned int> m_presenceMaps;
+   /// CPM TOB count vector for unpacking
+   std::vector<int> m_cpmTobCount;
+   /// RoI overflows for neutral data
+   std::vector<int> m_overflow;
+
+};
+
+inline int CmxCpSubBlock::dataWordId(const uint32_t word) const
+{
+  return (word >> s_dataWordIdBit) & s_dataWordIdMask;
+}
+
+inline int CmxCpSubBlock::sourceId(const uint32_t word) const
+{
+  return (word >> s_sourceIdBit) & s_sourceIdMask;
+}
+
+inline int CmxCpSubBlock::cpm(const uint32_t word) const
+{
+  return (word >> s_tobCpmBit) & s_tobCpmMask;
+}
+
+inline int CmxCpSubBlock::hlFlag(const uint32_t word) const
+{
+  return (word >> s_hlFlagBit) & s_hlFlagMask;
+}
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxEnergySubBlock.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxEnergySubBlock.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..384af45046ba4b6a83d500406f9f9d45848b9afe
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxEnergySubBlock.cxx
@@ -0,0 +1,597 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include <utility>
+
+#include "CmxEnergySubBlock.h"
+
+namespace LVL1BS {
+
+// Constant definitions
+
+const int      CmxEnergySubBlock::s_wordLength;
+
+const int      CmxEnergySubBlock::s_overflowBit;
+const int      CmxEnergySubBlock::s_errorBit;
+const int      CmxEnergySubBlock::s_etHitsBit;
+const int      CmxEnergySubBlock::s_energyTypeJemBit;
+const int      CmxEnergySubBlock::s_jemBit;
+const int      CmxEnergySubBlock::s_energyTypeBit;
+const int      CmxEnergySubBlock::s_sumTypeBit;
+const int      CmxEnergySubBlock::s_sourceBit;
+const int      CmxEnergySubBlock::s_wordIdBit;
+const int      CmxEnergySubBlock::s_maxJems;
+const int      CmxEnergySubBlock::s_maxSums;
+const uint32_t CmxEnergySubBlock::s_energyJemMask;
+const uint32_t CmxEnergySubBlock::s_energySumMask;
+const uint32_t CmxEnergySubBlock::s_errorMask;
+const uint32_t CmxEnergySubBlock::s_overflowMask;
+const uint32_t CmxEnergySubBlock::s_etHitsMask;
+const uint32_t CmxEnergySubBlock::s_jemMask;
+const uint32_t CmxEnergySubBlock::s_energyTypeMask;
+const uint32_t CmxEnergySubBlock::s_sumTypeMask;
+const uint32_t CmxEnergySubBlock::s_sourceMask;
+const uint32_t CmxEnergySubBlock::s_wordIdMask;
+
+const int      CmxEnergySubBlock::s_jemSumBits;
+const int      CmxEnergySubBlock::s_jemPaddingBits;
+const int      CmxEnergySubBlock::s_sumBitsEtCrate;
+const int      CmxEnergySubBlock::s_sumBitsEtSys;
+const int      CmxEnergySubBlock::s_sumBitsExEy;
+const int      CmxEnergySubBlock::s_bunchCrossingBits;
+const int      CmxEnergySubBlock::s_etHitMapsBits;
+const int      CmxEnergySubBlock::s_paddingBits;
+
+
+CmxEnergySubBlock::CmxEnergySubBlock()
+{
+}
+
+CmxEnergySubBlock::~CmxEnergySubBlock()
+{
+}
+
+// Clear all data
+
+void CmxEnergySubBlock::clear()
+{
+  L1CaloSubBlock::clear();
+  m_sumsData.clear();
+}
+
+// Return energy subsum for given JEM and energy type
+
+unsigned int CmxEnergySubBlock::energy(const int slice, const int jem,
+                                       const EnergyType eType) const
+{
+  unsigned int e = 0;
+  if (slice >= 0 && slice < timeslices() && !m_sumsData.empty()) {
+    if (jem >= 0 && jem < s_maxJems) {
+      e = m_sumsData[index(slice, jem) + eType] & s_energyJemMask;
+    }
+  }
+  return e;
+}
+
+// Return energy subsum error for given JEM and energy type
+
+int CmxEnergySubBlock::error(const int slice, const int jem,
+                             const EnergyType eType) const
+{
+  int parity = 0;
+  if (slice >= 0 && slice < timeslices() && !m_sumsData.empty()) {
+    if (jem >= 0 && jem < s_maxJems) {
+      parity = (m_sumsData[index(slice, jem) + eType] >> s_errorBit)
+                                                              & s_errorMask;
+    }
+  }
+  return parity<<1;
+}
+
+// Return energy subsum for given source, sum type and energy type
+
+unsigned int CmxEnergySubBlock::energy(const int slice,
+                                       const SourceType source,
+				       const SumType    sType,
+                                       const EnergyType eType) const
+{
+  unsigned int e = 0;
+  if (slice >= 0 && slice < timeslices() && !m_sumsData.empty()) {
+    const int pos = s_maxJems + 2*source + sType;
+    e = m_sumsData[index(slice, pos) + eType] & s_energySumMask;
+  }
+  return e;
+}
+
+// Return energy subsum error for given source, sum type and energy type
+
+int CmxEnergySubBlock::error(const int slice,
+                             const SourceType source,
+			     const SumType    sType,
+                             const EnergyType eType) const
+{
+  int parity = 0;
+  int overflow = 0;
+  if (slice >= 0 && slice < timeslices() && !m_sumsData.empty()) {
+    const int pos = s_maxJems + 2*source + sType;
+    const uint32_t word = m_sumsData[index(slice, pos) + eType];
+    overflow = (word >> s_overflowBit) & s_overflowMask;
+    if (source == REMOTE) parity = (word >> s_errorBit) & s_errorMask;
+  }
+  return (parity<<1) + overflow;
+}
+
+// Return hits map for given hits type and sum type
+
+unsigned int CmxEnergySubBlock::hits(const int slice,
+                                     const HitsType hType,
+				     const SumType  sType) const
+{
+  unsigned int map = 0;
+  if (slice >= 0 && slice < timeslices() && !m_sumsData.empty()) {
+    const int pos = s_maxJems + 2*TOTAL + sType;
+    map = (m_sumsData[index(slice, pos) + hType] >> s_etHitsBit)
+                                                             & s_etHitsMask;
+  }
+  return map;
+}
+
+// Store energy subsums and errors for given JEM
+
+void CmxEnergySubBlock::setSubsums(const int slice, const int jem,
+                                   const unsigned int ex, const unsigned int ey,
+				   const unsigned int et, const int exError,
+				   const int eyError, const int etError)
+{
+  if (slice >= 0 && slice < timeslices() && jem >= 0 && jem < s_maxJems) {
+    resize();
+    const int ix = index(slice, jem);
+    unsigned int energy = 0;
+    int          error  = 0;
+    int          parity = 0;
+    uint32_t     word   = 0;
+    for (int eType = 0; eType < MAX_ENERGY_TYPE; ++eType) {
+      if (eType == ENERGY_EX) {
+        energy = ex;
+	error  = exError;
+      } else if (eType == ENERGY_EY) {
+        energy = ey;
+	error  = eyError;
+      } else {
+        energy = et;
+	error = etError;
+      }
+      parity = (error >> 1) & s_errorMask;
+      if (energy || parity) {
+        word  = energy & s_energyJemMask;
+        word |= parity    << s_errorBit;
+        word |= eType     << s_energyTypeJemBit;
+        word |= jem       << s_jemBit;
+        word |= MODULE_ID << s_wordIdBit;
+        m_sumsData[ix + eType] = word;
+      }
+    }
+  }
+}
+
+// Store energy subsums and errors for given source and sum type
+
+void CmxEnergySubBlock::setSubsums(const int slice, const SourceType source,
+                                   const SumType sType,
+                                   const unsigned int ex, const unsigned int ey,
+				   const unsigned int et, const int exError,
+				   const int eyError, const int etError)
+{
+  if (slice >= 0 && slice < timeslices()) {
+    resize();
+    const int pos = s_maxJems + 2*source + sType;
+    const int ix = index(slice, pos);
+    unsigned int energy   = 0;
+    int          error    = 0;
+    int          overflow = 0;
+    int          parity   = 0;
+    uint32_t     word     = 0;
+    uint32_t     baseword = (CRATE_SYSTEM_ID << s_wordIdBit) +
+                            (source          << s_sourceBit) +
+			    (sType           << s_sumTypeBit);
+    for (int eType = 0; eType < MAX_ENERGY_TYPE; ++eType) {
+      if (eType == ENERGY_EX) {
+        energy = ex;
+	error  = exError;
+      } else if (eType == ENERGY_EY) {
+        energy = ey;
+	error  = eyError;
+      } else {
+        energy = et;
+	error  = etError;
+      }
+      overflow = error & s_overflowMask;
+      parity   = (source == REMOTE) ? ((error >> 1) & s_errorMask) : 0;
+      if (energy || overflow || parity) {
+	word  = m_sumsData[ix + eType];
+        word |= energy    & s_energySumMask;
+	word |= overflow << s_overflowBit;
+        word |= parity   << s_errorBit;
+        word |= eType    << s_energyTypeBit;
+	word |= baseword;
+        m_sumsData[ix + eType] = word;
+      }
+    }
+  }
+}
+
+// Store hits map for given hits type and sum type
+
+void CmxEnergySubBlock::setEtHits(const int slice, const HitsType hType,
+                                  const SumType sType, const unsigned int map)
+{
+  if (map && slice >= 0 && slice < timeslices()) {
+    resize();
+    const int pos = s_maxJems + 2*TOTAL + sType;
+    const int ix  = index(slice, pos);
+    uint32_t word = m_sumsData[ix + hType];
+    word |= (map & s_etHitsMask) << s_etHitsBit;
+    word |=  hType               << s_energyTypeBit;
+    word |=  sType               << s_sumTypeBit;
+    word |=  TOTAL               << s_sourceBit;
+    word |=  CRATE_SYSTEM_ID     << s_wordIdBit;
+    m_sumsData[ix + hType] = word;
+  }
+}
+
+// Packing/Unpacking routines
+
+bool CmxEnergySubBlock::pack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 3:                                                      //<<== CHECK
+      switch (format()) {
+        case NEUTRAL:
+	  rc = packNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = packUncompressed();
+	  break;
+        default:
+	  break;
+      }
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
+
+bool CmxEnergySubBlock::unpack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 3:                                                      //<<== CHECK
+      switch (format()) {
+        case NEUTRAL:
+	  rc = unpackNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = unpackUncompressed();
+	  break;
+        default:
+	  setUnpackErrorCode(UNPACK_FORMAT);
+	  break;
+      }
+      break;
+    default:
+      setUnpackErrorCode(UNPACK_VERSION);
+      break;
+  }
+  return rc;
+}
+
+// Return data index appropriate to format
+
+int CmxEnergySubBlock::index(const int slice, const int pos) const
+{
+  int ix = 3*pos;
+  if (format() == NEUTRAL) ix += slice * s_maxSums;
+  return ix;
+}
+
+// Resize the sums vector according to format
+
+void CmxEnergySubBlock::resize()
+{
+  if (m_sumsData.empty()) {
+    int size = s_maxSums;
+    if (format() == NEUTRAL) size *= timeslices();
+    m_sumsData.resize(size);
+  }
+}
+
+// Pack neutral data
+
+bool CmxEnergySubBlock::packNeutral()
+{
+  resize();
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    for (int pin = 0; pin < s_maxJems; ++pin) {
+      // JEM energy sums (jem == pin); parity errors
+      packerNeutral(pin, energy(slice, pin, ENERGY_EX), s_jemSumBits);
+      packerNeutral(pin, 0, s_jemPaddingBits);
+      packerNeutral(pin, (error(slice, pin, ENERGY_EX)>>1), 1);
+      packerNeutral(pin, energy(slice, pin, ENERGY_EY), s_jemSumBits);
+      packerNeutral(pin, 0, s_jemPaddingBits);
+      packerNeutral(pin, (error(slice, pin, ENERGY_EY)>>1), 1);
+      packerNeutral(pin, energy(slice, pin, ENERGY_ET), s_jemSumBits);
+      packerNeutral(pin, 0, s_jemPaddingBits);
+      packerNeutral(pin, (error(slice, pin, ENERGY_ET)>>1), 1);
+      packerNeutral(pin, 0, s_jemSumBits);
+      packerNeutral(pin, 0, s_jemPaddingBits);
+      packerNeutral(pin, 0, 1);
+    }
+    // Remote Ex, Ey, Et
+    int pin = s_maxJems;
+    packerNeutral(pin, energy(slice, REMOTE, STANDARD, ENERGY_EX),
+                                                           s_sumBitsExEy);
+    packerNeutral(pin, (error(slice, REMOTE, STANDARD, ENERGY_EX)>>1), 1);
+    packerNeutral(pin, energy(slice, REMOTE, RESTRICTED_WEIGHTED, ENERGY_EX),
+                                                           s_sumBitsExEy);
+    packerNeutral(pin, (error(slice, REMOTE, RESTRICTED_WEIGHTED,
+                                                       ENERGY_EX)>>1), 1);
+    packerNeutral(pin, (error(slice, REMOTE, STANDARD, ENERGY_EX)&0x1), 1); // Seems inconsistent with SLink?
+    packerNeutral(pin, energy(slice, REMOTE, STANDARD, ENERGY_EY),
+                                                           s_sumBitsExEy);
+    packerNeutral(pin, (error(slice, REMOTE, STANDARD, ENERGY_EY)>>1), 1);
+    packerNeutral(pin, energy(slice, REMOTE, RESTRICTED_WEIGHTED, ENERGY_EY),
+                                                           s_sumBitsExEy);
+    packerNeutral(pin, (error(slice, REMOTE, RESTRICTED_WEIGHTED,
+                                                       ENERGY_EY)>>1), 1);
+    packerNeutral(pin, (error(slice, REMOTE, STANDARD, ENERGY_EY)&0x1), 1);
+    packerNeutral(pin, energy(slice, REMOTE, STANDARD, ENERGY_ET),
+                                                           s_sumBitsEtCrate);
+    packerNeutral(pin, 0, 1);
+    packerNeutral(pin, energy(slice, REMOTE, RESTRICTED_WEIGHTED, ENERGY_ET),
+                                                           s_sumBitsEtCrate);
+    packerNeutral(pin, (error(slice, REMOTE, STANDARD, ENERGY_ET)&0x1), 1);
+    // Local Ex, Ey, Et
+    ++pin;
+    packerNeutral(pin, energy(slice, LOCAL, STANDARD, ENERGY_EX),
+                                                           s_sumBitsExEy);
+    packerNeutral(pin, 0, 1);
+    packerNeutral(pin, energy(slice, LOCAL, RESTRICTED_WEIGHTED, ENERGY_EX),
+                                                           s_sumBitsExEy);
+    packerNeutral(pin, 0, 1);
+    packerNeutral(pin, (error(slice, LOCAL, STANDARD, ENERGY_EX)&0x1), 1);
+    packerNeutral(pin, energy(slice, LOCAL, STANDARD, ENERGY_EY),
+                                                           s_sumBitsExEy);
+    packerNeutral(pin, 0, 1);
+    packerNeutral(pin, energy(slice, LOCAL, RESTRICTED_WEIGHTED, ENERGY_EY),
+                                                           s_sumBitsExEy);
+    packerNeutral(pin, 0, 1);
+    packerNeutral(pin, (error(slice, LOCAL, STANDARD, ENERGY_EY)&0x1), 1);
+    packerNeutral(pin, energy(slice, LOCAL, STANDARD, ENERGY_ET),
+                                                           s_sumBitsEtCrate);
+    packerNeutral(pin, 0, 1);
+    packerNeutral(pin, energy(slice, LOCAL, RESTRICTED_WEIGHTED, ENERGY_ET),
+                                                           s_sumBitsEtCrate);
+    packerNeutral(pin, (error(slice, LOCAL, STANDARD, ENERGY_ET)&0x1), 1);
+    // Total Ex, Ey, Et
+    ++pin;
+    packerNeutral(pin, energy(slice, TOTAL, STANDARD, ENERGY_EX),
+                                                           s_sumBitsExEy);
+    packerNeutral(pin, 0, 1);
+    packerNeutral(pin, energy(slice, TOTAL, RESTRICTED_WEIGHTED, ENERGY_EX),
+                                                           s_sumBitsExEy);
+    packerNeutral(pin, 0, 1);
+    packerNeutral(pin, (error(slice, TOTAL, STANDARD, ENERGY_EX)&0x1), 1);
+    packerNeutral(pin, energy(slice, TOTAL, STANDARD, ENERGY_EY),
+                                                           s_sumBitsExEy);
+    packerNeutral(pin, 0, 1);
+    packerNeutral(pin, energy(slice, TOTAL, RESTRICTED_WEIGHTED, ENERGY_EY),
+                                                           s_sumBitsExEy);
+    packerNeutral(pin, (error(slice, TOTAL, STANDARD, ENERGY_ET)&0x1), 1);
+    packerNeutral(pin, (error(slice, TOTAL, STANDARD, ENERGY_EY)&0x1), 1);
+    packerNeutral(pin, energy(slice, TOTAL, STANDARD, ENERGY_ET),
+                                                           s_sumBitsEtSys);
+    packerNeutral(pin, energy(slice, TOTAL, RESTRICTED_WEIGHTED, ENERGY_ET),
+                                                           s_sumBitsEtSys);
+    // Bunchcrossing number, Fifo overflow
+    ++pin;
+    packerNeutral(pin, bunchCrossing(), s_bunchCrossingBits);
+    packerNeutral(pin, daqOverflow(), 1);
+    // Et hit maps
+    packerNeutral(pin, hits(slice, SUM_ET, STANDARD), s_etHitMapsBits);
+    packerNeutral(pin, hits(slice, MISSING_ET, STANDARD), s_etHitMapsBits);
+    packerNeutral(pin, hits(slice, MISSING_ET_SIG, STANDARD), s_etHitMapsBits);
+    packerNeutral(pin, hits(slice, SUM_ET, RESTRICTED_WEIGHTED),
+                                                          s_etHitMapsBits);
+    packerNeutral(pin, hits(slice, MISSING_ET, RESTRICTED_WEIGHTED),
+                                                          s_etHitMapsBits);
+    packerNeutral(pin, 0, s_paddingBits);
+    // G-Link parity errors
+    for (int p = 0; p <= pin; ++p) packerNeutralParity(p);
+  }
+  return true;
+}
+
+// Pack uncompressed data
+
+bool CmxEnergySubBlock::packUncompressed()
+{
+  std::vector<uint32_t>::const_iterator pos;
+  for (pos = m_sumsData.begin(); pos != m_sumsData.end(); ++pos) {
+    if (*pos) packer(*pos, s_wordLength);
+  }
+  packerFlush();
+  return true;
+}
+
+// Unpack neutral data
+
+bool CmxEnergySubBlock::unpackNeutral()
+{
+  resize();
+  int bunchCrossing = 0;            // BunchCrossing number
+  int overflow = 0;                 // FIFO overflow
+  int parity = 0;                   // GLink parity
+  unsigned int en[MAX_ENERGY_TYPE]; // Energies standard
+  unsigned int rn[MAX_ENERGY_TYPE]; // Energies restricted/weighted
+  int er[MAX_ENERGY_TYPE];          // Errors standard (bit 0 overflow, bit 1 parity)
+  int rr[MAX_ENERGY_TYPE];          // Errors restricted/weighted
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    for (int pin = 0; pin < s_maxJems; ++pin) {
+      // JEM energy sums (jem == pin); parity errors
+      for (int eType = 0; eType < MAX_ENERGY_TYPE; ++eType) {
+        en[eType] = unpackerNeutral(pin, s_jemSumBits);
+        unpackerNeutral(pin, s_jemPaddingBits);
+        er[eType] = unpackerNeutral(pin, 1) << 1;
+      }
+      unpackerNeutral(pin, s_jemSumBits);
+      unpackerNeutral(pin, s_jemPaddingBits);
+      unpackerNeutral(pin, 1);
+      setSubsums(slice, pin, en[ENERGY_EX], en[ENERGY_EY], en[ENERGY_ET],
+                             er[ENERGY_EX], er[ENERGY_EY], er[ENERGY_ET]);
+    }
+    // Remote Ex, Ey, Et, parity, overflow
+    int pin = s_maxJems;
+    en[ENERGY_EX]  = unpackerNeutral(pin, s_sumBitsExEy);
+    er[ENERGY_EX]  = unpackerNeutral(pin, 1) << 1;
+    er[ENERGY_ET]  = er[ENERGY_EX];
+    rn[ENERGY_EX]  = unpackerNeutral(pin, s_sumBitsExEy);
+    rr[ENERGY_EX]  = unpackerNeutral(pin, 1) << 1;
+    rr[ENERGY_ET]  = rr[ENERGY_EX];
+    er[ENERGY_EX] |= unpackerNeutral(pin, 1);                 // Or is it both?
+    en[ENERGY_EY]  = unpackerNeutral(pin, s_sumBitsExEy);
+    er[ENERGY_EY]  = unpackerNeutral(pin, 1) << 1;
+    er[ENERGY_ET] |= er[ENERGY_EY];
+    rn[ENERGY_EY]  = unpackerNeutral(pin, s_sumBitsExEy);
+    rr[ENERGY_EY]  = unpackerNeutral(pin, 1) << 1;
+    rr[ENERGY_ET] |= rr[ENERGY_EY];
+    er[ENERGY_EY] |= unpackerNeutral(pin, 1);
+    en[ENERGY_ET]  = unpackerNeutral(pin, s_sumBitsEtCrate);
+    unpackerNeutral(pin, 1);
+    rn[ENERGY_ET]  = unpackerNeutral(pin, s_sumBitsEtCrate);
+    er[ENERGY_ET] |= unpackerNeutral(pin, 1);
+    setSubsums(slice, REMOTE, STANDARD,
+               en[ENERGY_EX], en[ENERGY_EY], en[ENERGY_ET],
+	       er[ENERGY_EX], er[ENERGY_EY], er[ENERGY_ET]);
+    setSubsums(slice, REMOTE, RESTRICTED_WEIGHTED,
+               rn[ENERGY_EX], rn[ENERGY_EY], rn[ENERGY_ET],
+	       rr[ENERGY_EX], rr[ENERGY_EY], rr[ENERGY_ET]);
+    // Local Ex, Ey, Et, overflow
+    ++pin;
+    en[ENERGY_EX] = unpackerNeutral(pin, s_sumBitsExEy);
+    unpackerNeutral(pin, 1);
+    rn[ENERGY_EX] = unpackerNeutral(pin, s_sumBitsExEy);
+    unpackerNeutral(pin, 1);
+    er[ENERGY_EX] = unpackerNeutral(pin, 1);                 // Or is it both?
+    rr[ENERGY_EX] = 0;
+    en[ENERGY_EY] = unpackerNeutral(pin, s_sumBitsExEy);
+    unpackerNeutral(pin, 1);
+    rn[ENERGY_EY] = unpackerNeutral(pin, s_sumBitsExEy);
+    unpackerNeutral(pin, 1);
+    er[ENERGY_EY] = unpackerNeutral(pin, 1);
+    rr[ENERGY_EY] = 0;
+    en[ENERGY_ET] = unpackerNeutral(pin, s_sumBitsEtCrate);
+    unpackerNeutral(pin, 1);
+    rn[ENERGY_ET] = unpackerNeutral(pin, s_sumBitsEtCrate);
+    er[ENERGY_ET] = unpackerNeutral(pin, 1);
+    rr[ENERGY_ET] = 0;
+    setSubsums(slice, LOCAL, STANDARD,
+               en[ENERGY_EX], en[ENERGY_EY], en[ENERGY_ET],
+	       er[ENERGY_EX], er[ENERGY_EY], er[ENERGY_ET]);
+    setSubsums(slice, LOCAL, RESTRICTED_WEIGHTED,
+               rn[ENERGY_EX], rn[ENERGY_EY], rn[ENERGY_ET],
+	       rr[ENERGY_EX], rr[ENERGY_EY], rr[ENERGY_ET]);
+    // Total Ex, Ey, Et, overflow
+    ++pin;
+    en[ENERGY_EX] = unpackerNeutral(pin, s_sumBitsExEy);
+    unpackerNeutral(pin, 1);
+    rn[ENERGY_EX] = unpackerNeutral(pin, s_sumBitsExEy);
+    unpackerNeutral(pin, 1);
+    er[ENERGY_EX] = unpackerNeutral(pin, 1);                 // Or is it both?
+    rr[ENERGY_EX] = 0;
+    en[ENERGY_EY] = unpackerNeutral(pin, s_sumBitsExEy);
+    unpackerNeutral(pin, 1);
+    rn[ENERGY_EY] = unpackerNeutral(pin, s_sumBitsExEy);
+    er[ENERGY_ET] = unpackerNeutral(pin, 1);
+    rr[ENERGY_ET] = 0;
+    er[ENERGY_EY] = unpackerNeutral(pin, 1);
+    rr[ENERGY_EY] = 0;
+    en[ENERGY_ET] = unpackerNeutral(pin, s_sumBitsEtSys);
+    rn[ENERGY_ET] = unpackerNeutral(pin, s_sumBitsEtSys);
+    setSubsums(slice, TOTAL, STANDARD,
+               en[ENERGY_EX], en[ENERGY_EY], en[ENERGY_ET],
+	       er[ENERGY_EX], er[ENERGY_EY], er[ENERGY_ET]);
+    setSubsums(slice, TOTAL, RESTRICTED_WEIGHTED,
+               rn[ENERGY_EX], rn[ENERGY_EY], rn[ENERGY_ET],
+	       rr[ENERGY_EX], rr[ENERGY_EY], rr[ENERGY_ET]);
+    // Bunchcrossing number, Fifo overflow
+    ++pin;
+    bunchCrossing = unpackerNeutral(pin, s_bunchCrossingBits);
+    overflow = unpackerNeutral(pin, 1);
+    // Et hit maps
+    setEtHits(slice, SUM_ET, STANDARD,
+              unpackerNeutral(pin, s_etHitMapsBits));
+    setEtHits(slice, MISSING_ET, STANDARD,
+              unpackerNeutral(pin, s_etHitMapsBits));
+    setEtHits(slice, MISSING_ET_SIG, STANDARD,
+              unpackerNeutral(pin, s_etHitMapsBits));
+    setEtHits(slice, SUM_ET, RESTRICTED_WEIGHTED,
+              unpackerNeutral(pin, s_etHitMapsBits));
+    setEtHits(slice, MISSING_ET, RESTRICTED_WEIGHTED,
+              unpackerNeutral(pin, s_etHitMapsBits));
+    unpackerNeutral(pin, s_paddingBits);
+    // G-Link parity errors
+    for (int p = 0; p <= pin; ++p) parity |= unpackerNeutralParityError(p);
+  }
+  setBunchCrossing(bunchCrossing);
+  setDaqOverflow(overflow);
+  setGlinkParity(parity);
+
+  const bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  return rc;
+}
+
+// Unpack uncompressed data
+
+bool CmxEnergySubBlock::unpackUncompressed()
+{
+  resize();
+  unpackerInit();
+  bool error = false;
+  uint32_t word = unpacker(s_wordLength);
+  while (unpackerSuccess()) {
+    if (word) {
+      const int wordId = (word >> s_wordIdBit) & s_wordIdMask;
+      if (wordId == MODULE_ID) {
+        const int jem   = (word >> s_jemBit)           & s_jemMask;
+        const int eType = (word >> s_energyTypeJemBit) & s_energyTypeMask;
+        const int pos = 3*jem + eType;
+        if (eType < MAX_ENERGY_TYPE && m_sumsData[pos] == 0) {
+          m_sumsData[pos] = word;
+        } else error = true;
+      } else if (wordId == CRATE_SYSTEM_ID) {
+        const int source = (word >> s_sourceBit)     & s_sourceMask;
+        const int sType  = (word >> s_sumTypeBit)    & s_sumTypeMask;
+        const int eType  = (word >> s_energyTypeBit) & s_energyTypeMask;
+        const int pos = 3*(s_maxJems + 2*source + sType) + eType;
+        if (source < MAX_SOURCE_TYPE && eType < MAX_ENERGY_TYPE
+                                     && m_sumsData[pos] == 0) {
+          m_sumsData[pos] = word;
+        } else error = true;
+      } else error = true;
+      if (error) {
+        setUnpackErrorCode(UNPACK_SOURCE_ID);
+        return false;
+      }
+    }
+    word = unpacker(s_wordLength);
+  }
+  return true;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxEnergySubBlock.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxEnergySubBlock.h
new file mode 100755
index 0000000000000000000000000000000000000000..7766d869d6067a67912a375947764460a48a4431
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxEnergySubBlock.h
@@ -0,0 +1,121 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CMXENERGYSUBBLOCK_H
+#define TRIGT1CALOBYTESTREAM_CMXENERGYSUBBLOCK_H
+
+#include <stdint.h>
+#include <vector>
+
+#include "CmxSubBlock.h"
+
+namespace LVL1BS {
+
+/** Sub-Block class for CMX-Energy data post LS1.
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version X.xxx                                          //<<== CHECK
+ *
+ *  @author Peter Faulkner
+ */
+
+class CmxEnergySubBlock : public CmxSubBlock {
+
+ public:
+   enum WordIdType { MODULE_ID, CRATE_SYSTEM_ID };
+   enum EnergyType { ENERGY_EX, ENERGY_EY, ENERGY_ET, MAX_ENERGY_TYPE };
+   enum HitsType   { MISSING_ET_SIG, MISSING_ET, SUM_ET, MAX_HITS_TYPE };
+   enum SourceType { REMOTE, LOCAL, TOTAL, MAX_SOURCE_TYPE };
+   enum SumType    { STANDARD, RESTRICTED_WEIGHTED, MAX_SUM_TYPE };
+
+   CmxEnergySubBlock();
+   ~CmxEnergySubBlock();
+
+   /// Clear all data
+   void clear();
+
+   /// Return energy subsum for given JEM and energy type
+   unsigned int energy(int slice, int jem, EnergyType eType) const;
+   /// Return energy subsum error for given JEM and energy type
+   int error(int slice, int jem, EnergyType eType) const;
+   /// Return energy subsum for given source, sum type and energy type
+   unsigned int energy(int slice, SourceType source, SumType sType,
+                                  EnergyType eType) const;
+   /// Return energy subsum error for given source, sum type and energy type
+   int error(int slice, SourceType source, SumType sType,
+                                           EnergyType eType) const;
+   /// Return hits map for given hits type and sum type
+   unsigned int hits(int slice, HitsType hType, SumType sType) const;
+
+   /// Store energy subsums and errors for given JEM
+   void setSubsums(int slice, int jem,
+                   unsigned int ex, unsigned int ey, unsigned int et,
+                   int exError, int eyError, int etError);
+   /// Store energy subsums and errors for given source and sum type
+   void setSubsums(int slice, SourceType source, SumType sType,
+                   unsigned int ex, unsigned int ey, unsigned int et,
+                   int exError, int eyError, int etError);
+   /// Store hits map for given hits type and sum type
+   void setEtHits(int slice, HitsType hType, SumType sType, unsigned int map);
+
+   /// Pack data
+   bool pack();
+   /// Unpack data
+   bool unpack();
+
+ private:
+   /// Data word length
+   static const int      s_wordLength       = 32;
+   //  Energy subsums bit positions and masks
+   static const int      s_overflowBit      = 15;
+   static const int      s_errorBit         = 16;
+   static const int      s_etHitsBit        = 16;
+   static const int      s_energyTypeJemBit = 23;
+   static const int      s_jemBit           = 25;
+   static const int      s_energyTypeBit    = 24;
+   static const int      s_sumTypeBit       = 26;
+   static const int      s_sourceBit        = 27;
+   static const int      s_wordIdBit        = 29;
+   static const int      s_maxJems          = 16;
+   static const int      s_maxSums          = 66;
+   static const uint32_t s_energyJemMask    = 0x3fff;
+   static const uint32_t s_energySumMask    = 0x7fff;
+   static const uint32_t s_errorMask        = 0x1;
+   static const uint32_t s_overflowMask     = 0x1;
+   static const uint32_t s_etHitsMask       = 0xff;
+   static const uint32_t s_jemMask          = 0xf;
+   static const uint32_t s_energyTypeMask   = 0x3;
+   static const uint32_t s_sumTypeMask      = 0x1;
+   static const uint32_t s_sourceMask       = 0x3;
+   static const uint32_t s_wordIdMask       = 0x7;
+   //  Neutral format
+   static const int      s_jemSumBits        = 14;
+   static const int      s_jemPaddingBits    = 9;
+   static const int      s_sumBitsEtCrate    = 14;
+   static const int      s_sumBitsEtSys      = 15;
+   static const int      s_sumBitsExEy       = 15;
+   static const int      s_bunchCrossingBits = 12;
+   static const int      s_etHitMapsBits     = 8;
+   static const int      s_paddingBits       = 43;
+
+   int  index(int slice, int pos) const;
+   void resize();
+
+   /// Pack neutral data
+   bool packNeutral();
+   /// Pack uncompressed data
+   bool packUncompressed();
+   /// Unpack neutral data
+   bool unpackNeutral();
+   /// Unpack uncompressed data
+   bool unpackUncompressed();
+
+   /// Energy subsums data
+   std::vector<uint32_t> m_sumsData;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxJetSubBlock.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxJetSubBlock.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..d80afa8e1e932e2213070355b95dc819c419bc2c
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxJetSubBlock.cxx
@@ -0,0 +1,630 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "CmxJetSubBlock.h"
+
+namespace LVL1BS {
+
+// Constant definitions
+
+const int      CmxJetSubBlock::s_wordLength;
+
+const int      CmxJetSubBlock::s_tobEnergyLgBit;
+const int      CmxJetSubBlock::s_tobEnergySmBit;
+const int      CmxJetSubBlock::s_tobErrorBit;
+const int      CmxJetSubBlock::s_tobCoordBit;
+const int      CmxJetSubBlock::s_tobFrameBit;
+const int      CmxJetSubBlock::s_tobJemBit;
+const int      CmxJetSubBlock::s_tobWordId;
+const uint32_t CmxJetSubBlock::s_tobEnergyLgMask;
+const uint32_t CmxJetSubBlock::s_tobEnergySmMask;
+const uint32_t CmxJetSubBlock::s_tobErrorMask;
+const uint32_t CmxJetSubBlock::s_tobCoordMask;
+const uint32_t CmxJetSubBlock::s_tobFrameMask;
+const uint32_t CmxJetSubBlock::s_tobJemMask;
+
+const int      CmxJetSubBlock::s_threshBit;
+const int      CmxJetSubBlock::s_threshErrorBit;
+const int      CmxJetSubBlock::s_hlFlagBit;
+const int      CmxJetSubBlock::s_sourceIdBit;
+const int      CmxJetSubBlock::s_dataWordIdBit;
+const int      CmxJetSubBlock::s_threshWordId;
+const uint32_t CmxJetSubBlock::s_threshMainMask;
+const uint32_t CmxJetSubBlock::s_threshFwdLMask;
+const uint32_t CmxJetSubBlock::s_threshFwdHMask;
+const uint32_t CmxJetSubBlock::s_errorMask;
+const uint32_t CmxJetSubBlock::s_topoCheckMask;
+const uint32_t CmxJetSubBlock::s_topoMapMask;
+const uint32_t CmxJetSubBlock::s_topoCountsMask;
+const uint32_t CmxJetSubBlock::s_hlFlagMask;
+const uint32_t CmxJetSubBlock::s_sourceIdMask;
+const uint32_t CmxJetSubBlock::s_dataWordIdMask;
+
+const int      CmxJetSubBlock::s_presenceBits;
+const int      CmxJetSubBlock::s_coordBits;
+const int      CmxJetSubBlock::s_energyLgBits;
+//const int      CmxJetSubBlock::s_energySmBits[8];
+const int      CmxJetSubBlock::s_energySmBits[8] = {3,6,5,4,7,2,9,0};
+const int      CmxJetSubBlock::s_threshMainBits;
+const int      CmxJetSubBlock::s_threshFwdLBits;
+const int      CmxJetSubBlock::s_threshFwdHBits;
+const int      CmxJetSubBlock::s_parityErrorBits;
+const int      CmxJetSubBlock::s_roiOverflowBits;
+const int      CmxJetSubBlock::s_paddingBits;
+const int      CmxJetSubBlock::s_bunchCrossingBits;
+const int      CmxJetSubBlock::s_fifoOverflowBits;
+const int      CmxJetSubBlock::s_topoChecksumBits;
+const int      CmxJetSubBlock::s_topoMapBits;
+const int      CmxJetSubBlock::s_topoCountsBits;
+const int      CmxJetSubBlock::s_topoPaddingBits;
+const int      CmxJetSubBlock::s_glinkPins;
+const int      CmxJetSubBlock::s_modules;
+const int      CmxJetSubBlock::s_tobsPerModule;
+const int      CmxJetSubBlock::s_muxPhases;
+
+
+CmxJetSubBlock::CmxJetSubBlock()
+{
+}
+
+CmxJetSubBlock::~CmxJetSubBlock()
+{
+}
+
+// Clear all data
+
+void CmxJetSubBlock::clear()
+{
+  L1CaloSubBlock::clear();
+  m_tobData.clear();
+  m_hitsData.clear();
+  m_presenceMaps.clear();
+  m_parityBits.clear();
+}
+
+// Return presence map for given JEM
+
+unsigned int CmxJetSubBlock::presenceMap(const int slice,
+                                         const int jem) const
+{
+  unsigned int map = 0;
+  const unsigned int ix = mapIndex(slice, jem);
+  if (ix < m_presenceMaps.size()) map = m_presenceMaps[ix];
+  return map;
+}
+
+// Return frame for given jem and tob
+
+int CmxJetSubBlock::frame(const int slice, const int jem, const int tob) const
+{
+  int fr = 0;
+  const unsigned int ix = tobIndex(slice, jem, tob);
+  if (ix < m_tobData.size()) {
+    fr = (m_tobData[ix] >> s_tobFrameBit) & s_tobFrameMask;
+  }
+  return fr;
+}
+
+// Return Local coordinate for given jem and tob
+
+int CmxJetSubBlock::localCoord(const int slice, const int jem, const int tob) const
+{
+  int coord = 0;
+  const unsigned int ix = tobIndex(slice, jem, tob);
+  if (ix < m_tobData.size()) {
+    coord = (m_tobData[ix] >> s_tobCoordBit) & s_tobCoordMask;
+  }
+  return coord;
+}
+
+// Return energy large window size for given jem and tob
+
+int CmxJetSubBlock::energyLarge(const int slice, const int jem, const int tob) const
+{
+  int et = 0;
+  const unsigned int ix = tobIndex(slice, jem, tob);
+  if (ix < m_tobData.size()) {
+    et = (m_tobData[ix] >> s_tobEnergyLgBit) & s_tobEnergyLgMask;
+  }
+  return et;
+}
+
+// Return energy small window size for given jem and tob
+
+int CmxJetSubBlock::energySmall(const int slice, const int jem, const int tob) const
+{
+  int et = 0;
+  const unsigned int ix = tobIndex(slice, jem, tob);
+  if (ix < m_tobData.size()) {
+    et = (m_tobData[ix] >> s_tobEnergySmBit) & s_tobEnergySmMask;
+  }
+  return et;
+}
+
+// Return error bits for given jem and tob
+
+int  CmxJetSubBlock::tobError(int slice, int jem, int tob) const
+{
+  int error = 0;
+  const unsigned int ix = tobIndex(slice, jem, tob);
+  if (ix < m_tobData.size()) {
+    error = (m_tobData[ix] >> s_tobErrorBit) & s_tobErrorMask;
+  }
+  return error;
+}
+
+// Return parity bits for given JEM
+
+int CmxJetSubBlock::parityBits(const int slice, const int jem) const
+{
+  int parity = 0;
+  const unsigned int ix = parIndex(slice, jem);
+  if (ix < m_parityBits.size()) parity = m_parityBits[ix];
+  return parity;
+}
+
+// Return hit/topo counts for given source ID and HL flag
+
+unsigned int CmxJetSubBlock::hits(const int slice, const int source,
+                                                   const int flag) const
+{
+  unsigned int hits = 0;
+  const unsigned int ix = hitIndex(slice, source, flag);
+  if (ix < m_hitsData.size()) {
+    uint32_t mask = s_threshMainMask;
+    if (source == REMOTE_FORWARD || source == LOCAL_FORWARD
+                                 || source == TOTAL_FORWARD) {
+      mask = (flag) ? s_threshFwdHMask : s_threshFwdLMask;
+    }
+    else if (source == TOPO_CHECKSUM)         mask = s_topoCheckMask;
+    else if (source == TOPO_OCCUPANCY_MAP)    mask = s_topoMapMask;
+    else if (source == TOPO_OCCUPANCY_COUNTS) mask = s_topoCountsMask;
+    hits = (m_hitsData[ix] >> s_threshBit) & mask;
+  }
+  return hits;
+}
+
+// Return hit error for given source ID and HL flag
+
+int CmxJetSubBlock::hitsError(const int slice, const int source,
+                                               const int flag) const
+{
+  int error = 0;
+  if (source == REMOTE_MAIN || source == LOCAL_MAIN || source == TOTAL_MAIN ||
+      source == REMOTE_FORWARD || source == LOCAL_FORWARD ||
+                                  source == TOTAL_FORWARD) {
+    const unsigned int ix = hitIndex(slice, source, flag);
+    if (ix < m_hitsData.size()) {
+      error = (m_hitsData[ix] >> s_threshErrorBit) & s_errorMask;
+    }
+  }
+  return error;
+}
+
+// Store presence map
+
+void CmxJetSubBlock::setPresenceMap(const int slice, const int jem,
+                                                     const unsigned int map)
+{
+  resize();
+  if (map) {
+    const unsigned int ix = mapIndex(slice, jem);
+    if (ix < m_presenceMaps.size()) m_presenceMaps[ix] = map;
+  }
+}
+
+// Store TOB (RoI) data for given JEM, frame, local coord
+
+void CmxJetSubBlock::setTob(const int slice, const int jem, const int frame,
+                            const int loc, const int energyLarge,
+			    const int energySmall, const int error)
+{
+  resize();
+  if (energyLarge || energySmall || error) {
+    uint32_t word = 0;
+    word |= (energyLarge & s_tobEnergyLgMask) << s_tobEnergyLgBit;
+    word |= (energySmall & s_tobEnergySmMask) << s_tobEnergySmBit;
+    word |= (error       & s_tobErrorMask)    << s_tobErrorBit;
+    word |= (loc         & s_tobCoordMask)    << s_tobCoordBit;
+    word |= (frame       & s_tobFrameMask)    << s_tobFrameBit;
+    word |= (jem         & s_tobJemMask)      << s_tobJemBit;
+    word |= (s_tobWordId)                     << s_dataWordIdBit;
+    // Order by frame == presence bit                                          // <<== CHECK
+    for (int tob = 0; tob < s_tobsPerModule; ++tob) {
+      const unsigned int ix = tobIndex(slice, jem, tob);
+      if (m_tobData[ix] == 0) {
+        m_tobData[ix] = word;
+	break;
+      } else {
+        const int frameOld = (m_tobData[ix]>>s_tobFrameBit)&s_tobFrameMask;
+	if (frame < frameOld) {
+          for (int i = s_tobsPerModule-tob-1; i > 0; --i) {
+	    m_tobData[ix + i] = m_tobData[ix + i - 1];
+          }
+	  m_tobData[ix] = word;
+	  break;
+        }
+      }
+    }
+  }
+}
+
+// Store parity bits for neutral format
+
+void CmxJetSubBlock::setParityBits(const int slice, const int jem,
+                                                    const int parity)
+{
+  resize();
+  if (parity) {
+    const unsigned int ix = parIndex(slice, jem);
+    if (ix < m_parityBits.size()) m_parityBits[ix] = parity;
+  }
+}
+
+// Store hit counts for given source ID and HL flag
+
+void CmxJetSubBlock::setHits(const int slice, const int source, const int flag,
+                             const unsigned int hits, const int error)
+{
+  resize();
+  const unsigned int ix = hitIndex(slice, source, flag);
+  if (ix < m_hitsData.size() && (hits || error)) {
+    uint32_t word = m_hitsData[ix];
+    uint32_t mask = s_threshMainMask;
+    if (source == REMOTE_FORWARD || source == LOCAL_FORWARD ||
+                                    source == TOTAL_FORWARD) {
+      mask = (flag) ? s_threshFwdHMask : s_threshFwdLMask;
+    }
+    else if (source == TOPO_CHECKSUM)         mask = s_topoCheckMask;
+    else if (source == TOPO_OCCUPANCY_MAP)    mask = s_topoMapMask;
+    else if (source == TOPO_OCCUPANCY_COUNTS) mask = s_topoCountsMask;
+    word |= (hits & mask)             << s_threshBit;
+    word |= (error & s_errorMask)     << s_threshErrorBit;
+    word |= (flag & s_hlFlagMask)     << s_hlFlagBit;
+    word |= (source & s_sourceIdMask) << s_sourceIdBit;
+    word |= (s_threshWordId)          << s_dataWordIdBit;
+    m_hitsData[ix] = word;
+  }
+}
+
+// Packing/Unpacking routines
+
+bool CmxJetSubBlock::pack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 2:                                                  // <<== CHECK
+      switch (format()) {
+        case NEUTRAL:
+	  rc = packNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = packUncompressed();
+	  break;
+        default:
+	  break;
+      }
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
+
+bool CmxJetSubBlock::unpack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 2:                                                  // <<== CHECK
+      switch (format()) {
+        case NEUTRAL:
+	  rc = unpackNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = unpackUncompressed();
+	  break;
+        default:
+	  setUnpackErrorCode(UNPACK_FORMAT);
+	  break;
+      }
+      break;
+    default:
+      setUnpackErrorCode(UNPACK_VERSION);
+      break;
+  }
+  return rc;
+}
+
+// Return presence map index appropriate to format
+
+unsigned int CmxJetSubBlock::mapIndex(const int slice,
+                                      const int jem) const
+{
+  unsigned int ix = jem;
+  if (format() == NEUTRAL) ix += slice * s_modules;
+  return ix;
+}
+
+// Return parity bits index appropriate to format
+
+unsigned int CmxJetSubBlock::parIndex(const int slice,
+                                      const int jem) const
+{
+  unsigned int ix = jem;
+  if (format() == NEUTRAL) ix += slice * s_modules;
+  return ix;
+}
+
+// Return tob data index appropriate to format
+
+unsigned int CmxJetSubBlock::tobIndex(const int slice, const int jem,
+                                             const int tob) const
+{
+  unsigned int ix = jem * s_tobsPerModule + tob;
+  if (format() == NEUTRAL) ix += slice * s_modules * s_tobsPerModule;
+  return ix;
+}
+
+// Return hits data index appropriate to format
+
+unsigned int CmxJetSubBlock::hitIndex(const int slice, const int source,
+                                             const int flag) const
+{
+  unsigned int ix = (source<<1)|flag;
+  if (format() == NEUTRAL) ix += slice * 2 * MAX_SOURCE_ID;
+  return ix;
+}
+
+// Resize the data vectors according to format
+
+void CmxJetSubBlock::resize()
+{
+  if (m_tobData.empty()) {
+    int size1 = s_modules * s_tobsPerModule;
+    int size2 = 2 * MAX_SOURCE_ID;
+    int size3 = s_modules;
+    int size4 = s_modules;
+    if (format() == NEUTRAL) {
+      size1 *= timeslices();
+      size2 *= timeslices();
+      size3 *= timeslices();
+      size4 *= timeslices();
+    }
+    m_tobData.resize(size1);
+    m_hitsData.resize(size2);
+    m_presenceMaps.resize(size3);
+    m_parityBits.resize(size4);
+  }
+}
+
+// Pack neutral data
+
+bool CmxJetSubBlock::packNeutral()
+{
+  resize();
+  std::vector<int> locVec(s_tobsPerModule);
+  std::vector<int> energyLgVec(s_tobsPerModule);
+  std::vector<int> energySmVec(s_tobsPerModule);
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    for (int pin = 0; pin < s_glinkPins; ++pin) {
+      if (pin < s_modules) { // TOB data
+	const int jem = pin;
+        // Presence map
+        packerNeutral(pin, presenceMap(slice, jem), s_presenceBits);
+	// Get tob data for this jem
+	locVec.clear();
+	energyLgVec.clear();
+	energySmVec.clear();
+	for (int tob = 0; tob < s_tobsPerModule; ++tob) {
+	  locVec.push_back(localCoord(slice, jem, tob));
+	  energyLgVec.push_back(energyLarge(slice, jem, tob));
+	  energySmVec.push_back(energySmall(slice, jem, tob));
+	}
+	const int parity = parityBits(slice, jem);
+	// And pack
+        for (int tob = 0; tob < s_tobsPerModule; ++tob) {
+	  // Energy small window LSB
+	  const int nbitsL = s_energySmBits[2*tob];
+	  packerNeutral(pin, energySmVec[tob], nbitsL);
+          // Local coordinates
+	  packerNeutral(pin, locVec[tob], s_coordBits);
+	  // Energy large window
+	  packerNeutral(pin, energyLgVec[tob], s_energyLgBits);
+	  // Backplane parity error
+	  packerNeutral(pin, (parity>>tob), s_parityErrorBits);
+	  // Energy small window HSB
+	  const int nbitsH = s_energySmBits[2*tob+1];
+	  if (nbitsH > 0) {
+	    packerNeutral(pin, (energySmVec[tob]>>nbitsL), nbitsH);
+	  }
+        }
+      } else { // Hits and Topo data
+        if (pin < s_glinkPins-1) {
+          // Remote, local and total hits; parity error
+          const int source1 = pin - s_modules + REMOTE_MAIN;
+          const int source2 = pin - s_modules + REMOTE_FORWARD;
+          packerNeutral(pin, hits(slice, source1, 0), s_threshMainBits);
+          packerNeutral(pin, hitsError(slice, source1, 0), s_parityErrorBits);
+          packerNeutral(pin, hits(slice, source1, 1), s_threshMainBits);
+          packerNeutral(pin, (hitsError(slice, source1, 1)>>1), s_parityErrorBits);
+          packerNeutral(pin, hits(slice, source2, 0), s_threshFwdLBits);
+          packerNeutral(pin, hitsError(slice, source2, 0), s_parityErrorBits);
+          packerNeutral(pin, hits(slice, source2, 1), s_threshFwdHBits);
+          packerNeutral(pin, (hitsError(slice, source2, 1)>>1), s_parityErrorBits);
+          packerNeutral(pin, (hitsError(slice, source1, 0)>>2), s_roiOverflowBits);
+	  packerNeutral(pin, 0, s_paddingBits);
+        } else {
+          // Bunch crossing number, Fifo overflow and Topo data
+          packerNeutral(pin, bunchCrossing(), s_bunchCrossingBits);
+	  packerNeutral(pin, daqOverflow(), s_fifoOverflowBits);
+	  packerNeutral(pin, hits(slice, TOPO_CHECKSUM, 0), s_topoChecksumBits);
+	  packerNeutral(pin, hits(slice, TOPO_OCCUPANCY_MAP, 0), s_topoMapBits);
+	  packerNeutral(pin, hits(slice, TOPO_OCCUPANCY_COUNTS, 0), s_topoCountsBits);
+	  packerNeutral(pin, hits(slice, TOPO_OCCUPANCY_COUNTS, 1), s_topoCountsBits);
+	  packerNeutral(pin, 0, s_topoPaddingBits);
+        }
+      }
+      // G-Link parity
+      packerNeutralParity(pin);
+    }
+  }
+  return true;
+}
+
+// Pack uncompressed data
+
+bool CmxJetSubBlock::packUncompressed()
+{
+  std::vector<uint32_t>::const_iterator pos;
+  for (pos = m_tobData.begin(); pos != m_tobData.end(); ++pos) {
+    if (*pos) packer(*pos, s_wordLength);
+  }
+  for (pos = m_hitsData.begin(); pos != m_hitsData.end(); ++pos) {
+    if (*pos) packer(*pos, s_wordLength);
+  }
+  packerFlush();
+  return true;
+}
+
+// Unpack neutral data
+
+bool CmxJetSubBlock::unpackNeutral()
+{
+  resize();
+  int bunchCrossing = 0;
+  int fifoOverflow = 0;
+  int glinkParity = 0;
+  std::vector<int> locVec(s_tobsPerModule);
+  std::vector<int> energyLgVec(s_tobsPerModule);
+  std::vector<int> energySmVec(s_tobsPerModule);
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    for (int pin = 0; pin < s_glinkPins; ++pin) {
+      if (pin < s_modules) { // TOB data
+        // Presence map
+	const unsigned int map = unpackerNeutral(pin, s_presenceBits);
+	locVec.assign(s_tobsPerModule, 0);
+	energyLgVec.assign(s_tobsPerModule, 0);
+	energySmVec.assign(s_tobsPerModule, 0);
+	int parity = 0;
+	for (int tob = 0; tob < s_tobsPerModule; ++tob) {
+	  // Energy small window LSB
+	  const int nbitsL = s_energySmBits[2*tob];
+	  energySmVec[tob] = unpackerNeutral(pin, nbitsL);
+	  // Local coordinates
+	  locVec[tob] = unpackerNeutral(pin, s_coordBits);
+	  // Energy large window
+	  energyLgVec[tob] = unpackerNeutral(pin, s_energyLgBits);
+	  // Backplane parity error
+	  parity |= (unpackerNeutral(pin, s_parityErrorBits) << tob);
+	  // Energy small window HSB
+	  const int nbitsH = s_energySmBits[2*tob+1];
+	  if (nbitsH > 0) {
+	    energySmVec[tob] += (unpackerNeutral(pin, nbitsH) << nbitsL);
+          }
+        }
+	const int error = (parity) ? 1 : 0;
+	int tob = 0;
+	const int jem = pin;
+	for (int frame = 0; frame < s_presenceBits && tob < s_tobsPerModule; ++frame) {  // <<== CHECK - assuming bit==frame
+	  if ((map>>frame)&1) {
+	    setTob(slice, jem, frame, locVec[tob], energyLgVec[tob],
+	                                           energySmVec[tob], error);
+	    ++tob;
+          }
+        }
+        setPresenceMap(slice, jem, map);
+	setParityBits(slice, jem, parity);
+      } else {  // Hits and Topo data
+        if (pin < s_glinkPins-1) {
+	  // Remote, local and total hits; parity error, RoI overflow
+          const int source1 = pin - s_modules + REMOTE_MAIN;
+          const int source2 = pin - s_modules + REMOTE_FORWARD;
+	  const unsigned int main0 = unpackerNeutral(pin, s_threshMainBits);
+	  int errorMain = unpackerNeutral(pin, s_parityErrorBits);
+	  const unsigned int main1 = unpackerNeutral(pin, s_threshMainBits);
+	  errorMain |= (unpackerNeutral(pin, s_parityErrorBits)<<1);
+	  const unsigned int fwd0 = unpackerNeutral(pin, s_threshFwdLBits);
+	  int errorFwd = unpackerNeutral(pin, s_parityErrorBits);
+	  const unsigned int fwd1 = unpackerNeutral(pin, s_threshFwdHBits);
+	  errorFwd |= (unpackerNeutral(pin, s_parityErrorBits)<<1);
+	  const int overflow = (unpackerNeutral(pin, s_roiOverflowBits)<<2);
+	  errorMain |= overflow;
+	  errorFwd  |= overflow;
+	  setHits(slice, source1, 0, main0, errorMain);
+	  setHits(slice, source1, 1, main1, errorMain);
+	  setHits(slice, source2, 0, fwd0,  errorFwd);
+	  setHits(slice, source2, 1, fwd1,  errorFwd);
+	  unpackerNeutral(pin, s_paddingBits);
+        } else {
+	  // Bunch crossing number, Fifo overflow and Topo data
+	  bunchCrossing = unpackerNeutral(pin, s_bunchCrossingBits);
+	  fifoOverflow |= unpackerNeutral(pin, s_fifoOverflowBits);
+	  unsigned int hits = unpackerNeutral(pin, s_topoChecksumBits);
+	  int error = 0;
+	  setHits(slice, TOPO_CHECKSUM, 0, hits, error);
+	  hits = unpackerNeutral(pin, s_topoMapBits);
+	  setHits(slice, TOPO_OCCUPANCY_MAP, 0, hits, error);
+	  hits = unpackerNeutral(pin, s_topoCountsBits);
+	  setHits(slice, TOPO_OCCUPANCY_COUNTS, 0, hits, error);
+	  hits = unpackerNeutral(pin, s_topoCountsBits);
+	  setHits(slice, TOPO_OCCUPANCY_COUNTS, 1, hits, error);
+	  unpackerNeutral(pin, s_topoPaddingBits);
+        }
+      }
+      // G-Link parity errors
+      glinkParity |= unpackerNeutralParityError(pin);
+    }
+  }
+  setBunchCrossing(bunchCrossing);
+  setDaqOverflow(fifoOverflow);
+  setGlinkParity(glinkParity);
+
+  const bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  return rc;
+}
+
+// Unpack uncompressed data
+
+bool CmxJetSubBlock::unpackUncompressed()
+{
+  resize();
+  const int maxHits = m_hitsData.size();
+  m_jemTobCount.assign(s_modules, 0);
+  unpackerInit();
+  uint32_t word = unpacker(s_wordLength);
+  while (unpackerSuccess()) {
+    const int id = dataWordId(word);
+    if (id == s_tobWordId) {  // TOB data
+      const int index = jem(word);
+      const int count = m_jemTobCount[index];
+      const int index2 = index*s_tobsPerModule + count;
+      if (count < s_tobsPerModule) {
+        m_tobData[index2] = word;
+        ++m_jemTobCount[index];
+      } else {
+        setUnpackErrorCode(UNPACK_EXCESS_TOBS);             // New code.  Check consequences
+	return false;
+      }
+    } else if (id == s_threshWordId) {  // Hits and Topo data
+      const int index = (sourceId(word)<<1) | hlFlag(word);
+      if (index < maxHits && m_hitsData[index] == 0) {
+        m_hitsData[index] = word;
+      } else {
+        setUnpackErrorCode(UNPACK_SOURCE_ID);
+	return false;
+      }
+    } else {
+      setUnpackErrorCode(UNPACK_DATA_ID);                // New code
+      return false;
+    }
+    word = unpacker(s_wordLength);
+  }
+  return true;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxJetSubBlock.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxJetSubBlock.h
new file mode 100755
index 0000000000000000000000000000000000000000..0dbfe58d9668cadbe61e99514e0dde824d2ab96f
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxJetSubBlock.h
@@ -0,0 +1,182 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CMXJETSUBBLOCK_H
+#define TRIGT1CALOBYTESTREAM_CMXJETSUBBLOCK_H
+
+#include <stdint.h>
+#include <vector>
+
+#include "CmxSubBlock.h"
+
+namespace LVL1BS {
+
+/** Sub-Block class for CMX-Jet data post LS1.
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version X.xxx                                           //<<== CHECK
+ *
+ *  @author Peter Faulkner
+ */
+
+class CmxJetSubBlock : public CmxSubBlock {
+
+ public:
+   enum SourceId { REMOTE_MAIN, LOCAL_MAIN, TOTAL_MAIN,
+                   REMOTE_FORWARD, LOCAL_FORWARD, TOTAL_FORWARD,
+		   TOPO_CHECKSUM, TOPO_OCCUPANCY_MAP, TOPO_OCCUPANCY_COUNTS,
+		   MAX_SOURCE_ID };
+
+   CmxJetSubBlock();
+   ~CmxJetSubBlock();
+
+   /// Clear all data
+   void clear();
+
+   /// Return presence map for given JEM
+   unsigned int presenceMap(int slice, int jem)       const;
+   /// Return frame for given jem and tob
+   int frame(int slice, int jem, int tob)             const;
+   /// Return Local coordinate for given jem and tob
+   int localCoord(int slice, int jem, int tob)        const;
+   /// Return energy large window size for given jem and tob
+   int energyLarge(int slice, int jem, int tob)       const;
+   /// Return energy small window size for given jem and tob
+   int energySmall(int slice, int jem, int tob)       const;
+   /// Return error bit for given jem and tob
+   int tobError(int slice, int jem, int tob)          const;
+   /// Return parity bits for given JEM
+   int parityBits(int slice, int jem)                 const;
+   /// Return hit/topo counts for given source ID and HL flag
+   unsigned int hits(int slice, int source, int flag) const;
+   /// Return hit error for given source ID and HL flag
+   int hitsError(int slice, int source, int flag)     const;
+
+   /// Store presence map
+   void setPresenceMap(int slice, int jem, unsigned int map);
+   /// Store TOB (RoI) data for given JEM, frame, local coord
+   void setTob(int slice, int jem, int frame, int loc,
+               int energyLarge, int energySmall, int error);
+   /// Store parity bits for neutral format
+   void setParityBits(int slice, int jem, int parity);
+   /// Store hit counts for given source ID and HL flag
+   void setHits(int slice, int source, int flag, unsigned int hits, int error);
+
+   /// Pack data
+   bool pack();
+   /// Unpack data
+   bool unpack();
+
+ private:
+   /// Data word length
+   static const int      s_wordLength      = 32;
+   //  TOB bit positions and masks
+   static const int      s_tobEnergyLgBit   = 0;
+   static const int      s_tobEnergySmBit   = 10;
+   static const int      s_tobErrorBit      = 19;
+   static const int      s_tobCoordBit      = 20;
+   static const int      s_tobFrameBit      = 22;
+   static const int      s_tobJemBit        = 25;
+   static const int      s_tobWordId        = 0;
+   static const uint32_t s_tobEnergyLgMask  = 0x3ff;
+   static const uint32_t s_tobEnergySmMask  = 0x1ff;
+   static const uint32_t s_tobErrorMask     = 0x1;
+   static const uint32_t s_tobCoordMask     = 0x3;
+   static const uint32_t s_tobFrameMask     = 0x7;
+   static const uint32_t s_tobJemMask       = 0xf;
+   //  Jet hit and topo counts bit positions and masks
+   static const int      s_threshBit        = 0;
+   static const int      s_threshErrorBit   = 16;
+   static const int      s_hlFlagBit        = 24;
+   static const int      s_sourceIdBit      = 25;
+   static const int      s_dataWordIdBit    = 29;
+   static const int      s_threshWordId     = 1;
+   static const uint32_t s_threshMainMask   = 0x7fff;
+   static const uint32_t s_threshFwdLMask   = 0xffff;
+   static const uint32_t s_threshFwdHMask   = 0x3fff;
+   static const uint32_t s_errorMask        = 0x7;    // Includes RoI overflow
+   static const uint32_t s_topoCheckMask    = 0xffff;
+   static const uint32_t s_topoMapMask      = 0xffff;
+   static const uint32_t s_topoCountsMask   = 0xffffff;
+   static const uint32_t s_hlFlagMask       = 0x1;
+   static const uint32_t s_sourceIdMask     = 0xf;
+   static const uint32_t s_dataWordIdMask   = 0x7;
+   //  Neutral format
+   static const int      s_presenceBits      = 8;
+   static const int      s_coordBits         = 2;
+   static const int      s_energyLgBits      = 10;
+   //static const int      s_energySmBits[8]   = {3,6,5,4,7,2,9,0};
+   static const int      s_energySmBits[8];
+   static const int      s_threshMainBits    = 15;
+   static const int      s_threshFwdLBits    = 16;
+   static const int      s_threshFwdHBits    = 14;
+   static const int      s_parityErrorBits   = 1;
+   static const int      s_roiOverflowBits   = 1;
+   static const int      s_paddingBits       = 31;
+   static const int      s_bunchCrossingBits = 12;
+   static const int      s_fifoOverflowBits  = 1;
+   static const int      s_topoChecksumBits  = 16;
+   static const int      s_topoMapBits       = 16;
+   static const int      s_topoCountsBits    = 24;
+   static const int      s_topoPaddingBits   = 3;
+   static const int      s_glinkPins         = 20;
+   static const int      s_modules           = 16;
+   static const int      s_tobsPerModule     = 4;
+   static const int      s_muxPhases         = 4;
+
+   int dataWordId(uint32_t word) const;
+   int sourceId(uint32_t word)   const;
+   int jem(uint32_t word)        const;
+   int hlFlag(uint32_t word)     const;
+   unsigned int mapIndex(int slice, int jem)              const;
+   unsigned int parIndex(int slice, int jem)              const;
+   unsigned int tobIndex(int slice, int jem, int tob)     const;
+   unsigned int hitIndex(int slice, int source, int flag) const;
+   void resize();
+
+   /// Pack neutral data
+   bool packNeutral();
+   /// Pack uncompressed data
+   bool packUncompressed();
+   /// Unpack neutral data
+   bool unpackNeutral();
+   /// Unpack uncompressed data
+   bool unpackUncompressed();
+
+   /// TOB data
+   std::vector<uint32_t> m_tobData;
+   /// Hits and topo data
+   std::vector<uint32_t> m_hitsData;
+   /// Presence maps
+   std::vector<unsigned int> m_presenceMaps;
+   /// Parity data for neutral format
+   std::vector<int> m_parityBits;
+   /// JEM TOB count vector for unpacking
+   std::vector<int> m_jemTobCount;
+
+};
+
+inline int CmxJetSubBlock::dataWordId(const uint32_t word) const
+{
+  return (word >> s_dataWordIdBit) & s_dataWordIdMask;
+}
+
+inline int CmxJetSubBlock::sourceId(const uint32_t word) const
+{
+  return (word >> s_sourceIdBit) & s_sourceIdMask;
+}
+
+inline int CmxJetSubBlock::jem(const uint32_t word) const
+{
+  return (word >> s_tobJemBit) & s_tobJemMask;
+}
+
+inline int CmxJetSubBlock::hlFlag(const uint32_t word) const
+{
+  return (word >> s_hlFlagBit) & s_hlFlagMask;
+}
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxSubBlock.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxSubBlock.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..77a380ae3d1fbc62015f9c5a63aa80d7fd97a24b
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxSubBlock.cxx
@@ -0,0 +1,89 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "CmxSubBlock.h"
+
+namespace LVL1BS {
+
+// Static constant definitions
+
+const int      CmxSubBlock::s_wordIdVal;
+
+const int      CmxSubBlock::s_cmxSummingBit;
+const int      CmxSubBlock::s_cmxFirmwareBit;
+const int      CmxSubBlock::s_cmxPositionBit;
+const uint32_t CmxSubBlock::s_cmxSummingMask;
+const uint32_t CmxSubBlock::s_cmxFirmwareMask;
+const uint32_t CmxSubBlock::s_cmxPositionMask;
+
+const int      CmxSubBlock::s_glinkBitsPerSlice;
+
+CmxSubBlock::CmxSubBlock()
+{
+}
+
+CmxSubBlock::~CmxSubBlock()
+{
+}
+
+// Store CMX header
+
+void CmxSubBlock::setCmxHeader(const int version, const int format,
+                               const int slice, const int crate,
+                               const int summing, const int firmware,
+			       const int position, const int timeslices)
+{
+  int module = 0;
+  module |= (summing  & s_cmxSummingMask)  << s_cmxSummingBit;
+  module |= (firmware & s_cmxFirmwareMask) << s_cmxFirmwareBit;
+  module |= (position & s_cmxPositionMask) << s_cmxPositionBit;
+  setHeader(s_wordIdVal, version, format, slice, crate, module, 0,
+                                                           timeslices);
+}
+
+// Return number of timeslices
+
+int CmxSubBlock::timeslices() const
+{
+  int slices = slices1();
+  if (slices == 0 && format() == NEUTRAL) {
+    slices = dataWords() / s_glinkBitsPerSlice;
+  }
+  if (slices == 0) slices = 1;
+  return slices;
+}
+
+// Static function to determine CMX type
+
+CmxSubBlock::CmxFirmwareCode CmxSubBlock::cmxType(const uint32_t word)
+{
+  CmxFirmwareCode type;
+  const int module = L1CaloSubBlock::module(word);
+  const int code   = (module >> s_cmxFirmwareBit) & s_cmxFirmwareMask;
+  switch (code) {
+    case CMX_CP:
+      type = CMX_CP;
+      break;
+    case CMX_JET:
+      type = CMX_JET;
+      break;
+    case CMX_ENERGY:
+      type = CMX_ENERGY;
+      break;
+    default:
+      type = CMX_UNKNOWN;
+      break;
+  }
+  return type;
+}
+
+// Static function to determine if header word corresponds to CMX block
+
+bool CmxSubBlock::cmxBlock(const uint32_t word)
+{
+  return L1CaloSubBlock::wordId(word) == s_wordIdVal;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxSubBlock.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxSubBlock.h
new file mode 100755
index 0000000000000000000000000000000000000000..7a5924177d2e7b0915748a6e2f9b20d26047cd55
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CmxSubBlock.h
@@ -0,0 +1,80 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CMXSUBBLOCK_H
+#define TRIGT1CALOBYTESTREAM_CMXSUBBLOCK_H
+
+#include <stdint.h>
+
+#include "L1CaloSubBlock.h"
+
+namespace LVL1BS {
+
+/** Sub-Block class for CMX data post LS1.
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version X.xxx                                         <<== CHECK
+ *
+ *  @author Peter Faulkner
+ */
+
+class CmxSubBlock : public L1CaloSubBlock {
+
+ public:
+   enum CmxFirmwareCode { CMX_CP = 0, CMX_JET = 1, CMX_ENERGY = 2,        // <<== CHECK
+                          CMX_UNKNOWN = 3 };
+   enum CmxSummingCode  { CRATE = 0, SYSTEM = 1 };
+   enum CmxPositions    { LEFT = 0, RIGHT = 1 };
+
+   CmxSubBlock();
+   ~CmxSubBlock();
+
+   /// Store CMX header
+   void setCmxHeader(int version, int format, int slice, int crate,
+                     int summing, int firmware, int position, int timeslices);
+
+   //   Return CMX specific header data
+   int cmxSumming()  const;
+   int cmxFirmware() const;
+   int cmxPosition() const;
+   int timeslices()  const;
+
+   /// CMX differentiation (CMX_CP, CMX_JET, or CMX_ENERGY)
+   static CmxFirmwareCode cmxType(uint32_t word);
+   /// Determine if header word corresponds to CMX
+   static bool cmxBlock(uint32_t word);
+
+ private:
+   /// CMX header word ID
+   static const int      s_wordIdVal        = 0xe;
+   //  CMX fields packed in module field
+   static const int      s_cmxSummingBit    = 3;
+   static const int      s_cmxFirmwareBit   = 1;
+   static const int      s_cmxPositionBit   = 0;
+   static const uint32_t s_cmxSummingMask   = 0x1;
+   static const uint32_t s_cmxFirmwareMask  = 0x3;
+   static const uint32_t s_cmxPositionMask  = 0x1;
+   /// Needed for neutral format
+   static const int      s_glinkBitsPerSlice = 97;
+
+};
+
+inline int CmxSubBlock::cmxSumming() const
+{
+  return (module() >> s_cmxSummingBit) & s_cmxSummingMask;
+}
+
+inline int CmxSubBlock::cmxFirmware() const
+{
+  return (module() >> s_cmxFirmwareBit) & s_cmxFirmwareMask;
+}
+
+inline int CmxSubBlock::cmxPosition() const
+{
+  return (module() >> s_cmxPositionBit) & s_cmxPositionMask;
+}
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamCnv.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamCnv.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..cbf15e1a2ebfc810d0fc5520f8da1b3c01d3625d
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamCnv.cxx
@@ -0,0 +1,115 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IByteStreamEventAccess.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "DataModel/DataVector.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/IRegistry.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "TrigT1CaloEvent/CPBSCollection.h"
+
+#include "CpByteStreamCnv.h"
+#include "CpByteStreamTool.h"
+
+namespace LVL1BS {
+
+CpByteStreamCnv::CpByteStreamCnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("CpByteStreamCnv"),
+      m_tool("LVL1BS::CpByteStreamTool/CpByteStreamTool"),
+      m_ByteStreamEventAccess("ByteStreamCnvSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+CpByteStreamCnv::~CpByteStreamCnv()
+{
+}
+
+// CLID
+
+const CLID& CpByteStreamCnv::classID()
+{
+  return ClassID_traits<LVL1::CPBSCollection>::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode CpByteStreamCnv::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << " - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  //Get ByteStreamCnvSvc
+  sc = m_ByteStreamEventAccess.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_ByteStreamEventAccess << endreq;
+    return sc;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_ByteStreamEventAccess << endreq;
+  }
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return sc;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  return StatusCode::SUCCESS;
+}
+
+// createRep should create the bytestream from RDOs.
+
+StatusCode CpByteStreamCnv::createRep( DataObject* pObj,
+                                        IOpaqueAddress*& pAddr )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createRep() called" << endreq;
+
+  RawEventWrite* re = m_ByteStreamEventAccess->getRawEvent();
+
+  LVL1::CPBSCollection* cp = 0;
+  if( !SG::fromStorable( pObj, cp ) ) {
+    m_log << MSG::ERROR << " Cannot cast to CPBSCollection" << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = pObj->registry()->name();
+
+  ByteStreamAddress* addr = new ByteStreamAddress( classID(), nm, "" );
+
+  pAddr = addr;
+
+  // Convert to ByteStream
+  return m_tool->convert( cp, re );
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamCnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamCnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..361ded10ce81f4b1354783200b88cb74b770fec4
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamCnv.h
@@ -0,0 +1,76 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPBYTESTREAMCNV_H
+#define TRIGT1CALOBYTESTREAM_CPBYTESTREAMCNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IByteStreamEventAccess;
+class IOpaqueAddress;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class CpByteStreamTool;
+
+/** ByteStream converter for CP container
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpByteStreamCnv: public Converter {
+
+  friend class CnvFactory<CpByteStreamCnv>;
+
+protected:
+
+  CpByteStreamCnv(ISvcLocator* svcloc);
+
+public:
+
+  ~CpByteStreamCnv();
+
+  virtual StatusCode initialize();
+  /// Create ByteStream from Cp Container
+  virtual StatusCode createRep(DataObject* pObj, IOpaqueAddress*& pAddr);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::CpByteStreamTool> m_tool;
+
+  /// Service for writing bytestream
+  ServiceHandle<IByteStreamEventAccess> m_ByteStreamEventAccess;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamTool.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamTool.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..a91f60534a3a92b7633ccde83108bd69011fe98c
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamTool.cxx
@@ -0,0 +1,1158 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <numeric>
+#include <set>
+#include <utility>
+
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "ByteStreamCnvSvcBase/FullEventAssembler.h"
+
+#include "TrigT1CaloEvent/CMMCPHits.h"
+#include "TrigT1CaloEvent/CPMHits.h"
+#include "TrigT1CaloEvent/CPMTower.h"
+#include "TrigT1CaloEvent/CPBSCollection.h"
+#include "TrigT1CaloUtils/DataError.h"
+#include "TrigT1CaloUtils/TriggerTowerKey.h"
+#include "TrigT1CaloMappingToolInterfaces/IL1CaloMappingTool.h"
+
+#include "CmmCpSubBlock.h"
+#include "CmmSubBlock.h"
+#include "CpmSubBlock.h"
+#include "L1CaloErrorByteStreamTool.h"
+#include "L1CaloSrcIdMap.h"
+#include "L1CaloSubBlock.h"
+#include "L1CaloUserHeader.h"
+#include "ModifySlices.h"
+
+#include "CpByteStreamTool.h"
+
+namespace LVL1BS {
+
+// Interface ID
+
+static const InterfaceID IID_ICpByteStreamTool("CpByteStreamTool", 1, 1);
+
+const InterfaceID& CpByteStreamTool::interfaceID()
+{
+  return IID_ICpByteStreamTool;
+}
+
+// Constructor
+
+CpByteStreamTool::CpByteStreamTool(const std::string& type,
+                                   const std::string& name,
+				   const IInterface*  parent)
+   : AthAlgTool(type, name, parent),
+     m_cpmMaps("LVL1::CpmMappingTool/CpmMappingTool"),
+     m_errorTool("LVL1BS::L1CaloErrorByteStreamTool/L1CaloErrorByteStreamTool"),
+     m_channels(80), m_crates(4), m_modules(14),
+     m_coreOverlap(0), m_subDetector(eformat::TDAQ_CALO_CLUSTER_PROC_DAQ),
+     m_srcIdMap(0), m_towerKey(0), m_cpmSubBlock(0), m_cmmCpSubBlock(0),
+     m_rodStatus(0), m_fea(0)
+{
+  declareInterface<CpByteStreamTool>(this);
+
+  declareProperty("CpmMappingTool", m_cpmMaps,
+                  "Crate/Module/Channel to Eta/Phi/Layer mapping tool");
+
+  declareProperty("CrateOffsetHw",  m_crateOffsetHw  = 8,
+                  "Offset of CP crate numbers in bytestream");
+  declareProperty("CrateOffsetSw",  m_crateOffsetSw  = 0,
+                  "Offset of CP crate numbers in RDOs");
+
+  // Properties for reading bytestream only
+  declareProperty("ROBSourceIDs",       m_sourceIDs,
+                  "ROB fragment source identifiers");
+
+  // Properties for writing bytestream only
+  declareProperty("DataVersion",    m_version     = 1,
+                  "Format version number in sub-block header");
+  declareProperty("DataFormat",     m_dataFormat  = 1,
+                  "Format identifier (0-1) in sub-block header");
+  declareProperty("SlinksPerCrate", m_slinks      = 2,
+                  "The number of S-Links per crate");
+  declareProperty("SimulSlices",    m_dfltSlices  = 1,
+                  "The number of slices in the simulation");
+  declareProperty("ForceSlices",    m_forceSlices = 0,
+                  "If >0, the number of slices in bytestream");
+
+}
+
+// Destructor
+
+CpByteStreamTool::~CpByteStreamTool()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode CpByteStreamTool::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = m_cpmMaps.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_cpmMaps << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_cpmMaps << endreq;
+
+  sc = m_errorTool.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_errorTool << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_errorTool << endreq;
+
+  m_srcIdMap      = new L1CaloSrcIdMap();
+  m_towerKey      = new LVL1::TriggerTowerKey();
+  m_cpmSubBlock   = new CpmSubBlock();
+  m_cmmCpSubBlock = new CmmCpSubBlock();
+  m_rodStatus     = new std::vector<uint32_t>(2);
+  m_fea           = new FullEventAssembler<L1CaloSrcIdMap>();
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode CpByteStreamTool::finalize()
+{
+  delete m_fea;
+  delete m_rodStatus;
+  delete m_cmmCpSubBlock;
+  delete m_cpmSubBlock;
+  delete m_towerKey;
+  delete m_srcIdMap;
+  return StatusCode::SUCCESS;
+}
+
+// Conversion bytestream to CPM towers
+
+StatusCode CpByteStreamTool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::CPMTower>* const ttCollection)
+{
+  m_ttCollection = ttCollection;
+  m_ttMap.clear();
+  return convertBs(robFrags, CPM_TOWERS);
+}
+
+// Conversion bytestream to CPM hits
+
+StatusCode CpByteStreamTool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::CPMHits>* const hitCollection)
+{
+  m_hitCollection = hitCollection;
+  m_hitsMap.clear();
+  return convertBs(robFrags, CPM_HITS);
+}
+
+// Conversion bytestream to CMM-CP hits
+
+StatusCode CpByteStreamTool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::CMMCPHits>* const hitCollection)
+{
+  m_cmmHitCollection = hitCollection;
+  m_cmmHitsMap.clear();
+  return convertBs(robFrags, CMM_CP_HITS);
+}
+
+// Conversion of CP container to bytestream
+
+StatusCode CpByteStreamTool::convert(const LVL1::CPBSCollection* const cp,
+                                           RawEventWrite* const re)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Clear the event assembler
+
+  m_fea->clear();
+  const uint16_t minorVersion = m_srcIdMap->minorVersion();
+  m_fea->setRodMinorVersion(minorVersion);
+  m_rodStatusMap.clear();
+
+  // Pointer to ROD data vector
+
+  FullEventAssembler<L1CaloSrcIdMap>::RODDATA* theROD = 0;
+
+  // Set up the container maps
+
+  setupCpmTowerMap(cp->towers());
+  setupCpmHitsMap(cp->hits());
+  setupCmmCpHitsMap(cp->cmmHits());
+
+  // Loop over data
+
+  const bool neutralFormat   = m_dataFormat == L1CaloSubBlock::NEUTRAL;
+  const int  modulesPerSlink = m_modules / m_slinks;
+  int timeslices    = 1;
+  int trigCpm       = 0;
+  int timeslicesNew = 1;
+  int trigCpmNew    = 0;
+  for (int crate=0; crate < m_crates; ++crate) {
+    const int hwCrate = crate + m_crateOffsetHw;
+
+    // CPM modules are numbered 1 to m_modules
+    for (int module=1; module <= m_modules; ++module) {
+      const int mod = module - 1;
+
+      // Pack required number of modules per slink
+
+      if (mod%modulesPerSlink == 0) {
+	const int daqOrRoi = 0;
+	const int slink = (m_slinks == 2) ? 2*(mod/modulesPerSlink)
+	                                  : mod/modulesPerSlink;
+        if (debug) {
+          msg() << "Treating crate " << hwCrate
+                << " slink " << slink << endreq;
+        }
+	// Get number of CPM slices and triggered slice offset
+	// for this slink
+	if ( ! slinkSlices(crate, module, modulesPerSlink,
+	                                  timeslices, trigCpm)) {
+	  msg(MSG::ERROR) << "Inconsistent number of slices or "
+	                  << "triggered slice offsets in data for crate "
+	                  << hwCrate << " slink " << slink << endreq;
+	  return StatusCode::FAILURE;
+        }
+	timeslicesNew = (m_forceSlices) ? m_forceSlices : timeslices;
+	trigCpmNew    = ModifySlices::peak(trigCpm, timeslices, timeslicesNew);
+        if (debug) {
+	  msg() << "Data Version/Format: " << m_version
+	        << " " << m_dataFormat << endreq
+                << "Slices/offset: " << timeslices << " " << trigCpm;
+	  if (timeslices != timeslicesNew) {
+	    msg() << " modified to " << timeslicesNew << " " << trigCpmNew;
+          }
+	  msg() << endreq;
+        }
+        L1CaloUserHeader userHeader;
+        userHeader.setCpm(trigCpmNew);
+	const uint32_t rodIdCpm = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+	                                                        m_subDetector);
+	theROD = m_fea->getRodData(rodIdCpm);
+	theROD->push_back(userHeader.header());
+	m_rodStatusMap.insert(make_pair(rodIdCpm, m_rodStatus));
+      }
+      if (debug) msg() << "Module " << module << endreq;
+
+      // Create a sub-block for each slice (except Neutral format)
+
+      m_cpmBlocks.clear();
+      for (int slice = 0; slice < timeslicesNew; ++slice) {
+        CpmSubBlock* const subBlock = new CpmSubBlock();
+	subBlock->setCpmHeader(m_version, m_dataFormat, slice,
+	                       hwCrate, module, timeslicesNew);
+        m_cpmBlocks.push_back(subBlock);
+	if (neutralFormat) break;
+      }
+
+      // Find CPM towers corresponding to each eta/phi pair and fill
+      // sub-blocks
+
+      for (int chan=0; chan < m_channels; ++chan) {
+	double eta = 0.;
+	double phi = 0.;
+	int layer = 0;
+	if (m_cpmMaps->mapping(crate, module, chan, eta, phi, layer)) {
+          const unsigned int key = m_towerKey->ttKey(phi, eta);
+          const LVL1::CPMTower* const tt = findCpmTower(key);
+	  if (tt ) {
+	    std::vector<int> emData;
+	    std::vector<int> hadData;
+	    std::vector<int> emError;
+	    std::vector<int> hadError;
+	    ModifySlices::data(tt->emEnergyVec(),  emData,   timeslicesNew);
+	    ModifySlices::data(tt->hadEnergyVec(), hadData,  timeslicesNew);
+	    ModifySlices::data(tt->emErrorVec(),   emError,  timeslicesNew);
+	    ModifySlices::data(tt->hadErrorVec(),  hadError, timeslicesNew);
+            for (int slice = 0; slice < timeslicesNew; ++slice) {
+	      const LVL1::DataError emErrBits(emError[slice]);
+	      const LVL1::DataError hadErrBits(hadError[slice]);
+	      const int emErr  =
+	                      (emErrBits.get(LVL1::DataError::LinkDown) << 1) |
+	                       emErrBits.get(LVL1::DataError::Parity);
+	      const int hadErr =
+	                     (hadErrBits.get(LVL1::DataError::LinkDown) << 1) |
+	                      hadErrBits.get(LVL1::DataError::Parity);
+	      const int index  = ( neutralFormat ) ? 0 : slice;
+              CpmSubBlock* const subBlock = m_cpmBlocks[index];
+              subBlock->fillTowerData(slice, chan, emData[slice],
+	                              hadData[slice], emErr, hadErr);
+	    }
+          }
+        }
+      }
+
+      // Add CPM hits
+
+      const LVL1::CPMHits* const hits = findCpmHits(crate, module);
+      if (hits) {
+        std::vector<unsigned int> vec0;
+        std::vector<unsigned int> vec1;
+	ModifySlices::data(hits->HitsVec0(), vec0, timeslicesNew);
+	ModifySlices::data(hits->HitsVec1(), vec1, timeslicesNew);
+        for (int slice = 0; slice < timeslicesNew; ++slice) {
+	  const int index = ( neutralFormat ) ? 0 : slice;
+	  CpmSubBlock* const subBlock = m_cpmBlocks[index];
+	  subBlock->setHits(slice, vec0[slice], vec1[slice]);
+        }
+      }
+      
+      // Pack and write the sub-blocks
+
+      DataVector<CpmSubBlock>::const_iterator pos;
+      for (pos = m_cpmBlocks.begin(); pos != m_cpmBlocks.end(); ++pos) {
+        CpmSubBlock* const subBlock = *pos;
+	if ( !subBlock->pack()) {
+	  msg(MSG::ERROR) << "CPM sub-block packing failed" << endreq;
+	  return StatusCode::FAILURE;
+	}
+        if (debug) {
+          msg() << "CPM sub-block data words: "
+	        << subBlock->dataWords() << endreq;
+        }
+	subBlock->write(theROD);
+      }
+    }
+
+    // Append CMMs to last S-Link of the crate
+
+    // Create a sub-block for each slice (except Neutral format)
+
+    m_cmmHit0Blocks.clear();
+    m_cmmHit1Blocks.clear();
+    const int summing = (crate == m_crates - 1) ? CmmSubBlock::SYSTEM
+                                                : CmmSubBlock::CRATE;
+    for (int slice = 0; slice < timeslicesNew; ++slice) {
+      CmmCpSubBlock* const h0Block = new CmmCpSubBlock();
+      h0Block->setCmmHeader(m_version, m_dataFormat, slice, hwCrate,
+                            summing, CmmSubBlock::CMM_CP,
+			    CmmSubBlock::RIGHT, timeslicesNew);
+      m_cmmHit0Blocks.push_back(h0Block);
+      CmmCpSubBlock* const h1Block = new CmmCpSubBlock();
+      h1Block->setCmmHeader(m_version, m_dataFormat, slice, hwCrate,
+                            summing, CmmSubBlock::CMM_CP,
+			    CmmSubBlock::LEFT, timeslicesNew);
+      m_cmmHit1Blocks.push_back(h1Block);
+      if (neutralFormat) break;
+    }
+
+    // CMM-CP
+
+    const int maxDataID = LVL1::CMMCPHits::MAXID;
+    for (int dataID = 1; dataID < maxDataID; ++dataID) {
+      int source = dataID;
+      if (dataID > m_modules) {
+        if (summing == CmmSubBlock::CRATE && 
+	    dataID != LVL1::CMMCPHits::LOCAL) continue;
+	switch (dataID) {
+	  case LVL1::CMMCPHits::LOCAL:
+	    source = CmmCpSubBlock::LOCAL;
+	    break;
+	  case LVL1::CMMCPHits::REMOTE_0:
+	    source = CmmCpSubBlock::REMOTE_0;
+	    break;
+	  case LVL1::CMMCPHits::REMOTE_1:
+	    source = CmmCpSubBlock::REMOTE_1;
+	    break;
+	  case LVL1::CMMCPHits::REMOTE_2:
+	    source = CmmCpSubBlock::REMOTE_2;
+	    break;
+	  case LVL1::CMMCPHits::TOTAL:
+	    source = CmmCpSubBlock::TOTAL;
+	    break;
+          default:
+	    continue;
+        }
+      }
+      const LVL1::CMMCPHits* const ch = findCmmCpHits(crate, dataID);
+      if ( ch ) {
+        std::vector<unsigned int> hits0;
+        std::vector<unsigned int> hits1;
+        std::vector<int> err0;
+        std::vector<int> err1;
+	ModifySlices::data(ch->HitsVec0(),  hits0, timeslicesNew);
+	ModifySlices::data(ch->HitsVec1(),  hits1, timeslicesNew);
+	ModifySlices::data(ch->ErrorVec0(), err0,  timeslicesNew);
+	ModifySlices::data(ch->ErrorVec1(), err1,  timeslicesNew);
+	for (int slice = 0; slice < timeslicesNew; ++slice) {
+	  const LVL1::DataError err0Bits(err0[slice]);
+	  const LVL1::DataError err1Bits(err1[slice]);
+	  const int index = ( neutralFormat ) ? 0 : slice;
+	  CmmCpSubBlock* subBlock = m_cmmHit0Blocks[index];
+	  subBlock->setHits(slice, source, hits0[slice],
+	                           err0Bits.get(LVL1::DataError::Parity));
+	  subBlock = m_cmmHit1Blocks[index];
+	  subBlock->setHits(slice, source, hits1[slice],
+	                           err1Bits.get(LVL1::DataError::Parity));
+        }
+      }
+    }
+    DataVector<CmmCpSubBlock>::const_iterator cos = m_cmmHit0Blocks.begin();
+    for (; cos != m_cmmHit0Blocks.end(); ++cos) {
+      CmmCpSubBlock* const subBlock = *cos;
+      if ( !subBlock->pack()) {
+        msg(MSG::ERROR) << "CMM-Cp sub-block packing failed" << endreq;
+	return StatusCode::FAILURE;
+      }
+      if (debug) {
+        msg() << "CMM-Cp sub-block data words: "
+	      << subBlock->dataWords() << endreq;
+      }
+      subBlock->write(theROD);
+    }
+    cos = m_cmmHit1Blocks.begin();
+    for (; cos != m_cmmHit1Blocks.end(); ++cos) {
+      CmmCpSubBlock* const subBlock = *cos;
+      if ( !subBlock->pack()) {
+        msg(MSG::ERROR) << "CMM-Cp sub-block packing failed" << endreq;
+	return StatusCode::FAILURE;
+      }
+      if (debug) {
+        msg() << "CMM-Cp sub-block data words: "
+	      << subBlock->dataWords() << endreq;
+      }
+      subBlock->write(theROD);
+    }
+  }
+
+  // Fill the raw event
+
+  m_fea->fill(re, msg());
+
+  // Set ROD status words
+
+  //L1CaloRodStatus::setStatus(re, m_rodStatusMap, m_srcIdMap);
+
+  return StatusCode::SUCCESS;
+}
+
+// Return reference to vector with all possible Source Identifiers
+
+const std::vector<uint32_t>& CpByteStreamTool::sourceIDs(
+                                               const std::string& sgKey)
+{
+  // Check if overlap tower channels wanted
+  const std::string flag("Overlap");
+  const std::string::size_type pos = sgKey.find(flag);
+  m_coreOverlap =
+   (pos == std::string::npos || pos != sgKey.length() - flag.length()) ? 0 : 1;
+
+  if (m_sourceIDs.empty()) {
+    const int maxCrates = m_crates + m_crateOffsetHw;
+    const int maxSlinks = m_srcIdMap->maxSlinks();
+    for (int hwCrate = m_crateOffsetHw; hwCrate < maxCrates; ++hwCrate) {
+      for (int slink = 0; slink < maxSlinks; ++slink) {
+        const int daqOrRoi = 0;
+        const uint32_t rodId = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+                                                             m_subDetector);
+        const uint32_t robId = m_srcIdMap->getRobID(rodId);
+        m_sourceIDs.push_back(robId);
+      }
+    }
+  }
+  return m_sourceIDs;
+}
+
+// Convert bytestream to given container type
+
+StatusCode CpByteStreamTool::convertBs(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            const CollectionType collection)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Loop over ROB fragments
+
+  int robCount = 0;
+  std::set<uint32_t> dupCheck;
+  ROBIterator rob    = robFrags.begin();
+  ROBIterator robEnd = robFrags.end();
+  for (; rob != robEnd; ++rob) {
+
+    if (debug) {
+      ++robCount;
+      msg() << "Treating ROB fragment " << robCount << endreq;
+    }
+
+    // Skip fragments with ROB status errors
+
+    uint32_t robid = (*rob)->source_id();
+    if ((*rob)->nstatus() > 0) {
+      ROBPointer robData;
+      (*rob)->status(robData);
+      if (*robData != 0) {
+        m_errorTool->robError(robid, *robData);
+	if (debug) msg() << "ROB status error - skipping fragment" << endreq;
+	continue;
+      }
+    }
+
+    // Skip duplicate fragments
+
+    if (!dupCheck.insert(robid).second) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_DUPLICATE_ROB);
+      if (debug) msg() << "Skipping duplicate ROB fragment" << endreq;
+      continue;
+    }
+
+    // Unpack ROD data (slinks)
+
+    RODPointer payloadBeg;
+    RODPointer payload;
+    RODPointer payloadEnd;
+    (*rob)->rod_data(payloadBeg);
+    payloadEnd = payloadBeg + (*rob)->rod_ndata();
+    payload = payloadBeg;
+    if (payload == payloadEnd) {
+      if (debug) msg() << "ROB fragment empty" << endreq;
+      continue;
+    }
+
+    // Check identifier
+    const uint32_t sourceID = (*rob)->rod_source_id();
+    if (m_srcIdMap->getRobID(sourceID) != robid           ||
+        m_srcIdMap->subDet(sourceID)   != m_subDetector   ||
+	m_srcIdMap->daqOrRoi(sourceID) != 0               ||
+       (m_srcIdMap->slink(sourceID) != 0 && m_srcIdMap->slink(sourceID) != 2) ||
+	m_srcIdMap->crate(sourceID)    <  m_crateOffsetHw ||
+	m_srcIdMap->crate(sourceID)    >= m_crateOffsetHw + m_crates) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_ROD_ID);
+      if (debug) {
+        msg() << "Wrong source identifier in data: ROD "
+	      << MSG::hex << sourceID << "  ROB " << robid
+	      << MSG::dec << endreq;
+      }
+      continue;
+    }
+    const int rodCrate = m_srcIdMap->crate(sourceID);
+    if (debug) {
+      msg() << "Treating crate " << rodCrate 
+            << " slink " << m_srcIdMap->slink(sourceID) << endreq;
+    }
+
+    // First word should be User Header
+    if ( !L1CaloUserHeader::isValid(*payload) ) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
+      if (debug) msg() << "Invalid or missing user header" << endreq;
+      continue;
+    }
+    L1CaloUserHeader userHeader(*payload);
+    const int minorVersion = (*rob)->rod_version() & 0xffff;
+    userHeader.setVersion(minorVersion);
+    const int headerWords = userHeader.words();
+    if (headerWords != 1) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
+      if (debug) msg() << "Unexpected number of user header words: "
+                       << headerWords << endreq;
+      continue;
+    }
+    for (int i = 0; i < headerWords; ++i) ++payload;
+    // triggered slice offsets
+    int trigCpm = userHeader.cpm();
+    int trigCmm = userHeader.cpCmm();
+    if (debug) {
+      msg() << "Minor format version number: " << MSG::hex
+            << minorVersion << MSG::dec << endreq
+            << "CPM triggered slice offset: "  << trigCpm << endreq
+            << "CMM triggered slice offset: "  << trigCmm << endreq;
+    }
+    if (trigCpm != trigCmm) {
+      const int newTrig = (trigCpm > trigCmm) ? trigCpm : trigCmm;
+      trigCpm = newTrig;
+      trigCmm = newTrig;
+      if (debug) msg() << "Changed both offsets to " << newTrig << endreq;
+    }
+
+    // Loop over sub-blocks
+
+    m_rodErr = L1CaloSubBlock::ERROR_NONE;
+    while (payload != payloadEnd) {
+      
+      if (L1CaloSubBlock::wordType(*payload) != L1CaloSubBlock::HEADER) {
+        if (debug) msg() << "Unexpected data sequence" << endreq;
+	m_rodErr = L1CaloSubBlock::ERROR_MISSING_HEADER;
+	break;
+      }
+      if (CmmSubBlock::cmmBlock(*payload)) {
+        // CMM
+	if (CmmSubBlock::cmmType(*payload) == CmmSubBlock::CMM_CP) {
+	  m_cmmCpSubBlock->clear();
+          payload = m_cmmCpSubBlock->read(payload, payloadEnd);
+	  if (m_cmmCpSubBlock->crate() != rodCrate) {
+	    if (debug) msg() << "Inconsistent crate number in ROD source ID"
+	                     << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	    break;
+          }
+	  if (collection == CMM_CP_HITS) {
+	    decodeCmmCp(m_cmmCpSubBlock, trigCmm);
+	    if (m_rodErr != L1CaloSubBlock::ERROR_NONE) {
+	      if (debug) msg() << "decodeCmmCp failed" << endreq;
+	      break;
+	    }
+          }
+        } else {
+          if (debug) msg() << "Invalid CMM type in module field" << endreq;
+          m_rodErr = L1CaloSubBlock::ERROR_MODULE_NUMBER;
+	  break;
+        }
+      } else {
+        // CPM
+        CpmSubBlock subBlock;
+	m_cpmSubBlock->clear();
+        payload = m_cpmSubBlock->read(payload, payloadEnd);
+	if (m_cpmSubBlock->crate() != rodCrate) {
+	  if (debug) msg() << "Inconsistent crate number in ROD source ID"
+	                   << endreq;
+	  m_rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	  break;
+        }
+	if (collection == CPM_TOWERS || collection == CPM_HITS) {
+	  decodeCpm(m_cpmSubBlock, trigCpm, collection);
+	  if (m_rodErr != L1CaloSubBlock::ERROR_NONE) {
+	    if (debug) msg() << "decodeCpm failed" << endreq;
+	    break;
+	  }
+        }
+      }
+    }
+    if (m_rodErr != L1CaloSubBlock::ERROR_NONE) 
+                                       m_errorTool->rodError(robid, m_rodErr);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Unpack CMM-CP sub-block
+
+void CpByteStreamTool::decodeCmmCp(CmmCpSubBlock* subBlock, int trigCmm)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  const int hwCrate    = subBlock->crate();
+  const int module     = subBlock->cmmPosition();
+  const int firmware   = subBlock->cmmFirmware();
+  const int summing    = subBlock->cmmSumming();
+  const int timeslices = subBlock->timeslices();
+  const int sliceNum   = subBlock->slice();
+  if (debug) {
+    msg() << "CMM-CP: Crate "  << hwCrate
+          << "  Module "       << module
+	  << "  Firmware "     << firmware
+	  << "  Summing "      << summing
+          << "  Total slices " << timeslices
+          << "  Slice "        << sliceNum    << endreq;
+  }
+  if (timeslices <= trigCmm) {
+    if (debug) msg() << "Triggered CMM slice from header "
+                     << "inconsistent with number of slices: "
+                     << trigCmm << ", " << timeslices << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  if (timeslices <= sliceNum) {
+    if (debug) msg() << "Total slices inconsistent with slice number: "
+                     << timeslices << ", " << sliceNum << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  // Unpack sub-block
+  if (subBlock->dataWords() && !subBlock->unpack()) {
+    if (debug) {
+      std::string errMsg(subBlock->unpackErrorMsg());
+      msg() << "CMM-CP sub-block unpacking failed: " << errMsg << endreq;
+    }
+    m_rodErr = subBlock->unpackErrorCode();
+    return;
+  }
+
+  // Retrieve required data
+
+  const bool neutralFormat = subBlock->format() == L1CaloSubBlock::NEUTRAL;
+  const uint32_t subStatus = subBlock->subStatus();
+  const int crate    = hwCrate - m_crateOffsetHw;
+  const int swCrate  = crate   + m_crateOffsetSw;
+  const int maxSid   = CmmCpSubBlock::MAX_SOURCE_ID;
+  const int sliceBeg = ( neutralFormat ) ? 0          : sliceNum;
+  const int sliceEnd = ( neutralFormat ) ? timeslices : sliceNum + 1;
+  for (int slice = sliceBeg; slice < sliceEnd; ++slice) {
+
+    // Jet hit counts
+
+    for (int source = 1; source < maxSid; ++source) {
+      int dataID = source;
+      if (source > m_modules) {
+        if (summing == CmmSubBlock::CRATE &&
+	    source != CmmCpSubBlock::LOCAL) continue;
+	switch (source) {
+	  case CmmCpSubBlock::LOCAL:
+	    dataID = LVL1::CMMCPHits::LOCAL;
+	    break;
+	  case CmmCpSubBlock::REMOTE_0:
+	    dataID = LVL1::CMMCPHits::REMOTE_0;
+	    break;
+	  case CmmCpSubBlock::REMOTE_1:
+	    dataID = LVL1::CMMCPHits::REMOTE_1;
+	    break;
+	  case CmmCpSubBlock::REMOTE_2:
+	    dataID = LVL1::CMMCPHits::REMOTE_2;
+	    break;
+	  case CmmCpSubBlock::TOTAL:
+	    dataID = LVL1::CMMCPHits::TOTAL;
+	    break;
+          default:
+	    continue;
+        }
+      }
+      const unsigned int hits = subBlock->hits(slice, source);
+      int err = subBlock->hitsError(slice, source);
+      LVL1::DataError errorBits;
+      errorBits.set(LVL1::DataError::Parity, err & 0x1);
+      errorBits.set(LVL1::DataError::SubStatusWord, subStatus);
+      err = errorBits.error();
+      if (hits || err) {
+        LVL1::CMMCPHits* ch = findCmmCpHits(crate, dataID);
+	if ( ! ch ) {   // create new CMM hits
+	  m_hitsVec0.assign(timeslices, 0);
+	  m_hitsVec1.assign(timeslices, 0);
+	  m_errVec0.assign(timeslices, 0);
+	  m_errVec1.assign(timeslices, 0);
+	  if (module == CmmSubBlock::RIGHT) {
+	    m_hitsVec0[slice] = hits;
+	    m_errVec0[slice]  = err;
+	  } else {
+	    m_hitsVec1[slice] = hits;
+	    m_errVec1[slice]  = err;
+	  }
+	  ch = new LVL1::CMMCPHits(swCrate, dataID, m_hitsVec0, m_hitsVec1,
+	                                    m_errVec0, m_errVec1, trigCmm);
+          const int key = crate*100 + dataID;
+	  m_cmmHitsMap.insert(std::make_pair(key, ch));
+	  m_cmmHitCollection->push_back(ch);
+        } else {
+	  m_hitsVec0 = ch->HitsVec0();
+	  m_hitsVec1 = ch->HitsVec1();
+	  m_errVec0  = ch->ErrorVec0();
+	  m_errVec1  = ch->ErrorVec1();
+	  const int nsl = m_hitsVec0.size();
+	  if (timeslices != nsl) {
+	    if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                     << endreq;
+            m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    return;
+          }
+	  if ((module == CmmSubBlock::RIGHT && (m_hitsVec0[slice] != 0 ||
+	       m_errVec0[slice] != 0)) || (module == CmmSubBlock::LEFT &&
+	       (m_hitsVec1[slice] != 0 || m_errVec1[slice]  != 0))) {
+            if (debug) msg() << "Duplicate data for slice " << slice << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    return;
+          }
+	  if (module == CmmSubBlock::RIGHT) {
+	    m_hitsVec0[slice] = hits;
+	    m_errVec0[slice]  = err;
+	  } else {
+	    m_hitsVec1[slice] = hits;
+	    m_errVec1[slice]  = err;
+	  }
+	  ch->addHits(m_hitsVec0, m_hitsVec1, m_errVec0, m_errVec1);
+        }
+      }
+    }
+  }
+  
+  return;
+}
+
+// Unpack CPM sub-block
+
+void CpByteStreamTool::decodeCpm(CpmSubBlock* subBlock,
+                                 int trigCpm, const CollectionType collection)
+{
+  const bool debug   = msgLvl(MSG::DEBUG);
+  const bool verbose = msgLvl(MSG::VERBOSE);
+  if (debug) msg(MSG::DEBUG);
+
+  const int hwCrate    = subBlock->crate();
+  const int module     = subBlock->module();
+  const int timeslices = subBlock->timeslices();
+  const int sliceNum   = subBlock->slice();
+  if (debug) {
+    msg() << "CPM: Crate "     << hwCrate
+          << "  Module "       << module
+          << "  Total slices " << timeslices
+          << "  Slice "        << sliceNum    << endreq;
+  }
+  if (module < 1 || module > m_modules) {
+    if (debug) msg() << "Unexpected module number: " << module << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_MODULE_NUMBER;
+    return;
+  }
+  if (timeslices <= trigCpm) {
+    if (debug) msg() << "Triggered CPM slice from header "
+                     << "inconsistent with number of slices: "
+                     << trigCpm << ", " << timeslices << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  if (timeslices <= sliceNum) {
+    if (debug) msg() << "Total slices inconsistent with slice number: "
+                     << timeslices << ", " << sliceNum << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  // Unpack sub-block
+  if (subBlock->dataWords() && !subBlock->unpack()) {
+    if (debug) {
+      std::string errMsg(subBlock->unpackErrorMsg());
+      msg() << "CPM sub-block unpacking failed: " << errMsg << endreq;
+    }
+    m_rodErr = subBlock->unpackErrorCode();
+    return;
+  }
+
+  // Retrieve required data
+
+  const bool neutralFormat = subBlock->format() == L1CaloSubBlock::NEUTRAL;
+  LVL1::DataError dErr;
+  dErr.set(LVL1::DataError::SubStatusWord, subBlock->subStatus());
+  const int ssError  = dErr.error();
+  const int crate    = hwCrate - m_crateOffsetHw;
+  const int swCrate  = crate   + m_crateOffsetSw;
+  const int sliceBeg = ( neutralFormat ) ? 0          : sliceNum;
+  const int sliceEnd = ( neutralFormat ) ? timeslices : sliceNum + 1;
+  for (int slice = sliceBeg; slice < sliceEnd; ++slice) {
+
+    if (collection == CPM_TOWERS) {
+
+      // Loop over tower channels and fill CPM towers
+
+      for (int chan = 0; chan < m_channels; ++chan) {
+        if (!ssError && !subBlock->anyTowerData(chan)) continue;
+	const int em     = subBlock->emData(slice, chan);
+	const int had    = subBlock->hadData(slice, chan);
+	const int emErr  = subBlock->emError(slice, chan);
+	const int hadErr = subBlock->hadError(slice, chan);
+	int emErr1 = ssError;
+	if (emErr) {
+	  LVL1::DataError emErrBits(emErr1);
+	  emErrBits.set(LVL1::DataError::Parity, emErr & 0x1);
+	  emErrBits.set(LVL1::DataError::LinkDown, (emErr >> 1) & 0x1);
+	  emErr1 = emErrBits.error();
+	}
+	int hadErr1 = ssError;
+	if (hadErr) {
+	  LVL1::DataError hadErrBits(hadErr1);
+	  hadErrBits.set(LVL1::DataError::Parity, hadErr & 0x1);
+	  hadErrBits.set(LVL1::DataError::LinkDown, (hadErr >> 1) & 0x1);
+	  hadErr1 = hadErrBits.error();
+	}
+        if (em || had || emErr1 || hadErr1) {
+	  double eta = 0.;
+	  double phi = 0.;
+	  int layer = 0;
+	  if (m_cpmMaps->mapping(crate, module, chan, eta, phi, layer)) {
+	    if (layer == m_coreOverlap) {
+	      const unsigned int key = m_towerKey->ttKey(phi, eta);
+	      LVL1::CPMTower* tt = findCpmTower(key);
+	      if ( ! tt ) {   // create new CPM tower
+	        m_emVec.assign(timeslices, 0);
+	        m_hadVec.assign(timeslices, 0);
+	        m_emErrVec.assign(timeslices, 0);
+	        m_hadErrVec.assign(timeslices, 0);
+	        m_emVec[slice]     = em;
+	        m_hadVec[slice]    = had;
+	        m_emErrVec[slice]  = emErr1;
+	        m_hadErrVec[slice] = hadErr1;
+	        tt = new LVL1::CPMTower(phi, eta, m_emVec, m_emErrVec,
+	                                          m_hadVec, m_hadErrVec, trigCpm);
+	        m_ttMap.insert(std::make_pair(key, tt));
+	        m_ttCollection->push_back(tt);
+              } else {
+	        m_emVec     = tt->emEnergyVec();
+	        m_hadVec    = tt->hadEnergyVec();
+	        m_emErrVec  = tt->emErrorVec();
+	        m_hadErrVec = tt->hadErrorVec();
+		const int nsl = m_emVec.size();
+	        if (timeslices != nsl) {
+	          if (debug) {
+		    msg() << "Inconsistent number of slices in sub-blocks"
+	                  << endreq;
+	          }
+                  m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	          return;
+                }
+		if (m_emVec[slice]    != 0 || m_hadVec[slice]    != 0 ||
+		    m_emErrVec[slice] != 0 || m_hadErrVec[slice] != 0) {
+                  if (debug) msg() << "Duplicate data for slice "
+		                   << slice << endreq;
+	          m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	          return;
+                }
+	        m_emVec[slice]     = em;
+	        m_hadVec[slice]    = had;
+	        m_emErrVec[slice]  = emErr1;
+	        m_hadErrVec[slice] = hadErr1;
+	        tt->fill(m_emVec, m_emErrVec, m_hadVec, m_hadErrVec, trigCpm);
+	      }
+	    }
+          } else if (verbose && (em || had || emErr || hadErr)) {
+	    msg(MSG::VERBOSE) << "Non-zero data but no channel mapping for channel "
+	                      << chan << endreq;
+	    msg(MSG::DEBUG);
+          }
+        } else if (verbose) {
+	  msg(MSG::VERBOSE) << "No CPM tower data for channel "
+	                    << chan << " slice " << slice << endreq;
+	  msg(MSG::DEBUG);
+        }
+      }
+    } else if (collection == CPM_HITS) {
+
+      // Get CPM hits
+
+      const unsigned int hits0 = subBlock->hits0(slice);
+      const unsigned int hits1 = subBlock->hits1(slice);
+      if (hits0 || hits1) {
+        LVL1::CPMHits* ch = findCpmHits(crate, module);
+	if ( ! ch ) {   // create new CPM hits
+	  m_hitsVec0.assign(timeslices, 0);
+	  m_hitsVec1.assign(timeslices, 0);
+	  m_hitsVec0[slice] = hits0;
+	  m_hitsVec1[slice] = hits1;
+	  ch = new LVL1::CPMHits(swCrate, module, m_hitsVec0, m_hitsVec1, trigCpm);
+	  m_hitsMap.insert(std::make_pair(crate*m_modules+module-1, ch));
+	  m_hitCollection->push_back(ch);
+        } else {
+	  m_hitsVec0 = ch->HitsVec0();
+	  m_hitsVec1 = ch->HitsVec1();
+	  const int nsl = m_hitsVec0.size();
+	  if (timeslices != nsl) {
+	    if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                     << endreq;
+            m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    return;
+          }
+	  if (m_hitsVec0[slice] != 0 || m_hitsVec1[slice] != 0) {
+            if (debug) msg() << "Duplicate data for slice " << slice << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    return;
+          }
+	  m_hitsVec0[slice] = hits0;
+	  m_hitsVec1[slice] = hits1;
+	  ch->addHits(m_hitsVec0, m_hitsVec1);
+        }
+      } else if (verbose) {
+        msg(MSG::VERBOSE) << "No CPM hits data for crate/module/slice "
+                          << hwCrate << "/" << module << "/" << slice
+   			  << endreq;
+        msg(MSG::DEBUG);
+      }
+    }
+  }
+  return;
+}
+
+// Find a CPM tower for given key
+
+LVL1::CPMTower* CpByteStreamTool::findCpmTower(const unsigned int key)
+{
+  LVL1::CPMTower* tt = 0;
+  CpmTowerMap::const_iterator mapIter;
+  mapIter = m_ttMap.find(key);
+  if (mapIter != m_ttMap.end()) tt = mapIter->second;
+  return tt;
+}
+
+// Find CPM hits for given crate, module
+
+LVL1::CPMHits* CpByteStreamTool::findCpmHits(const int crate, const int module)
+{
+  LVL1::CPMHits* hits = 0;
+  CpmHitsMap::const_iterator mapIter;
+  mapIter = m_hitsMap.find(crate*m_modules + module - 1);
+  if (mapIter != m_hitsMap.end()) hits = mapIter->second;
+  return hits;
+}
+
+// Find CMM-CP hits for given crate, dataID
+
+LVL1::CMMCPHits* CpByteStreamTool::findCmmCpHits(const int crate,
+                                                 const int dataID)
+{
+  LVL1::CMMCPHits* hits = 0;
+  CmmCpHitsMap::const_iterator mapIter;
+  mapIter = m_cmmHitsMap.find(crate*100 + dataID);
+  if (mapIter != m_cmmHitsMap.end()) hits = mapIter->second;
+  return hits;
+}
+
+// Set up CPM tower map
+
+void CpByteStreamTool::setupCpmTowerMap(const CpmTowerCollection*
+                                                          const ttCollection)
+{
+  m_ttMap.clear();
+  if (ttCollection) {
+    CpmTowerCollection::const_iterator pos  = ttCollection->begin();
+    CpmTowerCollection::const_iterator pose = ttCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::CPMTower* const tt = *pos;
+      const unsigned int key = m_towerKey->ttKey(tt->phi(), tt->eta());
+      m_ttMap.insert(std::make_pair(key, tt));
+    }
+  }
+}
+
+// Set up CPM hits map
+
+void CpByteStreamTool::setupCpmHitsMap(const CpmHitsCollection*
+                                                        const hitCollection)
+{
+  m_hitsMap.clear();
+  if (hitCollection) {
+    CpmHitsCollection::const_iterator pos  = hitCollection->begin();
+    CpmHitsCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::CPMHits* const hits = *pos;
+      const int crate = hits->crate() - m_crateOffsetSw;
+      const int key   = m_modules * crate + hits->module() - 1;
+      m_hitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Set up CMM-CP hits map
+
+void CpByteStreamTool::setupCmmCpHitsMap(const CmmCpHitsCollection*
+                                                          const hitCollection)
+{
+  m_cmmHitsMap.clear();
+  if (hitCollection) {
+    CmmCpHitsCollection::const_iterator pos  = hitCollection->begin();
+    CmmCpHitsCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::CMMCPHits* const hits = *pos;
+      const int crate = hits->crate() - m_crateOffsetSw;
+      const int key   = crate*100 + hits->dataID();
+      m_cmmHitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Get number of slices and triggered slice offset for next slink
+
+bool CpByteStreamTool::slinkSlices(const int crate, const int module,
+                  const int modulesPerSlink, int& timeslices, int& trigCpm)
+{
+  int slices = -1;
+  int trigC  = m_dfltSlices/2;
+  for (int mod = module; mod < module + modulesPerSlink; ++mod) {
+    for (int chan = 0; chan < m_channels; ++chan) {
+      double eta = 0.;
+      double phi = 0.;
+      int layer = 0;
+      if ( !m_cpmMaps->mapping(crate, mod, chan, eta, phi, layer)) continue;
+      const unsigned int key = m_towerKey->ttKey(phi, eta);
+      const LVL1::CPMTower* const tt = findCpmTower(key);
+      if ( !tt ) continue;
+      const int numdat = 4;
+      std::vector<int> sums(numdat);
+      std::vector<int> sizes(numdat);
+      sums[0] = std::accumulate((tt->emEnergyVec()).begin(),
+                                (tt->emEnergyVec()).end(), 0);
+      sums[1] = std::accumulate((tt->hadEnergyVec()).begin(),
+                                (tt->hadEnergyVec()).end(), 0);
+      sums[2] = std::accumulate((tt->emErrorVec()).begin(),
+                                (tt->emErrorVec()).end(), 0);
+      sums[3] = std::accumulate((tt->hadErrorVec()).begin(),
+                                (tt->hadErrorVec()).end(), 0);
+      sizes[0] = (tt->emEnergyVec()).size();
+      sizes[1] = (tt->hadEnergyVec()).size();
+      sizes[2] = (tt->emErrorVec()).size();
+      sizes[3] = (tt->hadErrorVec()).size();
+      const int peak = tt->peak();
+      for (int i = 0; i < numdat; ++i) {
+        if (sums[i] == 0) continue;
+        if (slices < 0) {
+	  slices = sizes[i];
+	  trigC  = peak;
+	} else if (slices != sizes[i] || trigC != peak) return false;
+      }
+    }
+    const LVL1::CPMHits* const hits = findCpmHits(crate, mod);
+    if (hits) {
+      const int numdat = 2;
+      std::vector<unsigned int> sums(numdat);
+      std::vector<int> sizes(numdat);
+      sums[0] = std::accumulate((hits->HitsVec0()).begin(),
+                                         (hits->HitsVec0()).end(), 0);
+      sums[1] = std::accumulate((hits->HitsVec1()).begin(),
+                                         (hits->HitsVec1()).end(), 0);
+      sizes[0] = (hits->HitsVec0()).size();
+      sizes[1] = (hits->HitsVec1()).size();
+      const int peak = hits->peak();
+      for (int i = 0; i < numdat; ++i) {
+        if (sums[i] == 0) continue;
+        if (slices < 0) {
+	  slices = sizes[i];
+	  trigC  = peak;
+        } else if (slices != sizes[i] || trigC != peak) return false;
+      }
+    }
+  }
+  // CMM last slink of crate
+  if (module/modulesPerSlink == m_slinks - 1) {
+    const int maxDataID = LVL1::CMMCPHits::MAXID;
+    for (int dataID = 0; dataID < maxDataID; ++dataID) {
+      const int numdat = 4;
+      std::vector<unsigned int> sums(numdat);
+      std::vector<int> sizes(numdat);
+      const LVL1::CMMCPHits* const hits = findCmmCpHits(crate, dataID);
+      if (hits) {
+        sums[0] = std::accumulate((hits->HitsVec0()).begin(),
+                                             (hits->HitsVec0()).end(), 0);
+        sums[1] = std::accumulate((hits->HitsVec1()).begin(),
+                                             (hits->HitsVec1()).end(), 0);
+        sums[2] = std::accumulate((hits->ErrorVec0()).begin(),
+                                             (hits->ErrorVec0()).end(), 0);
+        sums[3] = std::accumulate((hits->ErrorVec1()).begin(),
+                                             (hits->ErrorVec1()).end(), 0);
+        sizes[0] = (hits->HitsVec0()).size();
+        sizes[1] = (hits->HitsVec1()).size();
+        sizes[2] = (hits->ErrorVec0()).size();
+        sizes[3] = (hits->ErrorVec1()).size();
+        const int peak = hits->peak();
+        for (int i = 0; i < numdat; ++i) {
+          if (sums[i] == 0) continue;
+          if (slices < 0) {
+	    slices = sizes[i];
+	    trigC  = peak;
+          } else if (slices != sizes[i] || trigC != peak) return false;
+        }
+      }
+    }
+  }
+  if (slices < 0) slices = m_dfltSlices;
+  timeslices = slices;
+  trigCpm    = trigC;
+  return true;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamTool.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamTool.h
new file mode 100755
index 0000000000000000000000000000000000000000..11ce0ad1b9d4e94b181f7ff0d2767b0463990e51
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamTool.h
@@ -0,0 +1,206 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPBYTESTREAMTOOL_H
+#define TRIGT1CALOBYTESTREAM_CPBYTESTREAMTOOL_H
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+#include "ByteStreamData/RawEvent.h"
+#include "DataModel/DataVector.h"
+#include "eformat/SourceIdentifier.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class IInterface;
+class InterfaceID;
+class StatusCode;
+
+template <typename> class FullEventAssembler;
+
+namespace LVL1 {
+  class CMMCPHits;
+  class CPMHits;
+  class CPMTower;
+  class CPBSCollection;
+  class IL1CaloMappingTool;
+  class TriggerTowerKey;
+}
+
+namespace LVL1BS {
+
+class CmmCpSubBlock;
+class CpmSubBlock;
+class L1CaloErrorByteStreamTool;
+class L1CaloSrcIdMap;
+
+/** Tool to perform ROB fragments to CPM towers, CPM hits and CMM-CP hits,
+ *  and CP container to raw data conversions.
+ *
+ *  Based on ROD document version 1_09h.
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpByteStreamTool : public AthAlgTool {
+
+ public:
+   CpByteStreamTool(const std::string& type, const std::string& name,
+                                             const IInterface* parent);
+   virtual ~CpByteStreamTool();
+
+   /// AlgTool InterfaceID
+   static const InterfaceID& interfaceID();
+
+   virtual StatusCode initialize();
+   virtual StatusCode finalize();
+
+   /// Convert ROB fragments to CPM towers
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::CPMTower>* ttCollection);
+   /// Convert ROB fragments to CPM hits
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::CPMHits>* hitCollection);
+   /// Convert ROB fragments to CMM-CP hits
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::CMMCPHits>* hitCollection);
+
+   /// Convert CP Container to bytestream
+   StatusCode convert(const LVL1::CPBSCollection* cp, RawEventWrite* re);
+
+   /// Return reference to vector with all possible Source Identifiers
+   const std::vector<uint32_t>& sourceIDs(const std::string& sgKey);
+
+ private:
+
+   enum CollectionType { CPM_TOWERS, CPM_HITS, CMM_CP_HITS };
+
+   typedef DataVector<LVL1::CPMTower>                    CpmTowerCollection;
+   typedef DataVector<LVL1::CPMHits>                     CpmHitsCollection;
+   typedef DataVector<LVL1::CMMCPHits>                   CmmCpHitsCollection;
+   typedef std::map<unsigned int, LVL1::CPMTower*>       CpmTowerMap;
+   typedef std::map<int, LVL1::CPMHits*>                 CpmHitsMap;
+   typedef std::map<int, LVL1::CMMCPHits*>               CmmCpHitsMap;
+   typedef IROBDataProviderSvc::VROBFRAG::const_iterator ROBIterator;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      ROBPointer;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      RODPointer;
+
+   /// Convert bytestream to given container type
+   StatusCode convertBs(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                        CollectionType collection);
+   /// Unpack CMM-CP sub-block
+   void decodeCmmCp(CmmCpSubBlock* subBlock, int trigCmm);
+   /// Unpack CPM sub-block
+   void decodeCpm(CpmSubBlock* subBlock, int trigCpm, CollectionType collection);
+
+   /// Find a CPM tower for given key
+   LVL1::CPMTower*  findCpmTower(unsigned int key);
+   /// Find CPM hits for given crate, module
+   LVL1::CPMHits*   findCpmHits(int crate, int module);
+   /// Find CMM-CP hits for given crate, data ID
+   LVL1::CMMCPHits* findCmmCpHits(int crate, int dataID);
+
+   /// Set up CPM tower map
+   void setupCpmTowerMap(const CpmTowerCollection* ttCollection);
+   /// Set up CPM hits map
+   void setupCpmHitsMap(const CpmHitsCollection* hitCollection);
+   /// Set up CMM-CP hits map
+   void setupCmmCpHitsMap(const CmmCpHitsCollection* hitCollection);
+
+   /// Get number of slices and triggered slice offset for next slink
+   bool slinkSlices(int crate, int module, int modulesPerSlink,
+                    int& timeslices, int& trigJem);
+
+   /// Channel mapping tool
+   ToolHandle<LVL1::IL1CaloMappingTool> m_cpmMaps;
+   /// Error collection tool
+   ToolHandle<LVL1BS::L1CaloErrorByteStreamTool> m_errorTool;
+
+   /// Hardware crate number offset
+   int m_crateOffsetHw;
+   /// Software crate number offset
+   int m_crateOffsetSw;
+   /// Sub_block header version
+   int m_version;
+   /// Data compression format
+   int m_dataFormat;
+   /// Number of channels per module
+   int m_channels;
+   /// Number of crates
+   int m_crates;
+   /// Number of CPM modules per crate
+   int m_modules;
+   /// Number of slinks per crate when writing out bytestream
+   int m_slinks;
+   /// Default number of slices in simulation
+   int m_dfltSlices;
+   /// Force number of slices in bytestream
+   int m_forceSlices;
+   /// Tower channels to accept (1=Core, 2=Overlap)
+   int m_coreOverlap;
+   /// Unpacking error code
+   unsigned int m_rodErr;
+   /// ROB source IDs
+   std::vector<uint32_t> m_sourceIDs;
+   /// Sub-detector type
+   eformat::SubDetector m_subDetector;
+   /// Source ID converter
+   L1CaloSrcIdMap* m_srcIdMap;
+   /// Trigger tower key provider
+   LVL1::TriggerTowerKey* m_towerKey;
+   /// CPM sub-block for unpacking
+   CpmSubBlock* m_cpmSubBlock;
+   /// CMM-CP sub-block for unpacking
+   CmmCpSubBlock* m_cmmCpSubBlock;
+   /// Hits0 vector for unpacking
+   std::vector<unsigned int> m_hitsVec0;
+   /// Hits1 vector for unpacking
+   std::vector<unsigned int> m_hitsVec1;
+   /// Error0 vector for unpacking
+   std::vector<int> m_errVec0;
+   /// Error1 vector for unpacking
+   std::vector<int> m_errVec1;
+   /// EM data vector for unpacking
+   std::vector<int> m_emVec;
+   /// Had data vector for unpacking
+   std::vector<int> m_hadVec;
+   /// EM error data vector for unpacking
+   std::vector<int> m_emErrVec;
+   /// Had error data vector for unpacking
+   std::vector<int> m_hadErrVec;
+   /// Vector for current CPM sub-blocks
+   DataVector<CpmSubBlock> m_cpmBlocks;
+   /// Vector for current CMM-CP hit0 sub-blocks
+   DataVector<CmmCpSubBlock> m_cmmHit0Blocks;
+   /// Vector for current CMM-CP hit1 sub-blocks
+   DataVector<CmmCpSubBlock> m_cmmHit1Blocks;
+   /// Current CPM tower collection
+   CpmTowerCollection*  m_ttCollection;
+   /// Current CPM hits collection
+   CpmHitsCollection*   m_hitCollection;
+   /// Current CMM-CP hits collection
+   CmmCpHitsCollection* m_cmmHitCollection;
+   /// CPM tower map
+   CpmTowerMap  m_ttMap;
+   /// CPM hits map
+   CpmHitsMap   m_hitsMap;
+   /// CMM-CP hits map
+   CmmCpHitsMap m_cmmHitsMap;
+   /// ROD Status words
+   std::vector<uint32_t>* m_rodStatus;
+   /// ROD status map
+   std::map<uint32_t, std::vector<uint32_t>* > m_rodStatusMap;
+   /// Event assembler
+   FullEventAssembler<L1CaloSrcIdMap>* m_fea;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV1Cnv.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV1Cnv.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..256aacf7a6d53a3d871a231953a9191c0f97b94f
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV1Cnv.cxx
@@ -0,0 +1,115 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IByteStreamEventAccess.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "DataModel/DataVector.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/IRegistry.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "TrigT1CaloEvent/CPBSCollectionV1.h"
+
+#include "CpByteStreamV1Cnv.h"
+#include "CpByteStreamV1Tool.h"
+
+namespace LVL1BS {
+
+CpByteStreamV1Cnv::CpByteStreamV1Cnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("CpByteStreamV1Cnv"),
+      m_tool("LVL1BS::CpByteStreamV1Tool/CpByteStreamV1Tool"),
+      m_ByteStreamEventAccess("ByteStreamCnvSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+CpByteStreamV1Cnv::~CpByteStreamV1Cnv()
+{
+}
+
+// CLID
+
+const CLID& CpByteStreamV1Cnv::classID()
+{
+  return ClassID_traits<LVL1::CPBSCollectionV1>::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode CpByteStreamV1Cnv::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << " - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  //Get ByteStreamCnvSvc
+  sc = m_ByteStreamEventAccess.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_ByteStreamEventAccess << endreq;
+    return sc;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_ByteStreamEventAccess << endreq;
+  }
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return sc;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  return StatusCode::SUCCESS;
+}
+
+// createRep should create the bytestream from RDOs.
+
+StatusCode CpByteStreamV1Cnv::createRep( DataObject* pObj,
+                                        IOpaqueAddress*& pAddr )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createRep() called" << endreq;
+
+  RawEventWrite* re = m_ByteStreamEventAccess->getRawEvent();
+
+  LVL1::CPBSCollectionV1* cp = 0;
+  if( !SG::fromStorable( pObj, cp ) ) {
+    m_log << MSG::ERROR << " Cannot cast to CPBSCollectionV1" << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = pObj->registry()->name();
+
+  ByteStreamAddress* addr = new ByteStreamAddress( classID(), nm, "" );
+
+  pAddr = addr;
+
+  // Convert to ByteStream
+  return m_tool->convert( cp, re );
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV1Cnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV1Cnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..48dcce6ca189479c7a58cbad7339db071c0ef237
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV1Cnv.h
@@ -0,0 +1,76 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPBYTESTREAMV1CNV_H
+#define TRIGT1CALOBYTESTREAM_CPBYTESTREAMV1CNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IByteStreamEventAccess;
+class IOpaqueAddress;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class CpByteStreamV1Tool;
+
+/** ByteStream converter for CP container
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpByteStreamV1Cnv: public Converter {
+
+  friend class CnvFactory<CpByteStreamV1Cnv>;
+
+protected:
+
+  CpByteStreamV1Cnv(ISvcLocator* svcloc);
+
+public:
+
+  ~CpByteStreamV1Cnv();
+
+  virtual StatusCode initialize();
+  /// Create ByteStream from Cp Container
+  virtual StatusCode createRep(DataObject* pObj, IOpaqueAddress*& pAddr);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::CpByteStreamV1Tool> m_tool;
+
+  /// Service for writing bytestream
+  ServiceHandle<IByteStreamEventAccess> m_ByteStreamEventAccess;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV1Tool.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV1Tool.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..39df3097db4387cfb101f2878765afadd37c6f08
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV1Tool.cxx
@@ -0,0 +1,1173 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <numeric>
+#include <set>
+#include <utility>
+
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "ByteStreamCnvSvcBase/FullEventAssembler.h"
+
+#include "TrigT1CaloEvent/CMMCPHits.h"
+#include "TrigT1CaloEvent/CPMHits.h"
+#include "TrigT1CaloEvent/CPMTower.h"
+#include "TrigT1CaloEvent/CPBSCollectionV1.h"
+#include "TrigT1CaloUtils/DataError.h"
+#include "TrigT1CaloUtils/TriggerTowerKey.h"
+#include "TrigT1CaloMappingToolInterfaces/IL1CaloMappingTool.h"
+
+#include "CmmCpSubBlock.h"
+#include "CmmSubBlock.h"
+#include "CpmSubBlockV1.h"
+#include "L1CaloErrorByteStreamTool.h"
+#include "L1CaloSrcIdMap.h"
+#include "L1CaloSubBlock.h"
+#include "L1CaloUserHeader.h"
+#include "ModifySlices.h"
+
+#include "CpByteStreamV1Tool.h"
+
+namespace LVL1BS {
+
+// Interface ID
+
+static const InterfaceID IID_ICpByteStreamV1Tool("CpByteStreamV1Tool", 1, 1);
+
+const InterfaceID& CpByteStreamV1Tool::interfaceID()
+{
+  return IID_ICpByteStreamV1Tool;
+}
+
+// Constructor
+
+CpByteStreamV1Tool::CpByteStreamV1Tool(const std::string& type,
+                                   const std::string& name,
+				   const IInterface*  parent)
+   : AthAlgTool(type, name, parent),
+     m_cpmMaps("LVL1::CpmMappingTool/CpmMappingTool"),
+     m_errorTool("LVL1BS::L1CaloErrorByteStreamTool/L1CaloErrorByteStreamTool"),
+     m_channels(80), m_crates(4), m_modules(14),
+     m_coreOverlap(0), m_subDetector(eformat::TDAQ_CALO_CLUSTER_PROC_DAQ),
+     m_srcIdMap(0), m_towerKey(0), m_cpmSubBlock(0), m_cmmCpSubBlock(0),
+     m_rodStatus(0), m_fea(0)
+{
+  declareInterface<CpByteStreamV1Tool>(this);
+
+  declareProperty("CpmMappingTool", m_cpmMaps,
+                  "Crate/Module/Channel to Eta/Phi/Layer mapping tool");
+  declareProperty("ErrorTool", m_errorTool,
+                  "Tool to collect errors for monitoring");
+
+  declareProperty("CrateOffsetHw",  m_crateOffsetHw  = 8,
+                  "Offset of CP crate numbers in bytestream");
+  declareProperty("CrateOffsetSw",  m_crateOffsetSw  = 0,
+                  "Offset of CP crate numbers in RDOs");
+
+  // Properties for reading bytestream only
+  declareProperty("ROBSourceIDs",       m_sourceIDs,
+                  "ROB fragment source identifiers");
+
+  // Properties for writing bytestream only
+  declareProperty("DataVersion",    m_version     = 1,
+                  "Format version number in sub-block header");
+  declareProperty("DataFormat",     m_dataFormat  = 1,
+                  "Format identifier (0-1) in sub-block header");
+  declareProperty("SlinksPerCrate", m_slinks      = 2,
+                  "The number of S-Links per crate");
+  declareProperty("SimulSlices",    m_dfltSlices  = 1,
+                  "The number of slices in the simulation");
+  declareProperty("ForceSlices",    m_forceSlices = 0,
+                  "If >0, the number of slices in bytestream");
+  declareProperty("CrateMin",       m_crateMin = 0,
+                  "Minimum crate number, allows partial output");
+  declareProperty("CrateMax",       m_crateMax = m_crates-1,
+                  "Maximum crate number, allows partial output");
+
+}
+
+// Destructor
+
+CpByteStreamV1Tool::~CpByteStreamV1Tool()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode CpByteStreamV1Tool::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = m_cpmMaps.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_cpmMaps << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_cpmMaps << endreq;
+
+  sc = m_errorTool.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_errorTool << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_errorTool << endreq;
+
+  m_srcIdMap      = new L1CaloSrcIdMap();
+  m_towerKey      = new LVL1::TriggerTowerKey();
+  m_cpmSubBlock   = new CpmSubBlockV1();
+  m_cmmCpSubBlock = new CmmCpSubBlock();
+  m_rodStatus     = new std::vector<uint32_t>(2);
+  m_fea           = new FullEventAssembler<L1CaloSrcIdMap>();
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode CpByteStreamV1Tool::finalize()
+{
+  delete m_fea;
+  delete m_rodStatus;
+  delete m_cmmCpSubBlock;
+  delete m_cpmSubBlock;
+  delete m_towerKey;
+  delete m_srcIdMap;
+  return StatusCode::SUCCESS;
+}
+
+// Conversion bytestream to CPM towers
+
+StatusCode CpByteStreamV1Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::CPMTower>* const ttCollection)
+{
+  m_ttCollection = ttCollection;
+  m_ttMap.clear();
+  return convertBs(robFrags, CPM_TOWERS);
+}
+
+// Conversion bytestream to CPM hits
+
+StatusCode CpByteStreamV1Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::CPMHits>* const hitCollection)
+{
+  m_hitCollection = hitCollection;
+  m_hitsMap.clear();
+  return convertBs(robFrags, CPM_HITS);
+}
+
+// Conversion bytestream to CMM-CP hits
+
+StatusCode CpByteStreamV1Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::CMMCPHits>* const hitCollection)
+{
+  m_cmmHitCollection = hitCollection;
+  m_cmmHitsMap.clear();
+  return convertBs(robFrags, CMM_CP_HITS);
+}
+
+// Conversion of CP container to bytestream
+
+StatusCode CpByteStreamV1Tool::convert(const LVL1::CPBSCollectionV1* const cp,
+                                             RawEventWrite* const re)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Clear the event assembler
+
+  m_fea->clear();
+  const uint16_t minorVersion = m_srcIdMap->minorVersionPreLS1();
+  m_fea->setRodMinorVersion(minorVersion);
+  m_rodStatusMap.clear();
+
+  // Pointer to ROD data vector
+
+  FullEventAssembler<L1CaloSrcIdMap>::RODDATA* theROD = 0;
+
+  // Set up the container maps
+
+  setupCpmTowerMap(cp->towers());
+  setupCpmHitsMap(cp->hits());
+  setupCmmCpHitsMap(cp->cmmHits());
+
+  // Loop over data
+
+  const bool neutralFormat   = m_dataFormat == L1CaloSubBlock::NEUTRAL;
+  const int  modulesPerSlink = m_modules / m_slinks;
+  int timeslices    = 1;
+  int trigCpm       = 0;
+  int timeslicesNew = 1;
+  int trigCpmNew    = 0;
+  for (int crate = m_crateMin; crate <= m_crateMax; ++crate) {
+    const int hwCrate = crate + m_crateOffsetHw;
+
+    // CPM modules are numbered 1 to m_modules
+    for (int module=1; module <= m_modules; ++module) {
+      const int mod = module - 1;
+
+      // Pack required number of modules per slink
+
+      if (mod%modulesPerSlink == 0) {
+	const int daqOrRoi = 0;
+	const int slink = (m_slinks == 2) ? 2*(mod/modulesPerSlink)
+	                                  : mod/modulesPerSlink;
+        if (debug) {
+          msg() << "Treating crate " << hwCrate
+                << " slink " << slink << endreq;
+        }
+	// Get number of CPM slices and triggered slice offset
+	// for this slink
+	if ( ! slinkSlices(crate, module, modulesPerSlink,
+	                                  timeslices, trigCpm)) {
+	  msg(MSG::ERROR) << "Inconsistent number of slices or "
+	                  << "triggered slice offsets in data for crate "
+	                  << hwCrate << " slink " << slink << endreq;
+	  return StatusCode::FAILURE;
+        }
+	timeslicesNew = (m_forceSlices) ? m_forceSlices : timeslices;
+	trigCpmNew    = ModifySlices::peak(trigCpm, timeslices, timeslicesNew);
+        if (debug) {
+	  msg() << "Data Version/Format: " << m_version
+	        << " " << m_dataFormat << endreq
+                << "Slices/offset: " << timeslices << " " << trigCpm;
+	  if (timeslices != timeslicesNew) {
+	    msg() << " modified to " << timeslicesNew << " " << trigCpmNew;
+          }
+	  msg() << endreq;
+        }
+        L1CaloUserHeader userHeader;
+        userHeader.setCpm(trigCpmNew);
+	const uint32_t rodIdCpm = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+	                                                        m_subDetector);
+	theROD = m_fea->getRodData(rodIdCpm);
+	theROD->push_back(userHeader.header());
+	m_rodStatusMap.insert(make_pair(rodIdCpm, m_rodStatus));
+      }
+      if (debug) msg() << "Module " << module << endreq;
+
+      // Create a sub-block for each slice (except Neutral format)
+
+      m_cpmBlocks.clear();
+      for (int slice = 0; slice < timeslicesNew; ++slice) {
+        CpmSubBlockV1* const subBlock = new CpmSubBlockV1();
+	subBlock->setCpmHeader(m_version, m_dataFormat, slice,
+	                       hwCrate, module, timeslicesNew);
+        m_cpmBlocks.push_back(subBlock);
+	if (neutralFormat) break;
+      }
+
+      // Find CPM towers corresponding to each eta/phi pair and fill
+      // sub-blocks
+
+      for (int chan=0; chan < m_channels; ++chan) {
+	double eta = 0.;
+	double phi = 0.;
+	int layer = 0;
+	if (m_cpmMaps->mapping(crate, module, chan, eta, phi, layer)) {
+          const unsigned int key = m_towerKey->ttKey(phi, eta);
+          const LVL1::CPMTower* const tt = findCpmTower(key);
+	  if (tt ) {
+	    std::vector<int> emData;
+	    std::vector<int> hadData;
+	    std::vector<int> emError;
+	    std::vector<int> hadError;
+	    ModifySlices::data(tt->emEnergyVec(),  emData,   timeslicesNew);
+	    ModifySlices::data(tt->hadEnergyVec(), hadData,  timeslicesNew);
+	    ModifySlices::data(tt->emErrorVec(),   emError,  timeslicesNew);
+	    ModifySlices::data(tt->hadErrorVec(),  hadError, timeslicesNew);
+            for (int slice = 0; slice < timeslicesNew; ++slice) {
+	      const LVL1::DataError emErrBits(emError[slice]);
+	      const LVL1::DataError hadErrBits(hadError[slice]);
+	      const int emErr  =
+	                      (emErrBits.get(LVL1::DataError::LinkDown) << 1) |
+	                       emErrBits.get(LVL1::DataError::Parity);
+	      const int hadErr =
+	                     (hadErrBits.get(LVL1::DataError::LinkDown) << 1) |
+	                      hadErrBits.get(LVL1::DataError::Parity);
+	      const int index  = ( neutralFormat ) ? 0 : slice;
+              CpmSubBlockV1* const subBlock = m_cpmBlocks[index];
+              subBlock->fillTowerData(slice, chan, emData[slice],
+	                              hadData[slice], emErr, hadErr);
+	    }
+          }
+        }
+      }
+
+      // Add CPM hits
+
+      const LVL1::CPMHits* const hits = findCpmHits(crate, module);
+      if (hits) {
+        std::vector<unsigned int> vec0;
+        std::vector<unsigned int> vec1;
+	ModifySlices::data(hits->HitsVec0(), vec0, timeslicesNew);
+	ModifySlices::data(hits->HitsVec1(), vec1, timeslicesNew);
+        for (int slice = 0; slice < timeslicesNew; ++slice) {
+	  const int index = ( neutralFormat ) ? 0 : slice;
+	  CpmSubBlockV1* const subBlock = m_cpmBlocks[index];
+	  subBlock->setHits(slice, vec0[slice], vec1[slice]);
+        }
+      }
+      
+      // Pack and write the sub-blocks
+
+      DataVector<CpmSubBlockV1>::const_iterator pos;
+      for (pos = m_cpmBlocks.begin(); pos != m_cpmBlocks.end(); ++pos) {
+        CpmSubBlockV1* const subBlock = *pos;
+	if ( !subBlock->pack()) {
+	  msg(MSG::ERROR) << "CPM sub-block packing failed" << endreq;
+	  return StatusCode::FAILURE;
+	}
+        if (debug) {
+          msg() << "CPM sub-block data words: "
+	        << subBlock->dataWords() << endreq;
+        }
+	subBlock->write(theROD);
+      }
+    }
+
+    // Append CMMs to last S-Link of the crate
+
+    // Create a sub-block for each slice (except Neutral format)
+
+    m_cmmHit0Blocks.clear();
+    m_cmmHit1Blocks.clear();
+    const int summing = (crate == m_crates - 1) ? CmmSubBlock::SYSTEM
+                                                : CmmSubBlock::CRATE;
+    for (int slice = 0; slice < timeslicesNew; ++slice) {
+      CmmCpSubBlock* const h0Block = new CmmCpSubBlock();
+      h0Block->setCmmHeader(m_version, m_dataFormat, slice, hwCrate,
+                            summing, CmmSubBlock::CMM_CP,
+			    CmmSubBlock::RIGHT, timeslicesNew);
+      m_cmmHit0Blocks.push_back(h0Block);
+      CmmCpSubBlock* const h1Block = new CmmCpSubBlock();
+      h1Block->setCmmHeader(m_version, m_dataFormat, slice, hwCrate,
+                            summing, CmmSubBlock::CMM_CP,
+			    CmmSubBlock::LEFT, timeslicesNew);
+      m_cmmHit1Blocks.push_back(h1Block);
+      if (neutralFormat) break;
+    }
+
+    // CMM-CP
+
+    const int maxDataID = LVL1::CMMCPHits::MAXID;
+    for (int dataID = 1; dataID < maxDataID; ++dataID) {
+      int source = dataID;
+      if (dataID > m_modules) {
+        if (summing == CmmSubBlock::CRATE && 
+	    dataID != LVL1::CMMCPHits::LOCAL) continue;
+	switch (dataID) {
+	  case LVL1::CMMCPHits::LOCAL:
+	    source = CmmCpSubBlock::LOCAL;
+	    break;
+	  case LVL1::CMMCPHits::REMOTE_0:
+	    source = CmmCpSubBlock::REMOTE_0;
+	    break;
+	  case LVL1::CMMCPHits::REMOTE_1:
+	    source = CmmCpSubBlock::REMOTE_1;
+	    break;
+	  case LVL1::CMMCPHits::REMOTE_2:
+	    source = CmmCpSubBlock::REMOTE_2;
+	    break;
+	  case LVL1::CMMCPHits::TOTAL:
+	    source = CmmCpSubBlock::TOTAL;
+	    break;
+          default:
+	    continue;
+        }
+      }
+      const LVL1::CMMCPHits* const ch = findCmmCpHits(crate, dataID);
+      if ( ch ) {
+        std::vector<unsigned int> hits0;
+        std::vector<unsigned int> hits1;
+        std::vector<int> err0;
+        std::vector<int> err1;
+	ModifySlices::data(ch->HitsVec0(),  hits0, timeslicesNew);
+	ModifySlices::data(ch->HitsVec1(),  hits1, timeslicesNew);
+	ModifySlices::data(ch->ErrorVec0(), err0,  timeslicesNew);
+	ModifySlices::data(ch->ErrorVec1(), err1,  timeslicesNew);
+	for (int slice = 0; slice < timeslicesNew; ++slice) {
+	  const LVL1::DataError err0Bits(err0[slice]);
+	  const LVL1::DataError err1Bits(err1[slice]);
+	  const int index = ( neutralFormat ) ? 0 : slice;
+	  CmmCpSubBlock* subBlock = m_cmmHit0Blocks[index];
+	  subBlock->setHits(slice, source, hits0[slice],
+	                           err0Bits.get(LVL1::DataError::Parity));
+	  subBlock = m_cmmHit1Blocks[index];
+	  subBlock->setHits(slice, source, hits1[slice],
+	                           err1Bits.get(LVL1::DataError::Parity));
+        }
+      }
+    }
+    DataVector<CmmCpSubBlock>::const_iterator cos = m_cmmHit0Blocks.begin();
+    for (; cos != m_cmmHit0Blocks.end(); ++cos) {
+      CmmCpSubBlock* const subBlock = *cos;
+      if ( !subBlock->pack()) {
+        msg(MSG::ERROR) << "CMM-Cp sub-block packing failed" << endreq;
+	return StatusCode::FAILURE;
+      }
+      if (debug) {
+        msg() << "CMM-Cp sub-block data words: "
+	      << subBlock->dataWords() << endreq;
+      }
+      subBlock->write(theROD);
+    }
+    cos = m_cmmHit1Blocks.begin();
+    for (; cos != m_cmmHit1Blocks.end(); ++cos) {
+      CmmCpSubBlock* const subBlock = *cos;
+      if ( !subBlock->pack()) {
+        msg(MSG::ERROR) << "CMM-Cp sub-block packing failed" << endreq;
+	return StatusCode::FAILURE;
+      }
+      if (debug) {
+        msg() << "CMM-Cp sub-block data words: "
+	      << subBlock->dataWords() << endreq;
+      }
+      subBlock->write(theROD);
+    }
+  }
+
+  // Fill the raw event
+
+  m_fea->fill(re, msg());
+
+  // Set ROD status words
+
+  //L1CaloRodStatus::setStatus(re, m_rodStatusMap, m_srcIdMap);
+
+  return StatusCode::SUCCESS;
+}
+
+// Return reference to vector with all possible Source Identifiers
+
+const std::vector<uint32_t>& CpByteStreamV1Tool::sourceIDs(
+                                               const std::string& sgKey)
+{
+  // Check if overlap tower channels wanted
+  const std::string flag("Overlap");
+  const std::string::size_type pos = sgKey.find(flag);
+  m_coreOverlap =
+   (pos == std::string::npos || pos != sgKey.length() - flag.length()) ? 0 : 1;
+
+  if (m_sourceIDs.empty()) {
+    const int maxCrates = m_crates + m_crateOffsetHw;
+    const int maxSlinks = m_srcIdMap->maxSlinks();
+    for (int hwCrate = m_crateOffsetHw; hwCrate < maxCrates; ++hwCrate) {
+      for (int slink = 0; slink < maxSlinks; ++slink) {
+        const int daqOrRoi = 0;
+        const uint32_t rodId = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+                                                             m_subDetector);
+        const uint32_t robId = m_srcIdMap->getRobID(rodId);
+        m_sourceIDs.push_back(robId);
+      }
+    }
+  }
+  return m_sourceIDs;
+}
+
+// Convert bytestream to given container type
+
+StatusCode CpByteStreamV1Tool::convertBs(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            const CollectionType collection)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Loop over ROB fragments
+
+  int robCount = 0;
+  std::set<uint32_t> dupCheck;
+  ROBIterator rob    = robFrags.begin();
+  ROBIterator robEnd = robFrags.end();
+  for (; rob != robEnd; ++rob) {
+
+    if (debug) {
+      ++robCount;
+      msg() << "Treating ROB fragment " << robCount << endreq;
+    }
+
+    // Skip fragments with ROB status errors
+
+    uint32_t robid = (*rob)->source_id();
+    if ((*rob)->nstatus() > 0) {
+      ROBPointer robData;
+      (*rob)->status(robData);
+      if (*robData != 0) {
+        m_errorTool->robError(robid, *robData);
+	if (debug) msg() << "ROB status error - skipping fragment" << endreq;
+	continue;
+      }
+    }
+
+    // Skip duplicate fragments
+
+    if (!dupCheck.insert(robid).second) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_DUPLICATE_ROB);
+      if (debug) msg() << "Skipping duplicate ROB fragment" << endreq;
+      continue;
+    }
+
+    // Unpack ROD data (slinks)
+
+    RODPointer payloadBeg;
+    RODPointer payload;
+    RODPointer payloadEnd;
+    (*rob)->rod_data(payloadBeg);
+    payloadEnd = payloadBeg + (*rob)->rod_ndata();
+    payload = payloadBeg;
+    if (payload == payloadEnd) {
+      if (debug) msg() << "ROB fragment empty" << endreq;
+      continue;
+    }
+
+    // Check identifier
+    const uint32_t sourceID = (*rob)->rod_source_id();
+    if (m_srcIdMap->getRobID(sourceID) != robid           ||
+        m_srcIdMap->subDet(sourceID)   != m_subDetector   ||
+	m_srcIdMap->daqOrRoi(sourceID) != 0               ||
+       (m_srcIdMap->slink(sourceID) != 0 && m_srcIdMap->slink(sourceID) != 2) ||
+	m_srcIdMap->crate(sourceID)    <  m_crateOffsetHw ||
+	m_srcIdMap->crate(sourceID)    >= m_crateOffsetHw + m_crates) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_ROD_ID);
+      if (debug) {
+        msg() << "Wrong source identifier in data: ROD "
+	      << MSG::hex << sourceID << "  ROB " << robid
+	      << MSG::dec << endreq;
+      }
+      continue;
+    }
+
+    // Check minor version
+    const int minorVersion = (*rob)->rod_version() & 0xffff;
+    if (minorVersion > m_srcIdMap->minorVersionPreLS1()) {
+      if (debug) msg() << "Skipping post-LS1 data" << endreq;
+      continue;
+    }
+    const int rodCrate = m_srcIdMap->crate(sourceID);
+    if (debug) {
+      msg() << "Treating crate " << rodCrate 
+            << " slink " << m_srcIdMap->slink(sourceID) << endreq;
+    }
+
+    // First word should be User Header
+    if ( !L1CaloUserHeader::isValid(*payload) ) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
+      if (debug) msg() << "Invalid or missing user header" << endreq;
+      continue;
+    }
+    L1CaloUserHeader userHeader(*payload);
+    userHeader.setVersion(minorVersion);
+    const int headerWords = userHeader.words();
+    if (headerWords != 1) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
+      if (debug) msg() << "Unexpected number of user header words: "
+                       << headerWords << endreq;
+      continue;
+    }
+    for (int i = 0; i < headerWords; ++i) ++payload;
+    // triggered slice offsets
+    int trigCpm = userHeader.cpm();
+    int trigCmm = userHeader.cpCmm();
+    if (debug) {
+      msg() << "Minor format version number: " << MSG::hex
+            << minorVersion << MSG::dec << endreq
+            << "CPM triggered slice offset: "  << trigCpm << endreq
+            << "CMM triggered slice offset: "  << trigCmm << endreq;
+    }
+    if (trigCpm != trigCmm) {
+      const int newTrig = (trigCpm > trigCmm) ? trigCpm : trigCmm;
+      trigCpm = newTrig;
+      trigCmm = newTrig;
+      if (debug) msg() << "Changed both offsets to " << newTrig << endreq;
+    }
+
+    // Loop over sub-blocks
+
+    m_rodErr = L1CaloSubBlock::ERROR_NONE;
+    while (payload != payloadEnd) {
+      
+      if (L1CaloSubBlock::wordType(*payload) != L1CaloSubBlock::HEADER) {
+        if (debug) msg() << "Unexpected data sequence" << endreq;
+	m_rodErr = L1CaloSubBlock::ERROR_MISSING_HEADER;
+	break;
+      }
+      if (L1CaloSubBlock::version(*payload) != 1) {                          // <<== CHECK
+        if (debug) msg() << "Skipping post-LS1 data" << endreq;
+	break;
+      }
+      if (CmmSubBlock::cmmBlock(*payload)) {
+        // CMM
+	if (CmmSubBlock::cmmType(*payload) == CmmSubBlock::CMM_CP) {
+	  m_cmmCpSubBlock->clear();
+          payload = m_cmmCpSubBlock->read(payload, payloadEnd);
+	  if (m_cmmCpSubBlock->crate() != rodCrate) {
+	    if (debug) msg() << "Inconsistent crate number in ROD source ID"
+	                     << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	    break;
+          }
+	  if (collection == CMM_CP_HITS) {
+	    decodeCmmCp(m_cmmCpSubBlock, trigCmm);
+	    if (m_rodErr != L1CaloSubBlock::ERROR_NONE) {
+	      if (debug) msg() << "decodeCmmCp failed" << endreq;
+	      break;
+	    }
+          }
+        } else {
+          if (debug) msg() << "Invalid CMM type in module field" << endreq;
+          m_rodErr = L1CaloSubBlock::ERROR_MODULE_NUMBER;
+	  break;
+        }
+      } else {
+        // CPM
+	m_cpmSubBlock->clear();
+        payload = m_cpmSubBlock->read(payload, payloadEnd);
+	if (m_cpmSubBlock->crate() != rodCrate) {
+	  if (debug) msg() << "Inconsistent crate number in ROD source ID"
+	                   << endreq;
+	  m_rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	  break;
+        }
+	if (collection == CPM_TOWERS || collection == CPM_HITS) {
+	  decodeCpm(m_cpmSubBlock, trigCpm, collection);
+	  if (m_rodErr != L1CaloSubBlock::ERROR_NONE) {
+	    if (debug) msg() << "decodeCpm failed" << endreq;
+	    break;
+	  }
+        }
+      }
+    }
+    if (m_rodErr != L1CaloSubBlock::ERROR_NONE) 
+                                       m_errorTool->rodError(robid, m_rodErr);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Unpack CMM-CP sub-block
+
+void CpByteStreamV1Tool::decodeCmmCp(CmmCpSubBlock* subBlock, int trigCmm)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  const int hwCrate    = subBlock->crate();
+  const int module     = subBlock->cmmPosition();
+  const int firmware   = subBlock->cmmFirmware();
+  const int summing    = subBlock->cmmSumming();
+  const int timeslices = subBlock->timeslices();
+  const int sliceNum   = subBlock->slice();
+  if (debug) {
+    msg() << "CMM-CP: Crate "  << hwCrate
+          << "  Module "       << module
+	  << "  Firmware "     << firmware
+	  << "  Summing "      << summing
+          << "  Total slices " << timeslices
+          << "  Slice "        << sliceNum    << endreq;
+  }
+  if (timeslices <= trigCmm) {
+    if (debug) msg() << "Triggered CMM slice from header "
+                     << "inconsistent with number of slices: "
+                     << trigCmm << ", " << timeslices << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  if (timeslices <= sliceNum) {
+    if (debug) msg() << "Total slices inconsistent with slice number: "
+                     << timeslices << ", " << sliceNum << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  // Unpack sub-block
+  if (subBlock->dataWords() && !subBlock->unpack()) {
+    if (debug) {
+      std::string errMsg(subBlock->unpackErrorMsg());
+      msg() << "CMM-CP sub-block unpacking failed: " << errMsg << endreq;
+    }
+    m_rodErr = subBlock->unpackErrorCode();
+    return;
+  }
+
+  // Retrieve required data
+
+  const bool neutralFormat = subBlock->format() == L1CaloSubBlock::NEUTRAL;
+  const uint32_t subStatus = subBlock->subStatus();
+  const int crate    = hwCrate - m_crateOffsetHw;
+  const int swCrate  = crate   + m_crateOffsetSw;
+  const int maxSid   = CmmCpSubBlock::MAX_SOURCE_ID;
+  const int sliceBeg = ( neutralFormat ) ? 0          : sliceNum;
+  const int sliceEnd = ( neutralFormat ) ? timeslices : sliceNum + 1;
+  for (int slice = sliceBeg; slice < sliceEnd; ++slice) {
+
+    // Jet hit counts
+
+    for (int source = 1; source < maxSid; ++source) {
+      int dataID = source;
+      if (source > m_modules) {
+        if (summing == CmmSubBlock::CRATE &&
+	    source != CmmCpSubBlock::LOCAL) continue;
+	switch (source) {
+	  case CmmCpSubBlock::LOCAL:
+	    dataID = LVL1::CMMCPHits::LOCAL;
+	    break;
+	  case CmmCpSubBlock::REMOTE_0:
+	    dataID = LVL1::CMMCPHits::REMOTE_0;
+	    break;
+	  case CmmCpSubBlock::REMOTE_1:
+	    dataID = LVL1::CMMCPHits::REMOTE_1;
+	    break;
+	  case CmmCpSubBlock::REMOTE_2:
+	    dataID = LVL1::CMMCPHits::REMOTE_2;
+	    break;
+	  case CmmCpSubBlock::TOTAL:
+	    dataID = LVL1::CMMCPHits::TOTAL;
+	    break;
+          default:
+	    continue;
+        }
+      }
+      const unsigned int hits = subBlock->hits(slice, source);
+      int err = subBlock->hitsError(slice, source);
+      LVL1::DataError errorBits;
+      errorBits.set(LVL1::DataError::Parity, err & 0x1);
+      errorBits.set(LVL1::DataError::SubStatusWord, subStatus);
+      err = errorBits.error();
+      if (hits || err) {
+        LVL1::CMMCPHits* ch = findCmmCpHits(crate, dataID);
+	if ( ! ch ) {   // create new CMM hits
+	  m_hitsVec0.assign(timeslices, 0);
+	  m_hitsVec1.assign(timeslices, 0);
+	  m_errVec0.assign(timeslices, 0);
+	  m_errVec1.assign(timeslices, 0);
+	  if (module == CmmSubBlock::RIGHT) {
+	    m_hitsVec0[slice] = hits;
+	    m_errVec0[slice]  = err;
+	  } else {
+	    m_hitsVec1[slice] = hits;
+	    m_errVec1[slice]  = err;
+	  }
+	  ch = new LVL1::CMMCPHits(swCrate, dataID, m_hitsVec0, m_hitsVec1,
+	                                    m_errVec0, m_errVec1, trigCmm);
+          const int key = crate*100 + dataID;
+	  m_cmmHitsMap.insert(std::make_pair(key, ch));
+	  m_cmmHitCollection->push_back(ch);
+        } else {
+	  m_hitsVec0 = ch->HitsVec0();
+	  m_hitsVec1 = ch->HitsVec1();
+	  m_errVec0  = ch->ErrorVec0();
+	  m_errVec1  = ch->ErrorVec1();
+	  const int nsl = m_hitsVec0.size();
+	  if (timeslices != nsl) {
+	    if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                     << endreq;
+            m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    return;
+          }
+	  if ((module == CmmSubBlock::RIGHT && (m_hitsVec0[slice] != 0 ||
+	       m_errVec0[slice] != 0)) || (module == CmmSubBlock::LEFT &&
+	       (m_hitsVec1[slice] != 0 || m_errVec1[slice]  != 0))) {
+            if (debug) msg() << "Duplicate data for slice " << slice << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    return;
+          }
+	  if (module == CmmSubBlock::RIGHT) {
+	    m_hitsVec0[slice] = hits;
+	    m_errVec0[slice]  = err;
+	  } else {
+	    m_hitsVec1[slice] = hits;
+	    m_errVec1[slice]  = err;
+	  }
+	  ch->addHits(m_hitsVec0, m_hitsVec1, m_errVec0, m_errVec1);
+        }
+      }
+    }
+  }
+  
+  return;
+}
+
+// Unpack CPM sub-block
+
+void CpByteStreamV1Tool::decodeCpm(CpmSubBlockV1* subBlock,
+                                   int trigCpm, const CollectionType collection)
+{
+  const bool debug   = msgLvl(MSG::DEBUG);
+  const bool verbose = msgLvl(MSG::VERBOSE);
+  if (debug) msg(MSG::DEBUG);
+
+  const int hwCrate    = subBlock->crate();
+  const int module     = subBlock->module();
+  const int timeslices = subBlock->timeslices();
+  const int sliceNum   = subBlock->slice();
+  if (debug) {
+    msg() << "CPM: Crate "     << hwCrate
+          << "  Module "       << module
+          << "  Total slices " << timeslices
+          << "  Slice "        << sliceNum    << endreq;
+  }
+  if (module < 1 || module > m_modules) {
+    if (debug) msg() << "Unexpected module number: " << module << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_MODULE_NUMBER;
+    return;
+  }
+  if (timeslices <= trigCpm) {
+    if (debug) msg() << "Triggered CPM slice from header "
+                     << "inconsistent with number of slices: "
+                     << trigCpm << ", " << timeslices << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  if (timeslices <= sliceNum) {
+    if (debug) msg() << "Total slices inconsistent with slice number: "
+                     << timeslices << ", " << sliceNum << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  // Unpack sub-block
+  if (subBlock->dataWords() && !subBlock->unpack()) {
+    if (debug) {
+      std::string errMsg(subBlock->unpackErrorMsg());
+      msg() << "CPM sub-block unpacking failed: " << errMsg << endreq;
+    }
+    m_rodErr = subBlock->unpackErrorCode();
+    return;
+  }
+
+  // Retrieve required data
+
+  const bool neutralFormat = subBlock->format() == L1CaloSubBlock::NEUTRAL;
+  LVL1::DataError dErr;
+  dErr.set(LVL1::DataError::SubStatusWord, subBlock->subStatus());
+  const int ssError  = dErr.error();
+  const int crate    = hwCrate - m_crateOffsetHw;
+  const int swCrate  = crate   + m_crateOffsetSw;
+  const int sliceBeg = ( neutralFormat ) ? 0          : sliceNum;
+  const int sliceEnd = ( neutralFormat ) ? timeslices : sliceNum + 1;
+  for (int slice = sliceBeg; slice < sliceEnd; ++slice) {
+
+    if (collection == CPM_TOWERS) {
+
+      // Loop over tower channels and fill CPM towers
+
+      for (int chan = 0; chan < m_channels; ++chan) {
+        if (!ssError && !subBlock->anyTowerData(chan)) continue;
+	const int em     = subBlock->emData(slice, chan);
+	const int had    = subBlock->hadData(slice, chan);
+	const int emErr  = subBlock->emError(slice, chan);
+	const int hadErr = subBlock->hadError(slice, chan);
+	int emErr1 = ssError;
+	if (emErr) {
+	  LVL1::DataError emErrBits(emErr1);
+	  emErrBits.set(LVL1::DataError::Parity, emErr & 0x1);
+	  emErrBits.set(LVL1::DataError::LinkDown, (emErr >> 1) & 0x1);
+	  emErr1 = emErrBits.error();
+	}
+	int hadErr1 = ssError;
+	if (hadErr) {
+	  LVL1::DataError hadErrBits(hadErr1);
+	  hadErrBits.set(LVL1::DataError::Parity, hadErr & 0x1);
+	  hadErrBits.set(LVL1::DataError::LinkDown, (hadErr >> 1) & 0x1);
+	  hadErr1 = hadErrBits.error();
+	}
+        if (em || had || emErr1 || hadErr1) {
+	  double eta = 0.;
+	  double phi = 0.;
+	  int layer = 0;
+	  if (m_cpmMaps->mapping(crate, module, chan, eta, phi, layer)) {
+	    if (layer == m_coreOverlap) {
+	      const unsigned int key = m_towerKey->ttKey(phi, eta);
+	      LVL1::CPMTower* tt = findCpmTower(key);
+	      if ( ! tt ) {   // create new CPM tower
+	        m_emVec.assign(timeslices, 0);
+	        m_hadVec.assign(timeslices, 0);
+	        m_emErrVec.assign(timeslices, 0);
+	        m_hadErrVec.assign(timeslices, 0);
+	        m_emVec[slice]     = em;
+	        m_hadVec[slice]    = had;
+	        m_emErrVec[slice]  = emErr1;
+	        m_hadErrVec[slice] = hadErr1;
+	        tt = new LVL1::CPMTower(phi, eta, m_emVec, m_emErrVec,
+	                                          m_hadVec, m_hadErrVec, trigCpm);
+	        m_ttMap.insert(std::make_pair(key, tt));
+	        m_ttCollection->push_back(tt);
+              } else {
+	        m_emVec     = tt->emEnergyVec();
+	        m_hadVec    = tt->hadEnergyVec();
+	        m_emErrVec  = tt->emErrorVec();
+	        m_hadErrVec = tt->hadErrorVec();
+		const int nsl = m_emVec.size();
+	        if (timeslices != nsl) {
+	          if (debug) {
+		    msg() << "Inconsistent number of slices in sub-blocks"
+	                  << endreq;
+	          }
+                  m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	          return;
+                }
+		if (m_emVec[slice]    != 0 || m_hadVec[slice]    != 0 ||
+		    m_emErrVec[slice] != 0 || m_hadErrVec[slice] != 0) {
+                  if (debug) msg() << "Duplicate data for slice "
+		                   << slice << endreq;
+	          m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	          return;
+                }
+	        m_emVec[slice]     = em;
+	        m_hadVec[slice]    = had;
+	        m_emErrVec[slice]  = emErr1;
+	        m_hadErrVec[slice] = hadErr1;
+	        tt->fill(m_emVec, m_emErrVec, m_hadVec, m_hadErrVec, trigCpm);
+	      }
+	    }
+          } else if (verbose && (em || had || emErr || hadErr)) {
+	    msg(MSG::VERBOSE) << "Non-zero data but no channel mapping for channel "
+	                      << chan << endreq;
+	    msg(MSG::DEBUG);
+          }
+        } else if (verbose) {
+	  msg(MSG::VERBOSE) << "No CPM tower data for channel "
+	                    << chan << " slice " << slice << endreq;
+	  msg(MSG::DEBUG);
+        }
+      }
+    } else if (collection == CPM_HITS) {
+
+      // Get CPM hits
+
+      const unsigned int hits0 = subBlock->hits0(slice);
+      const unsigned int hits1 = subBlock->hits1(slice);
+      if (hits0 || hits1) {
+        LVL1::CPMHits* ch = findCpmHits(crate, module);
+	if ( ! ch ) {   // create new CPM hits
+	  m_hitsVec0.assign(timeslices, 0);
+	  m_hitsVec1.assign(timeslices, 0);
+	  m_hitsVec0[slice] = hits0;
+	  m_hitsVec1[slice] = hits1;
+	  ch = new LVL1::CPMHits(swCrate, module, m_hitsVec0, m_hitsVec1, trigCpm);
+	  m_hitsMap.insert(std::make_pair(crate*m_modules+module-1, ch));
+	  m_hitCollection->push_back(ch);
+        } else {
+	  m_hitsVec0 = ch->HitsVec0();
+	  m_hitsVec1 = ch->HitsVec1();
+	  const int nsl = m_hitsVec0.size();
+	  if (timeslices != nsl) {
+	    if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                     << endreq;
+            m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    return;
+          }
+	  if (m_hitsVec0[slice] != 0 || m_hitsVec1[slice] != 0) {
+            if (debug) msg() << "Duplicate data for slice " << slice << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    return;
+          }
+	  m_hitsVec0[slice] = hits0;
+	  m_hitsVec1[slice] = hits1;
+	  ch->addHits(m_hitsVec0, m_hitsVec1);
+        }
+      } else if (verbose) {
+        msg(MSG::VERBOSE) << "No CPM hits data for crate/module/slice "
+                          << hwCrate << "/" << module << "/" << slice
+   			  << endreq;
+        msg(MSG::DEBUG);
+      }
+    }
+  }
+  return;
+}
+
+// Find a CPM tower for given key
+
+LVL1::CPMTower* CpByteStreamV1Tool::findCpmTower(const unsigned int key)
+{
+  LVL1::CPMTower* tt = 0;
+  CpmTowerMap::const_iterator mapIter;
+  mapIter = m_ttMap.find(key);
+  if (mapIter != m_ttMap.end()) tt = mapIter->second;
+  return tt;
+}
+
+// Find CPM hits for given crate, module
+
+LVL1::CPMHits* CpByteStreamV1Tool::findCpmHits(const int crate, const int module)
+{
+  LVL1::CPMHits* hits = 0;
+  CpmHitsMap::const_iterator mapIter;
+  mapIter = m_hitsMap.find(crate*m_modules + module - 1);
+  if (mapIter != m_hitsMap.end()) hits = mapIter->second;
+  return hits;
+}
+
+// Find CMM-CP hits for given crate, dataID
+
+LVL1::CMMCPHits* CpByteStreamV1Tool::findCmmCpHits(const int crate,
+                                                   const int dataID)
+{
+  LVL1::CMMCPHits* hits = 0;
+  CmmCpHitsMap::const_iterator mapIter;
+  mapIter = m_cmmHitsMap.find(crate*100 + dataID);
+  if (mapIter != m_cmmHitsMap.end()) hits = mapIter->second;
+  return hits;
+}
+
+// Set up CPM tower map
+
+void CpByteStreamV1Tool::setupCpmTowerMap(const CpmTowerCollection*
+                                                          const ttCollection)
+{
+  m_ttMap.clear();
+  if (ttCollection) {
+    CpmTowerCollection::const_iterator pos  = ttCollection->begin();
+    CpmTowerCollection::const_iterator pose = ttCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::CPMTower* const tt = *pos;
+      const unsigned int key = m_towerKey->ttKey(tt->phi(), tt->eta());
+      m_ttMap.insert(std::make_pair(key, tt));
+    }
+  }
+}
+
+// Set up CPM hits map
+
+void CpByteStreamV1Tool::setupCpmHitsMap(const CpmHitsCollection*
+                                                        const hitCollection)
+{
+  m_hitsMap.clear();
+  if (hitCollection) {
+    CpmHitsCollection::const_iterator pos  = hitCollection->begin();
+    CpmHitsCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::CPMHits* const hits = *pos;
+      const int crate = hits->crate() - m_crateOffsetSw;
+      const int key   = m_modules * crate + hits->module() - 1;
+      m_hitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Set up CMM-CP hits map
+
+void CpByteStreamV1Tool::setupCmmCpHitsMap(const CmmCpHitsCollection*
+                                                          const hitCollection)
+{
+  m_cmmHitsMap.clear();
+  if (hitCollection) {
+    CmmCpHitsCollection::const_iterator pos  = hitCollection->begin();
+    CmmCpHitsCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::CMMCPHits* const hits = *pos;
+      const int crate = hits->crate() - m_crateOffsetSw;
+      const int key   = crate*100 + hits->dataID();
+      m_cmmHitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Get number of slices and triggered slice offset for next slink
+
+bool CpByteStreamV1Tool::slinkSlices(const int crate, const int module,
+                  const int modulesPerSlink, int& timeslices, int& trigCpm)
+{
+  int slices = -1;
+  int trigC  = m_dfltSlices/2;
+  for (int mod = module; mod < module + modulesPerSlink; ++mod) {
+    for (int chan = 0; chan < m_channels; ++chan) {
+      double eta = 0.;
+      double phi = 0.;
+      int layer = 0;
+      if ( !m_cpmMaps->mapping(crate, mod, chan, eta, phi, layer)) continue;
+      const unsigned int key = m_towerKey->ttKey(phi, eta);
+      const LVL1::CPMTower* const tt = findCpmTower(key);
+      if ( !tt ) continue;
+      const int numdat = 4;
+      std::vector<int> sums(numdat);
+      std::vector<int> sizes(numdat);
+      sums[0] = std::accumulate((tt->emEnergyVec()).begin(),
+                                (tt->emEnergyVec()).end(), 0);
+      sums[1] = std::accumulate((tt->hadEnergyVec()).begin(),
+                                (tt->hadEnergyVec()).end(), 0);
+      sums[2] = std::accumulate((tt->emErrorVec()).begin(),
+                                (tt->emErrorVec()).end(), 0);
+      sums[3] = std::accumulate((tt->hadErrorVec()).begin(),
+                                (tt->hadErrorVec()).end(), 0);
+      sizes[0] = (tt->emEnergyVec()).size();
+      sizes[1] = (tt->hadEnergyVec()).size();
+      sizes[2] = (tt->emErrorVec()).size();
+      sizes[3] = (tt->hadErrorVec()).size();
+      const int peak = tt->peak();
+      for (int i = 0; i < numdat; ++i) {
+        if (sums[i] == 0) continue;
+        if (slices < 0) {
+	  slices = sizes[i];
+	  trigC  = peak;
+	} else if (slices != sizes[i] || trigC != peak) return false;
+      }
+    }
+    const LVL1::CPMHits* const hits = findCpmHits(crate, mod);
+    if (hits) {
+      const int numdat = 2;
+      std::vector<unsigned int> sums(numdat);
+      std::vector<int> sizes(numdat);
+      sums[0] = std::accumulate((hits->HitsVec0()).begin(),
+                                         (hits->HitsVec0()).end(), 0);
+      sums[1] = std::accumulate((hits->HitsVec1()).begin(),
+                                         (hits->HitsVec1()).end(), 0);
+      sizes[0] = (hits->HitsVec0()).size();
+      sizes[1] = (hits->HitsVec1()).size();
+      const int peak = hits->peak();
+      for (int i = 0; i < numdat; ++i) {
+        if (sums[i] == 0) continue;
+        if (slices < 0) {
+	  slices = sizes[i];
+	  trigC  = peak;
+        } else if (slices != sizes[i] || trigC != peak) return false;
+      }
+    }
+  }
+  // CMM last slink of crate
+  if (module/modulesPerSlink == m_slinks - 1) {
+    const int maxDataID = LVL1::CMMCPHits::MAXID;
+    for (int dataID = 0; dataID < maxDataID; ++dataID) {
+      const int numdat = 4;
+      std::vector<unsigned int> sums(numdat);
+      std::vector<int> sizes(numdat);
+      const LVL1::CMMCPHits* const hits = findCmmCpHits(crate, dataID);
+      if (hits) {
+        sums[0] = std::accumulate((hits->HitsVec0()).begin(),
+                                             (hits->HitsVec0()).end(), 0);
+        sums[1] = std::accumulate((hits->HitsVec1()).begin(),
+                                             (hits->HitsVec1()).end(), 0);
+        sums[2] = std::accumulate((hits->ErrorVec0()).begin(),
+                                             (hits->ErrorVec0()).end(), 0);
+        sums[3] = std::accumulate((hits->ErrorVec1()).begin(),
+                                             (hits->ErrorVec1()).end(), 0);
+        sizes[0] = (hits->HitsVec0()).size();
+        sizes[1] = (hits->HitsVec1()).size();
+        sizes[2] = (hits->ErrorVec0()).size();
+        sizes[3] = (hits->ErrorVec1()).size();
+        const int peak = hits->peak();
+        for (int i = 0; i < numdat; ++i) {
+          if (sums[i] == 0) continue;
+          if (slices < 0) {
+	    slices = sizes[i];
+	    trigC  = peak;
+          } else if (slices != sizes[i] || trigC != peak) return false;
+        }
+      }
+    }
+  }
+  if (slices < 0) slices = m_dfltSlices;
+  timeslices = slices;
+  trigCpm    = trigC;
+  return true;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV1Tool.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV1Tool.h
new file mode 100755
index 0000000000000000000000000000000000000000..f1653634171e9808c66f199dcccfb2ce552232b6
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV1Tool.h
@@ -0,0 +1,210 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPBYTESTREAMV1TOOL_H
+#define TRIGT1CALOBYTESTREAM_CPBYTESTREAMV1TOOL_H
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+#include "ByteStreamData/RawEvent.h"
+#include "DataModel/DataVector.h"
+#include "eformat/SourceIdentifier.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class IInterface;
+class InterfaceID;
+class StatusCode;
+
+template <typename> class FullEventAssembler;
+
+namespace LVL1 {
+  class CMMCPHits;
+  class CPMHits;
+  class CPMTower;
+  class CPBSCollectionV1;
+  class IL1CaloMappingTool;
+  class TriggerTowerKey;
+}
+
+namespace LVL1BS {
+
+class CmmCpSubBlock;
+class CpmSubBlockV1;
+class L1CaloErrorByteStreamTool;
+class L1CaloSrcIdMap;
+
+/** Tool to perform ROB fragments to CPM towers, CPM hits and CMM-CP hits,
+ *  and CP container to raw data conversions.
+ *
+ *  Based on ROD document version 1_09h.
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpByteStreamV1Tool : public AthAlgTool {
+
+ public:
+   CpByteStreamV1Tool(const std::string& type, const std::string& name,
+                                               const IInterface* parent);
+   virtual ~CpByteStreamV1Tool();
+
+   /// AlgTool InterfaceID
+   static const InterfaceID& interfaceID();
+
+   virtual StatusCode initialize();
+   virtual StatusCode finalize();
+
+   /// Convert ROB fragments to CPM towers
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::CPMTower>* ttCollection);
+   /// Convert ROB fragments to CPM hits
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::CPMHits>* hitCollection);
+   /// Convert ROB fragments to CMM-CP hits
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::CMMCPHits>* hitCollection);
+
+   /// Convert CP Container to bytestream
+   StatusCode convert(const LVL1::CPBSCollectionV1* cp, RawEventWrite* re);
+
+   /// Return reference to vector with all possible Source Identifiers
+   const std::vector<uint32_t>& sourceIDs(const std::string& sgKey);
+
+ private:
+
+   enum CollectionType { CPM_TOWERS, CPM_HITS, CMM_CP_HITS };
+
+   typedef DataVector<LVL1::CPMTower>                    CpmTowerCollection;
+   typedef DataVector<LVL1::CPMHits>                     CpmHitsCollection;
+   typedef DataVector<LVL1::CMMCPHits>                   CmmCpHitsCollection;
+   typedef std::map<unsigned int, LVL1::CPMTower*>       CpmTowerMap;
+   typedef std::map<int, LVL1::CPMHits*>                 CpmHitsMap;
+   typedef std::map<int, LVL1::CMMCPHits*>               CmmCpHitsMap;
+   typedef IROBDataProviderSvc::VROBFRAG::const_iterator ROBIterator;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      ROBPointer;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      RODPointer;
+
+   /// Convert bytestream to given container type
+   StatusCode convertBs(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                        CollectionType collection);
+   /// Unpack CMM-CP sub-block
+   void decodeCmmCp(CmmCpSubBlock* subBlock, int trigCmm);
+   /// Unpack CPM sub-block
+   void decodeCpm(CpmSubBlockV1* subBlock, int trigCpm, CollectionType collection);
+
+   /// Find a CPM tower for given key
+   LVL1::CPMTower*  findCpmTower(unsigned int key);
+   /// Find CPM hits for given crate, module
+   LVL1::CPMHits*   findCpmHits(int crate, int module);
+   /// Find CMM-CP hits for given crate, data ID
+   LVL1::CMMCPHits* findCmmCpHits(int crate, int dataID);
+
+   /// Set up CPM tower map
+   void setupCpmTowerMap(const CpmTowerCollection* ttCollection);
+   /// Set up CPM hits map
+   void setupCpmHitsMap(const CpmHitsCollection* hitCollection);
+   /// Set up CMM-CP hits map
+   void setupCmmCpHitsMap(const CmmCpHitsCollection* hitCollection);
+
+   /// Get number of slices and triggered slice offset for next slink
+   bool slinkSlices(int crate, int module, int modulesPerSlink,
+                    int& timeslices, int& trigJem);
+
+   /// Channel mapping tool
+   ToolHandle<LVL1::IL1CaloMappingTool> m_cpmMaps;
+   /// Error collection tool
+   ToolHandle<LVL1BS::L1CaloErrorByteStreamTool> m_errorTool;
+
+   /// Hardware crate number offset
+   int m_crateOffsetHw;
+   /// Software crate number offset
+   int m_crateOffsetSw;
+   /// Sub_block header version
+   int m_version;
+   /// Data compression format
+   int m_dataFormat;
+   /// Number of channels per module
+   int m_channels;
+   /// Number of crates
+   int m_crates;
+   /// Number of CPM modules per crate
+   int m_modules;
+   /// Number of slinks per crate when writing out bytestream
+   int m_slinks;
+   /// Default number of slices in simulation
+   int m_dfltSlices;
+   /// Force number of slices in bytestream
+   int m_forceSlices;
+   /// Minimum crate number when writing out bytestream
+   int m_crateMin;
+   /// Maximum crate number when writing out bytestream
+   int m_crateMax;
+   /// Tower channels to accept (1=Core, 2=Overlap)
+   int m_coreOverlap;
+   /// Unpacking error code
+   unsigned int m_rodErr;
+   /// ROB source IDs
+   std::vector<uint32_t> m_sourceIDs;
+   /// Sub-detector type
+   eformat::SubDetector m_subDetector;
+   /// Source ID converter
+   L1CaloSrcIdMap* m_srcIdMap;
+   /// Trigger tower key provider
+   LVL1::TriggerTowerKey* m_towerKey;
+   /// CPM sub-block for unpacking
+   CpmSubBlockV1* m_cpmSubBlock;
+   /// CMM-CP sub-block for unpacking
+   CmmCpSubBlock* m_cmmCpSubBlock;
+   /// Hits0 vector for unpacking
+   std::vector<unsigned int> m_hitsVec0;
+   /// Hits1 vector for unpacking
+   std::vector<unsigned int> m_hitsVec1;
+   /// Error0 vector for unpacking
+   std::vector<int> m_errVec0;
+   /// Error1 vector for unpacking
+   std::vector<int> m_errVec1;
+   /// EM data vector for unpacking
+   std::vector<int> m_emVec;
+   /// Had data vector for unpacking
+   std::vector<int> m_hadVec;
+   /// EM error data vector for unpacking
+   std::vector<int> m_emErrVec;
+   /// Had error data vector for unpacking
+   std::vector<int> m_hadErrVec;
+   /// Vector for current CPM sub-blocks
+   DataVector<CpmSubBlockV1> m_cpmBlocks;
+   /// Vector for current CMM-CP hit0 sub-blocks
+   DataVector<CmmCpSubBlock> m_cmmHit0Blocks;
+   /// Vector for current CMM-CP hit1 sub-blocks
+   DataVector<CmmCpSubBlock> m_cmmHit1Blocks;
+   /// Current CPM tower collection
+   CpmTowerCollection*  m_ttCollection;
+   /// Current CPM hits collection
+   CpmHitsCollection*   m_hitCollection;
+   /// Current CMM-CP hits collection
+   CmmCpHitsCollection* m_cmmHitCollection;
+   /// CPM tower map
+   CpmTowerMap  m_ttMap;
+   /// CPM hits map
+   CpmHitsMap   m_hitsMap;
+   /// CMM-CP hits map
+   CmmCpHitsMap m_cmmHitsMap;
+   /// ROD Status words
+   std::vector<uint32_t>* m_rodStatus;
+   /// ROD status map
+   std::map<uint32_t, std::vector<uint32_t>* > m_rodStatusMap;
+   /// Event assembler
+   FullEventAssembler<L1CaloSrcIdMap>* m_fea;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV2Cnv.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV2Cnv.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..d648bf2d3b5b44056fb97dcef9fbc19e6187c8cc
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV2Cnv.cxx
@@ -0,0 +1,115 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IByteStreamEventAccess.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "DataModel/DataVector.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/IRegistry.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "TrigT1CaloEvent/CPBSCollectionV2.h"
+
+#include "CpByteStreamV2Cnv.h"
+#include "CpByteStreamV2Tool.h"
+
+namespace LVL1BS {
+
+CpByteStreamV2Cnv::CpByteStreamV2Cnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("CpByteStreamV2Cnv"),
+      m_tool("LVL1BS::CpByteStreamV2Tool/CpByteStreamV2Tool"),
+      m_ByteStreamEventAccess("ByteStreamCnvSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+CpByteStreamV2Cnv::~CpByteStreamV2Cnv()
+{
+}
+
+// CLID
+
+const CLID& CpByteStreamV2Cnv::classID()
+{
+  return ClassID_traits<LVL1::CPBSCollectionV2>::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode CpByteStreamV2Cnv::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << " - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  //Get ByteStreamCnvSvc
+  sc = m_ByteStreamEventAccess.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_ByteStreamEventAccess << endreq;
+    return sc;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_ByteStreamEventAccess << endreq;
+  }
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return sc;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  return StatusCode::SUCCESS;
+}
+
+// createRep should create the bytestream from RDOs.
+
+StatusCode CpByteStreamV2Cnv::createRep( DataObject* pObj,
+                                         IOpaqueAddress*& pAddr )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createRep() called" << endreq;
+
+  RawEventWrite* re = m_ByteStreamEventAccess->getRawEvent();
+
+  LVL1::CPBSCollectionV2* cp = 0;
+  if( !SG::fromStorable( pObj, cp ) ) {
+    m_log << MSG::ERROR << " Cannot cast to CPBSCollectionV2" << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = pObj->registry()->name();
+
+  ByteStreamAddress* addr = new ByteStreamAddress( classID(), nm, "" );
+
+  pAddr = addr;
+
+  // Convert to ByteStream
+  return m_tool->convert( cp, re );
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV2Cnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV2Cnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..f7fc65683ff973980b9174dc14a29e9a76e2a028
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV2Cnv.h
@@ -0,0 +1,76 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPBYTESTREAMV2CNV_H
+#define TRIGT1CALOBYTESTREAM_CPBYTESTREAMV2CNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IByteStreamEventAccess;
+class IOpaqueAddress;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class CpByteStreamV2Tool;
+
+/** ByteStream converter for CP container post LS1
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpByteStreamV2Cnv: public Converter {
+
+  friend class CnvFactory<CpByteStreamV2Cnv>;
+
+protected:
+
+  CpByteStreamV2Cnv(ISvcLocator* svcloc);
+
+public:
+
+  ~CpByteStreamV2Cnv();
+
+  virtual StatusCode initialize();
+  /// Create ByteStream from Cp Container
+  virtual StatusCode createRep(DataObject* pObj, IOpaqueAddress*& pAddr);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::CpByteStreamV2Tool> m_tool;
+
+  /// Service for writing bytestream
+  ServiceHandle<IByteStreamEventAccess> m_ByteStreamEventAccess;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV2Tool.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV2Tool.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..54f6a7f72dde59bf88583b3bce7a97200096443d
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV2Tool.cxx
@@ -0,0 +1,1204 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <numeric>
+#include <set>
+#include <utility>
+
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "ByteStreamCnvSvcBase/FullEventAssembler.h"
+
+#include "TrigT1CaloEvent/CMXCPHits.h"
+#include "TrigT1CaloEvent/CMXCPTob.h"
+#include "TrigT1CaloEvent/CPMTower.h"
+#include "TrigT1CaloEvent/CPBSCollectionV2.h"
+#include "TrigT1CaloUtils/DataError.h"
+#include "TrigT1CaloUtils/TriggerTowerKey.h"
+#include "TrigT1CaloMappingToolInterfaces/IL1CaloMappingTool.h"
+
+#include "CmxCpSubBlock.h"
+#include "CmxSubBlock.h"
+#include "CpmSubBlockV2.h"
+#include "L1CaloErrorByteStreamTool.h"
+#include "L1CaloSrcIdMap.h"
+#include "L1CaloSubBlock.h"
+#include "L1CaloUserHeader.h"
+#include "ModifySlices.h"
+
+#include "CpByteStreamV2Tool.h"
+
+namespace LVL1BS {
+
+// Interface ID
+
+static const InterfaceID IID_ICpByteStreamV2Tool("CpByteStreamV2Tool", 1, 1);
+
+const InterfaceID& CpByteStreamV2Tool::interfaceID()
+{
+  return IID_ICpByteStreamV2Tool;
+}
+
+// Constructor
+
+CpByteStreamV2Tool::CpByteStreamV2Tool(const std::string& type,
+                                       const std::string& name,
+		  		       const IInterface*  parent)
+   : AthAlgTool(type, name, parent),
+     m_cpmMaps("LVL1::CpmMappingTool/CpmMappingTool"),
+     m_errorTool("LVL1BS::L1CaloErrorByteStreamTool/L1CaloErrorByteStreamTool"),
+     m_channels(80), m_crates(4), m_modules(14), m_cmxs(2), m_maxTobs(5),
+     m_chips(16), m_locs(4),
+     m_coreOverlap(0), m_subDetector(eformat::TDAQ_CALO_CLUSTER_PROC_DAQ),
+     m_srcIdMap(0), m_towerKey(0), m_cpmSubBlock(0), m_cmxCpSubBlock(0),
+     m_rodStatus(0), m_fea(0)
+{
+  declareInterface<CpByteStreamV2Tool>(this);
+
+  declareProperty("CpmMappingTool", m_cpmMaps,
+                  "Crate/Module/Channel to Eta/Phi/Layer mapping tool");
+  declareProperty("ErrorTool", m_errorTool,
+                  "Tool to collect errors for monitoring");
+
+  declareProperty("CrateOffsetHw",  m_crateOffsetHw  = 8,
+                  "Offset of CP crate numbers in bytestream");
+  declareProperty("CrateOffsetSw",  m_crateOffsetSw  = 0,
+                  "Offset of CP crate numbers in RDOs");
+
+  // Properties for reading bytestream only
+  declareProperty("ROBSourceIDs",       m_sourceIDs,
+                  "ROB fragment source identifiers");
+
+  // Properties for writing bytestream only
+  declareProperty("DataVersion",    m_version     = 2,                //  <<== CHECK
+                  "Format version number in sub-block header");
+  declareProperty("DataFormat",     m_dataFormat  = 1,
+                  "Format identifier (0-1) in sub-block header");
+  declareProperty("SlinksPerCrate", m_slinks      = 2,
+                  "The number of S-Links per crate");
+  declareProperty("SimulSlices",    m_dfltSlices  = 1,
+                  "The number of slices in the simulation");
+  declareProperty("ForceSlices",    m_forceSlices = 0,
+                  "If >0, the number of slices in bytestream");
+  declareProperty("CrateMin",       m_crateMin = 0,
+                  "Minimum crate number, allows partial output");
+  declareProperty("CrateMax",       m_crateMax = m_crates-1,
+                  "Maximum crate number, allows partial output");
+
+}
+
+// Destructor
+
+CpByteStreamV2Tool::~CpByteStreamV2Tool()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode CpByteStreamV2Tool::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = m_cpmMaps.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_cpmMaps << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_cpmMaps << endreq;
+
+  sc = m_errorTool.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_errorTool << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_errorTool << endreq;
+
+  m_srcIdMap      = new L1CaloSrcIdMap();
+  m_towerKey      = new LVL1::TriggerTowerKey();
+  m_cpmSubBlock   = new CpmSubBlockV2();
+  m_cmxCpSubBlock = new CmxCpSubBlock();
+  m_rodStatus     = new std::vector<uint32_t>(2);
+  m_fea           = new FullEventAssembler<L1CaloSrcIdMap>();
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode CpByteStreamV2Tool::finalize()
+{
+  delete m_fea;
+  delete m_rodStatus;
+  delete m_cmxCpSubBlock;
+  delete m_cpmSubBlock;
+  delete m_towerKey;
+  delete m_srcIdMap;
+  return StatusCode::SUCCESS;
+}
+
+// Conversion bytestream to CPM towers
+
+StatusCode CpByteStreamV2Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::CPMTower>* const ttCollection)
+{
+  m_ttCollection = ttCollection;
+  m_ttMap.clear();
+  return convertBs(robFrags, CPM_TOWERS);
+}
+
+// Conversion bytestream to CMX-CP TOBs
+
+StatusCode CpByteStreamV2Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::CMXCPTob>* const tobCollection)
+{
+  m_tobCollection = tobCollection;
+  m_tobMap.clear();
+  return convertBs(robFrags, CMX_CP_TOBS);
+}
+
+// Conversion bytestream to CMX-CP hits
+
+StatusCode CpByteStreamV2Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::CMXCPHits>* const hitCollection)
+{
+  m_hitCollection = hitCollection;
+  m_hitsMap.clear();
+  return convertBs(robFrags, CMX_CP_HITS);
+}
+
+// Conversion of CP container to bytestream
+
+StatusCode CpByteStreamV2Tool::convert(const LVL1::CPBSCollectionV2* const cp,
+                                           RawEventWrite* const re)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Clear the event assembler
+
+  m_fea->clear();
+  const uint16_t minorVersion = m_srcIdMap->minorVersion();
+  m_fea->setRodMinorVersion(minorVersion);
+  m_rodStatusMap.clear();
+
+  // Pointer to ROD data vector
+
+  FullEventAssembler<L1CaloSrcIdMap>::RODDATA* theROD = 0;
+
+  // Set up the container maps
+
+  setupCpmTowerMap(cp->towers());
+  setupCmxCpTobMap(cp->tobs());
+  setupCmxCpHitsMap(cp->hits());
+
+  // Loop over data
+
+  const bool neutralFormat   = m_dataFormat == L1CaloSubBlock::NEUTRAL;
+  const int  modulesPerSlink = m_modules / m_slinks;
+  int timeslices    = 1;
+  int trigCpm       = 0;
+  int timeslicesNew = 1;
+  int trigCpmNew    = 0;
+  for (int crate = m_crateMin; crate <= m_crateMax; ++crate) {
+    const int hwCrate = crate + m_crateOffsetHw;
+
+    // CPM modules are numbered 1 to m_modules
+    for (int module=1; module <= m_modules; ++module) {
+      const int mod = module - 1;
+
+      // Pack required number of modules per slink
+
+      if (mod%modulesPerSlink == 0) {
+	const int daqOrRoi = 0;
+	const int slink = (m_slinks == 2) ? 2*(mod/modulesPerSlink)
+	                                  : mod/modulesPerSlink;
+        if (debug) {
+          msg() << "Treating crate " << hwCrate
+                << " slink " << slink << endreq;
+        }
+	// Get number of CPM slices and triggered slice offset
+	// for this slink
+	if ( ! slinkSlices(crate, module, modulesPerSlink,
+	                                  timeslices, trigCpm)) {
+	  msg(MSG::ERROR) << "Inconsistent number of slices or "
+	                  << "triggered slice offsets in data for crate "
+	                  << hwCrate << " slink " << slink << endreq;
+	  return StatusCode::FAILURE;
+        }
+	timeslicesNew = (m_forceSlices) ? m_forceSlices : timeslices;
+	trigCpmNew    = ModifySlices::peak(trigCpm, timeslices, timeslicesNew);
+        if (debug) {
+	  msg() << "Data Version/Format: " << m_version
+	        << " " << m_dataFormat << endreq
+                << "Slices/offset: " << timeslices << " " << trigCpm;
+	  if (timeslices != timeslicesNew) {
+	    msg() << " modified to " << timeslicesNew << " " << trigCpmNew;
+          }
+	  msg() << endreq;
+        }
+        L1CaloUserHeader userHeader;
+        userHeader.setCpm(trigCpmNew);
+	const uint32_t rodIdCpm = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+	                                                        m_subDetector);
+	theROD = m_fea->getRodData(rodIdCpm);
+	theROD->push_back(userHeader.header());
+	m_rodStatusMap.insert(make_pair(rodIdCpm, m_rodStatus));
+      }
+      if (debug) msg() << "Module " << module << endreq;
+
+      // Create a sub-block for each slice (except Neutral format)
+
+      m_cpmBlocks.clear();
+      for (int slice = 0; slice < timeslicesNew; ++slice) {
+        CpmSubBlockV2* const subBlock = new CpmSubBlockV2();
+	subBlock->setCpmHeader(m_version, m_dataFormat, slice,
+	                       hwCrate, module, timeslicesNew);
+        m_cpmBlocks.push_back(subBlock);
+	if (neutralFormat) break;
+      }
+
+      // Find CPM towers corresponding to each eta/phi pair and fill
+      // sub-blocks
+
+      for (int chan=0; chan < m_channels; ++chan) {
+	double eta = 0.;
+	double phi = 0.;
+	int layer = 0;
+	if (m_cpmMaps->mapping(crate, module, chan, eta, phi, layer)) {
+          const unsigned int key = m_towerKey->ttKey(phi, eta);
+          const LVL1::CPMTower* const tt = findCpmTower(key);
+	  if (tt ) {
+	    std::vector<int> emData;
+	    std::vector<int> hadData;
+	    std::vector<int> emError;
+	    std::vector<int> hadError;
+	    ModifySlices::data(tt->emEnergyVec(),  emData,   timeslicesNew);
+	    ModifySlices::data(tt->hadEnergyVec(), hadData,  timeslicesNew);
+	    ModifySlices::data(tt->emErrorVec(),   emError,  timeslicesNew);
+	    ModifySlices::data(tt->hadErrorVec(),  hadError, timeslicesNew);
+            for (int slice = 0; slice < timeslicesNew; ++slice) {
+	      const LVL1::DataError emErrBits(emError[slice]);
+	      const LVL1::DataError hadErrBits(hadError[slice]);
+	      const int emErr  =
+	                      (emErrBits.get(LVL1::DataError::LinkDown) << 1) |
+	                       emErrBits.get(LVL1::DataError::Parity);
+	      const int hadErr =
+	                     (hadErrBits.get(LVL1::DataError::LinkDown) << 1) |
+	                      hadErrBits.get(LVL1::DataError::Parity);
+	      const int index  = ( neutralFormat ) ? 0 : slice;
+              CpmSubBlockV2* const subBlock = m_cpmBlocks[index];
+              subBlock->fillTowerData(slice, chan, emData[slice],
+	                              hadData[slice], emErr, hadErr);
+	      if ((emErrBits.error() >> LVL1::DataError::GLinkParity)) {
+	        int gLinkParity   = emErrBits.get(LVL1::DataError::GLinkParity);
+		int gLinkProtocol = emErrBits.get(LVL1::DataError::GLinkProtocol);
+		int bCNMismatch   = emErrBits.get(LVL1::DataError::BCNMismatch);
+		int fIFOOverflow  = emErrBits.get(LVL1::DataError::FIFOOverflow);
+		int moduleError   = emErrBits.get(LVL1::DataError::ModuleError);
+		int gLinkDown     = emErrBits.get(LVL1::DataError::GLinkDown);
+		int gLinkTimeout  = emErrBits.get(LVL1::DataError::GLinkTimeout);
+		uint32_t failingBCN = emErrBits.get(LVL1::DataError::FailingBCN);
+		subBlock->setStatus(failingBCN, gLinkTimeout, gLinkDown,
+		                    moduleError, fIFOOverflow, bCNMismatch,
+				    gLinkProtocol, gLinkParity);
+	      }
+	    }
+          }
+        }
+      }
+      
+      // Pack and write the sub-blocks
+
+      DataVector<CpmSubBlockV2>::const_iterator pos;
+      for (pos = m_cpmBlocks.begin(); pos != m_cpmBlocks.end(); ++pos) {
+        CpmSubBlockV2* const subBlock = *pos;
+	if ( !subBlock->pack()) {
+	  msg(MSG::ERROR) << "CPM sub-block packing failed" << endreq;
+	  return StatusCode::FAILURE;
+	}
+        if (debug) {
+          msg() << "CPM sub-block data words: "
+	        << subBlock->dataWords() << endreq;
+        }
+	subBlock->write(theROD);
+      }
+    }
+
+    // Append CMXs to last S-Link of the crate
+
+    for (int cmx = 0; cmx < m_cmxs; ++cmx) {
+
+      // Create a sub-block for each slice (except Neutral format)
+
+      m_cmxBlocks.clear();
+      const int summing = (crate == m_crates - 1) ? CmxSubBlock::SYSTEM
+                                                  : CmxSubBlock::CRATE;
+      for (int slice = 0; slice < timeslicesNew; ++slice) {
+        CmxCpSubBlock* const block = new CmxCpSubBlock();
+        block->setCmxHeader(m_version, m_dataFormat, slice, hwCrate,
+                            summing, CmxSubBlock::CMX_CP, cmx, timeslicesNew);
+        m_cmxBlocks.push_back(block);
+        if (neutralFormat) break;
+      }
+
+      // CMX-CP Tobs
+
+      for (int cpm = 1; cpm <= m_modules; ++cpm) {
+        for (int chip = 0; chip < m_chips; ++chip) {
+	  for (int loc = 0; loc < m_locs; ++loc) {
+	    const int key = tobKey(crate, cmx, cpm, chip, loc);
+	    const LVL1::CMXCPTob* const ct = findCmxCpTob(key);
+            if ( ct ) {
+	      std::vector<int> energy;
+	      std::vector<int> isolation;
+	      std::vector<int> error;
+	      std::vector<unsigned int> presence;
+	      ModifySlices::data(ct->energyVec(),      energy,    timeslicesNew);
+	      ModifySlices::data(ct->isolationVec(),   isolation, timeslicesNew);
+	      ModifySlices::data(ct->errorVec(),       error,     timeslicesNew);
+	      ModifySlices::data(ct->presenceMapVec(), presence,  timeslicesNew);
+	      for (int slice = 0; slice < timeslicesNew; ++slice) {
+	        const LVL1::DataError errBits(error[slice]);
+		int err = errBits.get(LVL1::DataError::ParityMerge);
+		err |= (errBits.get(LVL1::DataError::ParityPhase0))<<1;
+		err |= (errBits.get(LVL1::DataError::ParityPhase1))<<2;
+		err |= (errBits.get(LVL1::DataError::ParityPhase2))<<3;
+		err |= (errBits.get(LVL1::DataError::ParityPhase3))<<4;
+		err |= (errBits.get(LVL1::DataError::Overflow))<<5;
+		const int index = ( neutralFormat ) ? 0 : slice;
+		CmxCpSubBlock* const subBlock = m_cmxBlocks[index];
+		subBlock->setTob(slice, cpm, chip, loc, energy[slice],
+		                 isolation[slice], err);
+	        subBlock->setPresenceMap(slice, cpm, presence[slice]);
+              }
+            }
+          }
+        }
+      }
+
+      // CMX-CP Hits
+
+      for (int source = 0; source < LVL1::CMXCPHits::MAXSOURCE; ++source) {
+	const int key = hitsKey(crate, cmx, source);
+        const LVL1::CMXCPHits* const ch = findCmxCpHits(key);
+        if ( ch ) {
+          std::vector<unsigned int> hits0;
+          std::vector<unsigned int> hits1;
+          std::vector<int> err0;
+          std::vector<int> err1;
+	  ModifySlices::data(ch->hitsVec0(),  hits0, timeslicesNew);
+	  ModifySlices::data(ch->hitsVec1(),  hits1, timeslicesNew);
+	  ModifySlices::data(ch->errorVec0(), err0,  timeslicesNew);
+	  ModifySlices::data(ch->errorVec1(), err1,  timeslicesNew);
+	  for (int slice = 0; slice < timeslicesNew; ++slice) {
+	    const LVL1::DataError err0Bits(err0[slice]);
+	    const LVL1::DataError err1Bits(err1[slice]);
+	    const int index = ( neutralFormat ) ? 0 : slice;
+	    CmxCpSubBlock* const subBlock = m_cmxBlocks[index];
+	    subBlock->setHits(slice, source, 0, hits0[slice],                // Assuming CMXCPHits::source == CmxCpSubBlock::source
+	                           err0Bits.get(LVL1::DataError::Parity));
+	    subBlock->setHits(slice, source, 1, hits1[slice],
+	                           err1Bits.get(LVL1::DataError::Parity));
+	    if (neutralFormat) { // Neutral format wants RoI overflow bit
+	      subBlock->setRoiOverflow(slice, source,
+	                         err0Bits.get(LVL1::DataError::Overflow));
+	    }
+          }
+        }
+      }
+      DataVector<CmxCpSubBlock>::const_iterator cos = m_cmxBlocks.begin();
+      for (; cos != m_cmxBlocks.end(); ++cos) {
+        CmxCpSubBlock* const subBlock = *cos;
+        if ( !subBlock->pack()) {
+          msg(MSG::ERROR) << "CMX-Cp sub-block packing failed" << endreq;
+	  return StatusCode::FAILURE;
+        }
+        if (debug) {
+          msg() << "CMX-Cp sub-block data words: "
+	        << subBlock->dataWords() << endreq;
+        }
+        subBlock->write(theROD);
+      }
+    }
+  }
+
+  // Fill the raw event
+
+  m_fea->fill(re, msg());
+
+  // Set ROD status words
+
+  //L1CaloRodStatus::setStatus(re, m_rodStatusMap, m_srcIdMap);
+
+  return StatusCode::SUCCESS;
+}
+
+// Return reference to vector with all possible Source Identifiers
+
+const std::vector<uint32_t>& CpByteStreamV2Tool::sourceIDs(
+                                               const std::string& sgKey)
+{
+  // Check if overlap tower channels wanted
+  const std::string flag("Overlap");
+  const std::string::size_type pos = sgKey.find(flag);
+  m_coreOverlap =
+   (pos == std::string::npos || pos != sgKey.length() - flag.length()) ? 0 : 1;
+
+  if (m_sourceIDs.empty()) {
+    const int maxCrates = m_crates + m_crateOffsetHw;
+    const int maxSlinks = m_srcIdMap->maxSlinks();
+    for (int hwCrate = m_crateOffsetHw; hwCrate < maxCrates; ++hwCrate) {
+      for (int slink = 0; slink < maxSlinks; ++slink) {
+        const int daqOrRoi = 0;
+        const uint32_t rodId = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+                                                             m_subDetector);
+        const uint32_t robId = m_srcIdMap->getRobID(rodId);
+        m_sourceIDs.push_back(robId);
+      }
+    }
+  }
+  return m_sourceIDs;
+}
+
+// Convert bytestream to given container type
+
+StatusCode CpByteStreamV2Tool::convertBs(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            const CollectionType collection)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Loop over ROB fragments
+
+  int robCount = 0;
+  std::set<uint32_t> dupCheck;
+  ROBIterator rob    = robFrags.begin();
+  ROBIterator robEnd = robFrags.end();
+  for (; rob != robEnd; ++rob) {
+
+    if (debug) {
+      ++robCount;
+      msg() << "Treating ROB fragment " << robCount << endreq;
+    }
+
+    // Skip fragments with ROB status errors
+
+    const uint32_t robid = (*rob)->source_id();
+    if ((*rob)->nstatus() > 0) {
+      ROBPointer robData;
+      (*rob)->status(robData);
+      if (*robData != 0) {
+        m_errorTool->robError(robid, *robData);
+	if (debug) msg() << "ROB status error - skipping fragment" << endreq;
+	continue;
+      }
+    }
+
+    // Skip duplicate fragments
+
+    if (!dupCheck.insert(robid).second) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_DUPLICATE_ROB);
+      if (debug) msg() << "Skipping duplicate ROB fragment" << endreq;
+      continue;
+    }
+
+    // Unpack ROD data (slinks)
+
+    RODPointer payloadBeg;
+    RODPointer payload;
+    RODPointer payloadEnd;
+    (*rob)->rod_data(payloadBeg);
+    payloadEnd = payloadBeg + (*rob)->rod_ndata();
+    payload = payloadBeg;
+    if (payload == payloadEnd) {
+      if (debug) msg() << "ROB fragment empty" << endreq;
+      continue;
+    }
+
+    // Check identifier
+    const uint32_t sourceID = (*rob)->rod_source_id();
+    if (m_srcIdMap->getRobID(sourceID) != robid           ||
+        m_srcIdMap->subDet(sourceID)   != m_subDetector   ||
+	m_srcIdMap->daqOrRoi(sourceID) != 0               ||
+       (m_srcIdMap->slink(sourceID) != 0 && m_srcIdMap->slink(sourceID) != 2) ||
+	m_srcIdMap->crate(sourceID)    <  m_crateOffsetHw ||
+	m_srcIdMap->crate(sourceID)    >= m_crateOffsetHw + m_crates) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_ROD_ID);
+      if (debug) {
+        msg() << "Wrong source identifier in data: ROD "
+	      << MSG::hex << sourceID << "  ROB " << robid
+	      << MSG::dec << endreq;
+      }
+      continue;
+    }
+
+    // Check minor version
+    const int minorVersion = (*rob)->rod_version() & 0xffff;
+    if (minorVersion <= m_srcIdMap->minorVersionPreLS1()) {
+      if (debug) msg() << "Skipping pre-LS1 data" << endreq;
+      continue;
+    }
+    const int rodCrate = m_srcIdMap->crate(sourceID);
+    if (debug) {
+      msg() << "Treating crate " << rodCrate 
+            << " slink " << m_srcIdMap->slink(sourceID) << endreq;
+    }
+
+    // First word should be User Header
+    if ( !L1CaloUserHeader::isValid(*payload) ) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
+      if (debug) msg() << "Invalid or missing user header" << endreq;
+      continue;
+    }
+    L1CaloUserHeader userHeader(*payload);
+    userHeader.setVersion(minorVersion);
+    const int headerWords = userHeader.words();
+    if (headerWords != 1) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
+      if (debug) msg() << "Unexpected number of user header words: "
+                       << headerWords << endreq;
+      continue;
+    }
+    for (int i = 0; i < headerWords; ++i) ++payload;
+    // triggered slice offset
+    const int trigCpm = userHeader.cpm();
+    if (debug) {
+      msg() << "Minor format version number: " << MSG::hex
+            << minorVersion << MSG::dec << endreq
+            << "Triggered slice offset: "  << trigCpm << endreq;
+    }
+
+    // Loop over sub-blocks
+
+    m_rodErr = L1CaloSubBlock::ERROR_NONE;
+    while (payload != payloadEnd) {
+      
+      if (L1CaloSubBlock::wordType(*payload) != L1CaloSubBlock::HEADER) {
+        if (debug) msg() << "Unexpected data sequence" << endreq;
+	m_rodErr = L1CaloSubBlock::ERROR_MISSING_HEADER;
+	break;
+      }
+      if (L1CaloSubBlock::version(*payload) == 1) {                            // <<== CHECK
+        if (debug) msg() << "Skipping pre-LS1 data" << endreq;                 // Why again - also done above ?
+	break;
+      }
+      if (CmxSubBlock::cmxBlock(*payload)) {
+        // CMX
+	if (CmxSubBlock::cmxType(*payload) == CmxSubBlock::CMX_CP) {
+	  m_cmxCpSubBlock->clear();
+          payload = m_cmxCpSubBlock->read(payload, payloadEnd);
+	  if (m_cmxCpSubBlock->crate() != rodCrate) {
+	    if (debug) msg() << "Inconsistent crate number in ROD source ID"
+	                     << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	    break;
+          }
+	  if (collection == CMX_CP_TOBS || collection == CMX_CP_HITS) {
+	    decodeCmxCp(m_cmxCpSubBlock, trigCpm, collection);
+	    if (m_rodErr != L1CaloSubBlock::ERROR_NONE) {
+	      if (debug) msg() << "decodeCmxCp failed" << endreq;
+	      break;
+	    }
+          }
+        } else {
+          if (debug) msg() << "Invalid CMX type in module field" << endreq;
+          m_rodErr = L1CaloSubBlock::ERROR_MODULE_NUMBER;
+	  break;
+        }
+      } else {
+        // CPM
+	m_cpmSubBlock->clear();
+        payload = m_cpmSubBlock->read(payload, payloadEnd);
+	if (m_cpmSubBlock->crate() != rodCrate) {
+	  if (debug) msg() << "Inconsistent crate number in ROD source ID"
+	                   << endreq;
+	  m_rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	  break;
+        }
+	if (collection == CPM_TOWERS) {
+	  decodeCpm(m_cpmSubBlock, trigCpm);
+	  if (m_rodErr != L1CaloSubBlock::ERROR_NONE) {
+	    if (debug) msg() << "decodeCpm failed" << endreq;
+	    break;
+	  }
+        }
+      }
+    }
+    if (m_rodErr != L1CaloSubBlock::ERROR_NONE) 
+                                       m_errorTool->rodError(robid, m_rodErr);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Unpack CMX-CP sub-block
+
+void CpByteStreamV2Tool::decodeCmxCp(CmxCpSubBlock* subBlock, int trigCpm,
+                                     CollectionType collection)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  const int hwCrate    = subBlock->crate();
+  const int cmx        = subBlock->cmxPosition();
+  const int firmware   = subBlock->cmxFirmware();
+  const int summing    = subBlock->cmxSumming();
+  const int timeslices = subBlock->timeslices();
+  const int sliceNum   = subBlock->slice();
+  if (debug) {
+    msg() << "CMX-CP: Crate "  << hwCrate
+          << "  Position "     << cmx
+	  << "  Firmware "     << firmware
+	  << "  Summing "      << summing
+          << "  Total slices " << timeslices
+          << "  Slice "        << sliceNum
+	  << endreq;
+  }
+  if (timeslices <= trigCpm) {
+    if (debug) msg() << "Triggered slice from header "
+                     << "inconsistent with number of slices: "
+                     << trigCpm << ", " << timeslices << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  if (timeslices <= sliceNum) {
+    if (debug) msg() << "Total slices inconsistent with slice number: "
+                     << timeslices << ", " << sliceNum << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  // Unpack sub-block
+  if (subBlock->dataWords() && !subBlock->unpack()) {
+    if (debug) {
+      std::string errMsg(subBlock->unpackErrorMsg());
+      msg() << "CMX-CP sub-block unpacking failed: " << errMsg << endreq;
+    }
+    m_rodErr = subBlock->unpackErrorCode();
+    return;
+  }
+
+  // Retrieve required data
+
+  const bool neutralFormat = subBlock->format() == L1CaloSubBlock::NEUTRAL;
+  LVL1::DataError dErr;
+  dErr.set(LVL1::DataError::SubStatusWord, subBlock->subStatus());
+  const int subStatus = dErr.error();
+  const int crate    = hwCrate - m_crateOffsetHw;
+  const int swCrate  = crate   + m_crateOffsetSw;
+  const int maxSid   = CmxCpSubBlock::MAX_SOURCE_ID;
+  const int sliceBeg = ( neutralFormat ) ? 0          : sliceNum;
+  const int sliceEnd = ( neutralFormat ) ? timeslices : sliceNum + 1;
+  for (int slice = sliceBeg; slice < sliceEnd; ++slice) {
+
+    if (collection == CMX_CP_TOBS) {
+
+      // TOBs
+
+      for (int cpm = 1; cpm <= m_modules; ++cpm) {
+        const unsigned int presenceMap = subBlock->presenceMap(slice, cpm);
+        for (int tob = 0; tob < m_maxTobs; ++tob) {
+	  const int energy = subBlock->energy(slice, cpm, tob);
+	  const int isolation = subBlock->isolation(slice, cpm, tob);
+	  int error = subBlock->tobError(slice, cpm, tob);
+	  if (energy == 0 && isolation == 0 && error == 0) break;
+	  const int loc = subBlock->localCoord(slice, cpm, tob);
+	  const int chip = subBlock->chip(slice, cpm, tob);
+	  LVL1::DataError errBits(subStatus);
+          if (error) {
+	    errBits.set(LVL1::DataError::Parity, (error&0x1f) ? 1 : 0);
+	    errBits.set(LVL1::DataError::ParityMerge, error);
+	    errBits.set(LVL1::DataError::ParityPhase0, (error>>1));
+	    errBits.set(LVL1::DataError::ParityPhase1, (error>>2));
+	    errBits.set(LVL1::DataError::ParityPhase2, (error>>3));
+	    errBits.set(LVL1::DataError::ParityPhase3, (error>>4));
+	    errBits.set(LVL1::DataError::Overflow,     (error>>5));
+          }
+	  error = errBits.error();
+	  const int key = tobKey(crate, cmx, cpm, chip, loc);
+	  LVL1::CMXCPTob* tb = findCmxCpTob(key);
+	  if ( ! tb ) { // create new CMX TOB
+	    m_energyVec.assign(timeslices, 0);
+	    m_isolVec.assign(timeslices, 0);
+	    m_errorVec.assign(timeslices, 0);
+	    m_presenceMapVec.assign(timeslices, 0);
+	    m_energyVec[slice] = energy;
+	    m_isolVec[slice]   = isolation;
+	    m_errorVec[slice]  = error;
+	    m_presenceMapVec[slice] = presenceMap;
+	    tb = new LVL1::CMXCPTob(swCrate, cmx, cpm, chip, loc,
+	                            m_energyVec, m_isolVec, m_errorVec,
+				    m_presenceMapVec, trigCpm);
+	    m_tobMap.insert(std::make_pair(key, tb));
+	    m_tobCollection->push_back(tb);
+          } else {
+	    m_energyVec = tb->energyVec();
+	    m_isolVec   = tb->isolationVec();
+	    m_errorVec  = tb->errorVec();
+	    m_presenceMapVec = tb->presenceMapVec();
+	    const int nsl = m_energyVec.size();
+	    if (timeslices != nsl) {
+	      if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                       << endreq;
+              m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	      return;
+            }
+	    if (m_energyVec[slice] != 0 || m_isolVec[slice] != 0 ||
+	        m_errorVec[slice]  != 0) {
+              if (debug) msg() << "Duplicate data for slice " << slice << endreq;
+	      m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	      return;
+            }
+	    m_energyVec[slice] = energy;
+	    m_isolVec[slice]   = isolation;
+	    m_errorVec[slice]  = error;
+	    m_presenceMapVec[slice] = presenceMap;
+	    tb->addTob(m_energyVec, m_isolVec, m_errorVec, m_presenceMapVec);
+          }
+        }
+      }
+
+    } else if (collection == CMX_CP_HITS) {
+
+      // Hit/Topo counts
+
+      for (int source = 0; source < maxSid; ++source) {
+        if (summing == CmxSubBlock::CRATE &&
+	    (source == CmxCpSubBlock::REMOTE_0 ||
+	     source == CmxCpSubBlock::REMOTE_1 ||
+	     source == CmxCpSubBlock::REMOTE_2 ||
+	     source == CmxCpSubBlock::TOTAL)) continue;
+        const unsigned int hits0 = subBlock->hits(slice, source, 0); //low
+        const unsigned int hits1 = subBlock->hits(slice, source, 1); //high
+        int err0 = subBlock->hitsError(slice, source, 0);
+        int err1 = subBlock->hitsError(slice, source, 1);
+	int overflow = subBlock->roiOverflow(slice, source);
+        LVL1::DataError err0Bits(subStatus);
+        err0Bits.set(LVL1::DataError::Parity, err0);
+        err0Bits.set(LVL1::DataError::Overflow, overflow);
+        err0 = err0Bits.error();
+        LVL1::DataError err1Bits(subStatus);
+        err1Bits.set(LVL1::DataError::Parity, err1);
+        err1Bits.set(LVL1::DataError::Overflow, overflow);
+        err1 = err1Bits.error();
+        if (hits0 || hits1 || err0 || err1) {
+	  const int key = hitsKey(crate, cmx, source);
+          LVL1::CMXCPHits* ch = findCmxCpHits(key);
+	  if ( ! ch ) {   // create new CMX hits
+	    m_hitsVec0.assign(timeslices, 0);
+	    m_hitsVec1.assign(timeslices, 0);
+	    m_errVec0.assign(timeslices, 0);
+	    m_errVec1.assign(timeslices, 0);
+	    m_hitsVec0[slice] = hits0;
+	    m_hitsVec1[slice] = hits1;
+	    m_errVec0[slice]  = err0;
+	    m_errVec1[slice]  = err1;
+	    ch = new LVL1::CMXCPHits(swCrate, cmx, source,
+	                             m_hitsVec0, m_hitsVec1,
+	                             m_errVec0, m_errVec1, trigCpm);
+	    m_hitsMap.insert(std::make_pair(key, ch));
+	    m_hitCollection->push_back(ch);
+          } else {
+	    m_hitsVec0 = ch->hitsVec0();
+	    m_hitsVec1 = ch->hitsVec1();
+	    m_errVec0  = ch->errorVec0();
+	    m_errVec1  = ch->errorVec1();
+	    const int nsl = m_hitsVec0.size();
+	    if (timeslices != nsl) {
+	      if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                       << endreq;
+              m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	      return;
+            }
+	    if (m_hitsVec0[slice] != 0 || m_hitsVec1[slice] != 0 ||
+	        m_errVec0[slice]  != 0 || m_errVec1[slice]  != 0) {
+              if (debug) msg() << "Duplicate data for slice " << slice << endreq;
+	      m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	      return;
+            }
+	    m_hitsVec0[slice] = hits0;
+	    m_hitsVec1[slice] = hits1;
+	    m_errVec0[slice]  = err0;
+	    m_errVec1[slice]  = err1;
+	    ch->addHits(m_hitsVec0, m_hitsVec1, m_errVec0, m_errVec1);
+	  }
+        }
+      }
+    }
+  }
+  
+  return;
+}
+
+// Unpack CPM sub-block
+
+void CpByteStreamV2Tool::decodeCpm(CpmSubBlockV2* subBlock, int trigCpm)
+{
+  const bool debug   = msgLvl(MSG::DEBUG);
+  const bool verbose = msgLvl(MSG::VERBOSE);
+  if (debug) msg(MSG::DEBUG);
+
+  const int hwCrate    = subBlock->crate();
+  const int module     = subBlock->module();
+  const int timeslices = subBlock->timeslices();
+  const int sliceNum   = subBlock->slice();
+  if (debug) {
+    msg() << "CPM: Crate "     << hwCrate
+          << "  Module "       << module
+          << "  Total slices " << timeslices
+          << "  Slice "        << sliceNum    << endreq;
+  }
+  if (module < 1 || module > m_modules) {
+    if (debug) msg() << "Unexpected module number: " << module << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_MODULE_NUMBER;
+    return;
+  }
+  if (timeslices <= trigCpm) {
+    if (debug) msg() << "Triggered slice from header "
+                     << "inconsistent with number of slices: "
+                     << trigCpm << ", " << timeslices << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  if (timeslices <= sliceNum) {
+    if (debug) msg() << "Total slices inconsistent with slice number: "
+                     << timeslices << ", " << sliceNum << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  // Unpack sub-block
+  if (subBlock->dataWords() && !subBlock->unpack()) {
+    if (debug) {
+      std::string errMsg(subBlock->unpackErrorMsg());
+      msg() << "CPM sub-block unpacking failed: " << errMsg << endreq;
+    }
+    m_rodErr = subBlock->unpackErrorCode();
+    return;
+  }
+
+  // Retrieve required data
+
+  const bool neutralFormat = subBlock->format() == L1CaloSubBlock::NEUTRAL;
+  LVL1::DataError dErr;
+  dErr.set(LVL1::DataError::SubStatusWord, subBlock->subStatus());
+  const int subStatus = dErr.error();
+  const int crate     = hwCrate - m_crateOffsetHw;
+  const int sliceBeg  = ( neutralFormat ) ? 0          : sliceNum;
+  const int sliceEnd  = ( neutralFormat ) ? timeslices : sliceNum + 1;
+  for (int slice = sliceBeg; slice < sliceEnd; ++slice) {
+
+    // Loop over tower channels and fill CPM towers
+
+    for (int chan = 0; chan < m_channels; ++chan) {
+      if (!subStatus && !subBlock->anyTowerData(chan)) continue;
+      const int em     = subBlock->emData(slice, chan);
+      const int had    = subBlock->hadData(slice, chan);
+      const int emErr  = subBlock->emError(slice, chan);
+      const int hadErr = subBlock->hadError(slice, chan);
+      int emErr1 = subStatus;
+      if (emErr) {
+        LVL1::DataError emErrBits(emErr1);
+        emErrBits.set(LVL1::DataError::Parity, emErr & 0x1);
+        emErrBits.set(LVL1::DataError::LinkDown, (emErr >> 1) & 0x1);
+        emErr1 = emErrBits.error();
+      }
+      int hadErr1 = subStatus;
+      if (hadErr) {
+        LVL1::DataError hadErrBits(hadErr1);
+        hadErrBits.set(LVL1::DataError::Parity, hadErr & 0x1);
+        hadErrBits.set(LVL1::DataError::LinkDown, (hadErr >> 1) & 0x1);
+        hadErr1 = hadErrBits.error();
+      }
+      if (em || had || emErr1 || hadErr1) {
+        double eta = 0.;
+        double phi = 0.;
+        int layer = 0;
+        if (m_cpmMaps->mapping(crate, module, chan, eta, phi, layer)) {
+          if (layer == m_coreOverlap) {
+            const unsigned int key = m_towerKey->ttKey(phi, eta);
+            LVL1::CPMTower* tt = findCpmTower(key);
+            if ( ! tt ) {   // create new CPM tower
+              m_emVec.assign(timeslices, 0);
+              m_hadVec.assign(timeslices, 0);
+              m_emErrVec.assign(timeslices, 0);
+              m_hadErrVec.assign(timeslices, 0);
+              m_emVec[slice]     = em;
+              m_hadVec[slice]    = had;
+              m_emErrVec[slice]  = emErr1;
+              m_hadErrVec[slice] = hadErr1;
+              tt = new LVL1::CPMTower(phi, eta, m_emVec, m_emErrVec,
+                                                m_hadVec, m_hadErrVec, trigCpm);
+              m_ttMap.insert(std::make_pair(key, tt));
+              m_ttCollection->push_back(tt);
+            } else {
+              m_emVec     = tt->emEnergyVec();
+              m_hadVec    = tt->hadEnergyVec();
+              m_emErrVec  = tt->emErrorVec();
+              m_hadErrVec = tt->hadErrorVec();
+      	      const int nsl = m_emVec.size();
+	      if (timeslices != nsl) {
+	        if (debug) {
+	          msg() << "Inconsistent number of slices in sub-blocks"
+	                << endreq;
+	        }
+                m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	        return;
+              }
+	      if (m_emVec[slice]    != 0 || m_hadVec[slice]    != 0 ||
+	          m_emErrVec[slice] != 0 || m_hadErrVec[slice] != 0) {
+                if (debug) msg() << "Duplicate data for slice "
+	                         << slice << endreq;
+	        m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	        return;
+              }
+	      m_emVec[slice]     = em;
+	      m_hadVec[slice]    = had;
+	      m_emErrVec[slice]  = emErr1;
+	      m_hadErrVec[slice] = hadErr1;
+	      tt->fill(m_emVec, m_emErrVec, m_hadVec, m_hadErrVec, trigCpm);
+	    }
+	  }
+        } else if (verbose && (em || had || emErr || hadErr)) {
+	  msg(MSG::VERBOSE) << "Non-zero data but no channel mapping for channel "
+	                    << chan << endreq;
+	  msg(MSG::DEBUG);
+        }
+      } else if (verbose) {
+        msg(MSG::VERBOSE) << "No CPM tower data for channel "
+                          << chan << " slice " << slice << endreq;
+        msg(MSG::DEBUG);
+      }
+    }
+  }
+  return;
+}
+
+// Find a CPM tower for given key
+
+LVL1::CPMTower* CpByteStreamV2Tool::findCpmTower(const unsigned int key)
+{
+  LVL1::CPMTower* tt = 0;
+  CpmTowerMap::const_iterator mapIter;
+  mapIter = m_ttMap.find(key);
+  if (mapIter != m_ttMap.end()) tt = mapIter->second;
+  return tt;
+}
+
+// Find CMX-CP TOB for given key
+
+LVL1::CMXCPTob* CpByteStreamV2Tool::findCmxCpTob(const int key)
+{
+  LVL1::CMXCPTob* tob = 0;
+  CmxCpTobMap::const_iterator mapIter;
+  mapIter = m_tobMap.find(key);
+  if (mapIter != m_tobMap.end()) tob = mapIter->second;
+  return tob;
+}
+
+// Find CMX-CP hits for given key
+
+LVL1::CMXCPHits* CpByteStreamV2Tool::findCmxCpHits(const int key)
+{
+  LVL1::CMXCPHits* hits = 0;
+  CmxCpHitsMap::const_iterator mapIter;
+  mapIter = m_hitsMap.find(key);
+  if (mapIter != m_hitsMap.end()) hits = mapIter->second;
+  return hits;
+}
+
+// Set up CPM tower map
+
+void CpByteStreamV2Tool::setupCpmTowerMap(const CpmTowerCollection*
+                                                          const ttCollection)
+{
+  m_ttMap.clear();
+  if (ttCollection) {
+    CpmTowerCollection::const_iterator pos  = ttCollection->begin();
+    CpmTowerCollection::const_iterator pose = ttCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::CPMTower* const tt = *pos;
+      const unsigned int key = m_towerKey->ttKey(tt->phi(), tt->eta());
+      m_ttMap.insert(std::make_pair(key, tt));
+    }
+  }
+}
+
+// Set up CMX-CP TOB map
+
+void CpByteStreamV2Tool::setupCmxCpTobMap(const CmxCpTobCollection*
+                                                        const tobCollection)
+{
+  m_tobMap.clear();
+  if (tobCollection) {
+    CmxCpTobCollection::const_iterator pos  = tobCollection->begin();
+    CmxCpTobCollection::const_iterator pose = tobCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::CMXCPTob* const tob = *pos;
+      const int crate = tob->crate() - m_crateOffsetSw;
+      const int cmx = tob->cmx();
+      const int cpm = tob->cpm();
+      const int chip = tob->chip();
+      const int loc = tob->location();
+      const int key = tobKey(crate, cmx, cpm, chip, loc);
+      m_tobMap.insert(std::make_pair(key, tob));
+    }
+  }
+}
+
+// Set up CMX-CP hits map
+
+void CpByteStreamV2Tool::setupCmxCpHitsMap(const CmxCpHitsCollection*
+                                                          const hitCollection)
+{
+  m_hitsMap.clear();
+  if (hitCollection) {
+    CmxCpHitsCollection::const_iterator pos  = hitCollection->begin();
+    CmxCpHitsCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::CMXCPHits* const hits = *pos;
+      const int crate = hits->crate() - m_crateOffsetSw;
+      const int cmx = hits->cmx();
+      const int source = hits->source();
+      const int key = hitsKey(crate, cmx, source);
+      m_hitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Key for TOBs
+
+int CpByteStreamV2Tool::tobKey(const int crate, const int cmx, const int cpm,
+                               const int chip, const int loc) const
+{
+  return (((((((crate<<1)|cmx)<<4)|cpm)<<4)|chip)<<2)|loc;
+}
+
+// Key for Hits
+
+int CpByteStreamV2Tool::hitsKey(const int crate, const int cmx,
+                                                 const int source) const
+{
+  return (((crate<<1)|cmx)<<3)|source;
+}
+
+// Get number of slices and triggered slice offset for next slink
+
+bool CpByteStreamV2Tool::slinkSlices(const int crate, const int module,
+                  const int modulesPerSlink, int& timeslices, int& trigCpm)
+{
+  int slices = -1;
+  int trigC  = m_dfltSlices/2;
+  for (int mod = module; mod < module + modulesPerSlink; ++mod) {
+    for (int chan = 0; chan < m_channels; ++chan) {
+      double eta = 0.;
+      double phi = 0.;
+      int layer = 0;
+      if ( !m_cpmMaps->mapping(crate, mod, chan, eta, phi, layer)) continue;
+      const unsigned int key = m_towerKey->ttKey(phi, eta);
+      const LVL1::CPMTower* const tt = findCpmTower(key);
+      if ( !tt ) continue;
+      const int numdat = 4;
+      std::vector<int> sums(numdat);
+      std::vector<int> sizes(numdat);
+      sums[0] = std::accumulate((tt->emEnergyVec()).begin(),
+                                (tt->emEnergyVec()).end(), 0);
+      sums[1] = std::accumulate((tt->hadEnergyVec()).begin(),
+                                (tt->hadEnergyVec()).end(), 0);
+      sums[2] = std::accumulate((tt->emErrorVec()).begin(),
+                                (tt->emErrorVec()).end(), 0);
+      sums[3] = std::accumulate((tt->hadErrorVec()).begin(),
+                                (tt->hadErrorVec()).end(), 0);
+      sizes[0] = (tt->emEnergyVec()).size();
+      sizes[1] = (tt->hadEnergyVec()).size();
+      sizes[2] = (tt->emErrorVec()).size();
+      sizes[3] = (tt->hadErrorVec()).size();
+      const int peak = tt->peak();
+      for (int i = 0; i < numdat; ++i) {
+        if (sums[i] == 0) continue;
+        if (slices < 0) {
+	  slices = sizes[i];
+	  trigC  = peak;
+	} else if (slices != sizes[i] || trigC != peak) return false;
+      }
+    }
+  }
+  // CMXs last slink of crate
+  if (module/modulesPerSlink == m_slinks - 1) {
+    for (int cmx = 0; cmx < m_cmxs; ++cmx) {
+      for (int cpm = 1; cpm <= m_modules; ++ cpm) {
+        for (int chip = 0; chip < m_chips; ++chip) {
+	  for (int loc = 0; loc < m_locs; ++loc) {
+	    const int key = tobKey(crate, cmx, cpm, chip, loc);
+	    const LVL1::CMXCPTob* const tob = findCmxCpTob(key);
+	    if (tob) {
+	      const int numdat = 3;
+	      std::vector<int> sums(numdat);
+	      std::vector<int> sizes(numdat);
+	      sums[0] = std::accumulate((tob->energyVec()).begin(),
+	                                (tob->energyVec()).end(), 0);
+	      sums[1] = std::accumulate((tob->isolationVec()).begin(),
+	                                (tob->isolationVec()).end(), 0);
+	      sums[2] = std::accumulate((tob->errorVec()).begin(),
+	                                (tob->errorVec()).end(), 0);
+              sizes[0] = (tob->energyVec()).size();
+              sizes[1] = (tob->isolationVec()).size();
+              sizes[2] = (tob->errorVec()).size();
+              const int peak = tob->peak();
+              for (int i = 0; i < numdat; ++i) {
+                if (sums[i] == 0) continue;
+                if (slices < 0) {
+	          slices = sizes[i];
+	          trigC  = peak;
+                } else if (slices != sizes[i] || trigC != peak) return false;
+	      }
+	    }
+	  }
+        }
+      }
+      for (int source = 0; source < LVL1::CMXCPHits::MAXSOURCE; ++source) {
+        const int key = hitsKey(crate, cmx, source);
+        const LVL1::CMXCPHits* const hits = findCmxCpHits(key);
+        if (hits) {
+          const int numdat = 4;
+          std::vector<unsigned int> sums(numdat);
+          std::vector<int> sizes(numdat);
+          sums[0] = std::accumulate((hits->hitsVec0()).begin(),
+                                               (hits->hitsVec0()).end(), 0);
+          sums[1] = std::accumulate((hits->hitsVec1()).begin(),
+                                               (hits->hitsVec1()).end(), 0);
+          sums[2] = std::accumulate((hits->errorVec0()).begin(),
+                                               (hits->errorVec0()).end(), 0);
+          sums[3] = std::accumulate((hits->errorVec1()).begin(),
+                                               (hits->errorVec1()).end(), 0);
+          sizes[0] = (hits->hitsVec0()).size();
+          sizes[1] = (hits->hitsVec1()).size();
+          sizes[2] = (hits->errorVec0()).size();
+          sizes[3] = (hits->errorVec1()).size();
+          const int peak = hits->peak();
+          for (int i = 0; i < numdat; ++i) {
+            if (sums[i] == 0) continue;
+            if (slices < 0) {
+	      slices = sizes[i];
+	      trigC  = peak;
+            } else if (slices != sizes[i] || trigC != peak) return false;
+	  }
+        }
+      }
+    }
+  }
+  if (slices < 0) slices = m_dfltSlices;
+  timeslices = slices;
+  trigCpm    = trigC;
+  return true;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV2Tool.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV2Tool.h
new file mode 100755
index 0000000000000000000000000000000000000000..4ec2bb568c5b1f0de67ce79bbe74825d13d4eadd
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpByteStreamV2Tool.h
@@ -0,0 +1,230 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPBYTESTREAMV2TOOL_H
+#define TRIGT1CALOBYTESTREAM_CPBYTESTREAMV2TOOL_H
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+#include "ByteStreamData/RawEvent.h"
+#include "DataModel/DataVector.h"
+#include "eformat/SourceIdentifier.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class IInterface;
+class InterfaceID;
+class StatusCode;
+
+template <typename> class FullEventAssembler;
+
+namespace LVL1 {
+  class CMXCPHits;
+  class CMXCPTob;
+  class CPMTower;
+  class CPBSCollectionV2;
+  class IL1CaloMappingTool;
+  class TriggerTowerKey;
+}
+
+namespace LVL1BS {
+
+class CmxCpSubBlock;
+class CpmSubBlockV2;
+class L1CaloErrorByteStreamTool;
+class L1CaloSrcIdMap;
+
+/** Tool to perform ROB fragments to CPM towers, CMX-CP TOBs and CMX-CP hits,
+ *  and CP container to raw data conversions.
+ *
+ *  Based on ROD document version X_xxx.
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpByteStreamV2Tool : public AthAlgTool {
+
+ public:
+   CpByteStreamV2Tool(const std::string& type, const std::string& name,
+                                               const IInterface* parent);
+   virtual ~CpByteStreamV2Tool();
+
+   /// AlgTool InterfaceID
+   static const InterfaceID& interfaceID();
+
+   virtual StatusCode initialize();
+   virtual StatusCode finalize();
+
+   /// Convert ROB fragments to CPM towers
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::CPMTower>* ttCollection);
+   /// Convert ROB fragments to CMX-CP TOBs
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::CMXCPTob>* tobCollection);
+   /// Convert ROB fragments to CMX-CP hits
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::CMXCPHits>* hitCollection);
+
+   /// Convert CP Container to bytestream
+   StatusCode convert(const LVL1::CPBSCollectionV2* cp, RawEventWrite* re);
+
+   /// Return reference to vector with all possible Source Identifiers
+   const std::vector<uint32_t>& sourceIDs(const std::string& sgKey);
+
+ private:
+
+   enum CollectionType { CPM_TOWERS, CMX_CP_TOBS, CMX_CP_HITS };
+
+   typedef DataVector<LVL1::CPMTower>                    CpmTowerCollection;
+   typedef DataVector<LVL1::CMXCPTob>                    CmxCpTobCollection;
+   typedef DataVector<LVL1::CMXCPHits>                   CmxCpHitsCollection;
+   typedef std::map<unsigned int, LVL1::CPMTower*>       CpmTowerMap;
+   typedef std::map<int, LVL1::CMXCPTob*>                CmxCpTobMap;
+   typedef std::map<int, LVL1::CMXCPHits*>               CmxCpHitsMap;
+   typedef IROBDataProviderSvc::VROBFRAG::const_iterator ROBIterator;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      ROBPointer;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      RODPointer;
+
+   /// Convert bytestream to given container type
+   StatusCode convertBs(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                        CollectionType collection);
+   /// Unpack CMX-CP sub-block
+   void decodeCmxCp(CmxCpSubBlock* subBlock, int trigCpm,
+                                             CollectionType collection);
+   /// Unpack CPM sub-block
+   void decodeCpm(CpmSubBlockV2* subBlock, int trigCpm);
+
+   /// Find a CPM tower for given key
+   LVL1::CPMTower*  findCpmTower(unsigned int key);
+   /// Find CMX-CP TOB for given key
+   LVL1::CMXCPTob*  findCmxCpTob(int key);
+   /// Find CMX-CP hits for given key
+   LVL1::CMXCPHits* findCmxCpHits(int key);
+
+   /// Set up CPM tower map
+   void setupCpmTowerMap(const CpmTowerCollection* ttCollection);
+   /// Set up CMX-CP TOB map
+   void setupCmxCpTobMap(const CmxCpTobCollection* tobCollection);
+   /// Set up CMX-CP hits map
+   void setupCmxCpHitsMap(const CmxCpHitsCollection* hitCollection);
+
+   /// Key for TOBs
+   int tobKey(int crate, int cmx, int cpm, int chip, int loc) const;
+   /// Key for Hits
+   int hitsKey(int crate, int cmx, int source) const;
+
+   /// Get number of slices and triggered slice offset for next slink
+   bool slinkSlices(int crate, int module, int modulesPerSlink,
+                    int& timeslices, int& trigJem);
+
+   /// Channel mapping tool
+   ToolHandle<LVL1::IL1CaloMappingTool> m_cpmMaps;
+   /// Error collection tool
+   ToolHandle<LVL1BS::L1CaloErrorByteStreamTool> m_errorTool;
+
+   /// Hardware crate number offset
+   int m_crateOffsetHw;
+   /// Software crate number offset
+   int m_crateOffsetSw;
+   /// Sub_block header version
+   int m_version;
+   /// Data compression format
+   int m_dataFormat;
+   /// Number of channels per module
+   int m_channels;
+   /// Number of crates
+   int m_crates;
+   /// Number of CPM modules per crate
+   int m_modules;
+   /// Number of CMXs per crate
+   int m_cmxs;
+   /// Maximum number of TOBS per module
+   int m_maxTobs;
+   /// Number of chips
+   int m_chips;
+   /// Number of Local coordinates
+   int m_locs;
+   /// Number of slinks per crate when writing out bytestream
+   int m_slinks;
+   /// Default number of slices in simulation
+   int m_dfltSlices;
+   /// Force number of slices in bytestream
+   int m_forceSlices;
+   /// Minimum crate number when writing out bytestream
+   int m_crateMin;
+   /// Maximum crate number when writing out bytestream
+   int m_crateMax;
+   /// Tower channels to accept (1=Core, 2=Overlap)
+   int m_coreOverlap;
+   /// Unpacking error code
+   unsigned int m_rodErr;
+   /// ROB source IDs
+   std::vector<uint32_t> m_sourceIDs;
+   /// Sub-detector type
+   eformat::SubDetector m_subDetector;
+   /// Source ID converter
+   L1CaloSrcIdMap* m_srcIdMap;
+   /// Trigger tower key provider
+   LVL1::TriggerTowerKey* m_towerKey;
+   /// CPM sub-block for unpacking
+   CpmSubBlockV2* m_cpmSubBlock;
+   /// CMX-CP sub-block for unpacking
+   CmxCpSubBlock* m_cmxCpSubBlock;
+   /// Energy vector for unpacking
+   std::vector<int> m_energyVec;
+   /// Isolation vector for unpacking
+   std::vector<int> m_isolVec;
+   /// TOB error vector for unpacking
+   std::vector<int> m_errorVec;
+   /// Presence map vector for unpacking
+   std::vector<unsigned int> m_presenceMapVec;
+   /// Hits0 vector for unpacking
+   std::vector<unsigned int> m_hitsVec0;
+   /// Hits1 vector for unpacking
+   std::vector<unsigned int> m_hitsVec1;
+   /// Error0 vector for unpacking
+   std::vector<int> m_errVec0;
+   /// Error1 vector for unpacking
+   std::vector<int> m_errVec1;
+   /// EM data vector for unpacking
+   std::vector<int> m_emVec;
+   /// Had data vector for unpacking
+   std::vector<int> m_hadVec;
+   /// EM error data vector for unpacking
+   std::vector<int> m_emErrVec;
+   /// Had error data vector for unpacking
+   std::vector<int> m_hadErrVec;
+   /// Vector for current CPM sub-blocks
+   DataVector<CpmSubBlockV2> m_cpmBlocks;
+   /// Vector for current CMX-CP sub-blocks
+   DataVector<CmxCpSubBlock> m_cmxBlocks;
+   /// Current CPM tower collection
+   CpmTowerCollection*  m_ttCollection;
+   /// Current CMX-CP TOB collection
+   CmxCpTobCollection*  m_tobCollection;
+   /// Current CMX-CP hits collection
+   CmxCpHitsCollection* m_hitCollection;
+   /// CPM tower map
+   CpmTowerMap  m_ttMap;
+   /// CMX-CP TOB map
+   CmxCpTobMap  m_tobMap;
+   /// CMX-CP hits map
+   CmxCpHitsMap m_hitsMap;
+   /// ROD Status words
+   std::vector<uint32_t>* m_rodStatus;
+   /// ROD status map
+   std::map<uint32_t, std::vector<uint32_t>* > m_rodStatusMap;
+   /// Event assembler
+   FullEventAssembler<L1CaloSrcIdMap>* m_fea;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamCnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamCnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..b5968927e5a47e4b03f7afa4e809483fcb6e6d70
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamCnv.h
@@ -0,0 +1,79 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPREADBYTESTREAMCNV_H
+#define TRIGT1CALOBYTESTREAM_CPREADBYTESTREAMCNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IOpaqueAddress;
+class IROBDataProviderSvc;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class CpByteStreamTool;
+
+/** ByteStream converter for CP component containers.
+ *
+ *  @author Peter Faulkner
+ */
+
+template <typename Container>
+class CpReadByteStreamCnv: public Converter {
+
+  friend class CnvFactory<CpReadByteStreamCnv<Container> >;
+
+protected:
+
+  CpReadByteStreamCnv(ISvcLocator* svcloc);
+
+public:
+
+  ~CpReadByteStreamCnv();
+
+  virtual StatusCode initialize();
+  /// Create Container from ByteStream
+  virtual StatusCode createObj(IOpaqueAddress* pAddr, DataObject*& pObj);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::CpByteStreamTool> m_tool;
+
+  /// Service for reading bytestream
+  ServiceHandle<IROBDataProviderSvc> m_robDataProvider;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#include "CpReadByteStreamCnv.icc"
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamCnv.icc b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamCnv.icc
new file mode 100755
index 0000000000000000000000000000000000000000..bda64c7c709588abd6430f647ae2f7e08f364d48
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamCnv.icc
@@ -0,0 +1,140 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+#include <stdint.h>
+#include <typeinfo>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "CpByteStreamTool.h"
+
+namespace LVL1BS {
+
+template <typename Container>
+CpReadByteStreamCnv<Container>::CpReadByteStreamCnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("CpReadByteStreamCnv"),
+      m_tool("LVL1BS::CpByteStreamTool/CpByteStreamTool"),
+      m_robDataProvider("ROBDataProviderSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+template <typename Container>
+CpReadByteStreamCnv<Container>::~CpReadByteStreamCnv()
+{
+}
+
+// CLID
+
+template <typename Container>
+const CLID& CpReadByteStreamCnv<Container>::classID()
+{
+  return ClassID_traits<Container>::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+template <typename Container>
+StatusCode CpReadByteStreamCnv<Container>::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << "<"
+                      << typeid(Container).name() << "> - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return sc;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  // Get ROBDataProvider
+  sc = m_robDataProvider.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_robDataProvider << endreq;
+    return sc ;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_robDataProvider << endreq;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// createObj should create the RDO from bytestream.
+
+template <typename Container>
+StatusCode CpReadByteStreamCnv<Container>::createObj( IOpaqueAddress* pAddr,
+                                                       DataObject*& pObj )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createObj() called" << endreq;
+
+  ByteStreamAddress *pBS_Addr;
+  pBS_Addr = dynamic_cast<ByteStreamAddress *>( pAddr );
+  if ( !pBS_Addr ) {
+    m_log << MSG::ERROR << " Can not cast to ByteStreamAddress " << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = *( pBS_Addr->par() );
+
+  if (m_debug) m_log << MSG::DEBUG << " Creating Objects " << nm << endreq;
+
+  // get SourceIDs
+  const std::vector<uint32_t>& vID(m_tool->sourceIDs(nm));
+
+  // get ROB fragments
+  IROBDataProviderSvc::VROBFRAG robFrags;
+  m_robDataProvider->getROBData( vID, robFrags );
+
+  // size check
+  Container* const collection = new Container;
+  if (m_debug) {
+    m_log << MSG::DEBUG << " Number of ROB fragments is " << robFrags.size()
+          << endreq;
+  }
+  if (robFrags.size() == 0) {
+    pObj = SG::asStorable(collection) ;
+    return StatusCode::SUCCESS;
+  }
+
+  StatusCode sc = m_tool->convert(robFrags, collection);
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << " Failed to create Objects   " << nm << endreq;
+    delete collection;
+    return sc;
+  }
+
+  pObj = SG::asStorable(collection);
+
+  return sc;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamV1Cnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamV1Cnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..1c80738fca8bf82411eeafffee7311cb8002b467
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamV1Cnv.h
@@ -0,0 +1,79 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPREADBYTESTREAMV1CNV_H
+#define TRIGT1CALOBYTESTREAM_CPREADBYTESTREAMV1CNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IOpaqueAddress;
+class IROBDataProviderSvc;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class CpByteStreamV1Tool;
+
+/** ByteStream converter for CP component containers.
+ *
+ *  @author Peter Faulkner
+ */
+
+template <typename Container>
+class CpReadByteStreamV1Cnv: public Converter {
+
+  friend class CnvFactory<CpReadByteStreamV1Cnv<Container> >;
+
+protected:
+
+  CpReadByteStreamV1Cnv(ISvcLocator* svcloc);
+
+public:
+
+  ~CpReadByteStreamV1Cnv();
+
+  virtual StatusCode initialize();
+  /// Create Container from ByteStream
+  virtual StatusCode createObj(IOpaqueAddress* pAddr, DataObject*& pObj);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::CpByteStreamV1Tool> m_tool;
+
+  /// Service for reading bytestream
+  ServiceHandle<IROBDataProviderSvc> m_robDataProvider;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#include "CpReadByteStreamV1Cnv.icc"
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamV1Cnv.icc b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamV1Cnv.icc
new file mode 100755
index 0000000000000000000000000000000000000000..77e3e86da6f54e88e8ee662ac58cca4e45dde8b3
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamV1Cnv.icc
@@ -0,0 +1,140 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+#include <stdint.h>
+#include <typeinfo>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "CpByteStreamV1Tool.h"
+
+namespace LVL1BS {
+
+template <typename Container>
+CpReadByteStreamV1Cnv<Container>::CpReadByteStreamV1Cnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("CpReadByteStreamV1Cnv"),
+      m_tool("LVL1BS::CpByteStreamV1Tool/CpByteStreamV1Tool"),
+      m_robDataProvider("ROBDataProviderSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+template <typename Container>
+CpReadByteStreamV1Cnv<Container>::~CpReadByteStreamV1Cnv()
+{
+}
+
+// CLID
+
+template <typename Container>
+const CLID& CpReadByteStreamV1Cnv<Container>::classID()
+{
+  return ClassID_traits<Container>::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+template <typename Container>
+StatusCode CpReadByteStreamV1Cnv<Container>::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << "<"
+                      << typeid(Container).name() << "> - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return sc;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  // Get ROBDataProvider
+  sc = m_robDataProvider.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_robDataProvider << endreq;
+    return sc ;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_robDataProvider << endreq;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// createObj should create the RDO from bytestream.
+
+template <typename Container>
+StatusCode CpReadByteStreamV1Cnv<Container>::createObj( IOpaqueAddress* pAddr,
+                                                       DataObject*& pObj )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createObj() called" << endreq;
+
+  ByteStreamAddress *pBS_Addr;
+  pBS_Addr = dynamic_cast<ByteStreamAddress *>( pAddr );
+  if ( !pBS_Addr ) {
+    m_log << MSG::ERROR << " Can not cast to ByteStreamAddress " << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = *( pBS_Addr->par() );
+
+  if (m_debug) m_log << MSG::DEBUG << " Creating Objects " << nm << endreq;
+
+  // get SourceIDs
+  const std::vector<uint32_t>& vID(m_tool->sourceIDs(nm));
+
+  // get ROB fragments
+  IROBDataProviderSvc::VROBFRAG robFrags;
+  m_robDataProvider->getROBData( vID, robFrags );
+
+  // size check
+  Container* const collection = new Container;
+  if (m_debug) {
+    m_log << MSG::DEBUG << " Number of ROB fragments is " << robFrags.size()
+          << endreq;
+  }
+  if (robFrags.size() == 0) {
+    pObj = SG::asStorable(collection) ;
+    return StatusCode::SUCCESS;
+  }
+
+  StatusCode sc = m_tool->convert(robFrags, collection);
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << " Failed to create Objects   " << nm << endreq;
+    delete collection;
+    return sc;
+  }
+
+  pObj = SG::asStorable(collection);
+
+  return sc;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamV1V2Cnv.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamV1V2Cnv.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..2491d3848509220388aabacc3f274a41ffc10066
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamV1V2Cnv.cxx
@@ -0,0 +1,162 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+#include <stdint.h>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IByteStreamEventAccess.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "DataModel/DataVector.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/IRegistry.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "TrigT1CaloEvent/CPMTower.h"
+
+#include "CpReadByteStreamV1V2Cnv.h"
+#include "CpByteStreamV1Tool.h"
+#include "CpByteStreamV2Tool.h"
+
+namespace LVL1BS {
+
+CpReadByteStreamV1V2Cnv::CpReadByteStreamV1V2Cnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("CpReadByteStreamV1V2Cnv"),
+      m_tool1("LVL1BS::CpByteStreamV1Tool/CpByteStreamV1Tool"),
+      m_tool2("LVL1BS::CpByteStreamV2Tool/CpByteStreamV2Tool"),
+      m_robDataProvider("ROBDataProviderSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+CpReadByteStreamV1V2Cnv::~CpReadByteStreamV1V2Cnv()
+{
+}
+
+// CLID
+
+const CLID& CpReadByteStreamV1V2Cnv::classID()
+{
+  return ClassID_traits<DataVector<LVL1::CPMTower> >::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode CpReadByteStreamV1V2Cnv::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << " - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  // Retrieve Tools
+  sc = m_tool1.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool1 << endreq;
+    return StatusCode::FAILURE;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool1 << endreq;
+  sc = m_tool2.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool2 << endreq;
+    return StatusCode::FAILURE;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool2 << endreq;
+
+  // Get ROBDataProvider
+  sc = m_robDataProvider.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::WARNING << "Failed to retrieve service "
+          << m_robDataProvider << endreq;
+    return sc ;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_robDataProvider << endreq;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// createObj should create the RDO from bytestream.
+
+StatusCode CpReadByteStreamV1V2Cnv::createObj( IOpaqueAddress* pAddr,
+                                               DataObject*& pObj )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createObj() called" << endreq;
+
+  ByteStreamAddress *pBS_Addr;
+  pBS_Addr = dynamic_cast<ByteStreamAddress *>( pAddr );
+  if ( !pBS_Addr ) {
+    m_log << MSG::ERROR << " Can not cast to ByteStreamAddress " << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = *( pBS_Addr->par() );
+
+  if (m_debug) m_log << MSG::DEBUG << " Creating Objects " << nm << endreq;
+
+  // get SourceIDs
+  const std::vector<uint32_t>& vID1(m_tool1->sourceIDs(nm));
+  const std::vector<uint32_t>& vID2(m_tool2->sourceIDs(nm));
+
+  // get ROB fragments
+  IROBDataProviderSvc::VROBFRAG robFrags1;
+  m_robDataProvider->getROBData( vID1, robFrags1 );
+  IROBDataProviderSvc::VROBFRAG robFrags2;
+  m_robDataProvider->getROBData( vID2, robFrags2 );
+
+  // size check
+  DataVector<LVL1::CPMTower>* const towerCollection = new DataVector<LVL1::CPMTower>;
+  if (m_debug) {
+    m_log << MSG::DEBUG << " Number of ROB fragments is " << robFrags1.size()
+          << ", " << robFrags2.size() << endreq;
+  }
+  if (robFrags1.size() == 0 && robFrags2.size() == 0) {
+    pObj = SG::asStorable(towerCollection) ;
+    return StatusCode::SUCCESS;
+  }
+
+  // Pre-LS1 data
+  if (robFrags1.size() > 0) {
+    StatusCode sc = m_tool1->convert(robFrags1, towerCollection);
+    if ( sc.isFailure() ) {
+      m_log << MSG::ERROR << " Failed to create Objects   " << nm << endreq;
+      delete towerCollection;
+      return sc;
+    }
+  }
+  // Post-LS1 data
+  if (robFrags2.size() > 0) {
+    StatusCode sc = m_tool2->convert(robFrags2, towerCollection);
+    if ( sc.isFailure() ) {
+      m_log << MSG::ERROR << " Failed to create Objects   " << nm << endreq;
+      delete towerCollection;
+      return sc;
+    }
+  }
+
+  pObj = SG::asStorable(towerCollection);
+
+  return StatusCode::SUCCESS;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamV1V2Cnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamV1V2Cnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..b55dc3b28de44ac56cde156b1013f11ea776db77
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamV1V2Cnv.h
@@ -0,0 +1,82 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPREADBYTESTREAMV1V2CNV_H
+#define TRIGT1CALOBYTESTREAM_CPREADBYTESTREAMV1V2CNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IByteStreamEventAccess;
+class IOpaqueAddress;
+class IROBDataProviderSvc;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class CpByteStreamV1Tool;
+class CpByteStreamV2Tool;
+
+/** ByteStream converter for Cluster Processor Module Towers
+ *  allowing for data containing pre-LS1 or post-LS1 format sub-blocks.
+ *  For reading only.
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpReadByteStreamV1V2Cnv: public Converter {
+
+  friend class CnvFactory<CpReadByteStreamV1V2Cnv>;
+
+protected:
+
+  CpReadByteStreamV1V2Cnv(ISvcLocator* svcloc);
+
+public:
+
+  ~CpReadByteStreamV1V2Cnv();
+
+  virtual StatusCode initialize();
+  /// Create CPM Towers from ByteStream
+  virtual StatusCode createObj(IOpaqueAddress* pAddr, DataObject*& pObj);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work pre-LS1
+  ToolHandle<LVL1BS::CpByteStreamV1Tool> m_tool1;
+  /// Tool that does the actual work post-LS1
+  ToolHandle<LVL1BS::CpByteStreamV2Tool> m_tool2;
+
+  /// Service for reading bytestream
+  ServiceHandle<IROBDataProviderSvc> m_robDataProvider;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamV2Cnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamV2Cnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..c68f220336600372f847b91693719ff138236daa
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamV2Cnv.h
@@ -0,0 +1,79 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPREADBYTESTREAMV2CNV_H
+#define TRIGT1CALOBYTESTREAM_CPREADBYTESTREAMV2CNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IOpaqueAddress;
+class IROBDataProviderSvc;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class CpByteStreamV2Tool;
+
+/** ByteStream converter for CP component containers post LS1.
+ *
+ *  @author Peter Faulkner
+ */
+
+template <typename Container>
+class CpReadByteStreamV2Cnv: public Converter {
+
+  friend class CnvFactory<CpReadByteStreamV2Cnv<Container> >;
+
+protected:
+
+  CpReadByteStreamV2Cnv(ISvcLocator* svcloc);
+
+public:
+
+  ~CpReadByteStreamV2Cnv();
+
+  virtual StatusCode initialize();
+  /// Create Container from ByteStream
+  virtual StatusCode createObj(IOpaqueAddress* pAddr, DataObject*& pObj);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::CpByteStreamV2Tool> m_tool;
+
+  /// Service for reading bytestream
+  ServiceHandle<IROBDataProviderSvc> m_robDataProvider;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#include "CpReadByteStreamV2Cnv.icc"
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamV2Cnv.icc b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamV2Cnv.icc
new file mode 100755
index 0000000000000000000000000000000000000000..26bff4683e7900b4395cbabdae19222c6cc54413
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpReadByteStreamV2Cnv.icc
@@ -0,0 +1,140 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+#include <stdint.h>
+#include <typeinfo>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "CpByteStreamV2Tool.h"
+
+namespace LVL1BS {
+
+template <typename Container>
+CpReadByteStreamV2Cnv<Container>::CpReadByteStreamV2Cnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("CpReadByteStreamV2Cnv"),
+      m_tool("LVL1BS::CpByteStreamV2Tool/CpByteStreamV2Tool"),
+      m_robDataProvider("ROBDataProviderSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+template <typename Container>
+CpReadByteStreamV2Cnv<Container>::~CpReadByteStreamV2Cnv()
+{
+}
+
+// CLID
+
+template <typename Container>
+const CLID& CpReadByteStreamV2Cnv<Container>::classID()
+{
+  return ClassID_traits<Container>::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+template <typename Container>
+StatusCode CpReadByteStreamV2Cnv<Container>::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << "<"
+                      << typeid(Container).name() << "> - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return sc;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  // Get ROBDataProvider
+  sc = m_robDataProvider.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_robDataProvider << endreq;
+    return sc ;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_robDataProvider << endreq;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// createObj should create the RDO from bytestream.
+
+template <typename Container>
+StatusCode CpReadByteStreamV2Cnv<Container>::createObj( IOpaqueAddress* pAddr,
+                                                        DataObject*& pObj )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createObj() called" << endreq;
+
+  ByteStreamAddress *pBS_Addr;
+  pBS_Addr = dynamic_cast<ByteStreamAddress *>( pAddr );
+  if ( !pBS_Addr ) {
+    m_log << MSG::ERROR << " Can not cast to ByteStreamAddress " << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = *( pBS_Addr->par() );
+
+  if (m_debug) m_log << MSG::DEBUG << " Creating Objects " << nm << endreq;
+
+  // get SourceIDs
+  const std::vector<uint32_t>& vID(m_tool->sourceIDs(nm));
+
+  // get ROB fragments
+  IROBDataProviderSvc::VROBFRAG robFrags;
+  m_robDataProvider->getROBData( vID, robFrags );
+
+  // size check
+  Container* const collection = new Container;
+  if (m_debug) {
+    m_log << MSG::DEBUG << " Number of ROB fragments is " << robFrags.size()
+          << endreq;
+  }
+  if (robFrags.size() == 0) {
+    pObj = SG::asStorable(collection) ;
+    return StatusCode::SUCCESS;
+  }
+
+  StatusCode sc = m_tool->convert(robFrags, collection);
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << " Failed to create Objects   " << nm << endreq;
+    delete collection;
+    return sc;
+  }
+
+  pObj = SG::asStorable(collection);
+
+  return sc;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamCnv.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamCnv.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..1140456502851439804b89b20b58f7407139fdcb
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamCnv.cxx
@@ -0,0 +1,178 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+#include <stdint.h>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IByteStreamEventAccess.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "DataModel/DataVector.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/IRegistry.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "TrigT1CaloEvent/CPMRoI.h"
+
+#include "CpmRoiByteStreamCnv.h"
+#include "CpmRoiByteStreamTool.h"
+
+namespace LVL1BS {
+
+CpmRoiByteStreamCnv::CpmRoiByteStreamCnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("CpmRoiByteStreamCnv"),
+      m_tool("LVL1BS::CpmRoiByteStreamTool/CpmRoiByteStreamTool"),
+      m_robDataProvider("ROBDataProviderSvc", m_name),
+      m_ByteStreamEventAccess("ByteStreamCnvSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+CpmRoiByteStreamCnv::~CpmRoiByteStreamCnv()
+{
+}
+
+// CLID
+
+const CLID& CpmRoiByteStreamCnv::classID()
+{
+  return ClassID_traits<DataVector<LVL1::CPMRoI> >::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode CpmRoiByteStreamCnv::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << " - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  //Get ByteStreamCnvSvc
+  sc = m_ByteStreamEventAccess.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_ByteStreamEventAccess << endreq;
+    return sc;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_ByteStreamEventAccess << endreq;
+  }
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return StatusCode::FAILURE;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  // Get ROBDataProvider
+  sc = m_robDataProvider.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::WARNING << "Failed to retrieve service "
+          << m_robDataProvider << endreq;
+    // return is disabled for Write BS which does not require ROBDataProviderSvc
+    // return sc ;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_robDataProvider << endreq;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// createObj should create the RDO from bytestream.
+
+StatusCode CpmRoiByteStreamCnv::createObj( IOpaqueAddress* pAddr,
+                                        DataObject*& pObj )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createObj() called" << endreq;
+
+  ByteStreamAddress *pBS_Addr;
+  pBS_Addr = dynamic_cast<ByteStreamAddress *>( pAddr );
+  if ( !pBS_Addr ) {
+    m_log << MSG::ERROR << " Can not cast to ByteStreamAddress " << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = *( pBS_Addr->par() );
+
+  if (m_debug) m_log << MSG::DEBUG << " Creating Objects " << nm << endreq;
+
+  // get SourceIDs
+  const std::vector<uint32_t>& vID(m_tool->sourceIDs(nm));
+
+  // get ROB fragments
+  IROBDataProviderSvc::VROBFRAG robFrags;
+  m_robDataProvider->getROBData( vID, robFrags );
+
+  // size check
+  DataVector<LVL1::CPMRoI>* const roiCollection = new DataVector<LVL1::CPMRoI>;
+  if (m_debug) {
+    m_log << MSG::DEBUG << " Number of ROB fragments is " << robFrags.size()
+          << endreq;
+  }
+  if (robFrags.size() == 0) {
+    pObj = SG::asStorable(roiCollection) ;
+    return StatusCode::SUCCESS;
+  }
+
+  StatusCode sc = m_tool->convert(robFrags, roiCollection);
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << " Failed to create Objects   " << nm << endreq;
+    delete roiCollection;
+    return sc;
+  }
+
+  pObj = SG::asStorable(roiCollection);
+
+  return sc;
+}
+
+// createRep should create the bytestream from RDOs.
+
+StatusCode CpmRoiByteStreamCnv::createRep( DataObject* pObj,
+                                        IOpaqueAddress*& pAddr )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createRep() called" << endreq;
+
+  RawEventWrite* re = m_ByteStreamEventAccess->getRawEvent();
+
+  DataVector<LVL1::CPMRoI>* roiCollection = 0;
+  if( !SG::fromStorable( pObj, roiCollection ) ) {
+    m_log << MSG::ERROR << " Cannot cast to DataVector<CPMRoI>" << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = pObj->registry()->name();
+
+  ByteStreamAddress* addr = new ByteStreamAddress( classID(), nm, "" );
+
+  pAddr = addr;
+
+  // Convert to ByteStream
+  return m_tool->convert( roiCollection, re );
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamCnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamCnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..0626179e1dd3d8e0b6ce698bfd9b96f168aa97eb
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamCnv.h
@@ -0,0 +1,81 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPMROIBYTESTREAMCNV_H
+#define TRIGT1CALOBYTESTREAM_CPMROIBYTESTREAMCNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IByteStreamEventAccess;
+class IOpaqueAddress;
+class IROBDataProviderSvc;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class CpmRoiByteStreamTool;
+
+/** ByteStream converter for Cluster Processor Module RoIs.
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpmRoiByteStreamCnv: public Converter {
+
+  friend class CnvFactory<CpmRoiByteStreamCnv>;
+
+protected:
+
+  CpmRoiByteStreamCnv(ISvcLocator* svcloc);
+
+public:
+
+  ~CpmRoiByteStreamCnv();
+
+  virtual StatusCode initialize();
+  /// Create CPM RoIs from ByteStream
+  virtual StatusCode createObj(IOpaqueAddress* pAddr, DataObject*& pObj);
+  /// Create ByteStream from CPM RoIs
+  virtual StatusCode createRep(DataObject* pObj, IOpaqueAddress*& pAddr);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::CpmRoiByteStreamTool> m_tool;
+
+  /// Service for reading bytestream
+  ServiceHandle<IROBDataProviderSvc> m_robDataProvider;
+  /// Service for writing bytestream
+  ServiceHandle<IByteStreamEventAccess> m_ByteStreamEventAccess;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamTool.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamTool.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..5b3e8a77e7cdb1d61a5c376088497abdee8c8de7
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamTool.cxx
@@ -0,0 +1,432 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <numeric>
+#include <set>
+#include <utility>
+
+#include "ByteStreamCnvSvcBase/FullEventAssembler.h"
+
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "TrigT1CaloEvent/CPMRoI.h"
+
+#include "CpmRoiSubBlock.h"
+#include "L1CaloErrorByteStreamTool.h"
+#include "L1CaloSrcIdMap.h"
+#include "L1CaloUserHeader.h"
+
+#include "CpmRoiByteStreamTool.h"
+
+namespace LVL1BS {
+
+// Interface ID
+
+static const InterfaceID IID_ICpmRoiByteStreamTool("CpmRoiByteStreamTool",
+                                                                        1, 1);
+
+const InterfaceID& CpmRoiByteStreamTool::interfaceID()
+{
+  return IID_ICpmRoiByteStreamTool;
+}
+
+// Constructor
+
+CpmRoiByteStreamTool::CpmRoiByteStreamTool(const std::string& type,
+                                           const std::string& name,
+                                           const IInterface*  parent)
+  : AthAlgTool(type, name, parent),
+    m_errorTool("LVL1BS::L1CaloErrorByteStreamTool/L1CaloErrorByteStreamTool"),
+    m_crates(4), m_modules(14), m_srcIdMap(0), m_subBlock(0), m_rodStatus(0),
+    m_fea(0)
+{
+  declareInterface<CpmRoiByteStreamTool>(this);
+
+  declareProperty("CrateOffsetHw",  m_crateOffsetHw = 8,
+                  "Offset of CP crate numbers in bytestream");
+  declareProperty("CrateOffsetSw",  m_crateOffsetSw = 0,
+                  "Offset of CP crate numbers in RDOs");
+
+  // Properties for reading bytestream only
+  declareProperty("ROBSourceIDs",       m_sourceIDs,
+                  "ROB fragment source identifiers");
+  declareProperty("ROBSourceIDsRoIB",   m_sourceIDsRoIB,
+                  "ROB fragment source identifiers for RoIBs");
+
+  // Properties for writing bytestream only
+  declareProperty("DataVersion",    m_version       = 1,
+                  "Format version number in sub-block header");
+  declareProperty("DataFormat",     m_dataFormat    = 1,
+                  "Format identifier (0-1) in sub-block header");
+  declareProperty("SlinksPerCrate", m_slinks        = 1,
+                  "The number of S-Links per crate");
+
+}
+
+// Destructor
+
+CpmRoiByteStreamTool::~CpmRoiByteStreamTool()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode CpmRoiByteStreamTool::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = m_errorTool.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_errorTool << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_errorTool << endreq;
+
+  m_subDetector = eformat::TDAQ_CALO_CLUSTER_PROC_ROI;
+  m_srcIdMap    = new L1CaloSrcIdMap();
+  m_rodStatus   = new std::vector<uint32_t>(2);
+  m_subBlock    = new CpmRoiSubBlock();
+  m_fea         = new FullEventAssembler<L1CaloSrcIdMap>();
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode CpmRoiByteStreamTool::finalize()
+{
+  delete m_fea;
+  delete m_subBlock;
+  delete m_rodStatus;
+  delete m_srcIdMap;
+  return StatusCode::SUCCESS;
+}
+
+// Convert ROB fragments to CPM RoIs
+
+StatusCode CpmRoiByteStreamTool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::CPMRoI>* const roiCollection)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Loop over ROB fragments
+
+  int robCount = 0;
+  std::set<uint32_t> dupCheck;
+  std::set<uint32_t> dupRoiCheck;
+  ROBIterator rob    = robFrags.begin();
+  ROBIterator robEnd = robFrags.end();
+  for (; rob != robEnd; ++rob) {
+
+    if (debug) {
+      ++robCount;
+      msg() << "Treating ROB fragment " << robCount << endreq;
+    }
+
+    // Skip fragments with ROB status errors
+
+    uint32_t robid = (*rob)->source_id();
+    if ((*rob)->nstatus() > 0) {
+      ROBPointer robData;
+      (*rob)->status(robData);
+      if (*robData != 0) {
+        m_errorTool->robError(robid, *robData);
+	if (debug) msg() << "ROB status error - skipping fragment" << endreq;
+	continue;
+      }
+    }
+
+    // Skip duplicate fragments
+
+    if (!dupCheck.insert(robid).second) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_DUPLICATE_ROB);
+      if (debug) msg() << "Skipping duplicate ROB fragment" << endreq;
+      continue;
+    }
+
+    // Unpack ROD data (slinks)
+
+    RODPointer payloadBeg;
+    RODPointer payload;
+    RODPointer payloadEnd;
+    (*rob)->rod_data(payloadBeg);
+    payloadEnd = payloadBeg + (*rob)->rod_ndata();
+    payload = payloadBeg;
+    if (payload == payloadEnd) {
+      if (debug) msg() << "ROB fragment empty" << endreq;
+      continue;
+    }
+
+    // Check identifier
+    const uint32_t sourceID = (*rob)->rod_source_id();
+    if (m_srcIdMap->getRobID(sourceID) != robid           ||
+        m_srcIdMap->subDet(sourceID)   != m_subDetector   ||
+        m_srcIdMap->daqOrRoi(sourceID) != 1               ||
+       (m_srcIdMap->slink(sourceID) != 0 && m_srcIdMap->slink(sourceID) != 2) ||
+	m_srcIdMap->crate(sourceID)    <  m_crateOffsetHw ||
+	m_srcIdMap->crate(sourceID)    >= m_crateOffsetHw + m_crates) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_ROD_ID);
+      if (debug) {
+        msg() << "Wrong source identifier in data: "
+              << MSG::hex << sourceID << MSG::dec << endreq;
+      }
+      continue;
+    }
+    const int rodCrate = m_srcIdMap->crate(sourceID);
+    if (debug) {
+      msg() << "Treating crate " << rodCrate 
+            << " slink " << m_srcIdMap->slink(sourceID) << endreq;
+    }
+
+    // First word may be User Header
+    if (L1CaloUserHeader::isValid(*payload)) {
+      L1CaloUserHeader userHeader(*payload);
+      const int minorVersion = (*rob)->rod_version() & 0xffff;
+      userHeader.setVersion(minorVersion);
+      const int headerWords = userHeader.words();
+      if (headerWords != 1 ) {
+        m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
+        if (debug) msg() << "Unexpected number of user header words: "
+                         << headerWords << endreq;
+        continue;
+      }
+      for (int i = 0; i < headerWords; ++i) ++payload;
+    }
+
+    // Loop over sub-blocks if there are any
+
+    unsigned int rodErr = L1CaloSubBlock::ERROR_NONE;
+    while (payload != payloadEnd) {
+      
+      if (L1CaloSubBlock::wordType(*payload) == L1CaloSubBlock::HEADER) {
+        m_subBlock->clear();
+        payload = m_subBlock->read(payload, payloadEnd);
+        if (debug) {
+          msg() << "CPM RoI sub-block: Crate " << m_subBlock->crate()
+                << "  Module " << m_subBlock->module() << endreq;
+        }
+        // Unpack sub-block
+        if (m_subBlock->dataWords() && !m_subBlock->unpack()) {
+          if (debug) {
+            std::string errMsg(m_subBlock->unpackErrorMsg());
+	    msg() << "CPM RoI sub-block unpacking failed: " << errMsg << endreq;
+	  }
+	  rodErr = m_subBlock->unpackErrorCode();
+	  break;
+        }
+	const int numChips = 8;
+	const int numLocs  = 2;
+	for (int chip = 0; chip < numChips; ++chip) {
+	  for (int loc = 0; loc < numLocs; ++loc) {
+	    const LVL1::CPMRoI roi = m_subBlock->roi(chip, loc);
+            if (roi.hits() || roi.error()) {
+              roiCollection->push_back(new LVL1::CPMRoI(roi));
+            }
+          }
+        }
+      } else {
+        // Just RoI word
+	LVL1::CPMRoI roi;
+	if (roi.setRoiWord(*payload)) {
+          if (roi.crate() != rodCrate - m_crateOffsetHw) {
+	    if (debug) msg() << "Inconsistent RoI crate number: "
+	                     << roi.crate() << endreq;
+            rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	    break;
+          }
+	  if (roi.cpm() == 0 || roi.cpm() > m_modules) {
+	    if (debug) msg() << "Invalid CPM number: "
+	                     << roi.cpm() << endreq;
+            rodErr = L1CaloSubBlock::ERROR_MODULE_NUMBER;
+	    break;
+          }
+	  const uint32_t location = (*payload) & 0xfffc0000;
+          if (dupRoiCheck.insert(location).second) {
+	    if (roi.hits() || roi.error()) {
+	      roiCollection->push_back(new LVL1::CPMRoI(*payload));
+	    }
+	  } else {
+	    if (debug) msg() << "Duplicate RoI word "
+	                     << MSG::hex << *payload << MSG::dec << endreq;
+            rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    break;
+          }
+	} else {
+	  if (debug) msg() << "Invalid RoI word "
+	                   << MSG::hex << *payload << MSG::dec << endreq;
+	  rodErr = L1CaloSubBlock::ERROR_ROI_TYPE;
+	  break;
+        }
+	++payload;
+      }
+    }
+    if (rodErr != L1CaloSubBlock::ERROR_NONE)
+                                        m_errorTool->rodError(robid, rodErr);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Convert CPM RoI to bytestream
+
+StatusCode CpmRoiByteStreamTool::convert(
+           const DataVector<LVL1::CPMRoI>* const roiCollection,
+	   RawEventWrite* const re)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Clear the event assembler
+
+  m_fea->clear();
+  const uint16_t minorVersion = m_srcIdMap->minorVersion();
+  m_fea->setRodMinorVersion(minorVersion);
+  m_rodStatusMap.clear();
+
+  // Pointer to ROD data vector
+
+  FullEventAssembler<L1CaloSrcIdMap>::RODDATA* theROD = 0;
+
+  // Set up the container map
+
+  setupCpmRoiMap(roiCollection);
+  CpmRoiMap::const_iterator mapIter    = m_roiMap.begin();
+  CpmRoiMap::const_iterator mapIterEnd = m_roiMap.end();
+
+  // Loop over data
+
+  const bool neutralFormat = m_dataFormat == L1CaloSubBlock::NEUTRAL;
+  const int  modulesPerSlink = m_modules / m_slinks;
+  for (int crate=0; crate < m_crates; ++crate) {
+    const int hwCrate = crate + m_crateOffsetHw;
+
+    // CPM modules are numbered 1 to m_modules
+    for (int module=1; module <= m_modules; ++module) {
+      const int mod = module - 1;
+
+      // Pack required number of modules per slink
+
+      if (mod%modulesPerSlink == 0) {
+	const int daqOrRoi = 1;
+	const int slink = (m_slinks == 2) ? 2*(mod/modulesPerSlink)
+	                                  : mod/modulesPerSlink;
+        if (debug) {
+          msg() << "Treating crate " << hwCrate
+                << " slink " << slink << endreq
+	        << "Data Version/Format: " << m_version
+	        << " " << m_dataFormat << endreq;
+        }
+	const uint32_t rodIdCpm = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+	                                                        m_subDetector);
+	theROD = m_fea->getRodData(rodIdCpm);
+	if (neutralFormat) {
+          const L1CaloUserHeader userHeader;
+	  theROD->push_back(userHeader.header());
+        }
+	m_rodStatusMap.insert(make_pair(rodIdCpm, m_rodStatus));
+      }
+      if (debug) msg() << "Module " << module << endreq;
+
+      // Create a sub-block (Neutral format only)
+
+      if (neutralFormat) {
+        m_subBlock->clear();
+	m_subBlock->setRoiHeader(m_version, hwCrate, module);
+      }
+
+      // Find CPM RoIs for this module
+
+      for (; mapIter != mapIterEnd; ++mapIter) {
+        const LVL1::CPMRoI* const roi = mapIter->second;
+	if (roi->crate() < crate)  continue;
+	if (roi->crate() > crate)  break;
+	if (roi->cpm()   < module) continue;
+	if (roi->cpm()   > module) break;
+	if (roi->hits() || roi->error()) {
+	  if (neutralFormat) m_subBlock->fillRoi(*roi);
+          else theROD->push_back(roi->roiWord());
+        }
+      }
+      
+      // Pack and write the sub-block
+
+      if (neutralFormat) {
+        if ( !m_subBlock->pack()) {
+          msg(MSG::ERROR) << "CPMRoI sub-block packing failed" << endreq;
+ 	  return StatusCode::FAILURE;
+        }
+	if (debug) {
+	  msg() << "CPMRoI sub-block data words: "
+	        << m_subBlock->dataWords() << endreq;
+        }
+        m_subBlock->write(theROD);
+      }
+    }
+  }
+
+  // Fill the raw event
+
+  m_fea->fill(re, msg());
+
+  // Set ROD status words
+
+  //L1CaloRodStatus::setStatus(re, m_rodStatusMap, m_srcIdMap);
+
+  return StatusCode::SUCCESS;
+}
+
+// Return reference to vector with all possible Source Identifiers
+
+const std::vector<uint32_t>& CpmRoiByteStreamTool::sourceIDs(
+                                                      const std::string& sgKey)
+{
+  const std::string flag("RoIB");
+  const std::string::size_type pos = sgKey.find(flag);
+  const bool roiDaq =
+           (pos == std::string::npos || pos != sgKey.length() - flag.length());
+  const bool empty  = (roiDaq) ? m_sourceIDs.empty() : m_sourceIDsRoIB.empty();
+  if (empty) {
+    const int maxCrates = m_crates + m_crateOffsetHw;
+    const int maxSlinks = m_srcIdMap->maxSlinks();
+    for (int hwCrate = m_crateOffsetHw; hwCrate < maxCrates; ++hwCrate) {
+      for (int slink = 0; slink < maxSlinks; ++slink) {
+        const int daqOrRoi = 1;
+        const uint32_t rodId = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+                                                             m_subDetector);
+        const uint32_t robId = m_srcIdMap->getRobID(rodId);
+	if (roiDaq) {
+	  if (slink < 2) m_sourceIDs.push_back(robId);
+	} else if (slink >= 2) m_sourceIDsRoIB.push_back(robId);
+      }
+    }
+  }
+  return (roiDaq) ? m_sourceIDs : m_sourceIDsRoIB;
+}
+
+// Set up CPM RoI map
+
+void CpmRoiByteStreamTool::setupCpmRoiMap(const CpmRoiCollection*
+                                                          const roiCollection)
+{
+  m_roiMap.clear();
+  if (roiCollection) {
+    CpmRoiCollection::const_iterator pos  = roiCollection->begin();
+    CpmRoiCollection::const_iterator pose = roiCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CPMRoI* const roi = *pos;
+      const uint32_t key = roi->roiWord();
+      m_roiMap.insert(std::make_pair(key, roi));
+    }
+  }
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamTool.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamTool.h
new file mode 100755
index 0000000000000000000000000000000000000000..50dff2a413b055788812af4b396e2380fffa75a4
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamTool.h
@@ -0,0 +1,120 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPMROIBYTESTREAMTOOL_H
+#define TRIGT1CALOBYTESTREAM_CPMROIBYTESTREAMTOOL_H
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+#include "ByteStreamData/RawEvent.h"
+#include "DataModel/DataVector.h"
+#include "eformat/SourceIdentifier.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class IInterface;
+class InterfaceID;
+class StatusCode;
+
+template <typename> class FullEventAssembler;
+
+namespace LVL1 {
+  class CPMRoI;
+}
+
+namespace LVL1BS {
+
+class CpmRoiSubBlock;
+class L1CaloErrorByteStreamTool;
+class L1CaloSrcIdMap;
+
+/** Tool to perform ROB fragments to CPM RoI and CPM RoI to raw data
+ *  conversions.
+ *
+ *  Based on ROD document version 1_09h.
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpmRoiByteStreamTool : public AthAlgTool {
+
+ public:
+   CpmRoiByteStreamTool(const std::string& type, const std::string& name,
+                        const IInterface* parent);
+   virtual ~CpmRoiByteStreamTool();
+
+   /// AlgTool InterfaceID
+   static const InterfaceID& interfaceID();
+
+   virtual StatusCode initialize();
+   virtual StatusCode finalize();
+
+   /// Convert ROB fragments to CPM RoIs
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::CPMRoI>* roiCollection);
+
+   /// Convert CPM RoI to bytestream
+   StatusCode convert(const DataVector<LVL1::CPMRoI>* roiCollection,
+                      RawEventWrite* re);
+
+   /// Return reference to vector with all possible Source Identifiers
+   const std::vector<uint32_t>& sourceIDs(const std::string& sgKey);
+
+ private:
+
+   typedef DataVector<LVL1::CPMRoI>                      CpmRoiCollection;
+   typedef std::map<uint32_t, const LVL1::CPMRoI*>       CpmRoiMap;
+   typedef IROBDataProviderSvc::VROBFRAG::const_iterator ROBIterator;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      ROBPointer;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      RODPointer;
+
+   /// Set up CPM RoI map
+   void setupCpmRoiMap(const CpmRoiCollection* roiCollection);
+
+   /// Error collection tool
+   ToolHandle<LVL1BS::L1CaloErrorByteStreamTool> m_errorTool;
+
+   /// Hardware crate number offset
+   int m_crateOffsetHw;
+   /// Software crate number offset
+   int m_crateOffsetSw;
+   /// Sub_block header version
+   int m_version;
+   /// Data compression format
+   int m_dataFormat;
+   /// Number of crates
+   int m_crates;
+   /// Number of CPM modules per crate
+   int m_modules;
+   /// Number of slinks per crate when writing out bytestream
+   int m_slinks;
+   /// ROB source IDs
+   std::vector<uint32_t> m_sourceIDs;
+   /// ROB source IDs for RoIB
+   std::vector<uint32_t> m_sourceIDsRoIB;
+   /// Sub-detector type
+   eformat::SubDetector m_subDetector;
+   /// Source ID converter
+   L1CaloSrcIdMap* m_srcIdMap;
+   /// Sub-block for neutral format
+   CpmRoiSubBlock* m_subBlock;
+   /// CPM RoI map
+   CpmRoiMap m_roiMap;
+   /// ROD Status words
+   std::vector<uint32_t>* m_rodStatus;
+   /// ROD status map
+   std::map<uint32_t, std::vector<uint32_t>* > m_rodStatusMap;
+   /// Event assembler
+   FullEventAssembler<L1CaloSrcIdMap>* m_fea;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV1Cnv.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV1Cnv.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..840c9e913ffebbebb674a82e8533c4f1127ad887
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV1Cnv.cxx
@@ -0,0 +1,178 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+#include <stdint.h>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IByteStreamEventAccess.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "DataModel/DataVector.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/IRegistry.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "TrigT1CaloEvent/CPMRoI.h"
+
+#include "CpmRoiByteStreamV1Cnv.h"
+#include "CpmRoiByteStreamV1Tool.h"
+
+namespace LVL1BS {
+
+CpmRoiByteStreamV1Cnv::CpmRoiByteStreamV1Cnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("CpmRoiByteStreamV1Cnv"),
+      m_tool("LVL1BS::CpmRoiByteStreamV1Tool/CpmRoiByteStreamV1Tool"),
+      m_robDataProvider("ROBDataProviderSvc", m_name),
+      m_ByteStreamEventAccess("ByteStreamCnvSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+CpmRoiByteStreamV1Cnv::~CpmRoiByteStreamV1Cnv()
+{
+}
+
+// CLID
+
+const CLID& CpmRoiByteStreamV1Cnv::classID()
+{
+  return ClassID_traits<DataVector<LVL1::CPMRoI> >::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode CpmRoiByteStreamV1Cnv::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << " - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  //Get ByteStreamCnvSvc
+  sc = m_ByteStreamEventAccess.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_ByteStreamEventAccess << endreq;
+    return sc;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_ByteStreamEventAccess << endreq;
+  }
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return StatusCode::FAILURE;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  // Get ROBDataProvider
+  sc = m_robDataProvider.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::WARNING << "Failed to retrieve service "
+          << m_robDataProvider << endreq;
+    // return is disabled for Write BS which does not require ROBDataProviderSvc
+    // return sc ;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_robDataProvider << endreq;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// createObj should create the RDO from bytestream.
+
+StatusCode CpmRoiByteStreamV1Cnv::createObj( IOpaqueAddress* pAddr,
+                                             DataObject*& pObj )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createObj() called" << endreq;
+
+  ByteStreamAddress *pBS_Addr;
+  pBS_Addr = dynamic_cast<ByteStreamAddress *>( pAddr );
+  if ( !pBS_Addr ) {
+    m_log << MSG::ERROR << " Can not cast to ByteStreamAddress " << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = *( pBS_Addr->par() );
+
+  if (m_debug) m_log << MSG::DEBUG << " Creating Objects " << nm << endreq;
+
+  // get SourceIDs
+  const std::vector<uint32_t>& vID(m_tool->sourceIDs(nm));
+
+  // get ROB fragments
+  IROBDataProviderSvc::VROBFRAG robFrags;
+  m_robDataProvider->getROBData( vID, robFrags );
+
+  // size check
+  DataVector<LVL1::CPMRoI>* const roiCollection = new DataVector<LVL1::CPMRoI>;
+  if (m_debug) {
+    m_log << MSG::DEBUG << " Number of ROB fragments is " << robFrags.size()
+          << endreq;
+  }
+  if (robFrags.size() == 0) {
+    pObj = SG::asStorable(roiCollection) ;
+    return StatusCode::SUCCESS;
+  }
+
+  StatusCode sc = m_tool->convert(robFrags, roiCollection);
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << " Failed to create Objects   " << nm << endreq;
+    delete roiCollection;
+    return sc;
+  }
+
+  pObj = SG::asStorable(roiCollection);
+
+  return sc;
+}
+
+// createRep should create the bytestream from RDOs.
+
+StatusCode CpmRoiByteStreamV1Cnv::createRep( DataObject* pObj,
+                                             IOpaqueAddress*& pAddr )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createRep() called" << endreq;
+
+  RawEventWrite* re = m_ByteStreamEventAccess->getRawEvent();
+
+  DataVector<LVL1::CPMRoI>* roiCollection = 0;
+  if( !SG::fromStorable( pObj, roiCollection ) ) {
+    m_log << MSG::ERROR << " Cannot cast to DataVector<CPMRoI>" << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = pObj->registry()->name();
+
+  ByteStreamAddress* addr = new ByteStreamAddress( classID(), nm, "" );
+
+  pAddr = addr;
+
+  // Convert to ByteStream
+  return m_tool->convert( roiCollection, re );
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV1Cnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV1Cnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..60a6c0ff31026ae1ec785b8275cd47c40db50d48
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV1Cnv.h
@@ -0,0 +1,81 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPMROIBYTESTREAMV1CNV_H
+#define TRIGT1CALOBYTESTREAM_CPMROIBYTESTREAMV1CNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IByteStreamEventAccess;
+class IOpaqueAddress;
+class IROBDataProviderSvc;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class CpmRoiByteStreamV1Tool;
+
+/** ByteStream converter for Cluster Processor Module RoIs.
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpmRoiByteStreamV1Cnv: public Converter {
+
+  friend class CnvFactory<CpmRoiByteStreamV1Cnv>;
+
+protected:
+
+  CpmRoiByteStreamV1Cnv(ISvcLocator* svcloc);
+
+public:
+
+  ~CpmRoiByteStreamV1Cnv();
+
+  virtual StatusCode initialize();
+  /// Create CPM RoIs from ByteStream
+  virtual StatusCode createObj(IOpaqueAddress* pAddr, DataObject*& pObj);
+  /// Create ByteStream from CPM RoIs
+  virtual StatusCode createRep(DataObject* pObj, IOpaqueAddress*& pAddr);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::CpmRoiByteStreamV1Tool> m_tool;
+
+  /// Service for reading bytestream
+  ServiceHandle<IROBDataProviderSvc> m_robDataProvider;
+  /// Service for writing bytestream
+  ServiceHandle<IByteStreamEventAccess> m_ByteStreamEventAccess;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV1Tool.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV1Tool.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..3f6d4851491a7fc27f4205c78e7f7c9dcb2cea46
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV1Tool.cxx
@@ -0,0 +1,444 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <numeric>
+#include <set>
+#include <utility>
+
+#include "ByteStreamCnvSvcBase/FullEventAssembler.h"
+
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "TrigT1CaloEvent/CPMRoI.h"
+
+#include "CpmRoiSubBlockV1.h"
+#include "L1CaloErrorByteStreamTool.h"
+#include "L1CaloSrcIdMap.h"
+#include "L1CaloUserHeader.h"
+
+#include "CpmRoiByteStreamV1Tool.h"
+
+namespace LVL1BS {
+
+// Interface ID
+
+static const InterfaceID IID_ICpmRoiByteStreamV1Tool("CpmRoiByteStreamV1Tool",
+                                                                        1, 1);
+
+const InterfaceID& CpmRoiByteStreamV1Tool::interfaceID()
+{
+  return IID_ICpmRoiByteStreamV1Tool;
+}
+
+// Constructor
+
+CpmRoiByteStreamV1Tool::CpmRoiByteStreamV1Tool(const std::string& type,
+                                               const std::string& name,
+                                               const IInterface*  parent)
+  : AthAlgTool(type, name, parent),
+    m_errorTool("LVL1BS::L1CaloErrorByteStreamTool/L1CaloErrorByteStreamTool"),
+    m_crates(4), m_modules(14), m_srcIdMap(0), m_subBlock(0), m_rodStatus(0),
+    m_fea(0)
+{
+  declareInterface<CpmRoiByteStreamV1Tool>(this);
+
+  declareProperty("ErrorTool", m_errorTool,
+                  "Tool to collect errors for monitoring");
+  declareProperty("CrateOffsetHw",  m_crateOffsetHw = 8,
+                  "Offset of CP crate numbers in bytestream");
+  declareProperty("CrateOffsetSw",  m_crateOffsetSw = 0,
+                  "Offset of CP crate numbers in RDOs");
+
+  // Properties for reading bytestream only
+  declareProperty("ROBSourceIDs",       m_sourceIDs,
+                  "ROB fragment source identifiers");
+  declareProperty("ROBSourceIDsRoIB",   m_sourceIDsRoIB,
+                  "ROB fragment source identifiers for RoIBs");
+
+  // Properties for writing bytestream only
+  declareProperty("DataVersion",    m_version       = 1,
+                  "Format version number in sub-block header");
+  declareProperty("DataFormat",     m_dataFormat    = 1,
+                  "Format identifier (0-1) in sub-block header");
+  declareProperty("SlinksPerCrate", m_slinks        = 1,
+                  "The number of S-Links per crate");
+  declareProperty("CrateMin",       m_crateMin = 0,
+                  "Minimum crate number, allows partial output");
+  declareProperty("CrateMax",       m_crateMax = m_crates-1,
+                  "Maximum crate number, allows partial output");
+
+}
+
+// Destructor
+
+CpmRoiByteStreamV1Tool::~CpmRoiByteStreamV1Tool()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode CpmRoiByteStreamV1Tool::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = m_errorTool.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_errorTool << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_errorTool << endreq;
+
+  m_subDetector = eformat::TDAQ_CALO_CLUSTER_PROC_ROI;
+  m_srcIdMap    = new L1CaloSrcIdMap();
+  m_rodStatus   = new std::vector<uint32_t>(2);
+  m_subBlock    = new CpmRoiSubBlockV1();
+  m_fea         = new FullEventAssembler<L1CaloSrcIdMap>();
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode CpmRoiByteStreamV1Tool::finalize()
+{
+  delete m_fea;
+  delete m_subBlock;
+  delete m_rodStatus;
+  delete m_srcIdMap;
+  return StatusCode::SUCCESS;
+}
+
+// Convert ROB fragments to CPM RoIs
+
+StatusCode CpmRoiByteStreamV1Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::CPMRoI>* const roiCollection)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Loop over ROB fragments
+
+  int robCount = 0;
+  std::set<uint32_t> dupCheck;
+  std::set<uint32_t> dupRoiCheck;
+  ROBIterator rob    = robFrags.begin();
+  ROBIterator robEnd = robFrags.end();
+  for (; rob != robEnd; ++rob) {
+
+    if (debug) {
+      ++robCount;
+      msg() << "Treating ROB fragment " << robCount << endreq;
+    }
+
+    // Skip fragments with ROB status errors
+
+    uint32_t robid = (*rob)->source_id();
+    if ((*rob)->nstatus() > 0) {
+      ROBPointer robData;
+      (*rob)->status(robData);
+      if (*robData != 0) {
+        m_errorTool->robError(robid, *robData);
+	if (debug) msg() << "ROB status error - skipping fragment" << endreq;
+	continue;
+      }
+    }
+
+    // Skip duplicate fragments
+
+    if (!dupCheck.insert(robid).second) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_DUPLICATE_ROB);
+      if (debug) msg() << "Skipping duplicate ROB fragment" << endreq;
+      continue;
+    }
+
+    // Unpack ROD data (slinks)
+
+    RODPointer payloadBeg;
+    RODPointer payload;
+    RODPointer payloadEnd;
+    (*rob)->rod_data(payloadBeg);
+    payloadEnd = payloadBeg + (*rob)->rod_ndata();
+    payload = payloadBeg;
+    if (payload == payloadEnd) {
+      if (debug) msg() << "ROB fragment empty" << endreq;
+      continue;
+    }
+
+    // Check identifier
+    const uint32_t sourceID = (*rob)->rod_source_id();
+    if (m_srcIdMap->getRobID(sourceID) != robid           ||
+        m_srcIdMap->subDet(sourceID)   != m_subDetector   ||
+        m_srcIdMap->daqOrRoi(sourceID) != 1               ||
+       (m_srcIdMap->slink(sourceID) != 0 && m_srcIdMap->slink(sourceID) != 2) ||
+	m_srcIdMap->crate(sourceID)    <  m_crateOffsetHw ||
+	m_srcIdMap->crate(sourceID)    >= m_crateOffsetHw + m_crates) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_ROD_ID);
+      if (debug) {
+        msg() << "Wrong source identifier in data: "
+              << MSG::hex << sourceID << MSG::dec << endreq;
+      }
+      continue;
+    }
+
+    // Check minor version
+    const int minorVersion = (*rob)->rod_version() & 0xffff;
+    if (minorVersion > m_srcIdMap->minorVersionPreLS1()) {
+      if (debug) msg() << "Skipping post-LS1 data" << endreq;
+      continue;
+    }
+    const int rodCrate = m_srcIdMap->crate(sourceID);
+    if (debug) {
+      msg() << "Treating crate " << rodCrate 
+            << " slink " << m_srcIdMap->slink(sourceID) << endreq;
+    }
+
+    // First word may be User Header
+    if (L1CaloUserHeader::isValid(*payload)) {
+      L1CaloUserHeader userHeader(*payload);
+      userHeader.setVersion(minorVersion);
+      const int headerWords = userHeader.words();
+      if (headerWords != 1 ) {
+        m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
+        if (debug) msg() << "Unexpected number of user header words: "
+                         << headerWords << endreq;
+        continue;
+      }
+      for (int i = 0; i < headerWords; ++i) ++payload;
+    }
+
+    // Loop over sub-blocks if there are any
+
+    unsigned int rodErr = L1CaloSubBlock::ERROR_NONE;
+    while (payload != payloadEnd) {
+      
+      if (L1CaloSubBlock::wordType(*payload) == L1CaloSubBlock::HEADER) {
+        m_subBlock->clear();
+        payload = m_subBlock->read(payload, payloadEnd);
+        if (debug) {
+          msg() << "CPM RoI sub-block: Crate " << m_subBlock->crate()
+                << "  Module " << m_subBlock->module() << endreq;
+        }
+        // Unpack sub-block
+        if (m_subBlock->dataWords() && !m_subBlock->unpack()) {
+          if (debug) {
+            std::string errMsg(m_subBlock->unpackErrorMsg());
+	    msg() << "CPM RoI sub-block unpacking failed: " << errMsg << endreq;
+	  }
+	  rodErr = m_subBlock->unpackErrorCode();
+	  break;
+        }
+	const int numChips = 8;
+	const int numLocs  = 2;
+	for (int chip = 0; chip < numChips; ++chip) {
+	  for (int loc = 0; loc < numLocs; ++loc) {
+	    const LVL1::CPMRoI roi = m_subBlock->roi(chip, loc);
+            if (roi.hits() || roi.error()) {
+              roiCollection->push_back(new LVL1::CPMRoI(roi));
+            }
+          }
+        }
+      } else {
+        // Just RoI word
+	LVL1::CPMRoI roi;
+	if (roi.setRoiWord(*payload)) {
+          if (roi.crate() != rodCrate - m_crateOffsetHw) {
+	    if (debug) msg() << "Inconsistent RoI crate number: "
+	                     << roi.crate() << endreq;
+            rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	    break;
+          }
+	  if (roi.cpm() == 0 || roi.cpm() > m_modules) {
+	    if (debug) msg() << "Invalid CPM number: "
+	                     << roi.cpm() << endreq;
+            rodErr = L1CaloSubBlock::ERROR_MODULE_NUMBER;
+	    break;
+          }
+	  const uint32_t location = (*payload) & 0xfffc0000;
+          if (dupRoiCheck.insert(location).second) {
+	    if (roi.hits() || roi.error()) {
+	      roiCollection->push_back(new LVL1::CPMRoI(*payload));
+	    }
+	  } else {
+	    if (debug) msg() << "Duplicate RoI word "
+	                     << MSG::hex << *payload << MSG::dec << endreq;
+            rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    break;
+	  }
+	} else {
+	  if (debug) msg() << "Invalid RoI word "
+	                   << MSG::hex << *payload << MSG::dec << endreq;
+          rodErr = L1CaloSubBlock::ERROR_ROI_TYPE;
+	  break;
+        }
+	++payload;
+      }
+    }
+    if (rodErr != L1CaloSubBlock::ERROR_NONE)
+                                        m_errorTool->rodError(robid, rodErr);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Convert CPM RoI to bytestream
+
+StatusCode CpmRoiByteStreamV1Tool::convert(
+           const DataVector<LVL1::CPMRoI>* const roiCollection,
+	   RawEventWrite* const re)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Clear the event assembler
+
+  m_fea->clear();
+  uint16_t minorVersion = m_srcIdMap->minorVersionPreLS1();
+  m_fea->setRodMinorVersion(minorVersion);
+  m_rodStatusMap.clear();
+
+  // Pointer to ROD data vector
+
+  FullEventAssembler<L1CaloSrcIdMap>::RODDATA* theROD = 0;
+
+  // Set up the container map
+
+  setupCpmRoiMap(roiCollection);
+  CpmRoiMap::const_iterator mapIter    = m_roiMap.begin();
+  CpmRoiMap::const_iterator mapIterEnd = m_roiMap.end();
+
+  // Loop over data
+
+  const bool neutralFormat = m_dataFormat == L1CaloSubBlock::NEUTRAL;
+  const int  modulesPerSlink = m_modules / m_slinks;
+  for (int crate = m_crateMin; crate <= m_crateMax; ++crate) {
+    const int hwCrate = crate + m_crateOffsetHw;
+
+    // CPM modules are numbered 1 to m_modules
+    for (int module=1; module <= m_modules; ++module) {
+      const int mod = module - 1;
+
+      // Pack required number of modules per slink
+
+      if (mod%modulesPerSlink == 0) {
+	const int daqOrRoi = 1;
+	const int slink = (m_slinks == 2) ? 2*(mod/modulesPerSlink)
+	                                  : mod/modulesPerSlink;
+        if (debug) {
+          msg() << "Treating crate " << hwCrate
+                << " slink " << slink << endreq
+	        << "Data Version/Format: " << m_version
+	        << " " << m_dataFormat << endreq;
+        }
+	const uint32_t rodIdCpm = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+	                                                        m_subDetector);
+	theROD = m_fea->getRodData(rodIdCpm);
+	if (neutralFormat) {
+          const L1CaloUserHeader userHeader;
+	  theROD->push_back(userHeader.header());
+        }
+	m_rodStatusMap.insert(make_pair(rodIdCpm, m_rodStatus));
+      }
+      if (debug) msg() << "Module " << module << endreq;
+
+      // Create a sub-block (Neutral format only)
+
+      if (neutralFormat) {
+        m_subBlock->clear();
+	m_subBlock->setRoiHeader(m_version, hwCrate, module);
+      }
+
+      // Find CPM RoIs for this module
+
+      for (; mapIter != mapIterEnd; ++mapIter) {
+        const LVL1::CPMRoI* const roi = mapIter->second;
+	if (roi->crate() < crate)  continue;
+	if (roi->crate() > crate)  break;
+	if (roi->cpm()   < module) continue;
+	if (roi->cpm()   > module) break;
+	if (roi->hits() || roi->error()) {
+	  if (neutralFormat) m_subBlock->fillRoi(*roi);
+          else theROD->push_back(roi->roiWord());
+        }
+      }
+      
+      // Pack and write the sub-block
+
+      if (neutralFormat) {
+        if ( !m_subBlock->pack()) {
+          msg(MSG::ERROR) << "CPMRoI sub-block packing failed" << endreq;
+ 	  return StatusCode::FAILURE;
+        }
+	if (debug) {
+	  msg() << "CPMRoI sub-block data words: "
+	        << m_subBlock->dataWords() << endreq;
+        }
+        m_subBlock->write(theROD);
+      }
+    }
+  }
+
+  // Fill the raw event
+
+  m_fea->fill(re, msg());
+
+  // Set ROD status words
+
+  //L1CaloRodStatus::setStatus(re, m_rodStatusMap, m_srcIdMap);
+
+  return StatusCode::SUCCESS;
+}
+
+// Return reference to vector with all possible Source Identifiers
+
+const std::vector<uint32_t>& CpmRoiByteStreamV1Tool::sourceIDs(
+                                                      const std::string& sgKey)
+{
+  const std::string flag("RoIB");
+  const std::string::size_type pos = sgKey.find(flag);
+  const bool roiDaq =
+           (pos == std::string::npos || pos != sgKey.length() - flag.length());
+  const bool empty  = (roiDaq) ? m_sourceIDs.empty() : m_sourceIDsRoIB.empty();
+  if (empty) {
+    const int maxCrates = m_crates + m_crateOffsetHw;
+    const int maxSlinks = m_srcIdMap->maxSlinks();
+    for (int hwCrate = m_crateOffsetHw; hwCrate < maxCrates; ++hwCrate) {
+      for (int slink = 0; slink < maxSlinks; ++slink) {
+        const int daqOrRoi = 1;
+        const uint32_t rodId = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+                                                             m_subDetector);
+        const uint32_t robId = m_srcIdMap->getRobID(rodId);
+	if (roiDaq) {
+	  if (slink < 2) m_sourceIDs.push_back(robId);
+	} else if (slink >= 2) m_sourceIDsRoIB.push_back(robId);
+      }
+    }
+  }
+  return (roiDaq) ? m_sourceIDs : m_sourceIDsRoIB;
+}
+
+// Set up CPM RoI map
+
+void CpmRoiByteStreamV1Tool::setupCpmRoiMap(const CpmRoiCollection*
+                                                          const roiCollection)
+{
+  m_roiMap.clear();
+  if (roiCollection) {
+    CpmRoiCollection::const_iterator pos  = roiCollection->begin();
+    CpmRoiCollection::const_iterator pose = roiCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CPMRoI* const roi = *pos;
+      const uint32_t key = roi->roiWord();
+      m_roiMap.insert(std::make_pair(key, roi));
+    }
+  }
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV1Tool.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV1Tool.h
new file mode 100755
index 0000000000000000000000000000000000000000..42420950da954778cc109421db658ca324e9cf9a
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV1Tool.h
@@ -0,0 +1,124 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPMROIBYTESTREAMV1TOOL_H
+#define TRIGT1CALOBYTESTREAM_CPMROIBYTESTREAMV1TOOL_H
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+#include "ByteStreamData/RawEvent.h"
+#include "DataModel/DataVector.h"
+#include "eformat/SourceIdentifier.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class IInterface;
+class InterfaceID;
+class StatusCode;
+
+template <typename> class FullEventAssembler;
+
+namespace LVL1 {
+  class CPMRoI;
+}
+
+namespace LVL1BS {
+
+class CpmRoiSubBlockV1;
+class L1CaloErrorByteStreamTool;
+class L1CaloSrcIdMap;
+
+/** Tool to perform ROB fragments to CPM RoI and CPM RoI to raw data
+ *  conversions.
+ *
+ *  Based on ROD document version 1_09h.
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpmRoiByteStreamV1Tool : public AthAlgTool {
+
+ public:
+   CpmRoiByteStreamV1Tool(const std::string& type, const std::string& name,
+                          const IInterface* parent);
+   virtual ~CpmRoiByteStreamV1Tool();
+
+   /// AlgTool InterfaceID
+   static const InterfaceID& interfaceID();
+
+   virtual StatusCode initialize();
+   virtual StatusCode finalize();
+
+   /// Convert ROB fragments to CPM RoIs
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::CPMRoI>* roiCollection);
+
+   /// Convert CPM RoI to bytestream
+   StatusCode convert(const DataVector<LVL1::CPMRoI>* roiCollection,
+                      RawEventWrite* re);
+
+   /// Return reference to vector with all possible Source Identifiers
+   const std::vector<uint32_t>& sourceIDs(const std::string& sgKey);
+
+ private:
+
+   typedef DataVector<LVL1::CPMRoI>                      CpmRoiCollection;
+   typedef std::map<uint32_t, const LVL1::CPMRoI*>       CpmRoiMap;
+   typedef IROBDataProviderSvc::VROBFRAG::const_iterator ROBIterator;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      ROBPointer;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      RODPointer;
+
+   /// Set up CPM RoI map
+   void setupCpmRoiMap(const CpmRoiCollection* roiCollection);
+
+   /// Error collection tool
+   ToolHandle<LVL1BS::L1CaloErrorByteStreamTool> m_errorTool;
+
+   /// Hardware crate number offset
+   int m_crateOffsetHw;
+   /// Software crate number offset
+   int m_crateOffsetSw;
+   /// Sub_block header version
+   int m_version;
+   /// Data compression format
+   int m_dataFormat;
+   /// Number of crates
+   int m_crates;
+   /// Number of CPM modules per crate
+   int m_modules;
+   /// Number of slinks per crate when writing out bytestream
+   int m_slinks;
+   /// Minimum crate number when writing out bytestream
+   int m_crateMin;
+   /// Maximum crate number when writing out bytestream
+   int m_crateMax;
+   /// ROB source IDs
+   std::vector<uint32_t> m_sourceIDs;
+   /// ROB source IDs for RoIB
+   std::vector<uint32_t> m_sourceIDsRoIB;
+   /// Sub-detector type
+   eformat::SubDetector m_subDetector;
+   /// Source ID converter
+   L1CaloSrcIdMap* m_srcIdMap;
+   /// Sub-block for neutral format
+   CpmRoiSubBlockV1* m_subBlock;
+   /// CPM RoI map
+   CpmRoiMap m_roiMap;
+   /// ROD Status words
+   std::vector<uint32_t>* m_rodStatus;
+   /// ROD status map
+   std::map<uint32_t, std::vector<uint32_t>* > m_rodStatusMap;
+   /// Event assembler
+   FullEventAssembler<L1CaloSrcIdMap>* m_fea;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV2Cnv.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV2Cnv.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..1488112b394b1213625d5d4fd9aeee994ec2f714
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV2Cnv.cxx
@@ -0,0 +1,178 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+#include <stdint.h>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IByteStreamEventAccess.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "DataModel/DataVector.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/IRegistry.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "TrigT1CaloEvent/CPMTobRoI.h"
+
+#include "CpmRoiByteStreamV2Cnv.h"
+#include "CpmRoiByteStreamV2Tool.h"
+
+namespace LVL1BS {
+
+CpmRoiByteStreamV2Cnv::CpmRoiByteStreamV2Cnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("CpmRoiByteStreamV2Cnv"),
+      m_tool("LVL1BS::CpmRoiByteStreamV2Tool/CpmRoiByteStreamV2Tool"),
+      m_robDataProvider("ROBDataProviderSvc", m_name),
+      m_ByteStreamEventAccess("ByteStreamCnvSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+CpmRoiByteStreamV2Cnv::~CpmRoiByteStreamV2Cnv()
+{
+}
+
+// CLID
+
+const CLID& CpmRoiByteStreamV2Cnv::classID()
+{
+  return ClassID_traits<DataVector<LVL1::CPMTobRoI> >::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode CpmRoiByteStreamV2Cnv::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << " - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  //Get ByteStreamCnvSvc
+  sc = m_ByteStreamEventAccess.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_ByteStreamEventAccess << endreq;
+    return sc;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_ByteStreamEventAccess << endreq;
+  }
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return StatusCode::FAILURE;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  // Get ROBDataProvider
+  sc = m_robDataProvider.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::WARNING << "Failed to retrieve service "
+          << m_robDataProvider << endreq;
+    // return is disabled for Write BS which does not require ROBDataProviderSvc
+    // return sc ;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_robDataProvider << endreq;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// createObj should create the RDO from bytestream.
+
+StatusCode CpmRoiByteStreamV2Cnv::createObj( IOpaqueAddress* pAddr,
+                                        DataObject*& pObj )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createObj() called" << endreq;
+
+  ByteStreamAddress *pBS_Addr;
+  pBS_Addr = dynamic_cast<ByteStreamAddress *>( pAddr );
+  if ( !pBS_Addr ) {
+    m_log << MSG::ERROR << " Can not cast to ByteStreamAddress " << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = *( pBS_Addr->par() );
+
+  if (m_debug) m_log << MSG::DEBUG << " Creating Objects " << nm << endreq;
+
+  // get SourceIDs
+  const std::vector<uint32_t>& vID(m_tool->sourceIDs(nm));
+
+  // get ROB fragments
+  IROBDataProviderSvc::VROBFRAG robFrags;
+  m_robDataProvider->getROBData( vID, robFrags );
+
+  // size check
+  DataVector<LVL1::CPMTobRoI>* const roiCollection = new DataVector<LVL1::CPMTobRoI>;
+  if (m_debug) {
+    m_log << MSG::DEBUG << " Number of ROB fragments is " << robFrags.size()
+          << endreq;
+  }
+  if (robFrags.size() == 0) {
+    pObj = SG::asStorable(roiCollection) ;
+    return StatusCode::SUCCESS;
+  }
+
+  StatusCode sc = m_tool->convert(robFrags, roiCollection);
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << " Failed to create Objects   " << nm << endreq;
+    delete roiCollection;
+    return sc;
+  }
+
+  pObj = SG::asStorable(roiCollection);
+
+  return sc;
+}
+
+// createRep should create the bytestream from RDOs.
+
+StatusCode CpmRoiByteStreamV2Cnv::createRep( DataObject* pObj,
+                                        IOpaqueAddress*& pAddr )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createRep() called" << endreq;
+
+  RawEventWrite* re = m_ByteStreamEventAccess->getRawEvent();
+
+  DataVector<LVL1::CPMTobRoI>* roiCollection = 0;
+  if( !SG::fromStorable( pObj, roiCollection ) ) {
+    m_log << MSG::ERROR << " Cannot cast to DataVector<CPMTobRoI>" << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = pObj->registry()->name();
+
+  ByteStreamAddress* addr = new ByteStreamAddress( classID(), nm, "" );
+
+  pAddr = addr;
+
+  // Convert to ByteStream
+  return m_tool->convert( roiCollection, re );
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV2Cnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV2Cnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..741f6db5e3b39ebbb700ba3a4cda207f04c8496d
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV2Cnv.h
@@ -0,0 +1,81 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPMROIBYTESTREAMV2CNV_H
+#define TRIGT1CALOBYTESTREAM_CPMROIBYTESTREAMV2CNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IByteStreamEventAccess;
+class IOpaqueAddress;
+class IROBDataProviderSvc;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class CpmRoiByteStreamV2Tool;
+
+/** ByteStream converter for Cluster Processor Module RoIs post LS1.
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpmRoiByteStreamV2Cnv: public Converter {
+
+  friend class CnvFactory<CpmRoiByteStreamV2Cnv>;
+
+protected:
+
+  CpmRoiByteStreamV2Cnv(ISvcLocator* svcloc);
+
+public:
+
+  ~CpmRoiByteStreamV2Cnv();
+
+  virtual StatusCode initialize();
+  /// Create CPM RoIs from ByteStream
+  virtual StatusCode createObj(IOpaqueAddress* pAddr, DataObject*& pObj);
+  /// Create ByteStream from CPM RoIs
+  virtual StatusCode createRep(DataObject* pObj, IOpaqueAddress*& pAddr);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::CpmRoiByteStreamV2Tool> m_tool;
+
+  /// Service for reading bytestream
+  ServiceHandle<IROBDataProviderSvc> m_robDataProvider;
+  /// Service for writing bytestream
+  ServiceHandle<IByteStreamEventAccess> m_ByteStreamEventAccess;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV2Tool.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV2Tool.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..9d2f51b22a48b94ade85d6e04c7bb08a01acfb14
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV2Tool.cxx
@@ -0,0 +1,466 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <numeric>
+#include <set>
+#include <utility>
+
+#include "ByteStreamCnvSvcBase/FullEventAssembler.h"
+
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "TrigT1CaloEvent/CPMTobRoI.h"
+
+#include "CpmRoiSubBlockV2.h"
+#include "L1CaloErrorByteStreamTool.h"
+#include "L1CaloSrcIdMap.h"
+#include "L1CaloUserHeader.h"
+
+#include "CpmRoiByteStreamV2Tool.h"
+
+namespace LVL1BS {
+
+// Interface ID
+
+static const InterfaceID IID_ICpmRoiByteStreamV2Tool("CpmRoiByteStreamV2Tool",
+                                                                        1, 1);
+
+const InterfaceID& CpmRoiByteStreamV2Tool::interfaceID()
+{
+  return IID_ICpmRoiByteStreamV2Tool;
+}
+
+// Constructor
+
+CpmRoiByteStreamV2Tool::CpmRoiByteStreamV2Tool(const std::string& type,
+                                               const std::string& name,
+                                               const IInterface*  parent)
+  : AthAlgTool(type, name, parent),
+    m_errorTool("LVL1BS::L1CaloErrorByteStreamTool/L1CaloErrorByteStreamTool"),
+    m_crates(4), m_modules(14), m_srcIdMap(0), m_subBlock(0), m_rodStatus(0),
+    m_fea(0)
+{
+  declareInterface<CpmRoiByteStreamV2Tool>(this);
+
+  declareProperty("ErrorTool", m_errorTool,
+                  "Tool to collect errors for monitoring");
+  declareProperty("CrateOffsetHw",  m_crateOffsetHw = 8,
+                  "Offset of CP crate numbers in bytestream");
+  declareProperty("CrateOffsetSw",  m_crateOffsetSw = 0,
+                  "Offset of CP crate numbers in RDOs");
+
+  // Properties for reading bytestream only
+  declareProperty("ROBSourceIDs",       m_sourceIDs,
+                  "ROB fragment source identifiers");
+  declareProperty("ROBSourceIDsRoIB",   m_sourceIDsRoIB,
+                  "ROB fragment source identifiers for RoIBs");
+
+  // Properties for writing bytestream only
+  declareProperty("DataVersion",    m_version       = 2,
+                  "Format version number in sub-block header");
+  declareProperty("DataFormat",     m_dataFormat    = 1,
+                  "Format identifier (0-1) in sub-block header");
+  declareProperty("SlinksPerCrate", m_slinks        = 1,
+                  "The number of S-Links per crate");
+  declareProperty("CrateMin",       m_crateMin = 0,
+                  "Minimum crate number, allows partial output");
+  declareProperty("CrateMax",       m_crateMax = m_crates-1,
+                  "Maximum crate number, allows partial output");
+
+}
+
+// Destructor
+
+CpmRoiByteStreamV2Tool::~CpmRoiByteStreamV2Tool()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode CpmRoiByteStreamV2Tool::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = m_errorTool.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_errorTool << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_errorTool << endreq;
+
+  m_subDetector = eformat::TDAQ_CALO_CLUSTER_PROC_ROI;
+  m_srcIdMap    = new L1CaloSrcIdMap();
+  m_rodStatus   = new std::vector<uint32_t>(2);
+  m_subBlock    = new CpmRoiSubBlockV2();
+  m_fea         = new FullEventAssembler<L1CaloSrcIdMap>();
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode CpmRoiByteStreamV2Tool::finalize()
+{
+  delete m_fea;
+  delete m_subBlock;
+  delete m_rodStatus;
+  delete m_srcIdMap;
+  return StatusCode::SUCCESS;
+}
+
+// Convert ROB fragments to CPM RoIs
+
+StatusCode CpmRoiByteStreamV2Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::CPMTobRoI>* const roiCollection)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Loop over ROB fragments
+
+  int robCount = 0;
+  std::set<uint32_t> dupCheck;
+  std::set<uint32_t> dupRoiCheck;
+  ROBIterator rob    = robFrags.begin();
+  ROBIterator robEnd = robFrags.end();
+  for (; rob != robEnd; ++rob) {
+
+    if (debug) {
+      ++robCount;
+      msg() << "Treating ROB fragment " << robCount << endreq;
+    }
+
+    // Skip fragments with ROB status errors
+
+    uint32_t robid = (*rob)->source_id();
+    if ((*rob)->nstatus() > 0) {
+      ROBPointer robData;
+      (*rob)->status(robData);
+      if (*robData != 0) {
+        m_errorTool->robError(robid, *robData);
+	if (debug) msg() << "ROB status error - skipping fragment" << endreq;
+	continue;
+      }
+    }
+
+    // Skip duplicate fragments
+
+    if (!dupCheck.insert(robid).second) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_DUPLICATE_ROB);
+      if (debug) msg() << "Skipping duplicate ROB fragment" << endreq;
+      continue;
+    }
+
+    // Unpack ROD data (slinks)
+
+    RODPointer payloadBeg;
+    RODPointer payload;
+    RODPointer payloadEnd;
+    (*rob)->rod_data(payloadBeg);
+    payloadEnd = payloadBeg + (*rob)->rod_ndata();
+    payload = payloadBeg;
+    if (payload == payloadEnd) {
+      if (debug) msg() << "ROB fragment empty" << endreq;
+      continue;
+    }
+
+    // Check identifier
+    const uint32_t sourceID = (*rob)->rod_source_id();
+    if (m_srcIdMap->getRobID(sourceID) != robid           ||
+        m_srcIdMap->subDet(sourceID)   != m_subDetector   ||
+        m_srcIdMap->daqOrRoi(sourceID) != 1               ||
+       (m_srcIdMap->slink(sourceID) != 0 && m_srcIdMap->slink(sourceID) != 2) ||
+	m_srcIdMap->crate(sourceID)    <  m_crateOffsetHw ||
+	m_srcIdMap->crate(sourceID)    >= m_crateOffsetHw + m_crates) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_ROD_ID);
+      if (debug) {
+        msg() << "Wrong source identifier in data: "
+              << MSG::hex << sourceID << MSG::dec << endreq;
+      }
+      continue;
+    }
+
+    // Check minor version
+    const int minorVersion = (*rob)->rod_version() & 0xffff;
+    if (minorVersion <= m_srcIdMap->minorVersionPreLS1()) {
+      if (debug) msg() << "Skipping pre-LS1 data" << endreq;
+      continue;
+    }
+    const int rodCrate = m_srcIdMap->crate(sourceID);
+    if (debug) {
+      msg() << "Treating crate " << rodCrate 
+            << " slink " << m_srcIdMap->slink(sourceID) << endreq;
+    }
+
+    // First word may be User Header
+    if (L1CaloUserHeader::isValid(*payload)) {
+      L1CaloUserHeader userHeader(*payload);
+      userHeader.setVersion(minorVersion);
+      const int headerWords = userHeader.words();
+      if (headerWords != 1 ) {
+        m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
+        if (debug) msg() << "Unexpected number of user header words: "
+                         << headerWords << endreq;
+        continue;
+      }
+      for (int i = 0; i < headerWords; ++i) ++payload;
+    }
+
+    // Loop over sub-blocks if there are any
+
+    unsigned int rodErr = L1CaloSubBlock::ERROR_NONE;
+    while (payload != payloadEnd) {
+      
+      if (L1CaloSubBlock::wordType(*payload) == L1CaloSubBlock::HEADER) {
+        m_subBlock->clear();
+        payload = m_subBlock->read(payload, payloadEnd);
+        if (debug) {
+          msg() << "CPM RoI sub-block: Crate " << m_subBlock->crate()
+                << "  Module " << m_subBlock->module() << endreq;
+        }
+        // Unpack sub-block
+        if (m_subBlock->dataWords() && !m_subBlock->unpack()) {
+          if (debug) {
+            std::string errMsg(m_subBlock->unpackErrorMsg());
+	    msg() << "CPM RoI sub-block unpacking failed: " << errMsg << endreq;
+	  }
+	  rodErr = m_subBlock->unpackErrorCode();
+	  break;
+        }
+	const int numChips = 8;
+	const int numLocs  = 2;
+	const int numTypes = 2;
+	for (int chip = 0; chip < numChips; ++chip) {
+	  for (int loc = 0; loc < numLocs; ++loc) {
+	    for (int type = 0; type < numTypes; ++type) {
+	      const LVL1::CPMTobRoI roi = m_subBlock->roi(chip, loc, type);
+              if (roi.energy() || roi.isolation()) {
+                roiCollection->push_back(new LVL1::CPMTobRoI(roi));
+	      }
+            }
+          }
+        }
+      } else {
+        // Just RoI word
+	LVL1::CPMTobRoI roi;
+	if (roi.setRoiWord(*payload)) {
+          if (roi.crate() != rodCrate - m_crateOffsetHw) {
+	    if (debug) msg() << "Inconsistent RoI crate number: "
+	                     << roi.crate() << endreq;
+            rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	    break;
+          }
+	  if (roi.cpm() == 0 || roi.cpm() > m_modules) {
+	    if (debug) msg() << "Invalid CPM number: "
+	                     << roi.cpm() << endreq;
+            rodErr = L1CaloSubBlock::ERROR_MODULE_NUMBER;
+	    break;
+          }
+	  const uint32_t location = (*payload) & 0xffff0000;
+          if (dupRoiCheck.insert(location).second) {
+	    if (roi.energy() || roi.isolation()) {
+	      roiCollection->push_back(new LVL1::CPMTobRoI(*payload));
+	    }
+	  } else {
+	    if (debug) msg() << "Duplicate RoI word "
+	                     << MSG::hex << *payload << MSG::dec << endreq;
+            rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    break;
+          }
+	} else {
+	  if (debug) msg() << "Invalid RoI word "
+	                   << MSG::hex << *payload << MSG::dec << endreq;
+	  rodErr = L1CaloSubBlock::ERROR_ROI_TYPE;
+	  break;
+        }
+	++payload;
+      }
+    }
+    if (rodErr != L1CaloSubBlock::ERROR_NONE)
+                                        m_errorTool->rodError(robid, rodErr);
+  }
+  if (debug) {
+    msg() << "Number of RoIs read = " << roiCollection->size() << endreq;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Convert CPM RoI to bytestream
+
+StatusCode CpmRoiByteStreamV2Tool::convert(
+           const DataVector<LVL1::CPMTobRoI>* const roiCollection,
+	   RawEventWrite* const re)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Clear the event assembler
+
+  m_fea->clear();
+  const uint16_t minorVersion = m_srcIdMap->minorVersion();
+  m_fea->setRodMinorVersion(minorVersion);
+  m_rodStatusMap.clear();
+
+  // Pointer to ROD data vector
+
+  FullEventAssembler<L1CaloSrcIdMap>::RODDATA* theROD = 0;
+
+  // Set up the container map
+
+  setupCpmRoiMap(roiCollection);
+  if (debug) {
+    msg() << "Number of RoIs to be written = " << roiCollection->size()
+          << " (collection), " << m_roiMap.size() << " (map)"
+          << endreq;
+  }
+  int count = 0;
+  CpmRoiMap::const_iterator mapIter    = m_roiMap.begin();
+  CpmRoiMap::const_iterator mapIterEnd = m_roiMap.end();
+
+  // Loop over data
+
+  const bool neutralFormat = m_dataFormat == L1CaloSubBlock::NEUTRAL;
+  const int  modulesPerSlink = m_modules / m_slinks;
+  for (int crate = m_crateMin; crate <= m_crateMax; ++crate) {
+    const int hwCrate = crate + m_crateOffsetHw;
+
+    // CPM modules are numbered 1 to m_modules
+    for (int module=1; module <= m_modules; ++module) {
+      const int mod = module - 1;
+
+      // Pack required number of modules per slink
+
+      if (mod%modulesPerSlink == 0) {
+	const int daqOrRoi = 1;
+	const int slink = (m_slinks == 2) ? 2*(mod/modulesPerSlink)
+	                                  : mod/modulesPerSlink;
+        if (debug) {
+          msg() << "Treating crate " << hwCrate
+                << " slink " << slink << endreq
+	        << "Data Version/Format: " << m_version
+	        << " " << m_dataFormat << endreq;
+        }
+	const uint32_t rodIdCpm = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+	                                                        m_subDetector);
+	theROD = m_fea->getRodData(rodIdCpm);
+	if (neutralFormat) {
+          const L1CaloUserHeader userHeader;
+	  theROD->push_back(userHeader.header());
+        }
+	m_rodStatusMap.insert(make_pair(rodIdCpm, m_rodStatus));
+      }
+      if (debug) msg() << "Module " << module << endreq;
+
+      // Create a sub-block (Neutral format only)
+
+      if (neutralFormat) {
+        m_subBlock->clear();
+	m_subBlock->setRoiHeader(m_version, hwCrate, module);
+      }
+
+      // Find CPM RoIs for this module
+
+      for (; mapIter != mapIterEnd; ++mapIter) {
+        const LVL1::CPMTobRoI* const roi = mapIter->second;
+	if (roi->crate() < crate)  continue;
+	if (roi->crate() > crate)  break;
+	if (roi->cpm()   < module) continue;
+	if (roi->cpm()   > module) break;
+	if (roi->energy() || roi->isolation()) {
+	  if (neutralFormat) m_subBlock->fillRoi(*roi);
+          else theROD->push_back(roi->roiWord());
+	  ++count;
+        }
+      }
+      
+      // Pack and write the sub-block
+
+      if (neutralFormat) {
+        if ( !m_subBlock->pack()) {
+          msg(MSG::ERROR) << "CPMTobRoI sub-block packing failed" << endreq;
+ 	  return StatusCode::FAILURE;
+        }
+	if (debug) {
+	  msg() << "CPMTobRoI sub-block data words: "
+	        << m_subBlock->dataWords() << endreq;
+        }
+        m_subBlock->write(theROD);
+      }
+    }
+  }
+  if (debug) {
+    msg() << "Number of RoIs written = " << count << endreq;
+  }
+
+  // Fill the raw event
+
+  m_fea->fill(re, msg());
+  if (debug) msg() << MSG::dec; // fill seems to leave it in hex
+
+  // Set ROD status words
+
+  //L1CaloRodStatus::setStatus(re, m_rodStatusMap, m_srcIdMap);
+
+  return StatusCode::SUCCESS;
+}
+
+// Return reference to vector with all possible Source Identifiers
+
+const std::vector<uint32_t>& CpmRoiByteStreamV2Tool::sourceIDs(
+                                                      const std::string& sgKey)
+{
+  const std::string flag("RoIB");
+  const std::string::size_type pos = sgKey.find(flag);
+  const bool roiDaq =
+           (pos == std::string::npos || pos != sgKey.length() - flag.length());
+  const bool empty  = (roiDaq) ? m_sourceIDs.empty() : m_sourceIDsRoIB.empty();
+  if (empty) {
+    const int maxCrates = m_crates + m_crateOffsetHw;
+    const int maxSlinks = m_srcIdMap->maxSlinks();
+    for (int hwCrate = m_crateOffsetHw; hwCrate < maxCrates; ++hwCrate) {
+      for (int slink = 0; slink < maxSlinks; ++slink) {
+        const int daqOrRoi = 1;
+        const uint32_t rodId = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+                                                             m_subDetector);
+        const uint32_t robId = m_srcIdMap->getRobID(rodId);
+	if (roiDaq) {
+	  if (slink < 2) m_sourceIDs.push_back(robId);
+	} else if (slink >= 2) m_sourceIDsRoIB.push_back(robId);
+      }
+    }
+  }
+  return (roiDaq) ? m_sourceIDs : m_sourceIDsRoIB;
+}
+
+// Set up CPM RoI map
+
+void CpmRoiByteStreamV2Tool::setupCpmRoiMap(const CpmRoiCollection*
+                                                          const roiCollection)
+{
+  m_roiMap.clear();
+  if (roiCollection) {
+    CpmRoiCollection::const_iterator pos  = roiCollection->begin();
+    CpmRoiCollection::const_iterator pose = roiCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CPMTobRoI* const roi = *pos;
+      const int type = roi->type();
+      const int crate = roi->crate();
+      const int cpm = roi->cpm();
+      const int chip = roi->chip();
+      const int loc = roi->location()>>2;
+      const uint32_t key = (((((((crate<<4)|cpm)<<3)|chip)<<1)|loc)<<1)|type;
+      m_roiMap.insert(std::make_pair(key, roi));
+    }
+  }
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV2Tool.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV2Tool.h
new file mode 100755
index 0000000000000000000000000000000000000000..874a4f4c90fcdd3fabc323f7ee39ffddcf5b8e1e
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiByteStreamV2Tool.h
@@ -0,0 +1,124 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPMROIBYTESTREAMV2TOOL_H
+#define TRIGT1CALOBYTESTREAM_CPMROIBYTESTREAMV2TOOL_H
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+#include "ByteStreamData/RawEvent.h"
+#include "DataModel/DataVector.h"
+#include "eformat/SourceIdentifier.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class IInterface;
+class InterfaceID;
+class StatusCode;
+
+template <typename> class FullEventAssembler;
+
+namespace LVL1 {
+  class CPMTobRoI;
+}
+
+namespace LVL1BS {
+
+class CpmRoiSubBlockV2;
+class L1CaloErrorByteStreamTool;
+class L1CaloSrcIdMap;
+
+/** Tool to perform ROB fragments to CPM TOB RoI and CPM TOB RoI to raw data
+ *  conversions.
+ *
+ *  Based on ROD document version X_xxx.                                          <<== CHECK
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpmRoiByteStreamV2Tool : public AthAlgTool {
+
+ public:
+   CpmRoiByteStreamV2Tool(const std::string& type, const std::string& name,
+                          const IInterface* parent);
+   virtual ~CpmRoiByteStreamV2Tool();
+
+   /// AlgTool InterfaceID
+   static const InterfaceID& interfaceID();
+
+   virtual StatusCode initialize();
+   virtual StatusCode finalize();
+
+   /// Convert ROB fragments to CPM RoIs
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::CPMTobRoI>* roiCollection);
+
+   /// Convert CPM RoI to bytestream
+   StatusCode convert(const DataVector<LVL1::CPMTobRoI>* roiCollection,
+                      RawEventWrite* re);
+
+   /// Return reference to vector with all possible Source Identifiers
+   const std::vector<uint32_t>& sourceIDs(const std::string& sgKey);
+
+ private:
+
+   typedef DataVector<LVL1::CPMTobRoI>                   CpmRoiCollection;
+   typedef std::map<uint32_t, const LVL1::CPMTobRoI*>    CpmRoiMap;
+   typedef IROBDataProviderSvc::VROBFRAG::const_iterator ROBIterator;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      ROBPointer;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      RODPointer;
+
+   /// Set up CPM RoI map
+   void setupCpmRoiMap(const CpmRoiCollection* roiCollection);
+
+   /// Error collection tool
+   ToolHandle<LVL1BS::L1CaloErrorByteStreamTool> m_errorTool;
+
+   /// Hardware crate number offset
+   int m_crateOffsetHw;
+   /// Software crate number offset
+   int m_crateOffsetSw;
+   /// Sub_block header version
+   int m_version;
+   /// Data compression format
+   int m_dataFormat;
+   /// Number of crates
+   int m_crates;
+   /// Number of CPM modules per crate
+   int m_modules;
+   /// Number of slinks per crate when writing out bytestream
+   int m_slinks;
+   /// Minimum crate number when writing out bytestream
+   int m_crateMin;
+   /// Maximum crate number when writing out bytestream
+   int m_crateMax;
+   /// ROB source IDs
+   std::vector<uint32_t> m_sourceIDs;
+   /// ROB source IDs for RoIB
+   std::vector<uint32_t> m_sourceIDsRoIB;
+   /// Sub-detector type
+   eformat::SubDetector m_subDetector;
+   /// Source ID converter
+   L1CaloSrcIdMap* m_srcIdMap;
+   /// Sub-block for neutral format
+   CpmRoiSubBlockV2* m_subBlock;
+   /// CPM RoI map
+   CpmRoiMap m_roiMap;
+   /// ROD Status words
+   std::vector<uint32_t>* m_rodStatus;
+   /// ROD status map
+   std::map<uint32_t, std::vector<uint32_t>* > m_rodStatusMap;
+   /// Event assembler
+   FullEventAssembler<L1CaloSrcIdMap>* m_fea;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiSubBlock.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiSubBlock.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..07ef5f5f95059080f2919f5509599bdec87390eb
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiSubBlock.cxx
@@ -0,0 +1,159 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "TrigT1CaloEvent/CPMRoI.h"
+
+#include "CpmRoiSubBlock.h"
+
+namespace LVL1BS {
+
+// Constant definitions
+
+const int CpmRoiSubBlock::s_wordIdVal;
+
+const int CpmRoiSubBlock::s_glinkPins;
+const int CpmRoiSubBlock::s_hitsLen;
+const int CpmRoiSubBlock::s_errorLen;
+const int CpmRoiSubBlock::s_locationLen;
+const int CpmRoiSubBlock::s_bunchCrossingBits;
+
+
+CpmRoiSubBlock::CpmRoiSubBlock()
+{
+}
+
+CpmRoiSubBlock::~CpmRoiSubBlock()
+{
+}
+
+// Clear all data
+
+void CpmRoiSubBlock::clear()
+{
+  L1CaloSubBlock::clear();
+  m_roiData.clear();
+}
+
+// Store header
+
+void CpmRoiSubBlock::setRoiHeader(const int version, const int crate,
+                                                     const int module)
+{
+  setHeader(s_wordIdVal, version, NEUTRAL, 0, crate, module, 0, 1);
+}
+
+// Store RoI
+
+void CpmRoiSubBlock::fillRoi(const LVL1::CPMRoI roi)
+{
+  const LVL1::CPMRoI roiTemp(crate(), module(), 0, 0, 0, 0);
+  if (roi.crate() == roiTemp.crate() && roi.cpm() == roiTemp.cpm()) {
+    m_roiData.resize(s_glinkPins);
+    const int pin = (roi.chip() << 1) |
+                    ((roi.location() >> s_locationLen) & 0x1);
+    m_roiData[pin] = roi;
+  }
+}
+
+// Return RoI for given chip and location (left/right) 
+
+LVL1::CPMRoI CpmRoiSubBlock::roi(const int chip, const int loc) const
+{
+  const int pin = (chip << 1) | (loc & 0x1);
+  if (pin < s_glinkPins && !m_roiData.empty()) return m_roiData[pin];
+  else return LVL1::CPMRoI(0);
+}
+
+// Packing/Unpacking routines
+
+bool CpmRoiSubBlock::pack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = packNeutral();
+	  break;
+        default:
+	  break;
+      }
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
+
+bool CpmRoiSubBlock::unpack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = unpackNeutral();
+	  break;
+        default:
+	  setUnpackErrorCode(UNPACK_FORMAT);
+	  break;
+      }
+      break;
+    default:
+      setUnpackErrorCode(UNPACK_VERSION);
+      break;
+  }
+  return rc;
+}
+
+// Pack neutral data
+
+bool CpmRoiSubBlock::packNeutral()
+{
+  m_roiData.resize(s_glinkPins);
+  for (int pin = 0; pin < s_glinkPins; ++pin) {
+    // RoI data
+    const LVL1::CPMRoI& roi(m_roiData[pin]);
+    packerNeutral(pin, roi.hits(), s_hitsLen);
+    packerNeutral(pin, roi.error(), s_errorLen);
+    packerNeutral(pin, roi.location(), s_locationLen);
+    // Bunch Crossing number
+    if (pin < s_bunchCrossingBits) {
+      packerNeutral(pin, bunchCrossing() >> pin, 1);
+    } else packerNeutral(pin, 0, 1);
+    // G-Link parity
+    packerNeutralParity(pin);
+  }
+  return true;
+}
+
+// Unpack neutral data
+
+bool CpmRoiSubBlock::unpackNeutral()
+{
+  m_roiData.resize(s_glinkPins);
+  int bunchCrossing = 0;
+  for (int pin = 0; pin < s_glinkPins; ++pin) {
+    // RoI data
+    const int hits  = unpackerNeutral(pin, s_hitsLen);
+    const int error = unpackerNeutral(pin, s_errorLen);
+    const int loc   = unpackerNeutral(pin, s_locationLen) |
+                                           ((pin & 0x1) << s_locationLen);
+    const int chip = pin >> 1;
+    m_roiData[pin] = LVL1::CPMRoI(crate(), module(), chip, loc, hits, error);
+    // Bunch Crossing number
+    if (pin < s_bunchCrossingBits) {
+      bunchCrossing |= unpackerNeutral(pin, 1) << pin;
+    } else unpackerNeutral(pin, 1);
+    // G-Link parity error
+    unpackerNeutralParityError(pin);
+  }
+  setBunchCrossing(bunchCrossing);
+  const bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  return rc;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiSubBlock.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiSubBlock.h
new file mode 100755
index 0000000000000000000000000000000000000000..48b027a228ea41352c444cdb07a4b05ea3e872dc
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiSubBlock.h
@@ -0,0 +1,70 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPMROISUBBLOCK_H
+#define TRIGT1CALOBYTESTREAM_CPMROISUBBLOCK_H
+
+#include <vector>
+
+#include "L1CaloSubBlock.h"
+
+namespace LVL1 {
+  class CPMRoI;
+}
+
+namespace LVL1BS {
+
+/** Sub-Block class for CPM RoI data (neutral format).
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version 1.09h
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpmRoiSubBlock : public L1CaloSubBlock {
+
+ public:
+   CpmRoiSubBlock();
+   ~CpmRoiSubBlock();
+
+   /// Clear all data
+   void clear();
+
+   /// Store header
+   void setRoiHeader(int version, int crate, int module);
+   /// Store RoI
+   void fillRoi(LVL1::CPMRoI roi);
+
+   /// Return RoI for given chip and location (left/right)
+   LVL1::CPMRoI roi(int chip, int loc) const;
+
+   /// Pack data
+   bool pack();
+   /// Unpack data
+   bool unpack();
+
+ private:
+   /// Header word ID
+   static const int s_wordIdVal         = 0xc;
+   //  G-Link/Neutral format
+   static const int s_glinkPins         = 16;
+   static const int s_hitsLen           = 16;
+   static const int s_errorLen          = 2;
+   static const int s_locationLen       = 2;
+   static const int s_bunchCrossingBits = 12;
+
+   /// Pack neutral data
+   bool packNeutral();
+   /// Unpack neutral data
+   bool unpackNeutral();
+
+   /// RoI words
+   std::vector<LVL1::CPMRoI> m_roiData;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiSubBlockV1.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiSubBlockV1.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..23ac7bca1ccac8168d735d8e1f85b9e9c4f388d9
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiSubBlockV1.cxx
@@ -0,0 +1,159 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "TrigT1CaloEvent/CPMRoI.h"
+
+#include "CpmRoiSubBlockV1.h"
+
+namespace LVL1BS {
+
+// Constant definitions
+
+const int CpmRoiSubBlockV1::s_wordIdVal;
+
+const int CpmRoiSubBlockV1::s_glinkPins;
+const int CpmRoiSubBlockV1::s_hitsLen;
+const int CpmRoiSubBlockV1::s_errorLen;
+const int CpmRoiSubBlockV1::s_locationLen;
+const int CpmRoiSubBlockV1::s_bunchCrossingBits;
+
+
+CpmRoiSubBlockV1::CpmRoiSubBlockV1()
+{
+}
+
+CpmRoiSubBlockV1::~CpmRoiSubBlockV1()
+{
+}
+
+// Clear all data
+
+void CpmRoiSubBlockV1::clear()
+{
+  L1CaloSubBlock::clear();
+  m_roiData.clear();
+}
+
+// Store header
+
+void CpmRoiSubBlockV1::setRoiHeader(const int version, const int crate,
+                                                       const int module)
+{
+  setHeader(s_wordIdVal, version, NEUTRAL, 0, crate, module, 0, 1);
+}
+
+// Store RoI
+
+void CpmRoiSubBlockV1::fillRoi(const LVL1::CPMRoI roi)
+{
+  const LVL1::CPMRoI roiTemp(crate(), module(), 0, 0, 0, 0);
+  if (roi.crate() == roiTemp.crate() && roi.cpm() == roiTemp.cpm()) {
+    m_roiData.resize(s_glinkPins);
+    const int pin = (roi.chip() << 1) |
+                    ((roi.location() >> s_locationLen) & 0x1);
+    m_roiData[pin] = roi;
+  }
+}
+
+// Return RoI for given chip and location (left/right) 
+
+LVL1::CPMRoI CpmRoiSubBlockV1::roi(const int chip, const int loc) const
+{
+  const int pin = (chip << 1) | (loc & 0x1);
+  if (pin < s_glinkPins && !m_roiData.empty()) return m_roiData[pin];
+  else return LVL1::CPMRoI(0);
+}
+
+// Packing/Unpacking routines
+
+bool CpmRoiSubBlockV1::pack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = packNeutral();
+	  break;
+        default:
+	  break;
+      }
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
+
+bool CpmRoiSubBlockV1::unpack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = unpackNeutral();
+	  break;
+        default:
+	  setUnpackErrorCode(UNPACK_FORMAT);
+	  break;
+      }
+      break;
+    default:
+      setUnpackErrorCode(UNPACK_VERSION);
+      break;
+  }
+  return rc;
+}
+
+// Pack neutral data
+
+bool CpmRoiSubBlockV1::packNeutral()
+{
+  m_roiData.resize(s_glinkPins);
+  for (int pin = 0; pin < s_glinkPins; ++pin) {
+    // RoI data
+    const LVL1::CPMRoI& roi(m_roiData[pin]);
+    packerNeutral(pin, roi.hits(), s_hitsLen);
+    packerNeutral(pin, roi.error(), s_errorLen);
+    packerNeutral(pin, roi.location(), s_locationLen);
+    // Bunch Crossing number
+    if (pin < s_bunchCrossingBits) {
+      packerNeutral(pin, bunchCrossing() >> pin, 1);
+    } else packerNeutral(pin, 0, 1);
+    // G-Link parity
+    packerNeutralParity(pin);
+  }
+  return true;
+}
+
+// Unpack neutral data
+
+bool CpmRoiSubBlockV1::unpackNeutral()
+{
+  m_roiData.resize(s_glinkPins);
+  int bunchCrossing = 0;
+  for (int pin = 0; pin < s_glinkPins; ++pin) {
+    // RoI data
+    const int hits  = unpackerNeutral(pin, s_hitsLen);
+    const int error = unpackerNeutral(pin, s_errorLen);
+    const int loc   = unpackerNeutral(pin, s_locationLen) |
+                                           ((pin & 0x1) << s_locationLen);
+    const int chip = pin >> 1;
+    m_roiData[pin] = LVL1::CPMRoI(crate(), module(), chip, loc, hits, error);
+    // Bunch Crossing number
+    if (pin < s_bunchCrossingBits) {
+      bunchCrossing |= unpackerNeutral(pin, 1) << pin;
+    } else unpackerNeutral(pin, 1);
+    // G-Link parity error
+    unpackerNeutralParityError(pin);
+  }
+  setBunchCrossing(bunchCrossing);
+  const bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  return rc;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiSubBlockV1.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiSubBlockV1.h
new file mode 100755
index 0000000000000000000000000000000000000000..4ea8945f1c002a92b762fba9ae9f8680b509ca25
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiSubBlockV1.h
@@ -0,0 +1,70 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPMROISUBBLOCKV1_H
+#define TRIGT1CALOBYTESTREAM_CPMROISUBBLOCKV1_H
+
+#include <vector>
+
+#include "L1CaloSubBlock.h"
+
+namespace LVL1 {
+  class CPMRoI;
+}
+
+namespace LVL1BS {
+
+/** Sub-Block class for CPM RoI data (neutral format).
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version 1.09h
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpmRoiSubBlockV1 : public L1CaloSubBlock {
+
+ public:
+   CpmRoiSubBlockV1();
+   ~CpmRoiSubBlockV1();
+
+   /// Clear all data
+   void clear();
+
+   /// Store header
+   void setRoiHeader(int version, int crate, int module);
+   /// Store RoI
+   void fillRoi(LVL1::CPMRoI roi);
+
+   /// Return RoI for given chip and location (left/right)
+   LVL1::CPMRoI roi(int chip, int loc) const;
+
+   /// Pack data
+   bool pack();
+   /// Unpack data
+   bool unpack();
+
+ private:
+   /// Header word ID
+   static const int s_wordIdVal         = 0xc;
+   //  G-Link/Neutral format
+   static const int s_glinkPins         = 16;
+   static const int s_hitsLen           = 16;
+   static const int s_errorLen          = 2;
+   static const int s_locationLen       = 2;
+   static const int s_bunchCrossingBits = 12;
+
+   /// Pack neutral data
+   bool packNeutral();
+   /// Unpack neutral data
+   bool unpackNeutral();
+
+   /// RoI words
+   std::vector<LVL1::CPMRoI> m_roiData;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiSubBlockV2.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiSubBlockV2.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..6cd53dfacc0809d60c8269f502a77573229e5ed2
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiSubBlockV2.cxx
@@ -0,0 +1,177 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "TrigT1CaloEvent/CPMTobRoI.h"
+
+#include "CpmRoiSubBlockV2.h"
+
+namespace LVL1BS {
+
+// Constant definitions
+
+const int CpmRoiSubBlockV2::s_wordIdVal;
+
+const int CpmRoiSubBlockV2::s_glinkPins;
+const int CpmRoiSubBlockV2::s_energyLen;
+const int CpmRoiSubBlockV2::s_isolLen;
+const int CpmRoiSubBlockV2::s_locationLen;
+const int CpmRoiSubBlockV2::s_bunchCrossingBits;
+
+
+CpmRoiSubBlockV2::CpmRoiSubBlockV2()
+{
+}
+
+CpmRoiSubBlockV2::~CpmRoiSubBlockV2()
+{
+}
+
+// Clear all data
+
+void CpmRoiSubBlockV2::clear()
+{
+  L1CaloSubBlock::clear();
+  m_roiData.clear();
+}
+
+// Store header
+
+void CpmRoiSubBlockV2::setRoiHeader(const int version, const int crate,
+                                                       const int module)
+{
+  setHeader(s_wordIdVal, version, NEUTRAL, 0, crate, module, 0, 1);
+}
+
+// Store RoI
+
+void CpmRoiSubBlockV2::fillRoi(const LVL1::CPMTobRoI roi)
+{
+  const LVL1::CPMTobRoI roiTemp(crate(), module(), 0, 0, 0, 0, 0);
+  if (roi.crate() == roiTemp.crate() && roi.cpm() == roiTemp.cpm()) {
+    m_roiData.resize(2*s_glinkPins);
+    const int pin = (roi.chip() << 1) |
+                    ((roi.location() >> s_locationLen) & 0x1);
+    const int type = roi.type(); // em or tau (0/1)
+    m_roiData[2*pin+type] = roi;
+  }
+}
+
+// Return RoI for given chip and location (left/right) and type (em/tau)
+
+LVL1::CPMTobRoI CpmRoiSubBlockV2::roi(const int chip, const int loc,
+                                                      const int type) const
+{
+  const int pin = (chip << 1) | (loc & 0x1);
+  if (pin < s_glinkPins && !m_roiData.empty()) return m_roiData[2*pin+type];
+  else return LVL1::CPMTobRoI(0);
+}
+
+// Packing/Unpacking routines
+
+bool CpmRoiSubBlockV2::pack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 2:                                     // <<== CHECK
+      switch (format()) {
+        case NEUTRAL:
+	  rc = packNeutral();
+	  break;
+        default:
+	  break;
+      }
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
+
+bool CpmRoiSubBlockV2::unpack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 2:                                     // <<== CHECK
+      switch (format()) {
+        case NEUTRAL:
+	  rc = unpackNeutral();
+	  break;
+        default:
+	  setUnpackErrorCode(UNPACK_FORMAT);
+	  break;
+      }
+      break;
+    default:
+      setUnpackErrorCode(UNPACK_VERSION);
+      break;
+  }
+  return rc;
+}
+
+// Pack neutral data
+
+bool CpmRoiSubBlockV2::packNeutral()
+{
+  m_roiData.resize(2*s_glinkPins);
+  for (int pin = 0; pin < s_glinkPins; ++pin) {
+    // RoI data
+    const int idx = 2*pin;
+    const LVL1::CPMTobRoI& roiEm(m_roiData[idx]);
+    const LVL1::CPMTobRoI& roiTau(m_roiData[idx+1]);
+    packerNeutral(pin, roiEm.energy(), s_energyLen);
+    packerNeutral(pin, roiEm.isolation(), s_isolLen);
+    packerNeutral(pin, 0, 1); //parity
+    packerNeutral(pin, roiTau.energy(), s_energyLen);
+    packerNeutral(pin, roiTau.isolation(), s_isolLen);
+    packerNeutral(pin, 0, 1); //parity
+    packerNeutral(pin, 0, 1); //error
+    packerNeutral(pin, (roiEm.location()|roiTau.location()), s_locationLen);
+    // Bunch Crossing number
+    if (pin < s_bunchCrossingBits) {
+      packerNeutral(pin, bunchCrossing() >> pin, 1);
+    } else packerNeutral(pin, 0, 1);
+    // G-Link parity
+    packerNeutralParity(pin);
+  }
+  return true;
+}
+
+// Unpack neutral data
+
+bool CpmRoiSubBlockV2::unpackNeutral()
+{
+  m_roiData.resize(2*s_glinkPins);
+  int bunchCrossing = 0;
+  for (int pin = 0; pin < s_glinkPins; ++pin) {
+    // RoI data
+    const int energyEm = unpackerNeutral(pin, s_energyLen);
+    const int isolEm   = unpackerNeutral(pin, s_isolLen);
+    unpackerNeutral(pin, 1); //parity
+    const int energyTau = unpackerNeutral(pin, s_energyLen);
+    const int isolTau   = unpackerNeutral(pin, s_isolLen);
+    unpackerNeutral(pin, 1); //parity
+    unpackerNeutral(pin, 1); //error
+    const int loc = unpackerNeutral(pin, s_locationLen) |
+                                           ((pin & 0x1) << s_locationLen);
+    const int chip = pin >> 1;
+    const int idx = 2*pin;
+    m_roiData[idx] = LVL1::CPMTobRoI(crate(), module(), chip, loc, 0,
+                                                        energyEm, isolEm);
+    m_roiData[idx+1] = LVL1::CPMTobRoI(crate(), module(), chip, loc, 1,
+                                                      energyTau, isolTau);
+    // Bunch Crossing number
+    if (pin < s_bunchCrossingBits) {
+      bunchCrossing |= unpackerNeutral(pin, 1) << pin;
+    } else unpackerNeutral(pin, 1);
+    // G-Link parity error
+    unpackerNeutralParityError(pin);
+  }
+  setBunchCrossing(bunchCrossing);
+  const bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  return rc;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiSubBlockV2.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiSubBlockV2.h
new file mode 100755
index 0000000000000000000000000000000000000000..c058e8645d718723e6a8f8f1e40b78bfa3631346
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmRoiSubBlockV2.h
@@ -0,0 +1,70 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPMROISUBBLOCKV2_H
+#define TRIGT1CALOBYTESTREAM_CPMROISUBBLOCKV2_H
+
+#include <vector>
+
+#include "L1CaloSubBlock.h"
+
+namespace LVL1 {
+  class CPMTobRoI;
+}
+
+namespace LVL1BS {
+
+/** Sub-Block class for CPM RoI data (neutral format) post LS1.
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version X.xxx                                         <<== CHECK
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpmRoiSubBlockV2 : public L1CaloSubBlock {
+
+ public:
+   CpmRoiSubBlockV2();
+   ~CpmRoiSubBlockV2();
+
+   /// Clear all data
+   void clear();
+
+   /// Store header
+   void setRoiHeader(int version, int crate, int module);
+   /// Store RoI
+   void fillRoi(LVL1::CPMTobRoI roi);
+
+   /// Return RoI for given chip and location (left/right) and type (em/tau)
+   LVL1::CPMTobRoI roi(int chip, int loc, int type) const;
+
+   /// Pack data
+   bool pack();
+   /// Unpack data
+   bool unpack();
+
+ private:
+   /// Header word ID
+   static const int s_wordIdVal         = 0xc;
+   //  G-Link/Neutral format
+   static const int s_glinkPins         = 16;
+   static const int s_energyLen         = 8;
+   static const int s_isolLen           = 5;
+   static const int s_locationLen       = 2;
+   static const int s_bunchCrossingBits = 12;
+
+   /// Pack neutral data
+   bool packNeutral();
+   /// Unpack neutral data
+   bool unpackNeutral();
+
+   /// RoI words
+   std::vector<LVL1::CPMTobRoI> m_roiData;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmSubBlock.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmSubBlock.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..7fb764a2c89ae68661aec4a245c0be8ec7ffcb3f
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmSubBlock.cxx
@@ -0,0 +1,455 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "CpmSubBlock.h"
+
+namespace LVL1BS {
+
+// Constant definitions
+
+const int      CpmSubBlock::s_wordIdVal;
+
+const int      CpmSubBlock::s_wordLength;
+
+const int      CpmSubBlock::s_ttDataABit;
+const int      CpmSubBlock::s_ttDataBBit;
+const int      CpmSubBlock::s_parityABit;
+const int      CpmSubBlock::s_parityBBit;
+const int      CpmSubBlock::s_linkDownABit;
+const int      CpmSubBlock::s_linkDownBBit;
+const int      CpmSubBlock::s_pairBit;
+const int      CpmSubBlock::s_fpgaBit;
+const int      CpmSubBlock::s_dataIdBit;
+const int      CpmSubBlock::s_ttWordId;
+const uint32_t CpmSubBlock::s_ttDataMask;
+const uint32_t CpmSubBlock::s_pairPinMask;
+const uint32_t CpmSubBlock::s_dataIdMask;
+
+const int      CpmSubBlock::s_indicatorBit;
+const int      CpmSubBlock::s_threshWordId;
+const uint32_t CpmSubBlock::s_threshMask;
+
+const int      CpmSubBlock::s_pairsPerPin;
+const int      CpmSubBlock::s_wordsPerPin;
+const int      CpmSubBlock::s_ttBits;
+const int      CpmSubBlock::s_errBits;
+const int      CpmSubBlock::s_hitBits;
+const int      CpmSubBlock::s_hitWords;
+const int      CpmSubBlock::s_glinkPins;
+const int      CpmSubBlock::s_glinkBitsPerSlice;
+
+
+CpmSubBlock::CpmSubBlock() : m_channels(80)
+{
+  m_chanPresent.assign(m_channels, 0);
+}
+
+CpmSubBlock::~CpmSubBlock()
+{
+}
+
+// Clear all data
+
+void CpmSubBlock::clear()
+{
+  L1CaloSubBlock::clear();
+  m_ttData.clear();
+  m_hitData.clear();
+  m_chanPresent.assign(m_channels, 0);
+}
+
+// Store CPM header
+
+void CpmSubBlock::setCpmHeader(const int version, const int format,
+                               const int slice, const int crate,
+                               const int module, const int timeslices)
+{
+  setHeader(s_wordIdVal, version, format, slice, crate, module, 0, timeslices);
+}
+
+// Store trigger tower data
+
+void CpmSubBlock::fillTowerData(const int slice, const int channel,
+                                const int em, const int had,
+                                const int emErr, const int hadErr)
+{
+  if (channel < m_channels && (em || emErr || had || hadErr)) {
+    resize(m_ttData, m_channels);
+    int dat = em;
+    int err = emErr;
+    for (int pinOffset = 0; pinOffset < 2; ++pinOffset) {
+      if (dat || err) {
+        const int pin  = 2 * (channel/s_wordsPerPin) + pinOffset;
+        const int pair = (channel % s_wordsPerPin) / 2;
+        const int pos  = pin * s_pairsPerPin + pair;
+        const int ix   = index(slice) * m_channels + pos;
+        uint32_t word = m_ttData[ix];
+        if (channel % 2 == 0) {
+          word |= (dat & s_ttDataMask) << s_ttDataABit;
+	  word |= (err & 0x1)          << s_parityABit;
+	  word |= ((err >> 1) & 0x1)   << s_linkDownABit;
+        } else {
+          word |= (dat & s_ttDataMask) << s_ttDataBBit;
+	  word |= (err & 0x1)          << s_parityBBit;
+	  word |= ((err >> 1) & 0x1)   << s_linkDownBBit;
+        }
+        word |= pair       << s_pairBit;
+        word |= pin        << s_fpgaBit;
+        word |= s_ttWordId << s_dataIdBit;
+        m_ttData[ix] = word;
+      }
+      dat = had;
+      err = hadErr;
+    }
+    m_chanPresent[channel] = 1;
+  }
+}
+
+// Store hit counts
+
+void CpmSubBlock::setHits(const int slice, const unsigned int hit0,
+                                           const unsigned int hit1)
+{
+  if (hit0 || hit1) {
+    resize(m_hitData, 2);
+    const int ix = index(slice)*2;
+    unsigned int hits = hit0;
+    for (int indicator = 0; indicator < 2; ++indicator) {
+      if (hits) {
+        uint32_t word = (hits & s_threshMask);
+        word |= indicator      << s_indicatorBit;
+        word |= s_threshWordId << s_dataIdBit;
+        m_hitData[ix + indicator] = word;
+      }
+      hits = hit1;
+    }
+  }
+}
+
+// Return Em data for given channel
+
+int CpmSubBlock::emData(const int slice, const int channel) const
+{
+  return data(slice, channel, 0);
+}
+
+// Return Had data for given channel
+
+int CpmSubBlock::hadData(const int slice, const int channel) const
+{
+  return data(slice, channel, 1);
+}
+
+// Return Em error for given channel
+
+int CpmSubBlock::emError(const int slice, const int channel) const
+{
+  return error(slice, channel, 0);
+}
+
+// Return Had error for given channel
+
+int CpmSubBlock::hadError(const int slice, const int channel) const
+{
+  return error(slice, channel, 1);
+}
+
+// Return first hit counts word
+
+unsigned int CpmSubBlock::hits0(const int slice) const
+{
+  return hits(slice, 0);
+}
+
+// Return second hit counts word
+
+unsigned int CpmSubBlock::hits1(const int slice) const
+{
+  return hits(slice, 1);
+}
+
+// Return number of timeslices
+
+int CpmSubBlock::timeslices() const
+{
+  int slices = slices1();
+  if (slices == 0 && format() == NEUTRAL) {
+    slices = dataWords() / s_glinkBitsPerSlice;
+  }
+  if (slices == 0) slices = 1;
+  return slices;
+}
+
+// Packing/Unpacking routines
+
+bool CpmSubBlock::pack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = packNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = packUncompressed();
+	  break;
+        default:
+	  break;
+      }
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
+
+bool CpmSubBlock::unpack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = unpackNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = unpackUncompressed();
+	  break;
+        default:
+	  setUnpackErrorCode(UNPACK_FORMAT);
+	  break;
+      }
+      break;
+    default:
+      setUnpackErrorCode(UNPACK_VERSION);
+      break;
+  }
+  return rc;
+}
+
+// Return data for given channel and pin offset
+
+int CpmSubBlock::data(const int slice, const int channel,
+                                       const int offset) const
+{
+  int dat = 0;
+  if (slice >= 0 && slice < timeslices() &&
+      channel >= 0 && channel < m_channels && !m_ttData.empty()) {
+    const int pin  = 2 * (channel/s_wordsPerPin) + offset;
+    const int pair = (channel % s_wordsPerPin) / 2;
+    const int pos  = pin * s_pairsPerPin + pair;
+    const int ix   = index(slice) * m_channels + pos;
+    const uint32_t word = m_ttData[ix];
+    if (channel % 2 == 0) {
+           dat = (word >> s_ttDataABit) & s_ttDataMask;
+    } else dat = (word >> s_ttDataBBit) & s_ttDataMask;
+  }
+  return dat;
+}
+
+// Return error for given channel and pin offset
+
+int CpmSubBlock::error(const int slice, const int channel,
+                                        const int offset) const
+{
+  int err = 0;
+  if (slice >= 0 && slice < timeslices() &&
+      channel >= 0 && channel < m_channels && !m_ttData.empty()) {
+    const int pin  = 2 * (channel/s_wordsPerPin) + offset;
+    const int pair = (channel % s_wordsPerPin) / 2;
+    const int pos  = pin * s_pairsPerPin + pair;
+    const int ix   = index(slice) * m_channels + pos;
+    const uint32_t word = m_ttData[ix];
+    if (channel % 2 == 0) {
+      err  =  (word >> s_parityABit)   & 0x1;
+      err |= ((word >> s_linkDownABit) & 0x1) << 1;
+    } else {
+      err  =  (word >> s_parityBBit)   & 0x1;
+      err |= ((word >> s_linkDownBBit) & 0x1) << 1;
+    }
+  }
+  return err;
+}
+
+// Return hit counts with given offset
+
+unsigned int CpmSubBlock::hits(const int slice, const int offset) const
+{
+  unsigned int hit = 0;
+  if (slice >= 0 && slice < timeslices() && !m_hitData.empty()) {
+    hit = m_hitData[index(slice)*2 + offset] & s_threshMask;
+  }
+  return hit;
+}
+
+// Return data index appropriate to format
+
+int CpmSubBlock::index(const int slice) const
+{
+  return (format() == NEUTRAL) ? slice : 0;
+}
+
+// Resize a data vector according to format
+
+void CpmSubBlock::resize(std::vector<uint32_t>& vec, int channels)
+{
+  if (vec.empty()) {
+    int size = channels;
+    if (format() == NEUTRAL) size *= timeslices();
+    vec.resize(size);
+  }
+}
+
+// Pack neutral data
+
+bool CpmSubBlock::packNeutral()
+{
+  resize(m_ttData, m_channels);
+  resize(m_hitData, 2);
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    const unsigned int hit0 = hits0(slice);
+    const unsigned int hit1 = hits1(slice);
+    for (int pin = 0; pin < s_glinkPins; ++pin) {
+      // Trigger tower data
+      for (int pair = 0; pair < s_pairsPerPin; ++pair) {
+        for (int i = 0; i < 2; ++i) {
+          const int channel = s_wordsPerPin*(pin/2) + 2*pair + i;
+	  if ((pin & 0x1)) { // Odd pins Had, even Em
+	    packerNeutral(pin, hadData(slice,  channel), s_ttBits);
+	    packerNeutral(pin, hadError(slice, channel), s_errBits);
+          } else {
+	    packerNeutral(pin, emData(slice,  channel), s_ttBits);
+	    packerNeutral(pin, emError(slice, channel), s_errBits);
+	  }
+        }
+      }
+      // Hits and Bunch Crossing number
+      if (pin < s_hitWords) {
+        packerNeutral(pin, hit0 >> pin*s_hitBits, s_hitBits);
+      } else if (pin < 2*s_hitWords) {
+        packerNeutral(pin, hit1 >> (pin - s_hitWords)*s_hitBits, s_hitBits);
+      } else {
+        packerNeutral(pin, bunchCrossing() >> (pin - 2*s_hitWords)*s_hitBits,
+	                                                         s_hitBits);
+      }
+      // G-Link parity
+      packerNeutralParity(pin);
+    }
+  }
+  return true;
+}
+
+// Pack uncompressed data
+
+bool CpmSubBlock::packUncompressed()
+{
+  // Trigger tower data
+  std::vector<uint32_t>::const_iterator pos;
+  for (pos = m_ttData.begin(); pos != m_ttData.end(); ++pos) {
+    if (*pos) packer(*pos, s_wordLength);
+  }
+        
+  // Hits data
+  for (pos = m_hitData.begin(); pos != m_hitData.end(); ++pos) {
+    if (*pos) packer(*pos, s_wordLength);
+  }
+  packerFlush();
+  return true;
+}
+
+// Unpack neutral data
+
+bool CpmSubBlock::unpackNeutral()
+{
+  resize(m_ttData, m_channels);
+  resize(m_hitData, 2);
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    unsigned int hit0 = 0;
+    unsigned int hit1 = 0;
+    int bunchCrossing = 0;
+    for (int pin = 0; pin < s_glinkPins; ++pin) {
+      // Trigger tower data
+      for (int pair = 0; pair < s_pairsPerPin; ++pair) {
+	for (int i = 0; i < 2; ++i) {
+          const int channel = s_wordsPerPin*(pin/2) + 2*pair + i;
+	  int em     = 0;
+	  int had    = 0;
+	  int emErr  = 0;
+	  int hadErr = 0;
+	  if ((pin & 0x1)) { // Odd pins Had, even Em
+	    em     = emData(slice, channel);
+	    had    = unpackerNeutral(pin, s_ttBits);
+	    emErr  = emError(slice, channel);
+	    hadErr = unpackerNeutral(pin, s_errBits);
+	  } else {
+	    em     = unpackerNeutral(pin, s_ttBits);
+	    had    = hadData(slice, channel);
+	    emErr  = unpackerNeutral(pin, s_errBits);
+	    hadErr = hadError(slice, channel);
+          }
+	  fillTowerData(slice, channel, em, had, emErr, hadErr);
+        }
+      }
+      // Hits and Bunch Crossing number
+      if (pin < s_hitWords) {
+        hit0 |= unpackerNeutral(pin, s_hitBits) << pin*s_hitBits;
+      } else if (pin < 2*s_hitWords) {
+        hit1 |= unpackerNeutral(pin, s_hitBits) << (pin - s_hitWords)*s_hitBits;
+      } else {
+	bunchCrossing |= unpackerNeutral(pin, s_hitBits)
+	                                << (pin - 2*s_hitWords)*s_hitBits;
+      }
+      // G-Link parity error
+      unpackerNeutralParityError(pin);
+    }
+    setHits(slice, hit0, hit1);
+    setBunchCrossing(bunchCrossing);
+  }
+  const bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  return rc;
+}
+
+// Unpack uncompressed data
+
+bool CpmSubBlock::unpackUncompressed()
+{
+  resize(m_ttData, m_channels);
+  resize(m_hitData, 2);
+  unpackerInit();
+  uint32_t word = unpacker(s_wordLength);
+  while (unpackerSuccess()) {
+    const int id = dataId(word);
+    bool err = false;
+    // Trigger tower data
+    if (id == s_ttWordId) {
+      const int ix = (word >> s_pairBit) & s_pairPinMask;
+      if (ix < m_channels && m_ttData[ix] == 0) {
+        m_ttData[ix] = word;
+	const int pin  = ix/4;
+	const int pair = ix%4;
+	const int channel = s_wordsPerPin*(pin/2) + 2*pair;
+	m_chanPresent[channel]   = 1;
+	m_chanPresent[channel+1] = 1;
+      } else err = true;
+    // Hits
+    } else {
+      const int indicator = (word >> s_indicatorBit) & 0x1;
+      if (m_hitData[indicator] == 0) m_hitData[indicator] = word;
+      else err = true;
+    }
+    if (err) {
+      setUnpackErrorCode(UNPACK_SOURCE_ID);
+      return false;
+    }
+    word = unpacker(s_wordLength);
+  }
+  return true;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmSubBlock.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmSubBlock.h
new file mode 100755
index 0000000000000000000000000000000000000000..e8b3778e943df781a84c08f0f846e4e7b8150fef
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmSubBlock.h
@@ -0,0 +1,141 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPMSUBBLOCK_H
+#define TRIGT1CALOBYTESTREAM_CPMSUBBLOCK_H
+
+#include <stdint.h>
+#include <vector>
+
+#include "L1CaloSubBlock.h"
+
+namespace LVL1BS {
+
+/** Sub-Block class for CPM data.
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version 1.06d
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpmSubBlock : public L1CaloSubBlock {
+
+ public:
+   CpmSubBlock();
+   ~CpmSubBlock();
+
+   /// Clear all data
+   void clear();
+
+   /// Store CPM header
+   void setCpmHeader(int version, int format, int slice, int crate,
+                     int module, int timeslices);
+   /// Store trigger tower data
+   void fillTowerData(int slice, int channel, int em, int had,
+	                                      int emErr, int hadErr);
+   /// Store hit counts
+   void setHits(int slice, unsigned int hit0, unsigned int hit1);
+
+   /// Return Em data for given channel
+   int  emData(int slice, int channel)   const;
+   /// Return Had data for given channel
+   int  hadData(int slice, int channel)  const;
+   /// Return Em error for given channel
+   int  emError(int slice, int channel)  const;
+   /// Return Had error for given channel
+   int  hadError(int slice, int channel) const;
+   /// Return e/gamma hit counts
+   unsigned int hits0(int slice)         const;
+   /// Return tau hit counts
+   unsigned int hits1(int slice)         const;
+   /// Return number of timeslices
+   int  timeslices()                     const;
+   /// Return true if there is tower data for given channel
+   bool anyTowerData(int channel)        const;
+
+   /// Pack data
+   bool pack();
+   /// Unpack data
+   bool unpack();
+
+ private:
+   /// CPM header word ID
+   static const int      s_wordIdVal       = 0xc;
+   /// Data word length
+   static const int      s_wordLength      = 32;
+   //  Trigger tower data word bit positions and masks
+   static const int      s_ttDataABit      = 0;
+   static const int      s_ttDataBBit      = 9;
+   static const int      s_parityABit      = 8;
+   static const int      s_parityBBit      = 17;
+   static const int      s_linkDownABit    = 19;
+   static const int      s_linkDownBBit    = 20;
+   static const int      s_pairBit         = 21;
+   static const int      s_fpgaBit         = 23;
+   static const int      s_dataIdBit       = 30;
+   static const int      s_ttWordId        = 0x1;
+   static const uint32_t s_ttDataMask      = 0xff;
+   static const uint32_t s_pairPinMask     = 0x7f;
+   static const uint32_t s_dataIdMask      = 0x3;
+   //  Hit counts bit positions and masks
+   static const int      s_indicatorBit    = 24;
+   static const int      s_threshWordId    = 0x2;
+   static const uint32_t s_threshMask      = 0xffffff;
+   //  G-Link/Neutral format
+   static const int      s_pairsPerPin       = 4;
+   static const int      s_wordsPerPin       = 8;
+   static const int      s_ttBits            = 8;
+   static const int      s_errBits           = 2;
+   static const int      s_hitBits           = 3;
+   static const int      s_hitWords          = 8;
+   static const int      s_glinkPins         = 20;
+   static const int      s_glinkBitsPerSlice = 84;
+
+   /// Return data WordID
+   int  dataId(uint32_t word) const;
+   /// Return data for given channel and pin offset
+   int  data(int slice, int channel, int offset)  const;
+   /// Return error for given channel and pin offset
+   int  error(int slice, int channel, int offset) const;
+   /// Return hit counts with given offset
+   unsigned int hits(int slice, int offset)       const;
+   /// Return data index appropriate to format
+   int  index(int slice)      const;
+   /// Resize a data vector according to format
+   void resize(std::vector<uint32_t>& vec, int channels);
+
+   /// Pack neutral data
+   bool packNeutral();
+   /// Pack uncompressed data
+   bool packUncompressed();
+   /// Unpack neutral data
+   bool unpackNeutral();
+   /// Unpack uncompressed data
+   bool unpackUncompressed();
+
+   /// Trigger tower data
+   std::vector<uint32_t> m_ttData;
+   /// Hit counts
+   std::vector<uint32_t> m_hitData;
+   /// Channel present flags vector
+   std::vector<int> m_chanPresent;
+   /// Number of Trigger tower channels per module
+   int m_channels;
+
+};
+
+inline int CpmSubBlock::dataId(const uint32_t word) const
+{
+  return (word >> s_dataIdBit) & s_dataIdMask;
+}
+
+inline bool CpmSubBlock::anyTowerData(const int channel) const
+{
+  return m_chanPresent[channel];
+}
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmSubBlockV1.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmSubBlockV1.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..797c7f08407c56329d83eea080bc9befa019e0b2
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmSubBlockV1.cxx
@@ -0,0 +1,455 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "CpmSubBlockV1.h"
+
+namespace LVL1BS {
+
+// Constant definitions
+
+const int      CpmSubBlockV1::s_wordIdVal;
+
+const int      CpmSubBlockV1::s_wordLength;
+
+const int      CpmSubBlockV1::s_ttDataABit;
+const int      CpmSubBlockV1::s_ttDataBBit;
+const int      CpmSubBlockV1::s_parityABit;
+const int      CpmSubBlockV1::s_parityBBit;
+const int      CpmSubBlockV1::s_linkDownABit;
+const int      CpmSubBlockV1::s_linkDownBBit;
+const int      CpmSubBlockV1::s_pairBit;
+const int      CpmSubBlockV1::s_fpgaBit;
+const int      CpmSubBlockV1::s_dataIdBit;
+const int      CpmSubBlockV1::s_ttWordId;
+const uint32_t CpmSubBlockV1::s_ttDataMask;
+const uint32_t CpmSubBlockV1::s_pairPinMask;
+const uint32_t CpmSubBlockV1::s_dataIdMask;
+
+const int      CpmSubBlockV1::s_indicatorBit;
+const int      CpmSubBlockV1::s_threshWordId;
+const uint32_t CpmSubBlockV1::s_threshMask;
+
+const int      CpmSubBlockV1::s_pairsPerPin;
+const int      CpmSubBlockV1::s_wordsPerPin;
+const int      CpmSubBlockV1::s_ttBits;
+const int      CpmSubBlockV1::s_errBits;
+const int      CpmSubBlockV1::s_hitBits;
+const int      CpmSubBlockV1::s_hitWords;
+const int      CpmSubBlockV1::s_glinkPins;
+const int      CpmSubBlockV1::s_glinkBitsPerSlice;
+
+
+CpmSubBlockV1::CpmSubBlockV1() : m_channels(80)
+{
+  m_chanPresent.assign(m_channels, 0);
+}
+
+CpmSubBlockV1::~CpmSubBlockV1()
+{
+}
+
+// Clear all data
+
+void CpmSubBlockV1::clear()
+{
+  L1CaloSubBlock::clear();
+  m_ttData.clear();
+  m_hitData.clear();
+  m_chanPresent.assign(m_channels, 0);
+}
+
+// Store CPM header
+
+void CpmSubBlockV1::setCpmHeader(const int version, const int format,
+                                 const int slice, const int crate,
+                                 const int module, const int timeslices)
+{
+  setHeader(s_wordIdVal, version, format, slice, crate, module, 0, timeslices);
+}
+
+// Store trigger tower data
+
+void CpmSubBlockV1::fillTowerData(const int slice, const int channel,
+                                  const int em, const int had,
+                                  const int emErr, const int hadErr)
+{
+  if (channel < m_channels && (em || emErr || had || hadErr)) {
+    resize(m_ttData, m_channels);
+    int dat = em;
+    int err = emErr;
+    for (int pinOffset = 0; pinOffset < 2; ++pinOffset) {
+      if (dat || err) {
+        const int pin  = 2 * (channel/s_wordsPerPin) + pinOffset;
+        const int pair = (channel % s_wordsPerPin) / 2;
+        const int pos  = pin * s_pairsPerPin + pair;
+        const int ix   = index(slice) * m_channels + pos;
+        uint32_t word = m_ttData[ix];
+        if (channel % 2 == 0) {
+          word |= (dat & s_ttDataMask) << s_ttDataABit;
+	  word |= (err & 0x1)          << s_parityABit;
+	  word |= ((err >> 1) & 0x1)   << s_linkDownABit;
+        } else {
+          word |= (dat & s_ttDataMask) << s_ttDataBBit;
+	  word |= (err & 0x1)          << s_parityBBit;
+	  word |= ((err >> 1) & 0x1)   << s_linkDownBBit;
+        }
+        word |= pair       << s_pairBit;
+        word |= pin        << s_fpgaBit;
+        word |= s_ttWordId << s_dataIdBit;
+        m_ttData[ix] = word;
+      }
+      dat = had;
+      err = hadErr;
+    }
+    m_chanPresent[channel] = 1;
+  }
+}
+
+// Store hit counts
+
+void CpmSubBlockV1::setHits(const int slice, const unsigned int hit0,
+                                             const unsigned int hit1)
+{
+  if (hit0 || hit1) {
+    resize(m_hitData, 2);
+    const int ix = index(slice)*2;
+    unsigned int hits = hit0;
+    for (int indicator = 0; indicator < 2; ++indicator) {
+      if (hits) {
+        uint32_t word = (hits & s_threshMask);
+        word |= indicator      << s_indicatorBit;
+        word |= s_threshWordId << s_dataIdBit;
+        m_hitData[ix + indicator] = word;
+      }
+      hits = hit1;
+    }
+  }
+}
+
+// Return Em data for given channel
+
+int CpmSubBlockV1::emData(const int slice, const int channel) const
+{
+  return data(slice, channel, 0);
+}
+
+// Return Had data for given channel
+
+int CpmSubBlockV1::hadData(const int slice, const int channel) const
+{
+  return data(slice, channel, 1);
+}
+
+// Return Em error for given channel
+
+int CpmSubBlockV1::emError(const int slice, const int channel) const
+{
+  return error(slice, channel, 0);
+}
+
+// Return Had error for given channel
+
+int CpmSubBlockV1::hadError(const int slice, const int channel) const
+{
+  return error(slice, channel, 1);
+}
+
+// Return first hit counts word
+
+unsigned int CpmSubBlockV1::hits0(const int slice) const
+{
+  return hits(slice, 0);
+}
+
+// Return second hit counts word
+
+unsigned int CpmSubBlockV1::hits1(const int slice) const
+{
+  return hits(slice, 1);
+}
+
+// Return number of timeslices
+
+int CpmSubBlockV1::timeslices() const
+{
+  int slices = slices1();
+  if (slices == 0 && format() == NEUTRAL) {
+    slices = dataWords() / s_glinkBitsPerSlice;
+  }
+  if (slices == 0) slices = 1;
+  return slices;
+}
+
+// Packing/Unpacking routines
+
+bool CpmSubBlockV1::pack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = packNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = packUncompressed();
+	  break;
+        default:
+	  break;
+      }
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
+
+bool CpmSubBlockV1::unpack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = unpackNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = unpackUncompressed();
+	  break;
+        default:
+	  setUnpackErrorCode(UNPACK_FORMAT);
+	  break;
+      }
+      break;
+    default:
+      setUnpackErrorCode(UNPACK_VERSION);
+      break;
+  }
+  return rc;
+}
+
+// Return data for given channel and pin offset
+
+int CpmSubBlockV1::data(const int slice, const int channel,
+                                         const int offset) const
+{
+  int dat = 0;
+  if (slice >= 0 && slice < timeslices() &&
+      channel >= 0 && channel < m_channels && !m_ttData.empty()) {
+    const int pin  = 2 * (channel/s_wordsPerPin) + offset;
+    const int pair = (channel % s_wordsPerPin) / 2;
+    const int pos  = pin * s_pairsPerPin + pair;
+    const int ix   = index(slice) * m_channels + pos;
+    const uint32_t word = m_ttData[ix];
+    if (channel % 2 == 0) {
+           dat = (word >> s_ttDataABit) & s_ttDataMask;
+    } else dat = (word >> s_ttDataBBit) & s_ttDataMask;
+  }
+  return dat;
+}
+
+// Return error for given channel and pin offset
+
+int CpmSubBlockV1::error(const int slice, const int channel,
+                                          const int offset) const
+{
+  int err = 0;
+  if (slice >= 0 && slice < timeslices() &&
+      channel >= 0 && channel < m_channels && !m_ttData.empty()) {
+    const int pin  = 2 * (channel/s_wordsPerPin) + offset;
+    const int pair = (channel % s_wordsPerPin) / 2;
+    const int pos  = pin * s_pairsPerPin + pair;
+    const int ix   = index(slice) * m_channels + pos;
+    const uint32_t word = m_ttData[ix];
+    if (channel % 2 == 0) {
+      err  =  (word >> s_parityABit)   & 0x1;
+      err |= ((word >> s_linkDownABit) & 0x1) << 1;
+    } else {
+      err  =  (word >> s_parityBBit)   & 0x1;
+      err |= ((word >> s_linkDownBBit) & 0x1) << 1;
+    }
+  }
+  return err;
+}
+
+// Return hit counts with given offset
+
+unsigned int CpmSubBlockV1::hits(const int slice, const int offset) const
+{
+  unsigned int hit = 0;
+  if (slice >= 0 && slice < timeslices() && !m_hitData.empty()) {
+    hit = m_hitData[index(slice)*2 + offset] & s_threshMask;
+  }
+  return hit;
+}
+
+// Return data index appropriate to format
+
+int CpmSubBlockV1::index(const int slice) const
+{
+  return (format() == NEUTRAL) ? slice : 0;
+}
+
+// Resize a data vector according to format
+
+void CpmSubBlockV1::resize(std::vector<uint32_t>& vec, int channels)
+{
+  if (vec.empty()) {
+    int size = channels;
+    if (format() == NEUTRAL) size *= timeslices();
+    vec.resize(size);
+  }
+}
+
+// Pack neutral data
+
+bool CpmSubBlockV1::packNeutral()
+{
+  resize(m_ttData, m_channels);
+  resize(m_hitData, 2);
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    const unsigned int hit0 = hits0(slice);
+    const unsigned int hit1 = hits1(slice);
+    for (int pin = 0; pin < s_glinkPins; ++pin) {
+      // Trigger tower data
+      for (int pair = 0; pair < s_pairsPerPin; ++pair) {
+        for (int i = 0; i < 2; ++i) {
+          const int channel = s_wordsPerPin*(pin/2) + 2*pair + i;
+	  if ((pin & 0x1)) { // Odd pins Had, even Em
+	    packerNeutral(pin, hadData(slice,  channel), s_ttBits);
+	    packerNeutral(pin, hadError(slice, channel), s_errBits);
+          } else {
+	    packerNeutral(pin, emData(slice,  channel), s_ttBits);
+	    packerNeutral(pin, emError(slice, channel), s_errBits);
+	  }
+        }
+      }
+      // Hits and Bunch Crossing number
+      if (pin < s_hitWords) {
+        packerNeutral(pin, hit0 >> pin*s_hitBits, s_hitBits);
+      } else if (pin < 2*s_hitWords) {
+        packerNeutral(pin, hit1 >> (pin - s_hitWords)*s_hitBits, s_hitBits);
+      } else {
+        packerNeutral(pin, bunchCrossing() >> (pin - 2*s_hitWords)*s_hitBits,
+	                                                         s_hitBits);
+      }
+      // G-Link parity
+      packerNeutralParity(pin);
+    }
+  }
+  return true;
+}
+
+// Pack uncompressed data
+
+bool CpmSubBlockV1::packUncompressed()
+{
+  // Trigger tower data
+  std::vector<uint32_t>::const_iterator pos;
+  for (pos = m_ttData.begin(); pos != m_ttData.end(); ++pos) {
+    if (*pos) packer(*pos, s_wordLength);
+  }
+        
+  // Hits data
+  for (pos = m_hitData.begin(); pos != m_hitData.end(); ++pos) {
+    if (*pos) packer(*pos, s_wordLength);
+  }
+  packerFlush();
+  return true;
+}
+
+// Unpack neutral data
+
+bool CpmSubBlockV1::unpackNeutral()
+{
+  resize(m_ttData, m_channels);
+  resize(m_hitData, 2);
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    unsigned int hit0 = 0;
+    unsigned int hit1 = 0;
+    int bunchCrossing = 0;
+    for (int pin = 0; pin < s_glinkPins; ++pin) {
+      // Trigger tower data
+      for (int pair = 0; pair < s_pairsPerPin; ++pair) {
+	for (int i = 0; i < 2; ++i) {
+          const int channel = s_wordsPerPin*(pin/2) + 2*pair + i;
+	  int em     = 0;
+	  int had    = 0;
+	  int emErr  = 0;
+	  int hadErr = 0;
+	  if ((pin & 0x1)) { // Odd pins Had, even Em
+	    em     = emData(slice, channel);
+	    had    = unpackerNeutral(pin, s_ttBits);
+	    emErr  = emError(slice, channel);
+	    hadErr = unpackerNeutral(pin, s_errBits);
+	  } else {
+	    em     = unpackerNeutral(pin, s_ttBits);
+	    had    = hadData(slice, channel);
+	    emErr  = unpackerNeutral(pin, s_errBits);
+	    hadErr = hadError(slice, channel);
+          }
+	  fillTowerData(slice, channel, em, had, emErr, hadErr);
+        }
+      }
+      // Hits and Bunch Crossing number
+      if (pin < s_hitWords) {
+        hit0 |= unpackerNeutral(pin, s_hitBits) << pin*s_hitBits;
+      } else if (pin < 2*s_hitWords) {
+        hit1 |= unpackerNeutral(pin, s_hitBits) << (pin - s_hitWords)*s_hitBits;
+      } else {
+	bunchCrossing |= unpackerNeutral(pin, s_hitBits)
+	                                << (pin - 2*s_hitWords)*s_hitBits;
+      }
+      // G-Link parity error
+      unpackerNeutralParityError(pin);
+    }
+    setHits(slice, hit0, hit1);
+    setBunchCrossing(bunchCrossing);
+  }
+  const bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  return rc;
+}
+
+// Unpack uncompressed data
+
+bool CpmSubBlockV1::unpackUncompressed()
+{
+  resize(m_ttData, m_channels);
+  resize(m_hitData, 2);
+  unpackerInit();
+  uint32_t word = unpacker(s_wordLength);
+  while (unpackerSuccess()) {
+    const int id = dataId(word);
+    bool err = false;
+    // Trigger tower data
+    if (id == s_ttWordId) {
+      const int ix = (word >> s_pairBit) & s_pairPinMask;
+      if (ix < m_channels && m_ttData[ix] == 0) {
+        m_ttData[ix] = word;
+	const int pin  = ix/4;
+	const int pair = ix%4;
+	const int channel = s_wordsPerPin*(pin/2) + 2*pair;
+	m_chanPresent[channel]   = 1;
+	m_chanPresent[channel+1] = 1;
+      } else err = true;
+    // Hits
+    } else {
+      const int indicator = (word >> s_indicatorBit) & 0x1;
+      if (m_hitData[indicator] == 0) m_hitData[indicator] = word;
+      else err = true;
+    }
+    if (err) {
+      setUnpackErrorCode(UNPACK_SOURCE_ID);
+      return false;
+    }
+    word = unpacker(s_wordLength);
+  }
+  return true;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmSubBlockV1.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmSubBlockV1.h
new file mode 100755
index 0000000000000000000000000000000000000000..d866d240abe80c951fb504d8cefee9a82e19967a
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmSubBlockV1.h
@@ -0,0 +1,141 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPMSUBBLOCKV1_H
+#define TRIGT1CALOBYTESTREAM_CPMSUBBLOCKV1_H
+
+#include <stdint.h>
+#include <vector>
+
+#include "L1CaloSubBlock.h"
+
+namespace LVL1BS {
+
+/** Sub-Block class for CPM data.
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version 1.06d
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpmSubBlockV1 : public L1CaloSubBlock {
+
+ public:
+   CpmSubBlockV1();
+   ~CpmSubBlockV1();
+
+   /// Clear all data
+   void clear();
+
+   /// Store CPM header
+   void setCpmHeader(int version, int format, int slice, int crate,
+                     int module, int timeslices);
+   /// Store trigger tower data
+   void fillTowerData(int slice, int channel, int em, int had,
+	                                      int emErr, int hadErr);
+   /// Store hit counts
+   void setHits(int slice, unsigned int hit0, unsigned int hit1);
+
+   /// Return Em data for given channel
+   int  emData(int slice, int channel)   const;
+   /// Return Had data for given channel
+   int  hadData(int slice, int channel)  const;
+   /// Return Em error for given channel
+   int  emError(int slice, int channel)  const;
+   /// Return Had error for given channel
+   int  hadError(int slice, int channel) const;
+   /// Return e/gamma hit counts
+   unsigned int hits0(int slice)         const;
+   /// Return tau hit counts
+   unsigned int hits1(int slice)         const;
+   /// Return number of timeslices
+   int  timeslices()                     const;
+   /// Return true if there is tower data for given channel
+   bool anyTowerData(int channel)        const;
+
+   /// Pack data
+   bool pack();
+   /// Unpack data
+   bool unpack();
+
+ private:
+   /// CPM header word ID
+   static const int      s_wordIdVal       = 0xc;
+   /// Data word length
+   static const int      s_wordLength      = 32;
+   //  Trigger tower data word bit positions and masks
+   static const int      s_ttDataABit      = 0;
+   static const int      s_ttDataBBit      = 9;
+   static const int      s_parityABit      = 8;
+   static const int      s_parityBBit      = 17;
+   static const int      s_linkDownABit    = 19;
+   static const int      s_linkDownBBit    = 20;
+   static const int      s_pairBit         = 21;
+   static const int      s_fpgaBit         = 23;
+   static const int      s_dataIdBit       = 30;
+   static const int      s_ttWordId        = 0x1;
+   static const uint32_t s_ttDataMask      = 0xff;
+   static const uint32_t s_pairPinMask     = 0x7f;
+   static const uint32_t s_dataIdMask      = 0x3;
+   //  Hit counts bit positions and masks
+   static const int      s_indicatorBit    = 24;
+   static const int      s_threshWordId    = 0x2;
+   static const uint32_t s_threshMask      = 0xffffff;
+   //  G-Link/Neutral format
+   static const int      s_pairsPerPin       = 4;
+   static const int      s_wordsPerPin       = 8;
+   static const int      s_ttBits            = 8;
+   static const int      s_errBits           = 2;
+   static const int      s_hitBits           = 3;
+   static const int      s_hitWords          = 8;
+   static const int      s_glinkPins         = 20;
+   static const int      s_glinkBitsPerSlice = 84;
+
+   /// Return data WordID
+   int  dataId(uint32_t word) const;
+   /// Return data for given channel and pin offset
+   int  data(int slice, int channel, int offset)  const;
+   /// Return error for given channel and pin offset
+   int  error(int slice, int channel, int offset) const;
+   /// Return hit counts with given offset
+   unsigned int hits(int slice, int offset)       const;
+   /// Return data index appropriate to format
+   int  index(int slice)      const;
+   /// Resize a data vector according to format
+   void resize(std::vector<uint32_t>& vec, int channels);
+
+   /// Pack neutral data
+   bool packNeutral();
+   /// Pack uncompressed data
+   bool packUncompressed();
+   /// Unpack neutral data
+   bool unpackNeutral();
+   /// Unpack uncompressed data
+   bool unpackUncompressed();
+
+   /// Trigger tower data
+   std::vector<uint32_t> m_ttData;
+   /// Hit counts
+   std::vector<uint32_t> m_hitData;
+   /// Channel present flags vector
+   std::vector<int> m_chanPresent;
+   /// Number of Trigger tower channels per module
+   int m_channels;
+
+};
+
+inline int CpmSubBlockV1::dataId(const uint32_t word) const
+{
+  return (word >> s_dataIdBit) & s_dataIdMask;
+}
+
+inline bool CpmSubBlockV1::anyTowerData(const int channel) const
+{
+  return m_chanPresent[channel];
+}
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmSubBlockV2.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmSubBlockV2.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..44612bbf9ce0b70f3e6a0ec0953d3a663f237c29
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmSubBlockV2.cxx
@@ -0,0 +1,382 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "CpmSubBlockV2.h"
+
+namespace LVL1BS {
+
+// Constant definitions
+
+const int      CpmSubBlockV2::s_wordIdVal;
+
+const int      CpmSubBlockV2::s_wordLength;
+
+const int      CpmSubBlockV2::s_ttDataABit;
+const int      CpmSubBlockV2::s_ttDataBBit;
+const int      CpmSubBlockV2::s_parityABit;
+const int      CpmSubBlockV2::s_parityBBit;
+const int      CpmSubBlockV2::s_linkDownABit;
+const int      CpmSubBlockV2::s_linkDownBBit;
+const int      CpmSubBlockV2::s_pairBit;
+const int      CpmSubBlockV2::s_fpgaBit;
+const int      CpmSubBlockV2::s_dataIdBit;
+const int      CpmSubBlockV2::s_ttWordId;
+const uint32_t CpmSubBlockV2::s_ttDataMask;
+const uint32_t CpmSubBlockV2::s_pairPinMask;
+const uint32_t CpmSubBlockV2::s_dataIdMask;
+
+const int      CpmSubBlockV2::s_pairsPerPin;
+const int      CpmSubBlockV2::s_wordsPerPin;
+const int      CpmSubBlockV2::s_ttBits;
+const int      CpmSubBlockV2::s_errBits;
+const int      CpmSubBlockV2::s_bcnBits;
+const int      CpmSubBlockV2::s_bcnPin;
+const int      CpmSubBlockV2::s_glinkPins;
+const int      CpmSubBlockV2::s_glinkBitsPerSlice;
+
+
+CpmSubBlockV2::CpmSubBlockV2() : m_channels(80)
+{
+  m_chanPresent.assign(m_channels, 0);
+}
+
+CpmSubBlockV2::~CpmSubBlockV2()
+{
+}
+
+// Clear all data
+
+void CpmSubBlockV2::clear()
+{
+  L1CaloSubBlock::clear();
+  m_ttData.clear();
+  m_chanPresent.assign(m_channels, 0);
+}
+
+// Store CPM header
+
+void CpmSubBlockV2::setCpmHeader(const int version, const int format,
+                                 const int slice, const int crate,
+                                 const int module, const int timeslices)
+{
+  setHeader(s_wordIdVal, version, format, slice, crate, module, 0, timeslices);
+}
+
+// Store trigger tower data
+
+void CpmSubBlockV2::fillTowerData(const int slice, const int channel,
+                                  const int em, const int had,
+                                  const int emErr, const int hadErr)
+{
+  if (channel < m_channels && (em || emErr || had || hadErr)) {
+    resize(m_ttData, m_channels);
+    int dat = em;
+    int err = emErr;
+    for (int pinOffset = 0; pinOffset < 2; ++pinOffset) {
+      if (dat || err) {
+        const int pin  = 2 * (channel/s_wordsPerPin) + pinOffset;
+        const int pair = (channel % s_wordsPerPin) / 2;
+        const int pos  = pin * s_pairsPerPin + pair;
+        const int ix   = index(slice) * m_channels + pos;
+        uint32_t word = m_ttData[ix];
+        if (channel % 2 == 0) {
+          word |= (dat & s_ttDataMask) << s_ttDataABit;
+	  word |= (err & 0x1)          << s_parityABit;
+	  word |= ((err >> 1) & 0x1)   << s_linkDownABit;
+        } else {
+          word |= (dat & s_ttDataMask) << s_ttDataBBit;
+	  word |= (err & 0x1)          << s_parityBBit;
+	  word |= ((err >> 1) & 0x1)   << s_linkDownBBit;
+        }
+        word |= pair       << s_pairBit;
+        word |= pin        << s_fpgaBit;
+        word |= s_ttWordId << s_dataIdBit;
+        m_ttData[ix] = word;
+      }
+      dat = had;
+      err = hadErr;
+    }
+    m_chanPresent[channel] = 1;
+  }
+}
+
+// Return Em data for given channel
+
+int CpmSubBlockV2::emData(const int slice, const int channel) const
+{
+  return data(slice, channel, 0);
+}
+
+// Return Had data for given channel
+
+int CpmSubBlockV2::hadData(const int slice, const int channel) const
+{
+  return data(slice, channel, 1);
+}
+
+// Return Em error for given channel
+
+int CpmSubBlockV2::emError(const int slice, const int channel) const
+{
+  return error(slice, channel, 0);
+}
+
+// Return Had error for given channel
+
+int CpmSubBlockV2::hadError(const int slice, const int channel) const
+{
+  return error(slice, channel, 1);
+}
+
+// Return number of timeslices
+
+int CpmSubBlockV2::timeslices() const
+{
+  int slices = slices1();
+  if (slices == 0 && format() == NEUTRAL) {
+    slices = dataWords() / s_glinkBitsPerSlice;
+  }
+  if (slices == 0) slices = 1;
+  return slices;
+}
+
+// Packing/Unpacking routines
+
+bool CpmSubBlockV2::pack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 2:                         // <<== CHECK
+      switch (format()) {
+        case NEUTRAL:
+	  rc = packNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = packUncompressed();
+	  break;
+        default:
+	  break;
+      }
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
+
+bool CpmSubBlockV2::unpack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 2:                         // <<== CHECK
+      switch (format()) {
+        case NEUTRAL:
+	  rc = unpackNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = unpackUncompressed();
+	  break;
+        default:
+	  setUnpackErrorCode(UNPACK_FORMAT);
+	  break;
+      }
+      break;
+    default:
+      setUnpackErrorCode(UNPACK_VERSION);
+      break;
+  }
+  return rc;
+}
+
+// Return data for given channel and pin offset
+
+int CpmSubBlockV2::data(const int slice, const int channel,
+                                         const int offset) const
+{
+  int dat = 0;
+  if (slice >= 0 && slice < timeslices() &&
+      channel >= 0 && channel < m_channels && !m_ttData.empty()) {
+    const int pin  = 2 * (channel/s_wordsPerPin) + offset;
+    const int pair = (channel % s_wordsPerPin) / 2;
+    const int pos  = pin * s_pairsPerPin + pair;
+    const int ix   = index(slice) * m_channels + pos;
+    const uint32_t word = m_ttData[ix];
+    if (channel % 2 == 0) {
+           dat = (word >> s_ttDataABit) & s_ttDataMask;
+    } else dat = (word >> s_ttDataBBit) & s_ttDataMask;
+  }
+  return dat;
+}
+
+// Return error for given channel and pin offset
+
+int CpmSubBlockV2::error(const int slice, const int channel,
+                                          const int offset) const
+{
+  int err = 0;
+  if (slice >= 0 && slice < timeslices() &&
+      channel >= 0 && channel < m_channels && !m_ttData.empty()) {
+    const int pin  = 2 * (channel/s_wordsPerPin) + offset;
+    const int pair = (channel % s_wordsPerPin) / 2;
+    const int pos  = pin * s_pairsPerPin + pair;
+    const int ix   = index(slice) * m_channels + pos;
+    const uint32_t word = m_ttData[ix];
+    if (channel % 2 == 0) {
+      err  =  (word >> s_parityABit)   & 0x1;
+      err |= ((word >> s_linkDownABit) & 0x1) << 1;
+    } else {
+      err  =  (word >> s_parityBBit)   & 0x1;
+      err |= ((word >> s_linkDownBBit) & 0x1) << 1;
+    }
+  }
+  return err;
+}
+
+// Return data index appropriate to format
+
+int CpmSubBlockV2::index(const int slice) const
+{
+  return (format() == NEUTRAL) ? slice : 0;
+}
+
+// Resize a data vector according to format
+
+void CpmSubBlockV2::resize(std::vector<uint32_t>& vec, int channels)
+{
+  if (vec.empty()) {
+    int size = channels;
+    if (format() == NEUTRAL) size *= timeslices();
+    vec.resize(size);
+  }
+}
+
+// Pack neutral data
+
+bool CpmSubBlockV2::packNeutral()
+{
+  resize(m_ttData, m_channels);
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    for (int pin = 0; pin < s_glinkPins; ++pin) {
+      // Trigger tower data
+      for (int pair = 0; pair < s_pairsPerPin; ++pair) {
+        for (int i = 0; i < 2; ++i) {
+          const int channel = s_wordsPerPin*(pin/2) + 2*pair + i;
+	  if ((pin & 0x1)) { // Odd pins Had, even Em
+	    packerNeutral(pin, hadData(slice,  channel), s_ttBits);
+	    packerNeutral(pin, hadError(slice, channel), s_errBits);
+          } else {
+	    packerNeutral(pin, emData(slice,  channel), s_ttBits);
+	    packerNeutral(pin, emError(slice, channel), s_errBits);
+	  }
+        }
+      }
+      // Padding and Bunch Crossing number
+      if (pin < s_bcnPin) {
+        packerNeutral(pin, 0, s_bcnBits);
+      } else {
+        packerNeutral(pin, bunchCrossing() >> (pin - s_bcnPin)*s_bcnBits,
+	                                                         s_bcnBits);
+      }
+      // G-Link parity
+      packerNeutralParity(pin);
+    }
+  }
+  return true;
+}
+
+// Pack uncompressed data
+
+bool CpmSubBlockV2::packUncompressed()
+{
+  // Trigger tower data
+  std::vector<uint32_t>::const_iterator pos;
+  for (pos = m_ttData.begin(); pos != m_ttData.end(); ++pos) {
+    if (*pos) packer(*pos, s_wordLength);
+  }
+  packerFlush();
+  return true;
+}
+
+// Unpack neutral data
+
+bool CpmSubBlockV2::unpackNeutral()
+{
+  resize(m_ttData, m_channels);
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    int bunchCrossing = 0;
+    for (int pin = 0; pin < s_glinkPins; ++pin) {
+      // Trigger tower data
+      for (int pair = 0; pair < s_pairsPerPin; ++pair) {
+	for (int i = 0; i < 2; ++i) {
+          const int channel = s_wordsPerPin*(pin/2) + 2*pair + i;
+	  int em     = 0;
+	  int had    = 0;
+	  int emErr  = 0;
+	  int hadErr = 0;
+	  if ((pin & 0x1)) { // Odd pins Had, even Em
+	    em     = emData(slice, channel);
+	    had    = unpackerNeutral(pin, s_ttBits);
+	    emErr  = emError(slice, channel);
+	    hadErr = unpackerNeutral(pin, s_errBits);
+	  } else {
+	    em     = unpackerNeutral(pin, s_ttBits);
+	    had    = hadData(slice, channel);
+	    emErr  = unpackerNeutral(pin, s_errBits);
+	    hadErr = hadError(slice, channel);
+          }
+	  fillTowerData(slice, channel, em, had, emErr, hadErr);
+        }
+      }
+      // Padding and Bunch Crossing number
+      if (pin < s_bcnPin) {
+        unpackerNeutral(pin, s_bcnBits);
+      } else {
+	bunchCrossing |= unpackerNeutral(pin, s_bcnBits)
+	                                << (pin - s_bcnPin)*s_bcnBits;
+      }
+      // G-Link parity error
+      unpackerNeutralParityError(pin);
+    }
+    setBunchCrossing(bunchCrossing);
+  }
+  const bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  return rc;
+}
+
+// Unpack uncompressed data
+
+bool CpmSubBlockV2::unpackUncompressed()
+{
+  resize(m_ttData, m_channels);
+  unpackerInit();
+  uint32_t word = unpacker(s_wordLength);
+  while (unpackerSuccess()) {
+    const int id = dataId(word);
+    bool err = false;
+    // Trigger tower data
+    if (id == s_ttWordId) {
+      const int ix = (word >> s_pairBit) & s_pairPinMask;
+      if (ix < m_channels && m_ttData[ix] == 0) {
+        m_ttData[ix] = word;
+	const int pin  = ix/4;
+	const int pair = ix%4;
+	const int channel = s_wordsPerPin*(pin/2) + 2*pair;
+	m_chanPresent[channel]   = 1;
+	m_chanPresent[channel+1] = 1;
+      } else err = true;
+    } else err = true;
+    if (err) {
+      setUnpackErrorCode(UNPACK_SOURCE_ID);
+      return false;
+    }
+    word = unpacker(s_wordLength);
+  }
+  return true;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmSubBlockV2.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmSubBlockV2.h
new file mode 100755
index 0000000000000000000000000000000000000000..c899117724ec32e49666d6c4d259df6ede854edb
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/CpmSubBlockV2.h
@@ -0,0 +1,127 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPMSUBBLOCKV2_H
+#define TRIGT1CALOBYTESTREAM_CPMSUBBLOCKV2_H
+
+#include <stdint.h>
+#include <vector>
+
+#include "L1CaloSubBlock.h"
+
+namespace LVL1BS {
+
+/** Sub-Block class for CPM data post LS1.
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version X.xxx                                          <<== CHECK
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpmSubBlockV2 : public L1CaloSubBlock {
+
+ public:
+   CpmSubBlockV2();
+   ~CpmSubBlockV2();
+
+   /// Clear all data
+   void clear();
+
+   /// Store CPM header
+   void setCpmHeader(int version, int format, int slice, int crate,
+                     int module, int timeslices);
+   /// Store trigger tower data
+   void fillTowerData(int slice, int channel, int em, int had,
+	                                      int emErr, int hadErr);
+
+   /// Return Em data for given channel
+   int  emData(int slice, int channel)   const;
+   /// Return Had data for given channel
+   int  hadData(int slice, int channel)  const;
+   /// Return Em error for given channel
+   int  emError(int slice, int channel)  const;
+   /// Return Had error for given channel
+   int  hadError(int slice, int channel) const;
+   /// Return number of timeslices
+   int  timeslices()                     const;
+   /// Return true if there is tower data for given channel
+   bool anyTowerData(int channel)        const;
+
+   /// Pack data
+   bool pack();
+   /// Unpack data
+   bool unpack();
+
+ private:
+   /// CPM header word ID
+   static const int      s_wordIdVal       = 0xc;
+   /// Data word length
+   static const int      s_wordLength      = 32;
+   //  Trigger tower data word bit positions and masks
+   static const int      s_ttDataABit      = 0;
+   static const int      s_ttDataBBit      = 9;
+   static const int      s_parityABit      = 8;
+   static const int      s_parityBBit      = 17;
+   static const int      s_linkDownABit    = 19;
+   static const int      s_linkDownBBit    = 20;
+   static const int      s_pairBit         = 21;
+   static const int      s_fpgaBit         = 23;
+   static const int      s_dataIdBit       = 30;
+   static const int      s_ttWordId        = 0x1;
+   static const uint32_t s_ttDataMask      = 0xff;
+   static const uint32_t s_pairPinMask     = 0x7f;
+   static const uint32_t s_dataIdMask      = 0x3;
+   //  G-Link/Neutral format
+   static const int      s_pairsPerPin       = 4;
+   static const int      s_wordsPerPin       = 8;
+   static const int      s_ttBits            = 8;
+   static const int      s_errBits           = 2;
+   static const int      s_bcnBits           = 3;
+   static const int      s_bcnPin            = 16;
+   static const int      s_glinkPins         = 20;
+   static const int      s_glinkBitsPerSlice = 84;
+
+   /// Return data WordID
+   int  dataId(uint32_t word) const;
+   /// Return data for given channel and pin offset
+   int  data(int slice, int channel, int offset)  const;
+   /// Return error for given channel and pin offset
+   int  error(int slice, int channel, int offset) const;
+   /// Return data index appropriate to format
+   int  index(int slice)      const;
+   /// Resize a data vector according to format
+   void resize(std::vector<uint32_t>& vec, int channels);
+
+   /// Pack neutral data
+   bool packNeutral();
+   /// Pack uncompressed data
+   bool packUncompressed();
+   /// Unpack neutral data
+   bool unpackNeutral();
+   /// Unpack uncompressed data
+   bool unpackUncompressed();
+
+   /// Trigger tower data
+   std::vector<uint32_t> m_ttData;
+   /// Channel present flags vector
+   std::vector<int> m_chanPresent;
+   /// Number of Trigger tower channels per module
+   int m_channels;
+
+};
+
+inline int CpmSubBlockV2::dataId(const uint32_t word) const
+{
+  return (word >> s_dataIdBit) & s_dataIdMask;
+}
+
+inline bool CpmSubBlockV2::anyTowerData(const int channel) const
+{
+  return m_chanPresent[channel];
+}
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/IPpmByteStreamSubsetTool.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/IPpmByteStreamSubsetTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..595e10f43db522148c2f08c1a0591406a473c42e
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/IPpmByteStreamSubsetTool.h
@@ -0,0 +1,43 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_IPPMBYTESTREAMSUBSETTOOL_H
+#define TRIGT1CALOBYTESTREAM_IPPMBYTESTREAMSUBSETTOOL_H
+
+#include <vector>
+
+#include "GaudiKernel/IAlgTool.h"
+#include "GaudiKernel/IInterface.h"
+
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+#include "DataModel/DataVector.h"
+
+namespace LVL1 {
+  class TriggerTower;
+}
+
+namespace LVL1BS {
+
+static const InterfaceID IID_IPpmByteStreamSubsetTool("LVL1BS::IPpmByteStreamSubsetTool", 1, 0);
+
+class IPpmByteStreamSubsetTool : virtual public IAlgTool {
+
+ public:
+   static const InterfaceID& interfaceID();
+
+   virtual StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                              DataVector<LVL1::TriggerTower>* ttCollection,
+			      const std::vector<unsigned int>& chanIds) = 0;
+   virtual void eventNumber(const unsigned int eN ) = 0;
+      
+};
+
+inline const InterfaceID& IPpmByteStreamSubsetTool::interfaceID()
+{ 
+  return IID_IPpmByteStreamSubsetTool;
+}
+
+} // end of namespace
+
+#endif 
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/ITriggerTowerSelectionTool.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/ITriggerTowerSelectionTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..aae45e46d687b5048eeb60d97e5d2283bee78e29
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/ITriggerTowerSelectionTool.h
@@ -0,0 +1,38 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_ITRIGGERTOWERSELECTIONTOOL_H
+#define TRIGT1CALOBYTESTREAM_ITRIGGERTOWERSELECTIONTOOL_H
+
+#include <vector>
+
+#include "GaudiKernel/IAlgTool.h"
+#include "GaudiKernel/IInterface.h"
+
+
+namespace LVL1BS {
+
+static const InterfaceID IID_ITriggerTowerSelectionTool("LVL1BS::ITriggerTowerSelectionTool", 1, 0);
+
+class ITriggerTowerSelectionTool : virtual public IAlgTool {
+
+ public:
+   static const InterfaceID& interfaceID();
+
+   virtual void channelIDs(double etaMin, double etaMax,
+                           double phiMin, double phiMax,
+			   std::vector<unsigned int>& chanIds) = 0;
+   virtual void robIDs(const std::vector<unsigned int>& chanIds,
+                             std::vector<unsigned int>& robs) = 0;
+      
+};
+
+inline const InterfaceID& ITriggerTowerSelectionTool::interfaceID()
+{ 
+  return IID_ITriggerTowerSelectionTool;
+}
+
+} // end of namespace
+
+#endif 
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JemJetElement.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemJetElement.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..d110fbeb6b84c5f8eb6f0cb7fa6e5ce1952cfb97
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemJetElement.cxx
@@ -0,0 +1,54 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "JemJetElement.h"
+
+namespace LVL1BS {
+
+// Constant definitions
+
+const int      JemJetElement::s_emDataBit;
+const int      JemJetElement::s_emParityBit;
+const int      JemJetElement::s_hadDataBit;
+const int      JemJetElement::s_hadParityBit;
+const int      JemJetElement::s_linkErrorBit;
+const int      JemJetElement::s_pairBit;
+const int      JemJetElement::s_pinBit;
+const int      JemJetElement::s_wordIdBit;
+const int      JemJetElement::s_jeWordId;
+const int      JemJetElement::s_pairsPerPin;
+const int      JemJetElement::s_pairOffset;
+const uint32_t JemJetElement::s_emDataMask;
+const uint32_t JemJetElement::s_emParityMask;
+const uint32_t JemJetElement::s_hadDataMask;
+const uint32_t JemJetElement::s_hadParityMask;
+const uint32_t JemJetElement::s_linkErrorMask;
+const uint32_t JemJetElement::s_pairMask;
+const uint32_t JemJetElement::s_pinMask;
+const uint32_t JemJetElement::s_wordIdMask;
+
+JemJetElement::JemJetElement(uint32_t word) : m_data(word)
+{
+}
+
+JemJetElement::JemJetElement(const int chan, const int emDat, const int hadDat,
+                             const int emParErr, const int hadParErr,
+			     const int linkErr)
+{
+  uint32_t word = 0;
+  word |= (emDat      & s_emDataMask)    << s_emDataBit;
+  word |= (emParErr   & s_emParityMask)  << s_emParityBit;
+  word |= (hadDat     & s_hadDataMask)   << s_hadDataBit;
+  word |= (hadParErr  & s_hadParityMask) << s_hadParityBit;
+  word |= (linkErr    & s_linkErrorMask) << s_linkErrorBit;
+  if (word) {
+    word |= ((chan % s_pairsPerPin + s_pairOffset) & s_pairMask) << s_pairBit;
+    word |= ((chan / s_pairsPerPin)                & s_pinMask)  << s_pinBit;
+    word |= (s_jeWordId & s_wordIdMask)    << s_wordIdBit;
+  }
+  m_data = word;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JemJetElement.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemJetElement.h
new file mode 100755
index 0000000000000000000000000000000000000000..a36385406e222f5ca5a31c0b2420c781d5d6e555
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemJetElement.h
@@ -0,0 +1,118 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEMJETELEMENT_H
+#define TRIGT1CALOBYTESTREAM_JEMJETELEMENT_H
+
+#include <stdint.h>
+
+namespace LVL1BS {
+
+/** JEM jet element dataword class.
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version 1.06d
+ *
+ *  @author Peter Faulkner
+ */
+
+class JemJetElement {
+
+ public:
+   JemJetElement(uint32_t word);
+   JemJetElement(int chan, int emDat, int hadDat,
+                 int emParErr, int hadParErr, int linkErr);
+
+   //  Return jet element data
+   int      channel()   const;
+   int      emData()    const;
+   int      hadData()   const;
+   int      emParity()  const;
+   int      hadParity() const;
+   int      linkError() const;
+   int      pair()      const;
+   int      pin()       const;
+   int      wordId()    const;
+   uint32_t data()      const;
+
+ private:
+   //  Jet Element data word bit positions and masks
+   static const int      s_emDataBit     = 0;
+   static const int      s_emParityBit   = 9;
+   static const int      s_hadDataBit    = 10;
+   static const int      s_hadParityBit  = 19;
+   static const int      s_linkErrorBit  = 20;
+   static const int      s_pairBit       = 23;
+   static const int      s_pinBit        = 25;
+   static const int      s_wordIdBit     = 30;
+   static const int      s_jeWordId      = 0x1;
+   static const int      s_pairsPerPin   = 3;
+   static const int      s_pairOffset    = 1;
+   static const uint32_t s_emDataMask    = 0x1ff;
+   static const uint32_t s_emParityMask  = 0x1;
+   static const uint32_t s_hadDataMask   = 0x1ff;
+   static const uint32_t s_hadParityMask = 0x1;
+   static const uint32_t s_linkErrorMask = 0x3;
+   static const uint32_t s_pairMask      = 0x3;
+   static const uint32_t s_pinMask       = 0x1f;
+   static const uint32_t s_wordIdMask    = 0x3;
+
+   /// Jet element data
+   uint32_t m_data;
+
+};
+
+inline int JemJetElement::channel() const
+{
+  return s_pairsPerPin * pin() + pair() - s_pairOffset;
+}
+
+inline int JemJetElement::emData() const
+{
+  return (m_data >> s_emDataBit) & s_emDataMask;
+}
+
+inline int JemJetElement::hadData() const
+{
+  return (m_data >> s_hadDataBit) & s_hadDataMask;
+}
+
+inline int JemJetElement::emParity() const
+{
+  return (m_data >> s_emParityBit) & s_emParityMask;
+}
+
+inline int JemJetElement::hadParity() const
+{
+  return (m_data >> s_hadParityBit) & s_hadParityMask;
+}
+
+inline int JemJetElement::linkError() const
+{
+  return (m_data >> s_linkErrorBit) & s_linkErrorMask;
+}
+
+inline int JemJetElement::pair() const
+{
+  return (m_data >> s_pairBit) & s_pairMask;
+}
+
+inline int JemJetElement::pin() const
+{
+  return (m_data >> s_pinBit) & s_pinMask;
+}
+
+inline int JemJetElement::wordId() const
+{
+  return (m_data >> s_wordIdBit) & s_wordIdMask;
+}
+
+inline uint32_t JemJetElement::data() const
+{
+  return m_data;
+}
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JemRoiSubBlock.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemRoiSubBlock.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..a26be0c6d712930fa578a79b2473d138f68cb7da
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemRoiSubBlock.cxx
@@ -0,0 +1,187 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "TrigT1CaloEvent/JEMRoI.h"
+
+#include "JemRoiSubBlock.h"
+
+namespace LVL1BS {
+
+// Constant definitions
+
+const int JemRoiSubBlock::s_wordIdVal;
+
+const int JemRoiSubBlock::s_frames;
+const int JemRoiSubBlock::s_framesPerPin;
+const int JemRoiSubBlock::s_bunchCrossingPin;
+const int JemRoiSubBlock::s_hitsBits;
+const int JemRoiSubBlock::s_locationBits;
+const int JemRoiSubBlock::s_saturationBits;
+const int JemRoiSubBlock::s_bunchCrossingBits;
+const int JemRoiSubBlock::s_paddingBits;
+
+
+JemRoiSubBlock::JemRoiSubBlock()
+{
+}
+
+JemRoiSubBlock::~JemRoiSubBlock()
+{
+}
+
+// Clear all data
+
+void JemRoiSubBlock::clear()
+{
+  L1CaloSubBlock::clear();
+  m_roiData.clear();
+}
+
+// Store header
+
+void JemRoiSubBlock::setRoiHeader(const int version, const int crate,
+                                                     const int module)
+{
+  setHeader(s_wordIdVal, version, NEUTRAL, 0, crate, module, 0, 1);
+}
+
+// Store RoI
+
+void JemRoiSubBlock::fillRoi(const LVL1::JEMRoI roi)
+{
+  const LVL1::JEMRoI roiTemp(crate(), module(), 0, 0, 0, 0, 0);
+  if (roi.crate() == roiTemp.crate() && roi.jem() == roiTemp.jem()) {
+    m_roiData.resize(2*s_frames);
+    const int pos = roi.frame() + roi.forward()*s_frames;
+    m_roiData[pos] = roi;
+  }
+}
+
+// Return RoI for given frame and forward
+
+LVL1::JEMRoI JemRoiSubBlock::roi(const int frame, const int forward) const
+{
+  const int pos = frame + forward*s_frames;
+  if (pos >= 0 && pos < 2*s_frames && !m_roiData.empty()) {
+    return m_roiData[pos];
+  } else return LVL1::JEMRoI(0);
+}
+
+// Packing/Unpacking routines
+
+bool JemRoiSubBlock::pack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = packNeutral();
+	  break;
+        default:
+	  break;
+      }
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
+
+bool JemRoiSubBlock::unpack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = unpackNeutral();
+	  break;
+        default:
+	  setUnpackErrorCode(UNPACK_FORMAT);
+	  break;
+      }
+      break;
+    default:
+      setUnpackErrorCode(UNPACK_VERSION);
+      break;
+  }
+  return rc;
+}
+
+// Pack neutral data
+
+bool JemRoiSubBlock::packNeutral()
+{
+  m_roiData.resize(2*s_frames);
+  int maxPin = 0;
+  // RoI data
+  for (int pos = 0; pos < 2*s_frames; ++pos) {
+    const LVL1::JEMRoI& roi(m_roiData[pos]);
+    int pin = pos/s_framesPerPin;
+    if (pin >= s_bunchCrossingPin) ++pin; // forward rois
+    packerNeutral(pin, roi.hits(),     s_hitsBits);
+    packerNeutral(pin, roi.error(),    s_saturationBits);
+    packerNeutral(pin, roi.location(), s_locationBits);
+    maxPin = pin;
+  }
+  // Bunch Crossing number
+  packerNeutral(s_bunchCrossingPin, bunchCrossing(), s_bunchCrossingBits);
+  packerNeutral(s_bunchCrossingPin, 0, s_paddingBits);
+  // G-Link parity
+  for (int pin = 0; pin <= maxPin; ++pin) packerNeutralParity(pin);
+
+  return true;
+}
+
+// Unpack neutral data
+
+bool JemRoiSubBlock::unpackNeutral()
+{
+  m_roiData.resize(2*s_frames);
+  int maxPin  = 0;
+  int forward = 0;
+  int parity  = 0;
+  // RoI data
+  for (int pos = 0; pos < 2*s_frames; ++pos) {
+    int pin = pos/s_framesPerPin;
+    if (pin >= s_bunchCrossingPin) {
+      ++pin;
+      forward = 1;
+    }
+    const int hits = unpackerNeutral(pin, s_hitsBits);
+    const int sat  = unpackerNeutral(pin, s_saturationBits);
+    const int loc  = unpackerNeutral(pin, s_locationBits);
+    const int err  = (parity << 1) | sat;
+    m_roiData[pos] = LVL1::JEMRoI(crate(), module(), pos%s_frames, loc,
+                                           forward, hits, err);
+    maxPin = pin;
+  }
+  // Bunch Crossing number
+  setBunchCrossing(unpackerNeutral(s_bunchCrossingPin, s_bunchCrossingBits));
+  unpackerNeutral(s_bunchCrossingPin, s_paddingBits);
+  // G-Link parity
+  parity = 1;
+  for (int pin = 0; pin <= maxPin; ++pin) {
+    const bool error = unpackerNeutralParityError(pin);
+    if (pin == s_bunchCrossingPin) continue;
+    if (error) {
+      for (int frame = 0; frame < s_framesPerPin; ++frame) {
+	int pos = pin * s_framesPerPin + frame;
+	if (pin > s_bunchCrossingPin) pos -= s_framesPerPin;
+        const LVL1::JEMRoI roi = m_roiData[pos];
+	const int err = (parity << 1) | roi.error();
+	m_roiData[pos] = LVL1::JEMRoI(roi.crate(), roi.jem(), roi.frame(),
+	                              roi.location(), roi.forward(),
+			              roi.hits(), err);
+      }
+    }
+  }
+  const bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  return rc;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JemRoiSubBlock.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemRoiSubBlock.h
new file mode 100755
index 0000000000000000000000000000000000000000..aa85ae733e99e0f9c4d3555070a1d9b09b205cd4
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemRoiSubBlock.h
@@ -0,0 +1,73 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEMROISUBBLOCK_H
+#define TRIGT1CALOBYTESTREAM_JEMROISUBBLOCK_H
+
+#include <vector>
+
+#include "L1CaloSubBlock.h"
+
+namespace LVL1 {
+  class JEMRoI;
+}
+
+namespace LVL1BS {
+
+/** Sub-Block class for JEM RoI data (neutral format).
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version 1.09h
+ *
+ *  @author Peter Faulkner
+ */
+
+class JemRoiSubBlock : public L1CaloSubBlock {
+
+ public:
+   JemRoiSubBlock();
+   ~JemRoiSubBlock();
+
+   /// Clear all data
+   void clear();
+
+   /// Store header
+   void setRoiHeader(int version, int crate, int module);
+   /// Store RoI
+   void fillRoi(LVL1::JEMRoI roi);
+
+   /// Return RoI for given frame and forward
+   LVL1::JEMRoI roi(int frame, int forward) const;
+
+   /// Pack data
+   bool pack();
+   /// Unpack data
+   bool unpack();
+
+ private:
+   /// Header word ID
+   static const int s_wordIdVal         = 0xc;
+   //  G-Link/Neutral format
+   static const int s_frames            = 8;
+   static const int s_framesPerPin      = 4;
+   static const int s_bunchCrossingPin  = 2;
+   static const int s_hitsBits          = 8;
+   static const int s_locationBits      = 2;
+   static const int s_saturationBits    = 1;
+   static const int s_bunchCrossingBits = 12;
+   static const int s_paddingBits       = 32;
+
+   /// Pack neutral data
+   bool packNeutral();
+   /// Unpack neutral data
+   bool unpackNeutral();
+
+   /// RoIs
+   std::vector<LVL1::JEMRoI> m_roiData;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JemRoiSubBlockV1.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemRoiSubBlockV1.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..1e79947681f8625a4efc7bba734aeb10dc84f4fb
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemRoiSubBlockV1.cxx
@@ -0,0 +1,187 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "TrigT1CaloEvent/JEMRoI.h"
+
+#include "JemRoiSubBlockV1.h"
+
+namespace LVL1BS {
+
+// Constant definitions
+
+const int JemRoiSubBlockV1::s_wordIdVal;
+
+const int JemRoiSubBlockV1::s_frames;
+const int JemRoiSubBlockV1::s_framesPerPin;
+const int JemRoiSubBlockV1::s_bunchCrossingPin;
+const int JemRoiSubBlockV1::s_hitsBits;
+const int JemRoiSubBlockV1::s_locationBits;
+const int JemRoiSubBlockV1::s_saturationBits;
+const int JemRoiSubBlockV1::s_bunchCrossingBits;
+const int JemRoiSubBlockV1::s_paddingBits;
+
+
+JemRoiSubBlockV1::JemRoiSubBlockV1()
+{
+}
+
+JemRoiSubBlockV1::~JemRoiSubBlockV1()
+{
+}
+
+// Clear all data
+
+void JemRoiSubBlockV1::clear()
+{
+  L1CaloSubBlock::clear();
+  m_roiData.clear();
+}
+
+// Store header
+
+void JemRoiSubBlockV1::setRoiHeader(const int version, const int crate,
+                                                       const int module)
+{
+  setHeader(s_wordIdVal, version, NEUTRAL, 0, crate, module, 0, 1);
+}
+
+// Store RoI
+
+void JemRoiSubBlockV1::fillRoi(const LVL1::JEMRoI roi)
+{
+  const LVL1::JEMRoI roiTemp(crate(), module(), 0, 0, 0, 0, 0);
+  if (roi.crate() == roiTemp.crate() && roi.jem() == roiTemp.jem()) {
+    m_roiData.resize(2*s_frames);
+    const int pos = roi.frame() + roi.forward()*s_frames;
+    m_roiData[pos] = roi;
+  }
+}
+
+// Return RoI for given frame and forward
+
+LVL1::JEMRoI JemRoiSubBlockV1::roi(const int frame, const int forward) const
+{
+  const int pos = frame + forward*s_frames;
+  if (pos >= 0 && pos < 2*s_frames && !m_roiData.empty()) {
+    return m_roiData[pos];
+  } else return LVL1::JEMRoI(0);
+}
+
+// Packing/Unpacking routines
+
+bool JemRoiSubBlockV1::pack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = packNeutral();
+	  break;
+        default:
+	  break;
+      }
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
+
+bool JemRoiSubBlockV1::unpack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = unpackNeutral();
+	  break;
+        default:
+	  setUnpackErrorCode(UNPACK_FORMAT);
+	  break;
+      }
+      break;
+    default:
+      setUnpackErrorCode(UNPACK_VERSION);
+      break;
+  }
+  return rc;
+}
+
+// Pack neutral data
+
+bool JemRoiSubBlockV1::packNeutral()
+{
+  m_roiData.resize(2*s_frames);
+  int maxPin = 0;
+  // RoI data
+  for (int pos = 0; pos < 2*s_frames; ++pos) {
+    const LVL1::JEMRoI& roi(m_roiData[pos]);
+    int pin = pos/s_framesPerPin;
+    if (pin >= s_bunchCrossingPin) ++pin; // forward rois
+    packerNeutral(pin, roi.hits(),     s_hitsBits);
+    packerNeutral(pin, roi.error(),    s_saturationBits);
+    packerNeutral(pin, roi.location(), s_locationBits);
+    maxPin = pin;
+  }
+  // Bunch Crossing number
+  packerNeutral(s_bunchCrossingPin, bunchCrossing(), s_bunchCrossingBits);
+  packerNeutral(s_bunchCrossingPin, 0, s_paddingBits);
+  // G-Link parity
+  for (int pin = 0; pin <= maxPin; ++pin) packerNeutralParity(pin);
+
+  return true;
+}
+
+// Unpack neutral data
+
+bool JemRoiSubBlockV1::unpackNeutral()
+{
+  m_roiData.resize(2*s_frames);
+  int maxPin  = 0;
+  int forward = 0;
+  int parity  = 0;
+  // RoI data
+  for (int pos = 0; pos < 2*s_frames; ++pos) {
+    int pin = pos/s_framesPerPin;
+    if (pin >= s_bunchCrossingPin) {
+      ++pin;
+      forward = 1;
+    }
+    const int hits = unpackerNeutral(pin, s_hitsBits);
+    const int sat  = unpackerNeutral(pin, s_saturationBits);
+    const int loc  = unpackerNeutral(pin, s_locationBits);
+    const int err  = (parity << 1) | sat;
+    m_roiData[pos] = LVL1::JEMRoI(crate(), module(), pos%s_frames, loc,
+                                           forward, hits, err);
+    maxPin = pin;
+  }
+  // Bunch Crossing number
+  setBunchCrossing(unpackerNeutral(s_bunchCrossingPin, s_bunchCrossingBits));
+  unpackerNeutral(s_bunchCrossingPin, s_paddingBits);
+  // G-Link parity
+  parity = 1;
+  for (int pin = 0; pin <= maxPin; ++pin) {
+    const bool error = unpackerNeutralParityError(pin);
+    if (pin == s_bunchCrossingPin) continue;
+    if (error) {
+      for (int frame = 0; frame < s_framesPerPin; ++frame) {
+	int pos = pin * s_framesPerPin + frame;
+	if (pin > s_bunchCrossingPin) pos -= s_framesPerPin;
+        const LVL1::JEMRoI roi = m_roiData[pos];
+	const int err = (parity << 1) | roi.error();
+	m_roiData[pos] = LVL1::JEMRoI(roi.crate(), roi.jem(), roi.frame(),
+	                              roi.location(), roi.forward(),
+			              roi.hits(), err);
+      }
+    }
+  }
+  const bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  return rc;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JemRoiSubBlockV1.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemRoiSubBlockV1.h
new file mode 100755
index 0000000000000000000000000000000000000000..c57701d1b31c90ff94fa3f9fb015c773389a0641
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemRoiSubBlockV1.h
@@ -0,0 +1,73 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEMROISUBBLOCKV1_H
+#define TRIGT1CALOBYTESTREAM_JEMROISUBBLOCKV1_H
+
+#include <vector>
+
+#include "L1CaloSubBlock.h"
+
+namespace LVL1 {
+  class JEMRoI;
+}
+
+namespace LVL1BS {
+
+/** Sub-Block class for JEM RoI data (neutral format) pre-LS1.
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version 1.09h
+ *
+ *  @author Peter Faulkner
+ */
+
+class JemRoiSubBlockV1 : public L1CaloSubBlock {
+
+ public:
+   JemRoiSubBlockV1();
+   ~JemRoiSubBlockV1();
+
+   /// Clear all data
+   void clear();
+
+   /// Store header
+   void setRoiHeader(int version, int crate, int module);
+   /// Store RoI
+   void fillRoi(LVL1::JEMRoI roi);
+
+   /// Return RoI for given frame and forward
+   LVL1::JEMRoI roi(int frame, int forward) const;
+
+   /// Pack data
+   bool pack();
+   /// Unpack data
+   bool unpack();
+
+ private:
+   /// Header word ID
+   static const int s_wordIdVal         = 0xc;
+   //  G-Link/Neutral format
+   static const int s_frames            = 8;
+   static const int s_framesPerPin      = 4;
+   static const int s_bunchCrossingPin  = 2;
+   static const int s_hitsBits          = 8;
+   static const int s_locationBits      = 2;
+   static const int s_saturationBits    = 1;
+   static const int s_bunchCrossingBits = 12;
+   static const int s_paddingBits       = 32;
+
+   /// Pack neutral data
+   bool packNeutral();
+   /// Unpack neutral data
+   bool unpackNeutral();
+
+   /// RoIs
+   std::vector<LVL1::JEMRoI> m_roiData;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JemRoiSubBlockV2.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemRoiSubBlockV2.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..6dcca955eb03dd9b625def1809001b17927c3122
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemRoiSubBlockV2.cxx
@@ -0,0 +1,167 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "TrigT1CaloEvent/JEMTobRoI.h"
+
+#include "JemRoiSubBlockV2.h"
+
+namespace LVL1BS {
+
+// Constant definitions
+
+const int JemRoiSubBlockV2::s_wordIdVal;
+
+const int JemRoiSubBlockV2::s_frames;
+const int JemRoiSubBlockV2::s_framesPerPin;
+const int JemRoiSubBlockV2::s_bunchCrossingPin;
+const int JemRoiSubBlockV2::s_energySmallBits;
+const int JemRoiSubBlockV2::s_energyLargeBits;
+const int JemRoiSubBlockV2::s_locationBits;
+const int JemRoiSubBlockV2::s_bunchCrossingBits;
+const int JemRoiSubBlockV2::s_paddingBits;
+
+
+JemRoiSubBlockV2::JemRoiSubBlockV2()
+{
+}
+
+JemRoiSubBlockV2::~JemRoiSubBlockV2()
+{
+}
+
+// Clear all data
+
+void JemRoiSubBlockV2::clear()
+{
+  L1CaloSubBlock::clear();
+  m_roiData.clear();
+}
+
+// Store header
+
+void JemRoiSubBlockV2::setRoiHeader(const int version, const int crate,
+                                                       const int module)
+{
+  setHeader(s_wordIdVal, version, NEUTRAL, 0, crate, module, 0, 1);
+}
+
+// Store RoI
+
+void JemRoiSubBlockV2::fillRoi(const LVL1::JEMTobRoI roi)
+{
+  const LVL1::JEMTobRoI roiTemp(crate(), module(), 0, 0, 0, 0);
+  if (roi.crate() == roiTemp.crate() && roi.jem() == roiTemp.jem()) {
+    m_roiData.resize(s_frames);
+    const int pos = roi.frame();
+    m_roiData[pos] = roi;
+  }
+}
+
+// Return RoI for given frame
+
+LVL1::JEMTobRoI JemRoiSubBlockV2::roi(const int frame) const
+{
+  if (frame >= 0 && frame < s_frames && !m_roiData.empty()) {
+    return m_roiData[frame];
+  } else return LVL1::JEMTobRoI(0);
+}
+
+// Packing/Unpacking routines
+
+bool JemRoiSubBlockV2::pack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 2:                                               //<< CHECK
+      switch (format()) {
+        case NEUTRAL:
+	  rc = packNeutral();
+	  break;
+        default:
+	  break;
+      }
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
+
+bool JemRoiSubBlockV2::unpack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 2:                                               //<< CHECK
+      switch (format()) {
+        case NEUTRAL:
+	  rc = unpackNeutral();
+	  break;
+        default:
+	  setUnpackErrorCode(UNPACK_FORMAT);
+	  break;
+      }
+      break;
+    default:
+      setUnpackErrorCode(UNPACK_VERSION);
+      break;
+  }
+  return rc;
+}
+
+// Pack neutral data
+
+bool JemRoiSubBlockV2::packNeutral()
+{
+  m_roiData.resize(s_frames);
+  int maxPin = 0;
+  // RoI data
+  for (int frame = 0; frame < s_frames; ++frame) {
+    const LVL1::JEMTobRoI& roi(m_roiData[frame]);
+    const int pin1 = frame/s_framesPerPin;
+    const int pin2 = s_bunchCrossingPin + pin1 + 1;
+    packerNeutral(pin1, roi.energyLarge(), s_energyLargeBits);
+    packerNeutral(pin1, 0, 1);
+    packerNeutral(pin2, roi.energySmall(), s_energySmallBits);
+    packerNeutral(pin2, roi.location(), s_locationBits);
+    maxPin = pin2;
+  }
+  // Bunch Crossing number
+  packerNeutral(s_bunchCrossingPin, bunchCrossing(), s_bunchCrossingBits);
+  packerNeutral(s_bunchCrossingPin, 0, s_paddingBits);
+  // G-Link parity
+  for (int pin = 0; pin <= maxPin; ++pin) packerNeutralParity(pin);
+
+  return true;
+}
+
+// Unpack neutral data
+
+bool JemRoiSubBlockV2::unpackNeutral()
+{
+  m_roiData.resize(s_frames);
+  int maxPin  = 0;
+  // RoI data
+  for (int frame = 0; frame < s_frames; ++frame) {
+    const int pin1 = frame/s_framesPerPin;
+    const int pin2 = s_bunchCrossingPin + pin1 + 1;
+    const int enLarge = unpackerNeutral(pin1, s_energyLargeBits);
+                        unpackerNeutral(pin1, 1);
+    const int enSmall = unpackerNeutral(pin2, s_energySmallBits);
+    const int loc     = unpackerNeutral(pin2, s_locationBits);
+    m_roiData[frame] = LVL1::JEMTobRoI(crate(), module(),
+                                               frame, loc, enLarge, enSmall);
+    maxPin = pin2;
+  }
+  // Bunch Crossing number
+  setBunchCrossing(unpackerNeutral(s_bunchCrossingPin, s_bunchCrossingBits));
+  unpackerNeutral(s_bunchCrossingPin, s_paddingBits);
+  // G-Link parity
+  for (int pin = 0; pin <= maxPin; ++pin) unpackerNeutralParityError(pin);
+  const bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  return rc;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JemRoiSubBlockV2.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemRoiSubBlockV2.h
new file mode 100755
index 0000000000000000000000000000000000000000..16aabd0afcd4f8bd1c036a44fc1fa37f55044fe3
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemRoiSubBlockV2.h
@@ -0,0 +1,73 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEMROISUBBLOCKV2_H
+#define TRIGT1CALOBYTESTREAM_JEMROISUBBLOCKV2_H
+
+#include <vector>
+
+#include "L1CaloSubBlock.h"
+
+namespace LVL1 {
+  class JEMTobRoI;
+}
+
+namespace LVL1BS {
+
+/** Sub-Block class for JEM RoI data (neutral format) post-LS1.
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version X.xxx                                           //<< CHECK
+ *
+ *  @author Peter Faulkner
+ */
+
+class JemRoiSubBlockV2 : public L1CaloSubBlock {
+
+ public:
+   JemRoiSubBlockV2();
+   ~JemRoiSubBlockV2();
+
+   /// Clear all data
+   void clear();
+
+   /// Store header
+   void setRoiHeader(int version, int crate, int module);
+   /// Store RoI
+   void fillRoi(LVL1::JEMTobRoI roi);
+
+   /// Return RoI for given frame
+   LVL1::JEMTobRoI roi(int frame) const;
+
+   /// Pack data
+   bool pack();
+   /// Unpack data
+   bool unpack();
+
+ private:
+   /// Header word ID
+   static const int s_wordIdVal         = 0xc;
+   //  G-Link/Neutral format
+   static const int s_frames            = 8;
+   static const int s_framesPerPin      = 4;
+   static const int s_bunchCrossingPin  = 2;
+   static const int s_energySmallBits   = 9;
+   static const int s_energyLargeBits   = 10;
+   static const int s_locationBits      = 2;
+   static const int s_bunchCrossingBits = 12;
+   static const int s_paddingBits       = 32;
+
+   /// Pack neutral data
+   bool packNeutral();
+   /// Unpack neutral data
+   bool unpackNeutral();
+
+   /// RoIs
+   std::vector<LVL1::JEMTobRoI> m_roiData;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JemSubBlock.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemSubBlock.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..40f5b945778998d48939667fdd3d156ff0a48b2a
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemSubBlock.cxx
@@ -0,0 +1,417 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "JemJetElement.h"
+#include "JemSubBlock.h"
+
+namespace LVL1BS {
+
+// Constant definitions
+
+const int      JemSubBlock::s_wordIdVal;
+
+const int      JemSubBlock::s_wordLength;
+
+const int      JemSubBlock::s_dataIdBit;
+const int      JemSubBlock::s_jeWordId;
+const uint32_t JemSubBlock::s_dataIdMask;
+
+const int      JemSubBlock::s_threshBit;
+const int      JemSubBlock::s_sourceIdBit;
+const int      JemSubBlock::s_jetIndicatorBit;
+const int      JemSubBlock::s_jetIndicator;
+const int      JemSubBlock::s_mainThreshId;
+const int      JemSubBlock::s_mainFwdThreshId;
+const int      JemSubBlock::s_threshWordId;
+const uint32_t JemSubBlock::s_threshMask;
+const uint32_t JemSubBlock::s_sourceIdMask;
+
+const int      JemSubBlock::s_exBit;
+const int      JemSubBlock::s_eyBit;
+const int      JemSubBlock::s_etBit;
+const int      JemSubBlock::s_sumIndicatorBit;
+const int      JemSubBlock::s_sumIndicator;
+const int      JemSubBlock::s_subsumId;
+const uint32_t JemSubBlock::s_exMask;
+const uint32_t JemSubBlock::s_eyMask;
+const uint32_t JemSubBlock::s_etMask;
+
+const int      JemSubBlock::s_pairsPerPin;
+const int      JemSubBlock::s_jetElementBits;
+const int      JemSubBlock::s_jePaddingBits;
+const int      JemSubBlock::s_jetHitsBits;
+const int      JemSubBlock::s_energyBits;
+const int      JemSubBlock::s_bunchCrossingBits;
+const int      JemSubBlock::s_hitPaddingBits;
+const int      JemSubBlock::s_glinkBitsPerSlice;
+
+
+JemSubBlock::JemSubBlock() : m_channels(44)
+{
+}
+
+JemSubBlock::~JemSubBlock()
+{
+}
+
+// Clear all data
+
+void JemSubBlock::clear()
+{
+  L1CaloSubBlock::clear();
+  m_jeData.clear();
+  m_jetHits.clear();
+  m_energySubsums.clear();
+}
+
+// Store JEM header
+
+void JemSubBlock::setJemHeader(const int version, const int format,
+                               const int slice, const int crate,
+                               const int module, const int timeslices)
+{
+  setHeader(s_wordIdVal, version, format, slice, crate, module, 0, timeslices);
+}
+
+// Store jet element data
+
+void JemSubBlock::fillJetElement(const int slice, const JemJetElement& jetEle)
+{
+  if (jetEle.data()) {
+    const int channel = jetEle.channel();
+    if (channel < m_channels) {
+      resize(m_jeData, m_channels);
+      m_jeData[index(slice)*m_channels + channel] = jetEle.data();
+    }
+  }
+}
+
+// Store jet hit counts
+
+void JemSubBlock::setJetHits(const int slice, const unsigned int hits)
+{
+  if (hits) {
+    const int jem = module();
+    const int sourceId = (jem == 0 || jem == 7 || jem == 8 || jem == 15)
+                         ? s_mainFwdThreshId : s_mainThreshId;
+    uint32_t word = 0;
+    word |= (hits           & s_threshMask)   << s_threshBit;
+    word |=  s_jetIndicator                   << s_jetIndicatorBit;
+    word |= (sourceId       & s_sourceIdMask) << s_sourceIdBit;
+    word |=  s_threshWordId                   << s_dataIdBit;
+    resize(m_jetHits);
+    m_jetHits[index(slice)] = word;
+  }
+}
+
+// Store energy subsum data
+
+void JemSubBlock::setEnergySubsums(const int slice, const unsigned int ex,
+                                   const unsigned int ey, const unsigned int et)
+{
+  uint32_t word = 0;
+  word |= (ex & s_exMask) << s_exBit;
+  word |= (ey & s_eyMask) << s_eyBit;
+  word |= (et & s_etMask) << s_etBit;
+  if (word) {
+    word |= s_sumIndicator << s_sumIndicatorBit;
+    word |= s_subsumId     << s_sourceIdBit;
+    word |= s_threshWordId << s_dataIdBit;
+    resize(m_energySubsums);
+    m_energySubsums[index(slice)] = word;
+  }
+}
+
+// Return jet element for given channel
+
+JemJetElement JemSubBlock::jetElement(const int slice, const int channel) const
+{
+  uint32_t je = 0;
+  if (slice >= 0 && slice < timeslices() &&
+      channel >= 0 && channel < m_channels && !m_jeData.empty()) {
+    je = m_jeData[index(slice)*m_channels + channel];
+  }
+  return JemJetElement(je);
+}
+
+// Return jet hit counts
+
+unsigned int JemSubBlock::jetHits(const int slice) const
+{
+  unsigned int hits = 0;
+  if (slice >= 0 && slice < timeslices() && !m_jetHits.empty()) {
+    hits = (m_jetHits[index(slice)] >> s_threshBit) & s_threshMask;
+  }
+  return hits;
+}
+
+// Return energy subsum Ex
+
+unsigned int JemSubBlock::ex(const int slice) const
+{
+  unsigned int ex = 0;
+  if (slice >= 0 && slice < timeslices() && !m_energySubsums.empty()) {
+    ex = (m_energySubsums[index(slice)] >> s_exBit) & s_exMask;
+  }
+  return ex;
+}
+
+// Return energy subsum Ey
+
+unsigned int JemSubBlock::ey(const int slice) const
+{
+  unsigned int ey = 0;
+  if (slice >= 0 && slice < timeslices() && !m_energySubsums.empty()) {
+    ey = (m_energySubsums[index(slice)] >> s_eyBit) & s_eyMask;
+  }
+  return ey;
+}
+
+// Return energy subsum Et
+
+unsigned int JemSubBlock::et(const int slice) const
+{
+  unsigned int et = 0;
+  if (slice >= 0 && slice < timeslices() && !m_energySubsums.empty()) {
+    et = (m_energySubsums[index(slice)] >> s_etBit) & s_etMask;
+  }
+  return et;
+}
+
+// Return number of timeslices
+
+int JemSubBlock::timeslices() const
+{
+  int slices = slices1();
+  if (slices == 0 && format() == NEUTRAL) {
+    slices = dataWords() / s_glinkBitsPerSlice;
+  }
+  if (slices == 0) slices = 1;
+  return slices;
+}
+
+// Packing/Unpacking routines
+
+bool JemSubBlock::pack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = packNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = packUncompressed();
+	  break;
+        default:
+	  break;
+      }
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
+
+bool JemSubBlock::unpack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = unpackNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = unpackUncompressed();
+	  break;
+        default:
+	  setUnpackErrorCode(UNPACK_FORMAT);
+	  break;
+      }
+      break;
+    default:
+      setUnpackErrorCode(UNPACK_VERSION);
+      break;
+  }
+  return rc;
+}
+
+// Return data index appropriate to format
+
+int JemSubBlock::index(const int slice) const
+{
+  return (format() == NEUTRAL) ? slice : 0;
+}
+
+// Resize a data vector according to format
+
+void JemSubBlock::resize(std::vector<uint32_t>& vec, const int channels)
+{
+  if (vec.empty()) {
+    int size = channels;
+    if (format() == NEUTRAL) size *= timeslices();
+    vec.resize(size);
+  }
+}
+
+// Pack neutral data
+
+bool JemSubBlock::packNeutral()
+{
+  resize(m_jeData, m_channels);
+  resize(m_jetHits);
+  resize(m_energySubsums);
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    // Jet element data
+    for (int channel = 0; channel < m_channels; ++channel) {
+      const int pin = channel / s_pairsPerPin;
+      const JemJetElement je = jetElement(slice, channel);
+      packerNeutral(pin, je.emData(), s_jetElementBits);
+      packerNeutral(pin, je.emParity(), 1);
+      packerNeutral(pin, je.linkError(), 1);
+      packerNeutral(pin, je.hadData(), s_jetElementBits);
+      packerNeutral(pin, je.hadParity(), 1);
+      packerNeutral(pin, (je.linkError() >> 1), 1);
+    }
+    // Pad out last jet element pin
+    int lastpin = (m_channels - 1) / s_pairsPerPin;
+    packerNeutral(lastpin, 0, s_jePaddingBits);
+    // Jet Hits and Energy Sums with parity bits
+    ++lastpin;
+    packerNeutral(lastpin, jetHits(slice), s_jetHitsBits);
+    packerNeutral(lastpin, parityBit(1, jetHits(slice), s_jetHitsBits), 1);
+    packerNeutral(lastpin, ex(slice), s_energyBits);
+    packerNeutral(lastpin, ey(slice), s_energyBits);
+    packerNeutral(lastpin, et(slice), s_energyBits);
+    int parity =  parityBit(1, ex(slice), s_energyBits);
+    parity = parityBit(parity, ey(slice), s_energyBits);
+    parity = parityBit(parity, et(slice), s_energyBits);
+    packerNeutral(lastpin, parity, 1);
+    // Bunch Crossing number and padding
+    packerNeutral(lastpin, bunchCrossing(), s_bunchCrossingBits);
+    packerNeutral(lastpin, 0, s_hitPaddingBits);
+    // G-Link parity
+    for (int pin = 0; pin <= lastpin; ++pin) packerNeutralParity(pin);
+  }
+  return true;
+}
+
+// Pack uncompressed data
+
+bool JemSubBlock::packUncompressed()
+{
+  // Jet element data
+  std::vector<uint32_t>::const_iterator pos;
+  for (pos = m_jeData.begin(); pos != m_jeData.end(); ++pos) {
+    if (*pos) packer(*pos, s_wordLength);
+  }
+        
+  // Hits and Subsum data
+  if ( !m_jetHits.empty() && m_jetHits[0]) packer(m_jetHits[0], s_wordLength);
+  if ( !m_energySubsums.empty() && m_energySubsums[0]) {
+    packer(m_energySubsums[0], s_wordLength);
+  }
+  packerFlush();
+  return true;
+}
+
+// Unpack neutral data
+
+bool JemSubBlock::unpackNeutral()
+{
+  resize(m_jeData, m_channels);
+  resize(m_jetHits);
+  resize(m_energySubsums);
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    // Jet element data
+    for (int channel = 0; channel < m_channels; ++channel) {
+      const int pin = channel / s_pairsPerPin;
+      const int emData     = unpackerNeutral(pin, s_jetElementBits);
+      const int emParity   = unpackerNeutral(pin, 1);
+            int linkError  = unpackerNeutral(pin, 1);
+      const int hadData    = unpackerNeutral(pin, s_jetElementBits);
+      const int hadParity  = unpackerNeutral(pin, 1);
+                linkError |= unpackerNeutral(pin, 1) << 1;
+      const JemJetElement je(channel, emData, hadData, emParity,
+                                              hadParity, linkError);
+      fillJetElement(slice, je);
+    }
+    // Padding from last jet element pin
+    int lastpin = (m_channels - 1) / s_pairsPerPin;
+    unpackerNeutral(lastpin, s_jePaddingBits);
+    // Jet Hits and Energy Sums
+    ++lastpin;
+    setJetHits(slice, unpackerNeutral(lastpin, s_jetHitsBits));
+    unpackerNeutral(lastpin, 1); // parity bit
+    const unsigned int ex = unpackerNeutral(lastpin, s_energyBits);
+    const unsigned int ey = unpackerNeutral(lastpin, s_energyBits);
+    const unsigned int et = unpackerNeutral(lastpin, s_energyBits);
+    setEnergySubsums(slice, ex, ey, et);
+    unpackerNeutral(lastpin, 1); // parity bit
+    // Bunch Crossing number and padding
+    setBunchCrossing(unpackerNeutral(lastpin, s_bunchCrossingBits));
+    unpackerNeutral(lastpin, s_hitPaddingBits);
+    // G-Link parity errors
+    for (int pin = 0; pin <= lastpin; ++pin) unpackerNeutralParityError(pin);
+  }
+  const bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  return rc;
+}
+
+// Unpack uncompressed data
+
+bool JemSubBlock::unpackUncompressed()
+{
+  resize(m_jeData, m_channels);
+  resize(m_jetHits);
+  resize(m_energySubsums);
+  unpackerInit();
+  uint32_t word = unpacker(s_wordLength);
+  while (unpackerSuccess()) {
+    const int id = dataId(word);
+    bool err = false;
+    // Jet element data
+    if (id == s_jeWordId) {
+      const JemJetElement jetEle(word);
+      const int channel = jetEle.channel();
+      if (channel < m_channels && m_jeData[channel] == 0) {
+        m_jeData[channel] = word;
+      } else err = true;
+    // Other data
+    } else {
+      switch (sourceId(word)) {
+	// Jet hit counts/thresholds
+        case s_mainThreshId:
+        case s_mainFwdThreshId: {
+	  if (m_jetHits[0] == 0) m_jetHits[0] = word;
+	  else err = true;
+          break;
+	}
+	// Energy subsums
+        case s_subsumId: {
+	  if (m_energySubsums[0] == 0) m_energySubsums[0] = word;
+	  else err = true;
+	  break;
+	}
+        default:
+	  err = true;
+	  break;
+      }
+    }
+    if (err) {
+      setUnpackErrorCode(UNPACK_SOURCE_ID);
+      return false;
+    }
+    word = unpacker(s_wordLength);
+  }
+  return true;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JemSubBlock.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemSubBlock.h
new file mode 100755
index 0000000000000000000000000000000000000000..b70fa822d8897e15067bd0191d9a54fe61dbdd80
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemSubBlock.h
@@ -0,0 +1,139 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEMSUBBLOCK_H
+#define TRIGT1CALOBYTESTREAM_JEMSUBBLOCK_H
+
+#include <stdint.h>
+#include <vector>
+
+#include "L1CaloSubBlock.h"
+
+namespace LVL1BS {
+
+class JemJetElement;
+
+/** Sub-Block class for JEM data.
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version 1.06d
+ *
+ *  @author Peter Faulkner
+ */
+
+class JemSubBlock : public L1CaloSubBlock {
+
+ public:
+   JemSubBlock();
+   ~JemSubBlock();
+
+   /// Clear all data
+   void clear();
+
+   /// Store JEM header
+   void setJemHeader(int version, int format, int slice, int crate,
+                     int module, int timeslices);
+   /// Store jet element data
+   void fillJetElement(int slice, const JemJetElement& jetEle);
+   /// Store jet hit counts
+   void setJetHits(int slice, unsigned int hits);
+   /// Store energy subsum data
+   void setEnergySubsums(int slice, unsigned int ex,
+	                 unsigned int ey, unsigned int et);
+
+   /// Return jet element for given channel
+   JemJetElement jetElement(int slice, int channel) const;
+   /// Return jet hit counts
+   unsigned int jetHits(int slice) const;
+   /// Return energy subsum Ex
+   unsigned int ex(int slice)      const;
+   /// Return energy subsum Ey
+   unsigned int ey(int slice)      const;
+   /// Return energy subsum Et
+   unsigned int et(int slice)      const;
+   /// Return number of timeslices
+   int  timeslices()               const;
+
+   /// Pack data
+   bool pack();
+   /// Unpack data
+   bool unpack();
+
+ private:
+   /// JEM header word ID
+   static const int      s_wordIdVal       = 0xc;
+   /// Data word length
+   static const int      s_wordLength      = 32;
+   //  Jet Element data word bit positions and masks
+   static const int      s_dataIdBit       = 30;
+   static const int      s_jeWordId        = 0x1;
+   static const uint32_t s_dataIdMask      = 0x3;
+   //  Jet hit counts bit positions and masks
+   static const int      s_threshBit       = 0;
+   static const int      s_sourceIdBit     = 25;
+   static const int      s_jetIndicatorBit = 24;
+   static const int      s_jetIndicator    = 0x0;
+   static const int      s_mainThreshId    = 20;
+   static const int      s_mainFwdThreshId = 21;
+   static const int      s_threshWordId    = 0x2;
+   static const uint32_t s_threshMask      = 0xffffff;
+   static const uint32_t s_sourceIdMask    = 0x1f;
+   //  Energy subsum data bit positions and masks
+   static const int      s_exBit           = 0;
+   static const int      s_eyBit           = 8;
+   static const int      s_etBit           = 16;
+   static const int      s_sumIndicatorBit = 24;
+   static const int      s_sumIndicator    = 0x1;
+   static const int      s_subsumId        = 22;
+   static const uint32_t s_exMask          = 0xff;
+   static const uint32_t s_eyMask          = 0xff;
+   static const uint32_t s_etMask          = 0xff;
+   //  Neutral format data lengths
+   static const int      s_pairsPerPin       = 3;
+   static const int      s_jetElementBits    = 9;
+   static const int      s_jePaddingBits     = 22;
+   static const int      s_jetHitsBits       = 24;
+   static const int      s_energyBits        = 8;
+   static const int      s_bunchCrossingBits = 12;
+   static const int      s_hitPaddingBits    = 4;
+   static const int      s_glinkBitsPerSlice = 67;
+
+   int sourceId(uint32_t word) const;
+   int dataId(uint32_t word)   const;
+   int index(int slice)        const;
+   void resize(std::vector<uint32_t>& vec, int channels = 1);
+
+   /// Pack neutral data
+   bool packNeutral();
+   /// Pack uncompressed data
+   bool packUncompressed();
+   /// Unpack neutral data
+   bool unpackNeutral();
+   /// Unpack uncompressed data
+   bool unpackUncompressed();
+
+   /// Jet element data
+   std::vector<uint32_t> m_jeData;
+   /// Jet hit counts
+   std::vector<uint32_t> m_jetHits;
+   /// Energy subsum data
+   std::vector<uint32_t> m_energySubsums;
+   /// Number of jet element channels
+   int m_channels;
+
+};
+
+inline int JemSubBlock::sourceId(const uint32_t word) const
+{
+  return (word >> s_sourceIdBit) & s_sourceIdMask;
+}
+
+inline int JemSubBlock::dataId(const uint32_t word) const
+{
+  return (word >> s_dataIdBit) & s_dataIdMask;
+}
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JemSubBlockV1.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemSubBlockV1.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..cec0c05c5b361d22c05af508dae0a2d9615c515f
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemSubBlockV1.cxx
@@ -0,0 +1,417 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "JemJetElement.h"
+#include "JemSubBlockV1.h"
+
+namespace LVL1BS {
+
+// Constant definitions
+
+const int      JemSubBlockV1::s_wordIdVal;
+
+const int      JemSubBlockV1::s_wordLength;
+
+const int      JemSubBlockV1::s_dataIdBit;
+const int      JemSubBlockV1::s_jeWordId;
+const uint32_t JemSubBlockV1::s_dataIdMask;
+
+const int      JemSubBlockV1::s_threshBit;
+const int      JemSubBlockV1::s_sourceIdBit;
+const int      JemSubBlockV1::s_jetIndicatorBit;
+const int      JemSubBlockV1::s_jetIndicator;
+const int      JemSubBlockV1::s_mainThreshId;
+const int      JemSubBlockV1::s_mainFwdThreshId;
+const int      JemSubBlockV1::s_threshWordId;
+const uint32_t JemSubBlockV1::s_threshMask;
+const uint32_t JemSubBlockV1::s_sourceIdMask;
+
+const int      JemSubBlockV1::s_exBit;
+const int      JemSubBlockV1::s_eyBit;
+const int      JemSubBlockV1::s_etBit;
+const int      JemSubBlockV1::s_sumIndicatorBit;
+const int      JemSubBlockV1::s_sumIndicator;
+const int      JemSubBlockV1::s_subsumId;
+const uint32_t JemSubBlockV1::s_exMask;
+const uint32_t JemSubBlockV1::s_eyMask;
+const uint32_t JemSubBlockV1::s_etMask;
+
+const int      JemSubBlockV1::s_pairsPerPin;
+const int      JemSubBlockV1::s_jetElementBits;
+const int      JemSubBlockV1::s_jePaddingBits;
+const int      JemSubBlockV1::s_jetHitsBits;
+const int      JemSubBlockV1::s_energyBits;
+const int      JemSubBlockV1::s_bunchCrossingBits;
+const int      JemSubBlockV1::s_hitPaddingBits;
+const int      JemSubBlockV1::s_glinkBitsPerSlice;
+
+
+JemSubBlockV1::JemSubBlockV1() : m_channels(44)
+{
+}
+
+JemSubBlockV1::~JemSubBlockV1()
+{
+}
+
+// Clear all data
+
+void JemSubBlockV1::clear()
+{
+  L1CaloSubBlock::clear();
+  m_jeData.clear();
+  m_jetHits.clear();
+  m_energySubsums.clear();
+}
+
+// Store JEM header
+
+void JemSubBlockV1::setJemHeader(const int version, const int format,
+                                 const int slice, const int crate,
+                                 const int module, const int timeslices)
+{
+  setHeader(s_wordIdVal, version, format, slice, crate, module, 0, timeslices);
+}
+
+// Store jet element data
+
+void JemSubBlockV1::fillJetElement(const int slice, const JemJetElement& jetEle)
+{
+  if (jetEle.data()) {
+    const int channel = jetEle.channel();
+    if (channel < m_channels) {
+      resize(m_jeData, m_channels);
+      m_jeData[index(slice)*m_channels + channel] = jetEle.data();
+    }
+  }
+}
+
+// Store jet hit counts
+
+void JemSubBlockV1::setJetHits(const int slice, const unsigned int hits)
+{
+  if (hits) {
+    const int jem = module();
+    const int sourceId = (jem == 0 || jem == 7 || jem == 8 || jem == 15)
+                         ? s_mainFwdThreshId : s_mainThreshId;
+    uint32_t word = 0;
+    word |= (hits           & s_threshMask)   << s_threshBit;
+    word |=  s_jetIndicator                   << s_jetIndicatorBit;
+    word |= (sourceId       & s_sourceIdMask) << s_sourceIdBit;
+    word |=  s_threshWordId                   << s_dataIdBit;
+    resize(m_jetHits);
+    m_jetHits[index(slice)] = word;
+  }
+}
+
+// Store energy subsum data
+
+void JemSubBlockV1::setEnergySubsums(const int slice, const unsigned int ex,
+                                     const unsigned int ey, const unsigned int et)
+{
+  uint32_t word = 0;
+  word |= (ex & s_exMask) << s_exBit;
+  word |= (ey & s_eyMask) << s_eyBit;
+  word |= (et & s_etMask) << s_etBit;
+  if (word) {
+    word |= s_sumIndicator << s_sumIndicatorBit;
+    word |= s_subsumId     << s_sourceIdBit;
+    word |= s_threshWordId << s_dataIdBit;
+    resize(m_energySubsums);
+    m_energySubsums[index(slice)] = word;
+  }
+}
+
+// Return jet element for given channel
+
+JemJetElement JemSubBlockV1::jetElement(const int slice, const int channel) const
+{
+  uint32_t je = 0;
+  if (slice >= 0 && slice < timeslices() &&
+      channel >= 0 && channel < m_channels && !m_jeData.empty()) {
+    je = m_jeData[index(slice)*m_channels + channel];
+  }
+  return JemJetElement(je);
+}
+
+// Return jet hit counts
+
+unsigned int JemSubBlockV1::jetHits(const int slice) const
+{
+  unsigned int hits = 0;
+  if (slice >= 0 && slice < timeslices() && !m_jetHits.empty()) {
+    hits = (m_jetHits[index(slice)] >> s_threshBit) & s_threshMask;
+  }
+  return hits;
+}
+
+// Return energy subsum Ex
+
+unsigned int JemSubBlockV1::ex(const int slice) const
+{
+  unsigned int ex = 0;
+  if (slice >= 0 && slice < timeslices() && !m_energySubsums.empty()) {
+    ex = (m_energySubsums[index(slice)] >> s_exBit) & s_exMask;
+  }
+  return ex;
+}
+
+// Return energy subsum Ey
+
+unsigned int JemSubBlockV1::ey(const int slice) const
+{
+  unsigned int ey = 0;
+  if (slice >= 0 && slice < timeslices() && !m_energySubsums.empty()) {
+    ey = (m_energySubsums[index(slice)] >> s_eyBit) & s_eyMask;
+  }
+  return ey;
+}
+
+// Return energy subsum Et
+
+unsigned int JemSubBlockV1::et(const int slice) const
+{
+  unsigned int et = 0;
+  if (slice >= 0 && slice < timeslices() && !m_energySubsums.empty()) {
+    et = (m_energySubsums[index(slice)] >> s_etBit) & s_etMask;
+  }
+  return et;
+}
+
+// Return number of timeslices
+
+int JemSubBlockV1::timeslices() const
+{
+  int slices = slices1();
+  if (slices == 0 && format() == NEUTRAL) {
+    slices = dataWords() / s_glinkBitsPerSlice;
+  }
+  if (slices == 0) slices = 1;
+  return slices;
+}
+
+// Packing/Unpacking routines
+
+bool JemSubBlockV1::pack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = packNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = packUncompressed();
+	  break;
+        default:
+	  break;
+      }
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
+
+bool JemSubBlockV1::unpack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = unpackNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = unpackUncompressed();
+	  break;
+        default:
+	  setUnpackErrorCode(UNPACK_FORMAT);
+	  break;
+      }
+      break;
+    default:
+      setUnpackErrorCode(UNPACK_VERSION);
+      break;
+  }
+  return rc;
+}
+
+// Return data index appropriate to format
+
+int JemSubBlockV1::index(const int slice) const
+{
+  return (format() == NEUTRAL) ? slice : 0;
+}
+
+// Resize a data vector according to format
+
+void JemSubBlockV1::resize(std::vector<uint32_t>& vec, const int channels)
+{
+  if (vec.empty()) {
+    int size = channels;
+    if (format() == NEUTRAL) size *= timeslices();
+    vec.resize(size);
+  }
+}
+
+// Pack neutral data
+
+bool JemSubBlockV1::packNeutral()
+{
+  resize(m_jeData, m_channels);
+  resize(m_jetHits);
+  resize(m_energySubsums);
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    // Jet element data
+    for (int channel = 0; channel < m_channels; ++channel) {
+      const int pin = channel / s_pairsPerPin;
+      const JemJetElement je = jetElement(slice, channel);
+      packerNeutral(pin, je.emData(), s_jetElementBits);
+      packerNeutral(pin, je.emParity(), 1);
+      packerNeutral(pin, je.linkError(), 1);
+      packerNeutral(pin, je.hadData(), s_jetElementBits);
+      packerNeutral(pin, je.hadParity(), 1);
+      packerNeutral(pin, (je.linkError() >> 1), 1);
+    }
+    // Pad out last jet element pin
+    int lastpin = (m_channels - 1) / s_pairsPerPin;
+    packerNeutral(lastpin, 0, s_jePaddingBits);
+    // Jet Hits and Energy Sums with parity bits
+    ++lastpin;
+    packerNeutral(lastpin, jetHits(slice), s_jetHitsBits);
+    packerNeutral(lastpin, parityBit(1, jetHits(slice), s_jetHitsBits), 1);
+    packerNeutral(lastpin, ex(slice), s_energyBits);
+    packerNeutral(lastpin, ey(slice), s_energyBits);
+    packerNeutral(lastpin, et(slice), s_energyBits);
+    int parity =  parityBit(1, ex(slice), s_energyBits);
+    parity = parityBit(parity, ey(slice), s_energyBits);
+    parity = parityBit(parity, et(slice), s_energyBits);
+    packerNeutral(lastpin, parity, 1);
+    // Bunch Crossing number and padding
+    packerNeutral(lastpin, bunchCrossing(), s_bunchCrossingBits);
+    packerNeutral(lastpin, 0, s_hitPaddingBits);
+    // G-Link parity
+    for (int pin = 0; pin <= lastpin; ++pin) packerNeutralParity(pin);
+  }
+  return true;
+}
+
+// Pack uncompressed data
+
+bool JemSubBlockV1::packUncompressed()
+{
+  // Jet element data
+  std::vector<uint32_t>::const_iterator pos;
+  for (pos = m_jeData.begin(); pos != m_jeData.end(); ++pos) {
+    if (*pos) packer(*pos, s_wordLength);
+  }
+        
+  // Hits and Subsum data
+  if ( !m_jetHits.empty() && m_jetHits[0]) packer(m_jetHits[0], s_wordLength);
+  if ( !m_energySubsums.empty() && m_energySubsums[0]) {
+    packer(m_energySubsums[0], s_wordLength);
+  }
+  packerFlush();
+  return true;
+}
+
+// Unpack neutral data
+
+bool JemSubBlockV1::unpackNeutral()
+{
+  resize(m_jeData, m_channels);
+  resize(m_jetHits);
+  resize(m_energySubsums);
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    // Jet element data
+    for (int channel = 0; channel < m_channels; ++channel) {
+      const int pin = channel / s_pairsPerPin;
+      const int emData     = unpackerNeutral(pin, s_jetElementBits);
+      const int emParity   = unpackerNeutral(pin, 1);
+            int linkError  = unpackerNeutral(pin, 1);
+      const int hadData    = unpackerNeutral(pin, s_jetElementBits);
+      const int hadParity  = unpackerNeutral(pin, 1);
+                linkError |= unpackerNeutral(pin, 1) << 1;
+      const JemJetElement je(channel, emData, hadData, emParity,
+                                              hadParity, linkError);
+      fillJetElement(slice, je);
+    }
+    // Padding from last jet element pin
+    int lastpin = (m_channels - 1) / s_pairsPerPin;
+    unpackerNeutral(lastpin, s_jePaddingBits);
+    // Jet Hits and Energy Sums
+    ++lastpin;
+    setJetHits(slice, unpackerNeutral(lastpin, s_jetHitsBits));
+    unpackerNeutral(lastpin, 1); // parity bit
+    const unsigned int ex = unpackerNeutral(lastpin, s_energyBits);
+    const unsigned int ey = unpackerNeutral(lastpin, s_energyBits);
+    const unsigned int et = unpackerNeutral(lastpin, s_energyBits);
+    setEnergySubsums(slice, ex, ey, et);
+    unpackerNeutral(lastpin, 1); // parity bit
+    // Bunch Crossing number and padding
+    setBunchCrossing(unpackerNeutral(lastpin, s_bunchCrossingBits));
+    unpackerNeutral(lastpin, s_hitPaddingBits);
+    // G-Link parity errors
+    for (int pin = 0; pin <= lastpin; ++pin) unpackerNeutralParityError(pin);
+  }
+  const bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  return rc;
+}
+
+// Unpack uncompressed data
+
+bool JemSubBlockV1::unpackUncompressed()
+{
+  resize(m_jeData, m_channels);
+  resize(m_jetHits);
+  resize(m_energySubsums);
+  unpackerInit();
+  uint32_t word = unpacker(s_wordLength);
+  while (unpackerSuccess()) {
+    const int id = dataId(word);
+    bool err = false;
+    // Jet element data
+    if (id == s_jeWordId) {
+      const JemJetElement jetEle(word);
+      const int channel = jetEle.channel();
+      if (channel < m_channels && m_jeData[channel] == 0) {
+        m_jeData[channel] = word;
+      } else err = true;
+    // Other data
+    } else {
+      switch (sourceId(word)) {
+	// Jet hit counts/thresholds
+        case s_mainThreshId:
+        case s_mainFwdThreshId: {
+	  if (m_jetHits[0] == 0) m_jetHits[0] = word;
+	  else err = true;
+          break;
+	}
+	// Energy subsums
+        case s_subsumId: {
+	  if (m_energySubsums[0] == 0) m_energySubsums[0] = word;
+	  else err = true;
+	  break;
+	}
+        default:
+	  err = true;
+	  break;
+      }
+    }
+    if (err) {
+      setUnpackErrorCode(UNPACK_SOURCE_ID);
+      return false;
+    }
+    word = unpacker(s_wordLength);
+  }
+  return true;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JemSubBlockV1.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemSubBlockV1.h
new file mode 100755
index 0000000000000000000000000000000000000000..22c8f2341c4d89e2457116a7b95090cedda33d4d
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemSubBlockV1.h
@@ -0,0 +1,139 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEMSUBBLOCKV1_H
+#define TRIGT1CALOBYTESTREAM_JEMSUBBLOCKV1_H
+
+#include <stdint.h>
+#include <vector>
+
+#include "L1CaloSubBlock.h"
+
+namespace LVL1BS {
+
+class JemJetElement;
+
+/** Sub-Block class for JEM data pre-LS1.
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version 1.06d
+ *
+ *  @author Peter Faulkner
+ */
+
+class JemSubBlockV1 : public L1CaloSubBlock {
+
+ public:
+   JemSubBlockV1();
+   ~JemSubBlockV1();
+
+   /// Clear all data
+   void clear();
+
+   /// Store JEM header
+   void setJemHeader(int version, int format, int slice, int crate,
+                     int module, int timeslices);
+   /// Store jet element data
+   void fillJetElement(int slice, const JemJetElement& jetEle);
+   /// Store jet hit counts
+   void setJetHits(int slice, unsigned int hits);
+   /// Store energy subsum data
+   void setEnergySubsums(int slice, unsigned int ex,
+	                 unsigned int ey, unsigned int et);
+
+   /// Return jet element for given channel
+   JemJetElement jetElement(int slice, int channel) const;
+   /// Return jet hit counts
+   unsigned int jetHits(int slice) const;
+   /// Return energy subsum Ex
+   unsigned int ex(int slice)      const;
+   /// Return energy subsum Ey
+   unsigned int ey(int slice)      const;
+   /// Return energy subsum Et
+   unsigned int et(int slice)      const;
+   /// Return number of timeslices
+   int  timeslices()               const;
+
+   /// Pack data
+   bool pack();
+   /// Unpack data
+   bool unpack();
+
+ private:
+   /// JEM header word ID
+   static const int      s_wordIdVal       = 0xc;
+   /// Data word length
+   static const int      s_wordLength      = 32;
+   //  Jet Element data word bit positions and masks
+   static const int      s_dataIdBit       = 30;
+   static const int      s_jeWordId        = 0x1;
+   static const uint32_t s_dataIdMask      = 0x3;
+   //  Jet hit counts bit positions and masks
+   static const int      s_threshBit       = 0;
+   static const int      s_sourceIdBit     = 25;
+   static const int      s_jetIndicatorBit = 24;
+   static const int      s_jetIndicator    = 0x0;
+   static const int      s_mainThreshId    = 20;
+   static const int      s_mainFwdThreshId = 21;
+   static const int      s_threshWordId    = 0x2;
+   static const uint32_t s_threshMask      = 0xffffff;
+   static const uint32_t s_sourceIdMask    = 0x1f;
+   //  Energy subsum data bit positions and masks
+   static const int      s_exBit           = 0;
+   static const int      s_eyBit           = 8;
+   static const int      s_etBit           = 16;
+   static const int      s_sumIndicatorBit = 24;
+   static const int      s_sumIndicator    = 0x1;
+   static const int      s_subsumId        = 22;
+   static const uint32_t s_exMask          = 0xff;
+   static const uint32_t s_eyMask          = 0xff;
+   static const uint32_t s_etMask          = 0xff;
+   //  Neutral format data lengths
+   static const int      s_pairsPerPin       = 3;
+   static const int      s_jetElementBits    = 9;
+   static const int      s_jePaddingBits     = 22;
+   static const int      s_jetHitsBits       = 24;
+   static const int      s_energyBits        = 8;
+   static const int      s_bunchCrossingBits = 12;
+   static const int      s_hitPaddingBits    = 4;
+   static const int      s_glinkBitsPerSlice = 67;
+
+   int sourceId(uint32_t word) const;
+   int dataId(uint32_t word)   const;
+   int index(int slice)        const;
+   void resize(std::vector<uint32_t>& vec, int channels = 1);
+
+   /// Pack neutral data
+   bool packNeutral();
+   /// Pack uncompressed data
+   bool packUncompressed();
+   /// Unpack neutral data
+   bool unpackNeutral();
+   /// Unpack uncompressed data
+   bool unpackUncompressed();
+
+   /// Jet element data
+   std::vector<uint32_t> m_jeData;
+   /// Jet hit counts
+   std::vector<uint32_t> m_jetHits;
+   /// Energy subsum data
+   std::vector<uint32_t> m_energySubsums;
+   /// Number of jet element channels
+   int m_channels;
+
+};
+
+inline int JemSubBlockV1::sourceId(const uint32_t word) const
+{
+  return (word >> s_sourceIdBit) & s_sourceIdMask;
+}
+
+inline int JemSubBlockV1::dataId(const uint32_t word) const
+{
+  return (word >> s_dataIdBit) & s_dataIdMask;
+}
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JemSubBlockV2.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemSubBlockV2.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..5531730266a22e1daa74970f04dcd5eadf4b0844
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemSubBlockV2.cxx
@@ -0,0 +1,371 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "JemJetElement.h"
+#include "JemSubBlockV2.h"
+
+namespace LVL1BS {
+
+// Constant definitions
+
+const int      JemSubBlockV2::s_wordIdVal;
+
+const int      JemSubBlockV2::s_wordLength;
+
+const int      JemSubBlockV2::s_dataIdBit;
+const int      JemSubBlockV2::s_jeWordId;
+const uint32_t JemSubBlockV2::s_dataIdMask;
+
+const int      JemSubBlockV2::s_energyWordId;
+const int      JemSubBlockV2::s_sourceIdBit;
+const uint32_t JemSubBlockV2::s_sourceIdMask;
+const int      JemSubBlockV2::s_etId;
+const int      JemSubBlockV2::s_exEyId;
+const int      JemSubBlockV2::s_exBit;
+const int      JemSubBlockV2::s_eyBit;
+const int      JemSubBlockV2::s_etBit;
+const uint32_t JemSubBlockV2::s_exMask;
+const uint32_t JemSubBlockV2::s_eyMask;
+const uint32_t JemSubBlockV2::s_etMask;
+
+const int      JemSubBlockV2::s_pairsPerPin;
+const int      JemSubBlockV2::s_jetElementBits;
+const int      JemSubBlockV2::s_jePaddingBits;
+const int      JemSubBlockV2::s_energyBits;
+const int      JemSubBlockV2::s_bunchCrossingBits;
+const int      JemSubBlockV2::s_energyPaddingBits;
+const int      JemSubBlockV2::s_glinkBitsPerSlice;
+
+
+JemSubBlockV2::JemSubBlockV2() : m_channels(44), m_energyWords(2)
+{
+}
+
+JemSubBlockV2::~JemSubBlockV2()
+{
+}
+
+// Clear all data
+
+void JemSubBlockV2::clear()
+{
+  L1CaloSubBlock::clear();
+  m_jeData.clear();
+  m_energySubsums.clear();
+}
+
+// Store JEM header
+
+void JemSubBlockV2::setJemHeader(const int version, const int format,
+                                 const int slice, const int crate,
+                                 const int module, const int timeslices)
+{
+  setHeader(s_wordIdVal, version, format, slice, crate, module, 0, timeslices);
+}
+
+// Store jet element data
+
+void JemSubBlockV2::fillJetElement(const int slice, const JemJetElement& jetEle)
+{
+  if (jetEle.data()) {
+    const int channel = jetEle.channel();
+    if (channel < m_channels) {
+      resize(m_jeData, m_channels);
+      m_jeData[index(slice, m_channels) + channel] = jetEle.data();
+    }
+  }
+}
+
+// Store energy subsum data
+
+void JemSubBlockV2::setEnergySubsums(const int slice, const unsigned int ex,
+                                     const unsigned int ey, const unsigned int et)
+{
+  uint32_t word1 = 0;
+  uint32_t word2 = 0;
+  word1 |= (et & s_etMask) << s_etBit;
+  word2 |= (ex & s_exMask) << s_exBit;
+  word2 |= (ey & s_eyMask) << s_eyBit;
+  if (word1 || word2) {
+    resize(m_energySubsums, m_energyWords);
+    const int ix = index(slice, m_energyWords);
+    if (word1) {
+      word1 |= s_etId         << s_sourceIdBit;
+      word1 |= s_energyWordId << s_dataIdBit;
+      m_energySubsums[ix] = word1;
+    }
+    if (word2) {
+      word2 |= s_exEyId       << s_sourceIdBit;
+      word2 |= s_energyWordId << s_dataIdBit;
+      m_energySubsums[ix+1] = word2;
+    }
+  }
+}
+
+// Return jet element for given channel
+
+JemJetElement JemSubBlockV2::jetElement(const int slice, const int channel) const
+{
+  uint32_t je = 0;
+  if (slice >= 0 && slice < timeslices() &&
+      channel >= 0 && channel < m_channels && !m_jeData.empty()) {
+    je = m_jeData[index(slice, m_channels) + channel];
+  }
+  return JemJetElement(je);
+}
+
+// Return energy subsum Ex
+
+unsigned int JemSubBlockV2::ex(const int slice) const
+{
+  unsigned int ex = 0;
+  if (slice >= 0 && slice < timeslices() && !m_energySubsums.empty()) {
+    ex = (m_energySubsums[index(slice, m_energyWords)+1] >> s_exBit) & s_exMask;
+  }
+  return ex;
+}
+
+// Return energy subsum Ey
+
+unsigned int JemSubBlockV2::ey(const int slice) const
+{
+  unsigned int ey = 0;
+  if (slice >= 0 && slice < timeslices() && !m_energySubsums.empty()) {
+    ey = (m_energySubsums[index(slice, m_energyWords)+1] >> s_eyBit) & s_eyMask;
+  }
+  return ey;
+}
+
+// Return energy subsum Et
+
+unsigned int JemSubBlockV2::et(const int slice) const
+{
+  unsigned int et = 0;
+  if (slice >= 0 && slice < timeslices() && !m_energySubsums.empty()) {
+    et = (m_energySubsums[index(slice, m_energyWords)] >> s_etBit) & s_etMask;
+  }
+  return et;
+}
+
+// Return number of timeslices
+
+int JemSubBlockV2::timeslices() const
+{
+  int slices = slices1();
+  if (slices == 0 && format() == NEUTRAL) {
+    slices = dataWords() / s_glinkBitsPerSlice;
+  }
+  if (slices == 0) slices = 1;
+  return slices;
+}
+
+// Packing/Unpacking routines
+
+bool JemSubBlockV2::pack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 2:                                                    //<< CHECK
+      switch (format()) {
+        case NEUTRAL:
+	  rc = packNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = packUncompressed();
+	  break;
+        default:
+	  break;
+      }
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
+
+bool JemSubBlockV2::unpack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 2:                                                    //<< CHECK
+      switch (format()) {
+        case NEUTRAL:
+	  rc = unpackNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  rc = unpackUncompressed();
+	  break;
+        default:
+	  setUnpackErrorCode(UNPACK_FORMAT);
+	  break;
+      }
+      break;
+    default:
+      setUnpackErrorCode(UNPACK_VERSION);
+      break;
+  }
+  return rc;
+}
+
+// Return data index appropriate to format
+
+int JemSubBlockV2::index(const int slice, const int channels) const
+{
+  return (format() == NEUTRAL) ? slice*channels : 0;
+}
+
+// Resize a data vector according to format
+
+void JemSubBlockV2::resize(std::vector<uint32_t>& vec, const int channels)
+{
+  if (vec.empty()) {
+    int size = channels;
+    if (format() == NEUTRAL) size *= timeslices();
+    vec.resize(size);
+  }
+}
+
+// Pack neutral data
+
+bool JemSubBlockV2::packNeutral()
+{
+  resize(m_jeData, m_channels);
+  resize(m_energySubsums, m_energyWords);
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    // Jet element data
+    for (int channel = 0; channel < m_channels; ++channel) {
+      const int pin = channel / s_pairsPerPin;
+      const JemJetElement je = jetElement(slice, channel);
+      packerNeutral(pin, je.emData(), s_jetElementBits);
+      packerNeutral(pin, je.emParity(), 1);
+      packerNeutral(pin, je.linkError(), 1);
+      packerNeutral(pin, je.hadData(), s_jetElementBits);
+      packerNeutral(pin, je.hadParity(), 1);
+      packerNeutral(pin, (je.linkError() >> 1), 1);
+    }
+    // Pad out last jet element pin
+    int lastpin = (m_channels - 1) / s_pairsPerPin;
+    packerNeutral(lastpin, 0, s_jePaddingBits);
+    // Energy Sums
+    ++lastpin;
+    packerNeutral(lastpin, ex(slice), s_energyBits);
+    packerNeutral(lastpin, ey(slice), s_energyBits);
+    packerNeutral(lastpin, et(slice), s_energyBits);
+    // Bunch Crossing number and padding
+    packerNeutral(lastpin, bunchCrossing(), s_bunchCrossingBits);
+    packerNeutral(lastpin, 0, s_energyPaddingBits);
+    // G-Link parity
+    for (int pin = 0; pin <= lastpin; ++pin) packerNeutralParity(pin);
+  }
+  return true;
+}
+
+// Pack uncompressed data
+
+bool JemSubBlockV2::packUncompressed()
+{
+  // Jet element data
+  std::vector<uint32_t>::const_iterator pos;
+  for (pos = m_jeData.begin(); pos != m_jeData.end(); ++pos) {
+    if (*pos) packer(*pos, s_wordLength);
+  }
+        
+  // Subsum data
+  if ( !m_energySubsums.empty() ) {
+    if (m_energySubsums[0]) packer(m_energySubsums[0], s_wordLength);
+    if (m_energySubsums[1]) packer(m_energySubsums[1], s_wordLength);
+  }
+  packerFlush();
+  return true;
+}
+
+// Unpack neutral data
+
+bool JemSubBlockV2::unpackNeutral()
+{
+  resize(m_jeData, m_channels);
+  resize(m_energySubsums, m_energyWords);
+  const int slices = timeslices();
+  for (int slice = 0; slice < slices; ++slice) {
+    // Jet element data
+    for (int channel = 0; channel < m_channels; ++channel) {
+      const int pin = channel / s_pairsPerPin;
+      const int emData     = unpackerNeutral(pin, s_jetElementBits);
+      const int emParity   = unpackerNeutral(pin, 1);
+            int linkError  = unpackerNeutral(pin, 1);
+      const int hadData    = unpackerNeutral(pin, s_jetElementBits);
+      const int hadParity  = unpackerNeutral(pin, 1);
+                linkError |= unpackerNeutral(pin, 1) << 1;
+      const JemJetElement je(channel, emData, hadData, emParity,
+                                              hadParity, linkError);
+      fillJetElement(slice, je);
+    }
+    // Padding from last jet element pin
+    int lastpin = (m_channels - 1) / s_pairsPerPin;
+    unpackerNeutral(lastpin, s_jePaddingBits);
+    // Energy Sums
+    ++lastpin;
+    const unsigned int ex = unpackerNeutral(lastpin, s_energyBits);
+    const unsigned int ey = unpackerNeutral(lastpin, s_energyBits);
+    const unsigned int et = unpackerNeutral(lastpin, s_energyBits);
+    setEnergySubsums(slice, ex, ey, et);
+    // Bunch Crossing number and padding
+    setBunchCrossing(unpackerNeutral(lastpin, s_bunchCrossingBits));
+    unpackerNeutral(lastpin, s_energyPaddingBits);
+    // G-Link parity errors
+    for (int pin = 0; pin <= lastpin; ++pin) unpackerNeutralParityError(pin);
+  }
+  const bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  return rc;
+}
+
+// Unpack uncompressed data
+
+bool JemSubBlockV2::unpackUncompressed()
+{
+  resize(m_jeData, m_channels);
+  resize(m_energySubsums, m_energyWords);
+  unpackerInit();
+  uint32_t word = unpacker(s_wordLength);
+  while (unpackerSuccess()) {
+    const int id = dataId(word);
+    bool err = false;
+    // Jet element data
+    if (id == s_jeWordId) {
+      const JemJetElement jetEle(word);
+      const int channel = jetEle.channel();
+      if (channel < m_channels && m_jeData[channel] == 0) {
+        m_jeData[channel] = word;
+      } else err = true;
+    // Energy subsums
+    } else if (id == s_energyWordId) {
+      switch (sourceId(word)) {
+        case s_etId: {
+	  if (m_energySubsums[0] == 0) m_energySubsums[0] = word;
+	  else err = true;
+	  break;
+	}
+        case s_exEyId: {
+	  if (m_energySubsums[1] == 0) m_energySubsums[1] = word;
+	  else err = true;
+	  break;
+	}
+        default:
+	  err = true;
+	  break;
+      }
+    } else err = true;
+    if (err) {
+      setUnpackErrorCode(UNPACK_SOURCE_ID);
+      return false;
+    }
+    word = unpacker(s_wordLength);
+  }
+  return true;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JemSubBlockV2.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemSubBlockV2.h
new file mode 100755
index 0000000000000000000000000000000000000000..845e2f70897d8f25e2c3544744976180a74649c9
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JemSubBlockV2.h
@@ -0,0 +1,126 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEMSUBBLOCKV2_H
+#define TRIGT1CALOBYTESTREAM_JEMSUBBLOCKV2_H
+
+#include <stdint.h>
+#include <vector>
+
+#include "L1CaloSubBlock.h"
+
+namespace LVL1BS {
+
+class JemJetElement;
+
+/** Sub-Block class for JEM data post LS1.
+ *
+ *  Based on "ATLAS Level-1 Calorimeter Trigger Read-out Driver"
+ *           Version X.xxx                                          //<<== CHECK
+ *
+ *  @author Peter Faulkner
+ */
+
+class JemSubBlockV2 : public L1CaloSubBlock {
+
+ public:
+   JemSubBlockV2();
+   ~JemSubBlockV2();
+
+   /// Clear all data
+   void clear();
+
+   /// Store JEM header
+   void setJemHeader(int version, int format, int slice, int crate,
+                     int module, int timeslices);
+   /// Store jet element data
+   void fillJetElement(int slice, const JemJetElement& jetEle);
+   /// Store energy subsum data
+   void setEnergySubsums(int slice, unsigned int ex,
+	                 unsigned int ey, unsigned int et);
+
+   /// Return jet element for given channel
+   JemJetElement jetElement(int slice, int channel) const;
+   /// Return energy subsum Ex
+   unsigned int ex(int slice)      const;
+   /// Return energy subsum Ey
+   unsigned int ey(int slice)      const;
+   /// Return energy subsum Et
+   unsigned int et(int slice)      const;
+   /// Return number of timeslices
+   int  timeslices()               const;
+
+   /// Pack data
+   bool pack();
+   /// Unpack data
+   bool unpack();
+
+ private:
+   /// JEM header word ID
+   static const int      s_wordIdVal       = 0xc;
+   /// Data word length
+   static const int      s_wordLength      = 32;
+   //  Jet Element data word bit positions and masks
+   static const int      s_dataIdBit       = 30;
+   static const int      s_jeWordId        = 0x1;
+   static const uint32_t s_dataIdMask      = 0x3;
+   //  Energy subsum data bit positions and masks
+   static const int      s_energyWordId    = 0x2;
+   static const int      s_sourceIdBit     = 28;
+   static const uint32_t s_sourceIdMask    = 0x3;
+   static const int      s_etId            = 2;
+   static const int      s_exEyId          = 3;
+   static const int      s_exBit           = 0;
+   static const int      s_eyBit           = 14;
+   static const int      s_etBit           = 14;
+   static const uint32_t s_exMask          = 0x3fff;
+   static const uint32_t s_eyMask          = 0x3fff;
+   static const uint32_t s_etMask          = 0x3fff;
+   //  Neutral format data lengths
+   static const int      s_pairsPerPin       = 3;
+   static const int      s_jetElementBits    = 9;
+   static const int      s_jePaddingBits     = 22;
+   static const int      s_energyBits        = 14;
+   static const int      s_bunchCrossingBits = 12;
+   static const int      s_energyPaddingBits = 12;
+   static const int      s_glinkBitsPerSlice = 67;
+
+   int sourceId(uint32_t word)        const;
+   int dataId(uint32_t word)          const;
+   int index(int slice, int channels) const;
+   void resize(std::vector<uint32_t>& vec, int channels);
+
+   /// Pack neutral data
+   bool packNeutral();
+   /// Pack uncompressed data
+   bool packUncompressed();
+   /// Unpack neutral data
+   bool unpackNeutral();
+   /// Unpack uncompressed data
+   bool unpackUncompressed();
+
+   /// Jet element data
+   std::vector<uint32_t> m_jeData;
+   /// Energy subsum data
+   std::vector<uint32_t> m_energySubsums;
+   /// Number of jet element channels
+   int m_channels;
+   /// Number of energy data words
+   int m_energyWords;
+
+};
+
+inline int JemSubBlockV2::sourceId(const uint32_t word) const
+{
+  return (word >> s_sourceIdBit) & s_sourceIdMask;
+}
+
+inline int JemSubBlockV2::dataId(const uint32_t word) const
+{
+  return (word >> s_dataIdBit) & s_dataIdMask;
+}
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamCnv.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamCnv.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..90cf6a9b2ac1843478d30b69de091fe7b56b1939
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamCnv.cxx
@@ -0,0 +1,115 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IByteStreamEventAccess.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "DataModel/DataVector.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/IRegistry.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "TrigT1CaloEvent/JEPBSCollection.h"
+
+#include "JepByteStreamCnv.h"
+#include "JepByteStreamTool.h"
+
+namespace LVL1BS {
+
+JepByteStreamCnv::JepByteStreamCnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("JepByteStreamCnv"),
+      m_tool("LVL1BS::JepByteStreamTool/JepByteStreamTool"),
+      m_ByteStreamEventAccess("ByteStreamCnvSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+JepByteStreamCnv::~JepByteStreamCnv()
+{
+}
+
+// CLID
+
+const CLID& JepByteStreamCnv::classID()
+{
+  return ClassID_traits<LVL1::JEPBSCollection>::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode JepByteStreamCnv::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << " - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  //Get ByteStreamCnvSvc
+  sc = m_ByteStreamEventAccess.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_ByteStreamEventAccess << endreq;
+    return sc;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_ByteStreamEventAccess << endreq;
+  }
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return StatusCode::FAILURE;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  return StatusCode::SUCCESS;
+}
+
+// createRep should create the bytestream from RDOs.
+
+StatusCode JepByteStreamCnv::createRep( DataObject* pObj,
+                                        IOpaqueAddress*& pAddr )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createRep() called" << endreq;
+
+  RawEventWrite* re = m_ByteStreamEventAccess->getRawEvent();
+
+  LVL1::JEPBSCollection* jep = 0;
+  if( !SG::fromStorable( pObj, jep ) ) {
+    m_log << MSG::ERROR << " Cannot cast to JEPBSCollection" << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = pObj->registry()->name();
+
+  ByteStreamAddress* addr = new ByteStreamAddress( classID(), nm, "" );
+
+  pAddr = addr;
+
+  // Convert to ByteStream
+  return m_tool->convert( jep, re );
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamCnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamCnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..36b4009d76208f548082464572acffd50868009d
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamCnv.h
@@ -0,0 +1,76 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEPBYTESTREAMCNV_H
+#define TRIGT1CALOBYTESTREAM_JEPBYTESTREAMCNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IByteStreamEventAccess;
+class IOpaqueAddress;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class JepByteStreamTool;
+
+/** ByteStream converter for JEP container
+ *
+ *  @author Peter Faulkner
+ */
+
+class JepByteStreamCnv: public Converter {
+
+  friend class CnvFactory<JepByteStreamCnv>;
+
+protected:
+
+  JepByteStreamCnv(ISvcLocator* svcloc);
+
+public:
+
+  ~JepByteStreamCnv();
+
+  virtual StatusCode initialize();
+  /// Create ByteStream from JEP Container
+  virtual StatusCode createRep(DataObject* pObj, IOpaqueAddress*& pAddr);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::JepByteStreamTool> m_tool;
+
+  /// Service for writing bytestream
+  ServiceHandle<IByteStreamEventAccess> m_ByteStreamEventAccess;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamTool.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamTool.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..d25542f6b8bbbae0965a085d01e9a10e73cb759a
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamTool.cxx
@@ -0,0 +1,1729 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <numeric>
+#include <set>
+#include <utility>
+
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "ByteStreamCnvSvcBase/FullEventAssembler.h"
+
+#include "TrigT1CaloEvent/CMMJetHits.h"
+#include "TrigT1CaloEvent/CMMEtSums.h"
+#include "TrigT1CaloEvent/JEMHits.h"
+#include "TrigT1CaloEvent/JEMEtSums.h"
+#include "TrigT1CaloEvent/JEPBSCollection.h"
+#include "TrigT1CaloEvent/JetElement.h"
+#include "TrigT1CaloUtils/DataError.h"
+#include "TrigT1CaloUtils/JetElementKey.h"
+#include "TrigT1CaloMappingToolInterfaces/IL1CaloMappingTool.h"
+
+#include "CmmEnergySubBlock.h"
+#include "CmmJetSubBlock.h"
+#include "CmmSubBlock.h"
+#include "JemJetElement.h"
+#include "JemSubBlock.h"
+#include "L1CaloErrorByteStreamTool.h"
+#include "L1CaloSrcIdMap.h"
+#include "L1CaloSubBlock.h"
+#include "L1CaloUserHeader.h"
+#include "ModifySlices.h"
+
+#include "JepByteStreamTool.h"
+
+namespace LVL1BS {
+
+// Interface ID
+
+static const InterfaceID IID_IJepByteStreamTool("JepByteStreamTool", 1, 1);
+
+const InterfaceID& JepByteStreamTool::interfaceID()
+{
+  return IID_IJepByteStreamTool;
+}
+
+// Constructor
+
+JepByteStreamTool::JepByteStreamTool(const std::string& type,
+                                     const std::string& name,
+				     const IInterface*  parent)
+  : AthAlgTool(type, name, parent),
+    m_jemMaps("LVL1::JemMappingTool/JemMappingTool"),
+    m_errorTool("LVL1BS::L1CaloErrorByteStreamTool/L1CaloErrorByteStreamTool"),
+    m_channels(44), m_crates(2), m_modules(16), m_coreOverlap(0),
+    m_subDetector(eformat::TDAQ_CALO_JET_PROC_DAQ),
+    m_srcIdMap(0), m_elementKey(0),
+    m_jemSubBlock(0), m_cmmEnergySubBlock(0), m_cmmJetSubBlock(0),
+    m_rodStatus(0), m_fea(0)
+{
+  declareInterface<JepByteStreamTool>(this);
+
+  declareProperty("JemMappingTool", m_jemMaps,
+                  "Crate/Module/Channel to Eta/Phi/Layer mapping tool");
+
+  declareProperty("CrateOffsetHw",  m_crateOffsetHw = 12,
+                  "Offset of JEP crate numbers in bytestream");
+  declareProperty("CrateOffsetSw",  m_crateOffsetSw = 0,
+                  "Offset of JEP crate numbers in RDOs");
+  declareProperty("SlinksPerCrate", m_slinks        = 4,
+                  "The number of S-Links per crate");
+
+  // Properties for reading bytestream only
+  declareProperty("ROBSourceIDs",       m_sourceIDs,
+                  "ROB fragment source identifiers");
+
+  // Properties for writing bytestream only
+  declareProperty("DataVersion",    m_version     = 1,
+                  "Format version number in sub-block header");
+  declareProperty("DataFormat",     m_dataFormat  = 1,
+                  "Format identifier (0-1) in sub-block header");
+  declareProperty("SimulSlices",    m_dfltSlices  = 1,
+                  "The number of slices in the simulation");
+  declareProperty("ForceSlices",    m_forceSlices = 0,
+                  "If >0, the number of slices in bytestream");
+
+}
+
+// Destructor
+
+JepByteStreamTool::~JepByteStreamTool()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode JepByteStreamTool::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = m_jemMaps.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_jemMaps << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_jemMaps << endreq;
+
+  sc = m_errorTool.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_errorTool << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_errorTool << endreq;
+
+  m_srcIdMap          = new L1CaloSrcIdMap();
+  m_elementKey        = new LVL1::JetElementKey();
+  m_jemSubBlock       = new JemSubBlock();
+  m_cmmEnergySubBlock = new CmmEnergySubBlock();
+  m_cmmJetSubBlock    = new CmmJetSubBlock();
+  m_rodStatus         = new std::vector<uint32_t>(2);
+  m_fea               = new FullEventAssembler<L1CaloSrcIdMap>();
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode JepByteStreamTool::finalize()
+{
+  delete m_fea;
+  delete m_rodStatus;
+  delete m_cmmJetSubBlock;
+  delete m_cmmEnergySubBlock;
+  delete m_jemSubBlock;
+  delete m_elementKey;
+  delete m_srcIdMap;
+  return StatusCode::SUCCESS;
+}
+
+// Conversion bytestream to jet elements
+
+StatusCode JepByteStreamTool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::JetElement>* const jeCollection)
+{
+  m_jeCollection = jeCollection;
+  m_jeMap.clear();
+  return convertBs(robFrags, JET_ELEMENTS);
+}
+
+// Conversion bytestream to jet hits
+
+StatusCode JepByteStreamTool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::JEMHits>* const hitCollection)
+{
+  m_hitCollection = hitCollection;
+  m_hitsMap.clear();
+  return convertBs(robFrags, JET_HITS);
+}
+
+// Conversion bytestream to energy sums
+
+StatusCode JepByteStreamTool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::JEMEtSums>* const etCollection)
+{
+  m_etCollection = etCollection;
+  m_etMap.clear();
+  return convertBs(robFrags, ENERGY_SUMS);
+}
+
+// Conversion bytestream to CMM hits
+
+StatusCode JepByteStreamTool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::CMMJetHits>* const hitCollection)
+{
+  m_cmmHitCollection = hitCollection;
+  m_cmmHitsMap.clear();
+  return convertBs(robFrags, CMM_HITS);
+}
+
+// Conversion bytestream to CMM energy sums
+
+StatusCode JepByteStreamTool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::CMMEtSums>* const etCollection)
+{
+  m_cmmEtCollection = etCollection;
+  m_cmmEtMap.clear();
+  return convertBs(robFrags, CMM_SUMS);
+}
+
+// Conversion of JEP container to bytestream
+
+StatusCode JepByteStreamTool::convert(const LVL1::JEPBSCollection* const jep,
+                                            RawEventWrite* const re)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Clear the event assembler
+
+  m_fea->clear();
+  const uint16_t minorVersion = m_srcIdMap->minorVersion();
+  m_fea->setRodMinorVersion(minorVersion);
+  m_rodStatusMap.clear();
+
+  // Pointer to ROD data vector
+
+  FullEventAssembler<L1CaloSrcIdMap>::RODDATA* theROD = 0;
+
+  // Set up the container maps
+
+  setupJeMap(jep->JetElements());
+  setupHitsMap(jep->JetHits());
+  setupEtMap(jep->EnergySums());
+  setupCmmHitsMap(jep->CmmHits());
+  setupCmmEtMap(jep->CmmSums());
+
+  // Loop over data
+
+  const bool neutralFormat = m_dataFormat == L1CaloSubBlock::NEUTRAL;
+  const int modulesPerSlink = m_modules / m_slinks;
+  int timeslices       = 1;
+  int trigJem          = 0;
+  int timeslicesNew    = 1;
+  int trigJemNew       = 0;
+  for (int crate=0; crate < m_crates; ++crate) {
+    const int hwCrate = crate + m_crateOffsetHw;
+
+    for (int module=0; module < m_modules; ++module) {
+
+      // Pack required number of modules per slink
+
+      if (module%modulesPerSlink == 0) {
+	const int daqOrRoi = 0;
+	const int slink = module/modulesPerSlink;
+        if (debug) {
+          msg() << "Treating crate " << hwCrate
+                << " slink " << slink << endreq;
+        }
+	// Get number of JEM slices and triggered slice offset
+	// for this slink
+	if ( ! slinkSlices(crate, module, modulesPerSlink,
+	                                  timeslices, trigJem)) {
+	  msg(MSG::ERROR) << "Inconsistent number of slices or "
+	                  << "triggered slice offsets in data for crate "
+	                  << hwCrate << " slink " << slink << endreq;
+	  return StatusCode::FAILURE;
+        }
+	timeslicesNew = (m_forceSlices) ? m_forceSlices : timeslices;
+	trigJemNew    = ModifySlices::peak(trigJem, timeslices, timeslicesNew);
+        if (debug) {
+	  msg() << "Data Version/Format: " << m_version
+	        << " " << m_dataFormat << endreq
+                << "Slices/offset: " << timeslices << " " << trigJem;
+	  if (timeslices != timeslicesNew) {
+	    msg() << " modified to " << timeslicesNew << " " << trigJemNew;
+          }
+	  msg() << endreq;
+        }
+        L1CaloUserHeader userHeader;
+        userHeader.setJem(trigJemNew);
+	const uint32_t rodIdJem = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+	                                                        m_subDetector);
+	theROD = m_fea->getRodData(rodIdJem);
+	theROD->push_back(userHeader.header());
+	m_rodStatusMap.insert(make_pair(rodIdJem, m_rodStatus));
+      }
+      if (debug) msg() << "Module " << module << endreq;
+
+      // Create a sub-block for each slice (except Neutral format)
+
+      m_jemBlocks.clear();
+      for (int slice = 0; slice < timeslicesNew; ++slice) {
+        JemSubBlock* const subBlock = new JemSubBlock();
+	subBlock->setJemHeader(m_version, m_dataFormat, slice,
+	                       hwCrate, module, timeslicesNew);
+        m_jemBlocks.push_back(subBlock);
+	if (neutralFormat) break;
+      }
+
+      // Find jet elements corresponding to each eta/phi pair and fill
+      // sub-blocks
+
+      for (int chan=0; chan < m_channels; ++chan) {
+	double eta = 0.;
+	double phi = 0.;
+	int layer = 0;
+	if (m_jemMaps->mapping(crate, module, chan, eta, phi, layer)) {
+          const LVL1::JetElement* const je = findJetElement(eta, phi);
+	  if (je ) {
+	    std::vector<int> emData;
+	    std::vector<int> hadData;
+	    std::vector<int> emErrors;
+	    std::vector<int> hadErrors;
+	    ModifySlices::data(je->emEnergyVec(),  emData,    timeslicesNew);
+	    ModifySlices::data(je->hadEnergyVec(), hadData,   timeslicesNew);
+	    ModifySlices::data(je->emErrorVec(),   emErrors,  timeslicesNew);
+	    ModifySlices::data(je->hadErrorVec(),  hadErrors, timeslicesNew);
+            for (int slice = 0; slice < timeslicesNew; ++slice) {
+	      const LVL1::DataError emErrBits(emErrors[slice]);
+	      const LVL1::DataError hadErrBits(hadErrors[slice]);
+	      const int index = ( neutralFormat ) ? 0 : slice;
+              JemSubBlock* const subBlock = m_jemBlocks[index];
+	      const JemJetElement jetEle(chan, emData[slice], hadData[slice],
+	                  emErrBits.get(LVL1::DataError::Parity),
+	                  hadErrBits.get(LVL1::DataError::Parity),
+			  emErrBits.get(LVL1::DataError::LinkDown) +
+			 (hadErrBits.get(LVL1::DataError::LinkDown) << 1));
+              subBlock->fillJetElement(slice, jetEle);
+	    }
+          }
+        }
+      }
+
+      // Add jet hits and energy subsums
+
+      const LVL1::JEMHits* const hits = findJetHits(crate, module);
+      if (hits) {
+        std::vector<unsigned int> vec;
+	ModifySlices::data(hits->JetHitsVec(), vec, timeslicesNew);
+        for (int slice = 0; slice < timeslicesNew; ++slice) {
+	  const int index = ( neutralFormat ) ? 0 : slice;
+	  JemSubBlock* const subBlock = m_jemBlocks[index];
+	  subBlock->setJetHits(slice, vec[slice]);
+        }
+      }
+      const LVL1::JEMEtSums* const et = findEnergySums(crate, module);
+      if (et) {
+        std::vector<unsigned int> exVec;
+        std::vector<unsigned int> eyVec;
+        std::vector<unsigned int> etVec;
+	ModifySlices::data(et->ExVec(), exVec, timeslicesNew);
+	ModifySlices::data(et->EyVec(), eyVec, timeslicesNew);
+	ModifySlices::data(et->EtVec(), etVec, timeslicesNew);
+	for (int slice = 0; slice < timeslicesNew; ++slice) {
+	  const int index = ( neutralFormat ) ? 0 : slice;
+	  JemSubBlock* const subBlock = m_jemBlocks[index];
+	  subBlock->setEnergySubsums(slice, exVec[slice], eyVec[slice],
+	                                                  etVec[slice]);
+        }
+      }
+      
+      // Pack and write the sub-blocks
+
+      DataVector<JemSubBlock>::const_iterator pos;
+      for (pos = m_jemBlocks.begin(); pos != m_jemBlocks.end(); ++pos) {
+        JemSubBlock* const subBlock = *pos;
+	if ( !subBlock->pack()) {
+	  msg(MSG::ERROR) << "JEM sub-block packing failed" << endreq;
+	  return StatusCode::FAILURE;
+	}
+	if (debug) {
+	  msg() << "JEM sub-block data words: "
+	        << subBlock->dataWords() << endreq;
+	}
+	subBlock->write(theROD);
+      }
+    }
+
+    // Append CMMs to last S-Link of the crate
+
+    // Create a sub-block for each slice (except Neutral format)
+
+    m_cmmEnergyBlocks.clear();
+    m_cmmJetBlocks.clear();
+    const int summing = (crate == m_crates - 1) ? CmmSubBlock::SYSTEM
+                                                : CmmSubBlock::CRATE;
+    for (int slice = 0; slice < timeslicesNew; ++slice) {
+      CmmEnergySubBlock* const enBlock = new CmmEnergySubBlock();
+      const int cmmEnergyVersion = 2; // with Missing-ET-Sig
+      enBlock->setCmmHeader(cmmEnergyVersion, m_dataFormat, slice, hwCrate,
+                            summing, CmmSubBlock::CMM_ENERGY,
+			    CmmSubBlock::LEFT, timeslicesNew);
+      m_cmmEnergyBlocks.push_back(enBlock);
+      CmmJetSubBlock* const jetBlock = new CmmJetSubBlock();
+      jetBlock->setCmmHeader(m_version, m_dataFormat, slice, hwCrate,
+                             summing, CmmSubBlock::CMM_JET,
+			     CmmSubBlock::RIGHT, timeslicesNew);
+      m_cmmJetBlocks.push_back(jetBlock);
+      if (neutralFormat) break;
+    }
+
+    // CMM-Energy
+
+    int maxDataID = static_cast<int>(LVL1::CMMEtSums::MAXID);
+    for (int dataID = 0; dataID < maxDataID; ++dataID) {
+      int source = dataID;
+      if (dataID >= m_modules) {
+        if (summing == CmmSubBlock::CRATE && 
+	    dataID != LVL1::CMMEtSums::LOCAL) continue;
+	switch (dataID) {
+	  case LVL1::CMMEtSums::LOCAL:
+	    source = CmmEnergySubBlock::LOCAL;
+	    break;
+	  case LVL1::CMMEtSums::REMOTE:
+	    source = CmmEnergySubBlock::REMOTE;
+	    break;
+	  case LVL1::CMMEtSums::TOTAL:
+	    source = CmmEnergySubBlock::TOTAL;
+	    break;
+	  case LVL1::CMMEtSums::MISSING_ET_MAP:
+	  case LVL1::CMMEtSums::SUM_ET_MAP:
+	  case LVL1::CMMEtSums::MISSING_ET_SIG_MAP:
+	    break;
+          default:
+	    continue;
+        }
+      }
+      const LVL1::CMMEtSums* const sums = findCmmSums(crate, dataID);
+      if ( sums ) {
+        std::vector<unsigned int> ex;
+        std::vector<unsigned int> ey;
+        std::vector<unsigned int> et;
+        std::vector<int> exErr;
+        std::vector<int> eyErr;
+        std::vector<int> etErr;
+	ModifySlices::data(sums->ExVec(), ex, timeslicesNew);
+	ModifySlices::data(sums->EyVec(), ey, timeslicesNew);
+	ModifySlices::data(sums->EtVec(), et, timeslicesNew);
+	ModifySlices::data(sums->ExErrorVec(), exErr, timeslicesNew);
+	ModifySlices::data(sums->EyErrorVec(), eyErr, timeslicesNew);
+	ModifySlices::data(sums->EtErrorVec(), etErr, timeslicesNew);
+	for (int slice = 0; slice < timeslicesNew; ++slice) {
+	  const LVL1::DataError exErrBits(exErr[slice]);
+	  const LVL1::DataError eyErrBits(eyErr[slice]);
+	  const LVL1::DataError etErrBits(etErr[slice]);
+	  int exError = exErrBits.get(LVL1::DataError::Parity);
+	  int eyError = eyErrBits.get(LVL1::DataError::Parity);
+	  int etError = etErrBits.get(LVL1::DataError::Parity);
+	  if (dataID == LVL1::CMMEtSums::LOCAL ||
+	      dataID == LVL1::CMMEtSums::REMOTE ||
+	      dataID == LVL1::CMMEtSums::TOTAL) {
+	    exError = (exError << 1) + exErrBits.get(LVL1::DataError::Overflow);
+	    eyError = (eyError << 1) + eyErrBits.get(LVL1::DataError::Overflow);
+	    etError = (etError << 1) + etErrBits.get(LVL1::DataError::Overflow);
+	  }
+	  const int index = ( neutralFormat ) ? 0 : slice;
+	  CmmEnergySubBlock* const subBlock = m_cmmEnergyBlocks[index];
+	  if (dataID == LVL1::CMMEtSums::MISSING_ET_MAP) {
+	    subBlock->setMissingEtHits(slice, et[slice]); 
+          } else if (dataID == LVL1::CMMEtSums::SUM_ET_MAP) {
+	    subBlock->setSumEtHits(slice, et[slice]); 
+	  } else if (dataID == LVL1::CMMEtSums::MISSING_ET_SIG_MAP) {
+	    subBlock->setMissingEtSigHits(slice, et[slice]);
+          } else {
+	    subBlock->setSubsums(slice, source,
+	                         ex[slice], ey[slice], et[slice],
+	                         exError, eyError, etError);
+          }
+        }
+      }
+    }
+    DataVector<CmmEnergySubBlock>::const_iterator pos;
+    pos = m_cmmEnergyBlocks.begin();
+    for (; pos != m_cmmEnergyBlocks.end(); ++pos) {
+      CmmEnergySubBlock* const subBlock = *pos;
+      if ( !subBlock->pack()) {
+        msg(MSG::ERROR) << "CMM-Energy sub-block packing failed" << endreq;
+	return StatusCode::FAILURE;
+      }
+      if (debug) {
+	msg() << "CMM-Energy sub-block data words: "
+	      << subBlock->dataWords() << endreq;
+      }
+      subBlock->write(theROD);
+    }
+
+    // CMM-Jet
+
+    maxDataID = static_cast<int>(LVL1::CMMJetHits::MAXID);
+    for (int dataID = 0; dataID < maxDataID; ++dataID) {
+      int source = dataID;
+      if (dataID >= m_modules) {
+        if (summing == CmmSubBlock::CRATE && 
+	    dataID != LVL1::CMMJetHits::LOCAL_MAIN &&
+	    dataID != LVL1::CMMJetHits::LOCAL_FORWARD) continue;
+	switch (dataID) {
+	  case LVL1::CMMJetHits::LOCAL_MAIN:
+	    source = CmmJetSubBlock::LOCAL_MAIN;
+	    break;
+	  case LVL1::CMMJetHits::REMOTE_MAIN:
+	    source = CmmJetSubBlock::REMOTE_MAIN;
+	    break;
+	  case LVL1::CMMJetHits::TOTAL_MAIN:
+	    source = CmmJetSubBlock::TOTAL_MAIN;
+	    break;
+	  case LVL1::CMMJetHits::LOCAL_FORWARD:
+	    source = CmmJetSubBlock::LOCAL_FORWARD;
+	    break;
+	  case LVL1::CMMJetHits::REMOTE_FORWARD:
+	    source = CmmJetSubBlock::REMOTE_FORWARD;
+	    break;
+	  case LVL1::CMMJetHits::TOTAL_FORWARD:
+	    source = CmmJetSubBlock::TOTAL_FORWARD;
+	    break;
+	  case LVL1::CMMJetHits::ET_MAP:
+	    break;
+          default:
+	    continue;
+        }
+      }
+      const LVL1::CMMJetHits* const ch = findCmmHits(crate, dataID);
+      if ( ch ) {
+        std::vector<unsigned int> hits;
+        std::vector<int> errs;
+	ModifySlices::data(ch->HitsVec(),  hits, timeslicesNew);
+	ModifySlices::data(ch->ErrorVec(), errs, timeslicesNew);
+	for (int slice = 0; slice < timeslicesNew; ++slice) {
+	  const LVL1::DataError errBits(errs[slice]);
+	  const int index = ( neutralFormat ) ? 0 : slice;
+	  CmmJetSubBlock* const subBlock = m_cmmJetBlocks[index];
+	  if (dataID == LVL1::CMMJetHits::ET_MAP) {
+	    subBlock->setJetEtMap(slice, hits[slice]);
+          } else {
+	    subBlock->setJetHits(slice, source, hits[slice],
+	                         errBits.get(LVL1::DataError::Parity));
+          }
+        }
+      }
+    }
+    DataVector<CmmJetSubBlock>::const_iterator jos;
+    jos = m_cmmJetBlocks.begin();
+    for (; jos != m_cmmJetBlocks.end(); ++jos) {
+      CmmJetSubBlock* const subBlock = *jos;
+      if ( !subBlock->pack()) {
+        msg(MSG::ERROR) << "CMM-Jet sub-block packing failed" << endreq;
+	return StatusCode::FAILURE;
+      }
+      if (debug) {
+	msg() << "CMM-Jet sub-block data words: "
+	      << subBlock->dataWords() << endreq;
+      }
+      subBlock->write(theROD);
+    }
+  }
+
+  // Fill the raw event
+
+  m_fea->fill(re, msg());
+
+  // Set ROD status words
+
+  //L1CaloRodStatus::setStatus(re, m_rodStatusMap, m_srcIdMap);
+
+  return StatusCode::SUCCESS;
+}
+
+// Return reference to vector with all possible Source Identifiers
+
+const std::vector<uint32_t>& JepByteStreamTool::sourceIDs(
+                                                const std::string& sgKey)
+{
+  // Check if overlap jet element channels wanted
+  const std::string flag("Overlap");
+  const std::string::size_type pos = sgKey.find(flag);
+  m_coreOverlap =
+   (pos == std::string::npos || pos != sgKey.length() - flag.length()) ? 0 : 1;
+
+  if (m_sourceIDs.empty()) {
+    const int maxCrates = m_crates + m_crateOffsetHw;
+    const int maxSlinks = m_srcIdMap->maxSlinks();
+    for (int hwCrate = m_crateOffsetHw; hwCrate < maxCrates; ++hwCrate) {
+      for (int slink = 0; slink < maxSlinks; ++slink) {
+        const int daqOrRoi = 0;
+        const uint32_t rodId = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+                                                             m_subDetector);
+        const uint32_t robId = m_srcIdMap->getRobID(rodId);
+        m_sourceIDs.push_back(robId);
+      }
+    }
+  }
+  return m_sourceIDs;
+}
+
+// Convert bytestream to given container type
+
+StatusCode JepByteStreamTool::convertBs(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            const CollectionType collection)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Loop over ROB fragments
+
+  int robCount = 0;
+  std::set<uint32_t> dupCheck;
+  ROBIterator rob    = robFrags.begin();
+  ROBIterator robEnd = robFrags.end();
+  for (; rob != robEnd; ++rob) {
+
+    if (debug) {
+      ++robCount;
+      msg() << "Treating ROB fragment " << robCount << endreq;
+    }
+
+    // Skip fragments with ROB status errors
+
+    uint32_t robid = (*rob)->source_id();
+    if ((*rob)->nstatus() > 0) {
+      ROBPointer robData;
+      (*rob)->status(robData);
+      if (*robData != 0) {
+        m_errorTool->robError(robid, *robData);
+	if (debug) msg() << "ROB status error - skipping fragment" << endreq;
+	continue;
+      }
+    }
+
+    // Skip duplicate fragments
+
+    if (!dupCheck.insert(robid).second) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_DUPLICATE_ROB);
+      if (debug) msg() << "Skipping duplicate ROB fragment" << endreq;
+      continue;
+    }
+
+    // Unpack ROD data (slinks)
+
+    RODPointer payloadBeg;
+    RODPointer payload;
+    RODPointer payloadEnd;
+    (*rob)->rod_data(payloadBeg);
+    payloadEnd = payloadBeg + (*rob)->rod_ndata();
+    payload = payloadBeg;
+    if (payload == payloadEnd) {
+      if (debug) msg() << "ROB fragment empty" << endreq;
+      continue;
+    }
+
+    // Check identifier
+    const uint32_t sourceID = (*rob)->rod_source_id();
+    if (m_srcIdMap->getRobID(sourceID) != robid           ||
+        m_srcIdMap->subDet(sourceID)   != m_subDetector   ||
+	m_srcIdMap->daqOrRoi(sourceID) != 0               ||
+	m_srcIdMap->slink(sourceID)    >= m_slinks        ||
+	m_srcIdMap->crate(sourceID)    <  m_crateOffsetHw ||
+	m_srcIdMap->crate(sourceID)    >= m_crateOffsetHw + m_crates) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_ROD_ID);
+      if (debug) {
+        msg() << "Wrong source identifier in data: ROD "
+              << MSG::hex << sourceID << "  ROB " << robid
+	      << MSG::dec << endreq;
+      }
+      continue;
+    }
+    const int rodCrate = m_srcIdMap->crate(sourceID);
+    if (debug) {
+      msg() << "Treating crate " << rodCrate 
+            << " slink " << m_srcIdMap->slink(sourceID) << endreq;
+    }
+
+    // First word should be User Header
+    if ( !L1CaloUserHeader::isValid(*payload) ) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
+      if (debug) msg() << "Invalid or missing user header" << endreq;
+      continue;
+    }
+    L1CaloUserHeader userHeader(*payload);
+    const int minorVersion = (*rob)->rod_version() & 0xffff;
+    userHeader.setVersion(minorVersion);
+    const int headerWords = userHeader.words();
+    if (headerWords != 1) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
+      if (debug) msg() << "Unexpected number of user header words: "
+                       << headerWords << endreq;
+      continue;
+    }
+    for (int i = 0; i < headerWords; ++i) ++payload;
+    // triggered slice offsets
+    int trigJem = userHeader.jem();
+    int trigCmm = userHeader.jepCmm();
+    if (debug) {
+      msg() << "Minor format version number: " << MSG::hex
+            << minorVersion << MSG::dec << endreq
+            << "JEM triggered slice offset: " << trigJem << endreq
+            << "CMM triggered slice offset: " << trigCmm << endreq;
+    }
+    if (trigJem != trigCmm) {
+      const int newTrig = (trigJem > trigCmm) ? trigJem : trigCmm;
+      trigJem = newTrig;
+      trigCmm = newTrig;
+      if (debug) msg() << "Changed both offsets to " << newTrig << endreq;
+    }
+
+    // Loop over sub-blocks
+
+    m_rodErr = L1CaloSubBlock::ERROR_NONE;
+    while (payload != payloadEnd) {
+      
+      if (L1CaloSubBlock::wordType(*payload) != L1CaloSubBlock::HEADER) {
+        if (debug) msg() << "Unexpected data sequence" << endreq;
+	m_rodErr = L1CaloSubBlock::ERROR_MISSING_HEADER;
+	break;
+      }
+      if (CmmSubBlock::cmmBlock(*payload)) {
+        // CMMs
+	if (CmmSubBlock::cmmType(*payload) == CmmSubBlock::CMM_JET) {
+	  m_cmmJetSubBlock->clear();
+          payload = m_cmmJetSubBlock->read(payload, payloadEnd);
+	  if (m_cmmJetSubBlock->crate() != rodCrate) {
+	    if (debug) msg() << "Inconsistent crate number in ROD source ID"
+	                     << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	    break;
+          }
+	  if (collection == CMM_HITS) {
+	    decodeCmmJet(m_cmmJetSubBlock, trigCmm);
+	    if (m_rodErr != L1CaloSubBlock::ERROR_NONE) {
+	      if (debug) msg() << "decodeCmmJet failed" << endreq;
+	      break;
+	    }
+          }
+        } else if (CmmSubBlock::cmmType(*payload) == CmmSubBlock::CMM_ENERGY) {
+	  m_cmmEnergySubBlock->clear();
+	  payload = m_cmmEnergySubBlock->read(payload, payloadEnd);
+	  if (m_cmmEnergySubBlock->crate() != rodCrate) {
+	    if (debug) msg() << "Inconsistent crate number in ROD source ID"
+	                     << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	    break;
+          }
+	  if (collection == CMM_SUMS) {
+	    decodeCmmEnergy(m_cmmEnergySubBlock, trigCmm);
+	    if (m_rodErr != L1CaloSubBlock::ERROR_NONE) {
+	      if (debug) msg() << "decodeCmmEnergy failed" << endreq;
+	      break;
+	    }
+          }
+	} else {
+	  if (debug) msg() << "Invalid CMM type in module field" << endreq;
+	  m_rodErr = L1CaloSubBlock::ERROR_MODULE_NUMBER;
+	  break;
+        }
+      } else {
+        // JEM
+	m_jemSubBlock->clear();
+        payload = m_jemSubBlock->read(payload, payloadEnd);
+	if (m_jemSubBlock->crate() != rodCrate) {
+	  if (debug) msg() << "Inconsistent crate number in ROD source ID"
+	                   << endreq;
+	  m_rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	  break;
+        }
+	if (collection == JET_ELEMENTS || collection == JET_HITS ||
+	                                  collection == ENERGY_SUMS) {
+	  decodeJem(m_jemSubBlock, trigJem, collection);
+	  if (m_rodErr != L1CaloSubBlock::ERROR_NONE) {
+	    if (debug) msg() << "decodeJem failed" << endreq;
+	    break;
+	  }
+        }
+      }
+    }
+    if (m_rodErr != L1CaloSubBlock::ERROR_NONE)
+                                       m_errorTool->rodError(robid, m_rodErr);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Unpack CMM-Energy sub-block
+
+void JepByteStreamTool::decodeCmmEnergy(CmmEnergySubBlock* subBlock,
+                                                                 int trigCmm)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  const int hwCrate    = subBlock->crate();
+  const int module     = subBlock->cmmPosition();
+  const int firmware   = subBlock->cmmFirmware();
+  const int summing    = subBlock->cmmSumming();
+  const int timeslices = subBlock->timeslices();
+  const int sliceNum   = subBlock->slice();
+  if (debug) {
+    msg() << "CMM-Energy: Crate " << hwCrate
+          << "  Module "          << module
+	  << "  Firmware "        << firmware
+	  << "  Summing "         << summing
+          << "  Total slices "    << timeslices
+          << "  Slice "           << sliceNum    << endreq;
+  }
+  if (timeslices <= trigCmm) {
+    if (debug) msg() << "Triggered CMM slice from header "
+                     << "inconsistent with number of slices: "
+                     << trigCmm << ", " << timeslices << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  if (timeslices <= sliceNum) {
+    if (debug) msg() << "Total slices inconsistent with slice number: "
+                     << timeslices << ", " << sliceNum << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  // Unpack sub-block
+  if (subBlock->dataWords() && !subBlock->unpack()) {
+    if (debug) {
+      std::string errMsg(subBlock->unpackErrorMsg());
+      msg() << "CMM-Energy sub-block unpacking failed: " << errMsg << endreq;
+    }
+    m_rodErr = subBlock->unpackErrorCode();
+    return;
+  }
+
+  // Retrieve required data
+
+  const bool neutralFormat = subBlock->format() == L1CaloSubBlock::NEUTRAL;
+  const int crate    = hwCrate - m_crateOffsetHw;
+  const int swCrate  = crate   + m_crateOffsetSw;
+  const int maxSid   = static_cast<int>(CmmEnergySubBlock::MAX_SOURCE_ID);
+  LVL1::DataError derr;
+  derr.set(LVL1::DataError::SubStatusWord, subBlock->subStatus());
+  const int ssError = derr.error();
+  const int sliceBeg = ( neutralFormat ) ? 0          : sliceNum;
+  const int sliceEnd = ( neutralFormat ) ? timeslices : sliceNum + 1;
+  for (int slice = sliceBeg; slice < sliceEnd; ++slice) {
+
+    // Energy sums
+
+    for (int source = 0; source < maxSid; ++source) {
+      int dataID = source;
+      if (source >= m_modules) {
+        if (summing == CmmSubBlock::CRATE &&
+	    source != CmmEnergySubBlock::LOCAL) continue;
+	switch (source) {
+	  case CmmEnergySubBlock::LOCAL:
+	    dataID = LVL1::CMMEtSums::LOCAL;
+	    break;
+          case CmmEnergySubBlock::REMOTE:
+	    dataID = LVL1::CMMEtSums::REMOTE;
+	    break;
+          case CmmEnergySubBlock::TOTAL:
+	    dataID = LVL1::CMMEtSums::TOTAL;
+	    break;
+          default:
+	    continue;
+        }
+      }
+      const unsigned int ex = subBlock->ex(slice, source);
+      const unsigned int ey = subBlock->ey(slice, source);
+      const unsigned int et = subBlock->et(slice, source);
+      int exErr = subBlock->exError(slice, source);
+      int eyErr = subBlock->eyError(slice, source);
+      int etErr = subBlock->etError(slice, source);
+      LVL1::DataError exErrBits(ssError);
+      LVL1::DataError eyErrBits(ssError);
+      LVL1::DataError etErrBits(ssError);
+      if (dataID == LVL1::CMMEtSums::LOCAL ||
+          dataID == LVL1::CMMEtSums::REMOTE ||
+	  dataID == LVL1::CMMEtSums::TOTAL) {
+        exErrBits.set(LVL1::DataError::Overflow, exErr);
+	exErrBits.set(LVL1::DataError::Parity,   exErr >> 1);
+        eyErrBits.set(LVL1::DataError::Overflow, eyErr);
+	eyErrBits.set(LVL1::DataError::Parity,   eyErr >> 1);
+        etErrBits.set(LVL1::DataError::Overflow, etErr);
+	etErrBits.set(LVL1::DataError::Parity,   etErr >> 1);
+      } else {
+        exErrBits.set(LVL1::DataError::Parity, exErr);
+        eyErrBits.set(LVL1::DataError::Parity, eyErr);
+        etErrBits.set(LVL1::DataError::Parity, etErr);
+      }
+      exErr = exErrBits.error();
+      eyErr = eyErrBits.error();
+      etErr = etErrBits.error();
+      if (ex || ey || et || exErr || eyErr || etErr) {
+        LVL1::CMMEtSums* sums = findCmmSums(crate, dataID);
+	if ( ! sums ) {   // create new CMM energy sums
+	  m_exVec.assign(timeslices, 0);
+	  m_eyVec.assign(timeslices, 0);
+	  m_etVec.assign(timeslices, 0);
+	  m_exErrVec.assign(timeslices, 0);
+	  m_eyErrVec.assign(timeslices, 0);
+	  m_etErrVec.assign(timeslices, 0);
+	  m_exVec[slice] = ex;
+	  m_eyVec[slice] = ey;
+	  m_etVec[slice] = et;
+	  m_exErrVec[slice] = exErr;
+	  m_eyErrVec[slice] = eyErr;
+	  m_etErrVec[slice] = etErr;
+	  sums = new LVL1::CMMEtSums(swCrate, dataID, m_etVec, m_exVec, m_eyVec,
+				     m_etErrVec, m_exErrVec, m_eyErrVec, trigCmm);
+          const int key = crate*100 + dataID;
+	  m_cmmEtMap.insert(std::make_pair(key, sums));
+	  m_cmmEtCollection->push_back(sums);
+        } else {
+	  m_exVec = sums->ExVec();
+	  m_eyVec = sums->EyVec();
+	  m_etVec = sums->EtVec();
+	  m_exErrVec = sums->ExErrorVec();
+	  m_eyErrVec = sums->EyErrorVec();
+	  m_etErrVec = sums->EtErrorVec();
+	  const int nsl = m_exVec.size();
+	  if (timeslices != nsl) {
+	    if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                     << endreq;
+            m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    return;
+          }
+	  if (m_exVec[slice] != 0 || m_eyVec[slice] != 0 || m_etVec[slice] != 0 ||
+	      m_exErrVec[slice] != 0 || m_eyErrVec[slice] != 0 ||
+              m_etErrVec[slice] != 0) {
+            if (debug) msg() << "Duplicate data for slice " << slice << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    return;
+          }
+	  m_exVec[slice] = ex;
+	  m_eyVec[slice] = ey;
+	  m_etVec[slice] = et;
+	  m_exErrVec[slice] = exErr;
+	  m_eyErrVec[slice] = eyErr;
+	  m_etErrVec[slice] = etErr;
+	  sums->addEx(m_exVec, m_exErrVec);
+	  sums->addEy(m_eyVec, m_eyErrVec);
+	  sums->addEt(m_etVec, m_etErrVec);
+        }
+      }
+    }
+
+    // Hit maps - store as Et
+
+    if (summing == CmmSubBlock::SYSTEM) {
+      const unsigned int missEt = subBlock->missingEtHits(slice);
+      if ( missEt || ssError ) {
+        const int dataID = LVL1::CMMEtSums::MISSING_ET_MAP;
+        LVL1::CMMEtSums* map = findCmmSums(crate, dataID);
+        if ( ! map ) {
+	  m_etVec.assign(timeslices, 0);
+	  m_etErrVec.assign(timeslices, 0);
+	  m_etVec[slice]    = missEt;
+	  m_etErrVec[slice] = ssError;
+	  map = new LVL1::CMMEtSums(swCrate, dataID,
+	                            m_etVec, m_etVec, m_etVec,
+	  			    m_etErrVec, m_etErrVec, m_etErrVec, trigCmm);
+          const int key = crate*100 + dataID;
+	  m_cmmEtMap.insert(std::make_pair(key, map));
+	  m_cmmEtCollection->push_back(map);
+        } else {
+          m_etVec    = map->EtVec();
+          m_etErrVec = map->EtErrorVec();
+	  const int nsl = m_etVec.size();
+	  if (timeslices != nsl) {
+	    if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                     << endreq;
+            m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    return;
+          }
+	  if (m_etVec[slice] != 0 || m_etErrVec[slice] != 0) {
+	    if (debug) msg() << "Duplicate data for slice " << slice << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    return;
+          }
+	  m_etVec[slice]     = missEt;
+	  m_etErrVec[slice]  = ssError;
+	  map->addEx(m_etVec, m_etErrVec);
+	  map->addEy(m_etVec, m_etErrVec);
+	  map->addEt(m_etVec, m_etErrVec);
+        }
+      }
+      const unsigned int sumEt = subBlock->sumEtHits(slice);
+      if ( sumEt || ssError ) {
+        const int dataID = LVL1::CMMEtSums::SUM_ET_MAP;
+        LVL1::CMMEtSums* map = findCmmSums(crate, dataID);
+        if ( ! map ) {
+          m_etVec.assign(timeslices, 0);
+          m_etErrVec.assign(timeslices, 0);
+	  m_etVec[slice]    = sumEt;
+	  m_etErrVec[slice] = ssError;
+	  map = new LVL1::CMMEtSums(swCrate, dataID,
+	                            m_etVec, m_etVec, m_etVec,
+				    m_etErrVec, m_etErrVec, m_etErrVec, trigCmm);
+          const int key = crate*100 + dataID;
+	  m_cmmEtMap.insert(std::make_pair(key, map));
+	  m_cmmEtCollection->push_back(map);
+        } else {
+          m_etVec    = map->EtVec();
+          m_etErrVec = map->EtErrorVec();
+	  const int nsl = m_etVec.size();
+	  if (timeslices != nsl) {
+	    if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                     << endreq;
+            m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    return;
+          }
+	  if (m_etVec[slice] != 0 || m_etErrVec[slice] != 0) {
+	    if (debug) msg() << "Duplicate data for slice " << slice << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    return;
+          }
+	  m_etVec[slice]    = sumEt;
+	  m_etErrVec[slice] = ssError;
+	  map->addEx(m_etVec, m_etErrVec);
+	  map->addEy(m_etVec, m_etErrVec);
+	  map->addEt(m_etVec, m_etErrVec);
+        }
+      }
+      if (subBlock->version() > 1) {
+        const unsigned int missEtSig = subBlock->missingEtSigHits(slice);
+        if ( missEtSig || ssError ) {
+          const int dataID = LVL1::CMMEtSums::MISSING_ET_SIG_MAP;
+          LVL1::CMMEtSums* map = findCmmSums(crate, dataID);
+          if ( ! map ) {
+            m_etVec.assign(timeslices, 0);
+            m_etErrVec.assign(timeslices, 0);
+	    m_etVec[slice]    = missEtSig;
+	    m_etErrVec[slice] = ssError;
+	    map = new LVL1::CMMEtSums(swCrate, dataID,
+	                              m_etVec, m_etVec, m_etVec,
+	    			      m_etErrVec, m_etErrVec, m_etErrVec, trigCmm);
+            const int key = crate*100 + dataID;
+	    m_cmmEtMap.insert(std::make_pair(key, map));
+	    m_cmmEtCollection->push_back(map);
+          } else {
+            m_etVec    = map->EtVec();
+            m_etErrVec = map->EtErrorVec();
+	    const int nsl = m_etVec.size();
+	    if (timeslices != nsl) {
+	      if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                       << endreq;
+              m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	      return;
+            }
+	    if (m_etVec[slice] != 0 || m_etErrVec[slice] != 0) {
+	      if (debug) msg() << "Duplicate data for slice "
+	                       << slice << endreq;
+	      m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	      return;
+            }
+	    m_etVec[slice]    = missEtSig;
+	    m_etErrVec[slice] = ssError;
+	    map->addEx(m_etVec, m_etErrVec);
+	    map->addEy(m_etVec, m_etErrVec);
+	    map->addEt(m_etVec, m_etErrVec);
+          }
+        }
+      }
+    }
+  }
+  
+  return;
+}
+
+// Unpack CMM-Jet sub-block
+
+void JepByteStreamTool::decodeCmmJet(CmmJetSubBlock* subBlock, int trigCmm)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  const int hwCrate    = subBlock->crate();
+  const int module     = subBlock->cmmPosition();
+  const int firmware   = subBlock->cmmFirmware();
+  const int summing    = subBlock->cmmSumming();
+  const int timeslices = subBlock->timeslices();
+  const int sliceNum   = subBlock->slice();
+  if (debug) {
+    msg() << "CMM-Jet: Crate " << hwCrate
+          << "  Module "       << module
+	  << "  Firmware "     << firmware
+	  << "  Summing "      << summing
+          << "  Total slices " << timeslices
+          << "  Slice "        << sliceNum    << endreq;
+  }
+  if (timeslices <= trigCmm) {
+    if (debug) msg() << "Triggered CMM slice from header "
+                     << "inconsistent with number of slices: "
+                     << trigCmm << ", " << timeslices << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  if (timeslices <= sliceNum) {
+    if (debug) msg() << "Total slices inconsistent with slice number: "
+                     << timeslices << ", " << sliceNum << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  // Unpack sub-block
+  if (subBlock->dataWords() && !subBlock->unpack()) {
+    if (debug) {
+      std::string errMsg(subBlock->unpackErrorMsg());
+      msg() << "CMM-Jet sub-block unpacking failed: " << errMsg << endreq;
+    }
+    m_rodErr = subBlock->unpackErrorCode();
+    return;
+  }
+
+  // Retrieve required data
+
+  const bool neutralFormat = subBlock->format() == L1CaloSubBlock::NEUTRAL;
+  const int crate    = hwCrate - m_crateOffsetHw;
+  const int swCrate  = crate   + m_crateOffsetSw;
+  const int maxSid   = static_cast<int>(CmmJetSubBlock::MAX_SOURCE_ID);
+  LVL1::DataError derr;
+  derr.set(LVL1::DataError::SubStatusWord, subBlock->subStatus());
+  const int ssError = derr.error();
+  const int sliceBeg = ( neutralFormat ) ? 0          : sliceNum;
+  const int sliceEnd = ( neutralFormat ) ? timeslices : sliceNum + 1;
+  for (int slice = sliceBeg; slice < sliceEnd; ++slice) {
+
+    // Jet hit counts
+
+    for (int source = 0; source < maxSid; ++source) {
+      int dataID = source;
+      if (source >= m_modules) {
+        if (summing == CmmSubBlock::CRATE &&
+	    source != CmmJetSubBlock::LOCAL_MAIN     &&
+	    source != CmmJetSubBlock::LOCAL_FORWARD) continue;
+	switch (source) {
+	  case CmmJetSubBlock::LOCAL_MAIN:
+	    dataID = LVL1::CMMJetHits::LOCAL_MAIN;
+	    break;
+	  case CmmJetSubBlock::REMOTE_MAIN:
+	    dataID = LVL1::CMMJetHits::REMOTE_MAIN;
+	    break;
+	  case CmmJetSubBlock::TOTAL_MAIN:
+	    dataID = LVL1::CMMJetHits::TOTAL_MAIN;
+	    break;
+	  case CmmJetSubBlock::LOCAL_FORWARD:
+	    dataID = LVL1::CMMJetHits::LOCAL_FORWARD;
+	    break;
+	  case CmmJetSubBlock::REMOTE_FORWARD:
+	    dataID = LVL1::CMMJetHits::REMOTE_FORWARD;
+	    break;
+	  case CmmJetSubBlock::TOTAL_FORWARD:
+	    dataID = LVL1::CMMJetHits::TOTAL_FORWARD;
+	    break;
+          default:
+	    continue;
+        }
+      }
+      const unsigned int hits = subBlock->jetHits(slice, source);
+      LVL1::DataError errBits(ssError);
+      errBits.set(LVL1::DataError::Parity,
+                        subBlock->jetHitsError(slice, source));
+      const int err = errBits.error();
+      if (hits || err) {
+        LVL1::CMMJetHits* jh = findCmmHits(crate, dataID);
+	if ( ! jh ) {   // create new CMM hits
+	  m_hitsVec.assign(timeslices, 0);
+	  m_errVec.assign(timeslices, 0);
+	  m_hitsVec[slice] = hits;
+	  m_errVec[slice]  = err;
+	  jh = new LVL1::CMMJetHits(swCrate, dataID, m_hitsVec, m_errVec, trigCmm);
+          const int key = crate*100 + dataID;
+	  m_cmmHitsMap.insert(std::make_pair(key, jh));
+	  m_cmmHitCollection->push_back(jh);
+        } else {
+	  m_hitsVec = jh->HitsVec();
+	  m_errVec  = jh->ErrorVec();
+	  const int nsl = m_hitsVec.size();
+	  if (timeslices != nsl) {
+	    if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                     << endreq;
+            m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    return;
+          }
+	  if (m_hitsVec[slice] != 0 || m_errVec[slice] != 0) {
+	    if (debug) msg() << "Duplicate data for slice " << slice << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    return;
+          }
+	  m_hitsVec[slice] = hits;
+	  m_errVec[slice]  = err;
+	  jh->addHits(m_hitsVec, m_errVec);
+        }
+      }
+    }
+
+    // Hit map - store as hits
+
+    if (summing == CmmSubBlock::SYSTEM) {
+      const unsigned int etMap = subBlock->jetEtMap(slice);
+      if ( etMap || ssError ) {
+        const int dataID = LVL1::CMMJetHits::ET_MAP;
+        LVL1::CMMJetHits* map = findCmmHits(crate, dataID);
+        if ( ! map ) {
+          m_hitsVec.assign(timeslices, 0);
+          m_errVec.assign(timeslices, 0);
+	  m_hitsVec[slice] = etMap;
+	  m_errVec[slice]  = ssError;
+	  map = new LVL1::CMMJetHits(swCrate, dataID, m_hitsVec, m_errVec, trigCmm);
+          const int key = crate*100 + dataID;
+	  m_cmmHitsMap.insert(std::make_pair(key, map));
+	  m_cmmHitCollection->push_back(map);
+        } else {
+          m_hitsVec = map->HitsVec();
+          m_errVec  = map->ErrorVec();
+	  const int nsl = m_hitsVec.size();
+	  if (timeslices != nsl) {
+	    if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                     << endreq;
+            m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    return;
+          }
+	  if (m_hitsVec[slice] != 0 || m_errVec[slice] != 0) {
+	    if (debug) msg() << "Duplicate data for slice " << slice << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    return;
+          }
+	  m_hitsVec[slice] = etMap;
+	  m_errVec[slice]  = ssError;
+	  map->addHits(m_hitsVec, m_errVec);
+        }
+      }
+    }
+  }
+  
+  return;
+}
+
+// Unpack JEM sub-block
+
+void JepByteStreamTool::decodeJem(JemSubBlock* subBlock, int trigJem,
+                                        const CollectionType collection)
+{
+  const bool debug   = msgLvl(MSG::DEBUG);
+  const bool verbose = msgLvl(MSG::VERBOSE);
+  if (debug) msg(MSG::DEBUG);
+
+  const int hwCrate    = subBlock->crate();
+  const int module     = subBlock->module();
+  const int timeslices = subBlock->timeslices();
+  const int sliceNum   = subBlock->slice();
+  if (debug) {
+    msg() << "JEM: Crate "     << hwCrate
+          << "  Module "       << module
+          << "  Total slices " << timeslices
+          << "  Slice "        << sliceNum    << endreq;
+  }
+  if (timeslices <= trigJem) {
+    if (debug) msg() << "Triggered JEM slice from header "
+                     << "inconsistent with number of slices: "
+                     << trigJem << ", " << timeslices << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  if (timeslices <= sliceNum) {
+    if (debug) msg() << "Total slices inconsistent with slice number: "
+                     << timeslices << ", " << sliceNum << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  // Unpack sub-block
+  if (subBlock->dataWords() && !subBlock->unpack()) {
+    if (debug) {
+      std::string errMsg(subBlock->unpackErrorMsg());
+      msg() << "JEM sub-block unpacking failed: " << errMsg << endreq;
+    }
+    m_rodErr = subBlock->unpackErrorCode();
+    return;
+  }
+
+  // Retrieve required data
+
+  const bool neutralFormat = subBlock->format() == L1CaloSubBlock::NEUTRAL;
+  const int crate    = hwCrate - m_crateOffsetHw;
+  const int swCrate  = crate   + m_crateOffsetSw;
+  LVL1::DataError derr;
+  derr.set(LVL1::DataError::SubStatusWord, subBlock->subStatus());
+  const int ssError = derr.error();
+  std::vector<int> dummy(timeslices);
+  const int sliceBeg = ( neutralFormat ) ? 0          : sliceNum;
+  const int sliceEnd = ( neutralFormat ) ? timeslices : sliceNum + 1;
+  for (int slice = sliceBeg; slice < sliceEnd; ++slice) {
+
+    if (collection == JET_ELEMENTS) {
+
+      // Loop over jet element channels and fill jet elements
+
+      for (int chan = 0; chan < m_channels; ++chan) {
+        const JemJetElement jetEle(subBlock->jetElement(slice, chan));
+        if (jetEle.data() || ssError) {
+	  double eta = 0.;
+	  double phi = 0.;
+	  int layer = 0;
+	  if (m_jemMaps->mapping(crate, module, chan, eta, phi, layer)) {
+	    if (layer == m_coreOverlap) {
+	      LVL1::JetElement* je = findJetElement(eta, phi);
+	      if ( ! je ) {   // create new jet element
+	        const unsigned int key = m_elementKey->jeKey(phi, eta);
+	        je = new LVL1::JetElement(phi, eta, dummy, dummy, key,
+	                                  dummy, dummy, dummy, trigJem);
+	        m_jeMap.insert(std::make_pair(key, je));
+	        m_jeCollection->push_back(je);
+              } else {
+	        const std::vector<int>& emEnergy(je->emEnergyVec());
+		const std::vector<int>& hadEnergy(je->hadEnergyVec());
+		const std::vector<int>& emError(je->emErrorVec());
+		const std::vector<int>& hadError(je->hadErrorVec());
+		const int nsl = emEnergy.size();
+		if (timeslices != nsl) {
+		  if (debug) {
+		    msg() << "Inconsistent number of slices in sub-blocks"
+		          << endreq;
+                  }
+		  m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+		  return;
+                }
+		if (emEnergy[slice] != 0 || hadEnergy[slice] != 0 ||
+		    emError[slice]  != 0 || hadError[slice]  != 0) {
+                  if (debug) msg() << "Duplicate data for slice "
+		                   << slice << endreq;
+                  m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+		  return;
+                }
+              }
+	      LVL1::DataError emErrBits(ssError);
+	      LVL1::DataError hadErrBits(ssError);
+	      const int linkError = jetEle.linkError();
+	      emErrBits.set(LVL1::DataError::Parity, jetEle.emParity());
+	      emErrBits.set(LVL1::DataError::LinkDown, linkError);
+	      hadErrBits.set(LVL1::DataError::Parity, jetEle.hadParity());
+	      hadErrBits.set(LVL1::DataError::LinkDown, linkError >> 1);
+	      je->addSlice(slice, jetEle.emData(), jetEle.hadData(),
+	                          emErrBits.error(), hadErrBits.error(),
+	           	          linkError);
+	    }
+          } else if (verbose && jetEle.data()) {
+	    msg(MSG::VERBOSE) << "Non-zero data but no channel mapping for channel "
+	                      << chan << endreq;
+	    msg(MSG::DEBUG);
+          }
+        } else if (verbose) {
+	  msg(MSG::VERBOSE) << "No jet element data for channel "
+	                    << chan << " slice " << slice << endreq;
+	  msg(MSG::DEBUG);
+        }
+      }
+    } else if (collection == JET_HITS) {
+
+      // Get jet hits
+
+      const unsigned int hits = subBlock->jetHits(slice);
+      if (hits) {
+        LVL1::JEMHits* jh = findJetHits(crate, module);
+	if ( ! jh ) {   // create new jet hits
+	  m_hitsVec.assign(timeslices, 0);
+	  m_hitsVec[slice] = hits;
+	  jh = new LVL1::JEMHits(swCrate, module, m_hitsVec, trigJem);
+	  m_hitsMap.insert(std::make_pair(crate*m_modules+module, jh));
+	  m_hitCollection->push_back(jh);
+        } else {
+	  m_hitsVec = jh->JetHitsVec();
+	  const int nsl = m_hitsVec.size();
+	  if (timeslices != nsl) {
+	    if (debug) {
+	      msg() << "Inconsistent number of slices in sub-blocks"
+	            << endreq;
+	    }
+            m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    return;
+          }
+	  if (m_hitsVec[slice] != 0) {
+	    if (debug) msg() << "Duplicate data for slice "
+	                     << slice << endreq;
+            m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    return;
+          }
+	  m_hitsVec[slice] = hits;
+	  jh->addJetHits(m_hitsVec);
+        }
+      } else if (verbose) {
+        msg(MSG::VERBOSE) << "No jet hits data for crate/module/slice "
+                          << hwCrate << "/" << module << "/" << slice
+			  << endreq;
+	msg(MSG::DEBUG);
+      }
+    } else if (collection == ENERGY_SUMS) {
+
+      // Get energy subsums
+
+      const unsigned int ex = subBlock->ex(slice);
+      const unsigned int ey = subBlock->ey(slice);
+      const unsigned int et = subBlock->et(slice);
+      if (ex | ey | et) {
+	LVL1::JEMEtSums* sums = findEnergySums(crate, module);
+	if ( ! sums ) {   // create new energy sums
+	  m_exVec.assign(timeslices, 0);
+	  m_eyVec.assign(timeslices, 0);
+	  m_etVec.assign(timeslices, 0);
+	  m_exVec[slice] = ex;
+	  m_eyVec[slice] = ey;
+	  m_etVec[slice] = et;
+	  sums = new LVL1::JEMEtSums(swCrate, module, m_etVec, m_exVec, m_eyVec,
+	                                                            trigJem);
+          m_etMap.insert(std::make_pair(crate*m_modules+module, sums));
+	  m_etCollection->push_back(sums);
+        } else {
+	  m_exVec = sums->ExVec();
+	  m_eyVec = sums->EyVec();
+	  m_etVec = sums->EtVec();
+	  const int nsl = m_exVec.size();
+	  if (timeslices != nsl) {
+	    if (debug) {
+	      msg() << "Inconsistent number of slices in sub-blocks"
+	            << endreq;
+	    }
+            m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    return;
+          }
+	  if (m_exVec[slice] != 0 || m_eyVec[slice] != 0 || m_etVec[slice] != 0) {
+	    if (debug) msg() << "Duplicate data for slice "
+	                     << slice << endreq;
+            m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    return;
+          }
+	  m_exVec[slice] = ex;
+	  m_eyVec[slice] = ey;
+	  m_etVec[slice] = et;
+	  sums->addEx(m_exVec);
+	  sums->addEy(m_eyVec);
+	  sums->addEt(m_etVec);
+        }
+      } else if (verbose) {
+        msg(MSG::VERBOSE) << "No energy sums data for crate/module/slice "
+                          << hwCrate << "/" << module << "/" << slice
+    			  << endreq;
+	msg(MSG::DEBUG);
+      }
+    }
+  }
+  return;
+}
+
+// Find a jet element given eta, phi
+
+LVL1::JetElement* JepByteStreamTool::findJetElement(const double eta,
+                                                    const double phi)
+{
+  LVL1::JetElement* tt = 0;
+  const unsigned int key = m_elementKey->jeKey(phi, eta);
+  JetElementMap::const_iterator mapIter;
+  mapIter = m_jeMap.find(key);
+  if (mapIter != m_jeMap.end()) tt = mapIter->second;
+  return tt;
+}
+
+// Find jet hits for given crate, module
+
+LVL1::JEMHits* JepByteStreamTool::findJetHits(const int crate,
+                                              const int module)
+{
+  LVL1::JEMHits* hits = 0;
+  JetHitsMap::const_iterator mapIter;
+  mapIter = m_hitsMap.find(crate*m_modules + module);
+  if (mapIter != m_hitsMap.end()) hits = mapIter->second;
+  return hits;
+}
+
+// Find energy sums for given crate, module
+
+LVL1::JEMEtSums* JepByteStreamTool::findEnergySums(const int crate,
+                                                   const int module)
+{
+  LVL1::JEMEtSums* sums = 0;
+  EnergySumsMap::const_iterator mapIter;
+  mapIter = m_etMap.find(crate*m_modules + module);
+  if (mapIter != m_etMap.end()) sums = mapIter->second;
+  return sums;
+}
+
+// Find CMM hits for given crate, dataID
+
+LVL1::CMMJetHits* JepByteStreamTool::findCmmHits(const int crate,
+                                                 const int dataID)
+{
+  LVL1::CMMJetHits* hits = 0;
+  CmmHitsMap::const_iterator mapIter;
+  mapIter = m_cmmHitsMap.find(crate*100 + dataID);
+  if (mapIter != m_cmmHitsMap.end()) hits = mapIter->second;
+  return hits;
+}
+
+// Find CMM energy sums for given crate, module, dataID
+
+LVL1::CMMEtSums* JepByteStreamTool::findCmmSums(const int crate,
+                                                const int dataID)
+{
+  LVL1::CMMEtSums* sums = 0;
+  CmmSumsMap::const_iterator mapIter;
+  mapIter = m_cmmEtMap.find(crate*100 + dataID);
+  if (mapIter != m_cmmEtMap.end()) sums = mapIter->second;
+  return sums;
+}
+
+// Set up jet element map
+
+void JepByteStreamTool::setupJeMap(const JetElementCollection*
+                                                        const jeCollection)
+{
+  m_jeMap.clear();
+  if (jeCollection) {
+    JetElementCollection::const_iterator pos  = jeCollection->begin();
+    JetElementCollection::const_iterator pose = jeCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::JetElement* const je = *pos;
+      const unsigned int key = m_elementKey->jeKey(je->phi(), je->eta());
+      m_jeMap.insert(std::make_pair(key, je));
+    }
+  }
+}
+
+// Set up jet hits map
+
+void JepByteStreamTool::setupHitsMap(const JetHitsCollection*
+                                                        const hitCollection)
+{
+  m_hitsMap.clear();
+  if (hitCollection) {
+    JetHitsCollection::const_iterator pos  = hitCollection->begin();
+    JetHitsCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::JEMHits* const hits = *pos;
+      const int crate = hits->crate() - m_crateOffsetSw;
+      const int key   = m_modules * crate + hits->module();
+      m_hitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Set up energy sums map
+
+void JepByteStreamTool::setupEtMap(const EnergySumsCollection*
+                                                         const etCollection)
+{
+  m_etMap.clear();
+  if (etCollection) {
+    EnergySumsCollection::const_iterator pos  = etCollection->begin();
+    EnergySumsCollection::const_iterator pose = etCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::JEMEtSums* const sums = *pos;
+      const int crate = sums->crate() - m_crateOffsetSw;
+      const int key   = m_modules * crate + sums->module();
+      m_etMap.insert(std::make_pair(key, sums));
+    }
+  }
+}
+
+// Set up CMM hits map
+
+void JepByteStreamTool::setupCmmHitsMap(const CmmHitsCollection*
+                                                         const hitCollection)
+{
+  m_cmmHitsMap.clear();
+  if (hitCollection) {
+    CmmHitsCollection::const_iterator pos  = hitCollection->begin();
+    CmmHitsCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::CMMJetHits* const hits = *pos;
+      const int crate = hits->crate() - m_crateOffsetSw;
+      const int key   = crate*100 + hits->dataID();
+      m_cmmHitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Set up CMM energy sums map
+
+void JepByteStreamTool::setupCmmEtMap(const CmmSumsCollection*
+                                                         const etCollection)
+{
+  m_cmmEtMap.clear();
+  if (etCollection) {
+    CmmSumsCollection::const_iterator pos  = etCollection->begin();
+    CmmSumsCollection::const_iterator pose = etCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::CMMEtSums* const sums = *pos;
+      const int crate = sums->crate() - m_crateOffsetSw;
+      const int key   = crate*100 + sums->dataID();
+      m_cmmEtMap.insert(std::make_pair(key, sums));
+    }
+  }
+}
+
+// Get number of slices and triggered slice offset for next slink
+
+bool JepByteStreamTool::slinkSlices(const int crate, const int module,
+                  const int modulesPerSlink, int& timeslices, int& trigJem)
+{
+  int slices = -1;
+  int trigJ  = m_dfltSlices/2;
+  for (int mod = module; mod < module + modulesPerSlink; ++mod) {
+    for (int chan = 0; chan < m_channels; ++chan) {
+      double eta = 0.;
+      double phi = 0.;
+      int layer = 0;
+      if ( !m_jemMaps->mapping(crate, mod, chan, eta, phi, layer)) continue;
+      const LVL1::JetElement* const je = findJetElement(eta, phi);
+      if ( !je ) continue;
+      const int numdat = 5;
+      std::vector<int> sums(numdat);
+      std::vector<int> sizes(numdat);
+      sums[0] = std::accumulate((je->emEnergyVec()).begin(),
+                                (je->emEnergyVec()).end(), 0);
+      sums[1] = std::accumulate((je->hadEnergyVec()).begin(),
+                                (je->hadEnergyVec()).end(), 0);
+      sums[2] = std::accumulate((je->emErrorVec()).begin(),
+                                (je->emErrorVec()).end(), 0);
+      sums[3] = std::accumulate((je->hadErrorVec()).begin(),
+                                (je->hadErrorVec()).end(), 0);
+      sums[4] = std::accumulate((je->linkErrorVec()).begin(),
+                                (je->linkErrorVec()).end(), 0);
+      sizes[0] = (je->emEnergyVec()).size();
+      sizes[1] = (je->hadEnergyVec()).size();
+      sizes[2] = (je->emErrorVec()).size();
+      sizes[3] = (je->hadErrorVec()).size();
+      sizes[4] = (je->linkErrorVec()).size();
+      const int peak = je->peak();
+      for (int i = 0; i < numdat; ++i) {
+        if (sums[i] == 0) continue;
+        if (slices < 0) {
+	  slices = sizes[i];
+	  trigJ  = peak;
+	} else if (slices != sizes[i] || trigJ != peak) return false;
+      }
+    }
+    const LVL1::JEMHits* const hits = findJetHits(crate, mod);
+    if (hits) {
+      const unsigned int sum = std::accumulate((hits->JetHitsVec()).begin(),
+                                               (hits->JetHitsVec()).end(), 0);
+      if (sum) {
+        const int size = (hits->JetHitsVec()).size();
+	const int peak = hits->peak();
+        if (slices < 0) {
+	  slices = size;
+	  trigJ  = peak;
+        } else if (slices != size || trigJ != peak) return false;
+      }
+    }
+    const LVL1::JEMEtSums* const et = findEnergySums(crate, mod);
+    if (et) {
+      const int numdat = 3;
+      std::vector<unsigned int> sums(numdat);
+      std::vector<int> sizes(numdat);
+      sums[0] = std::accumulate((et->ExVec()).begin(),
+                                (et->ExVec()).end(), 0);
+      sums[1] = std::accumulate((et->EyVec()).begin(),
+                                (et->EyVec()).end(), 0);
+      sums[2] = std::accumulate((et->EtVec()).begin(),
+                                (et->EtVec()).end(), 0);
+      sizes[0] = (et->ExVec()).size();
+      sizes[1] = (et->EyVec()).size();
+      sizes[2] = (et->EtVec()).size();
+      const int peak = et->peak();
+      for (int i = 0; i < numdat; ++i) {
+        if (sums[i] == 0) continue;
+	if (slices < 0) {
+	  slices = sizes[i];
+	  trigJ  = peak;
+        } else if (slices != sizes[i] || trigJ != peak) return false;
+      }
+    }
+  }
+  // CMM last slink of crate
+  if (module/modulesPerSlink == m_slinks - 1) {
+    const int maxDataID1 = LVL1::CMMJetHits::MAXID;
+    const int maxDataID2 = LVL1::CMMEtSums::MAXID;
+    const int maxDataID  = (maxDataID1 > maxDataID2) ? maxDataID1 : maxDataID2;
+    for (int dataID = 0; dataID < maxDataID; ++dataID) {
+      const int numdat = 6;
+      std::vector<unsigned int> sums(numdat);
+      std::vector<int> sizes(numdat);
+      const LVL1::CMMJetHits* hits = 0;
+      if (dataID < maxDataID1) hits = findCmmHits(crate, dataID);
+      if (hits) {
+        sums[0] = std::accumulate((hits->HitsVec()).begin(),
+                                             (hits->HitsVec()).end(), 0);
+        sums[1] = std::accumulate((hits->ErrorVec()).begin(),
+                                             (hits->ErrorVec()).end(), 0);
+        sizes[0] = (hits->HitsVec()).size();
+        sizes[1] = (hits->ErrorVec()).size();
+        const int peak = hits->peak();
+        for (int i = 0; i < 2; ++i) {
+          if (sums[i] == 0) continue;
+          if (slices < 0) {
+	    slices = sizes[i];
+	    trigJ  = peak;
+          } else if (slices != sizes[i] || trigJ != peak) return false;
+        }
+      }
+      const LVL1::CMMEtSums* et = 0;
+      if (dataID < maxDataID2) et = findCmmSums(crate, dataID);
+      if (et) {
+        sums[0] = std::accumulate((et->ExVec()).begin(),
+  				  (et->ExVec()).end(), 0);
+        sums[1] = std::accumulate((et->EyVec()).begin(),
+                                  (et->EyVec()).end(), 0);
+        sums[2] = std::accumulate((et->EtVec()).begin(),
+                                  (et->EtVec()).end(), 0);
+        sums[3] = std::accumulate((et->ExErrorVec()).begin(),
+                                  (et->ExErrorVec()).end(), 0);
+        sums[4] = std::accumulate((et->EyErrorVec()).begin(),
+                                  (et->EyErrorVec()).end(), 0);
+        sums[5] = std::accumulate((et->EtErrorVec()).begin(),
+                                  (et->EtErrorVec()).end(), 0);
+        sizes[0] = (et->ExVec()).size();
+        sizes[1] = (et->EyVec()).size();
+        sizes[2] = (et->EtVec()).size();
+        sizes[3] = (et->ExErrorVec()).size();
+        sizes[4] = (et->EyErrorVec()).size();
+        sizes[5] = (et->EtErrorVec()).size();
+        const int peak = et->peak();
+        for (int i = 0; i < numdat; ++i) {
+          if (sums[i] == 0) continue;
+	  if (slices < 0) {
+	    slices = sizes[i];
+	    trigJ  = peak;
+          } else if (slices != sizes[i] || trigJ != peak) return false;
+        }
+      }
+    }
+  }
+  if (slices < 0) slices = m_dfltSlices;
+  timeslices = slices;
+  trigJem    = trigJ;
+  return true;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamTool.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamTool.h
new file mode 100755
index 0000000000000000000000000000000000000000..8e307e6ec5e60f3f23278fb880a24989a7d804ab
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamTool.h
@@ -0,0 +1,240 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEPBYTESTREAMTOOL_H
+#define TRIGT1CALOBYTESTREAM_JEPBYTESTREAMTOOL_H
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+#include "ByteStreamData/RawEvent.h"
+#include "DataModel/DataVector.h"
+#include "eformat/SourceIdentifier.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class IInterface;
+class InterfaceID;
+class StatusCode;
+
+template <class T> class FullEventAssembler;
+
+namespace LVL1 {
+  class CMMJetHits;
+  class CMMEtSums;
+  class IL1CaloMappingTool;
+  class JEMHits;
+  class JEMEtSums;
+  class JEPBSCollection;
+  class JetElement;
+  class JetElementKey;
+}
+
+namespace LVL1BS {
+
+class CmmEnergySubBlock;
+class CmmJetSubBlock;
+class JemSubBlock;
+class L1CaloErrorByteStreamTool;
+class L1CaloSrcIdMap;
+
+/** Tool to perform ROB fragments to jet elements, jet hits and energy sums,
+ *  and JEP container to raw data conversions.
+ *
+ *  Based on ROD document version 1_09h.
+ *
+ *  @author Peter Faulkner
+ */
+
+class JepByteStreamTool : public AthAlgTool {
+
+ public:
+   JepByteStreamTool(const std::string& type, const std::string& name,
+                     const IInterface* parent);
+   virtual ~JepByteStreamTool();
+
+   /// AlgTool InterfaceID
+   static const InterfaceID& interfaceID();
+
+   virtual StatusCode initialize();
+   virtual StatusCode finalize();
+
+   /// Convert ROB fragments to jet elements
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::JetElement>* jeCollection);
+   /// Convert ROB fragments to jet hits
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::JEMHits>* hitCollection);
+   /// Convert ROB fragments to energy sums
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::JEMEtSums>* etCollection);
+   /// Convert ROB fragments to CMM jet hits
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::CMMJetHits>* hitCollection);
+   /// Convert ROB fragments to CMM energy sums
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::CMMEtSums>* etCollection);
+
+   /// Convert JEP Container to bytestream
+   StatusCode convert(const LVL1::JEPBSCollection* jep, RawEventWrite* re);
+
+   /// Return reference to vector with all possible Source Identifiers
+   const std::vector<uint32_t>& sourceIDs(const std::string& sgKey);
+
+ private:
+   enum CollectionType { JET_ELEMENTS, JET_HITS, ENERGY_SUMS,
+                                       CMM_HITS, CMM_SUMS };
+
+   typedef DataVector<LVL1::JetElement>                  JetElementCollection;
+   typedef DataVector<LVL1::JEMHits>                     JetHitsCollection;
+   typedef DataVector<LVL1::JEMEtSums>                   EnergySumsCollection;
+   typedef DataVector<LVL1::CMMJetHits>                  CmmHitsCollection;
+   typedef DataVector<LVL1::CMMEtSums>                   CmmSumsCollection;
+   typedef std::map<unsigned int, LVL1::JetElement*>     JetElementMap;
+   typedef std::map<int, LVL1::JEMHits*>                 JetHitsMap;
+   typedef std::map<int, LVL1::JEMEtSums*>               EnergySumsMap;
+   typedef std::map<int, LVL1::CMMJetHits*>              CmmHitsMap;
+   typedef std::map<int, LVL1::CMMEtSums*>               CmmSumsMap;
+   typedef IROBDataProviderSvc::VROBFRAG::const_iterator ROBIterator;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      ROBPointer;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      RODPointer;
+
+   /// Convert bytestream to given container type
+   StatusCode convertBs(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                        CollectionType collection);
+   /// Unpack CMM-Energy sub-block
+   void decodeCmmEnergy(CmmEnergySubBlock* subBlock, int trigCmm);
+   /// Unpack CMM-Jet sub-block
+   void decodeCmmJet(CmmJetSubBlock* subBlock, int trigCmm);
+   /// Unpack JEM sub-block
+   void decodeJem(JemSubBlock* subBlock, int trigJem,
+                                         CollectionType collection);
+
+   /// Find a jet element given eta, phi
+   LVL1::JetElement* findJetElement(double eta, double phi);
+   /// Find jet hits for given crate, module
+   LVL1::JEMHits*    findJetHits(int crate, int module);
+   /// Find energy sums for given crate, module
+   LVL1::JEMEtSums*  findEnergySums(int crate, int module);
+   /// Find CMM hits for given crate, data ID
+   LVL1::CMMJetHits* findCmmHits(int crate, int dataID);
+   /// Find CMM energy sums for given crate, data ID
+   LVL1::CMMEtSums*  findCmmSums(int crate, int dataID);
+
+   /// Set up jet element map
+   void setupJeMap(const JetElementCollection* jeCollection);
+   /// Set up jet hits map
+   void setupHitsMap(const JetHitsCollection* hitCollection);
+   /// Set up energy sums map
+   void setupEtMap(const EnergySumsCollection* enCollection);
+   /// Set up CMM hits map
+   void setupCmmHitsMap(const CmmHitsCollection* hitCollection);
+   /// Set up CMM energy sums map
+   void setupCmmEtMap(const CmmSumsCollection* enCollection);
+
+   /// Get number of slices and triggered slice offset for next slink
+   bool slinkSlices(int crate, int module, int modulesPerSlink,
+                    int& timeslices, int& trigJem);
+
+   /// Channel mapping tool
+   ToolHandle<LVL1::IL1CaloMappingTool> m_jemMaps;
+   /// Error collection tool
+   ToolHandle<LVL1BS::L1CaloErrorByteStreamTool> m_errorTool;
+
+   /// Hardware crate number offset
+   int m_crateOffsetHw;
+   /// Software crate number offset
+   int m_crateOffsetSw;
+   /// Sub_block header version
+   int m_version;
+   /// Data compression format
+   int m_dataFormat;
+   /// Number of channels per module
+   int m_channels;
+   /// Number of crates
+   int m_crates;
+   /// Number of JEM modules per crate
+   int m_modules;
+   /// Number of slinks per crate when writing out bytestream
+   int m_slinks;
+   /// Default number of slices in simulation
+   int m_dfltSlices;
+   /// Force number of slices in bytestream
+   int m_forceSlices;
+   /// Jet elements to accept (0=Core, 1=Overlap)
+   int m_coreOverlap;
+   /// Unpacking error code
+   unsigned int m_rodErr;
+   /// ROB source IDs
+   std::vector<uint32_t> m_sourceIDs;
+   /// Sub-detector type
+   eformat::SubDetector m_subDetector;
+   /// Source ID converter
+   L1CaloSrcIdMap* m_srcIdMap;
+   /// Jet element key provider
+   LVL1::JetElementKey* m_elementKey;
+   /// JemSubBlock for unpacking
+   JemSubBlock* m_jemSubBlock;
+   /// CmmEnergySubBlock for unpacking
+   CmmEnergySubBlock* m_cmmEnergySubBlock;
+   /// CmmJetSubBlock for unpacking
+   CmmJetSubBlock* m_cmmJetSubBlock;
+   /// Ex vector for unpacking
+   std::vector<unsigned int> m_exVec;
+   /// Ey vector for unpacking
+   std::vector<unsigned int> m_eyVec;
+   /// Et vector for unpacking
+   std::vector<unsigned int> m_etVec;
+   /// Ex error vector for unpacking
+   std::vector<int> m_exErrVec;
+   /// Ex error vector for unpacking
+   std::vector<int> m_eyErrVec;
+   /// Ex error vector for unpacking
+   std::vector<int> m_etErrVec;
+   /// Hits vector for unpacking
+   std::vector<unsigned int> m_hitsVec;
+   /// Error vector for unpacking
+   std::vector<int> m_errVec;
+   /// Vector for current JEM sub-blocks
+   DataVector<JemSubBlock> m_jemBlocks;
+   /// Vector for current CMM-Energy sub-blocks
+   DataVector<CmmEnergySubBlock> m_cmmEnergyBlocks;
+   /// Vector for current CMM-Jet sub-blocks
+   DataVector<CmmJetSubBlock> m_cmmJetBlocks;
+   /// Current jet elements collection
+   JetElementCollection* m_jeCollection;
+   /// Current jet hits collection
+   JetHitsCollection*    m_hitCollection;
+   /// Current energy sums collection
+   EnergySumsCollection* m_etCollection;
+   /// Current CMM hits collection
+   CmmHitsCollection*    m_cmmHitCollection;
+   /// Current CMM energy sums collection
+   CmmSumsCollection*    m_cmmEtCollection;
+   /// Jet element map
+   JetElementMap m_jeMap;
+   /// Jet hits map
+   JetHitsMap    m_hitsMap;
+   /// Energy sums map
+   EnergySumsMap m_etMap;
+   /// CMM hits map
+   CmmHitsMap    m_cmmHitsMap;
+   /// CMM energy sums map
+   CmmSumsMap    m_cmmEtMap;
+   /// ROD Status words
+   std::vector<uint32_t>* m_rodStatus;
+   /// ROD status map
+   std::map<uint32_t, std::vector<uint32_t>* > m_rodStatusMap;
+   /// Event assembler
+   FullEventAssembler<L1CaloSrcIdMap>* m_fea;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV1Cnv.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV1Cnv.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..338d7784d7d51a1e739123eb2d04d1506b5ce1f5
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV1Cnv.cxx
@@ -0,0 +1,115 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IByteStreamEventAccess.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "DataModel/DataVector.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/IRegistry.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "TrigT1CaloEvent/JEPBSCollectionV1.h"
+
+#include "JepByteStreamV1Cnv.h"
+#include "JepByteStreamV1Tool.h"
+
+namespace LVL1BS {
+
+JepByteStreamV1Cnv::JepByteStreamV1Cnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("JepByteStreamV1Cnv"),
+      m_tool("LVL1BS::JepByteStreamV1Tool/JepByteStreamV1Tool"),
+      m_ByteStreamEventAccess("ByteStreamCnvSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+JepByteStreamV1Cnv::~JepByteStreamV1Cnv()
+{
+}
+
+// CLID
+
+const CLID& JepByteStreamV1Cnv::classID()
+{
+  return ClassID_traits<LVL1::JEPBSCollectionV1>::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode JepByteStreamV1Cnv::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << " - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  //Get ByteStreamCnvSvc
+  sc = m_ByteStreamEventAccess.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_ByteStreamEventAccess << endreq;
+    return sc;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_ByteStreamEventAccess << endreq;
+  }
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return StatusCode::FAILURE;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  return StatusCode::SUCCESS;
+}
+
+// createRep should create the bytestream from RDOs.
+
+StatusCode JepByteStreamV1Cnv::createRep( DataObject* pObj,
+                                          IOpaqueAddress*& pAddr )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createRep() called" << endreq;
+
+  RawEventWrite* re = m_ByteStreamEventAccess->getRawEvent();
+
+  LVL1::JEPBSCollectionV1* jep = 0;
+  if( !SG::fromStorable( pObj, jep ) ) {
+    m_log << MSG::ERROR << " Cannot cast to JEPBSCollectionV1" << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = pObj->registry()->name();
+
+  ByteStreamAddress* addr = new ByteStreamAddress( classID(), nm, "" );
+
+  pAddr = addr;
+
+  // Convert to ByteStream
+  return m_tool->convert( jep, re );
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV1Cnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV1Cnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..cb2fa92759d1043032ee79283b755206815e9728
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV1Cnv.h
@@ -0,0 +1,76 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEPBYTESTREAMV1CNV_H
+#define TRIGT1CALOBYTESTREAM_JEPBYTESTREAMV1CNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IByteStreamEventAccess;
+class IOpaqueAddress;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class JepByteStreamV1Tool;
+
+/** ByteStream converter for JEP container
+ *
+ *  @author Peter Faulkner
+ */
+
+class JepByteStreamV1Cnv: public Converter {
+
+  friend class CnvFactory<JepByteStreamV1Cnv>;
+
+protected:
+
+  JepByteStreamV1Cnv(ISvcLocator* svcloc);
+
+public:
+
+  ~JepByteStreamV1Cnv();
+
+  virtual StatusCode initialize();
+  /// Create ByteStream from JEP Container
+  virtual StatusCode createRep(DataObject* pObj, IOpaqueAddress*& pAddr);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::JepByteStreamV1Tool> m_tool;
+
+  /// Service for writing bytestream
+  ServiceHandle<IByteStreamEventAccess> m_ByteStreamEventAccess;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV1Tool.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV1Tool.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..18c93477c36290bb224559da760e53eab1f08006
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV1Tool.cxx
@@ -0,0 +1,1741 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <numeric>
+#include <set>
+#include <utility>
+
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "ByteStreamCnvSvcBase/FullEventAssembler.h"
+
+#include "TrigT1CaloEvent/CMMJetHits.h"
+#include "TrigT1CaloEvent/CMMEtSums.h"
+#include "TrigT1CaloEvent/JEMHits.h"
+#include "TrigT1CaloEvent/JEMEtSums.h"
+#include "TrigT1CaloEvent/JEPBSCollectionV1.h"
+#include "TrigT1CaloEvent/JetElement.h"
+#include "TrigT1CaloUtils/DataError.h"
+#include "TrigT1CaloUtils/JetElementKey.h"
+#include "TrigT1CaloMappingToolInterfaces/IL1CaloMappingTool.h"
+
+#include "CmmEnergySubBlock.h"
+#include "CmmJetSubBlock.h"
+#include "CmmSubBlock.h"
+#include "JemJetElement.h"
+#include "JemSubBlockV1.h"
+#include "L1CaloErrorByteStreamTool.h"
+#include "L1CaloSrcIdMap.h"
+#include "L1CaloSubBlock.h"
+#include "L1CaloUserHeader.h"
+#include "ModifySlices.h"
+
+#include "JepByteStreamV1Tool.h"
+
+namespace LVL1BS {
+
+// Interface ID
+
+static const InterfaceID IID_IJepByteStreamV1Tool("JepByteStreamV1Tool", 1, 1);
+
+const InterfaceID& JepByteStreamV1Tool::interfaceID()
+{
+  return IID_IJepByteStreamV1Tool;
+}
+
+// Constructor
+
+JepByteStreamV1Tool::JepByteStreamV1Tool(const std::string& type,
+                                     const std::string& name,
+				     const IInterface*  parent)
+  : AthAlgTool(type, name, parent),
+    m_jemMaps("LVL1::JemMappingTool/JemMappingTool"),
+    m_errorTool("LVL1BS::L1CaloErrorByteStreamTool/L1CaloErrorByteStreamTool"),
+    m_channels(44), m_crates(2), m_modules(16), m_coreOverlap(0),
+    m_subDetector(eformat::TDAQ_CALO_JET_PROC_DAQ),
+    m_srcIdMap(0), m_elementKey(0),
+    m_jemSubBlock(0), m_cmmEnergySubBlock(0), m_cmmJetSubBlock(0),
+    m_rodStatus(0), m_fea(0)
+{
+  declareInterface<JepByteStreamV1Tool>(this);
+
+  declareProperty("JemMappingTool", m_jemMaps,
+                  "Crate/Module/Channel to Eta/Phi/Layer mapping tool");
+  declareProperty("ErrorTool", m_errorTool,
+                  "Tool to collect errors for monitoring");
+
+  declareProperty("CrateOffsetHw",  m_crateOffsetHw = 12,
+                  "Offset of JEP crate numbers in bytestream");
+  declareProperty("CrateOffsetSw",  m_crateOffsetSw = 0,
+                  "Offset of JEP crate numbers in RDOs");
+  declareProperty("SlinksPerCrate", m_slinks        = 4,
+                  "The number of S-Links per crate");
+
+  // Properties for reading bytestream only
+  declareProperty("ROBSourceIDs",       m_sourceIDs,
+                  "ROB fragment source identifiers");
+
+  // Properties for writing bytestream only
+  declareProperty("DataVersion",    m_version     = 1,
+                  "Format version number in sub-block header");
+  declareProperty("DataFormat",     m_dataFormat  = 1,
+                  "Format identifier (0-1) in sub-block header");
+  declareProperty("SimulSlices",    m_dfltSlices  = 1,
+                  "The number of slices in the simulation");
+  declareProperty("ForceSlices",    m_forceSlices = 0,
+                  "If >0, the number of slices in bytestream");
+  declareProperty("CrateMin",       m_crateMin = 0,
+                  "Minimum crate number, allows partial output");
+  declareProperty("CrateMax",       m_crateMax = m_crates-1,
+                  "Maximum crate number, allows partial output");
+
+}
+
+// Destructor
+
+JepByteStreamV1Tool::~JepByteStreamV1Tool()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode JepByteStreamV1Tool::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = m_jemMaps.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_jemMaps << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_jemMaps << endreq;
+
+  sc = m_errorTool.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_errorTool << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_errorTool << endreq;
+
+  m_srcIdMap          = new L1CaloSrcIdMap();
+  m_elementKey        = new LVL1::JetElementKey();
+  m_jemSubBlock       = new JemSubBlockV1();
+  m_cmmEnergySubBlock = new CmmEnergySubBlock();
+  m_cmmJetSubBlock    = new CmmJetSubBlock();
+  m_rodStatus         = new std::vector<uint32_t>(2);
+  m_fea               = new FullEventAssembler<L1CaloSrcIdMap>();
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode JepByteStreamV1Tool::finalize()
+{
+  delete m_fea;
+  delete m_rodStatus;
+  delete m_cmmJetSubBlock;
+  delete m_cmmEnergySubBlock;
+  delete m_jemSubBlock;
+  delete m_elementKey;
+  delete m_srcIdMap;
+  return StatusCode::SUCCESS;
+}
+
+// Conversion bytestream to jet elements
+
+StatusCode JepByteStreamV1Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::JetElement>* const jeCollection)
+{
+  m_jeCollection = jeCollection;
+  m_jeMap.clear();
+  return convertBs(robFrags, JET_ELEMENTS);
+}
+
+// Conversion bytestream to jet hits
+
+StatusCode JepByteStreamV1Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::JEMHits>* const hitCollection)
+{
+  m_hitCollection = hitCollection;
+  m_hitsMap.clear();
+  return convertBs(robFrags, JET_HITS);
+}
+
+// Conversion bytestream to energy sums
+
+StatusCode JepByteStreamV1Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::JEMEtSums>* const etCollection)
+{
+  m_etCollection = etCollection;
+  m_etMap.clear();
+  return convertBs(robFrags, ENERGY_SUMS);
+}
+
+// Conversion bytestream to CMM hits
+
+StatusCode JepByteStreamV1Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::CMMJetHits>* const hitCollection)
+{
+  m_cmmHitCollection = hitCollection;
+  m_cmmHitsMap.clear();
+  return convertBs(robFrags, CMM_HITS);
+}
+
+// Conversion bytestream to CMM energy sums
+
+StatusCode JepByteStreamV1Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::CMMEtSums>* const etCollection)
+{
+  m_cmmEtCollection = etCollection;
+  m_cmmEtMap.clear();
+  return convertBs(robFrags, CMM_SUMS);
+}
+
+// Conversion of JEP container to bytestream
+
+StatusCode JepByteStreamV1Tool::convert(const LVL1::JEPBSCollectionV1* const jep,
+                                            RawEventWrite* const re)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Clear the event assembler
+
+  m_fea->clear();
+  const uint16_t minorVersion = m_srcIdMap->minorVersionPreLS1();
+  m_fea->setRodMinorVersion(minorVersion);
+  m_rodStatusMap.clear();
+
+  // Pointer to ROD data vector
+
+  FullEventAssembler<L1CaloSrcIdMap>::RODDATA* theROD = 0;
+
+  // Set up the container maps
+
+  setupJeMap(jep->JetElements());
+  setupHitsMap(jep->JetHits());
+  setupEtMap(jep->EnergySums());
+  setupCmmHitsMap(jep->CmmHits());
+  setupCmmEtMap(jep->CmmSums());
+
+  // Loop over data
+
+  const bool neutralFormat = m_dataFormat == L1CaloSubBlock::NEUTRAL;
+  const int modulesPerSlink = m_modules / m_slinks;
+  int timeslices       = 1;
+  int trigJem          = 0;
+  int timeslicesNew    = 1;
+  int trigJemNew       = 0;
+  for (int crate = m_crateMin; crate <= m_crateMax; ++crate) {
+    const int hwCrate = crate + m_crateOffsetHw;
+
+    for (int module=0; module < m_modules; ++module) {
+
+      // Pack required number of modules per slink
+
+      if (module%modulesPerSlink == 0) {
+	const int daqOrRoi = 0;
+	const int slink = module/modulesPerSlink;
+        if (debug) {
+          msg() << "Treating crate " << hwCrate
+                << " slink " << slink << endreq;
+        }
+	// Get number of JEM slices and triggered slice offset
+	// for this slink
+	if ( ! slinkSlices(crate, module, modulesPerSlink,
+	                                  timeslices, trigJem)) {
+	  msg(MSG::ERROR) << "Inconsistent number of slices or "
+	                  << "triggered slice offsets in data for crate "
+	                  << hwCrate << " slink " << slink << endreq;
+	  return StatusCode::FAILURE;
+        }
+	timeslicesNew = (m_forceSlices) ? m_forceSlices : timeslices;
+	trigJemNew    = ModifySlices::peak(trigJem, timeslices, timeslicesNew);
+        if (debug) {
+	  msg() << "Data Version/Format: " << m_version
+	        << " " << m_dataFormat << endreq
+                << "Slices/offset: " << timeslices << " " << trigJem;
+	  if (timeslices != timeslicesNew) {
+	    msg() << " modified to " << timeslicesNew << " " << trigJemNew;
+          }
+	  msg() << endreq;
+        }
+        L1CaloUserHeader userHeader;
+        userHeader.setJem(trigJemNew);
+	const uint32_t rodIdJem = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+	                                                        m_subDetector);
+	theROD = m_fea->getRodData(rodIdJem);
+	theROD->push_back(userHeader.header());
+	m_rodStatusMap.insert(make_pair(rodIdJem, m_rodStatus));
+      }
+      if (debug) msg() << "Module " << module << endreq;
+
+      // Create a sub-block for each slice (except Neutral format)
+
+      m_jemBlocks.clear();
+      for (int slice = 0; slice < timeslicesNew; ++slice) {
+        JemSubBlockV1* const subBlock = new JemSubBlockV1();
+	subBlock->setJemHeader(m_version, m_dataFormat, slice,
+	                       hwCrate, module, timeslicesNew);
+        m_jemBlocks.push_back(subBlock);
+	if (neutralFormat) break;
+      }
+
+      // Find jet elements corresponding to each eta/phi pair and fill
+      // sub-blocks
+
+      for (int chan=0; chan < m_channels; ++chan) {
+	double eta = 0.;
+	double phi = 0.;
+	int layer = 0;
+	if (m_jemMaps->mapping(crate, module, chan, eta, phi, layer)) {
+          const LVL1::JetElement* const je = findJetElement(eta, phi);
+	  if (je ) {
+	    std::vector<int> emData;
+	    std::vector<int> hadData;
+	    std::vector<int> emErrors;
+	    std::vector<int> hadErrors;
+	    ModifySlices::data(je->emEnergyVec(),  emData,    timeslicesNew);
+	    ModifySlices::data(je->hadEnergyVec(), hadData,   timeslicesNew);
+	    ModifySlices::data(je->emErrorVec(),   emErrors,  timeslicesNew);
+	    ModifySlices::data(je->hadErrorVec(),  hadErrors, timeslicesNew);
+            for (int slice = 0; slice < timeslicesNew; ++slice) {
+	      const LVL1::DataError emErrBits(emErrors[slice]);
+	      const LVL1::DataError hadErrBits(hadErrors[slice]);
+	      const int index = ( neutralFormat ) ? 0 : slice;
+              JemSubBlockV1* const subBlock = m_jemBlocks[index];
+	      const JemJetElement jetEle(chan, emData[slice], hadData[slice],
+	                  emErrBits.get(LVL1::DataError::Parity),
+	                  hadErrBits.get(LVL1::DataError::Parity),
+			  emErrBits.get(LVL1::DataError::LinkDown) +
+			 (hadErrBits.get(LVL1::DataError::LinkDown) << 1));
+              subBlock->fillJetElement(slice, jetEle);
+	    }
+          }
+        }
+      }
+
+      // Add jet hits and energy subsums
+
+      const LVL1::JEMHits* const hits = findJetHits(crate, module);
+      if (hits) {
+        std::vector<unsigned int> vec;
+	ModifySlices::data(hits->JetHitsVec(), vec, timeslicesNew);
+        for (int slice = 0; slice < timeslicesNew; ++slice) {
+	  const int index = ( neutralFormat ) ? 0 : slice;
+	  JemSubBlockV1* const subBlock = m_jemBlocks[index];
+	  subBlock->setJetHits(slice, vec[slice]);
+        }
+      }
+      const LVL1::JEMEtSums* const et = findEnergySums(crate, module);
+      if (et) {
+        std::vector<unsigned int> exVec;
+        std::vector<unsigned int> eyVec;
+        std::vector<unsigned int> etVec;
+	ModifySlices::data(et->ExVec(), exVec, timeslicesNew);
+	ModifySlices::data(et->EyVec(), eyVec, timeslicesNew);
+	ModifySlices::data(et->EtVec(), etVec, timeslicesNew);
+	for (int slice = 0; slice < timeslicesNew; ++slice) {
+	  const int index = ( neutralFormat ) ? 0 : slice;
+	  JemSubBlockV1* const subBlock = m_jemBlocks[index];
+	  subBlock->setEnergySubsums(slice, exVec[slice], eyVec[slice],
+	                                                  etVec[slice]);
+        }
+      }
+      
+      // Pack and write the sub-blocks
+
+      DataVector<JemSubBlockV1>::const_iterator pos;
+      for (pos = m_jemBlocks.begin(); pos != m_jemBlocks.end(); ++pos) {
+        JemSubBlockV1* const subBlock = *pos;
+	if ( !subBlock->pack()) {
+	  msg(MSG::ERROR) << "JEM sub-block packing failed" << endreq;
+	  return StatusCode::FAILURE;
+	}
+	if (debug) {
+	  msg() << "JEM sub-block data words: "
+	        << subBlock->dataWords() << endreq;
+	}
+	subBlock->write(theROD);
+      }
+    }
+
+    // Append CMMs to last S-Link of the crate
+
+    // Create a sub-block for each slice (except Neutral format)
+
+    m_cmmEnergyBlocks.clear();
+    m_cmmJetBlocks.clear();
+    const int summing = (crate == m_crates - 1) ? CmmSubBlock::SYSTEM
+                                                : CmmSubBlock::CRATE;
+    for (int slice = 0; slice < timeslicesNew; ++slice) {
+      CmmEnergySubBlock* const enBlock = new CmmEnergySubBlock();
+      const int cmmEnergyVersion = 2; // with Missing-ET-Sig
+      enBlock->setCmmHeader(cmmEnergyVersion, m_dataFormat, slice, hwCrate,
+                            summing, CmmSubBlock::CMM_ENERGY,
+			    CmmSubBlock::LEFT, timeslicesNew);
+      m_cmmEnergyBlocks.push_back(enBlock);
+      CmmJetSubBlock* const jetBlock = new CmmJetSubBlock();
+      jetBlock->setCmmHeader(m_version, m_dataFormat, slice, hwCrate,
+                             summing, CmmSubBlock::CMM_JET,
+			     CmmSubBlock::RIGHT, timeslicesNew);
+      m_cmmJetBlocks.push_back(jetBlock);
+      if (neutralFormat) break;
+    }
+
+    // CMM-Energy
+
+    int maxDataID = static_cast<int>(LVL1::CMMEtSums::MAXID);
+    for (int dataID = 0; dataID < maxDataID; ++dataID) {
+      int source = dataID;
+      if (dataID >= m_modules) {
+        if (summing == CmmSubBlock::CRATE && 
+	    dataID != LVL1::CMMEtSums::LOCAL) continue;
+	switch (dataID) {
+	  case LVL1::CMMEtSums::LOCAL:
+	    source = CmmEnergySubBlock::LOCAL;
+	    break;
+	  case LVL1::CMMEtSums::REMOTE:
+	    source = CmmEnergySubBlock::REMOTE;
+	    break;
+	  case LVL1::CMMEtSums::TOTAL:
+	    source = CmmEnergySubBlock::TOTAL;
+	    break;
+	  case LVL1::CMMEtSums::MISSING_ET_MAP:
+	  case LVL1::CMMEtSums::SUM_ET_MAP:
+	  case LVL1::CMMEtSums::MISSING_ET_SIG_MAP:
+	    break;
+          default:
+	    continue;
+        }
+      }
+      const LVL1::CMMEtSums* const sums = findCmmSums(crate, dataID);
+      if ( sums ) {
+        std::vector<unsigned int> ex;
+        std::vector<unsigned int> ey;
+        std::vector<unsigned int> et;
+        std::vector<int> exErr;
+        std::vector<int> eyErr;
+        std::vector<int> etErr;
+	ModifySlices::data(sums->ExVec(), ex, timeslicesNew);
+	ModifySlices::data(sums->EyVec(), ey, timeslicesNew);
+	ModifySlices::data(sums->EtVec(), et, timeslicesNew);
+	ModifySlices::data(sums->ExErrorVec(), exErr, timeslicesNew);
+	ModifySlices::data(sums->EyErrorVec(), eyErr, timeslicesNew);
+	ModifySlices::data(sums->EtErrorVec(), etErr, timeslicesNew);
+	for (int slice = 0; slice < timeslicesNew; ++slice) {
+	  const LVL1::DataError exErrBits(exErr[slice]);
+	  const LVL1::DataError eyErrBits(eyErr[slice]);
+	  const LVL1::DataError etErrBits(etErr[slice]);
+	  int exError = exErrBits.get(LVL1::DataError::Parity);
+	  int eyError = eyErrBits.get(LVL1::DataError::Parity);
+	  int etError = etErrBits.get(LVL1::DataError::Parity);
+	  if (dataID == LVL1::CMMEtSums::LOCAL ||
+	      dataID == LVL1::CMMEtSums::REMOTE ||
+	      dataID == LVL1::CMMEtSums::TOTAL) {
+	    exError = (exError << 1) + exErrBits.get(LVL1::DataError::Overflow);
+	    eyError = (eyError << 1) + eyErrBits.get(LVL1::DataError::Overflow);
+	    etError = (etError << 1) + etErrBits.get(LVL1::DataError::Overflow);
+	  }
+	  const int index = ( neutralFormat ) ? 0 : slice;
+	  CmmEnergySubBlock* const subBlock = m_cmmEnergyBlocks[index];
+	  if (dataID == LVL1::CMMEtSums::MISSING_ET_MAP) {
+	    subBlock->setMissingEtHits(slice, et[slice]); 
+          } else if (dataID == LVL1::CMMEtSums::SUM_ET_MAP) {
+	    subBlock->setSumEtHits(slice, et[slice]); 
+	  } else if (dataID == LVL1::CMMEtSums::MISSING_ET_SIG_MAP) {
+	    subBlock->setMissingEtSigHits(slice, et[slice]);
+          } else {
+	    subBlock->setSubsums(slice, source,
+	                         ex[slice], ey[slice], et[slice],
+	                         exError, eyError, etError);
+          }
+        }
+      }
+    }
+    DataVector<CmmEnergySubBlock>::const_iterator pos;
+    pos = m_cmmEnergyBlocks.begin();
+    for (; pos != m_cmmEnergyBlocks.end(); ++pos) {
+      CmmEnergySubBlock* const subBlock = *pos;
+      if ( !subBlock->pack()) {
+        msg(MSG::ERROR) << "CMM-Energy sub-block packing failed" << endreq;
+	return StatusCode::FAILURE;
+      }
+      if (debug) {
+	msg() << "CMM-Energy sub-block data words: "
+	      << subBlock->dataWords() << endreq;
+      }
+      subBlock->write(theROD);
+    }
+
+    // CMM-Jet
+
+    maxDataID = static_cast<int>(LVL1::CMMJetHits::MAXID);
+    for (int dataID = 0; dataID < maxDataID; ++dataID) {
+      int source = dataID;
+      if (dataID >= m_modules) {
+        if (summing == CmmSubBlock::CRATE && 
+	    dataID != LVL1::CMMJetHits::LOCAL_MAIN &&
+	    dataID != LVL1::CMMJetHits::LOCAL_FORWARD) continue;
+	switch (dataID) {
+	  case LVL1::CMMJetHits::LOCAL_MAIN:
+	    source = CmmJetSubBlock::LOCAL_MAIN;
+	    break;
+	  case LVL1::CMMJetHits::REMOTE_MAIN:
+	    source = CmmJetSubBlock::REMOTE_MAIN;
+	    break;
+	  case LVL1::CMMJetHits::TOTAL_MAIN:
+	    source = CmmJetSubBlock::TOTAL_MAIN;
+	    break;
+	  case LVL1::CMMJetHits::LOCAL_FORWARD:
+	    source = CmmJetSubBlock::LOCAL_FORWARD;
+	    break;
+	  case LVL1::CMMJetHits::REMOTE_FORWARD:
+	    source = CmmJetSubBlock::REMOTE_FORWARD;
+	    break;
+	  case LVL1::CMMJetHits::TOTAL_FORWARD:
+	    source = CmmJetSubBlock::TOTAL_FORWARD;
+	    break;
+	  case LVL1::CMMJetHits::ET_MAP:
+	    break;
+          default:
+	    continue;
+        }
+      }
+      const LVL1::CMMJetHits* const ch = findCmmHits(crate, dataID);
+      if ( ch ) {
+        std::vector<unsigned int> hits;
+        std::vector<int> errs;
+	ModifySlices::data(ch->HitsVec(),  hits, timeslicesNew);
+	ModifySlices::data(ch->ErrorVec(), errs, timeslicesNew);
+	for (int slice = 0; slice < timeslicesNew; ++slice) {
+	  const LVL1::DataError errBits(errs[slice]);
+	  const int index = ( neutralFormat ) ? 0 : slice;
+	  CmmJetSubBlock* const subBlock = m_cmmJetBlocks[index];
+	  if (dataID == LVL1::CMMJetHits::ET_MAP) {
+	    subBlock->setJetEtMap(slice, hits[slice]);
+          } else {
+	    subBlock->setJetHits(slice, source, hits[slice],
+	                         errBits.get(LVL1::DataError::Parity));
+          }
+        }
+      }
+    }
+    DataVector<CmmJetSubBlock>::const_iterator jos;
+    jos = m_cmmJetBlocks.begin();
+    for (; jos != m_cmmJetBlocks.end(); ++jos) {
+      CmmJetSubBlock* const subBlock = *jos;
+      if ( !subBlock->pack()) {
+        msg(MSG::ERROR) << "CMM-Jet sub-block packing failed" << endreq;
+	return StatusCode::FAILURE;
+      }
+      if (debug) {
+	msg() << "CMM-Jet sub-block data words: "
+	      << subBlock->dataWords() << endreq;
+      }
+      subBlock->write(theROD);
+    }
+  }
+
+  // Fill the raw event
+
+  m_fea->fill(re, msg());
+
+  // Set ROD status words
+
+  //L1CaloRodStatus::setStatus(re, m_rodStatusMap, m_srcIdMap);
+
+  return StatusCode::SUCCESS;
+}
+
+// Return reference to vector with all possible Source Identifiers
+
+const std::vector<uint32_t>& JepByteStreamV1Tool::sourceIDs(
+                                                const std::string& sgKey)
+{
+  // Check if overlap jet element channels wanted
+  const std::string flag("Overlap");
+  const std::string::size_type pos = sgKey.find(flag);
+  m_coreOverlap =
+   (pos == std::string::npos || pos != sgKey.length() - flag.length()) ? 0 : 1;
+
+  if (m_sourceIDs.empty()) {
+    const int maxCrates = m_crates + m_crateOffsetHw;
+    const int maxSlinks = m_srcIdMap->maxSlinks();
+    for (int hwCrate = m_crateOffsetHw; hwCrate < maxCrates; ++hwCrate) {
+      for (int slink = 0; slink < maxSlinks; ++slink) {
+        const int daqOrRoi = 0;
+        const uint32_t rodId = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+                                                             m_subDetector);
+        const uint32_t robId = m_srcIdMap->getRobID(rodId);
+        m_sourceIDs.push_back(robId);
+      }
+    }
+  }
+  return m_sourceIDs;
+}
+
+// Convert bytestream to given container type
+
+StatusCode JepByteStreamV1Tool::convertBs(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            const CollectionType collection)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Loop over ROB fragments
+
+  int robCount = 0;
+  std::set<uint32_t> dupCheck;
+  ROBIterator rob    = robFrags.begin();
+  ROBIterator robEnd = robFrags.end();
+  for (; rob != robEnd; ++rob) {
+
+    if (debug) {
+      ++robCount;
+      msg() << "Treating ROB fragment " << robCount << endreq;
+    }
+
+    // Skip fragments with ROB status errors
+
+    uint32_t robid = (*rob)->source_id();
+    if ((*rob)->nstatus() > 0) {
+      ROBPointer robData;
+      (*rob)->status(robData);
+      if (*robData != 0) {
+        m_errorTool->robError(robid, *robData);
+	if (debug) msg() << "ROB status error - skipping fragment" << endreq;
+	continue;
+      }
+    }
+
+    // Skip duplicate fragments
+
+    if (!dupCheck.insert(robid).second) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_DUPLICATE_ROB);
+      if (debug) msg() << "Skipping duplicate ROB fragment" << endreq;
+      continue;
+    }
+
+    // Unpack ROD data (slinks)
+
+    RODPointer payloadBeg;
+    RODPointer payload;
+    RODPointer payloadEnd;
+    (*rob)->rod_data(payloadBeg);
+    payloadEnd = payloadBeg + (*rob)->rod_ndata();
+    payload = payloadBeg;
+    if (payload == payloadEnd) {
+      if (debug) msg() << "ROB fragment empty" << endreq;
+      continue;
+    }
+
+    // Check identifier
+    const uint32_t sourceID = (*rob)->rod_source_id();
+    if (m_srcIdMap->getRobID(sourceID) != robid           ||
+        m_srcIdMap->subDet(sourceID)   != m_subDetector   ||
+	m_srcIdMap->daqOrRoi(sourceID) != 0               ||
+	m_srcIdMap->slink(sourceID)    >= m_slinks        ||
+	m_srcIdMap->crate(sourceID)    <  m_crateOffsetHw ||
+	m_srcIdMap->crate(sourceID)    >= m_crateOffsetHw + m_crates) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_ROD_ID);
+      if (debug) {
+        msg() << "Wrong source identifier in data: ROD "
+              << MSG::hex << sourceID << "  ROB " << robid
+	      << MSG::dec << endreq;
+      }
+      continue;
+    }
+
+    // Check minor version
+    const int minorVersion = (*rob)->rod_version() & 0xffff;
+    if (minorVersion > m_srcIdMap->minorVersionPreLS1()) {
+      if (debug) msg() << "Skipping post-LS1 data" << endreq;
+      continue;
+    }
+    const int rodCrate = m_srcIdMap->crate(sourceID);
+    if (debug) {
+      msg() << "Treating crate " << rodCrate 
+            << " slink " << m_srcIdMap->slink(sourceID) << endreq;
+    }
+
+    // First word should be User Header
+    if ( !L1CaloUserHeader::isValid(*payload) ) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
+      if (debug) msg() << "Invalid or missing user header" << endreq;
+      continue;
+    }
+    L1CaloUserHeader userHeader(*payload);
+    userHeader.setVersion(minorVersion);
+    const int headerWords = userHeader.words();
+    if (headerWords != 1) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
+      if (debug) msg() << "Unexpected number of user header words: "
+                       << headerWords << endreq;
+      continue;
+    }
+    for (int i = 0; i < headerWords; ++i) ++payload;
+    // triggered slice offsets
+    int trigJem = userHeader.jem();
+    int trigCmm = userHeader.jepCmm();
+    if (debug) {
+      msg() << "Minor format version number: " << MSG::hex
+            << minorVersion << MSG::dec << endreq
+            << "JEM triggered slice offset: " << trigJem << endreq
+            << "CMM triggered slice offset: " << trigCmm << endreq;
+    }
+    if (trigJem != trigCmm) {
+      const int newTrig = (trigJem > trigCmm) ? trigJem : trigCmm;
+      trigJem = newTrig;
+      trigCmm = newTrig;
+      if (debug) msg() << "Changed both offsets to " << newTrig << endreq;
+    }
+
+    // Loop over sub-blocks
+
+    m_rodErr = L1CaloSubBlock::ERROR_NONE;
+    while (payload != payloadEnd) {
+      
+      if (L1CaloSubBlock::wordType(*payload) != L1CaloSubBlock::HEADER) {
+        if (debug) msg() << "Unexpected data sequence" << endreq;
+	m_rodErr = L1CaloSubBlock::ERROR_MISSING_HEADER;
+	break;
+      }
+      if (CmmSubBlock::cmmBlock(*payload)) {
+        // CMMs
+	if (CmmSubBlock::cmmType(*payload) == CmmSubBlock::CMM_JET) {
+	  m_cmmJetSubBlock->clear();
+          payload = m_cmmJetSubBlock->read(payload, payloadEnd);
+	  if (m_cmmJetSubBlock->crate() != rodCrate) {
+	    if (debug) msg() << "Inconsistent crate number in ROD source ID"
+	                     << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	    break;
+          }
+	  if (collection == CMM_HITS) {
+	    decodeCmmJet(m_cmmJetSubBlock, trigCmm);
+	    if (m_rodErr != L1CaloSubBlock::ERROR_NONE) {
+	      if (debug) msg() << "decodeCmmJet failed" << endreq;
+	      break;
+	    }
+          }
+        } else if (CmmSubBlock::cmmType(*payload) == CmmSubBlock::CMM_ENERGY) {
+	  m_cmmEnergySubBlock->clear();
+	  payload = m_cmmEnergySubBlock->read(payload, payloadEnd);
+	  if (m_cmmEnergySubBlock->crate() != rodCrate) {
+	    if (debug) msg() << "Inconsistent crate number in ROD source ID"
+	                     << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	    break;
+          }
+	  if (collection == CMM_SUMS) {
+	    decodeCmmEnergy(m_cmmEnergySubBlock, trigCmm);
+	    if (m_rodErr != L1CaloSubBlock::ERROR_NONE) {
+	      if (debug) msg() << "decodeCmmEnergy failed" << endreq;
+	      break;
+	    }
+          }
+	} else {
+	  if (debug) msg() << "Invalid CMM type in module field" << endreq;
+	  m_rodErr = L1CaloSubBlock::ERROR_MODULE_NUMBER;
+	  break;
+        }
+      } else {
+        // JEM
+	m_jemSubBlock->clear();
+        payload = m_jemSubBlock->read(payload, payloadEnd);
+	if (m_jemSubBlock->crate() != rodCrate) {
+	  if (debug) msg() << "Inconsistent crate number in ROD source ID"
+	                   << endreq;
+	  m_rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	  break;
+        }
+	if (collection == JET_ELEMENTS || collection == JET_HITS ||
+	                                  collection == ENERGY_SUMS) {
+	  decodeJem(m_jemSubBlock, trigJem, collection);
+	  if (m_rodErr != L1CaloSubBlock::ERROR_NONE) {
+	    if (debug) msg() << "decodeJem failed" << endreq;
+	    break;
+	  }
+        }
+      }
+    }
+    if (m_rodErr != L1CaloSubBlock::ERROR_NONE)
+                                       m_errorTool->rodError(robid, m_rodErr);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Unpack CMM-Energy sub-block
+
+void JepByteStreamV1Tool::decodeCmmEnergy(CmmEnergySubBlock* subBlock,
+                                                                 int trigCmm)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  const int hwCrate    = subBlock->crate();
+  const int module     = subBlock->cmmPosition();
+  const int firmware   = subBlock->cmmFirmware();
+  const int summing    = subBlock->cmmSumming();
+  const int timeslices = subBlock->timeslices();
+  const int sliceNum   = subBlock->slice();
+  if (debug) {
+    msg() << "CMM-Energy: Crate " << hwCrate
+          << "  Module "          << module
+	  << "  Firmware "        << firmware
+	  << "  Summing "         << summing
+          << "  Total slices "    << timeslices
+          << "  Slice "           << sliceNum    << endreq;
+  }
+  if (timeslices <= trigCmm) {
+    if (debug) msg() << "Triggered CMM slice from header "
+                     << "inconsistent with number of slices: "
+                     << trigCmm << ", " << timeslices << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  if (timeslices <= sliceNum) {
+    if (debug) msg() << "Total slices inconsistent with slice number: "
+                     << timeslices << ", " << sliceNum << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  // Unpack sub-block
+  if (subBlock->dataWords() && !subBlock->unpack()) {
+    if (debug) {
+      std::string errMsg(subBlock->unpackErrorMsg());
+      msg() << "CMM-Energy sub-block unpacking failed: " << errMsg << endreq;
+    }
+    m_rodErr = subBlock->unpackErrorCode();
+    return;
+  }
+
+  // Retrieve required data
+
+  const bool neutralFormat = subBlock->format() == L1CaloSubBlock::NEUTRAL;
+  const int crate    = hwCrate - m_crateOffsetHw;
+  const int swCrate  = crate   + m_crateOffsetSw;
+  const int maxSid   = static_cast<int>(CmmEnergySubBlock::MAX_SOURCE_ID);
+  LVL1::DataError derr;
+  derr.set(LVL1::DataError::SubStatusWord, subBlock->subStatus());
+  const int ssError = derr.error();
+  const int sliceBeg = ( neutralFormat ) ? 0          : sliceNum;
+  const int sliceEnd = ( neutralFormat ) ? timeslices : sliceNum + 1;
+  for (int slice = sliceBeg; slice < sliceEnd; ++slice) {
+
+    // Energy sums
+
+    for (int source = 0; source < maxSid; ++source) {
+      int dataID = source;
+      if (source >= m_modules) {
+        if (summing == CmmSubBlock::CRATE &&
+	    source != CmmEnergySubBlock::LOCAL) continue;
+	switch (source) {
+	  case CmmEnergySubBlock::LOCAL:
+	    dataID = LVL1::CMMEtSums::LOCAL;
+	    break;
+          case CmmEnergySubBlock::REMOTE:
+	    dataID = LVL1::CMMEtSums::REMOTE;
+	    break;
+          case CmmEnergySubBlock::TOTAL:
+	    dataID = LVL1::CMMEtSums::TOTAL;
+	    break;
+          default:
+	    continue;
+        }
+      }
+      const unsigned int ex = subBlock->ex(slice, source);
+      const unsigned int ey = subBlock->ey(slice, source);
+      const unsigned int et = subBlock->et(slice, source);
+      int exErr = subBlock->exError(slice, source);
+      int eyErr = subBlock->eyError(slice, source);
+      int etErr = subBlock->etError(slice, source);
+      LVL1::DataError exErrBits(ssError);
+      LVL1::DataError eyErrBits(ssError);
+      LVL1::DataError etErrBits(ssError);
+      if (dataID == LVL1::CMMEtSums::LOCAL ||
+          dataID == LVL1::CMMEtSums::REMOTE ||
+	  dataID == LVL1::CMMEtSums::TOTAL) {
+        exErrBits.set(LVL1::DataError::Overflow, exErr);
+	exErrBits.set(LVL1::DataError::Parity,   exErr >> 1);
+        eyErrBits.set(LVL1::DataError::Overflow, eyErr);
+	eyErrBits.set(LVL1::DataError::Parity,   eyErr >> 1);
+        etErrBits.set(LVL1::DataError::Overflow, etErr);
+	etErrBits.set(LVL1::DataError::Parity,   etErr >> 1);
+      } else {
+        exErrBits.set(LVL1::DataError::Parity, exErr);
+        eyErrBits.set(LVL1::DataError::Parity, eyErr);
+        etErrBits.set(LVL1::DataError::Parity, etErr);
+      }
+      exErr = exErrBits.error();
+      eyErr = eyErrBits.error();
+      etErr = etErrBits.error();
+      if (ex || ey || et || exErr || eyErr || etErr) {
+        LVL1::CMMEtSums* sums = findCmmSums(crate, dataID);
+	if ( ! sums ) {   // create new CMM energy sums
+	  m_exVec.assign(timeslices, 0);
+	  m_eyVec.assign(timeslices, 0);
+	  m_etVec.assign(timeslices, 0);
+	  m_exErrVec.assign(timeslices, 0);
+	  m_eyErrVec.assign(timeslices, 0);
+	  m_etErrVec.assign(timeslices, 0);
+	  m_exVec[slice] = ex;
+	  m_eyVec[slice] = ey;
+	  m_etVec[slice] = et;
+	  m_exErrVec[slice] = exErr;
+	  m_eyErrVec[slice] = eyErr;
+	  m_etErrVec[slice] = etErr;
+	  sums = new LVL1::CMMEtSums(swCrate, dataID, m_etVec, m_exVec, m_eyVec,
+				     m_etErrVec, m_exErrVec, m_eyErrVec, trigCmm);
+          const int key = crate*100 + dataID;
+	  m_cmmEtMap.insert(std::make_pair(key, sums));
+	  m_cmmEtCollection->push_back(sums);
+        } else {
+	  m_exVec = sums->ExVec();
+	  m_eyVec = sums->EyVec();
+	  m_etVec = sums->EtVec();
+	  m_exErrVec = sums->ExErrorVec();
+	  m_eyErrVec = sums->EyErrorVec();
+	  m_etErrVec = sums->EtErrorVec();
+	  const int nsl = m_exVec.size();
+	  if (timeslices != nsl) {
+	    if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                     << endreq;
+            m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    return;
+          }
+	  if (m_exVec[slice] != 0 || m_eyVec[slice] != 0 || m_etVec[slice] != 0 ||
+	      m_exErrVec[slice] != 0 || m_eyErrVec[slice] != 0 ||
+              m_etErrVec[slice] != 0) {
+            if (debug) msg() << "Duplicate data for slice " << slice << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    return;
+          }
+	  m_exVec[slice] = ex;
+	  m_eyVec[slice] = ey;
+	  m_etVec[slice] = et;
+	  m_exErrVec[slice] = exErr;
+	  m_eyErrVec[slice] = eyErr;
+	  m_etErrVec[slice] = etErr;
+	  sums->addEx(m_exVec, m_exErrVec);
+	  sums->addEy(m_eyVec, m_eyErrVec);
+	  sums->addEt(m_etVec, m_etErrVec);
+        }
+      }
+    }
+
+    // Hit maps - store as Et
+
+    if (summing == CmmSubBlock::SYSTEM) {
+      const unsigned int missEt = subBlock->missingEtHits(slice);
+      if ( missEt || ssError ) {
+        const int dataID = LVL1::CMMEtSums::MISSING_ET_MAP;
+        LVL1::CMMEtSums* map = findCmmSums(crate, dataID);
+        if ( ! map ) {
+	  m_etVec.assign(timeslices, 0);
+	  m_etErrVec.assign(timeslices, 0);
+	  m_etVec[slice]    = missEt;
+	  m_etErrVec[slice] = ssError;
+	  map = new LVL1::CMMEtSums(swCrate, dataID,
+	                            m_etVec, m_etVec, m_etVec,
+	  			    m_etErrVec, m_etErrVec, m_etErrVec, trigCmm);
+          const int key = crate*100 + dataID;
+	  m_cmmEtMap.insert(std::make_pair(key, map));
+	  m_cmmEtCollection->push_back(map);
+        } else {
+          m_etVec    = map->EtVec();
+          m_etErrVec = map->EtErrorVec();
+	  const int nsl = m_etVec.size();
+	  if (timeslices != nsl) {
+	    if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                     << endreq;
+            m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    return;
+          }
+	  if (m_etVec[slice] != 0 || m_etErrVec[slice] != 0) {
+	    if (debug) msg() << "Duplicate data for slice " << slice << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    return;
+          }
+	  m_etVec[slice]     = missEt;
+	  m_etErrVec[slice]  = ssError;
+	  map->addEx(m_etVec, m_etErrVec);
+	  map->addEy(m_etVec, m_etErrVec);
+	  map->addEt(m_etVec, m_etErrVec);
+        }
+      }
+      const unsigned int sumEt = subBlock->sumEtHits(slice);
+      if ( sumEt || ssError ) {
+        const int dataID = LVL1::CMMEtSums::SUM_ET_MAP;
+        LVL1::CMMEtSums* map = findCmmSums(crate, dataID);
+        if ( ! map ) {
+          m_etVec.assign(timeslices, 0);
+          m_etErrVec.assign(timeslices, 0);
+	  m_etVec[slice]    = sumEt;
+	  m_etErrVec[slice] = ssError;
+	  map = new LVL1::CMMEtSums(swCrate, dataID,
+	                            m_etVec, m_etVec, m_etVec,
+				    m_etErrVec, m_etErrVec, m_etErrVec, trigCmm);
+          const int key = crate*100 + dataID;
+	  m_cmmEtMap.insert(std::make_pair(key, map));
+	  m_cmmEtCollection->push_back(map);
+        } else {
+          m_etVec    = map->EtVec();
+          m_etErrVec = map->EtErrorVec();
+	  const int nsl = m_etVec.size();
+	  if (timeslices != nsl) {
+	    if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                     << endreq;
+            m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    return;
+          }
+	  if (m_etVec[slice] != 0 || m_etErrVec[slice] != 0) {
+	    if (debug) msg() << "Duplicate data for slice " << slice << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    return;
+          }
+	  m_etVec[slice]    = sumEt;
+	  m_etErrVec[slice] = ssError;
+	  map->addEx(m_etVec, m_etErrVec);
+	  map->addEy(m_etVec, m_etErrVec);
+	  map->addEt(m_etVec, m_etErrVec);
+        }
+      }
+      if (subBlock->version() > 1) {
+        const unsigned int missEtSig = subBlock->missingEtSigHits(slice);
+        if ( missEtSig || ssError ) {
+          const int dataID = LVL1::CMMEtSums::MISSING_ET_SIG_MAP;
+          LVL1::CMMEtSums* map = findCmmSums(crate, dataID);
+          if ( ! map ) {
+            m_etVec.assign(timeslices, 0);
+            m_etErrVec.assign(timeslices, 0);
+	    m_etVec[slice]    = missEtSig;
+	    m_etErrVec[slice] = ssError;
+	    map = new LVL1::CMMEtSums(swCrate, dataID,
+	                              m_etVec, m_etVec, m_etVec,
+	    			      m_etErrVec, m_etErrVec, m_etErrVec, trigCmm);
+            const int key = crate*100 + dataID;
+	    m_cmmEtMap.insert(std::make_pair(key, map));
+	    m_cmmEtCollection->push_back(map);
+          } else {
+            m_etVec    = map->EtVec();
+            m_etErrVec = map->EtErrorVec();
+	    const int nsl = m_etVec.size();
+	    if (timeslices != nsl) {
+	      if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                       << endreq;
+              m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	      return;
+            }
+	    if (m_etVec[slice] != 0 || m_etErrVec[slice] != 0) {
+	      if (debug) msg() << "Duplicate data for slice "
+	                       << slice << endreq;
+	      m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	      return;
+            }
+	    m_etVec[slice]    = missEtSig;
+	    m_etErrVec[slice] = ssError;
+	    map->addEx(m_etVec, m_etErrVec);
+	    map->addEy(m_etVec, m_etErrVec);
+	    map->addEt(m_etVec, m_etErrVec);
+          }
+        }
+      }
+    }
+  }
+  
+  return;
+}
+
+// Unpack CMM-Jet sub-block
+
+void JepByteStreamV1Tool::decodeCmmJet(CmmJetSubBlock* subBlock, int trigCmm)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  const int hwCrate    = subBlock->crate();
+  const int module     = subBlock->cmmPosition();
+  const int firmware   = subBlock->cmmFirmware();
+  const int summing    = subBlock->cmmSumming();
+  const int timeslices = subBlock->timeslices();
+  const int sliceNum   = subBlock->slice();
+  if (debug) {
+    msg() << "CMM-Jet: Crate " << hwCrate
+          << "  Module "       << module
+	  << "  Firmware "     << firmware
+	  << "  Summing "      << summing
+          << "  Total slices " << timeslices
+          << "  Slice "        << sliceNum    << endreq;
+  }
+  if (timeslices <= trigCmm) {
+    if (debug) msg() << "Triggered CMM slice from header "
+                     << "inconsistent with number of slices: "
+                     << trigCmm << ", " << timeslices << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  if (timeslices <= sliceNum) {
+    if (debug) msg() << "Total slices inconsistent with slice number: "
+                     << timeslices << ", " << sliceNum << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  // Unpack sub-block
+  if (subBlock->dataWords() && !subBlock->unpack()) {
+    if (debug) {
+      std::string errMsg(subBlock->unpackErrorMsg());
+      msg() << "CMM-Jet sub-block unpacking failed: " << errMsg << endreq;
+    }
+    m_rodErr = subBlock->unpackErrorCode();
+    return;
+  }
+
+  // Retrieve required data
+
+  const bool neutralFormat = subBlock->format() == L1CaloSubBlock::NEUTRAL;
+  const int crate    = hwCrate - m_crateOffsetHw;
+  const int swCrate  = crate   + m_crateOffsetSw;
+  const int maxSid   = static_cast<int>(CmmJetSubBlock::MAX_SOURCE_ID);
+  LVL1::DataError derr;
+  derr.set(LVL1::DataError::SubStatusWord, subBlock->subStatus());
+  const int ssError = derr.error();
+  const int sliceBeg = ( neutralFormat ) ? 0          : sliceNum;
+  const int sliceEnd = ( neutralFormat ) ? timeslices : sliceNum + 1;
+  for (int slice = sliceBeg; slice < sliceEnd; ++slice) {
+
+    // Jet hit counts
+
+    for (int source = 0; source < maxSid; ++source) {
+      int dataID = source;
+      if (source >= m_modules) {
+        if (summing == CmmSubBlock::CRATE &&
+	    source != CmmJetSubBlock::LOCAL_MAIN     &&
+	    source != CmmJetSubBlock::LOCAL_FORWARD) continue;
+	switch (source) {
+	  case CmmJetSubBlock::LOCAL_MAIN:
+	    dataID = LVL1::CMMJetHits::LOCAL_MAIN;
+	    break;
+	  case CmmJetSubBlock::REMOTE_MAIN:
+	    dataID = LVL1::CMMJetHits::REMOTE_MAIN;
+	    break;
+	  case CmmJetSubBlock::TOTAL_MAIN:
+	    dataID = LVL1::CMMJetHits::TOTAL_MAIN;
+	    break;
+	  case CmmJetSubBlock::LOCAL_FORWARD:
+	    dataID = LVL1::CMMJetHits::LOCAL_FORWARD;
+	    break;
+	  case CmmJetSubBlock::REMOTE_FORWARD:
+	    dataID = LVL1::CMMJetHits::REMOTE_FORWARD;
+	    break;
+	  case CmmJetSubBlock::TOTAL_FORWARD:
+	    dataID = LVL1::CMMJetHits::TOTAL_FORWARD;
+	    break;
+          default:
+	    continue;
+        }
+      }
+      const unsigned int hits = subBlock->jetHits(slice, source);
+      LVL1::DataError errBits(ssError);
+      errBits.set(LVL1::DataError::Parity,
+                        subBlock->jetHitsError(slice, source));
+      const int err = errBits.error();
+      if (hits || err) {
+        LVL1::CMMJetHits* jh = findCmmHits(crate, dataID);
+	if ( ! jh ) {   // create new CMM hits
+	  m_hitsVec.assign(timeslices, 0);
+	  m_errVec.assign(timeslices, 0);
+	  m_hitsVec[slice] = hits;
+	  m_errVec[slice]  = err;
+	  jh = new LVL1::CMMJetHits(swCrate, dataID, m_hitsVec, m_errVec, trigCmm);
+          const int key = crate*100 + dataID;
+	  m_cmmHitsMap.insert(std::make_pair(key, jh));
+	  m_cmmHitCollection->push_back(jh);
+        } else {
+	  m_hitsVec = jh->HitsVec();
+	  m_errVec  = jh->ErrorVec();
+	  const int nsl = m_hitsVec.size();
+	  if (timeslices != nsl) {
+	    if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                     << endreq;
+            m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    return;
+          }
+	  if (m_hitsVec[slice] != 0 || m_errVec[slice] != 0) {
+	    if (debug) msg() << "Duplicate data for slice " << slice << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    return;
+          }
+	  m_hitsVec[slice] = hits;
+	  m_errVec[slice]  = err;
+	  jh->addHits(m_hitsVec, m_errVec);
+        }
+      }
+    }
+
+    // Hit map - store as hits
+
+    if (summing == CmmSubBlock::SYSTEM) {
+      const unsigned int etMap = subBlock->jetEtMap(slice);
+      if ( etMap || ssError ) {
+        const int dataID = LVL1::CMMJetHits::ET_MAP;
+        LVL1::CMMJetHits* map = findCmmHits(crate, dataID);
+        if ( ! map ) {
+          m_hitsVec.assign(timeslices, 0);
+          m_errVec.assign(timeslices, 0);
+	  m_hitsVec[slice] = etMap;
+	  m_errVec[slice]  = ssError;
+	  map = new LVL1::CMMJetHits(swCrate, dataID, m_hitsVec, m_errVec, trigCmm);
+          const int key = crate*100 + dataID;
+	  m_cmmHitsMap.insert(std::make_pair(key, map));
+	  m_cmmHitCollection->push_back(map);
+        } else {
+          m_hitsVec = map->HitsVec();
+          m_errVec  = map->ErrorVec();
+	  const int nsl = m_hitsVec.size();
+	  if (timeslices != nsl) {
+	    if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                     << endreq;
+            m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    return;
+          }
+	  if (m_hitsVec[slice] != 0 || m_errVec[slice] != 0) {
+	    if (debug) msg() << "Duplicate data for slice " << slice << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    return;
+          }
+	  m_hitsVec[slice] = etMap;
+	  m_errVec[slice]  = ssError;
+	  map->addHits(m_hitsVec, m_errVec);
+        }
+      }
+    }
+  }
+  
+  return;
+}
+
+// Unpack JEM sub-block
+
+void JepByteStreamV1Tool::decodeJem(JemSubBlockV1* subBlock, int trigJem,
+                                        const CollectionType collection)
+{
+  const bool debug   = msgLvl(MSG::DEBUG);
+  const bool verbose = msgLvl(MSG::VERBOSE);
+  if (debug) msg(MSG::DEBUG);
+
+  const int hwCrate    = subBlock->crate();
+  const int module     = subBlock->module();
+  const int timeslices = subBlock->timeslices();
+  const int sliceNum   = subBlock->slice();
+  if (debug) {
+    msg() << "JEM: Crate "     << hwCrate
+          << "  Module "       << module
+          << "  Total slices " << timeslices
+          << "  Slice "        << sliceNum    << endreq;
+  }
+  if (timeslices <= trigJem) {
+    if (debug) msg() << "Triggered JEM slice from header "
+                     << "inconsistent with number of slices: "
+                     << trigJem << ", " << timeslices << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  if (timeslices <= sliceNum) {
+    if (debug) msg() << "Total slices inconsistent with slice number: "
+                     << timeslices << ", " << sliceNum << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  // Unpack sub-block
+  if (subBlock->dataWords() && !subBlock->unpack()) {
+    if (debug) {
+      std::string errMsg(subBlock->unpackErrorMsg());
+      msg() << "JEM sub-block unpacking failed: " << errMsg << endreq;
+    }
+    m_rodErr = subBlock->unpackErrorCode();
+    return;
+  }
+
+  // Retrieve required data
+
+  const bool neutralFormat = subBlock->format() == L1CaloSubBlock::NEUTRAL;
+  const int crate    = hwCrate - m_crateOffsetHw;
+  const int swCrate  = crate   + m_crateOffsetSw;
+  LVL1::DataError derr;
+  derr.set(LVL1::DataError::SubStatusWord, subBlock->subStatus());
+  const int ssError = derr.error();
+  std::vector<int> dummy(timeslices);
+  const int sliceBeg = ( neutralFormat ) ? 0          : sliceNum;
+  const int sliceEnd = ( neutralFormat ) ? timeslices : sliceNum + 1;
+  for (int slice = sliceBeg; slice < sliceEnd; ++slice) {
+
+    if (collection == JET_ELEMENTS) {
+
+      // Loop over jet element channels and fill jet elements
+
+      for (int chan = 0; chan < m_channels; ++chan) {
+        const JemJetElement jetEle(subBlock->jetElement(slice, chan));
+        if (jetEle.data() || ssError) {
+	  double eta = 0.;
+	  double phi = 0.;
+	  int layer = 0;
+	  if (m_jemMaps->mapping(crate, module, chan, eta, phi, layer)) {
+	    if (layer == m_coreOverlap) {
+	      LVL1::JetElement* je = findJetElement(eta, phi);
+	      if ( ! je ) {   // create new jet element
+	        const unsigned int key = m_elementKey->jeKey(phi, eta);
+	        je = new LVL1::JetElement(phi, eta, dummy, dummy, key,
+	                                  dummy, dummy, dummy, trigJem);
+	        m_jeMap.insert(std::make_pair(key, je));
+	        m_jeCollection->push_back(je);
+              } else {
+	        const std::vector<int>& emEnergy(je->emEnergyVec());
+		const std::vector<int>& hadEnergy(je->hadEnergyVec());
+		const std::vector<int>& emError(je->emErrorVec());
+		const std::vector<int>& hadError(je->hadErrorVec());
+		const int nsl = emEnergy.size();
+		if (timeslices != nsl) {
+		  if (debug) {
+		    msg() << "Inconsistent number of slices in sub-blocks"
+		          << endreq;
+                  }
+		  m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+		  return;
+                }
+		if (emEnergy[slice] != 0 || hadEnergy[slice] != 0 ||
+		    emError[slice]  != 0 || hadError[slice]  != 0) {
+                  if (debug) msg() << "Duplicate data for slice "
+		                   << slice << endreq;
+                  m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+		  return;
+                }
+              }
+	      LVL1::DataError emErrBits(ssError);
+	      LVL1::DataError hadErrBits(ssError);
+	      const int linkError = jetEle.linkError();
+	      emErrBits.set(LVL1::DataError::Parity, jetEle.emParity());
+	      emErrBits.set(LVL1::DataError::LinkDown, linkError);
+	      hadErrBits.set(LVL1::DataError::Parity, jetEle.hadParity());
+	      hadErrBits.set(LVL1::DataError::LinkDown, linkError >> 1);
+	      je->addSlice(slice, jetEle.emData(), jetEle.hadData(),
+	                          emErrBits.error(), hadErrBits.error(),
+	           	          linkError);
+	    }
+          } else if (verbose && jetEle.data()) {
+	    msg(MSG::VERBOSE) << "Non-zero data but no channel mapping for channel "
+	                      << chan << endreq;
+	    msg(MSG::DEBUG);
+          }
+        } else if (verbose) {
+	  msg(MSG::VERBOSE) << "No jet element data for channel "
+	                    << chan << " slice " << slice << endreq;
+	  msg(MSG::DEBUG);
+        }
+      }
+    } else if (collection == JET_HITS) {
+
+      // Get jet hits
+
+      const unsigned int hits = subBlock->jetHits(slice);
+      if (hits) {
+        LVL1::JEMHits* jh = findJetHits(crate, module);
+	if ( ! jh ) {   // create new jet hits
+	  m_hitsVec.assign(timeslices, 0);
+	  m_hitsVec[slice] = hits;
+	  jh = new LVL1::JEMHits(swCrate, module, m_hitsVec, trigJem);
+	  m_hitsMap.insert(std::make_pair(crate*m_modules+module, jh));
+	  m_hitCollection->push_back(jh);
+        } else {
+	  m_hitsVec = jh->JetHitsVec();
+	  const int nsl = m_hitsVec.size();
+	  if (timeslices != nsl) {
+	    if (debug) {
+	      msg() << "Inconsistent number of slices in sub-blocks"
+	            << endreq;
+	    }
+            m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    return;
+          }
+	  if (m_hitsVec[slice] != 0) {
+	    if (debug) msg() << "Duplicate data for slice "
+	                     << slice << endreq;
+            m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    return;
+          }
+	  m_hitsVec[slice] = hits;
+	  jh->addJetHits(m_hitsVec);
+        }
+      } else if (verbose) {
+        msg(MSG::VERBOSE) << "No jet hits data for crate/module/slice "
+                          << hwCrate << "/" << module << "/" << slice
+			  << endreq;
+	msg(MSG::DEBUG);
+      }
+    } else if (collection == ENERGY_SUMS) {
+
+      // Get energy subsums
+
+      const unsigned int ex = subBlock->ex(slice);
+      const unsigned int ey = subBlock->ey(slice);
+      const unsigned int et = subBlock->et(slice);
+      if (ex | ey | et) {
+	LVL1::JEMEtSums* sums = findEnergySums(crate, module);
+	if ( ! sums ) {   // create new energy sums
+	  m_exVec.assign(timeslices, 0);
+	  m_eyVec.assign(timeslices, 0);
+	  m_etVec.assign(timeslices, 0);
+	  m_exVec[slice] = ex;
+	  m_eyVec[slice] = ey;
+	  m_etVec[slice] = et;
+	  sums = new LVL1::JEMEtSums(swCrate, module, m_etVec, m_exVec, m_eyVec,
+	                                                            trigJem);
+          m_etMap.insert(std::make_pair(crate*m_modules+module, sums));
+	  m_etCollection->push_back(sums);
+        } else {
+	  m_exVec = sums->ExVec();
+	  m_eyVec = sums->EyVec();
+	  m_etVec = sums->EtVec();
+	  const int nsl = m_exVec.size();
+	  if (timeslices != nsl) {
+	    if (debug) {
+	      msg() << "Inconsistent number of slices in sub-blocks"
+	            << endreq;
+	    }
+            m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    return;
+          }
+	  if (m_exVec[slice] != 0 || m_eyVec[slice] != 0 || m_etVec[slice] != 0) {
+	    if (debug) msg() << "Duplicate data for slice "
+	                     << slice << endreq;
+            m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    return;
+          }
+	  m_exVec[slice] = ex;
+	  m_eyVec[slice] = ey;
+	  m_etVec[slice] = et;
+	  sums->addEx(m_exVec);
+	  sums->addEy(m_eyVec);
+	  sums->addEt(m_etVec);
+        }
+      } else if (verbose) {
+        msg(MSG::VERBOSE) << "No energy sums data for crate/module/slice "
+                          << hwCrate << "/" << module << "/" << slice
+    			  << endreq;
+	msg(MSG::DEBUG);
+      }
+    }
+  }
+  return;
+}
+
+// Find a jet element given eta, phi
+
+LVL1::JetElement* JepByteStreamV1Tool::findJetElement(const double eta,
+                                                    const double phi)
+{
+  LVL1::JetElement* tt = 0;
+  const unsigned int key = m_elementKey->jeKey(phi, eta);
+  JetElementMap::const_iterator mapIter;
+  mapIter = m_jeMap.find(key);
+  if (mapIter != m_jeMap.end()) tt = mapIter->second;
+  return tt;
+}
+
+// Find jet hits for given crate, module
+
+LVL1::JEMHits* JepByteStreamV1Tool::findJetHits(const int crate,
+                                              const int module)
+{
+  LVL1::JEMHits* hits = 0;
+  JetHitsMap::const_iterator mapIter;
+  mapIter = m_hitsMap.find(crate*m_modules + module);
+  if (mapIter != m_hitsMap.end()) hits = mapIter->second;
+  return hits;
+}
+
+// Find energy sums for given crate, module
+
+LVL1::JEMEtSums* JepByteStreamV1Tool::findEnergySums(const int crate,
+                                                   const int module)
+{
+  LVL1::JEMEtSums* sums = 0;
+  EnergySumsMap::const_iterator mapIter;
+  mapIter = m_etMap.find(crate*m_modules + module);
+  if (mapIter != m_etMap.end()) sums = mapIter->second;
+  return sums;
+}
+
+// Find CMM hits for given crate, dataID
+
+LVL1::CMMJetHits* JepByteStreamV1Tool::findCmmHits(const int crate,
+                                                 const int dataID)
+{
+  LVL1::CMMJetHits* hits = 0;
+  CmmHitsMap::const_iterator mapIter;
+  mapIter = m_cmmHitsMap.find(crate*100 + dataID);
+  if (mapIter != m_cmmHitsMap.end()) hits = mapIter->second;
+  return hits;
+}
+
+// Find CMM energy sums for given crate, module, dataID
+
+LVL1::CMMEtSums* JepByteStreamV1Tool::findCmmSums(const int crate,
+                                                const int dataID)
+{
+  LVL1::CMMEtSums* sums = 0;
+  CmmSumsMap::const_iterator mapIter;
+  mapIter = m_cmmEtMap.find(crate*100 + dataID);
+  if (mapIter != m_cmmEtMap.end()) sums = mapIter->second;
+  return sums;
+}
+
+// Set up jet element map
+
+void JepByteStreamV1Tool::setupJeMap(const JetElementCollection*
+                                                        const jeCollection)
+{
+  m_jeMap.clear();
+  if (jeCollection) {
+    JetElementCollection::const_iterator pos  = jeCollection->begin();
+    JetElementCollection::const_iterator pose = jeCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::JetElement* const je = *pos;
+      const unsigned int key = m_elementKey->jeKey(je->phi(), je->eta());
+      m_jeMap.insert(std::make_pair(key, je));
+    }
+  }
+}
+
+// Set up jet hits map
+
+void JepByteStreamV1Tool::setupHitsMap(const JetHitsCollection*
+                                                        const hitCollection)
+{
+  m_hitsMap.clear();
+  if (hitCollection) {
+    JetHitsCollection::const_iterator pos  = hitCollection->begin();
+    JetHitsCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::JEMHits* const hits = *pos;
+      const int crate = hits->crate() - m_crateOffsetSw;
+      const int key   = m_modules * crate + hits->module();
+      m_hitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Set up energy sums map
+
+void JepByteStreamV1Tool::setupEtMap(const EnergySumsCollection*
+                                                         const etCollection)
+{
+  m_etMap.clear();
+  if (etCollection) {
+    EnergySumsCollection::const_iterator pos  = etCollection->begin();
+    EnergySumsCollection::const_iterator pose = etCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::JEMEtSums* const sums = *pos;
+      const int crate = sums->crate() - m_crateOffsetSw;
+      const int key   = m_modules * crate + sums->module();
+      m_etMap.insert(std::make_pair(key, sums));
+    }
+  }
+}
+
+// Set up CMM hits map
+
+void JepByteStreamV1Tool::setupCmmHitsMap(const CmmHitsCollection*
+                                                         const hitCollection)
+{
+  m_cmmHitsMap.clear();
+  if (hitCollection) {
+    CmmHitsCollection::const_iterator pos  = hitCollection->begin();
+    CmmHitsCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::CMMJetHits* const hits = *pos;
+      const int crate = hits->crate() - m_crateOffsetSw;
+      const int key   = crate*100 + hits->dataID();
+      m_cmmHitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Set up CMM energy sums map
+
+void JepByteStreamV1Tool::setupCmmEtMap(const CmmSumsCollection*
+                                                         const etCollection)
+{
+  m_cmmEtMap.clear();
+  if (etCollection) {
+    CmmSumsCollection::const_iterator pos  = etCollection->begin();
+    CmmSumsCollection::const_iterator pose = etCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::CMMEtSums* const sums = *pos;
+      const int crate = sums->crate() - m_crateOffsetSw;
+      const int key   = crate*100 + sums->dataID();
+      m_cmmEtMap.insert(std::make_pair(key, sums));
+    }
+  }
+}
+
+// Get number of slices and triggered slice offset for next slink
+
+bool JepByteStreamV1Tool::slinkSlices(const int crate, const int module,
+                  const int modulesPerSlink, int& timeslices, int& trigJem)
+{
+  int slices = -1;
+  int trigJ  = m_dfltSlices/2;
+  for (int mod = module; mod < module + modulesPerSlink; ++mod) {
+    for (int chan = 0; chan < m_channels; ++chan) {
+      double eta = 0.;
+      double phi = 0.;
+      int layer = 0;
+      if ( !m_jemMaps->mapping(crate, mod, chan, eta, phi, layer)) continue;
+      const LVL1::JetElement* const je = findJetElement(eta, phi);
+      if ( !je ) continue;
+      const int numdat = 5;
+      std::vector<int> sums(numdat);
+      std::vector<int> sizes(numdat);
+      sums[0] = std::accumulate((je->emEnergyVec()).begin(),
+                                (je->emEnergyVec()).end(), 0);
+      sums[1] = std::accumulate((je->hadEnergyVec()).begin(),
+                                (je->hadEnergyVec()).end(), 0);
+      sums[2] = std::accumulate((je->emErrorVec()).begin(),
+                                (je->emErrorVec()).end(), 0);
+      sums[3] = std::accumulate((je->hadErrorVec()).begin(),
+                                (je->hadErrorVec()).end(), 0);
+      sums[4] = std::accumulate((je->linkErrorVec()).begin(),
+                                (je->linkErrorVec()).end(), 0);
+      sizes[0] = (je->emEnergyVec()).size();
+      sizes[1] = (je->hadEnergyVec()).size();
+      sizes[2] = (je->emErrorVec()).size();
+      sizes[3] = (je->hadErrorVec()).size();
+      sizes[4] = (je->linkErrorVec()).size();
+      const int peak = je->peak();
+      for (int i = 0; i < numdat; ++i) {
+        if (sums[i] == 0) continue;
+        if (slices < 0) {
+	  slices = sizes[i];
+	  trigJ  = peak;
+	} else if (slices != sizes[i] || trigJ != peak) return false;
+      }
+    }
+    const LVL1::JEMHits* const hits = findJetHits(crate, mod);
+    if (hits) {
+      const unsigned int sum = std::accumulate((hits->JetHitsVec()).begin(),
+                                               (hits->JetHitsVec()).end(), 0);
+      if (sum) {
+        const int size = (hits->JetHitsVec()).size();
+	const int peak = hits->peak();
+        if (slices < 0) {
+	  slices = size;
+	  trigJ  = peak;
+        } else if (slices != size || trigJ != peak) return false;
+      }
+    }
+    const LVL1::JEMEtSums* const et = findEnergySums(crate, mod);
+    if (et) {
+      const int numdat = 3;
+      std::vector<unsigned int> sums(numdat);
+      std::vector<int> sizes(numdat);
+      sums[0] = std::accumulate((et->ExVec()).begin(),
+                                (et->ExVec()).end(), 0);
+      sums[1] = std::accumulate((et->EyVec()).begin(),
+                                (et->EyVec()).end(), 0);
+      sums[2] = std::accumulate((et->EtVec()).begin(),
+                                (et->EtVec()).end(), 0);
+      sizes[0] = (et->ExVec()).size();
+      sizes[1] = (et->EyVec()).size();
+      sizes[2] = (et->EtVec()).size();
+      const int peak = et->peak();
+      for (int i = 0; i < numdat; ++i) {
+        if (sums[i] == 0) continue;
+	if (slices < 0) {
+	  slices = sizes[i];
+	  trigJ  = peak;
+        } else if (slices != sizes[i] || trigJ != peak) return false;
+      }
+    }
+  }
+  // CMM last slink of crate
+  if (module/modulesPerSlink == m_slinks - 1) {
+    const int maxDataID1 = LVL1::CMMJetHits::MAXID;
+    const int maxDataID2 = LVL1::CMMEtSums::MAXID;
+    const int maxDataID  = (maxDataID1 > maxDataID2) ? maxDataID1 : maxDataID2;
+    for (int dataID = 0; dataID < maxDataID; ++dataID) {
+      const int numdat = 6;
+      std::vector<unsigned int> sums(numdat);
+      std::vector<int> sizes(numdat);
+      const LVL1::CMMJetHits* hits = 0;
+      if (dataID < maxDataID1) hits = findCmmHits(crate, dataID);
+      if (hits) {
+        sums[0] = std::accumulate((hits->HitsVec()).begin(),
+                                             (hits->HitsVec()).end(), 0);
+        sums[1] = std::accumulate((hits->ErrorVec()).begin(),
+                                             (hits->ErrorVec()).end(), 0);
+        sizes[0] = (hits->HitsVec()).size();
+        sizes[1] = (hits->ErrorVec()).size();
+        const int peak = hits->peak();
+        for (int i = 0; i < 2; ++i) {
+          if (sums[i] == 0) continue;
+          if (slices < 0) {
+	    slices = sizes[i];
+	    trigJ  = peak;
+          } else if (slices != sizes[i] || trigJ != peak) return false;
+        }
+      }
+      const LVL1::CMMEtSums* et = 0;
+      if (dataID < maxDataID2) et = findCmmSums(crate, dataID);
+      if (et) {
+        sums[0] = std::accumulate((et->ExVec()).begin(),
+  				  (et->ExVec()).end(), 0);
+        sums[1] = std::accumulate((et->EyVec()).begin(),
+                                  (et->EyVec()).end(), 0);
+        sums[2] = std::accumulate((et->EtVec()).begin(),
+                                  (et->EtVec()).end(), 0);
+        sums[3] = std::accumulate((et->ExErrorVec()).begin(),
+                                  (et->ExErrorVec()).end(), 0);
+        sums[4] = std::accumulate((et->EyErrorVec()).begin(),
+                                  (et->EyErrorVec()).end(), 0);
+        sums[5] = std::accumulate((et->EtErrorVec()).begin(),
+                                  (et->EtErrorVec()).end(), 0);
+        sizes[0] = (et->ExVec()).size();
+        sizes[1] = (et->EyVec()).size();
+        sizes[2] = (et->EtVec()).size();
+        sizes[3] = (et->ExErrorVec()).size();
+        sizes[4] = (et->EyErrorVec()).size();
+        sizes[5] = (et->EtErrorVec()).size();
+        const int peak = et->peak();
+        for (int i = 0; i < numdat; ++i) {
+          if (sums[i] == 0) continue;
+	  if (slices < 0) {
+	    slices = sizes[i];
+	    trigJ  = peak;
+          } else if (slices != sizes[i] || trigJ != peak) return false;
+        }
+      }
+    }
+  }
+  if (slices < 0) slices = m_dfltSlices;
+  timeslices = slices;
+  trigJem    = trigJ;
+  return true;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV1Tool.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV1Tool.h
new file mode 100755
index 0000000000000000000000000000000000000000..85e61f68d1eba84d6fcaad58f27076768511f1e7
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV1Tool.h
@@ -0,0 +1,244 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEPBYTESTREAMV1TOOL_H
+#define TRIGT1CALOBYTESTREAM_JEPBYTESTREAMV1TOOL_H
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+#include "ByteStreamData/RawEvent.h"
+#include "DataModel/DataVector.h"
+#include "eformat/SourceIdentifier.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class IInterface;
+class InterfaceID;
+class StatusCode;
+
+template <class T> class FullEventAssembler;
+
+namespace LVL1 {
+  class CMMJetHits;
+  class CMMEtSums;
+  class IL1CaloMappingTool;
+  class JEMHits;
+  class JEMEtSums;
+  class JEPBSCollectionV1;
+  class JetElement;
+  class JetElementKey;
+}
+
+namespace LVL1BS {
+
+class CmmEnergySubBlock;
+class CmmJetSubBlock;
+class JemSubBlockV1;
+class L1CaloErrorByteStreamTool;
+class L1CaloSrcIdMap;
+
+/** Tool to perform ROB fragments to jet elements, jet hits and energy sums,
+ *  and JEP container to raw data conversions.
+ *
+ *  Based on ROD document version 1_09h.
+ *
+ *  @author Peter Faulkner
+ */
+
+class JepByteStreamV1Tool : public AthAlgTool {
+
+ public:
+   JepByteStreamV1Tool(const std::string& type, const std::string& name,
+                       const IInterface* parent);
+   virtual ~JepByteStreamV1Tool();
+
+   /// AlgTool InterfaceID
+   static const InterfaceID& interfaceID();
+
+   virtual StatusCode initialize();
+   virtual StatusCode finalize();
+
+   /// Convert ROB fragments to jet elements
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::JetElement>* jeCollection);
+   /// Convert ROB fragments to jet hits
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::JEMHits>* hitCollection);
+   /// Convert ROB fragments to energy sums
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::JEMEtSums>* etCollection);
+   /// Convert ROB fragments to CMM jet hits
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::CMMJetHits>* hitCollection);
+   /// Convert ROB fragments to CMM energy sums
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::CMMEtSums>* etCollection);
+
+   /// Convert JEP Container to bytestream
+   StatusCode convert(const LVL1::JEPBSCollectionV1* jep, RawEventWrite* re);
+
+   /// Return reference to vector with all possible Source Identifiers
+   const std::vector<uint32_t>& sourceIDs(const std::string& sgKey);
+
+ private:
+   enum CollectionType { JET_ELEMENTS, JET_HITS, ENERGY_SUMS,
+                                       CMM_HITS, CMM_SUMS };
+
+   typedef DataVector<LVL1::JetElement>                  JetElementCollection;
+   typedef DataVector<LVL1::JEMHits>                     JetHitsCollection;
+   typedef DataVector<LVL1::JEMEtSums>                   EnergySumsCollection;
+   typedef DataVector<LVL1::CMMJetHits>                  CmmHitsCollection;
+   typedef DataVector<LVL1::CMMEtSums>                   CmmSumsCollection;
+   typedef std::map<unsigned int, LVL1::JetElement*>     JetElementMap;
+   typedef std::map<int, LVL1::JEMHits*>                 JetHitsMap;
+   typedef std::map<int, LVL1::JEMEtSums*>               EnergySumsMap;
+   typedef std::map<int, LVL1::CMMJetHits*>              CmmHitsMap;
+   typedef std::map<int, LVL1::CMMEtSums*>               CmmSumsMap;
+   typedef IROBDataProviderSvc::VROBFRAG::const_iterator ROBIterator;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      ROBPointer;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      RODPointer;
+
+   /// Convert bytestream to given container type
+   StatusCode convertBs(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                        CollectionType collection);
+   /// Unpack CMM-Energy sub-block
+   void decodeCmmEnergy(CmmEnergySubBlock* subBlock, int trigCmm);
+   /// Unpack CMM-Jet sub-block
+   void decodeCmmJet(CmmJetSubBlock* subBlock, int trigCmm);
+   /// Unpack JEM sub-block
+   void decodeJem(JemSubBlockV1* subBlock, int trigJem,
+                                         CollectionType collection);
+
+   /// Find a jet element given eta, phi
+   LVL1::JetElement* findJetElement(double eta, double phi);
+   /// Find jet hits for given crate, module
+   LVL1::JEMHits*    findJetHits(int crate, int module);
+   /// Find energy sums for given crate, module
+   LVL1::JEMEtSums*  findEnergySums(int crate, int module);
+   /// Find CMM hits for given crate, data ID
+   LVL1::CMMJetHits* findCmmHits(int crate, int dataID);
+   /// Find CMM energy sums for given crate, data ID
+   LVL1::CMMEtSums*  findCmmSums(int crate, int dataID);
+
+   /// Set up jet element map
+   void setupJeMap(const JetElementCollection* jeCollection);
+   /// Set up jet hits map
+   void setupHitsMap(const JetHitsCollection* hitCollection);
+   /// Set up energy sums map
+   void setupEtMap(const EnergySumsCollection* enCollection);
+   /// Set up CMM hits map
+   void setupCmmHitsMap(const CmmHitsCollection* hitCollection);
+   /// Set up CMM energy sums map
+   void setupCmmEtMap(const CmmSumsCollection* enCollection);
+
+   /// Get number of slices and triggered slice offset for next slink
+   bool slinkSlices(int crate, int module, int modulesPerSlink,
+                    int& timeslices, int& trigJem);
+
+   /// Channel mapping tool
+   ToolHandle<LVL1::IL1CaloMappingTool> m_jemMaps;
+   /// Error collection tool
+   ToolHandle<LVL1BS::L1CaloErrorByteStreamTool> m_errorTool;
+
+   /// Hardware crate number offset
+   int m_crateOffsetHw;
+   /// Software crate number offset
+   int m_crateOffsetSw;
+   /// Sub_block header version
+   int m_version;
+   /// Data compression format
+   int m_dataFormat;
+   /// Number of channels per module
+   int m_channels;
+   /// Number of crates
+   int m_crates;
+   /// Number of JEM modules per crate
+   int m_modules;
+   /// Number of slinks per crate when writing out bytestream
+   int m_slinks;
+   /// Default number of slices in simulation
+   int m_dfltSlices;
+   /// Force number of slices in bytestream
+   int m_forceSlices;
+   /// Minimum crate number when writing out bytestream
+   int m_crateMin;
+   /// Maximum crate number when writing out bytestream
+   int m_crateMax;
+   /// Jet elements to accept (0=Core, 1=Overlap)
+   int m_coreOverlap;
+   /// Unpacking error code
+   unsigned int m_rodErr;
+   /// ROB source IDs
+   std::vector<uint32_t> m_sourceIDs;
+   /// Sub-detector type
+   eformat::SubDetector m_subDetector;
+   /// Source ID converter
+   L1CaloSrcIdMap* m_srcIdMap;
+   /// Jet element key provider
+   LVL1::JetElementKey* m_elementKey;
+   /// JemSubBlock for unpacking
+   JemSubBlockV1* m_jemSubBlock;
+   /// CmmEnergySubBlock for unpacking
+   CmmEnergySubBlock* m_cmmEnergySubBlock;
+   /// CmmJetSubBlock for unpacking
+   CmmJetSubBlock* m_cmmJetSubBlock;
+   /// Ex vector for unpacking
+   std::vector<unsigned int> m_exVec;
+   /// Ey vector for unpacking
+   std::vector<unsigned int> m_eyVec;
+   /// Et vector for unpacking
+   std::vector<unsigned int> m_etVec;
+   /// Ex error vector for unpacking
+   std::vector<int> m_exErrVec;
+   /// Ex error vector for unpacking
+   std::vector<int> m_eyErrVec;
+   /// Ex error vector for unpacking
+   std::vector<int> m_etErrVec;
+   /// Hits vector for unpacking
+   std::vector<unsigned int> m_hitsVec;
+   /// Error vector for unpacking
+   std::vector<int> m_errVec;
+   /// Vector for current JEM sub-blocks
+   DataVector<JemSubBlockV1> m_jemBlocks;
+   /// Vector for current CMM-Energy sub-blocks
+   DataVector<CmmEnergySubBlock> m_cmmEnergyBlocks;
+   /// Vector for current CMM-Jet sub-blocks
+   DataVector<CmmJetSubBlock> m_cmmJetBlocks;
+   /// Current jet elements collection
+   JetElementCollection* m_jeCollection;
+   /// Current jet hits collection
+   JetHitsCollection*    m_hitCollection;
+   /// Current energy sums collection
+   EnergySumsCollection* m_etCollection;
+   /// Current CMM hits collection
+   CmmHitsCollection*    m_cmmHitCollection;
+   /// Current CMM energy sums collection
+   CmmSumsCollection*    m_cmmEtCollection;
+   /// Jet element map
+   JetElementMap m_jeMap;
+   /// Jet hits map
+   JetHitsMap    m_hitsMap;
+   /// Energy sums map
+   EnergySumsMap m_etMap;
+   /// CMM hits map
+   CmmHitsMap    m_cmmHitsMap;
+   /// CMM energy sums map
+   CmmSumsMap    m_cmmEtMap;
+   /// ROD Status words
+   std::vector<uint32_t>* m_rodStatus;
+   /// ROD status map
+   std::map<uint32_t, std::vector<uint32_t>* > m_rodStatusMap;
+   /// Event assembler
+   FullEventAssembler<L1CaloSrcIdMap>* m_fea;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV2Cnv.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV2Cnv.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..ce5a90a2cac0f0a7fb4013e8db985960510cd8b6
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV2Cnv.cxx
@@ -0,0 +1,115 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IByteStreamEventAccess.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "DataModel/DataVector.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/IRegistry.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "TrigT1CaloEvent/JEPBSCollectionV2.h"
+
+#include "JepByteStreamV2Cnv.h"
+#include "JepByteStreamV2Tool.h"
+
+namespace LVL1BS {
+
+JepByteStreamV2Cnv::JepByteStreamV2Cnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("JepByteStreamV2Cnv"),
+      m_tool("LVL1BS::JepByteStreamV2Tool/JepByteStreamV2Tool"),
+      m_ByteStreamEventAccess("ByteStreamCnvSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+JepByteStreamV2Cnv::~JepByteStreamV2Cnv()
+{
+}
+
+// CLID
+
+const CLID& JepByteStreamV2Cnv::classID()
+{
+  return ClassID_traits<LVL1::JEPBSCollectionV2>::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode JepByteStreamV2Cnv::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << " - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  //Get ByteStreamCnvSvc
+  sc = m_ByteStreamEventAccess.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_ByteStreamEventAccess << endreq;
+    return sc;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_ByteStreamEventAccess << endreq;
+  }
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return StatusCode::FAILURE;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  return StatusCode::SUCCESS;
+}
+
+// createRep should create the bytestream from RDOs.
+
+StatusCode JepByteStreamV2Cnv::createRep( DataObject* pObj,
+                                          IOpaqueAddress*& pAddr )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createRep() called" << endreq;
+
+  RawEventWrite* re = m_ByteStreamEventAccess->getRawEvent();
+
+  LVL1::JEPBSCollectionV2* jep = 0;
+  if( !SG::fromStorable( pObj, jep ) ) {
+    m_log << MSG::ERROR << " Cannot cast to JEPBSCollectionV2" << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = pObj->registry()->name();
+
+  ByteStreamAddress* addr = new ByteStreamAddress( classID(), nm, "" );
+
+  pAddr = addr;
+
+  // Convert to ByteStream
+  return m_tool->convert( jep, re );
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV2Cnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV2Cnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..d10fe52f8a36ed659a8da476d99b6d4beddefcfe
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV2Cnv.h
@@ -0,0 +1,76 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEPBYTESTREAMV2CNV_H
+#define TRIGT1CALOBYTESTREAM_JEPBYTESTREAMV2CNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IByteStreamEventAccess;
+class IOpaqueAddress;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class JepByteStreamV2Tool;
+
+/** ByteStream converter for JEP container post LS1
+ *
+ *  @author Peter Faulkner
+ */
+
+class JepByteStreamV2Cnv: public Converter {
+
+  friend class CnvFactory<JepByteStreamV2Cnv>;
+
+protected:
+
+  JepByteStreamV2Cnv(ISvcLocator* svcloc);
+
+public:
+
+  ~JepByteStreamV2Cnv();
+
+  virtual StatusCode initialize();
+  /// Create ByteStream from JEP Container
+  virtual StatusCode createRep(DataObject* pObj, IOpaqueAddress*& pAddr);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::JepByteStreamV2Tool> m_tool;
+
+  /// Service for writing bytestream
+  ServiceHandle<IByteStreamEventAccess> m_ByteStreamEventAccess;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV2Tool.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV2Tool.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..1036e4f8d146bab2de4369f40076436ac75391b5
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV2Tool.cxx
@@ -0,0 +1,1783 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <numeric>
+#include <set>
+#include <utility>
+
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "ByteStreamCnvSvcBase/FullEventAssembler.h"
+
+#include "TrigT1CaloEvent/CMXJetHits.h"
+#include "TrigT1CaloEvent/CMXJetTob.h"
+#include "TrigT1CaloEvent/CMXEtSums.h"
+#include "TrigT1CaloEvent/JEMEtSums.h"
+#include "TrigT1CaloEvent/JEPBSCollectionV2.h"
+#include "TrigT1CaloEvent/JetElement.h"
+#include "TrigT1CaloUtils/DataError.h"
+#include "TrigT1CaloUtils/JetElementKey.h"
+#include "TrigT1CaloMappingToolInterfaces/IL1CaloMappingTool.h"
+
+#include "CmxEnergySubBlock.h"
+#include "CmxJetSubBlock.h"
+#include "CmxSubBlock.h"
+#include "JemJetElement.h"
+#include "JemSubBlockV2.h"
+#include "L1CaloErrorByteStreamTool.h"
+#include "L1CaloSrcIdMap.h"
+#include "L1CaloSubBlock.h"
+#include "L1CaloUserHeader.h"
+#include "ModifySlices.h"
+
+#include "JepByteStreamV2Tool.h"
+
+namespace LVL1BS {
+
+// Interface ID
+
+static const InterfaceID IID_IJepByteStreamV2Tool("JepByteStreamV2Tool", 1, 1);
+
+const InterfaceID& JepByteStreamV2Tool::interfaceID()
+{
+  return IID_IJepByteStreamV2Tool;
+}
+
+// Constructor
+
+JepByteStreamV2Tool::JepByteStreamV2Tool(const std::string& type,
+                                     const std::string& name,
+				     const IInterface*  parent)
+  : AthAlgTool(type, name, parent),
+    m_jemMaps("LVL1::JemMappingTool/JemMappingTool"),
+    m_errorTool("LVL1BS::L1CaloErrorByteStreamTool/L1CaloErrorByteStreamTool"),
+    m_channels(44), m_crates(2), m_modules(16), m_frames(8), m_locations(4),
+    m_maxTobs(4), m_coreOverlap(0),
+    m_subDetector(eformat::TDAQ_CALO_JET_PROC_DAQ),
+    m_srcIdMap(0), m_elementKey(0),
+    m_jemSubBlock(0), m_cmxEnergySubBlock(0), m_cmxJetSubBlock(0),
+    m_rodStatus(0), m_fea(0)
+{
+  declareInterface<JepByteStreamV2Tool>(this);
+
+  declareProperty("JemMappingTool", m_jemMaps,
+                  "Crate/Module/Channel to Eta/Phi/Layer mapping tool");
+  declareProperty("ErrorTool", m_errorTool,
+                  "Tool to collect errors for monitoring");
+
+  declareProperty("CrateOffsetHw",  m_crateOffsetHw = 12,
+                  "Offset of JEP crate numbers in bytestream");
+  declareProperty("CrateOffsetSw",  m_crateOffsetSw = 0,
+                  "Offset of JEP crate numbers in RDOs");
+  declareProperty("SlinksPerCrate", m_slinks        = 4,
+                  "The number of S-Links per crate");
+
+  // Properties for reading bytestream only
+  declareProperty("ROBSourceIDs",       m_sourceIDs,
+                  "ROB fragment source identifiers");
+
+  // Properties for writing bytestream only
+  declareProperty("DataVersion",    m_version     = 2,                      //<<== CHECK
+                  "Format version number in sub-block header");
+  declareProperty("DataFormat",     m_dataFormat  = 1,
+                  "Format identifier (0-1) in sub-block header");
+  declareProperty("SimulSlices",    m_dfltSlices  = 1,
+                  "The number of slices in the simulation");
+  declareProperty("ForceSlices",    m_forceSlices = 0,
+                  "If >0, the number of slices in bytestream");
+  declareProperty("CrateMin",       m_crateMin = 0,
+                  "Minimum crate number, allows partial output");
+  declareProperty("CrateMax",       m_crateMax = m_crates-1,
+                  "Maximum crate number, allows partial output");
+
+}
+
+// Destructor
+
+JepByteStreamV2Tool::~JepByteStreamV2Tool()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode JepByteStreamV2Tool::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = m_jemMaps.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_jemMaps << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_jemMaps << endreq;
+
+  sc = m_errorTool.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_errorTool << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_errorTool << endreq;
+
+  m_srcIdMap          = new L1CaloSrcIdMap();
+  m_elementKey        = new LVL1::JetElementKey();
+  m_jemSubBlock       = new JemSubBlockV2();
+  m_cmxEnergySubBlock = new CmxEnergySubBlock();
+  m_cmxJetSubBlock    = new CmxJetSubBlock();
+  m_rodStatus         = new std::vector<uint32_t>(2);
+  m_fea               = new FullEventAssembler<L1CaloSrcIdMap>();
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode JepByteStreamV2Tool::finalize()
+{
+  delete m_fea;
+  delete m_rodStatus;
+  delete m_cmxJetSubBlock;
+  delete m_cmxEnergySubBlock;
+  delete m_jemSubBlock;
+  delete m_elementKey;
+  delete m_srcIdMap;
+  return StatusCode::SUCCESS;
+}
+
+// Conversion bytestream to jet elements
+
+StatusCode JepByteStreamV2Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::JetElement>* const jeCollection)
+{
+  m_jeCollection = jeCollection;
+  m_jeMap.clear();
+  return convertBs(robFrags, JET_ELEMENTS);
+}
+
+// Conversion bytestream to energy sums
+
+StatusCode JepByteStreamV2Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::JEMEtSums>* const etCollection)
+{
+  m_etCollection = etCollection;
+  m_etMap.clear();
+  return convertBs(robFrags, ENERGY_SUMS);
+}
+
+// Conversion bytestream to CMX TOBs
+
+StatusCode JepByteStreamV2Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::CMXJetTob>* const tobCollection)
+{
+  m_cmxTobCollection = tobCollection;
+  m_cmxTobMap.clear();
+  return convertBs(robFrags, CMX_TOBS);
+}
+
+// Conversion bytestream to CMX hits
+
+StatusCode JepByteStreamV2Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::CMXJetHits>* const hitCollection)
+{
+  m_cmxHitCollection = hitCollection;
+  m_cmxHitsMap.clear();
+  return convertBs(robFrags, CMX_HITS);
+}
+
+// Conversion bytestream to CMX energy sums
+
+StatusCode JepByteStreamV2Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::CMXEtSums>* const etCollection)
+{
+  m_cmxEtCollection = etCollection;
+  m_cmxEtMap.clear();
+  return convertBs(robFrags, CMX_SUMS);
+}
+
+// Conversion of JEP container to bytestream
+
+StatusCode JepByteStreamV2Tool::convert(const LVL1::JEPBSCollectionV2* const jep,
+                                            RawEventWrite* const re)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Clear the event assembler
+
+  m_fea->clear();
+  const uint16_t minorVersion = m_srcIdMap->minorVersion();
+  m_fea->setRodMinorVersion(minorVersion);
+  m_rodStatusMap.clear();
+
+  // Pointer to ROD data vector
+
+  FullEventAssembler<L1CaloSrcIdMap>::RODDATA* theROD = 0;
+
+  // Set up the container maps
+
+  setupJeMap(jep->JetElements());
+  setupEtMap(jep->EnergySums());
+  setupCmxTobMap(jep->CmxTobs());
+  setupCmxHitsMap(jep->CmxHits());
+  setupCmxEtMap(jep->CmxSums());
+
+  // Loop over data
+
+  const bool neutralFormat = m_dataFormat == L1CaloSubBlock::NEUTRAL;
+  const int modulesPerSlink = m_modules / m_slinks;
+  int timeslices       = 1;
+  int trigJem          = 0;
+  int timeslicesNew    = 1;
+  int trigJemNew       = 0;
+  for (int crate = m_crateMin; crate <= m_crateMax; ++crate) {
+    const int hwCrate = crate + m_crateOffsetHw;
+
+    for (int module=0; module < m_modules; ++module) {
+
+      // Pack required number of modules per slink
+
+      if (module%modulesPerSlink == 0) {
+	const int daqOrRoi = 0;
+	const int slink = module/modulesPerSlink;
+        if (debug) {
+          msg() << "Treating crate " << hwCrate
+                << " slink " << slink << endreq;
+        }
+	// Get number of JEM slices and triggered slice offset
+	// for this slink
+	if ( ! slinkSlices(crate, module, modulesPerSlink,
+	                                  timeslices, trigJem)) {
+	  msg(MSG::ERROR) << "Inconsistent number of slices or "
+	                  << "triggered slice offsets in data for crate "
+	                  << hwCrate << " slink " << slink << endreq;
+	  return StatusCode::FAILURE;
+        }
+	timeslicesNew = (m_forceSlices) ? m_forceSlices : timeslices;
+	trigJemNew    = ModifySlices::peak(trigJem, timeslices, timeslicesNew);
+        if (debug) {
+	  msg() << "Data Version/Format: " << m_version
+	        << " " << m_dataFormat << endreq
+                << "Slices/offset: " << timeslices << " " << trigJem;
+	  if (timeslices != timeslicesNew) {
+	    msg() << " modified to " << timeslicesNew << " " << trigJemNew;
+          }
+	  msg() << endreq;
+        }
+        L1CaloUserHeader userHeader;
+        userHeader.setJem(trigJemNew);
+	const uint32_t rodIdJem = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+	                                                        m_subDetector);
+	theROD = m_fea->getRodData(rodIdJem);
+	theROD->push_back(userHeader.header());
+	m_rodStatusMap.insert(make_pair(rodIdJem, m_rodStatus));
+      }
+      if (debug) msg() << "Module " << module << endreq;
+
+      // Create a sub-block for each slice (except Neutral format)
+
+      m_jemBlocks.clear();
+      for (int slice = 0; slice < timeslicesNew; ++slice) {
+        JemSubBlockV2* const subBlock = new JemSubBlockV2();
+	subBlock->setJemHeader(m_version, m_dataFormat, slice,
+	                       hwCrate, module, timeslicesNew);
+        m_jemBlocks.push_back(subBlock);
+	if (neutralFormat) break;
+      }
+
+      // Find jet elements corresponding to each eta/phi pair and fill
+      // sub-blocks
+
+      for (int chan=0; chan < m_channels; ++chan) {
+	double eta = 0.;
+	double phi = 0.;
+	int layer = 0;
+	if (m_jemMaps->mapping(crate, module, chan, eta, phi, layer)) {
+          const LVL1::JetElement* const je = findJetElement(eta, phi);
+	  if (je ) {
+	    std::vector<int> emData;
+	    std::vector<int> hadData;
+	    std::vector<int> emErrors;
+	    std::vector<int> hadErrors;
+	    ModifySlices::data(je->emEnergyVec(),  emData,    timeslicesNew);
+	    ModifySlices::data(je->hadEnergyVec(), hadData,   timeslicesNew);
+	    ModifySlices::data(je->emErrorVec(),   emErrors,  timeslicesNew);
+	    ModifySlices::data(je->hadErrorVec(),  hadErrors, timeslicesNew);
+            for (int slice = 0; slice < timeslicesNew; ++slice) {
+	      const LVL1::DataError emErrBits(emErrors[slice]);
+	      const LVL1::DataError hadErrBits(hadErrors[slice]);
+	      const int index = ( neutralFormat ) ? 0 : slice;
+              JemSubBlockV2* const subBlock = m_jemBlocks[index];
+	      const JemJetElement jetEle(chan, emData[slice], hadData[slice],
+	                  emErrBits.get(LVL1::DataError::Parity),
+	                  hadErrBits.get(LVL1::DataError::Parity),
+			  emErrBits.get(LVL1::DataError::LinkDown) +
+			 (hadErrBits.get(LVL1::DataError::LinkDown) << 1));
+              subBlock->fillJetElement(slice, jetEle);
+	      if ((emErrBits.error() >> LVL1::DataError::GLinkParity)) {
+	        int gLinkParity   = emErrBits.get(LVL1::DataError::GLinkParity);
+		int gLinkProtocol = emErrBits.get(LVL1::DataError::GLinkProtocol);
+		int bCNMismatch   = emErrBits.get(LVL1::DataError::BCNMismatch);
+		int fIFOOverflow  = emErrBits.get(LVL1::DataError::FIFOOverflow);
+		int moduleError   = emErrBits.get(LVL1::DataError::ModuleError);
+		int gLinkDown     = emErrBits.get(LVL1::DataError::GLinkDown);
+		int gLinkTimeout  = emErrBits.get(LVL1::DataError::GLinkTimeout);
+		uint32_t failingBCN = emErrBits.get(LVL1::DataError::FailingBCN);
+		subBlock->setStatus(failingBCN, gLinkTimeout, gLinkDown,
+		                    moduleError, fIFOOverflow, bCNMismatch,
+				    gLinkProtocol, gLinkParity);
+	      }
+	    }
+          }
+        }
+      }
+
+      // Add energy subsums
+
+      const LVL1::JEMEtSums* const et = findEnergySums(crate, module);
+      if (et) {
+        std::vector<unsigned int> exVec;
+        std::vector<unsigned int> eyVec;
+        std::vector<unsigned int> etVec;
+	ModifySlices::data(et->ExVec(), exVec, timeslicesNew);
+	ModifySlices::data(et->EyVec(), eyVec, timeslicesNew);
+	ModifySlices::data(et->EtVec(), etVec, timeslicesNew);
+	for (int slice = 0; slice < timeslicesNew; ++slice) {
+	  const int index = ( neutralFormat ) ? 0 : slice;
+	  JemSubBlockV2* const subBlock = m_jemBlocks[index];
+	  subBlock->setEnergySubsums(slice, exVec[slice], eyVec[slice],
+	                                                  etVec[slice]);
+        }
+      }
+      
+      // Pack and write the sub-blocks
+
+      DataVector<JemSubBlockV2>::const_iterator pos;
+      for (pos = m_jemBlocks.begin(); pos != m_jemBlocks.end(); ++pos) {
+        JemSubBlockV2* const subBlock = *pos;
+	if ( !subBlock->pack()) {
+	  msg(MSG::ERROR) << "JEM sub-block packing failed" << endreq;
+	  return StatusCode::FAILURE;
+	}
+	if (debug) {
+	  msg() << "JEM sub-block data words: "
+	        << subBlock->dataWords() << endreq;
+	}
+	subBlock->write(theROD);
+      }
+    }
+
+    // Append CMXs to last S-Link of the crate
+
+    // Create a sub-block for each slice (except Neutral format)
+
+    m_cmxEnergyBlocks.clear();
+    m_cmxJetBlocks.clear();
+    const int summing = (crate == m_crates - 1) ? CmxSubBlock::SYSTEM
+                                                : CmxSubBlock::CRATE;
+    for (int slice = 0; slice < timeslicesNew; ++slice) {
+      CmxEnergySubBlock* const enBlock = new CmxEnergySubBlock();
+      const int cmxEnergyVersion = 3;                             // <<== CHECK  Make jo property for each sub-block?
+      enBlock->setCmxHeader(cmxEnergyVersion, m_dataFormat, slice, hwCrate,
+                            summing, CmxSubBlock::CMX_ENERGY,
+			    CmxSubBlock::LEFT, timeslicesNew);
+      m_cmxEnergyBlocks.push_back(enBlock);
+      CmxJetSubBlock* const jetBlock = new CmxJetSubBlock();
+      jetBlock->setCmxHeader(m_version, m_dataFormat, slice, hwCrate,
+                             summing, CmxSubBlock::CMX_JET,
+			     CmxSubBlock::RIGHT, timeslicesNew);
+      m_cmxJetBlocks.push_back(jetBlock);
+      if (neutralFormat) break;
+    }
+
+    // CMX-Energy
+
+    int maxSource = static_cast<int>(LVL1::CMXEtSums::MAX_SOURCE);
+    for (int source = 0; source < maxSource; ++source) {
+      if (source >= m_modules) {
+        if (summing == CmxSubBlock::CRATE && 
+	    source != LVL1::CMXEtSums::LOCAL_STANDARD &&
+	    source != LVL1::CMXEtSums::LOCAL_RESTRICTED) continue;
+      }
+      const LVL1::CMXEtSums* const sums = findCmxSums(crate, source);
+      if ( sums ) {
+        std::vector<unsigned int> ex;
+        std::vector<unsigned int> ey;
+        std::vector<unsigned int> et;
+        std::vector<int> exErr;
+        std::vector<int> eyErr;
+        std::vector<int> etErr;
+	ModifySlices::data(sums->ExVec(), ex, timeslicesNew);
+	ModifySlices::data(sums->EyVec(), ey, timeslicesNew);
+	ModifySlices::data(sums->EtVec(), et, timeslicesNew);
+	ModifySlices::data(sums->ExErrorVec(), exErr, timeslicesNew);
+	ModifySlices::data(sums->EyErrorVec(), eyErr, timeslicesNew);
+	ModifySlices::data(sums->EtErrorVec(), etErr, timeslicesNew);
+	for (int slice = 0; slice < timeslicesNew; ++slice) {
+	  const LVL1::DataError exErrBits(exErr[slice]);
+	  const LVL1::DataError eyErrBits(eyErr[slice]);
+	  const LVL1::DataError etErrBits(etErr[slice]);
+	  int exError = exErrBits.get(LVL1::DataError::Parity) << 1;
+	  int eyError = eyErrBits.get(LVL1::DataError::Parity) << 1;
+	  int etError = etErrBits.get(LVL1::DataError::Parity) << 1;
+	  if (source >= m_modules) {
+	    exError += exErrBits.get(LVL1::DataError::Overflow);
+	    eyError += eyErrBits.get(LVL1::DataError::Overflow);
+	    etError += etErrBits.get(LVL1::DataError::Overflow);
+	  }
+	  const int index = ( neutralFormat ) ? 0 : slice;
+	  CmxEnergySubBlock* const subBlock = m_cmxEnergyBlocks[index];
+	  if (source < m_modules) {
+	    subBlock->setSubsums(slice, source,
+	                         ex[slice], ey[slice], et[slice],
+				 exError, eyError, etError);
+          } else {
+	    CmxEnergySubBlock::SourceType srcType = CmxEnergySubBlock::MAX_SOURCE_TYPE;
+	    CmxEnergySubBlock::SumType    sumType = CmxEnergySubBlock::MAX_SUM_TYPE;
+	    CmxEnergySubBlock::HitsType   hitType = CmxEnergySubBlock::MAX_HITS_TYPE;
+	    energySubBlockTypes(source, srcType, sumType, hitType);
+	    if (srcType != CmxEnergySubBlock::MAX_SOURCE_TYPE) {
+	      subBlock->setSubsums(slice, srcType, sumType,
+	                           ex[slice], ey[slice], et[slice],
+				   exError, eyError, etError);
+            } else if (hitType != CmxEnergySubBlock::MAX_HITS_TYPE) {
+	      subBlock->setEtHits(slice, hitType, sumType, et[slice]);
+            }
+          }
+        }
+      }
+    }
+    DataVector<CmxEnergySubBlock>::const_iterator pos;
+    pos = m_cmxEnergyBlocks.begin();
+    for (; pos != m_cmxEnergyBlocks.end(); ++pos) {
+      CmxEnergySubBlock* const subBlock = *pos;
+      if ( !subBlock->pack()) {
+        msg(MSG::ERROR) << "CMX-Energy sub-block packing failed" << endreq;
+	return StatusCode::FAILURE;
+      }
+      if (debug) {
+	msg() << "CMX-Energy sub-block data words: "
+	      << subBlock->dataWords() << endreq;
+      }
+      subBlock->write(theROD);
+    }
+
+    // CMX-Jet TOBs
+
+    for (int jem = 0; jem < m_modules; ++jem) {
+      for (int frame = 0; frame < m_frames; ++frame) {
+        for (int loc = 0; loc < m_locations; ++loc) {
+	  const int key = tobKey(crate, jem, frame, loc);
+          const LVL1::CMXJetTob* const ct = findCmxTob(key);
+          if ( ct ) {
+            std::vector<int> energyLarge;
+            std::vector<int> energySmall;
+            std::vector<int> error;
+            std::vector<unsigned int> presence;
+            ModifySlices::data(ct->energyLgVec(),    energyLarge, timeslicesNew);
+            ModifySlices::data(ct->energySmVec(),    energySmall, timeslicesNew);
+            ModifySlices::data(ct->errorVec(),       error,       timeslicesNew);
+            ModifySlices::data(ct->presenceMapVec(), presence,    timeslicesNew);
+            for (int slice = 0; slice < timeslicesNew; ++slice) {
+              const LVL1::DataError errBits(error[slice]);
+	      int err0 = errBits.get(LVL1::DataError::Parity);
+              int err1 = errBits.get(LVL1::DataError::ParityPhase0);
+              err1 |= (errBits.get(LVL1::DataError::ParityPhase1))<<1;
+              err1 |= (errBits.get(LVL1::DataError::ParityPhase2))<<2;
+              err1 |= (errBits.get(LVL1::DataError::ParityPhase3))<<3;
+              const int index = ( neutralFormat ) ? 0 : slice;
+              CmxJetSubBlock* const subBlock = m_cmxJetBlocks[index];
+              subBlock->setTob(slice, jem, frame, loc, energyLarge[slice],
+                                                  energySmall[slice], err0);
+	      subBlock->setParityBits(slice, jem, err1); // for neutral format
+              subBlock->setPresenceMap(slice, jem, presence[slice]);
+            }
+          }
+        }
+      }
+    }
+
+    // CMX-Jet Hits
+
+    maxSource = static_cast<int>(LVL1::CMXJetHits::MAX_SOURCE);
+    for (int source = 0; source < maxSource; ++source) {
+      if (summing == CmxSubBlock::CRATE && 
+          (source == LVL1::CMXJetHits::REMOTE_MAIN    ||
+	   source == LVL1::CMXJetHits::TOTAL_MAIN     ||
+	   source == LVL1::CMXJetHits::REMOTE_FORWARD ||
+	   source == LVL1::CMXJetHits::TOTAL_FORWARD)) continue;
+      int sourceId = jetSubBlockSourceId(source);
+      if (sourceId == CmxJetSubBlock::MAX_SOURCE_ID) continue;
+      const LVL1::CMXJetHits* const ch = findCmxHits(crate, source);
+      if ( ch ) {
+        std::vector<unsigned int> hits0;
+        std::vector<unsigned int> hits1;
+        std::vector<int> err0;
+        std::vector<int> err1;
+	ModifySlices::data(ch->hitsVec0(),  hits0, timeslicesNew);
+	ModifySlices::data(ch->hitsVec1(),  hits1, timeslicesNew);
+	ModifySlices::data(ch->errorVec0(), err0,  timeslicesNew);
+	ModifySlices::data(ch->errorVec1(), err1,  timeslicesNew);
+	for (int slice = 0; slice < timeslicesNew; ++slice) {
+	  int error = 0;
+	  if (source != LVL1::CMXJetHits::TOPO_CHECKSUM &&
+	      source != LVL1::CMXJetHits::TOPO_OCCUPANCY_MAP &&
+	      source != LVL1::CMXJetHits::TOPO_OCCUPANCY_COUNTS) {
+	    const LVL1::DataError errBits0(err0[slice]);
+	    const LVL1::DataError errBits1(err1[slice]);
+	    error = (errBits0.get(LVL1::DataError::Overflow) |
+	             errBits1.get(LVL1::DataError::Overflow)) << 2;
+	    if (source == LVL1::CMXJetHits::REMOTE_MAIN ||
+	        source == LVL1::CMXJetHits::REMOTE_FORWARD) {
+	      error += (errBits0.get(LVL1::DataError::Parity) +
+	               (errBits1.get(LVL1::DataError::Parity) << 1));
+	    }
+	  }
+	  const int index = ( neutralFormat ) ? 0 : slice;
+	  CmxJetSubBlock* const subBlock = m_cmxJetBlocks[index];
+	  subBlock->setHits(slice, sourceId, 0, hits0[slice], error);
+	  if (source != LVL1::CMXJetHits::TOPO_CHECKSUM &&
+	      source != LVL1::CMXJetHits::TOPO_OCCUPANCY_MAP) {
+	    subBlock->setHits(slice, sourceId, 1, hits1[slice], error);
+          }
+        }
+      }
+    }
+    DataVector<CmxJetSubBlock>::const_iterator jos;
+    jos = m_cmxJetBlocks.begin();
+    for (; jos != m_cmxJetBlocks.end(); ++jos) {
+      CmxJetSubBlock* const subBlock = *jos;
+      if ( !subBlock->pack()) {
+        msg(MSG::ERROR) << "CMX-Jet sub-block packing failed" << endreq;
+	return StatusCode::FAILURE;
+      }
+      if (debug) {
+	msg() << "CMX-Jet sub-block data words: "
+	      << subBlock->dataWords() << endreq;
+      }
+      subBlock->write(theROD);
+    }
+  }
+
+  // Fill the raw event
+
+  m_fea->fill(re, msg());
+
+  // Set ROD status words
+
+  //L1CaloRodStatus::setStatus(re, m_rodStatusMap, m_srcIdMap);
+
+  return StatusCode::SUCCESS;
+}
+
+// Return reference to vector with all possible Source Identifiers
+
+const std::vector<uint32_t>& JepByteStreamV2Tool::sourceIDs(
+                                                const std::string& sgKey)
+{
+  // Check if overlap jet element channels wanted
+  const std::string flag("Overlap");
+  const std::string::size_type pos = sgKey.find(flag);
+  m_coreOverlap =
+   (pos == std::string::npos || pos != sgKey.length() - flag.length()) ? 0 : 1;
+
+  if (m_sourceIDs.empty()) {
+    const int maxCrates = m_crates + m_crateOffsetHw;
+    const int maxSlinks = m_srcIdMap->maxSlinks();
+    for (int hwCrate = m_crateOffsetHw; hwCrate < maxCrates; ++hwCrate) {
+      for (int slink = 0; slink < maxSlinks; ++slink) {
+        const int daqOrRoi = 0;
+        const uint32_t rodId = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+                                                             m_subDetector);
+        const uint32_t robId = m_srcIdMap->getRobID(rodId);
+        m_sourceIDs.push_back(robId);
+      }
+    }
+  }
+  return m_sourceIDs;
+}
+
+// Convert bytestream to given container type
+
+StatusCode JepByteStreamV2Tool::convertBs(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            const CollectionType collection)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Loop over ROB fragments
+
+  int robCount = 0;
+  std::set<uint32_t> dupCheck;
+  ROBIterator rob    = robFrags.begin();
+  ROBIterator robEnd = robFrags.end();
+  for (; rob != robEnd; ++rob) {
+
+    if (debug) {
+      ++robCount;
+      msg() << "Treating ROB fragment " << robCount << endreq;
+    }
+
+    // Skip fragments with ROB status errors
+
+    uint32_t robid = (*rob)->source_id();
+    if ((*rob)->nstatus() > 0) {
+      ROBPointer robData;
+      (*rob)->status(robData);
+      if (*robData != 0) {
+        m_errorTool->robError(robid, *robData);
+	if (debug) msg() << "ROB status error - skipping fragment" << endreq;
+	continue;
+      }
+    }
+
+    // Skip duplicate fragments
+
+    if (!dupCheck.insert(robid).second) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_DUPLICATE_ROB);
+      if (debug) msg() << "Skipping duplicate ROB fragment" << endreq;
+      continue;
+    }
+
+    // Unpack ROD data (slinks)
+
+    RODPointer payloadBeg;
+    RODPointer payload;
+    RODPointer payloadEnd;
+    (*rob)->rod_data(payloadBeg);
+    payloadEnd = payloadBeg + (*rob)->rod_ndata();
+    payload = payloadBeg;
+    if (payload == payloadEnd) {
+      if (debug) msg() << "ROB fragment empty" << endreq;
+      continue;
+    }
+
+    // Check identifier
+    const uint32_t sourceID = (*rob)->rod_source_id();
+    if (m_srcIdMap->getRobID(sourceID) != robid           ||
+        m_srcIdMap->subDet(sourceID)   != m_subDetector   ||
+	m_srcIdMap->daqOrRoi(sourceID) != 0               ||
+	m_srcIdMap->slink(sourceID)    >= m_slinks        ||
+	m_srcIdMap->crate(sourceID)    <  m_crateOffsetHw ||
+	m_srcIdMap->crate(sourceID)    >= m_crateOffsetHw + m_crates) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_ROD_ID);
+      if (debug) {
+        msg() << "Wrong source identifier in data: ROD "
+              << MSG::hex << sourceID << "  ROB " << robid
+	      << MSG::dec << endreq;
+      }
+      continue;
+    }
+
+    // Check minor version
+    const int minorVersion = (*rob)->rod_version() & 0xffff;
+    if (minorVersion <= m_srcIdMap->minorVersionPreLS1()) {
+      if (debug) msg() << "Skipping pre-LS1 data" << endreq;
+      continue;
+    }
+    const int rodCrate = m_srcIdMap->crate(sourceID);
+    if (debug) {
+      msg() << "Treating crate " << rodCrate 
+            << " slink " << m_srcIdMap->slink(sourceID) << endreq;
+    }
+
+    // First word should be User Header
+    if ( !L1CaloUserHeader::isValid(*payload) ) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
+      if (debug) msg() << "Invalid or missing user header" << endreq;
+      continue;
+    }
+    L1CaloUserHeader userHeader(*payload);
+    userHeader.setVersion(minorVersion);
+    const int headerWords = userHeader.words();
+    if (headerWords != 1) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
+      if (debug) msg() << "Unexpected number of user header words: "
+                       << headerWords << endreq;
+      continue;
+    }
+    for (int i = 0; i < headerWords; ++i) ++payload;
+    // triggered slice offsets
+    int trigJem = userHeader.jem();
+    if (debug) {
+      msg() << "Minor format version number: " << MSG::hex
+            << minorVersion << MSG::dec << endreq
+            << "JEM triggered slice offset: " << trigJem << endreq;
+    }
+
+    // Loop over sub-blocks
+
+    m_rodErr = L1CaloSubBlock::ERROR_NONE;
+    while (payload != payloadEnd) {
+      
+      if (L1CaloSubBlock::wordType(*payload) != L1CaloSubBlock::HEADER) {
+        if (debug) msg() << "Unexpected data sequence" << endreq;
+	m_rodErr = L1CaloSubBlock::ERROR_MISSING_HEADER;
+	break;
+      }
+      if (CmxSubBlock::cmxBlock(*payload)) {
+        // CMXs
+	if (CmxSubBlock::cmxType(*payload) == CmxSubBlock::CMX_JET) {
+	  m_cmxJetSubBlock->clear();
+          payload = m_cmxJetSubBlock->read(payload, payloadEnd);
+	  if (m_cmxJetSubBlock->crate() != rodCrate) {
+	    if (debug) msg() << "Inconsistent crate number in ROD source ID"
+	                     << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	    break;
+          }
+	  if (collection == CMX_HITS || collection == CMX_TOBS) {
+	    decodeCmxJet(m_cmxJetSubBlock, trigJem, collection);
+	    if (m_rodErr != L1CaloSubBlock::ERROR_NONE) {
+	      if (debug) msg() << "decodeCmxJet failed" << endreq;
+	      break;
+	    }
+          }
+        } else if (CmxSubBlock::cmxType(*payload) == CmxSubBlock::CMX_ENERGY) {
+	  m_cmxEnergySubBlock->clear();
+	  payload = m_cmxEnergySubBlock->read(payload, payloadEnd);
+	  if (m_cmxEnergySubBlock->crate() != rodCrate) {
+	    if (debug) msg() << "Inconsistent crate number in ROD source ID"
+	                     << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	    break;
+          }
+	  if (collection == CMX_SUMS) {
+	    decodeCmxEnergy(m_cmxEnergySubBlock, trigJem);
+	    if (m_rodErr != L1CaloSubBlock::ERROR_NONE) {
+	      if (debug) msg() << "decodeCmxEnergy failed" << endreq;
+	      break;
+	    }
+          }
+	} else {
+	  if (debug) msg() << "Invalid CMX type in module field" << endreq;
+	  m_rodErr = L1CaloSubBlock::ERROR_MODULE_NUMBER;
+	  break;
+        }
+      } else {
+        // JEM
+	m_jemSubBlock->clear();
+        payload = m_jemSubBlock->read(payload, payloadEnd);
+	if (m_jemSubBlock->crate() != rodCrate) {
+	  if (debug) msg() << "Inconsistent crate number in ROD source ID"
+	                   << endreq;
+	  m_rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	  break;
+        }
+	if (collection == JET_ELEMENTS || collection == ENERGY_SUMS) {
+	  decodeJem(m_jemSubBlock, trigJem, collection);
+	  if (m_rodErr != L1CaloSubBlock::ERROR_NONE) {
+	    if (debug) msg() << "decodeJem failed" << endreq;
+	    break;
+	  }
+        }
+      }
+    }
+    if (m_rodErr != L1CaloSubBlock::ERROR_NONE)
+                                       m_errorTool->rodError(robid, m_rodErr);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Unpack CMX-Energy sub-block
+
+void JepByteStreamV2Tool::decodeCmxEnergy(CmxEnergySubBlock* subBlock,
+                                                                 int trigJem)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  const int hwCrate    = subBlock->crate();
+  const int module     = subBlock->cmxPosition();
+  const int firmware   = subBlock->cmxFirmware();
+  const int summing    = subBlock->cmxSumming();
+  const int timeslices = subBlock->timeslices();
+  const int sliceNum   = subBlock->slice();
+  if (debug) {
+    msg() << "CMX-Energy: Crate " << hwCrate
+          << "  Module "          << module
+	  << "  Firmware "        << firmware
+	  << "  Summing "         << summing
+          << "  Total slices "    << timeslices
+          << "  Slice "           << sliceNum    << endreq;
+  }
+  if (timeslices <= trigJem) {
+    if (debug) msg() << "Triggered CMX slice from header "
+                     << "inconsistent with number of slices: "
+                     << trigJem << ", " << timeslices << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  if (timeslices <= sliceNum) {
+    if (debug) msg() << "Total slices inconsistent with slice number: "
+                     << timeslices << ", " << sliceNum << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  // Unpack sub-block
+  if (subBlock->dataWords() && !subBlock->unpack()) {
+    if (debug) {
+      std::string errMsg(subBlock->unpackErrorMsg());
+      msg() << "CMX-Energy sub-block unpacking failed: " << errMsg << endreq;
+    }
+    m_rodErr = subBlock->unpackErrorCode();
+    return;
+  }
+
+  // Retrieve required data
+
+  const bool neutralFormat = subBlock->format() == L1CaloSubBlock::NEUTRAL;
+  const int crate     = hwCrate - m_crateOffsetHw;
+  const int swCrate   = crate   + m_crateOffsetSw;
+  const int maxSource = static_cast<int>(LVL1::CMXEtSums::MAX_SOURCE);
+  std::vector<unsigned int>& exVec(m_uintVec0);
+  std::vector<unsigned int>& eyVec(m_uintVec1);
+  std::vector<unsigned int>& etVec(m_uintVec2);
+  std::vector<int>& exErrVec(m_intVec0);
+  std::vector<int>& eyErrVec(m_intVec1);
+  std::vector<int>& etErrVec(m_intVec2);
+  LVL1::DataError derr;
+  derr.set(LVL1::DataError::SubStatusWord, subBlock->subStatus());
+  const int ssError = derr.error();
+  const int sliceBeg = ( neutralFormat ) ? 0          : sliceNum;
+  const int sliceEnd = ( neutralFormat ) ? timeslices : sliceNum + 1;
+  for (int slice = sliceBeg; slice < sliceEnd; ++slice) {
+
+    // Energy sums
+
+    for (int source = 0; source < maxSource; ++source) {
+      if (source >= m_modules && summing == CmxSubBlock::CRATE && 
+	  source != LVL1::CMXEtSums::LOCAL_STANDARD &&
+	  source != LVL1::CMXEtSums::LOCAL_RESTRICTED) continue;
+      unsigned int ex = 0;
+      unsigned int ey = 0;
+      unsigned int et = 0;
+      int exErr = 0;
+      int eyErr = 0;
+      int etErr = 0;
+      LVL1::DataError exErrBits(ssError);
+      LVL1::DataError eyErrBits(ssError);
+      LVL1::DataError etErrBits(ssError);
+      if (source < m_modules) {
+        ex = subBlock->energy(slice, source, CmxEnergySubBlock::ENERGY_EX);
+        ey = subBlock->energy(slice, source, CmxEnergySubBlock::ENERGY_EY);
+        et = subBlock->energy(slice, source, CmxEnergySubBlock::ENERGY_ET);
+        exErr = subBlock->error(slice, source, CmxEnergySubBlock::ENERGY_EX);
+        eyErr = subBlock->error(slice, source, CmxEnergySubBlock::ENERGY_EY);
+        etErr = subBlock->error(slice, source, CmxEnergySubBlock::ENERGY_ET);
+	exErrBits.set(LVL1::DataError::Parity, exErr >> 1);
+	eyErrBits.set(LVL1::DataError::Parity, eyErr >> 1);
+	etErrBits.set(LVL1::DataError::Parity, etErr >> 1);
+        exErr = exErrBits.error();
+        eyErr = eyErrBits.error();
+        etErr = etErrBits.error();
+      } else {
+	CmxEnergySubBlock::SourceType srcType = CmxEnergySubBlock::MAX_SOURCE_TYPE;
+	CmxEnergySubBlock::SumType    sumType = CmxEnergySubBlock::MAX_SUM_TYPE;
+	CmxEnergySubBlock::HitsType   hitType = CmxEnergySubBlock::MAX_HITS_TYPE;
+	energySubBlockTypes(source, srcType, sumType, hitType);
+	if (srcType != CmxEnergySubBlock::MAX_SOURCE_TYPE) {
+	  ex = subBlock->energy(slice, srcType, sumType, CmxEnergySubBlock::ENERGY_EX);
+	  ey = subBlock->energy(slice, srcType, sumType, CmxEnergySubBlock::ENERGY_EY);
+	  et = subBlock->energy(slice, srcType, sumType, CmxEnergySubBlock::ENERGY_ET);
+	  exErr = subBlock->error(slice, srcType, sumType, CmxEnergySubBlock::ENERGY_EX);
+	  eyErr = subBlock->error(slice, srcType, sumType, CmxEnergySubBlock::ENERGY_EY);
+	  etErr = subBlock->error(slice, srcType, sumType, CmxEnergySubBlock::ENERGY_ET);
+	  exErrBits.set(LVL1::DataError::Overflow, exErr);
+	  eyErrBits.set(LVL1::DataError::Overflow, eyErr);
+	  etErrBits.set(LVL1::DataError::Overflow, etErr);
+	  if (srcType == CmxEnergySubBlock::REMOTE) {
+	    exErrBits.set(LVL1::DataError::Parity, exErr >> 1);
+	    eyErrBits.set(LVL1::DataError::Parity, eyErr >> 1);
+	    etErrBits.set(LVL1::DataError::Parity, etErr >> 1);
+          }
+        } else if (hitType != CmxEnergySubBlock::MAX_HITS_TYPE) {
+	  ex = subBlock->hits(slice, hitType, sumType);
+	  ey = ex;
+	  et = ex;
+        }
+      }
+      exErr = exErrBits.error();
+      eyErr = eyErrBits.error();
+      etErr = etErrBits.error();
+      if (ex || ey || et || exErr || eyErr || etErr) {
+        LVL1::CMXEtSums* sums = findCmxSums(crate, source);
+	if ( ! sums ) {   // create new CMX energy sums
+	  exVec.assign(timeslices, 0);
+	  eyVec.assign(timeslices, 0);
+	  etVec.assign(timeslices, 0);
+	  exErrVec.assign(timeslices, 0);
+	  eyErrVec.assign(timeslices, 0);
+	  etErrVec.assign(timeslices, 0);
+	  exVec[slice] = ex;
+	  eyVec[slice] = ey;
+	  etVec[slice] = et;
+	  exErrVec[slice] = exErr;
+	  eyErrVec[slice] = eyErr;
+	  etErrVec[slice] = etErr;
+	  sums = new LVL1::CMXEtSums(swCrate, source, etVec, exVec, eyVec,
+				     etErrVec, exErrVec, eyErrVec, trigJem);
+          const int key = crate*100 + source;
+	  m_cmxEtMap.insert(std::make_pair(key, sums));
+	  m_cmxEtCollection->push_back(sums);
+        } else {
+	  exVec = sums->ExVec();
+	  eyVec = sums->EyVec();
+	  etVec = sums->EtVec();
+	  exErrVec = sums->ExErrorVec();
+	  eyErrVec = sums->EyErrorVec();
+	  etErrVec = sums->EtErrorVec();
+	  const int nsl = exVec.size();
+	  if (timeslices != nsl) {
+	    if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                     << endreq;
+            m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    return;
+          }
+	  if (exVec[slice] != 0 || eyVec[slice] != 0 || etVec[slice] != 0 ||
+	      exErrVec[slice] != 0 || eyErrVec[slice] != 0 ||
+              etErrVec[slice] != 0) {
+            if (debug) msg() << "Duplicate data for slice " << slice << endreq;
+	    m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    return;
+          }
+	  exVec[slice] = ex;
+	  eyVec[slice] = ey;
+	  etVec[slice] = et;
+	  exErrVec[slice] = exErr;
+	  eyErrVec[slice] = eyErr;
+	  etErrVec[slice] = etErr;
+	  sums->addEx(exVec, exErrVec);
+	  sums->addEy(eyVec, eyErrVec);
+	  sums->addEt(etVec, etErrVec);
+        }
+      }
+    }
+  }
+  
+  return;
+}
+
+// Unpack CMX-Jet sub-block
+
+void JepByteStreamV2Tool::decodeCmxJet(CmxJetSubBlock* subBlock, int trigJem,
+                                             const CollectionType collection)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  const int hwCrate    = subBlock->crate();
+  const int module     = subBlock->cmxPosition();
+  const int firmware   = subBlock->cmxFirmware();
+  const int summing    = subBlock->cmxSumming();
+  const int timeslices = subBlock->timeslices();
+  const int sliceNum   = subBlock->slice();
+  if (debug) {
+    msg() << "CMX-Jet: Crate " << hwCrate
+          << "  Module "       << module
+	  << "  Firmware "     << firmware
+	  << "  Summing "      << summing
+          << "  Total slices " << timeslices
+          << "  Slice "        << sliceNum    << endreq;
+  }
+  if (timeslices <= trigJem) {
+    if (debug) msg() << "Triggered CMX slice from header "
+                     << "inconsistent with number of slices: "
+                     << trigJem << ", " << timeslices << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  if (timeslices <= sliceNum) {
+    if (debug) msg() << "Total slices inconsistent with slice number: "
+                     << timeslices << ", " << sliceNum << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  // Unpack sub-block
+  if (subBlock->dataWords() && !subBlock->unpack()) {
+    if (debug) {
+      std::string errMsg(subBlock->unpackErrorMsg());
+      msg() << "CMX-Jet sub-block unpacking failed: " << errMsg << endreq;
+    }
+    m_rodErr = subBlock->unpackErrorCode();
+    return;
+  }
+
+  // Retrieve required data
+
+  const bool neutralFormat = subBlock->format() == L1CaloSubBlock::NEUTRAL;
+  const int crate     = hwCrate - m_crateOffsetHw;
+  const int swCrate   = crate   + m_crateOffsetSw;
+  const int maxSource = static_cast<int>(LVL1::CMXJetHits::MAX_SOURCE);
+  std::vector<int>& energyLgVec(m_intVec0);
+  std::vector<int>& energySmVec(m_intVec1);
+  std::vector<int>& errorVec(m_intVec2);
+  std::vector<unsigned int>& presenceMapVec(m_uintVec0);
+  std::vector<unsigned int>& hit0Vec(m_uintVec0);
+  std::vector<unsigned int>& hit1Vec(m_uintVec1);
+  std::vector<int>& err0Vec(m_intVec0);
+  std::vector<int>& err1Vec(m_intVec1);
+  LVL1::DataError derr;
+  derr.set(LVL1::DataError::SubStatusWord, subBlock->subStatus());
+  const int ssError = derr.error();
+  const int sliceBeg = ( neutralFormat ) ? 0          : sliceNum;
+  const int sliceEnd = ( neutralFormat ) ? timeslices : sliceNum + 1;
+  for (int slice = sliceBeg; slice < sliceEnd; ++slice) {
+
+    // Jet TOBs
+
+    if (collection == CMX_TOBS) {
+
+      for (int jem = 0; jem < m_modules; ++jem) {
+        const unsigned int presenceMap = subBlock->presenceMap(slice, jem);
+        for (int tob = 0; tob < m_maxTobs; ++tob) {
+	  const int energyLarge = subBlock->energyLarge(slice, jem, tob);
+	  const int energySmall = subBlock->energySmall(slice, jem, tob);
+	  int error = subBlock->tobError(slice, jem, tob);
+	  if (energyLarge == 0 && energySmall == 0 && error == 0) break;
+	  const int loc = subBlock->localCoord(slice, jem, tob);
+	  const int frame = subBlock->frame(slice, jem, tob);
+	  LVL1::DataError errBits(ssError);
+	  if (error) {
+	    errBits.set(LVL1::DataError::Parity, error);
+            if (neutralFormat) {
+	      const int parity = subBlock->parityBits(slice, jem);
+	      errBits.set(LVL1::DataError::ParityPhase0, parity);
+	      errBits.set(LVL1::DataError::ParityPhase1, (parity>>1));
+	      errBits.set(LVL1::DataError::ParityPhase2, (parity>>2));
+	      errBits.set(LVL1::DataError::ParityPhase3, (parity>>3));
+	    }
+          }
+	  error = errBits.error();
+	  const int key = tobKey(crate, jem, frame, loc);
+	  LVL1::CMXJetTob* tb = findCmxTob(key);
+	  if ( ! tb ) { // create new CMX TOB
+	    energyLgVec.assign(timeslices, 0);
+	    energySmVec.assign(timeslices, 0);
+	    errorVec.assign(timeslices, 0);
+	    presenceMapVec.assign(timeslices, 0);
+	    energyLgVec[slice] = energyLarge;
+	    energySmVec[slice] = energySmall;
+	    errorVec[slice]    = error;
+	    presenceMapVec[slice] = presenceMap;
+	    tb = new LVL1::CMXJetTob(swCrate, jem, frame, loc,
+	                             energyLgVec, energySmVec, errorVec,
+				     presenceMapVec, trigJem);
+	    m_cmxTobMap.insert(std::make_pair(key, tb));
+	    m_cmxTobCollection->push_back(tb);
+          } else {
+	    energyLgVec = tb->energyLgVec();
+	    energySmVec = tb->energySmVec();
+	    errorVec    = tb->errorVec();
+	    presenceMapVec = tb->presenceMapVec();
+	    const int nsl = energyLgVec.size();
+	    if (timeslices != nsl) {
+	      if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                       << endreq;
+              m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	      return;
+            }
+	    if (energyLgVec[slice] != 0 || energySmVec[slice] != 0 ||
+	        errorVec[slice]  != 0 || presenceMapVec[slice] != 0) {
+              if (debug) msg() << "Duplicate data for slice " << slice << endreq;
+	      m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	      return;
+            }
+	    energyLgVec[slice] = energyLarge;
+	    energySmVec[slice] = energySmall;
+	    errorVec[slice]    = error;
+	    presenceMapVec[slice] = presenceMap;
+	    tb->addTob(energyLgVec, energySmVec, errorVec, presenceMapVec);
+          }
+        }
+      }
+    }
+
+    // Jet hit counts and topo info
+
+    else if (collection == CMX_HITS) {
+
+      for (int source = 0; source < maxSource; ++source) {
+        if (summing == CmxSubBlock::CRATE && 
+            (source == LVL1::CMXJetHits::REMOTE_MAIN    ||
+  	     source == LVL1::CMXJetHits::TOTAL_MAIN     ||
+	     source == LVL1::CMXJetHits::REMOTE_FORWARD ||
+	     source == LVL1::CMXJetHits::TOTAL_FORWARD)) continue;
+        int sourceId = jetSubBlockSourceId(source);
+        if (sourceId == CmxJetSubBlock::MAX_SOURCE_ID) continue;
+	const unsigned int hit0 = subBlock->hits(slice, sourceId, 0);
+	const unsigned int hit1 = subBlock->hits(slice, sourceId, 1);
+	int err0 = subBlock->hitsError(slice, sourceId, 0);
+	int err1 = subBlock->hitsError(slice, sourceId, 1);
+	LVL1::DataError err0Bits(ssError);
+	LVL1::DataError err1Bits(ssError);
+	err0Bits.set(LVL1::DataError::Parity, err0);
+	err1Bits.set(LVL1::DataError::Parity, err1>>1);
+	err0Bits.set(LVL1::DataError::Overflow, err0>>2);
+	err1Bits.set(LVL1::DataError::Overflow, err1>>2);
+	err0 = err0Bits.error();
+	err1 = err1Bits.error();
+	if (hit0 || hit1 || err0 || err1) {
+          LVL1::CMXJetHits* jh = findCmxHits(crate, source);
+	  if ( ! jh ) {   // create new CMX hits
+	    hit0Vec.assign(timeslices, 0);
+	    hit1Vec.assign(timeslices, 0);
+	    err0Vec.assign(timeslices, 0);
+	    err1Vec.assign(timeslices, 0);
+	    hit0Vec[slice] = hit0;
+	    hit1Vec[slice] = hit1;
+	    err0Vec[slice] = err0;
+	    err1Vec[slice] = err1;
+	    jh = new LVL1::CMXJetHits(swCrate, source, hit0Vec, hit1Vec,
+	                              err0Vec, err1Vec, trigJem);
+            const int key = crate*100 + source;
+	    m_cmxHitsMap.insert(std::make_pair(key, jh));
+	    m_cmxHitCollection->push_back(jh);
+          } else {
+	    hit0Vec = jh->hitsVec0();
+	    hit1Vec = jh->hitsVec1();
+	    err0Vec = jh->errorVec0();
+	    err1Vec = jh->errorVec1();
+	    const int nsl = hit0Vec.size();
+	    if (timeslices != nsl) {
+	      if (debug) msg() << "Inconsistent number of slices in sub-blocks"
+	                       << endreq;
+              m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	      return;
+            }
+	    if (hit0Vec[slice] != 0 || hit1Vec[slice] != 0 ||
+	        err0Vec[slice] != 0 || err1Vec[slice] != 0) {
+	      if (debug) msg() << "Duplicate data for slice " << slice << endreq;
+	      m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	      return;
+            }
+	    hit0Vec[slice] = hit0;
+	    hit1Vec[slice] = hit1;
+	    err0Vec[slice] = err0;
+	    err1Vec[slice] = err1;
+	    jh->addHits(hit0Vec, hit1Vec, err0Vec, err1Vec);
+          }
+        }
+      }
+    }
+  }
+  
+  return;
+}
+
+// Unpack JEM sub-block
+
+void JepByteStreamV2Tool::decodeJem(JemSubBlockV2* subBlock, int trigJem,
+                                        const CollectionType collection)
+{
+  const bool debug   = msgLvl(MSG::DEBUG);
+  const bool verbose = msgLvl(MSG::VERBOSE);
+  if (debug) msg(MSG::DEBUG);
+
+  const int hwCrate    = subBlock->crate();
+  const int module     = subBlock->module();
+  const int timeslices = subBlock->timeslices();
+  const int sliceNum   = subBlock->slice();
+  if (debug) {
+    msg() << "JEM: Crate "     << hwCrate
+          << "  Module "       << module
+          << "  Total slices " << timeslices
+          << "  Slice "        << sliceNum    << endreq;
+  }
+  if (timeslices <= trigJem) {
+    if (debug) msg() << "Triggered JEM slice from header "
+                     << "inconsistent with number of slices: "
+                     << trigJem << ", " << timeslices << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  if (timeslices <= sliceNum) {
+    if (debug) msg() << "Total slices inconsistent with slice number: "
+                     << timeslices << ", " << sliceNum << endreq;
+    m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+    return;
+  }
+  // Unpack sub-block
+  if (subBlock->dataWords() && !subBlock->unpack()) {
+    if (debug) {
+      std::string errMsg(subBlock->unpackErrorMsg());
+      msg() << "JEM sub-block unpacking failed: " << errMsg << endreq;
+    }
+    m_rodErr = subBlock->unpackErrorCode();
+    return;
+  }
+
+  // Retrieve required data
+
+  const bool neutralFormat = subBlock->format() == L1CaloSubBlock::NEUTRAL;
+  const int crate    = hwCrate - m_crateOffsetHw;
+  const int swCrate  = crate   + m_crateOffsetSw;
+  std::vector<unsigned int>& exVec(m_uintVec0);
+  std::vector<unsigned int>& eyVec(m_uintVec1);
+  std::vector<unsigned int>& etVec(m_uintVec2);
+  LVL1::DataError derr;
+  derr.set(LVL1::DataError::SubStatusWord, subBlock->subStatus());
+  const int ssError = derr.error();
+  std::vector<int> dummy(timeslices);
+  const int sliceBeg = ( neutralFormat ) ? 0          : sliceNum;
+  const int sliceEnd = ( neutralFormat ) ? timeslices : sliceNum + 1;
+  for (int slice = sliceBeg; slice < sliceEnd; ++slice) {
+
+    if (collection == JET_ELEMENTS) {
+
+      // Loop over jet element channels and fill jet elements
+
+      for (int chan = 0; chan < m_channels; ++chan) {
+        const JemJetElement jetEle(subBlock->jetElement(slice, chan));
+        if (jetEle.data() || ssError) {
+	  double eta = 0.;
+	  double phi = 0.;
+	  int layer = 0;
+	  if (m_jemMaps->mapping(crate, module, chan, eta, phi, layer)) {
+	    if (layer == m_coreOverlap) {
+	      LVL1::JetElement* je = findJetElement(eta, phi);
+	      if ( ! je ) {   // create new jet element
+	        const unsigned int key = m_elementKey->jeKey(phi, eta);
+	        je = new LVL1::JetElement(phi, eta, dummy, dummy, key,
+	                                  dummy, dummy, dummy, trigJem);
+	        m_jeMap.insert(std::make_pair(key, je));
+	        m_jeCollection->push_back(je);
+              } else {
+	        const std::vector<int>& emEnergy(je->emEnergyVec());
+		const std::vector<int>& hadEnergy(je->hadEnergyVec());
+		const std::vector<int>& emError(je->emErrorVec());
+		const std::vector<int>& hadError(je->hadErrorVec());
+		const int nsl = emEnergy.size();
+		if (timeslices != nsl) {
+		  if (debug) {
+		    msg() << "Inconsistent number of slices in sub-blocks"
+		          << endreq;
+                  }
+		  m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+		  return;
+                }
+		if (emEnergy[slice] != 0 || hadEnergy[slice] != 0 ||
+		    emError[slice]  != 0 || hadError[slice]  != 0) {
+                  if (debug) msg() << "Duplicate data for slice "
+		                   << slice << endreq;
+                  m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+		  return;
+                }
+              }
+	      LVL1::DataError emErrBits(ssError);
+	      LVL1::DataError hadErrBits(ssError);
+	      const int linkError = jetEle.linkError();
+	      emErrBits.set(LVL1::DataError::Parity, jetEle.emParity());
+	      emErrBits.set(LVL1::DataError::LinkDown, linkError);
+	      hadErrBits.set(LVL1::DataError::Parity, jetEle.hadParity());
+	      hadErrBits.set(LVL1::DataError::LinkDown, linkError >> 1);
+	      je->addSlice(slice, jetEle.emData(), jetEle.hadData(),
+	                          emErrBits.error(), hadErrBits.error(),
+	           	          linkError);
+	    }
+          } else if (verbose && jetEle.data()) {
+	    msg(MSG::VERBOSE) << "Non-zero data but no channel mapping for channel "
+	                      << chan << endreq;
+	    msg(MSG::DEBUG);
+          }
+        } else if (verbose) {
+	  msg(MSG::VERBOSE) << "No jet element data for channel "
+	                    << chan << " slice " << slice << endreq;
+	  msg(MSG::DEBUG);
+        }
+      }
+    } else if (collection == ENERGY_SUMS) {
+
+      // Get energy subsums
+
+      const unsigned int ex = subBlock->ex(slice);
+      const unsigned int ey = subBlock->ey(slice);
+      const unsigned int et = subBlock->et(slice);
+      if (ex | ey | et) {
+	LVL1::JEMEtSums* sums = findEnergySums(crate, module);
+	if ( ! sums ) {   // create new energy sums
+	  exVec.assign(timeslices, 0);
+	  eyVec.assign(timeslices, 0);
+	  etVec.assign(timeslices, 0);
+	  exVec[slice] = ex;
+	  eyVec[slice] = ey;
+	  etVec[slice] = et;
+	  sums = new LVL1::JEMEtSums(swCrate, module, etVec, exVec, eyVec,
+	                                                          trigJem);
+          m_etMap.insert(std::make_pair(crate*m_modules+module, sums));
+	  m_etCollection->push_back(sums);
+        } else {
+	  exVec = sums->ExVec();
+	  eyVec = sums->EyVec();
+	  etVec = sums->EtVec();
+	  const int nsl = exVec.size();
+	  if (timeslices != nsl) {
+	    if (debug) {
+	      msg() << "Inconsistent number of slices in sub-blocks"
+	            << endreq;
+	    }
+            m_rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    return;
+          }
+	  if (exVec[slice] != 0 || eyVec[slice] != 0 || etVec[slice] != 0) {
+	    if (debug) msg() << "Duplicate data for slice "
+	                     << slice << endreq;
+            m_rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    return;
+          }
+	  exVec[slice] = ex;
+	  eyVec[slice] = ey;
+	  etVec[slice] = et;
+	  sums->addEx(exVec);
+	  sums->addEy(eyVec);
+	  sums->addEt(etVec);
+        }
+      } else if (verbose) {
+        msg(MSG::VERBOSE) << "No energy sums data for crate/module/slice "
+                          << hwCrate << "/" << module << "/" << slice
+    			  << endreq;
+	msg(MSG::DEBUG);
+      }
+    }
+  }
+  return;
+}
+
+// Find TOB map key for given crate, jem, frame, loc
+
+int JepByteStreamV2Tool::tobKey(const int crate, const int jem,
+                                const int frame, const int loc)
+{
+  return ((((((crate<<4)+jem)<<3)+frame)<<2)+loc);
+}
+
+// Find a jet element given eta, phi
+
+LVL1::JetElement* JepByteStreamV2Tool::findJetElement(const double eta,
+                                                      const double phi)
+{
+  LVL1::JetElement* tt = 0;
+  const unsigned int key = m_elementKey->jeKey(phi, eta);
+  JetElementMap::const_iterator mapIter;
+  mapIter = m_jeMap.find(key);
+  if (mapIter != m_jeMap.end()) tt = mapIter->second;
+  return tt;
+}
+
+// Find energy sums for given crate, module
+
+LVL1::JEMEtSums* JepByteStreamV2Tool::findEnergySums(const int crate,
+                                                     const int module)
+{
+  LVL1::JEMEtSums* sums = 0;
+  EnergySumsMap::const_iterator mapIter;
+  mapIter = m_etMap.find(crate*m_modules + module);
+  if (mapIter != m_etMap.end()) sums = mapIter->second;
+  return sums;
+}
+
+// Find CMX TOB for given crate, jem, frame, loc
+
+LVL1::CMXJetTob* JepByteStreamV2Tool::findCmxTob(const int key)
+{
+  LVL1::CMXJetTob* tob = 0;
+  CmxTobMap::const_iterator mapIter;
+  mapIter = m_cmxTobMap.find(key);
+  if (mapIter != m_cmxTobMap.end()) tob = mapIter->second;
+  return tob;
+}
+
+// Find CMX hits for given crate, source
+
+LVL1::CMXJetHits* JepByteStreamV2Tool::findCmxHits(const int crate,
+                                                   const int source)
+{
+  LVL1::CMXJetHits* hits = 0;
+  CmxHitsMap::const_iterator mapIter;
+  mapIter = m_cmxHitsMap.find(crate*100 + source);
+  if (mapIter != m_cmxHitsMap.end()) hits = mapIter->second;
+  return hits;
+}
+
+// Find CMX energy sums for given crate, module, source
+
+LVL1::CMXEtSums* JepByteStreamV2Tool::findCmxSums(const int crate,
+                                                  const int source)
+{
+  LVL1::CMXEtSums* sums = 0;
+  CmxSumsMap::const_iterator mapIter;
+  mapIter = m_cmxEtMap.find(crate*100 + source);
+  if (mapIter != m_cmxEtMap.end()) sums = mapIter->second;
+  return sums;
+}
+
+// Set up jet element map
+
+void JepByteStreamV2Tool::setupJeMap(const JetElementCollection*
+                                                        const jeCollection)
+{
+  m_jeMap.clear();
+  if (jeCollection) {
+    JetElementCollection::const_iterator pos  = jeCollection->begin();
+    JetElementCollection::const_iterator pose = jeCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::JetElement* const je = *pos;
+      const unsigned int key = m_elementKey->jeKey(je->phi(), je->eta());
+      m_jeMap.insert(std::make_pair(key, je));
+    }
+  }
+}
+
+// Set up energy sums map
+
+void JepByteStreamV2Tool::setupEtMap(const EnergySumsCollection*
+                                                         const etCollection)
+{
+  m_etMap.clear();
+  if (etCollection) {
+    EnergySumsCollection::const_iterator pos  = etCollection->begin();
+    EnergySumsCollection::const_iterator pose = etCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::JEMEtSums* const sums = *pos;
+      const int crate = sums->crate() - m_crateOffsetSw;
+      const int key   = m_modules * crate + sums->module();
+      m_etMap.insert(std::make_pair(key, sums));
+    }
+  }
+}
+
+// Set up CMX TOB map
+
+void JepByteStreamV2Tool::setupCmxTobMap(const CmxTobCollection*
+                                                         const tobCollection)
+{
+  m_cmxTobMap.clear();
+  if (tobCollection) {
+    CmxTobCollection::const_iterator pos  = tobCollection->begin();
+    CmxTobCollection::const_iterator pose = tobCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::CMXJetTob* const tob = *pos;
+      const int crate = tob->crate() - m_crateOffsetSw;
+      const int jem   = tob->jem();
+      const int frame = tob->frame();
+      const int loc   = tob->location();
+      const int key   = tobKey(crate, jem, frame, loc);
+      m_cmxTobMap.insert(std::make_pair(key, tob));
+    }
+  }
+}
+
+// Set up CMX hits map
+
+void JepByteStreamV2Tool::setupCmxHitsMap(const CmxHitsCollection*
+                                                         const hitCollection)
+{
+  m_cmxHitsMap.clear();
+  if (hitCollection) {
+    CmxHitsCollection::const_iterator pos  = hitCollection->begin();
+    CmxHitsCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::CMXJetHits* const hits = *pos;
+      const int crate = hits->crate() - m_crateOffsetSw;
+      const int key   = crate*100 + hits->source();
+      m_cmxHitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Set up CMX energy sums map
+
+void JepByteStreamV2Tool::setupCmxEtMap(const CmxSumsCollection*
+                                                         const etCollection)
+{
+  m_cmxEtMap.clear();
+  if (etCollection) {
+    CmxSumsCollection::const_iterator pos  = etCollection->begin();
+    CmxSumsCollection::const_iterator pose = etCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::CMXEtSums* const sums = *pos;
+      const int crate = sums->crate() - m_crateOffsetSw;
+      const int key   = crate*100 + sums->source();
+      m_cmxEtMap.insert(std::make_pair(key, sums));
+    }
+  }
+}
+
+// Get number of slices and triggered slice offset for next slink
+
+bool JepByteStreamV2Tool::slinkSlices(const int crate, const int module,
+                  const int modulesPerSlink, int& timeslices, int& trigJem)
+{
+  int slices = -1;
+  int trigJ  = m_dfltSlices/2;
+  for (int mod = module; mod < module + modulesPerSlink; ++mod) {
+    for (int chan = 0; chan < m_channels; ++chan) {
+      double eta = 0.;
+      double phi = 0.;
+      int layer = 0;
+      if ( !m_jemMaps->mapping(crate, mod, chan, eta, phi, layer)) continue;
+      const LVL1::JetElement* const je = findJetElement(eta, phi);
+      if ( !je ) continue;
+      const int numdat = 5;
+      std::vector<int> sums(numdat);
+      std::vector<int> sizes(numdat);
+      sums[0] = std::accumulate((je->emEnergyVec()).begin(),
+                                (je->emEnergyVec()).end(), 0);
+      sums[1] = std::accumulate((je->hadEnergyVec()).begin(),
+                                (je->hadEnergyVec()).end(), 0);
+      sums[2] = std::accumulate((je->emErrorVec()).begin(),
+                                (je->emErrorVec()).end(), 0);
+      sums[3] = std::accumulate((je->hadErrorVec()).begin(),
+                                (je->hadErrorVec()).end(), 0);
+      sums[4] = std::accumulate((je->linkErrorVec()).begin(),
+                                (je->linkErrorVec()).end(), 0);
+      sizes[0] = (je->emEnergyVec()).size();
+      sizes[1] = (je->hadEnergyVec()).size();
+      sizes[2] = (je->emErrorVec()).size();
+      sizes[3] = (je->hadErrorVec()).size();
+      sizes[4] = (je->linkErrorVec()).size();
+      const int peak = je->peak();
+      for (int i = 0; i < numdat; ++i) {
+        if (sums[i] == 0) continue;
+        if (slices < 0) {
+	  slices = sizes[i];
+	  trigJ  = peak;
+	} else if (slices != sizes[i] || trigJ != peak) return false;
+      }
+    }
+    const LVL1::JEMEtSums* const et = findEnergySums(crate, mod);
+    if (et) {
+      const int numdat = 3;
+      std::vector<unsigned int> sums(numdat);
+      std::vector<int> sizes(numdat);
+      sums[0] = std::accumulate((et->ExVec()).begin(),
+                                (et->ExVec()).end(), 0);
+      sums[1] = std::accumulate((et->EyVec()).begin(),
+                                (et->EyVec()).end(), 0);
+      sums[2] = std::accumulate((et->EtVec()).begin(),
+                                (et->EtVec()).end(), 0);
+      sizes[0] = (et->ExVec()).size();
+      sizes[1] = (et->EyVec()).size();
+      sizes[2] = (et->EtVec()).size();
+      const int peak = et->peak();
+      for (int i = 0; i < numdat; ++i) {
+        if (sums[i] == 0) continue;
+	if (slices < 0) {
+	  slices = sizes[i];
+	  trigJ  = peak;
+        } else if (slices != sizes[i] || trigJ != peak) return false;
+      }
+    }
+  }
+  // CMX last slink of crate
+  if (module/modulesPerSlink == m_slinks - 1) {
+    for (int jem = module; jem < module + modulesPerSlink; ++jem) {
+      for (int frame = 0; frame < m_frames; ++frame) {
+        for (int loc = 0; loc < m_locations; ++loc) {
+	  const int key = tobKey(crate, jem, frame, loc);
+	  const LVL1::CMXJetTob* tob = findCmxTob(key);
+	  if (tob) {
+	    const int numdat = 4;
+            std::vector<int> sums(numdat);
+            std::vector<int> sizes(numdat);
+	    sums[0] = std::accumulate((tob->energyLgVec()).begin(),
+	                              (tob->energyLgVec()).end(), 0);
+	    sums[1] = std::accumulate((tob->energySmVec()).begin(),
+	                              (tob->energySmVec()).end(), 0);
+	    sums[2] = std::accumulate((tob->errorVec()).begin(),
+	                              (tob->errorVec()).end(), 0);
+	    sums[3] = std::accumulate((tob->presenceMapVec()).begin(),
+	                              (tob->presenceMapVec()).end(), 0);
+            sizes[0] = (tob->energyLgVec()).size();
+            sizes[1] = (tob->energySmVec()).size();
+            sizes[2] = (tob->errorVec()).size();
+            sizes[3] = (tob->presenceMapVec()).size();
+	    const int peak = tob->peak();
+	    for (int i = 0; i < numdat; ++i) {
+	      if (sums[i] == 0) continue;
+              if (slices < 0) {
+	        slices = sizes[i];
+	        trigJ  = peak;
+              } else if (slices != sizes[i] || trigJ != peak) return false;
+	    }
+          }
+        }
+      }
+    }
+    const int maxDataID1 = LVL1::CMXJetHits::MAX_SOURCE;
+    const int maxDataID2 = LVL1::CMXEtSums::MAX_SOURCE;
+    const int maxDataID  = (maxDataID1 > maxDataID2) ? maxDataID1 : maxDataID2;
+    for (int source = 0; source < maxDataID; ++source) {
+      const int numdat = 6;
+      std::vector<unsigned int> sums(numdat);
+      std::vector<int> sizes(numdat);
+      const LVL1::CMXJetHits* hits = 0;
+      if (source < maxDataID1) hits = findCmxHits(crate, source);
+      if (hits) {
+        sums[0] = std::accumulate((hits->hitsVec0()).begin(),
+                                             (hits->hitsVec0()).end(), 0);
+        sums[1] = std::accumulate((hits->hitsVec1()).begin(),
+                                             (hits->hitsVec1()).end(), 0);
+        sums[2] = std::accumulate((hits->errorVec0()).begin(),
+                                             (hits->errorVec0()).end(), 0);
+        sums[3] = std::accumulate((hits->errorVec1()).begin(),
+                                             (hits->errorVec1()).end(), 0);
+        sizes[0] = (hits->hitsVec0()).size();
+        sizes[1] = (hits->hitsVec1()).size();
+        sizes[2] = (hits->errorVec0()).size();
+        sizes[3] = (hits->errorVec1()).size();
+        const int peak = hits->peak();
+        for (int i = 0; i < 4; ++i) {
+          if (sums[i] == 0) continue;
+          if (slices < 0) {
+	    slices = sizes[i];
+	    trigJ  = peak;
+          } else if (slices != sizes[i] || trigJ != peak) return false;
+        }
+      }
+      const LVL1::CMXEtSums* et = 0;
+      if (source < maxDataID2) et = findCmxSums(crate, source);
+      if (et) {
+        sums[0] = std::accumulate((et->ExVec()).begin(),
+  				  (et->ExVec()).end(), 0);
+        sums[1] = std::accumulate((et->EyVec()).begin(),
+                                  (et->EyVec()).end(), 0);
+        sums[2] = std::accumulate((et->EtVec()).begin(),
+                                  (et->EtVec()).end(), 0);
+        sums[3] = std::accumulate((et->ExErrorVec()).begin(),
+                                  (et->ExErrorVec()).end(), 0);
+        sums[4] = std::accumulate((et->EyErrorVec()).begin(),
+                                  (et->EyErrorVec()).end(), 0);
+        sums[5] = std::accumulate((et->EtErrorVec()).begin(),
+                                  (et->EtErrorVec()).end(), 0);
+        sizes[0] = (et->ExVec()).size();
+        sizes[1] = (et->EyVec()).size();
+        sizes[2] = (et->EtVec()).size();
+        sizes[3] = (et->ExErrorVec()).size();
+        sizes[4] = (et->EyErrorVec()).size();
+        sizes[5] = (et->EtErrorVec()).size();
+        const int peak = et->peak();
+        for (int i = 0; i < numdat; ++i) {
+          if (sums[i] == 0) continue;
+	  if (slices < 0) {
+	    slices = sizes[i];
+	    trigJ  = peak;
+          } else if (slices != sizes[i] || trigJ != peak) return false;
+        }
+      }
+    }
+  }
+  if (slices < 0) slices = m_dfltSlices;
+  timeslices = slices;
+  trigJem    = trigJ;
+  return true;
+}
+
+// Get energy subBlock types from CMXEtSums source type
+
+void JepByteStreamV2Tool::energySubBlockTypes(const int source,
+                          CmxEnergySubBlock::SourceType& srcType,
+			  CmxEnergySubBlock::SumType&    sumType,
+			  CmxEnergySubBlock::HitsType&   hitType)
+{
+  switch (source) {
+    case LVL1::CMXEtSums::REMOTE_STANDARD:
+      srcType = CmxEnergySubBlock::REMOTE;
+      sumType = CmxEnergySubBlock::STANDARD;
+      break;
+    case LVL1::CMXEtSums::REMOTE_RESTRICTED:
+      srcType = CmxEnergySubBlock::REMOTE;
+      sumType = CmxEnergySubBlock::RESTRICTED_WEIGHTED;
+      break;
+    case LVL1::CMXEtSums::LOCAL_STANDARD:
+      srcType = CmxEnergySubBlock::LOCAL;
+      sumType = CmxEnergySubBlock::STANDARD;
+      break;
+    case LVL1::CMXEtSums::LOCAL_RESTRICTED:
+      srcType = CmxEnergySubBlock::LOCAL;
+      sumType = CmxEnergySubBlock::RESTRICTED_WEIGHTED;
+      break;
+    case LVL1::CMXEtSums::TOTAL_STANDARD:
+      srcType = CmxEnergySubBlock::TOTAL;
+      sumType = CmxEnergySubBlock::STANDARD;
+      break;
+    case LVL1::CMXEtSums::TOTAL_RESTRICTED:
+      srcType = CmxEnergySubBlock::TOTAL;
+      sumType = CmxEnergySubBlock::RESTRICTED_WEIGHTED;
+      break;
+    case LVL1::CMXEtSums::SUM_ET_STANDARD:
+      hitType = CmxEnergySubBlock::SUM_ET;
+      sumType = CmxEnergySubBlock::STANDARD;
+      break;
+    case LVL1::CMXEtSums::SUM_ET_RESTRICTED:
+      hitType = CmxEnergySubBlock::SUM_ET;
+      sumType = CmxEnergySubBlock::RESTRICTED_WEIGHTED;
+      break;
+    case LVL1::CMXEtSums::MISSING_ET_STANDARD:
+      hitType = CmxEnergySubBlock::MISSING_ET;
+      sumType = CmxEnergySubBlock::STANDARD;
+      break;
+    case LVL1::CMXEtSums::MISSING_ET_RESTRICTED:
+      hitType = CmxEnergySubBlock::MISSING_ET;
+      sumType = CmxEnergySubBlock::RESTRICTED_WEIGHTED;
+      break;
+    case LVL1::CMXEtSums::MISSING_ET_SIG_STANDARD:
+      hitType = CmxEnergySubBlock::MISSING_ET_SIG;
+      sumType = CmxEnergySubBlock::STANDARD;
+      break;
+    default:
+      break;
+  }
+}
+
+// Get jet hits subBlock source ID from CMXJetHits source type
+
+int JepByteStreamV2Tool::jetSubBlockSourceId(const int source)
+{
+  int sourceId = CmxJetSubBlock::MAX_SOURCE_ID;
+  switch (source) {
+    case LVL1::CMXJetHits::REMOTE_MAIN:
+      sourceId = CmxJetSubBlock::REMOTE_MAIN;
+      break;
+    case LVL1::CMXJetHits::LOCAL_MAIN:
+      sourceId = CmxJetSubBlock::LOCAL_MAIN;
+      break;
+    case LVL1::CMXJetHits::TOTAL_MAIN:
+      sourceId = CmxJetSubBlock::TOTAL_MAIN;
+      break;
+    case LVL1::CMXJetHits::REMOTE_FORWARD:
+      sourceId = CmxJetSubBlock::REMOTE_FORWARD;
+      break;
+    case LVL1::CMXJetHits::LOCAL_FORWARD:
+      sourceId = CmxJetSubBlock::LOCAL_FORWARD;
+      break;
+    case LVL1::CMXJetHits::TOTAL_FORWARD:
+      sourceId = CmxJetSubBlock::TOTAL_FORWARD;
+      break;
+    case LVL1::CMXJetHits::TOPO_CHECKSUM:
+      sourceId = CmxJetSubBlock::TOPO_CHECKSUM;
+      break;
+    case LVL1::CMXJetHits::TOPO_OCCUPANCY_MAP:
+      sourceId = CmxJetSubBlock::TOPO_OCCUPANCY_MAP;
+      break;
+    case LVL1::CMXJetHits::TOPO_OCCUPANCY_COUNTS:
+      sourceId = CmxJetSubBlock::TOPO_OCCUPANCY_COUNTS;
+      break;
+    default:
+      break;
+  }
+  return sourceId;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV2Tool.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV2Tool.h
new file mode 100755
index 0000000000000000000000000000000000000000..082775ca03798791dc9362b7ab016729d92d4785
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepByteStreamV2Tool.h
@@ -0,0 +1,257 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEPBYTESTREAMV2TOOL_H
+#define TRIGT1CALOBYTESTREAM_JEPBYTESTREAMV2TOOL_H
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+#include "ByteStreamData/RawEvent.h"
+#include "DataModel/DataVector.h"
+#include "eformat/SourceIdentifier.h"
+#include "GaudiKernel/ToolHandle.h"
+
+#include "CmxEnergySubBlock.h"
+
+class IInterface;
+class InterfaceID;
+class StatusCode;
+
+template <class T> class FullEventAssembler;
+
+namespace LVL1 {
+  class CMXJetHits;
+  class CMXJetTob;
+  class CMXEtSums;
+  class IL1CaloMappingTool;
+  class JEMEtSums;
+  class JEPBSCollectionV2;
+  class JetElement;
+  class JetElementKey;
+}
+
+namespace LVL1BS {
+
+class CmxJetSubBlock;
+class JemSubBlockV2;
+class L1CaloErrorByteStreamTool;
+class L1CaloSrcIdMap;
+
+/** Tool to perform ROB fragments to jet elements, jet hits and energy sums,
+ *  and JEP container to raw data conversions.
+ *
+ *  Based on ROD document version X_xxx.                                     <<== CHECK
+ *
+ *  @author Peter Faulkner
+ */
+
+class JepByteStreamV2Tool : public AthAlgTool {
+
+ public:
+   JepByteStreamV2Tool(const std::string& type, const std::string& name,
+                       const IInterface* parent);
+   virtual ~JepByteStreamV2Tool();
+
+   /// AlgTool InterfaceID
+   static const InterfaceID& interfaceID();
+
+   virtual StatusCode initialize();
+   virtual StatusCode finalize();
+
+   /// Convert ROB fragments to jet elements
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::JetElement>* jeCollection);
+   /// Convert ROB fragments to energy sums
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::JEMEtSums>* etCollection);
+   /// Convert ROB fragments to CMX TOBs
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::CMXJetTob>* tobCollection);
+   /// Convert ROB fragments to CMX jet hits
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::CMXJetHits>* hitCollection);
+   /// Convert ROB fragments to CMX energy sums
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::CMXEtSums>* etCollection);
+
+   /// Convert JEP Container to bytestream
+   StatusCode convert(const LVL1::JEPBSCollectionV2* jep, RawEventWrite* re);
+
+   /// Return reference to vector with all possible Source Identifiers
+   const std::vector<uint32_t>& sourceIDs(const std::string& sgKey);
+
+ private:
+   enum CollectionType { JET_ELEMENTS, ENERGY_SUMS, CMX_TOBS,
+                                       CMX_HITS, CMX_SUMS };
+
+   typedef DataVector<LVL1::JetElement>                  JetElementCollection;
+   typedef DataVector<LVL1::JEMEtSums>                   EnergySumsCollection;
+   typedef DataVector<LVL1::CMXJetTob>                   CmxTobCollection;
+   typedef DataVector<LVL1::CMXJetHits>                  CmxHitsCollection;
+   typedef DataVector<LVL1::CMXEtSums>                   CmxSumsCollection;
+   typedef std::map<unsigned int, LVL1::JetElement*>     JetElementMap;
+   typedef std::map<int, LVL1::JEMEtSums*>               EnergySumsMap;
+   typedef std::map<int, LVL1::CMXJetTob*>               CmxTobMap;
+   typedef std::map<int, LVL1::CMXJetHits*>              CmxHitsMap;
+   typedef std::map<int, LVL1::CMXEtSums*>               CmxSumsMap;
+   typedef IROBDataProviderSvc::VROBFRAG::const_iterator ROBIterator;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      ROBPointer;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      RODPointer;
+
+   /// Convert bytestream to given container type
+   StatusCode convertBs(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                        CollectionType collection);
+   /// Unpack CMX-Energy sub-block
+   void decodeCmxEnergy(CmxEnergySubBlock* subBlock, int trigJem);
+   /// Unpack CMX-Jet sub-block
+   void decodeCmxJet(CmxJetSubBlock* subBlock, int trigJem,
+                                         CollectionType collection);
+   /// Unpack JEM sub-block
+   void decodeJem(JemSubBlockV2* subBlock, int trigJem,
+                                         CollectionType collection);
+
+   /// Find TOB map key for given crate, jem, frame, loc
+   int tobKey(int crate, int jem, int frame, int loc);
+   /// Find a jet element given eta, phi
+   LVL1::JetElement* findJetElement(double eta, double phi);
+   /// Find energy sums for given crate, module
+   LVL1::JEMEtSums*  findEnergySums(int crate, int module);
+   /// Find CMX TOB for given key
+   LVL1::CMXJetTob*  findCmxTob(int key);
+   /// Find CMX hits for given crate, source
+   LVL1::CMXJetHits* findCmxHits(int crate, int source);
+   /// Find CMX energy sums for given crate, source
+   LVL1::CMXEtSums*  findCmxSums(int crate, int source);
+
+   /// Set up jet element map
+   void setupJeMap(const JetElementCollection* jeCollection);
+   /// Set up energy sums map
+   void setupEtMap(const EnergySumsCollection* enCollection);
+   /// Set up CMX TOB map
+   void setupCmxTobMap(const CmxTobCollection* tobCollection);
+   /// Set up CMX hits map
+   void setupCmxHitsMap(const CmxHitsCollection* hitCollection);
+   /// Set up CMX energy sums map
+   void setupCmxEtMap(const CmxSumsCollection* enCollection);
+
+   /// Get number of slices and triggered slice offset for next slink
+   bool slinkSlices(int crate, int module, int modulesPerSlink,
+                    int& timeslices, int& trigJem);
+   /// Get energy subBlock types from CMXEtSums source type
+   void energySubBlockTypes(int source,
+                            CmxEnergySubBlock::SourceType& srcType,
+			    CmxEnergySubBlock::SumType&    sumType,
+			    CmxEnergySubBlock::HitsType&   hitType);
+   /// Get jet hits subBlock source ID from CMXJetHits source type
+   int jetSubBlockSourceId(int source);
+
+   /// Channel mapping tool
+   ToolHandle<LVL1::IL1CaloMappingTool> m_jemMaps;
+   /// Error collection tool
+   ToolHandle<LVL1BS::L1CaloErrorByteStreamTool> m_errorTool;
+
+   /// Hardware crate number offset
+   int m_crateOffsetHw;
+   /// Software crate number offset
+   int m_crateOffsetSw;
+   /// Sub_block header version
+   int m_version;
+   /// Data compression format
+   int m_dataFormat;
+   /// Number of channels per module
+   int m_channels;
+   /// Number of crates
+   int m_crates;
+   /// Number of JEM modules per crate
+   int m_modules;
+   /// Number of RoI frames
+   int m_frames;
+   /// Number of RoI locations
+   int m_locations;
+   /// Maximum number of TOBs per module
+   int m_maxTobs;
+   /// Number of slinks per crate when writing out bytestream
+   int m_slinks;
+   /// Default number of slices in simulation
+   int m_dfltSlices;
+   /// Force number of slices in bytestream
+   int m_forceSlices;
+   /// Minimum crate number when writing out bytestream
+   int m_crateMin;
+   /// Maximum crate number when writing out bytestream
+   int m_crateMax;
+   /// Jet elements to accept (0=Core, 1=Overlap)
+   int m_coreOverlap;
+   /// Unpacking error code
+   unsigned int m_rodErr;
+   /// ROB source IDs
+   std::vector<uint32_t> m_sourceIDs;
+   /// Sub-detector type
+   eformat::SubDetector m_subDetector;
+   /// Source ID converter
+   L1CaloSrcIdMap* m_srcIdMap;
+   /// Jet element key provider
+   LVL1::JetElementKey* m_elementKey;
+   /// JemSubBlock for unpacking
+   JemSubBlockV2* m_jemSubBlock;
+   /// CmxEnergySubBlock for unpacking
+   CmxEnergySubBlock* m_cmxEnergySubBlock;
+   /// CmxJetSubBlock for unpacking
+   CmxJetSubBlock* m_cmxJetSubBlock;
+   /// Unsigned int unpacking vector 0
+   std::vector<unsigned int> m_uintVec0;
+   /// Unsigned int unpacking vector 1
+   std::vector<unsigned int> m_uintVec1;
+   /// Unsigned int unpacking vector 2
+   std::vector<unsigned int> m_uintVec2;
+   /// Int unpacking vector 0
+   std::vector<int> m_intVec0;
+   /// Int unpacking vector 1
+   std::vector<int> m_intVec1;
+   /// Int unpacking vector 2
+   std::vector<int> m_intVec2;
+   /// Vector for current JEM sub-blocks
+   DataVector<JemSubBlockV2> m_jemBlocks;
+   /// Vector for current CMX-Energy sub-blocks
+   DataVector<CmxEnergySubBlock> m_cmxEnergyBlocks;
+   /// Vector for current CMX-Jet sub-blocks
+   DataVector<CmxJetSubBlock> m_cmxJetBlocks;
+   /// Current jet elements collection
+   JetElementCollection* m_jeCollection;
+   /// Current energy sums collection
+   EnergySumsCollection* m_etCollection;
+   /// Current CMX TOB collection
+   CmxTobCollection*     m_cmxTobCollection;
+   /// Current CMX hits collection
+   CmxHitsCollection*    m_cmxHitCollection;
+   /// Current CMX energy sums collection
+   CmxSumsCollection*    m_cmxEtCollection;
+   /// Jet element map
+   JetElementMap m_jeMap;
+   /// Energy sums map
+   EnergySumsMap m_etMap;
+   /// CMX TOB map
+   CmxTobMap     m_cmxTobMap;
+   /// CMX hits map
+   CmxHitsMap    m_cmxHitsMap;
+   /// CMX energy sums map
+   CmxSumsMap    m_cmxEtMap;
+   /// ROD Status words
+   std::vector<uint32_t>* m_rodStatus;
+   /// ROD status map
+   std::map<uint32_t, std::vector<uint32_t>* > m_rodStatusMap;
+   /// Event assembler
+   FullEventAssembler<L1CaloSrcIdMap>* m_fea;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamCnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamCnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..4556068a8e101252e77246a589fc3756d26d35ce
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamCnv.h
@@ -0,0 +1,79 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEPREADBYTESTREAMCNV_H
+#define TRIGT1CALOBYTESTREAM_JEPREADBYTESTREAMCNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IOpaqueAddress;
+class IROBDataProviderSvc;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class JepByteStreamTool;
+
+/** ByteStream converter for JEP component containers.
+ *
+ *  @author Peter Faulkner
+ */
+
+template <typename Container>
+class JepReadByteStreamCnv: public Converter {
+
+  friend class CnvFactory<JepReadByteStreamCnv<Container> >;
+
+protected:
+
+  JepReadByteStreamCnv(ISvcLocator* svcloc);
+
+public:
+
+  ~JepReadByteStreamCnv();
+
+  virtual StatusCode initialize();
+  /// Create Container from ByteStream
+  virtual StatusCode createObj(IOpaqueAddress* pAddr, DataObject*& pObj);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<JepByteStreamTool> m_tool;
+
+  /// Service for reading bytestream
+  ServiceHandle<IROBDataProviderSvc> m_robDataProvider;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#include "JepReadByteStreamCnv.icc"
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamCnv.icc b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamCnv.icc
new file mode 100755
index 0000000000000000000000000000000000000000..57cc50e057958db59307b50932e29ccb4ccbe7ba
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamCnv.icc
@@ -0,0 +1,140 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+#include <stdint.h>
+#include <typeinfo>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "JepByteStreamTool.h"
+
+namespace LVL1BS {
+
+template <typename Container>
+JepReadByteStreamCnv<Container>::JepReadByteStreamCnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("JepReadByteStreamCnv"),
+      m_tool("LVL1BS::JepByteStreamTool/JepByteStreamTool"),
+      m_robDataProvider("ROBDataProviderSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+template <typename Container>
+JepReadByteStreamCnv<Container>::~JepReadByteStreamCnv()
+{
+}
+
+// CLID
+
+template <typename Container>
+const CLID& JepReadByteStreamCnv<Container>::classID()
+{
+  return ClassID_traits<Container>::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+template <typename Container>
+StatusCode JepReadByteStreamCnv<Container>::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << "<"
+                      << typeid(Container).name() << "> - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return StatusCode::FAILURE;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  // Get ROBDataProvider
+  sc = m_robDataProvider.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_robDataProvider << endreq;
+    return sc;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_robDataProvider << endreq;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// createObj should create the RDO from bytestream.
+
+template <typename Container>
+StatusCode JepReadByteStreamCnv<Container>::createObj( IOpaqueAddress* pAddr,
+                                                       DataObject*& pObj )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createObj() called" << endreq;
+
+  ByteStreamAddress *pBS_Addr;
+  pBS_Addr = dynamic_cast<ByteStreamAddress *>( pAddr );
+  if ( !pBS_Addr ) {
+    m_log << MSG::ERROR << " Can not cast to ByteStreamAddress " << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = *( pBS_Addr->par() );
+
+  if (m_debug) m_log << MSG::DEBUG << " Creating Objects " << nm << endreq;
+
+  // get SourceIDs
+  const std::vector<uint32_t>& vID(m_tool->sourceIDs(nm));
+
+  // get ROB fragments
+  IROBDataProviderSvc::VROBFRAG robFrags;
+  m_robDataProvider->getROBData( vID, robFrags );
+
+  // size check
+  Container* const collection = new Container;
+  if (m_debug) {
+    m_log << MSG::DEBUG << " Number of ROB fragments is " << robFrags.size()
+          << endreq;
+  }
+  if (robFrags.size() == 0) {
+    pObj = SG::asStorable(collection) ;
+    return StatusCode::SUCCESS;
+  }
+
+  StatusCode sc = m_tool->convert(robFrags, collection);
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << " Failed to create Objects   " << nm << endreq;
+    delete collection;
+    return sc;
+  }
+
+  pObj = SG::asStorable(collection);
+
+  return sc;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamV1Cnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamV1Cnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..07f0c0d66ffcc96248c1c5998349facd07048a4c
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamV1Cnv.h
@@ -0,0 +1,79 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEPREADBYTESTREAMV1CNV_H
+#define TRIGT1CALOBYTESTREAM_JEPREADBYTESTREAMV1CNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IOpaqueAddress;
+class IROBDataProviderSvc;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class JepByteStreamV1Tool;
+
+/** ByteStream converter for JEP component containers.
+ *
+ *  @author Peter Faulkner
+ */
+
+template <typename Container>
+class JepReadByteStreamV1Cnv: public Converter {
+
+  friend class CnvFactory<JepReadByteStreamV1Cnv<Container> >;
+
+protected:
+
+  JepReadByteStreamV1Cnv(ISvcLocator* svcloc);
+
+public:
+
+  ~JepReadByteStreamV1Cnv();
+
+  virtual StatusCode initialize();
+  /// Create Container from ByteStream
+  virtual StatusCode createObj(IOpaqueAddress* pAddr, DataObject*& pObj);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<JepByteStreamV1Tool> m_tool;
+
+  /// Service for reading bytestream
+  ServiceHandle<IROBDataProviderSvc> m_robDataProvider;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#include "JepReadByteStreamV1Cnv.icc"
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamV1Cnv.icc b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamV1Cnv.icc
new file mode 100755
index 0000000000000000000000000000000000000000..51d5adedbd19e2dbf6a0cb6c7cc5be0caada5f27
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamV1Cnv.icc
@@ -0,0 +1,140 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+#include <stdint.h>
+#include <typeinfo>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "JepByteStreamV1Tool.h"
+
+namespace LVL1BS {
+
+template <typename Container>
+JepReadByteStreamV1Cnv<Container>::JepReadByteStreamV1Cnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("JepReadByteStreamV1Cnv"),
+      m_tool("LVL1BS::JepByteStreamV1Tool/JepByteStreamV1Tool"),
+      m_robDataProvider("ROBDataProviderSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+template <typename Container>
+JepReadByteStreamV1Cnv<Container>::~JepReadByteStreamV1Cnv()
+{
+}
+
+// CLID
+
+template <typename Container>
+const CLID& JepReadByteStreamV1Cnv<Container>::classID()
+{
+  return ClassID_traits<Container>::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+template <typename Container>
+StatusCode JepReadByteStreamV1Cnv<Container>::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << "<"
+                      << typeid(Container).name() << "> - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return StatusCode::FAILURE;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  // Get ROBDataProvider
+  sc = m_robDataProvider.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_robDataProvider << endreq;
+    return sc;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_robDataProvider << endreq;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// createObj should create the RDO from bytestream.
+
+template <typename Container>
+StatusCode JepReadByteStreamV1Cnv<Container>::createObj( IOpaqueAddress* pAddr,
+                                                         DataObject*& pObj )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createObj() called" << endreq;
+
+  ByteStreamAddress *pBS_Addr;
+  pBS_Addr = dynamic_cast<ByteStreamAddress *>( pAddr );
+  if ( !pBS_Addr ) {
+    m_log << MSG::ERROR << " Can not cast to ByteStreamAddress " << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = *( pBS_Addr->par() );
+
+  if (m_debug) m_log << MSG::DEBUG << " Creating Objects " << nm << endreq;
+
+  // get SourceIDs
+  const std::vector<uint32_t>& vID(m_tool->sourceIDs(nm));
+
+  // get ROB fragments
+  IROBDataProviderSvc::VROBFRAG robFrags;
+  m_robDataProvider->getROBData( vID, robFrags );
+
+  // size check
+  Container* const collection = new Container;
+  if (m_debug) {
+    m_log << MSG::DEBUG << " Number of ROB fragments is " << robFrags.size()
+          << endreq;
+  }
+  if (robFrags.size() == 0) {
+    pObj = SG::asStorable(collection) ;
+    return StatusCode::SUCCESS;
+  }
+
+  StatusCode sc = m_tool->convert(robFrags, collection);
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << " Failed to create Objects   " << nm << endreq;
+    delete collection;
+    return sc;
+  }
+
+  pObj = SG::asStorable(collection);
+
+  return sc;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamV1V2Cnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamV1V2Cnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..9c0b17020bd89799991c3ea2a2c0dd5af43ab438
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamV1V2Cnv.h
@@ -0,0 +1,85 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEPREADBYTESTREAMV1V2CNV_H
+#define TRIGT1CALOBYTESTREAM_JEPREADBYTESTREAMV1V2CNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IByteStreamEventAccess;
+class IOpaqueAddress;
+class IROBDataProviderSvc;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class JepByteStreamV1Tool;
+class JepByteStreamV2Tool;
+
+/** ByteStream converter for JEP component containers which are unchanged
+ *  post-LS1.  Allows for data containing pre- or post-LS1 format sub-blocks.
+ *  For reading only.
+ *
+ *  @author Peter Faulkner
+ */
+
+template <typename Container>
+class JepReadByteStreamV1V2Cnv: public Converter {
+
+  friend class CnvFactory<JepReadByteStreamV1V2Cnv<Container> >;
+
+protected:
+
+  JepReadByteStreamV1V2Cnv(ISvcLocator* svcloc);
+
+public:
+
+  ~JepReadByteStreamV1V2Cnv();
+
+  virtual StatusCode initialize();
+  /// Create Container from ByteStream
+  virtual StatusCode createObj(IOpaqueAddress* pAddr, DataObject*& pObj);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work pre-LS1
+  ToolHandle<LVL1BS::JepByteStreamV1Tool> m_tool1;
+  /// Tool that does the actual work post-LS1
+  ToolHandle<LVL1BS::JepByteStreamV2Tool> m_tool2;
+
+  /// Service for reading bytestream
+  ServiceHandle<IROBDataProviderSvc> m_robDataProvider;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#include "JepReadByteStreamV1V2Cnv.icc"
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamV1V2Cnv.icc b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamV1V2Cnv.icc
new file mode 100755
index 0000000000000000000000000000000000000000..29f7c2b464f91ae875a154a02e826131f5ed7515
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamV1V2Cnv.icc
@@ -0,0 +1,164 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+#include <stdint.h>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IByteStreamEventAccess.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "DataModel/DataVector.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/IRegistry.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "JepByteStreamV1Tool.h"
+#include "JepByteStreamV2Tool.h"
+
+namespace LVL1BS {
+
+template <typename Container>
+JepReadByteStreamV1V2Cnv<Container>::JepReadByteStreamV1V2Cnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("JepReadByteStreamV1V2Cnv"),
+      m_tool1("LVL1BS::JepByteStreamV1Tool/JepByteStreamV1Tool"),
+      m_tool2("LVL1BS::JepByteStreamV2Tool/JepByteStreamV2Tool"),
+      m_robDataProvider("ROBDataProviderSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+template <typename Container>
+JepReadByteStreamV1V2Cnv<Container>::~JepReadByteStreamV1V2Cnv()
+{
+}
+
+// CLID
+
+template <typename Container>
+const CLID& JepReadByteStreamV1V2Cnv<Container>::classID()
+{
+  return ClassID_traits<Container>::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+template <typename Container>
+StatusCode JepReadByteStreamV1V2Cnv<Container>::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << " - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  // Retrieve Tools
+  sc = m_tool1.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool1 << endreq;
+    return StatusCode::FAILURE;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool1 << endreq;
+  sc = m_tool2.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool2 << endreq;
+    return StatusCode::FAILURE;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool2 << endreq;
+
+  // Get ROBDataProvider
+  sc = m_robDataProvider.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::WARNING << "Failed to retrieve service "
+          << m_robDataProvider << endreq;
+    return sc ;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_robDataProvider << endreq;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// createObj should create the RDO from bytestream.
+
+template <typename Container>
+StatusCode JepReadByteStreamV1V2Cnv<Container>::createObj( IOpaqueAddress* pAddr,
+                                                DataObject*& pObj )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createObj() called" << endreq;
+
+  ByteStreamAddress *pBS_Addr;
+  pBS_Addr = dynamic_cast<ByteStreamAddress *>( pAddr );
+  if ( !pBS_Addr ) {
+    m_log << MSG::ERROR << " Can not cast to ByteStreamAddress " << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = *( pBS_Addr->par() );
+
+  if (m_debug) m_log << MSG::DEBUG << " Creating Objects " << nm << endreq;
+
+  // get SourceIDs
+  const std::vector<uint32_t>& vID1(m_tool1->sourceIDs(nm));
+  const std::vector<uint32_t>& vID2(m_tool2->sourceIDs(nm));
+
+  // get ROB fragments
+  IROBDataProviderSvc::VROBFRAG robFrags1;
+  m_robDataProvider->getROBData( vID1, robFrags1 );
+  IROBDataProviderSvc::VROBFRAG robFrags2;
+  m_robDataProvider->getROBData( vID2, robFrags2 );
+
+  // size check
+  Container* const collection = new Container;
+  if (m_debug) {
+    m_log << MSG::DEBUG << " Number of ROB fragments is " << robFrags1.size()
+          << ", " << robFrags2.size() << endreq;
+  }
+  if (robFrags1.size() == 0 && robFrags2.size() == 0) {
+    pObj = SG::asStorable(collection) ;
+    return StatusCode::SUCCESS;
+  }
+
+  // Pre-LS1 data
+  if (robFrags1.size() > 0) {
+    StatusCode sc = m_tool1->convert(robFrags1, collection);
+    if ( sc.isFailure() ) {
+      m_log << MSG::ERROR << " Failed to create Objects   " << nm << endreq;
+      delete collection;
+      return sc;
+    }
+  }
+  // Post-LS1 data
+  if (robFrags2.size() > 0) {
+    StatusCode sc = m_tool2->convert(robFrags2, collection);
+    if ( sc.isFailure() ) {
+      m_log << MSG::ERROR << " Failed to create Objects   " << nm << endreq;
+      delete collection;
+      return sc;
+    }
+  }
+
+  pObj = SG::asStorable(collection);
+
+  return StatusCode::SUCCESS;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamV2Cnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamV2Cnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..babe217385f6f3424f29f32d387f6ec2cf90f90a
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamV2Cnv.h
@@ -0,0 +1,79 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEPREADBYTESTREAMV2CNV_H
+#define TRIGT1CALOBYTESTREAM_JEPREADBYTESTREAMV2CNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IOpaqueAddress;
+class IROBDataProviderSvc;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class JepByteStreamV2Tool;
+
+/** ByteStream converter for JEP component containers post LS1.
+ *
+ *  @author Peter Faulkner
+ */
+
+template <typename Container>
+class JepReadByteStreamV2Cnv: public Converter {
+
+  friend class CnvFactory<JepReadByteStreamV2Cnv<Container> >;
+
+protected:
+
+  JepReadByteStreamV2Cnv(ISvcLocator* svcloc);
+
+public:
+
+  ~JepReadByteStreamV2Cnv();
+
+  virtual StatusCode initialize();
+  /// Create Container from ByteStream
+  virtual StatusCode createObj(IOpaqueAddress* pAddr, DataObject*& pObj);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<JepByteStreamV2Tool> m_tool;
+
+  /// Service for reading bytestream
+  ServiceHandle<IROBDataProviderSvc> m_robDataProvider;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#include "JepReadByteStreamV2Cnv.icc"
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamV2Cnv.icc b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamV2Cnv.icc
new file mode 100755
index 0000000000000000000000000000000000000000..9aad45c04c12c1cb376acbb929f0b65536b98fe5
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepReadByteStreamV2Cnv.icc
@@ -0,0 +1,140 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+#include <stdint.h>
+#include <typeinfo>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "JepByteStreamV2Tool.h"
+
+namespace LVL1BS {
+
+template <typename Container>
+JepReadByteStreamV2Cnv<Container>::JepReadByteStreamV2Cnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("JepReadByteStreamV2Cnv"),
+      m_tool("LVL1BS::JepByteStreamV2Tool/JepByteStreamV2Tool"),
+      m_robDataProvider("ROBDataProviderSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+template <typename Container>
+JepReadByteStreamV2Cnv<Container>::~JepReadByteStreamV2Cnv()
+{
+}
+
+// CLID
+
+template <typename Container>
+const CLID& JepReadByteStreamV2Cnv<Container>::classID()
+{
+  return ClassID_traits<Container>::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+template <typename Container>
+StatusCode JepReadByteStreamV2Cnv<Container>::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << "<"
+                      << typeid(Container).name() << "> - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return StatusCode::FAILURE;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  // Get ROBDataProvider
+  sc = m_robDataProvider.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_robDataProvider << endreq;
+    return sc;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_robDataProvider << endreq;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// createObj should create the RDO from bytestream.
+
+template <typename Container>
+StatusCode JepReadByteStreamV2Cnv<Container>::createObj( IOpaqueAddress* pAddr,
+                                                         DataObject*& pObj )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createObj() called" << endreq;
+
+  ByteStreamAddress *pBS_Addr;
+  pBS_Addr = dynamic_cast<ByteStreamAddress *>( pAddr );
+  if ( !pBS_Addr ) {
+    m_log << MSG::ERROR << " Can not cast to ByteStreamAddress " << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = *( pBS_Addr->par() );
+
+  if (m_debug) m_log << MSG::DEBUG << " Creating Objects " << nm << endreq;
+
+  // get SourceIDs
+  const std::vector<uint32_t>& vID(m_tool->sourceIDs(nm));
+
+  // get ROB fragments
+  IROBDataProviderSvc::VROBFRAG robFrags;
+  m_robDataProvider->getROBData( vID, robFrags );
+
+  // size check
+  Container* const collection = new Container;
+  if (m_debug) {
+    m_log << MSG::DEBUG << " Number of ROB fragments is " << robFrags.size()
+          << endreq;
+  }
+  if (robFrags.size() == 0) {
+    pObj = SG::asStorable(collection) ;
+    return StatusCode::SUCCESS;
+  }
+
+  StatusCode sc = m_tool->convert(robFrags, collection);
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << " Failed to create Objects   " << nm << endreq;
+    delete collection;
+    return sc;
+  }
+
+  pObj = SG::asStorable(collection);
+
+  return sc;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamCnv.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamCnv.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..a643f5545dc4b84167d142467691e799c7eac72e
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamCnv.cxx
@@ -0,0 +1,115 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IByteStreamEventAccess.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "DataModel/DataVector.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/IRegistry.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "TrigT1CaloEvent/JEPRoIBSCollection.h"
+
+#include "JepRoiByteStreamCnv.h"
+#include "JepRoiByteStreamTool.h"
+
+namespace LVL1BS {
+
+JepRoiByteStreamCnv::JepRoiByteStreamCnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("JepRoiByteStreamCnv"),
+      m_tool("LVL1BS::JepRoiByteStreamTool/JepRoiByteStreamTool"),
+      m_ByteStreamEventAccess("ByteStreamCnvSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+JepRoiByteStreamCnv::~JepRoiByteStreamCnv()
+{
+}
+
+// CLID
+
+const CLID& JepRoiByteStreamCnv::classID()
+{
+  return ClassID_traits<LVL1::JEPRoIBSCollection>::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode JepRoiByteStreamCnv::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << " - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  //Get ByteStreamCnvSvc
+  sc = m_ByteStreamEventAccess.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_ByteStreamEventAccess << endreq;
+    return sc;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_ByteStreamEventAccess << endreq;
+  }
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return StatusCode::FAILURE;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  return StatusCode::SUCCESS;
+}
+
+// createRep should create the bytestream from RDOs.
+
+StatusCode JepRoiByteStreamCnv::createRep( DataObject* pObj,
+                                        IOpaqueAddress*& pAddr )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createRep() called" << endreq;
+
+  RawEventWrite* re = m_ByteStreamEventAccess->getRawEvent();
+
+  LVL1::JEPRoIBSCollection* jep = 0;
+  if( !SG::fromStorable( pObj, jep ) ) {
+    m_log << MSG::ERROR << " Cannot cast to JEPRoIBSCollection" << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = pObj->registry()->name();
+
+  ByteStreamAddress* addr = new ByteStreamAddress( classID(), nm, "" );
+
+  pAddr = addr;
+
+  // Convert to ByteStream
+  return m_tool->convert( jep, re );
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamCnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamCnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..decd4d00cc63a4ee8fd37898eae25ba2d3f6e392
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamCnv.h
@@ -0,0 +1,76 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEPROIBYTESTREAMCNV_H
+#define TRIGT1CALOBYTESTREAM_JEPROIBYTESTREAMCNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IByteStreamEventAccess;
+class IOpaqueAddress;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class JepRoiByteStreamTool;
+
+/** ByteStream converter for JEP RoI container
+ *
+ *  @author Peter Faulkner
+ */
+
+class JepRoiByteStreamCnv: public Converter {
+
+  friend class CnvFactory<JepRoiByteStreamCnv>;
+
+protected:
+
+  JepRoiByteStreamCnv(ISvcLocator* svcloc);
+
+public:
+
+  ~JepRoiByteStreamCnv();
+
+  virtual StatusCode initialize();
+  /// Create ByteStream from JEP Container
+  virtual StatusCode createRep(DataObject* pObj, IOpaqueAddress*& pAddr);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::JepRoiByteStreamTool> m_tool;
+
+  /// Service for writing bytestream
+  ServiceHandle<IByteStreamEventAccess> m_ByteStreamEventAccess;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamTool.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamTool.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..53193184b2d903a634dbb44d3287e9a836c73229
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamTool.cxx
@@ -0,0 +1,733 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <numeric>
+#include <set>
+#include <utility>
+
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "ByteStreamCnvSvcBase/FullEventAssembler.h"
+
+#include "TrigT1CaloEvent/CMMJetHits.h"
+#include "TrigT1CaloEvent/CMMEtSums.h"
+#include "TrigT1CaloEvent/CMMRoI.h"
+#include "TrigT1CaloEvent/JEMRoI.h"
+#include "TrigT1CaloEvent/JEPRoIBSCollection.h"
+
+#include "CmmEnergySubBlock.h"
+#include "CmmJetSubBlock.h"
+#include "CmmSubBlock.h"
+#include "JemRoiSubBlock.h"
+#include "L1CaloErrorByteStreamTool.h"
+#include "L1CaloSrcIdMap.h"
+#include "L1CaloSubBlock.h"
+#include "L1CaloUserHeader.h"
+
+#include "JepRoiByteStreamTool.h"
+
+namespace LVL1BS {
+
+// Interface ID
+
+static const InterfaceID IID_IJepRoiByteStreamTool("JepRoiByteStreamTool",
+                                                                        1, 1);
+
+const InterfaceID& JepRoiByteStreamTool::interfaceID()
+{
+  return IID_IJepRoiByteStreamTool;
+}
+
+// Constructor
+
+JepRoiByteStreamTool::JepRoiByteStreamTool(const std::string& type,
+                                           const std::string& name,
+				           const IInterface*  parent)
+  : AthAlgTool(type, name, parent),
+    m_errorTool("LVL1BS::L1CaloErrorByteStreamTool/L1CaloErrorByteStreamTool"),
+    m_crates(2), m_modules(16), m_srcIdMap(0), m_subBlock(0), m_rodStatus(0),
+    m_fea(0)
+{
+  declareInterface<JepRoiByteStreamTool>(this);
+
+  declareProperty("CrateOffsetHw",  m_crateOffsetHw = 12,
+                  "Offset of JEP crate numbers in bytestream");
+  declareProperty("CrateOffsetSw",  m_crateOffsetSw = 0,
+                  "Offset of JEP crate numbers in RDOs");
+
+  // Properties for reading bytestream only
+  declareProperty("ROBSourceIDs",       m_sourceIDs,
+                  "ROB fragment source identifiers");
+  declareProperty("ROBSourceIDsRoIB",   m_sourceIDsRoIB,
+                  "ROB fragment source identifiers");
+
+  // Properties for writing bytestream only
+  declareProperty("DataVersion",    m_version       = 1,
+                  "Format version number in sub-block header");
+  declareProperty("DataFormat",     m_dataFormat    = 1,
+                  "Format identifier (0-1) in sub-block header");
+  declareProperty("SlinksPerCrate", m_slinks        = 1,
+                  "The number of S-Links per crate");
+
+}
+
+// Destructor
+
+JepRoiByteStreamTool::~JepRoiByteStreamTool()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode JepRoiByteStreamTool::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = m_errorTool.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_errorTool << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_errorTool << endreq;
+
+  m_subDetector = eformat::TDAQ_CALO_JET_PROC_ROI;
+  m_srcIdMap    = new L1CaloSrcIdMap();
+  m_subBlock    = new JemRoiSubBlock();
+  m_rodStatus   = new std::vector<uint32_t>(2);
+  m_fea         = new FullEventAssembler<L1CaloSrcIdMap>();
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode JepRoiByteStreamTool::finalize()
+{
+  delete m_fea;
+  delete m_rodStatus;
+  delete m_subBlock;
+  delete m_srcIdMap;
+  return StatusCode::SUCCESS;
+}
+
+// Conversion bytestream to JEM RoI
+
+StatusCode JepRoiByteStreamTool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::JEMRoI>* const jeCollection)
+{
+  m_jeCollection = jeCollection;
+  return convertBs(robFrags, JEM_ROI);
+}
+
+// Conversion bytestream to CMM RoI
+
+StatusCode JepRoiByteStreamTool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            LVL1::CMMRoI* const cmCollection)
+{
+  m_cmCollection = cmCollection;
+  return convertBs(robFrags, CMM_ROI);
+}
+
+// Conversion of JEP container to bytestream
+
+StatusCode JepRoiByteStreamTool::convert(
+                                 const LVL1::JEPRoIBSCollection* const jep,
+                                 RawEventWrite* const re)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Clear the event assembler
+
+  m_fea->clear();
+  const uint16_t minorVersion = m_srcIdMap->minorVersion();
+  m_fea->setRodMinorVersion(minorVersion);
+  m_rodStatusMap.clear();
+
+  // Pointer to ROD data vector
+
+  FullEventAssembler<L1CaloSrcIdMap>::RODDATA* theROD = 0;
+
+  // Set up the container maps
+
+  const bool neutralFormat = m_dataFormat == L1CaloSubBlock::NEUTRAL;
+  setupJemRoiMap(jep->JemRoi());
+  JemRoiMap::const_iterator mapIter    = m_roiMap.begin();
+  JemRoiMap::const_iterator mapIterEnd = m_roiMap.end();
+  if (neutralFormat) {
+    setupCmmHitsMap(jep->CmmHits());
+    setupCmmEtMap(jep->CmmSums());
+  }
+
+  // Loop over JEM RoI data
+
+  const int modulesPerSlink = m_modules / m_slinks;
+  for (int crate=0; crate < m_crates; ++crate) {
+    const int hwCrate = crate + m_crateOffsetHw;
+
+    for (int module=0; module < m_modules; ++module) {
+
+      // Pack required number of modules per slink
+
+      if (module%modulesPerSlink == 0) {
+	const int daqOrRoi = 1;
+	const int slink = module/modulesPerSlink;
+        if (debug) {
+          msg() << "Treating crate " << hwCrate
+	        << " slink " << slink << endreq
+	        << "Data Version/Format: " << m_version
+	        << " " << m_dataFormat << endreq;
+        }
+	const uint32_t rodIdJem = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+	                                                        m_subDetector);
+	theROD = m_fea->getRodData(rodIdJem);
+        if (neutralFormat) {
+          const L1CaloUserHeader userHeader;
+	  theROD->push_back(userHeader.header());
+        }
+	m_rodStatusMap.insert(make_pair(rodIdJem, m_rodStatus));
+      }
+      if (debug) msg() << "JEM Module " << module << endreq;
+      if (!theROD) break; // for coverity, shouldn't happen
+
+      // Create a sub-block (Neutral format only)
+
+      if (neutralFormat) {
+        m_subBlock->clear();
+	m_subBlock->setRoiHeader(m_version, hwCrate, module);
+      }
+
+      // Find JEM RoIs for this module
+
+      for (; mapIter != mapIterEnd; ++mapIter) {
+        const LVL1::JEMRoI* const roi = mapIter->second;
+	if (roi->crate() < crate)  continue;
+	if (roi->crate() > crate)  break;
+	if (roi->jem()   < module) continue;
+	if (roi->jem()   > module) break;
+	if (roi->hits() || roi->error()) {
+	  if (neutralFormat) m_subBlock->fillRoi(*roi);
+	  else theROD->push_back(roi->roiWord());
+        }
+      }
+
+      // Pack and write the sub-block
+
+      if (neutralFormat) {
+        if ( !m_subBlock->pack()) {
+	  msg(MSG::ERROR) << "JEM RoI sub-block packing failed" << endreq;
+	  return StatusCode::FAILURE;
+        }
+	if (debug) {
+	  msg() << "JEM RoI sub-block data words: "
+	        << m_subBlock->dataWords() << endreq;
+	}
+	m_subBlock->write(theROD);
+      }
+    }
+    if (!theROD) break; // for coverity, shouldn't happen
+
+    // Append CMM RoIs to last S-Link of the system crate
+
+    if (crate != m_crates - 1) continue;
+
+    // Create sub-blocks for Neutral format
+
+    if (neutralFormat) {
+      const int timeslices = 1;
+      const int slice = 0;
+
+      // CMM-Energy
+
+      CmmEnergySubBlock enBlock;
+      const int cmmEnergyVersion = 2; // with Missing-ET-Sig
+      enBlock.setCmmHeader(cmmEnergyVersion, m_dataFormat, slice, hwCrate,
+                           CmmSubBlock::SYSTEM, CmmSubBlock::CMM_ENERGY,
+			   CmmSubBlock::LEFT, timeslices);
+      int maxDataID = static_cast<int>(LVL1::CMMEtSums::MAXID);
+      for (int dataID = 0; dataID < maxDataID; ++dataID) {
+        int source = dataID;
+        if (dataID >= m_modules) {
+	  switch (dataID) {
+	    case LVL1::CMMEtSums::LOCAL:
+	      source = CmmEnergySubBlock::LOCAL;
+	      break;
+	    case LVL1::CMMEtSums::REMOTE:
+	      source = CmmEnergySubBlock::REMOTE;
+	      break;
+	    case LVL1::CMMEtSums::TOTAL:
+	      source = CmmEnergySubBlock::TOTAL;
+	      break;
+	    case LVL1::CMMEtSums::MISSING_ET_MAP:
+	    case LVL1::CMMEtSums::SUM_ET_MAP:
+	    case LVL1::CMMEtSums::MISSING_ET_SIG_MAP:
+	      break;
+            default:
+	      continue;
+          }
+        }
+        const LVL1::CMMEtSums* const sums = findCmmSums(crate, dataID);
+        if ( sums ) {
+          const unsigned int ex = sums->Ex();
+          const unsigned int ey = sums->Ey();
+          const unsigned int et = sums->Et();
+          const int exErr = sums->ExError();
+          const int eyErr = sums->EyError();
+          const int etErr = sums->EtError();
+	  if (dataID == LVL1::CMMEtSums::MISSING_ET_MAP) {
+	    enBlock.setMissingEtHits(slice, et); 
+          } else if (dataID == LVL1::CMMEtSums::SUM_ET_MAP) {
+	    enBlock.setSumEtHits(slice, et); 
+	  } else if (dataID == LVL1::CMMEtSums::MISSING_ET_SIG_MAP) {
+	    enBlock.setMissingEtSigHits(slice, et);
+          } else {
+	    enBlock.setSubsums(slice, source, ex, ey, et, exErr, eyErr, etErr);
+          }
+        }
+      }
+      if ( !enBlock.pack()) {
+        msg(MSG::ERROR) << "CMM-Energy sub-block packing failed" << endreq;
+	return StatusCode::FAILURE;
+      }
+      if (debug) {
+	msg() << "CMM-Energy sub-block data words: "
+	      << enBlock.dataWords() << endreq;
+      }
+      enBlock.write(theROD);
+
+      // CMM-Jet
+
+      CmmJetSubBlock jetBlock;
+      jetBlock.setCmmHeader(m_version, m_dataFormat, slice, hwCrate,
+                            CmmSubBlock::SYSTEM, CmmSubBlock::CMM_JET,
+			    CmmSubBlock::RIGHT, timeslices);
+      maxDataID = static_cast<int>(LVL1::CMMJetHits::MAXID);
+      for (int dataID = 0; dataID < maxDataID; ++dataID) {
+        int source = dataID;
+        if (dataID >= m_modules) {
+	  switch (dataID) {
+	    case LVL1::CMMJetHits::LOCAL_MAIN:
+	      source = CmmJetSubBlock::LOCAL_MAIN;
+	      break;
+	    case LVL1::CMMJetHits::REMOTE_MAIN:
+	      source = CmmJetSubBlock::REMOTE_MAIN;
+	      break;
+	    case LVL1::CMMJetHits::TOTAL_MAIN:
+	      source = CmmJetSubBlock::TOTAL_MAIN;
+	      break;
+	    case LVL1::CMMJetHits::LOCAL_FORWARD:
+	      source = CmmJetSubBlock::LOCAL_FORWARD;
+	      break;
+	    case LVL1::CMMJetHits::REMOTE_FORWARD:
+	      source = CmmJetSubBlock::REMOTE_FORWARD;
+	      break;
+	    case LVL1::CMMJetHits::TOTAL_FORWARD:
+	      source = CmmJetSubBlock::TOTAL_FORWARD;
+	      break;
+	    case LVL1::CMMJetHits::ET_MAP:
+	      break;
+            default:
+	      continue;
+          }
+        }
+        const LVL1::CMMJetHits* const ch = findCmmHits(crate, dataID);
+        if ( ch ) {
+          const unsigned int hits = ch->Hits();
+          const int          errs = ch->Error();
+	  if (dataID == LVL1::CMMJetHits::ET_MAP) {
+	    jetBlock.setJetEtMap(slice, hits);
+          } else {
+	    jetBlock.setJetHits(slice, source, hits, errs);
+          }
+        }
+      }
+      if ( !jetBlock.pack()) {
+        msg(MSG::ERROR) << "CMM-Jet sub-block packing failed" << endreq;
+	return StatusCode::FAILURE;
+      }
+      if (debug) {
+	msg() << "CMM-Jet sub-block data words: "
+	      << jetBlock.dataWords() << endreq;
+      }
+      jetBlock.write(theROD);
+
+    } else {
+
+      // Standard format
+
+      const LVL1::CMMRoI* const roi = jep->CmmRoi();
+      if ( roi ) {
+	// Make sure word IDs are correct
+        const LVL1::CMMRoI roid(roi->jetEtHits(), roi->sumEtHits(),
+	            roi->missingEtHits(), roi->missingEtSigHits(),
+		    roi->ex(), roi->ey(), roi->et(),
+		    roi->jetEtError(), roi->sumEtError(),
+		    roi->missingEtError(), roi->missingEtSigError(),
+		    roi->exError(), roi->eyError(), roi->etError());
+        if (roid.jetEtHits() || roid.jetEtError()) {
+          theROD->push_back(roid.jetEtRoiWord());
+        }
+        // CMM-Energy RoIs are not zero-supressed unless all are zero
+	if (roid.sumEtHits() || roid.missingEtHits() ||
+	    roid.missingEtSigHits() || roid.ex() || roid.ey() || roid.et() ||
+	    roid.sumEtError() || roid.missingEtError() ||
+	    roid.missingEtSigError() || roid.exError() || roid.eyError() ||
+	    roid.etError()) {
+          theROD->push_back(roid.energyRoiWord0());
+          theROD->push_back(roid.energyRoiWord1());
+          theROD->push_back(roid.energyRoiWord2());
+        }
+      }
+    }
+  }
+
+  // Fill the raw event
+
+  m_fea->fill(re, msg());
+
+  // Set ROD status words
+
+  //L1CaloRodStatus::setStatus(re, m_rodStatusMap, m_srcIdMap);
+
+  return StatusCode::SUCCESS;
+}
+
+// Return reference to vector with all possible Source Identifiers
+
+const std::vector<uint32_t>& JepRoiByteStreamTool::sourceIDs(
+                                                   const std::string& sgKey)
+{
+  const std::string flag("RoIB");
+  const std::string::size_type pos = sgKey.find(flag);
+  const bool roiDaq =
+           (pos == std::string::npos || pos != sgKey.length() - flag.length());
+  const bool empty  = (roiDaq) ? m_sourceIDs.empty() : m_sourceIDsRoIB.empty();
+  if (empty) {
+    const int maxCrates = m_crates + m_crateOffsetHw;
+    const int maxSlinks = m_srcIdMap->maxSlinks();
+    for (int hwCrate = m_crateOffsetHw; hwCrate < maxCrates; ++hwCrate) {
+      for (int slink = 0; slink < maxSlinks; ++slink) {
+        const int daqOrRoi = 1;
+        const uint32_t rodId = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+                                                             m_subDetector);
+        const uint32_t robId = m_srcIdMap->getRobID(rodId);
+	if (roiDaq) {
+	  if (slink < 2) m_sourceIDs.push_back(robId);
+	} else if (slink >= 2) m_sourceIDsRoIB.push_back(robId);
+      }
+    }
+  }
+  return (roiDaq) ? m_sourceIDs : m_sourceIDsRoIB;
+}
+
+// Convert bytestream to given container type
+
+StatusCode JepRoiByteStreamTool::convertBs(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            const CollectionType collection)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Loop over ROB fragments
+
+  int robCount = 0;
+  std::set<uint32_t> dupCheck;
+  std::set<uint32_t> dupRoiCheck;
+  ROBIterator rob    = robFrags.begin();
+  ROBIterator robEnd = robFrags.end();
+  for (; rob != robEnd; ++rob) {
+
+    if (debug) {
+      ++robCount;
+      msg() << "Treating ROB fragment " << robCount << endreq;
+    }
+
+    // Skip fragments with ROB status errors
+
+    uint32_t robid = (*rob)->source_id();
+    if ((*rob)->nstatus() > 0) {
+      ROBPointer robData;
+      (*rob)->status(robData);
+      if (*robData != 0) {
+        m_errorTool->robError(robid, *robData);
+	if (debug) msg() << "ROB status error - skipping fragment" << endreq;
+	continue;
+      }
+    }
+
+    // Skip duplicate fragments
+
+    if (!dupCheck.insert(robid).second) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_DUPLICATE_ROB);
+      if (debug) msg() << "Skipping duplicate ROB fragment" << endreq;
+      continue;
+    }
+
+    // Unpack ROD data (slinks)
+
+    RODPointer payloadBeg;
+    RODPointer payload;
+    RODPointer payloadEnd;
+    (*rob)->rod_data(payloadBeg);
+    payloadEnd = payloadBeg + (*rob)->rod_ndata();
+    payload = payloadBeg;
+    if (payload == payloadEnd) {
+      if (debug) msg() << "ROB fragment empty" << endreq;
+      continue;
+    }
+
+    // Check identifier
+    const uint32_t sourceID = (*rob)->rod_source_id();
+    if (m_srcIdMap->getRobID(sourceID) != robid           ||
+        m_srcIdMap->subDet(sourceID)   != m_subDetector   ||
+	m_srcIdMap->daqOrRoi(sourceID) != 1               ||
+       (m_srcIdMap->slink(sourceID) != 0 && m_srcIdMap->slink(sourceID) != 2) ||
+        m_srcIdMap->crate(sourceID)    <  m_crateOffsetHw ||
+	m_srcIdMap->crate(sourceID)    >= m_crateOffsetHw + m_crates) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_ROD_ID);
+      if (debug) {
+        msg() << "Wrong source identifier in data: "
+	      << MSG::hex << sourceID << MSG::dec << endreq;
+      }
+      continue;
+    }
+    const int rodCrate = m_srcIdMap->crate(sourceID);
+    if (debug) {
+      msg() << "Treating crate " << rodCrate 
+            << " slink " << m_srcIdMap->slink(sourceID) << endreq;
+    }
+
+    // First word may be User Header
+    if (L1CaloUserHeader::isValid(*payload)) {
+      L1CaloUserHeader userHeader(*payload);
+      const int minorVersion = (*rob)->rod_version() & 0xffff;
+      userHeader.setVersion(minorVersion);
+      const int headerWords = userHeader.words();
+      if (headerWords != 1) {
+        m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
+        if (debug) msg() << "Unexpected number of user header words: "
+	                 << headerWords << endreq;
+        continue;
+      }
+      for (int i = 0; i < headerWords; ++i) ++payload;
+    }
+
+    // Loop over sub-blocks if there are any
+
+    unsigned int rodErr = L1CaloSubBlock::ERROR_NONE;
+    while (payload != payloadEnd) {
+      
+      if (L1CaloSubBlock::wordType(*payload) == L1CaloSubBlock::HEADER) {
+	const int slice = 0;
+        if (CmmSubBlock::cmmBlock(*payload)) {
+          // CMMs
+	  if (CmmSubBlock::cmmType(*payload) == CmmSubBlock::CMM_JET) {
+            CmmJetSubBlock subBlock;
+            payload = subBlock.read(payload, payloadEnd);
+	    if (collection == CMM_ROI) {
+	      if (subBlock.dataWords() && !subBlock.unpack()) {
+	        if (debug) {
+		  std::string errMsg(subBlock.unpackErrorMsg());
+	          msg() << "CMM-Jet sub-block unpacking failed: "
+		        << errMsg << endreq;
+	        }
+		rodErr = m_subBlock->unpackErrorCode();
+		break;
+              }
+	      const LVL1::CMMRoI roi(subBlock.jetEtMap(slice),
+	                             0,0,0,0,0,0,0,0,0,0,0,0,0);
+	      m_cmCollection->setRoiWord(roi.jetEtRoiWord());
+            }
+          } else {
+	    CmmEnergySubBlock subBlock;
+	    payload = subBlock.read(payload, payloadEnd);
+	    if (collection == CMM_ROI) {
+	      if (subBlock.dataWords() && !subBlock.unpack()) {
+	        if (debug) {
+		  std::string errMsg(subBlock.unpackErrorMsg());
+	          msg() << "CMM-Energy sub-block unpacking failed: "
+		        << errMsg << endreq;
+	        }
+		rodErr = m_subBlock->unpackErrorCode();
+		break;
+              }
+	      const LVL1::CMMRoI roi(0, subBlock.sumEtHits(slice),
+	                   subBlock.missingEtHits(slice),
+	                   subBlock.missingEtSigHits(slice),
+			   subBlock.ex(slice, CmmEnergySubBlock::TOTAL),
+			   subBlock.ey(slice, CmmEnergySubBlock::TOTAL),
+			   subBlock.et(slice, CmmEnergySubBlock::TOTAL),
+			   0, 0, 0, 0,
+			   subBlock.exError(slice, CmmEnergySubBlock::TOTAL),
+			   subBlock.eyError(slice, CmmEnergySubBlock::TOTAL),
+			   subBlock.etError(slice, CmmEnergySubBlock::TOTAL));
+	      m_cmCollection->setRoiWord(roi.energyRoiWord0());
+	      m_cmCollection->setRoiWord(roi.energyRoiWord1());
+	      m_cmCollection->setRoiWord(roi.energyRoiWord2());
+            }
+	  }
+        } else {
+          // JEM RoI
+          JemRoiSubBlock subBlock;
+          payload = subBlock.read(payload, payloadEnd);
+	  if (collection == JEM_ROI) {
+	    if (subBlock.dataWords() && !subBlock.unpack()) {
+	      if (debug) {
+		std::string errMsg(subBlock.unpackErrorMsg());
+	        msg() << "JEM RoI sub-block unpacking failed: "
+		      << errMsg << endreq;
+	      }
+              rodErr = m_subBlock->unpackErrorCode();
+              break;
+            }
+	    for (int frame = 0; frame < 8; ++frame) {
+	      for (int forward = 0; forward < 2; ++forward) {
+	        const LVL1::JEMRoI roi = subBlock.roi(frame, forward);
+		if (roi.hits() || roi.error()) {
+		  m_jeCollection->push_back(new LVL1::JEMRoI(roi));
+	        }
+	      }
+	    }
+          }
+        }
+      } else {
+        // Just RoI word
+	LVL1::JEMRoI jroi;
+	LVL1::CMMRoI croi;
+	if (jroi.setRoiWord(*payload)) {
+	  if (collection == JEM_ROI) {
+	    if (jroi.crate() != rodCrate - m_crateOffsetHw) {
+	      if (debug) msg() << "Inconsistent RoI crate number: "
+	                       << jroi.crate() << endreq;
+              rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	      break;
+            }
+	    const uint32_t location = (*payload) & 0xfffc0000;
+	    if (dupRoiCheck.insert(location).second) {
+	      if (jroi.hits() || jroi.error()) {
+	        m_jeCollection->push_back(new LVL1::JEMRoI(*payload));
+	      }
+	    } else {
+	      if (debug) msg() << "Duplicate RoI word "
+	                       << MSG::hex << *payload << MSG::dec << endreq;
+              rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	      break;
+            }
+	  }
+        } else if (croi.setRoiWord(*payload)) {
+	  if (collection == CMM_ROI) {
+	    uint32_t roiType = (*payload) & 0xf0000000;
+	    if ((roiType & 0xe0000000) == 0xa0000000) roiType = 0xa0000000;
+	    if (dupRoiCheck.insert(roiType).second) {
+	      m_cmCollection->setRoiWord(*payload);
+	    } else {
+	      if (debug) msg() << "Duplicate RoI word "
+	                       << MSG::hex << *payload << MSG::dec << endreq;
+              rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	      break;
+            }
+	  }
+        } else {
+	  if (debug) msg() << "Invalid RoI word "
+	                   << MSG::hex << *payload << MSG::dec << endreq;
+	  rodErr = L1CaloSubBlock::ERROR_ROI_TYPE;
+	  break;
+        }
+	++payload;
+      }
+    }
+    if (rodErr != L1CaloSubBlock::ERROR_NONE)
+                                        m_errorTool->rodError(robid, rodErr);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Find CMM hits for given crate, dataID
+
+const LVL1::CMMJetHits* JepRoiByteStreamTool::findCmmHits(const int crate,
+                                                          const int dataID)
+{
+  const LVL1::CMMJetHits* hits = 0;
+  CmmHitsMap::const_iterator mapIter;
+  mapIter = m_cmmHitsMap.find(crate*100 + dataID);
+  if (mapIter != m_cmmHitsMap.end()) hits = mapIter->second;
+  return hits;
+}
+
+// Find CMM energy sums for given crate, module, dataID
+
+const LVL1::CMMEtSums* JepRoiByteStreamTool::findCmmSums(const int crate,
+                                                         const int dataID)
+{
+  const LVL1::CMMEtSums* sums = 0;
+  CmmSumsMap::const_iterator mapIter;
+  mapIter = m_cmmEtMap.find(crate*100 + dataID);
+  if (mapIter != m_cmmEtMap.end()) sums = mapIter->second;
+  return sums;
+}
+
+// Set up JEM RoIs map
+
+void JepRoiByteStreamTool::setupJemRoiMap(const JemRoiCollection*
+                                                            const jeCollection)
+{
+  m_roiMap.clear();
+  if (jeCollection) {
+    JemRoiCollection::const_iterator pos  = jeCollection->begin();
+    JemRoiCollection::const_iterator pose = jeCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::JEMRoI* const roi = *pos;
+      const uint32_t key = roi->roiWord();
+      m_roiMap.insert(std::make_pair(key, roi));
+    }
+  }
+}
+
+// Set up CMM hits map
+
+void JepRoiByteStreamTool::setupCmmHitsMap(const CmmHitsCollection*
+                                                           const hitCollection)
+{
+  m_cmmHitsMap.clear();
+  if (hitCollection) {
+    CmmHitsCollection::const_iterator pos  = hitCollection->begin();
+    CmmHitsCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CMMJetHits* const hits = *pos;
+      const int crate = hits->crate() - m_crateOffsetSw;
+      const int key   = crate*100 + hits->dataID();
+      m_cmmHitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Set up CMM energy sums map
+
+void JepRoiByteStreamTool::setupCmmEtMap(const CmmSumsCollection*
+                                                            const etCollection)
+{
+  m_cmmEtMap.clear();
+  if (etCollection) {
+    CmmSumsCollection::const_iterator pos  = etCollection->begin();
+    CmmSumsCollection::const_iterator pose = etCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CMMEtSums* const sums = *pos;
+      const int crate = sums->crate() - m_crateOffsetSw;
+      const int key   = crate*100 + sums->dataID();
+      m_cmmEtMap.insert(std::make_pair(key, sums));
+    }
+  }
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamTool.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamTool.h
new file mode 100755
index 0000000000000000000000000000000000000000..4d791c700341771590633ae3d3a79f1708ce8d38
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamTool.h
@@ -0,0 +1,154 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEPROIBYTESTREAMTOOL_H
+#define TRIGT1CALOBYTESTREAM_JEPROIBYTESTREAMTOOL_H
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+#include "ByteStreamData/RawEvent.h"
+#include "DataModel/DataVector.h"
+#include "eformat/SourceIdentifier.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class IInterface;
+class InterfaceID;
+class StatusCode;
+
+template <class T> class FullEventAssembler;
+
+namespace LVL1 {
+  class CMMJetHits;
+  class CMMEtSums;
+  class CMMRoI;
+  class JEMRoI;
+  class JEPRoIBSCollection;
+}
+
+namespace LVL1BS {
+
+class CmmEnergySubBlock;
+class CmmJetSubBlock;
+class JemRoiSubBlock;
+class L1CaloErrorByteStreamTool;
+class L1CaloSrcIdMap;
+
+/** Tool to perform ROB fragments to JEM RoI and CMM RoI,
+ *  and JEP RoI container to raw data conversions.
+ *
+ *  Based on ROD document version 1_09h.
+ *
+ *  @author Peter Faulkner
+ */
+
+class JepRoiByteStreamTool : public AthAlgTool {
+
+ public:
+   JepRoiByteStreamTool(const std::string& type, const std::string& name,
+                        const IInterface* parent);
+   virtual ~JepRoiByteStreamTool();
+
+   /// AlgTool InterfaceID
+   static const InterfaceID& interfaceID();
+
+   virtual StatusCode initialize();
+   virtual StatusCode finalize();
+
+   /// Convert ROB fragments to JEM RoIs
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::JEMRoI>* jeCollection);
+   /// Convert ROB fragments to CMM RoIs
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      LVL1::CMMRoI* cmCollection);
+
+   /// Convert JEP RoI Container to bytestream
+   StatusCode convert(const LVL1::JEPRoIBSCollection* jep, RawEventWrite* re);
+
+   /// Return reference to vector with all possible Source Identifiers
+   const std::vector<uint32_t>& sourceIDs(const std::string& sgKey);
+
+ private:
+   enum CollectionType { JEM_ROI, CMM_ROI };
+
+   typedef DataVector<LVL1::JEMRoI>                      JemRoiCollection;
+   typedef DataVector<LVL1::CMMJetHits>                  CmmHitsCollection;
+   typedef DataVector<LVL1::CMMEtSums>                   CmmSumsCollection;
+   typedef std::map<uint32_t, const LVL1::JEMRoI*>       JemRoiMap;
+   typedef std::map<int, const LVL1::CMMJetHits*>        CmmHitsMap;
+   typedef std::map<int, const LVL1::CMMEtSums*>         CmmSumsMap;
+   typedef IROBDataProviderSvc::VROBFRAG::const_iterator ROBIterator;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      ROBPointer;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      RODPointer;
+
+   /// Convert bytestream to given container type
+   StatusCode convertBs(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                        CollectionType collection);
+
+   /// Error collection tool
+   ToolHandle<LVL1BS::L1CaloErrorByteStreamTool> m_errorTool;
+
+   /// Find CMM hits for given crate, data ID
+   const LVL1::CMMJetHits* findCmmHits(int crate, int dataID);
+   /// Find CMM energy sums for given crate, data ID
+   const LVL1::CMMEtSums*  findCmmSums(int crate, int dataID);
+
+   /// Set up JEM RoIs map
+   void setupJemRoiMap(const JemRoiCollection* jeCollection);
+   /// Set up CMM hits map
+   void setupCmmHitsMap(const CmmHitsCollection* hitCollection);
+   /// Set up CMM energy sums map
+   void setupCmmEtMap(const CmmSumsCollection* enCollection);
+
+   /// Hardware crate number offset
+   int m_crateOffsetHw;
+   /// Software crate number offset
+   int m_crateOffsetSw;
+   /// Sub_block header version
+   int m_version;
+   /// Data compression format
+   int m_dataFormat;
+   /// Number of crates
+   int m_crates;
+   /// Number of JEM modules per crate
+   int m_modules;
+   /// Number of slinks per crate when writing out bytestream
+   int m_slinks;
+   /// ROB source IDs
+   std::vector<uint32_t> m_sourceIDs;
+   /// ROB source IDs for RoIB
+   std::vector<uint32_t> m_sourceIDsRoIB;
+   /// Sub-detector type
+   eformat::SubDetector m_subDetector;
+   /// Source ID converter
+   L1CaloSrcIdMap* m_srcIdMap;
+   /// Sub-block for neutral format
+   JemRoiSubBlock*  m_subBlock;
+   /// Current JEM RoI collection
+   JemRoiCollection* m_jeCollection;
+   /// Current CMM RoI collection
+   LVL1::CMMRoI*     m_cmCollection;
+   /// JEM RoI map
+   JemRoiMap  m_roiMap;
+   /// CMM hits map
+   CmmHitsMap m_cmmHitsMap;
+   /// CMM energy sums map
+   CmmSumsMap m_cmmEtMap;
+   /// ROD Status words
+   std::vector<uint32_t>* m_rodStatus;
+   /// ROD status map
+   std::map<uint32_t, std::vector<uint32_t>* > m_rodStatusMap;
+   /// Event assembler
+   FullEventAssembler<L1CaloSrcIdMap>* m_fea;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV1Cnv.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV1Cnv.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..f0d1facd3e3fc1ee0317ee86eb88ed6d050a63fe
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV1Cnv.cxx
@@ -0,0 +1,115 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IByteStreamEventAccess.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "DataModel/DataVector.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/IRegistry.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "TrigT1CaloEvent/JEPRoIBSCollectionV1.h"
+
+#include "JepRoiByteStreamV1Cnv.h"
+#include "JepRoiByteStreamV1Tool.h"
+
+namespace LVL1BS {
+
+JepRoiByteStreamV1Cnv::JepRoiByteStreamV1Cnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("JepRoiByteStreamV1Cnv"),
+      m_tool("LVL1BS::JepRoiByteStreamV1Tool/JepRoiByteStreamV1Tool"),
+      m_ByteStreamEventAccess("ByteStreamCnvSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+JepRoiByteStreamV1Cnv::~JepRoiByteStreamV1Cnv()
+{
+}
+
+// CLID
+
+const CLID& JepRoiByteStreamV1Cnv::classID()
+{
+  return ClassID_traits<LVL1::JEPRoIBSCollectionV1>::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode JepRoiByteStreamV1Cnv::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << " - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  //Get ByteStreamCnvSvc
+  sc = m_ByteStreamEventAccess.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_ByteStreamEventAccess << endreq;
+    return sc;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_ByteStreamEventAccess << endreq;
+  }
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return StatusCode::FAILURE;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  return StatusCode::SUCCESS;
+}
+
+// createRep should create the bytestream from RDOs.
+
+StatusCode JepRoiByteStreamV1Cnv::createRep( DataObject* pObj,
+                                             IOpaqueAddress*& pAddr )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createRep() called" << endreq;
+
+  RawEventWrite* re = m_ByteStreamEventAccess->getRawEvent();
+
+  LVL1::JEPRoIBSCollectionV1* jep = 0;
+  if( !SG::fromStorable( pObj, jep ) ) {
+    m_log << MSG::ERROR << " Cannot cast to JEPRoIBSCollectionV1" << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = pObj->registry()->name();
+
+  ByteStreamAddress* addr = new ByteStreamAddress( classID(), nm, "" );
+
+  pAddr = addr;
+
+  // Convert to ByteStream
+  return m_tool->convert( jep, re );
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV1Cnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV1Cnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..11a168fbd9af84d7890d6824c1fd009888226e96
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV1Cnv.h
@@ -0,0 +1,76 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEPROIBYTESTREAMV1CNV_H
+#define TRIGT1CALOBYTESTREAM_JEPROIBYTESTREAMV1CNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IByteStreamEventAccess;
+class IOpaqueAddress;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class JepRoiByteStreamV1Tool;
+
+/** ByteStream converter for JEP RoI container
+ *
+ *  @author Peter Faulkner
+ */
+
+class JepRoiByteStreamV1Cnv: public Converter {
+
+  friend class CnvFactory<JepRoiByteStreamV1Cnv>;
+
+protected:
+
+  JepRoiByteStreamV1Cnv(ISvcLocator* svcloc);
+
+public:
+
+  ~JepRoiByteStreamV1Cnv();
+
+  virtual StatusCode initialize();
+  /// Create ByteStream from JEP Container
+  virtual StatusCode createRep(DataObject* pObj, IOpaqueAddress*& pAddr);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::JepRoiByteStreamV1Tool> m_tool;
+
+  /// Service for writing bytestream
+  ServiceHandle<IByteStreamEventAccess> m_ByteStreamEventAccess;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV1Tool.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV1Tool.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..4e8ce822b5c1440a3d30b1785bfa69866e5857d3
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV1Tool.cxx
@@ -0,0 +1,745 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <numeric>
+#include <set>
+#include <utility>
+
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "ByteStreamCnvSvcBase/FullEventAssembler.h"
+
+#include "TrigT1CaloEvent/CMMJetHits.h"
+#include "TrigT1CaloEvent/CMMEtSums.h"
+#include "TrigT1CaloEvent/CMMRoI.h"
+#include "TrigT1CaloEvent/JEMRoI.h"
+#include "TrigT1CaloEvent/JEPRoIBSCollectionV1.h"
+
+#include "CmmEnergySubBlock.h"
+#include "CmmJetSubBlock.h"
+#include "CmmSubBlock.h"
+#include "JemRoiSubBlockV1.h"
+#include "L1CaloErrorByteStreamTool.h"
+#include "L1CaloSrcIdMap.h"
+#include "L1CaloSubBlock.h"
+#include "L1CaloUserHeader.h"
+
+#include "JepRoiByteStreamV1Tool.h"
+
+namespace LVL1BS {
+
+// Interface ID
+
+static const InterfaceID IID_IJepRoiByteStreamV1Tool("JepRoiByteStreamV1Tool",
+                                                                        1, 1);
+
+const InterfaceID& JepRoiByteStreamV1Tool::interfaceID()
+{
+  return IID_IJepRoiByteStreamV1Tool;
+}
+
+// Constructor
+
+JepRoiByteStreamV1Tool::JepRoiByteStreamV1Tool(const std::string& type,
+                                               const std::string& name,
+    				               const IInterface*  parent)
+  : AthAlgTool(type, name, parent),
+    m_errorTool("LVL1BS::L1CaloErrorByteStreamTool/L1CaloErrorByteStreamTool"),
+    m_crates(2), m_modules(16), m_srcIdMap(0), m_subBlock(0), m_rodStatus(0),
+    m_fea(0)
+{
+  declareInterface<JepRoiByteStreamV1Tool>(this);
+
+  declareProperty("ErrorTool", m_errorTool,
+                  "Tool to collect errors for monitoring");
+  declareProperty("CrateOffsetHw",  m_crateOffsetHw = 12,
+                  "Offset of JEP crate numbers in bytestream");
+  declareProperty("CrateOffsetSw",  m_crateOffsetSw = 0,
+                  "Offset of JEP crate numbers in RDOs");
+
+  // Properties for reading bytestream only
+  declareProperty("ROBSourceIDs",       m_sourceIDs,
+                  "ROB fragment source identifiers");
+  declareProperty("ROBSourceIDsRoIB",   m_sourceIDsRoIB,
+                  "ROB fragment source identifiers");
+
+  // Properties for writing bytestream only
+  declareProperty("DataVersion",    m_version       = 1,
+                  "Format version number in sub-block header");
+  declareProperty("DataFormat",     m_dataFormat    = 1,
+                  "Format identifier (0-1) in sub-block header");
+  declareProperty("SlinksPerCrate", m_slinks        = 1,
+                  "The number of S-Links per crate");
+  declareProperty("CrateMin",       m_crateMin = 0,
+                  "Minimum crate number, allows partial output");
+  declareProperty("CrateMax",       m_crateMax = m_crates-1,
+                  "Maximum crate number, allows partial output");
+
+}
+
+// Destructor
+
+JepRoiByteStreamV1Tool::~JepRoiByteStreamV1Tool()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode JepRoiByteStreamV1Tool::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = m_errorTool.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_errorTool << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_errorTool << endreq;
+
+  m_subDetector = eformat::TDAQ_CALO_JET_PROC_ROI;
+  m_srcIdMap    = new L1CaloSrcIdMap();
+  m_subBlock    = new JemRoiSubBlockV1();
+  m_rodStatus   = new std::vector<uint32_t>(2);
+  m_fea         = new FullEventAssembler<L1CaloSrcIdMap>();
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode JepRoiByteStreamV1Tool::finalize()
+{
+  delete m_fea;
+  delete m_rodStatus;
+  delete m_subBlock;
+  delete m_srcIdMap;
+  return StatusCode::SUCCESS;
+}
+
+// Conversion bytestream to JEM RoI
+
+StatusCode JepRoiByteStreamV1Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::JEMRoI>* const jeCollection)
+{
+  m_jeCollection = jeCollection;
+  return convertBs(robFrags, JEM_ROI);
+}
+
+// Conversion bytestream to CMM RoI
+
+StatusCode JepRoiByteStreamV1Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            LVL1::CMMRoI* const cmCollection)
+{
+  m_cmCollection = cmCollection;
+  return convertBs(robFrags, CMM_ROI);
+}
+
+// Conversion of JEP container to bytestream
+
+StatusCode JepRoiByteStreamV1Tool::convert(
+                                 const LVL1::JEPRoIBSCollectionV1* const jep,
+                                 RawEventWrite* const re)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Clear the event assembler
+
+  m_fea->clear();
+  const uint16_t minorVersion = m_srcIdMap->minorVersionPreLS1();
+  m_fea->setRodMinorVersion(minorVersion);
+  m_rodStatusMap.clear();
+
+  // Pointer to ROD data vector
+
+  FullEventAssembler<L1CaloSrcIdMap>::RODDATA* theROD = 0;
+
+  // Set up the container maps
+
+  const bool neutralFormat = m_dataFormat == L1CaloSubBlock::NEUTRAL;
+  setupJemRoiMap(jep->JemRoi());
+  JemRoiMap::const_iterator mapIter    = m_roiMap.begin();
+  JemRoiMap::const_iterator mapIterEnd = m_roiMap.end();
+  if (neutralFormat) {
+    setupCmmHitsMap(jep->CmmHits());
+    setupCmmEtMap(jep->CmmSums());
+  }
+
+  // Loop over JEM RoI data
+
+  const int modulesPerSlink = m_modules / m_slinks;
+  for (int crate = m_crateMin; crate <= m_crateMax; ++crate) {
+    const int hwCrate = crate + m_crateOffsetHw;
+
+    for (int module=0; module < m_modules; ++module) {
+
+      // Pack required number of modules per slink
+
+      if (module%modulesPerSlink == 0) {
+	const int daqOrRoi = 1;
+	const int slink = module/modulesPerSlink;
+        if (debug) {
+          msg() << "Treating crate " << hwCrate
+	        << " slink " << slink << endreq
+	        << "Data Version/Format: " << m_version
+	        << " " << m_dataFormat << endreq;
+        }
+	const uint32_t rodIdJem = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+	                                                        m_subDetector);
+	theROD = m_fea->getRodData(rodIdJem);
+        if (neutralFormat) {
+          const L1CaloUserHeader userHeader;
+	  theROD->push_back(userHeader.header());
+        }
+	m_rodStatusMap.insert(make_pair(rodIdJem, m_rodStatus));
+      }
+      if (debug) msg() << "JEM Module " << module << endreq;
+      if (!theROD) break; // for coverity, shouldn't happen
+
+      // Create a sub-block (Neutral format only)
+
+      if (neutralFormat) {
+        m_subBlock->clear();
+	m_subBlock->setRoiHeader(m_version, hwCrate, module);
+      }
+
+      // Find JEM RoIs for this module
+
+      for (; mapIter != mapIterEnd; ++mapIter) {
+        const LVL1::JEMRoI* const roi = mapIter->second;
+	if (roi->crate() < crate)  continue;
+	if (roi->crate() > crate)  break;
+	if (roi->jem()   < module) continue;
+	if (roi->jem()   > module) break;
+	if (roi->hits() || roi->error()) {
+	  if (neutralFormat) m_subBlock->fillRoi(*roi);
+	  else theROD->push_back(roi->roiWord());
+        }
+      }
+
+      // Pack and write the sub-block
+
+      if (neutralFormat) {
+        if ( !m_subBlock->pack()) {
+	  msg(MSG::ERROR) << "JEM RoI sub-block packing failed" << endreq;
+	  return StatusCode::FAILURE;
+        }
+	if (debug) {
+	  msg() << "JEM RoI sub-block data words: "
+	        << m_subBlock->dataWords() << endreq;
+	}
+	m_subBlock->write(theROD);
+      }
+    }
+    if (!theROD) break; // for coverity, shouldn't happen
+
+    // Append CMM RoIs to last S-Link of the system crate
+
+    if (crate != m_crates - 1) continue;
+
+    // Create sub-blocks for Neutral format
+
+    if (neutralFormat) {
+      const int timeslices = 1;
+      const int slice = 0;
+
+      // CMM-Energy
+
+      CmmEnergySubBlock enBlock;
+      const int cmmEnergyVersion = 2; // with Missing-ET-Sig
+      enBlock.setCmmHeader(cmmEnergyVersion, m_dataFormat, slice, hwCrate,
+                           CmmSubBlock::SYSTEM, CmmSubBlock::CMM_ENERGY,
+			   CmmSubBlock::LEFT, timeslices);
+      int maxDataID = static_cast<int>(LVL1::CMMEtSums::MAXID);
+      for (int dataID = 0; dataID < maxDataID; ++dataID) {
+        int source = dataID;
+        if (dataID >= m_modules) {
+	  switch (dataID) {
+	    case LVL1::CMMEtSums::LOCAL:
+	      source = CmmEnergySubBlock::LOCAL;
+	      break;
+	    case LVL1::CMMEtSums::REMOTE:
+	      source = CmmEnergySubBlock::REMOTE;
+	      break;
+	    case LVL1::CMMEtSums::TOTAL:
+	      source = CmmEnergySubBlock::TOTAL;
+	      break;
+	    case LVL1::CMMEtSums::MISSING_ET_MAP:
+	    case LVL1::CMMEtSums::SUM_ET_MAP:
+	    case LVL1::CMMEtSums::MISSING_ET_SIG_MAP:
+	      break;
+            default:
+	      continue;
+          }
+        }
+        const LVL1::CMMEtSums* const sums = findCmmSums(crate, dataID);
+        if ( sums ) {
+          const unsigned int ex = sums->Ex();
+          const unsigned int ey = sums->Ey();
+          const unsigned int et = sums->Et();
+          const int exErr = sums->ExError();
+          const int eyErr = sums->EyError();
+          const int etErr = sums->EtError();
+	  if (dataID == LVL1::CMMEtSums::MISSING_ET_MAP) {
+	    enBlock.setMissingEtHits(slice, et); 
+          } else if (dataID == LVL1::CMMEtSums::SUM_ET_MAP) {
+	    enBlock.setSumEtHits(slice, et); 
+	  } else if (dataID == LVL1::CMMEtSums::MISSING_ET_SIG_MAP) {
+	    enBlock.setMissingEtSigHits(slice, et);
+          } else {
+	    enBlock.setSubsums(slice, source, ex, ey, et, exErr, eyErr, etErr);
+          }
+        }
+      }
+      if ( !enBlock.pack()) {
+        msg(MSG::ERROR) << "CMM-Energy sub-block packing failed" << endreq;
+	return StatusCode::FAILURE;
+      }
+      if (debug) {
+	msg() << "CMM-Energy sub-block data words: "
+	      << enBlock.dataWords() << endreq;
+      }
+      enBlock.write(theROD);
+
+      // CMM-Jet
+
+      CmmJetSubBlock jetBlock;
+      jetBlock.setCmmHeader(m_version, m_dataFormat, slice, hwCrate,
+                            CmmSubBlock::SYSTEM, CmmSubBlock::CMM_JET,
+			    CmmSubBlock::RIGHT, timeslices);
+      maxDataID = static_cast<int>(LVL1::CMMJetHits::MAXID);
+      for (int dataID = 0; dataID < maxDataID; ++dataID) {
+        int source = dataID;
+        if (dataID >= m_modules) {
+	  switch (dataID) {
+	    case LVL1::CMMJetHits::LOCAL_MAIN:
+	      source = CmmJetSubBlock::LOCAL_MAIN;
+	      break;
+	    case LVL1::CMMJetHits::REMOTE_MAIN:
+	      source = CmmJetSubBlock::REMOTE_MAIN;
+	      break;
+	    case LVL1::CMMJetHits::TOTAL_MAIN:
+	      source = CmmJetSubBlock::TOTAL_MAIN;
+	      break;
+	    case LVL1::CMMJetHits::LOCAL_FORWARD:
+	      source = CmmJetSubBlock::LOCAL_FORWARD;
+	      break;
+	    case LVL1::CMMJetHits::REMOTE_FORWARD:
+	      source = CmmJetSubBlock::REMOTE_FORWARD;
+	      break;
+	    case LVL1::CMMJetHits::TOTAL_FORWARD:
+	      source = CmmJetSubBlock::TOTAL_FORWARD;
+	      break;
+	    case LVL1::CMMJetHits::ET_MAP:
+	      break;
+            default:
+	      continue;
+          }
+        }
+        const LVL1::CMMJetHits* const ch = findCmmHits(crate, dataID);
+        if ( ch ) {
+          const unsigned int hits = ch->Hits();
+          const int          errs = ch->Error();
+	  if (dataID == LVL1::CMMJetHits::ET_MAP) {
+	    jetBlock.setJetEtMap(slice, hits);
+          } else {
+	    jetBlock.setJetHits(slice, source, hits, errs);
+          }
+        }
+      }
+      if ( !jetBlock.pack()) {
+        msg(MSG::ERROR) << "CMM-Jet sub-block packing failed" << endreq;
+	return StatusCode::FAILURE;
+      }
+      if (debug) {
+	msg() << "CMM-Jet sub-block data words: "
+	      << jetBlock.dataWords() << endreq;
+      }
+      jetBlock.write(theROD);
+
+    } else {
+
+      // Standard format
+
+      const LVL1::CMMRoI* const roi = jep->CmmRoi();
+      if ( roi ) {
+	// Make sure word IDs are correct
+        const LVL1::CMMRoI roid(roi->jetEtHits(), roi->sumEtHits(),
+	            roi->missingEtHits(), roi->missingEtSigHits(),
+		    roi->ex(), roi->ey(), roi->et(),
+		    roi->jetEtError(), roi->sumEtError(),
+		    roi->missingEtError(), roi->missingEtSigError(),
+		    roi->exError(), roi->eyError(), roi->etError());
+        if (roid.jetEtHits() || roid.jetEtError()) {
+          theROD->push_back(roid.jetEtRoiWord());
+        }
+        // CMM-Energy RoIs are not zero-supressed unless all are zero
+	if (roid.sumEtHits() || roid.missingEtHits() ||
+	    roid.missingEtSigHits() || roid.ex() || roid.ey() || roid.et() ||
+	    roid.sumEtError() || roid.missingEtError() ||
+	    roid.missingEtSigError() || roid.exError() || roid.eyError() ||
+	    roid.etError()) {
+          theROD->push_back(roid.energyRoiWord0());
+          theROD->push_back(roid.energyRoiWord1());
+          theROD->push_back(roid.energyRoiWord2());
+        }
+      }
+    }
+  }
+
+  // Fill the raw event
+
+  m_fea->fill(re, msg());
+
+  // Set ROD status words
+
+  //L1CaloRodStatus::setStatus(re, m_rodStatusMap, m_srcIdMap);
+
+  return StatusCode::SUCCESS;
+}
+
+// Return reference to vector with all possible Source Identifiers
+
+const std::vector<uint32_t>& JepRoiByteStreamV1Tool::sourceIDs(
+                                                   const std::string& sgKey)
+{
+  const std::string flag("RoIB");
+  const std::string::size_type pos = sgKey.find(flag);
+  const bool roiDaq =
+           (pos == std::string::npos || pos != sgKey.length() - flag.length());
+  const bool empty  = (roiDaq) ? m_sourceIDs.empty() : m_sourceIDsRoIB.empty();
+  if (empty) {
+    const int maxCrates = m_crates + m_crateOffsetHw;
+    const int maxSlinks = m_srcIdMap->maxSlinks();
+    for (int hwCrate = m_crateOffsetHw; hwCrate < maxCrates; ++hwCrate) {
+      for (int slink = 0; slink < maxSlinks; ++slink) {
+        const int daqOrRoi = 1;
+        const uint32_t rodId = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+                                                             m_subDetector);
+        const uint32_t robId = m_srcIdMap->getRobID(rodId);
+	if (roiDaq) {
+	  if (slink < 2) m_sourceIDs.push_back(robId);
+	} else if (slink >= 2) m_sourceIDsRoIB.push_back(robId);
+      }
+    }
+  }
+  return (roiDaq) ? m_sourceIDs : m_sourceIDsRoIB;
+}
+
+// Convert bytestream to given container type
+
+StatusCode JepRoiByteStreamV1Tool::convertBs(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            const CollectionType collection)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Loop over ROB fragments
+
+  int robCount = 0;
+  std::set<uint32_t> dupCheck;
+  std::set<uint32_t> dupRoiCheck;
+  ROBIterator rob    = robFrags.begin();
+  ROBIterator robEnd = robFrags.end();
+  for (; rob != robEnd; ++rob) {
+
+    if (debug) {
+      ++robCount;
+      msg() << "Treating ROB fragment " << robCount << endreq;
+    }
+
+    // Skip fragments with ROB status errors
+
+    uint32_t robid = (*rob)->source_id();
+    if ((*rob)->nstatus() > 0) {
+      ROBPointer robData;
+      (*rob)->status(robData);
+      if (*robData != 0) {
+        m_errorTool->robError(robid, *robData);
+	if (debug) msg() << "ROB status error - skipping fragment" << endreq;
+	continue;
+      }
+    }
+
+    // Skip duplicate fragments
+
+    if (!dupCheck.insert(robid).second) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_DUPLICATE_ROB);
+      if (debug) msg() << "Skipping duplicate ROB fragment" << endreq;
+      continue;
+    }
+
+    // Unpack ROD data (slinks)
+
+    RODPointer payloadBeg;
+    RODPointer payload;
+    RODPointer payloadEnd;
+    (*rob)->rod_data(payloadBeg);
+    payloadEnd = payloadBeg + (*rob)->rod_ndata();
+    payload = payloadBeg;
+    if (payload == payloadEnd) {
+      if (debug) msg() << "ROB fragment empty" << endreq;
+      continue;
+    }
+
+    // Check identifier
+    const uint32_t sourceID = (*rob)->rod_source_id();
+    if (m_srcIdMap->getRobID(sourceID) != robid           ||
+        m_srcIdMap->subDet(sourceID)   != m_subDetector   ||
+	m_srcIdMap->daqOrRoi(sourceID) != 1               ||
+       (m_srcIdMap->slink(sourceID) != 0 && m_srcIdMap->slink(sourceID) != 2) ||
+        m_srcIdMap->crate(sourceID)    <  m_crateOffsetHw ||
+	m_srcIdMap->crate(sourceID)    >= m_crateOffsetHw + m_crates) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_ROD_ID);
+      if (debug) {
+        msg() << "Wrong source identifier in data: "
+	      << MSG::hex << sourceID << MSG::dec << endreq;
+      }
+      continue;
+    }
+
+    // Check minor version
+    const int minorVersion = (*rob)->rod_version() & 0xffff;
+    if (minorVersion > m_srcIdMap->minorVersionPreLS1()) {
+      if (debug) msg() << "Skipping post-LS1 data" << endreq;
+      continue;
+    }
+    const int rodCrate = m_srcIdMap->crate(sourceID);
+    if (debug) {
+      msg() << "Treating crate " << rodCrate 
+            << " slink " << m_srcIdMap->slink(sourceID) << endreq;
+    }
+
+    // First word may be User Header
+    if (L1CaloUserHeader::isValid(*payload)) {
+      L1CaloUserHeader userHeader(*payload);
+      userHeader.setVersion(minorVersion);
+      const int headerWords = userHeader.words();
+      if (headerWords != 1) {
+        m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
+        if (debug) msg() << "Unexpected number of user header words: "
+	                 << headerWords << endreq;
+        continue;
+      }
+      for (int i = 0; i < headerWords; ++i) ++payload;
+    }
+
+    // Loop over sub-blocks if there are any
+
+    unsigned int rodErr = L1CaloSubBlock::ERROR_NONE;
+    while (payload != payloadEnd) {
+      
+      if (L1CaloSubBlock::wordType(*payload) == L1CaloSubBlock::HEADER) {
+	const int slice = 0;
+        if (CmmSubBlock::cmmBlock(*payload)) {
+          // CMMs
+	  if (CmmSubBlock::cmmType(*payload) == CmmSubBlock::CMM_JET) {
+            CmmJetSubBlock subBlock;
+            payload = subBlock.read(payload, payloadEnd);
+	    if (collection == CMM_ROI) {
+	      if (subBlock.dataWords() && !subBlock.unpack()) {
+	        if (debug) {
+		  std::string errMsg(subBlock.unpackErrorMsg());
+	          msg() << "CMM-Jet sub-block unpacking failed: "
+		        << errMsg << endreq;
+	        }
+		rodErr = m_subBlock->unpackErrorCode();
+		break;
+              }
+	      const LVL1::CMMRoI roi(subBlock.jetEtMap(slice),
+	                             0,0,0,0,0,0,0,0,0,0,0,0,0);
+	      m_cmCollection->setRoiWord(roi.jetEtRoiWord());
+            }
+          } else {
+	    CmmEnergySubBlock subBlock;
+	    payload = subBlock.read(payload, payloadEnd);
+	    if (collection == CMM_ROI) {
+	      if (subBlock.dataWords() && !subBlock.unpack()) {
+	        if (debug) {
+		  std::string errMsg(subBlock.unpackErrorMsg());
+	          msg() << "CMM-Energy sub-block unpacking failed: "
+		        << errMsg << endreq;
+	        }
+		rodErr = m_subBlock->unpackErrorCode();
+		break;
+              }
+	      const LVL1::CMMRoI roi(0, subBlock.sumEtHits(slice),
+	                   subBlock.missingEtHits(slice),
+	                   subBlock.missingEtSigHits(slice),
+			   subBlock.ex(slice, CmmEnergySubBlock::TOTAL),
+			   subBlock.ey(slice, CmmEnergySubBlock::TOTAL),
+			   subBlock.et(slice, CmmEnergySubBlock::TOTAL),
+			   0, 0, 0, 0,
+			   subBlock.exError(slice, CmmEnergySubBlock::TOTAL),
+			   subBlock.eyError(slice, CmmEnergySubBlock::TOTAL),
+			   subBlock.etError(slice, CmmEnergySubBlock::TOTAL));
+	      m_cmCollection->setRoiWord(roi.energyRoiWord0());
+	      m_cmCollection->setRoiWord(roi.energyRoiWord1());
+	      m_cmCollection->setRoiWord(roi.energyRoiWord2());
+            }
+	  }
+        } else {
+          // JEM RoI
+          JemRoiSubBlockV1 subBlock;
+          payload = subBlock.read(payload, payloadEnd);
+	  if (collection == JEM_ROI) {
+	    if (subBlock.dataWords() && !subBlock.unpack()) {
+	      if (debug) {
+		std::string errMsg(subBlock.unpackErrorMsg());
+	        msg() << "JEM RoI sub-block unpacking failed: "
+		      << errMsg << endreq;
+	      }
+              rodErr = m_subBlock->unpackErrorCode();
+              break;
+            }
+	    for (int frame = 0; frame < 8; ++frame) {
+	      for (int forward = 0; forward < 2; ++forward) {
+	        const LVL1::JEMRoI roi = subBlock.roi(frame, forward);
+		if (roi.hits() || roi.error()) {
+		  m_jeCollection->push_back(new LVL1::JEMRoI(roi));
+	        }
+	      }
+	    }
+          }
+        }
+      } else {
+        // Just RoI word
+	LVL1::JEMRoI jroi;
+	LVL1::CMMRoI croi;
+	if (jroi.setRoiWord(*payload)) {
+	  if (collection == JEM_ROI) {
+	    if (jroi.crate() != rodCrate - m_crateOffsetHw) {
+	      if (debug) msg() << "Inconsistent RoI crate number: "
+	                       << jroi.crate() << endreq;
+              rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	      break;
+            }
+	    const uint32_t location = (*payload) & 0xfffc0000;
+	    if (dupRoiCheck.insert(location).second) {
+	      if (jroi.hits() || jroi.error()) {
+	        m_jeCollection->push_back(new LVL1::JEMRoI(*payload));
+	      }
+	    } else {
+	      if (debug) msg() << "Duplicate RoI word "
+	                       << MSG::hex << *payload << MSG::dec << endreq;
+              rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	      break;
+            }
+	  }
+        } else if (croi.setRoiWord(*payload)) {
+	  if (collection == CMM_ROI) {
+	    uint32_t roiType = (*payload) & 0xf0000000;
+	    if ((roiType & 0xe0000000) == 0xa0000000) roiType = 0xa0000000;
+	    if (dupRoiCheck.insert(roiType).second) {
+	      m_cmCollection->setRoiWord(*payload);
+	    } else {
+	      if (debug) msg() << "Duplicate RoI word "
+	                       << MSG::hex << *payload << MSG::dec << endreq;
+              rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	      break;
+            }
+	  }
+        } else {
+	  if (debug) msg() << "Invalid RoI word "
+	                   << MSG::hex << *payload << MSG::dec << endreq;
+	  rodErr = L1CaloSubBlock::ERROR_ROI_TYPE;
+	  break;
+        }
+	++payload;
+      }
+    }
+    if (rodErr != L1CaloSubBlock::ERROR_NONE)
+                                        m_errorTool->rodError(robid, rodErr);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Find CMM hits for given crate, dataID
+
+const LVL1::CMMJetHits* JepRoiByteStreamV1Tool::findCmmHits(const int crate,
+                                                            const int dataID)
+{
+  const LVL1::CMMJetHits* hits = 0;
+  CmmHitsMap::const_iterator mapIter;
+  mapIter = m_cmmHitsMap.find(crate*100 + dataID);
+  if (mapIter != m_cmmHitsMap.end()) hits = mapIter->second;
+  return hits;
+}
+
+// Find CMM energy sums for given crate, module, dataID
+
+const LVL1::CMMEtSums* JepRoiByteStreamV1Tool::findCmmSums(const int crate,
+                                                           const int dataID)
+{
+  const LVL1::CMMEtSums* sums = 0;
+  CmmSumsMap::const_iterator mapIter;
+  mapIter = m_cmmEtMap.find(crate*100 + dataID);
+  if (mapIter != m_cmmEtMap.end()) sums = mapIter->second;
+  return sums;
+}
+
+// Set up JEM RoIs map
+
+void JepRoiByteStreamV1Tool::setupJemRoiMap(const JemRoiCollection*
+                                                            const jeCollection)
+{
+  m_roiMap.clear();
+  if (jeCollection) {
+    JemRoiCollection::const_iterator pos  = jeCollection->begin();
+    JemRoiCollection::const_iterator pose = jeCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::JEMRoI* const roi = *pos;
+      const uint32_t key = roi->roiWord();
+      m_roiMap.insert(std::make_pair(key, roi));
+    }
+  }
+}
+
+// Set up CMM hits map
+
+void JepRoiByteStreamV1Tool::setupCmmHitsMap(const CmmHitsCollection*
+                                                           const hitCollection)
+{
+  m_cmmHitsMap.clear();
+  if (hitCollection) {
+    CmmHitsCollection::const_iterator pos  = hitCollection->begin();
+    CmmHitsCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CMMJetHits* const hits = *pos;
+      const int crate = hits->crate() - m_crateOffsetSw;
+      const int key   = crate*100 + hits->dataID();
+      m_cmmHitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Set up CMM energy sums map
+
+void JepRoiByteStreamV1Tool::setupCmmEtMap(const CmmSumsCollection*
+                                                            const etCollection)
+{
+  m_cmmEtMap.clear();
+  if (etCollection) {
+    CmmSumsCollection::const_iterator pos  = etCollection->begin();
+    CmmSumsCollection::const_iterator pose = etCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CMMEtSums* const sums = *pos;
+      const int crate = sums->crate() - m_crateOffsetSw;
+      const int key   = crate*100 + sums->dataID();
+      m_cmmEtMap.insert(std::make_pair(key, sums));
+    }
+  }
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV1Tool.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV1Tool.h
new file mode 100755
index 0000000000000000000000000000000000000000..de988f341856c64d9c9d29fddc6f14afea7752da
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV1Tool.h
@@ -0,0 +1,158 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEPROIBYTESTREAMV1TOOL_H
+#define TRIGT1CALOBYTESTREAM_JEPROIBYTESTREAMV1TOOL_H
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+#include "ByteStreamData/RawEvent.h"
+#include "DataModel/DataVector.h"
+#include "eformat/SourceIdentifier.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class IInterface;
+class InterfaceID;
+class StatusCode;
+
+template <class T> class FullEventAssembler;
+
+namespace LVL1 {
+  class CMMJetHits;
+  class CMMEtSums;
+  class CMMRoI;
+  class JEMRoI;
+  class JEPRoIBSCollectionV1;
+}
+
+namespace LVL1BS {
+
+class CmmEnergySubBlock;
+class CmmJetSubBlock;
+class JemRoiSubBlockV1;
+class L1CaloErrorByteStreamTool;
+class L1CaloSrcIdMap;
+
+/** Tool to perform ROB fragments to JEM RoI and CMM RoI,
+ *  and JEP RoI container to raw data conversions.
+ *
+ *  Based on ROD document version 1_09h.
+ *
+ *  @author Peter Faulkner
+ */
+
+class JepRoiByteStreamV1Tool : public AthAlgTool {
+
+ public:
+   JepRoiByteStreamV1Tool(const std::string& type, const std::string& name,
+                          const IInterface* parent);
+   virtual ~JepRoiByteStreamV1Tool();
+
+   /// AlgTool InterfaceID
+   static const InterfaceID& interfaceID();
+
+   virtual StatusCode initialize();
+   virtual StatusCode finalize();
+
+   /// Convert ROB fragments to JEM RoIs
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::JEMRoI>* jeCollection);
+   /// Convert ROB fragments to CMM RoIs
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      LVL1::CMMRoI* cmCollection);
+
+   /// Convert JEP RoI Container to bytestream
+   StatusCode convert(const LVL1::JEPRoIBSCollectionV1* jep, RawEventWrite* re);
+
+   /// Return reference to vector with all possible Source Identifiers
+   const std::vector<uint32_t>& sourceIDs(const std::string& sgKey);
+
+ private:
+   enum CollectionType { JEM_ROI, CMM_ROI };
+
+   typedef DataVector<LVL1::JEMRoI>                      JemRoiCollection;
+   typedef DataVector<LVL1::CMMJetHits>                  CmmHitsCollection;
+   typedef DataVector<LVL1::CMMEtSums>                   CmmSumsCollection;
+   typedef std::map<uint32_t, const LVL1::JEMRoI*>       JemRoiMap;
+   typedef std::map<int, const LVL1::CMMJetHits*>        CmmHitsMap;
+   typedef std::map<int, const LVL1::CMMEtSums*>         CmmSumsMap;
+   typedef IROBDataProviderSvc::VROBFRAG::const_iterator ROBIterator;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      ROBPointer;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      RODPointer;
+
+   /// Convert bytestream to given container type
+   StatusCode convertBs(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                        CollectionType collection);
+
+   /// Error collection tool
+   ToolHandle<LVL1BS::L1CaloErrorByteStreamTool> m_errorTool;
+
+   /// Find CMM hits for given crate, data ID
+   const LVL1::CMMJetHits* findCmmHits(int crate, int dataID);
+   /// Find CMM energy sums for given crate, data ID
+   const LVL1::CMMEtSums*  findCmmSums(int crate, int dataID);
+
+   /// Set up JEM RoIs map
+   void setupJemRoiMap(const JemRoiCollection* jeCollection);
+   /// Set up CMM hits map
+   void setupCmmHitsMap(const CmmHitsCollection* hitCollection);
+   /// Set up CMM energy sums map
+   void setupCmmEtMap(const CmmSumsCollection* enCollection);
+
+   /// Hardware crate number offset
+   int m_crateOffsetHw;
+   /// Software crate number offset
+   int m_crateOffsetSw;
+   /// Sub_block header version
+   int m_version;
+   /// Data compression format
+   int m_dataFormat;
+   /// Number of crates
+   int m_crates;
+   /// Number of JEM modules per crate
+   int m_modules;
+   /// Number of slinks per crate when writing out bytestream
+   int m_slinks;
+   /// Minimum crate number when writing out bytestream
+   int m_crateMin;
+   /// Maximum crate number when writing out bytestream
+   int m_crateMax;
+   /// ROB source IDs
+   std::vector<uint32_t> m_sourceIDs;
+   /// ROB source IDs for RoIB
+   std::vector<uint32_t> m_sourceIDsRoIB;
+   /// Sub-detector type
+   eformat::SubDetector m_subDetector;
+   /// Source ID converter
+   L1CaloSrcIdMap* m_srcIdMap;
+   /// Sub-block for neutral format
+   JemRoiSubBlockV1*  m_subBlock;
+   /// Current JEM RoI collection
+   JemRoiCollection* m_jeCollection;
+   /// Current CMM RoI collection
+   LVL1::CMMRoI*     m_cmCollection;
+   /// JEM RoI map
+   JemRoiMap  m_roiMap;
+   /// CMM hits map
+   CmmHitsMap m_cmmHitsMap;
+   /// CMM energy sums map
+   CmmSumsMap m_cmmEtMap;
+   /// ROD Status words
+   std::vector<uint32_t>* m_rodStatus;
+   /// ROD status map
+   std::map<uint32_t, std::vector<uint32_t>* > m_rodStatusMap;
+   /// Event assembler
+   FullEventAssembler<L1CaloSrcIdMap>* m_fea;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV2Cnv.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV2Cnv.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..51c66370575956c6ff569e3b15245d18b1e4d6c6
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV2Cnv.cxx
@@ -0,0 +1,115 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IByteStreamEventAccess.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "DataModel/DataVector.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/IRegistry.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "TrigT1CaloEvent/JEPRoIBSCollectionV2.h"
+
+#include "JepRoiByteStreamV2Cnv.h"
+#include "JepRoiByteStreamV2Tool.h"
+
+namespace LVL1BS {
+
+JepRoiByteStreamV2Cnv::JepRoiByteStreamV2Cnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("JepRoiByteStreamV2Cnv"),
+      m_tool("LVL1BS::JepRoiByteStreamV2Tool/JepRoiByteStreamV2Tool"),
+      m_ByteStreamEventAccess("ByteStreamCnvSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+JepRoiByteStreamV2Cnv::~JepRoiByteStreamV2Cnv()
+{
+}
+
+// CLID
+
+const CLID& JepRoiByteStreamV2Cnv::classID()
+{
+  return ClassID_traits<LVL1::JEPRoIBSCollectionV2>::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode JepRoiByteStreamV2Cnv::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << " - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  //Get ByteStreamCnvSvc
+  sc = m_ByteStreamEventAccess.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_ByteStreamEventAccess << endreq;
+    return sc;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_ByteStreamEventAccess << endreq;
+  }
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return StatusCode::FAILURE;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  return StatusCode::SUCCESS;
+}
+
+// createRep should create the bytestream from RDOs.
+
+StatusCode JepRoiByteStreamV2Cnv::createRep( DataObject* pObj,
+                                             IOpaqueAddress*& pAddr )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createRep() called" << endreq;
+
+  RawEventWrite* re = m_ByteStreamEventAccess->getRawEvent();
+
+  LVL1::JEPRoIBSCollectionV2* jep = 0;
+  if( !SG::fromStorable( pObj, jep ) ) {
+    m_log << MSG::ERROR << " Cannot cast to JEPRoIBSCollectionV2" << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = pObj->registry()->name();
+
+  ByteStreamAddress* addr = new ByteStreamAddress( classID(), nm, "" );
+
+  pAddr = addr;
+
+  // Convert to ByteStream
+  return m_tool->convert( jep, re );
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV2Cnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV2Cnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..c165db2f8e994ce03e864d7d36c70619e1399626
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV2Cnv.h
@@ -0,0 +1,76 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEPROIBYTESTREAMV2CNV_H
+#define TRIGT1CALOBYTESTREAM_JEPROIBYTESTREAMV2CNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IByteStreamEventAccess;
+class IOpaqueAddress;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class JepRoiByteStreamV2Tool;
+
+/** ByteStream converter for JEP RoI container post LS1
+ *
+ *  @author Peter Faulkner
+ */
+
+class JepRoiByteStreamV2Cnv: public Converter {
+
+  friend class CnvFactory<JepRoiByteStreamV2Cnv>;
+
+protected:
+
+  JepRoiByteStreamV2Cnv(ISvcLocator* svcloc);
+
+public:
+
+  ~JepRoiByteStreamV2Cnv();
+
+  virtual StatusCode initialize();
+  /// Create ByteStream from JEP Container
+  virtual StatusCode createRep(DataObject* pObj, IOpaqueAddress*& pAddr);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::JepRoiByteStreamV2Tool> m_tool;
+
+  /// Service for writing bytestream
+  ServiceHandle<IByteStreamEventAccess> m_ByteStreamEventAccess;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV2Tool.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV2Tool.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..56caeb77712bf530a391e3b737dfb356e289f16d
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV2Tool.cxx
@@ -0,0 +1,698 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <numeric>
+#include <set>
+#include <utility>
+
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "ByteStreamCnvSvcBase/FullEventAssembler.h"
+
+#include "TrigT1CaloEvent/CMXEtSums.h"
+#include "TrigT1CaloEvent/CMXRoI.h"
+#include "TrigT1CaloEvent/JEMTobRoI.h"
+#include "TrigT1CaloEvent/JEPRoIBSCollectionV2.h"
+
+#include "CmxSubBlock.h"
+#include "JemRoiSubBlockV2.h"
+#include "L1CaloErrorByteStreamTool.h"
+#include "L1CaloSrcIdMap.h"
+#include "L1CaloSubBlock.h"
+#include "L1CaloUserHeader.h"
+
+#include "JepRoiByteStreamV2Tool.h"
+
+namespace LVL1BS {
+
+// Interface ID
+
+static const InterfaceID IID_IJepRoiByteStreamV2Tool("JepRoiByteStreamV2Tool",
+                                                                        1, 1);
+
+const InterfaceID& JepRoiByteStreamV2Tool::interfaceID()
+{
+  return IID_IJepRoiByteStreamV2Tool;
+}
+
+// Constructor
+
+JepRoiByteStreamV2Tool::JepRoiByteStreamV2Tool(const std::string& type,
+                                               const std::string& name,
+    				               const IInterface*  parent)
+  : AthAlgTool(type, name, parent),
+    m_errorTool("LVL1BS::L1CaloErrorByteStreamTool/L1CaloErrorByteStreamTool"),
+    m_crates(2), m_modules(16), m_frames(8), m_maxRoiWords(6),
+    m_srcIdMap(0), m_subBlock(0), m_rodStatus(0), m_fea(0)
+{
+  declareInterface<JepRoiByteStreamV2Tool>(this);
+
+  declareProperty("ErrorTool", m_errorTool,
+                  "Tool to collect errors for monitoring");
+  declareProperty("CrateOffsetHw",  m_crateOffsetHw = 12,
+                  "Offset of JEP crate numbers in bytestream");
+  declareProperty("CrateOffsetSw",  m_crateOffsetSw = 0,
+                  "Offset of JEP crate numbers in RDOs");
+
+  // Properties for reading bytestream only
+  declareProperty("ROBSourceIDs",       m_sourceIDs,
+                  "ROB fragment source identifiers");
+  declareProperty("ROBSourceIDsRoIB",   m_sourceIDsRoIB,
+                  "ROB fragment source identifiers");
+
+  // Properties for writing bytestream only
+  declareProperty("DataVersion",    m_version       = 2,                   //<<== CHECK
+                  "Format version number in sub-block header");
+  declareProperty("DataFormat",     m_dataFormat    = 1,
+                  "Format identifier (0-1) in sub-block header");
+  declareProperty("SlinksPerCrate", m_slinks        = 1,
+                  "The number of S-Links per crate");
+  declareProperty("CrateMin",       m_crateMin = 0,
+                  "Minimum crate number, allows partial output");
+  declareProperty("CrateMax",       m_crateMax = m_crates-1,
+                  "Maximum crate number, allows partial output");
+
+}
+
+// Destructor
+
+JepRoiByteStreamV2Tool::~JepRoiByteStreamV2Tool()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode JepRoiByteStreamV2Tool::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = m_errorTool.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_errorTool << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_errorTool << endreq;
+
+  m_subDetector = eformat::TDAQ_CALO_JET_PROC_ROI;
+  m_srcIdMap    = new L1CaloSrcIdMap();
+  m_subBlock    = new JemRoiSubBlockV2();
+  m_rodStatus   = new std::vector<uint32_t>(2);
+  m_fea         = new FullEventAssembler<L1CaloSrcIdMap>();
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode JepRoiByteStreamV2Tool::finalize()
+{
+  delete m_fea;
+  delete m_rodStatus;
+  delete m_subBlock;
+  delete m_srcIdMap;
+  return StatusCode::SUCCESS;
+}
+
+// Conversion bytestream to JEM RoI
+
+StatusCode JepRoiByteStreamV2Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::JEMTobRoI>* const jeCollection)
+{
+  m_jeCollection = jeCollection;
+  return convertBs(robFrags, JEM_ROI);
+}
+
+// Conversion bytestream to CMX RoI
+
+StatusCode JepRoiByteStreamV2Tool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            LVL1::CMXRoI* const cmCollection)
+{
+  m_cmCollection = cmCollection;
+  return convertBs(robFrags, CMX_ROI);
+}
+
+// Conversion of JEP container to bytestream
+
+StatusCode JepRoiByteStreamV2Tool::convert(
+                                 const LVL1::JEPRoIBSCollectionV2* const jep,
+                                 RawEventWrite* const re)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Clear the event assembler
+
+  m_fea->clear();
+  const uint16_t minorVersion = m_srcIdMap->minorVersion();
+  m_fea->setRodMinorVersion(minorVersion);
+  m_rodStatusMap.clear();
+
+  // Pointer to ROD data vector
+
+  FullEventAssembler<L1CaloSrcIdMap>::RODDATA* theROD = 0;
+
+  // Set up the container maps
+
+  const bool neutralFormat = m_dataFormat == L1CaloSubBlock::NEUTRAL;
+  setupJemRoiMap(jep->JemRoi());
+  JemRoiMap::const_iterator mapIter    = m_roiMap.begin();
+  JemRoiMap::const_iterator mapIterEnd = m_roiMap.end();
+  if (neutralFormat) {
+    setupCmxEtMap(jep->CmxSums());
+  }
+
+  // Loop over JEM RoI data
+
+  const int modulesPerSlink = m_modules / m_slinks;
+  for (int crate = m_crateMin; crate <= m_crateMax; ++crate) {
+    const int hwCrate = crate + m_crateOffsetHw;
+
+    for (int module=0; module < m_modules; ++module) {
+
+      // Pack required number of modules per slink
+
+      if (module%modulesPerSlink == 0) {
+	const int daqOrRoi = 1;
+	const int slink = module/modulesPerSlink;
+        if (debug) {
+          msg() << "Treating crate " << hwCrate
+	        << " slink " << slink << endreq
+	        << "Data Version/Format: " << m_version
+	        << " " << m_dataFormat << endreq;
+        }
+	const uint32_t rodIdJem = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+	                                                        m_subDetector);
+	theROD = m_fea->getRodData(rodIdJem);
+        if (neutralFormat) {
+          const L1CaloUserHeader userHeader;
+	  theROD->push_back(userHeader.header());
+        }
+	m_rodStatusMap.insert(make_pair(rodIdJem, m_rodStatus));
+      }
+      if (debug) msg() << "JEM Module " << module << endreq;
+      if (!theROD) break; // for coverity, shouldn't happen
+
+      // Create a sub-block (Neutral format only)
+
+      if (neutralFormat) {
+        m_subBlock->clear();
+	m_subBlock->setRoiHeader(m_version, hwCrate, module);
+      }
+
+      // Find JEM RoIs for this module
+
+      for (; mapIter != mapIterEnd; ++mapIter) {
+        const LVL1::JEMTobRoI* const roi = mapIter->second;
+	if (roi->crate() < crate)  continue;
+	if (roi->crate() > crate)  break;
+	if (roi->jem()   < module) continue;
+	if (roi->jem()   > module) break;
+	if (roi->energyLarge() || roi->energySmall()) {
+	  if (neutralFormat) m_subBlock->fillRoi(*roi);
+	  else theROD->push_back(roi->roiWord());
+        }
+      }
+
+      // Pack and write the sub-block
+
+      if (neutralFormat) {
+        if ( !m_subBlock->pack()) {
+	  msg(MSG::ERROR) << "JEM RoI sub-block packing failed" << endreq;
+	  return StatusCode::FAILURE;
+        }
+	if (debug) {
+	  msg() << "JEM RoI sub-block data words: "
+	        << m_subBlock->dataWords() << endreq;
+	}
+	m_subBlock->write(theROD);
+      }
+    }
+    if (!theROD) break; // for coverity, shouldn't happen
+
+    // Append CMX RoIs to last S-Link of the system crate
+
+    if (crate != m_crates - 1) continue;
+
+    // Create sub-blocks for Neutral format
+
+    if (neutralFormat) {
+      const int timeslices = 1;
+      const int slice = 0;
+
+      // CMX-Energy
+
+      CmxEnergySubBlock subBlock;
+      const int cmxEnergyVersion = 3;                                      //<<== CHECK
+      subBlock.setCmxHeader(cmxEnergyVersion, m_dataFormat, slice, hwCrate,
+                            CmxSubBlock::SYSTEM, CmxSubBlock::CMX_ENERGY,
+	 		    CmxSubBlock::LEFT, timeslices);
+      int maxSource = static_cast<int>(LVL1::CMXEtSums::MAX_SOURCE);
+      for (int source = 0; source < maxSource; ++source) {
+        const LVL1::CMXEtSums* const sums = findCmxSums(crate, source);
+        if ( sums ) {
+          const unsigned int ex = sums->Ex();
+          const unsigned int ey = sums->Ey();
+          const unsigned int et = sums->Et();
+          const int exErr = sums->ExError();
+          const int eyErr = sums->EyError();
+          const int etErr = sums->EtError();
+	  if (source < m_modules) {
+	    subBlock.setSubsums(slice, source, ex, ey, et, exErr, eyErr, etErr);
+	  } else {
+	    CmxEnergySubBlock::SourceType srcType = CmxEnergySubBlock::MAX_SOURCE_TYPE;
+	    CmxEnergySubBlock::SumType    sumType = CmxEnergySubBlock::MAX_SUM_TYPE;
+	    CmxEnergySubBlock::HitsType   hitType = CmxEnergySubBlock::MAX_HITS_TYPE;
+	    energySubBlockTypes(source, srcType, sumType, hitType);
+	    if (srcType != CmxEnergySubBlock::MAX_SOURCE_TYPE) {
+	      subBlock.setSubsums(slice, srcType, sumType, ex, ey, et,
+	                          exErr, eyErr, etErr);
+            } else if (hitType != CmxEnergySubBlock::MAX_HITS_TYPE) {
+	      subBlock.setEtHits(slice, hitType, sumType, et);
+            }
+          }
+        }
+      }
+      if ( !subBlock.pack()) {
+        msg(MSG::ERROR) << "CMX-Energy sub-block packing failed" << endreq;
+	return StatusCode::FAILURE;
+      }
+      if (debug) {
+	msg() << "CMX-Energy sub-block data words: "
+	      << subBlock.dataWords() << endreq;
+      }
+      subBlock.write(theROD);
+
+    } else {
+
+      // Standard format
+
+      const LVL1::CMXRoI* const roi = jep->CmxRoi();
+      if ( roi ) {
+        // CMX-Energy RoIs are not zero-supressed unless all are zero
+	for (int word = 0; word < m_maxRoiWords; ++word) {
+          theROD->push_back(roi->roiWord(word));
+        }
+      }
+    }
+  }
+
+  // Fill the raw event
+
+  m_fea->fill(re, msg());
+
+  // Set ROD status words
+
+  //L1CaloRodStatus::setStatus(re, m_rodStatusMap, m_srcIdMap);
+
+  return StatusCode::SUCCESS;
+}
+
+// Return reference to vector with all possible Source Identifiers
+
+const std::vector<uint32_t>& JepRoiByteStreamV2Tool::sourceIDs(
+                                                   const std::string& sgKey)
+{
+  const std::string flag("RoIB");
+  const std::string::size_type pos = sgKey.find(flag);
+  const bool roiDaq =
+           (pos == std::string::npos || pos != sgKey.length() - flag.length());
+  const bool empty  = (roiDaq) ? m_sourceIDs.empty() : m_sourceIDsRoIB.empty();
+  if (empty) {
+    const int maxCrates = m_crates + m_crateOffsetHw;
+    const int maxSlinks = m_srcIdMap->maxSlinks();
+    for (int hwCrate = m_crateOffsetHw; hwCrate < maxCrates; ++hwCrate) {
+      for (int slink = 0; slink < maxSlinks; ++slink) {
+        const int daqOrRoi = 1;
+        const uint32_t rodId = m_srcIdMap->getRodID(hwCrate, slink, daqOrRoi,
+                                                             m_subDetector);
+        const uint32_t robId = m_srcIdMap->getRobID(rodId);
+	if (roiDaq) {
+	  if (slink < 2) m_sourceIDs.push_back(robId);
+	} else if (slink >= 2) m_sourceIDsRoIB.push_back(robId);
+      }
+    }
+  }
+  return (roiDaq) ? m_sourceIDs : m_sourceIDsRoIB;
+}
+
+// Convert bytestream to given container type
+
+StatusCode JepRoiByteStreamV2Tool::convertBs(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            const CollectionType collection)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Loop over ROB fragments
+
+  int robCount = 0;
+  std::set<uint32_t> dupCheck;
+  std::set<uint32_t> dupRoiCheck;
+  ROBIterator rob    = robFrags.begin();
+  ROBIterator robEnd = robFrags.end();
+  for (; rob != robEnd; ++rob) {
+
+    if (debug) {
+      ++robCount;
+      msg() << "Treating ROB fragment " << robCount << endreq;
+    }
+
+    // Skip fragments with ROB status errors
+
+    uint32_t robid = (*rob)->source_id();
+    if ((*rob)->nstatus() > 0) {
+      ROBPointer robData;
+      (*rob)->status(robData);
+      if (*robData != 0) {
+        m_errorTool->robError(robid, *robData);
+	if (debug) msg() << "ROB status error - skipping fragment" << endreq;
+	continue;
+      }
+    }
+
+    // Skip duplicate fragments
+
+    if (!dupCheck.insert(robid).second) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_DUPLICATE_ROB);
+      if (debug) msg() << "Skipping duplicate ROB fragment" << endreq;
+      continue;
+    }
+
+    // Unpack ROD data (slinks)
+
+    RODPointer payloadBeg;
+    RODPointer payload;
+    RODPointer payloadEnd;
+    (*rob)->rod_data(payloadBeg);
+    payloadEnd = payloadBeg + (*rob)->rod_ndata();
+    payload = payloadBeg;
+    if (payload == payloadEnd) {
+      if (debug) msg() << "ROB fragment empty" << endreq;
+      continue;
+    }
+
+    // Check identifier
+    const uint32_t sourceID = (*rob)->rod_source_id();
+    if (m_srcIdMap->getRobID(sourceID) != robid           ||
+        m_srcIdMap->subDet(sourceID)   != m_subDetector   ||
+	m_srcIdMap->daqOrRoi(sourceID) != 1               ||
+       (m_srcIdMap->slink(sourceID) != 0 && m_srcIdMap->slink(sourceID) != 2) ||
+        m_srcIdMap->crate(sourceID)    <  m_crateOffsetHw ||
+	m_srcIdMap->crate(sourceID)    >= m_crateOffsetHw + m_crates) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_ROD_ID);
+      if (debug) {
+        msg() << "Wrong source identifier in data: "
+	      << MSG::hex << sourceID << MSG::dec << endreq;
+      }
+      continue;
+    }
+
+    // Check minor version
+    const int minorVersion = (*rob)->rod_version() & 0xffff;
+    if (minorVersion <= m_srcIdMap->minorVersionPreLS1()) {
+      if (debug) msg() << "Skipping pre-LS1 data" << endreq;
+      continue;
+    }
+    const int rodCrate = m_srcIdMap->crate(sourceID);
+    if (debug) {
+      msg() << "Treating crate " << rodCrate 
+            << " slink " << m_srcIdMap->slink(sourceID) << endreq;
+    }
+
+    // First word may be User Header
+    if (L1CaloUserHeader::isValid(*payload)) {
+      L1CaloUserHeader userHeader(*payload);
+      userHeader.setVersion(minorVersion);
+      const int headerWords = userHeader.words();
+      if (headerWords != 1) {
+        m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
+        if (debug) msg() << "Unexpected number of user header words: "
+	                 << headerWords << endreq;
+        continue;
+      }
+      for (int i = 0; i < headerWords; ++i) ++payload;
+    }
+
+    // Loop over sub-blocks if there are any
+
+    unsigned int rodErr = L1CaloSubBlock::ERROR_NONE;
+    while (payload != payloadEnd) {
+      
+      if (L1CaloSubBlock::wordType(*payload) == L1CaloSubBlock::HEADER) {
+	const int slice = 0;
+        if (CmxSubBlock::cmxBlock(*payload)) {
+          // CMXs
+	  if (CmxSubBlock::cmxType(*payload) == CmxSubBlock::CMX_ENERGY) {
+	    CmxEnergySubBlock subBlock;
+	    payload = subBlock.read(payload, payloadEnd);
+	    if (collection == CMX_ROI) {
+	      if (subBlock.dataWords() && !subBlock.unpack()) {
+	        if (debug) {
+		  std::string errMsg(subBlock.unpackErrorMsg());
+	          msg() << "CMX-Energy sub-block unpacking failed: "
+		        << errMsg << endreq;
+	        }
+		rodErr = m_subBlock->unpackErrorCode();
+		break;
+              }
+	      const LVL1::CMXRoI roi(
+	        subBlock.energy(slice, CmxEnergySubBlock::TOTAL,
+		                       CmxEnergySubBlock::STANDARD,
+				       CmxEnergySubBlock::ENERGY_EX),
+	        subBlock.energy(slice, CmxEnergySubBlock::TOTAL,
+		                       CmxEnergySubBlock::STANDARD,
+				       CmxEnergySubBlock::ENERGY_EY),
+	        subBlock.energy(slice, CmxEnergySubBlock::TOTAL,
+		                       CmxEnergySubBlock::STANDARD,
+				       CmxEnergySubBlock::ENERGY_ET),
+	        subBlock.error(slice, CmxEnergySubBlock::TOTAL,
+		                      CmxEnergySubBlock::STANDARD,
+				      CmxEnergySubBlock::ENERGY_EX),
+	        subBlock.error(slice, CmxEnergySubBlock::TOTAL,
+		                      CmxEnergySubBlock::STANDARD,
+				      CmxEnergySubBlock::ENERGY_EY),
+	        subBlock.error(slice, CmxEnergySubBlock::TOTAL,
+		                      CmxEnergySubBlock::STANDARD,
+				      CmxEnergySubBlock::ENERGY_ET),
+		subBlock.hits(slice, CmxEnergySubBlock::SUM_ET,
+		                     CmxEnergySubBlock::STANDARD),
+		subBlock.hits(slice, CmxEnergySubBlock::MISSING_ET,
+		                     CmxEnergySubBlock::STANDARD),
+		subBlock.hits(slice, CmxEnergySubBlock::MISSING_ET_SIG,
+		                     CmxEnergySubBlock::STANDARD),
+	        subBlock.energy(slice, CmxEnergySubBlock::TOTAL,
+		                       CmxEnergySubBlock::RESTRICTED_WEIGHTED,
+				       CmxEnergySubBlock::ENERGY_EX),
+	        subBlock.energy(slice, CmxEnergySubBlock::TOTAL,
+		                       CmxEnergySubBlock::RESTRICTED_WEIGHTED,
+				       CmxEnergySubBlock::ENERGY_EY),
+	        subBlock.energy(slice, CmxEnergySubBlock::TOTAL,
+		                       CmxEnergySubBlock::RESTRICTED_WEIGHTED,
+				       CmxEnergySubBlock::ENERGY_ET),
+	        subBlock.error(slice, CmxEnergySubBlock::TOTAL,
+		                      CmxEnergySubBlock::RESTRICTED_WEIGHTED,
+				      CmxEnergySubBlock::ENERGY_EX),
+	        subBlock.error(slice, CmxEnergySubBlock::TOTAL,
+		                      CmxEnergySubBlock::RESTRICTED_WEIGHTED,
+				      CmxEnergySubBlock::ENERGY_EY),
+	        subBlock.error(slice, CmxEnergySubBlock::TOTAL,
+		                      CmxEnergySubBlock::RESTRICTED_WEIGHTED,
+				      CmxEnergySubBlock::ENERGY_ET),
+		subBlock.hits(slice, CmxEnergySubBlock::SUM_ET,
+		                     CmxEnergySubBlock::RESTRICTED_WEIGHTED),
+		subBlock.hits(slice, CmxEnergySubBlock::MISSING_ET,
+		                     CmxEnergySubBlock::RESTRICTED_WEIGHTED));
+	      for (int word = 0; word < m_maxRoiWords; ++word) {
+	        m_cmCollection->setRoiWord(roi.roiWord(word));
+	      }
+            }
+	  }
+        } else {
+          // JEM RoI
+          JemRoiSubBlockV2 subBlock;
+          payload = subBlock.read(payload, payloadEnd);
+	  if (collection == JEM_ROI) {
+	    if (subBlock.dataWords() && !subBlock.unpack()) {
+	      if (debug) {
+		std::string errMsg(subBlock.unpackErrorMsg());
+	        msg() << "JEM RoI sub-block unpacking failed: "
+		      << errMsg << endreq;
+	      }
+              rodErr = m_subBlock->unpackErrorCode();
+              break;
+            }
+	    for (int frame = 0; frame < m_frames; ++frame) {
+	      const LVL1::JEMTobRoI roi = subBlock.roi(frame);
+	      if (roi.energyLarge() || roi.energySmall()) {
+		m_jeCollection->push_back(new LVL1::JEMTobRoI(roi));
+	      }
+	    }
+          }
+        }
+      } else {
+        // Just RoI word
+	LVL1::JEMTobRoI jroi;
+	LVL1::CMXRoI croi;
+	if (jroi.setRoiWord(*payload)) {
+	  if (collection == JEM_ROI) {
+	    if (jroi.crate() != rodCrate - m_crateOffsetHw) {
+	      if (debug) msg() << "Inconsistent RoI crate number: "
+	                       << jroi.crate() << endreq;
+              rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	      break;
+            }
+	    const uint32_t location = (*payload) & 0xfff80000;
+	    if (dupRoiCheck.insert(location).second) {
+	      if (jroi.energyLarge() || jroi.energySmall()) {
+	        m_jeCollection->push_back(new LVL1::JEMTobRoI(*payload));
+	      }
+	    } else {
+	      if (debug) msg() << "Duplicate RoI word "
+	                       << MSG::hex << *payload << MSG::dec << endreq;
+              rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	      break;
+            }
+	  }
+        } else if (croi.setRoiWord(*payload)) {
+	  if (collection == CMX_ROI) {
+	    const uint32_t roiType = (*payload) & 0xf8000000;
+	    if (dupRoiCheck.insert(roiType).second) {
+	      m_cmCollection->setRoiWord(*payload);
+	    } else {
+	      if (debug) msg() << "Duplicate RoI word "
+	                       << MSG::hex << *payload << MSG::dec << endreq;
+              rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	      break;
+            }
+	  }
+        } else {
+	  if (debug) msg() << "Invalid RoI word "
+	                   << MSG::hex << *payload << MSG::dec << endreq;
+	  rodErr = L1CaloSubBlock::ERROR_ROI_TYPE;
+	  break;
+        }
+	++payload;
+      }
+    }
+    if (rodErr != L1CaloSubBlock::ERROR_NONE)
+                                        m_errorTool->rodError(robid, rodErr);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Find CMX energy sums for given crate, source
+
+const LVL1::CMXEtSums* JepRoiByteStreamV2Tool::findCmxSums(const int crate,
+                                                           const int source)
+{
+  const LVL1::CMXEtSums* sums = 0;
+  CmxSumsMap::const_iterator mapIter;
+  mapIter = m_cmxEtMap.find(crate*100 + source);
+  if (mapIter != m_cmxEtMap.end()) sums = mapIter->second;
+  return sums;
+}
+
+// Set up JEM RoIs map
+
+void JepRoiByteStreamV2Tool::setupJemRoiMap(const JemRoiCollection*
+                                                            const jeCollection)
+{
+  m_roiMap.clear();
+  if (jeCollection) {
+    JemRoiCollection::const_iterator pos  = jeCollection->begin();
+    JemRoiCollection::const_iterator pose = jeCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::JEMTobRoI* const roi = *pos;
+      const uint32_t key = roi->roiWord();
+      m_roiMap.insert(std::make_pair(key, roi));
+    }
+  }
+}
+
+// Set up CMX energy sums map
+
+void JepRoiByteStreamV2Tool::setupCmxEtMap(const CmxSumsCollection*
+                                                            const etCollection)
+{
+  m_cmxEtMap.clear();
+  if (etCollection) {
+    CmxSumsCollection::const_iterator pos  = etCollection->begin();
+    CmxSumsCollection::const_iterator pose = etCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CMXEtSums* const sums = *pos;
+      const int crate = sums->crate() - m_crateOffsetSw;
+      const int key   = crate*100 + sums->source();
+      m_cmxEtMap.insert(std::make_pair(key, sums));
+    }
+  }
+}
+
+// Get energy subBlock types from CMXEtSums source type
+
+void JepRoiByteStreamV2Tool::energySubBlockTypes(const int source,
+                             CmxEnergySubBlock::SourceType& srcType,
+	   		     CmxEnergySubBlock::SumType&    sumType,
+			     CmxEnergySubBlock::HitsType&   hitType)
+{
+  switch (source) {
+    case LVL1::CMXEtSums::REMOTE_STANDARD:
+      srcType = CmxEnergySubBlock::REMOTE;
+      sumType = CmxEnergySubBlock::STANDARD;
+      break;
+    case LVL1::CMXEtSums::REMOTE_RESTRICTED:
+      srcType = CmxEnergySubBlock::REMOTE;
+      sumType = CmxEnergySubBlock::RESTRICTED_WEIGHTED;
+      break;
+    case LVL1::CMXEtSums::LOCAL_STANDARD:
+      srcType = CmxEnergySubBlock::LOCAL;
+      sumType = CmxEnergySubBlock::STANDARD;
+      break;
+    case LVL1::CMXEtSums::LOCAL_RESTRICTED:
+      srcType = CmxEnergySubBlock::LOCAL;
+      sumType = CmxEnergySubBlock::RESTRICTED_WEIGHTED;
+      break;
+    case LVL1::CMXEtSums::TOTAL_STANDARD:
+      srcType = CmxEnergySubBlock::TOTAL;
+      sumType = CmxEnergySubBlock::STANDARD;
+      break;
+    case LVL1::CMXEtSums::TOTAL_RESTRICTED:
+      srcType = CmxEnergySubBlock::TOTAL;
+      sumType = CmxEnergySubBlock::RESTRICTED_WEIGHTED;
+      break;
+    case LVL1::CMXEtSums::SUM_ET_STANDARD:
+      hitType = CmxEnergySubBlock::SUM_ET;
+      sumType = CmxEnergySubBlock::STANDARD;
+      break;
+    case LVL1::CMXEtSums::SUM_ET_RESTRICTED:
+      hitType = CmxEnergySubBlock::SUM_ET;
+      sumType = CmxEnergySubBlock::RESTRICTED_WEIGHTED;
+      break;
+    case LVL1::CMXEtSums::MISSING_ET_STANDARD:
+      hitType = CmxEnergySubBlock::MISSING_ET;
+      sumType = CmxEnergySubBlock::STANDARD;
+      break;
+    case LVL1::CMXEtSums::MISSING_ET_RESTRICTED:
+      hitType = CmxEnergySubBlock::MISSING_ET;
+      sumType = CmxEnergySubBlock::RESTRICTED_WEIGHTED;
+      break;
+    case LVL1::CMXEtSums::MISSING_ET_SIG_STANDARD:
+      hitType = CmxEnergySubBlock::MISSING_ET_SIG;
+      sumType = CmxEnergySubBlock::STANDARD;
+      break;
+    default:
+      break;
+  }
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV2Tool.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV2Tool.h
new file mode 100755
index 0000000000000000000000000000000000000000..c89982d70ab7341d2f06c5d4315173795460c8aa
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiByteStreamV2Tool.h
@@ -0,0 +1,158 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEPROIBYTESTREAMV2TOOL_H
+#define TRIGT1CALOBYTESTREAM_JEPROIBYTESTREAMV2TOOL_H
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+#include "ByteStreamData/RawEvent.h"
+#include "DataModel/DataVector.h"
+#include "eformat/SourceIdentifier.h"
+#include "GaudiKernel/ToolHandle.h"
+#include "CmxEnergySubBlock.h"
+
+class IInterface;
+class InterfaceID;
+class StatusCode;
+
+template <class T> class FullEventAssembler;
+
+namespace LVL1 {
+  class CMXEtSums;
+  class CMXRoI;
+  class JEMTobRoI;
+  class JEPRoIBSCollectionV2;
+}
+
+namespace LVL1BS {
+
+class JemRoiSubBlockV2;
+class L1CaloErrorByteStreamTool;
+class L1CaloSrcIdMap;
+
+/** Tool to perform ROB fragments to JEM RoI and CMX RoI,
+ *  and JEP RoI container to raw data conversions.
+ *
+ *  Based on ROD document version X_xxx.                                 <<== CHECK
+ *
+ *  @author Peter Faulkner
+ */
+
+class JepRoiByteStreamV2Tool : public AthAlgTool {
+
+ public:
+   JepRoiByteStreamV2Tool(const std::string& type, const std::string& name,
+                          const IInterface* parent);
+   virtual ~JepRoiByteStreamV2Tool();
+
+   /// AlgTool InterfaceID
+   static const InterfaceID& interfaceID();
+
+   virtual StatusCode initialize();
+   virtual StatusCode finalize();
+
+   /// Convert ROB fragments to JEM RoIs
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::JEMTobRoI>* jeCollection);
+   /// Convert ROB fragments to CMX RoIs
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      LVL1::CMXRoI* cmCollection);
+
+   /// Convert JEP RoI Container to bytestream
+   StatusCode convert(const LVL1::JEPRoIBSCollectionV2* jep, RawEventWrite* re);
+
+   /// Return reference to vector with all possible Source Identifiers
+   const std::vector<uint32_t>& sourceIDs(const std::string& sgKey);
+
+ private:
+   enum CollectionType { JEM_ROI, CMX_ROI };
+
+   typedef DataVector<LVL1::JEMTobRoI>                   JemRoiCollection;
+   typedef DataVector<LVL1::CMXEtSums>                   CmxSumsCollection;
+   typedef std::map<uint32_t, const LVL1::JEMTobRoI*>    JemRoiMap;
+   typedef std::map<int, const LVL1::CMXEtSums*>         CmxSumsMap;
+   typedef IROBDataProviderSvc::VROBFRAG::const_iterator ROBIterator;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      ROBPointer;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      RODPointer;
+
+   /// Convert bytestream to given container type
+   StatusCode convertBs(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                        CollectionType collection);
+
+   /// Error collection tool
+   ToolHandle<LVL1BS::L1CaloErrorByteStreamTool> m_errorTool;
+
+   /// Find CMX energy sums for given crate, source
+   const LVL1::CMXEtSums*  findCmxSums(int crate, int source);
+
+   /// Set up JEM RoIs map
+   void setupJemRoiMap(const JemRoiCollection* jeCollection);
+   /// Set up CMX energy sums map
+   void setupCmxEtMap(const CmxSumsCollection* enCollection);
+
+   /// Get energy subBlock types from CMXEtSums source type
+   void energySubBlockTypes(int source,
+                            CmxEnergySubBlock::SourceType& srcType,
+			    CmxEnergySubBlock::SumType&    sumType,
+			    CmxEnergySubBlock::HitsType&   hitType);
+
+   /// Hardware crate number offset
+   int m_crateOffsetHw;
+   /// Software crate number offset
+   int m_crateOffsetSw;
+   /// Sub_block header version
+   int m_version;
+   /// Data compression format
+   int m_dataFormat;
+   /// Number of crates
+   int m_crates;
+   /// Number of JEM modules per crate
+   int m_modules;
+   /// Number of RoI frames
+   int m_frames;
+   /// Number of CMX energy RoI words
+   int m_maxRoiWords;
+   /// Number of slinks per crate when writing out bytestream
+   int m_slinks;
+   /// Minimum crate number when writing out bytestream
+   int m_crateMin;
+   /// Maximum crate number when writing out bytestream
+   int m_crateMax;
+   /// ROB source IDs
+   std::vector<uint32_t> m_sourceIDs;
+   /// ROB source IDs for RoIB
+   std::vector<uint32_t> m_sourceIDsRoIB;
+   /// Sub-detector type
+   eformat::SubDetector m_subDetector;
+   /// Source ID converter
+   L1CaloSrcIdMap* m_srcIdMap;
+   /// Sub-block for neutral format
+   JemRoiSubBlockV2*  m_subBlock;
+   /// Current JEM RoI collection
+   JemRoiCollection* m_jeCollection;
+   /// Current CMX RoI collection
+   LVL1::CMXRoI*     m_cmCollection;
+   /// JEM RoI map
+   JemRoiMap  m_roiMap;
+   /// CMX energy sums map
+   CmxSumsMap m_cmxEtMap;
+   /// ROD Status words
+   std::vector<uint32_t>* m_rodStatus;
+   /// ROD status map
+   std::map<uint32_t, std::vector<uint32_t>* > m_rodStatusMap;
+   /// Event assembler
+   FullEventAssembler<L1CaloSrcIdMap>* m_fea;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiReadByteStreamCnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiReadByteStreamCnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..fc47779d44e6cb2fb5fa902a24102256d8947571
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiReadByteStreamCnv.h
@@ -0,0 +1,79 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEPROIREADBYTESTREAMCNV_H
+#define TRIGT1CALOBYTESTREAM_JEPROIREADBYTESTREAMCNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IOpaqueAddress;
+class IROBDataProviderSvc;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class JepRoiByteStreamTool;
+
+/** ByteStream converter for JEP component containers.
+ *
+ *  @author Peter Faulkner
+ */
+
+template <typename Container>
+class JepRoiReadByteStreamCnv: public Converter {
+
+  friend class CnvFactory<JepRoiReadByteStreamCnv<Container> >;
+
+protected:
+
+  JepRoiReadByteStreamCnv(ISvcLocator* svcloc);
+
+public:
+
+  ~JepRoiReadByteStreamCnv();
+
+  virtual StatusCode initialize();
+  /// Create Container from ByteStream
+  virtual StatusCode createObj(IOpaqueAddress* pAddr, DataObject*& pObj);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::JepRoiByteStreamTool> m_tool;
+
+  /// Service for reading bytestream
+  ServiceHandle<IROBDataProviderSvc> m_robDataProvider;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#include "JepRoiReadByteStreamCnv.icc"
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiReadByteStreamCnv.icc b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiReadByteStreamCnv.icc
new file mode 100755
index 0000000000000000000000000000000000000000..42f97957dae8e0722ebc2fde09a22991331d780a
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiReadByteStreamCnv.icc
@@ -0,0 +1,141 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+#include <stdint.h>
+#include <typeinfo>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "JepRoiByteStreamTool.h"
+
+namespace LVL1BS {
+
+template <typename Container>
+JepRoiReadByteStreamCnv<Container>::JepRoiReadByteStreamCnv(
+      ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("JepRoiReadByteStreamCnv"),
+      m_tool("LVL1BS::JepRoiByteStreamTool/JepRoiByteStreamTool"),
+      m_robDataProvider("ROBDataProviderSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+template <typename Container>
+JepRoiReadByteStreamCnv<Container>::~JepRoiReadByteStreamCnv()
+{
+}
+
+// CLID
+
+template <typename Container>
+const CLID& JepRoiReadByteStreamCnv<Container>::classID()
+{
+  return ClassID_traits<Container>::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+template <typename Container>
+StatusCode JepRoiReadByteStreamCnv<Container>::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << "<"
+                      << typeid(Container).name() << "> - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return sc;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  // Get ROBDataProvider
+  sc = m_robDataProvider.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_robDataProvider << endreq;
+    return sc ;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_robDataProvider << endreq;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// createObj should create the RDO from bytestream.
+
+template <typename Container>
+StatusCode JepRoiReadByteStreamCnv<Container>::createObj( IOpaqueAddress* pAddr,
+                                                          DataObject*& pObj )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createObj() called" << endreq;
+
+  ByteStreamAddress *pBS_Addr;
+  pBS_Addr = dynamic_cast<ByteStreamAddress *>( pAddr );
+  if ( !pBS_Addr ) {
+    m_log << MSG::ERROR << " Cannot cast to ByteStreamAddress " << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = *( pBS_Addr->par() );
+
+  if (m_debug) m_log << MSG::DEBUG << " Creating Objects " << nm << endreq;
+
+  // get SourceIDs
+  const std::vector<uint32_t>& vID(m_tool->sourceIDs(nm));
+
+  // get ROB fragments
+  IROBDataProviderSvc::VROBFRAG robFrags;
+  m_robDataProvider->getROBData( vID, robFrags );
+
+  // size check
+  Container* const collection = new Container;
+  if (m_debug) {
+    m_log << MSG::DEBUG << " Number of ROB fragments is " << robFrags.size()
+          << endreq;
+  }
+  if (robFrags.size() == 0) {
+    pObj = SG::asStorable(collection) ;
+    return StatusCode::SUCCESS;
+  }
+
+  StatusCode sc = m_tool->convert(robFrags, collection);
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << " Failed to create Objects   " << nm << endreq;
+    delete collection;
+    return sc;
+  }
+
+  pObj = SG::asStorable(collection);
+
+  return sc;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiReadByteStreamV1Cnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiReadByteStreamV1Cnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..60e1ea9128d212dddc02ec5c12288cb01ac3fff1
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiReadByteStreamV1Cnv.h
@@ -0,0 +1,79 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEPROIREADBYTESTREAMV1CNV_H
+#define TRIGT1CALOBYTESTREAM_JEPROIREADBYTESTREAMV1CNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IOpaqueAddress;
+class IROBDataProviderSvc;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class JepRoiByteStreamV1Tool;
+
+/** ByteStream converter for JEP component containers.
+ *
+ *  @author Peter Faulkner
+ */
+
+template <typename Container>
+class JepRoiReadByteStreamV1Cnv: public Converter {
+
+  friend class CnvFactory<JepRoiReadByteStreamV1Cnv<Container> >;
+
+protected:
+
+  JepRoiReadByteStreamV1Cnv(ISvcLocator* svcloc);
+
+public:
+
+  ~JepRoiReadByteStreamV1Cnv();
+
+  virtual StatusCode initialize();
+  /// Create Container from ByteStream
+  virtual StatusCode createObj(IOpaqueAddress* pAddr, DataObject*& pObj);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::JepRoiByteStreamV1Tool> m_tool;
+
+  /// Service for reading bytestream
+  ServiceHandle<IROBDataProviderSvc> m_robDataProvider;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#include "JepRoiReadByteStreamV1Cnv.icc"
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiReadByteStreamV1Cnv.icc b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiReadByteStreamV1Cnv.icc
new file mode 100755
index 0000000000000000000000000000000000000000..8cd3e21f28e4fcda3e7250c052b8d71a37cf4731
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiReadByteStreamV1Cnv.icc
@@ -0,0 +1,141 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+#include <stdint.h>
+#include <typeinfo>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "JepRoiByteStreamV1Tool.h"
+
+namespace LVL1BS {
+
+template <typename Container>
+JepRoiReadByteStreamV1Cnv<Container>::JepRoiReadByteStreamV1Cnv(
+      ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("JepRoiReadByteStreamV1Cnv"),
+      m_tool("LVL1BS::JepRoiByteStreamV1Tool/JepRoiByteStreamV1Tool"),
+      m_robDataProvider("ROBDataProviderSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+template <typename Container>
+JepRoiReadByteStreamV1Cnv<Container>::~JepRoiReadByteStreamV1Cnv()
+{
+}
+
+// CLID
+
+template <typename Container>
+const CLID& JepRoiReadByteStreamV1Cnv<Container>::classID()
+{
+  return ClassID_traits<Container>::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+template <typename Container>
+StatusCode JepRoiReadByteStreamV1Cnv<Container>::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << "<"
+                      << typeid(Container).name() << "> - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return sc;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  // Get ROBDataProvider
+  sc = m_robDataProvider.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_robDataProvider << endreq;
+    return sc ;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_robDataProvider << endreq;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// createObj should create the RDO from bytestream.
+
+template <typename Container>
+StatusCode JepRoiReadByteStreamV1Cnv<Container>::createObj( IOpaqueAddress* pAddr,
+                                                            DataObject*& pObj )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createObj() called" << endreq;
+
+  ByteStreamAddress *pBS_Addr;
+  pBS_Addr = dynamic_cast<ByteStreamAddress *>( pAddr );
+  if ( !pBS_Addr ) {
+    m_log << MSG::ERROR << " Cannot cast to ByteStreamAddress " << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = *( pBS_Addr->par() );
+
+  if (m_debug) m_log << MSG::DEBUG << " Creating Objects " << nm << endreq;
+
+  // get SourceIDs
+  const std::vector<uint32_t>& vID(m_tool->sourceIDs(nm));
+
+  // get ROB fragments
+  IROBDataProviderSvc::VROBFRAG robFrags;
+  m_robDataProvider->getROBData( vID, robFrags );
+
+  // size check
+  Container* const collection = new Container;
+  if (m_debug) {
+    m_log << MSG::DEBUG << " Number of ROB fragments is " << robFrags.size()
+          << endreq;
+  }
+  if (robFrags.size() == 0) {
+    pObj = SG::asStorable(collection) ;
+    return StatusCode::SUCCESS;
+  }
+
+  StatusCode sc = m_tool->convert(robFrags, collection);
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << " Failed to create Objects   " << nm << endreq;
+    delete collection;
+    return sc;
+  }
+
+  pObj = SG::asStorable(collection);
+
+  return sc;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiReadByteStreamV2Cnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiReadByteStreamV2Cnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..5e7ca43e80f02bcc644fbffc99f486467127675b
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiReadByteStreamV2Cnv.h
@@ -0,0 +1,79 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEPROIREADBYTESTREAMV2CNV_H
+#define TRIGT1CALOBYTESTREAM_JEPROIREADBYTESTREAMV2CNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IOpaqueAddress;
+class IROBDataProviderSvc;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class JepRoiByteStreamV2Tool;
+
+/** ByteStream converter for JEP component containers post LS1.
+ *
+ *  @author Peter Faulkner
+ */
+
+template <typename Container>
+class JepRoiReadByteStreamV2Cnv: public Converter {
+
+  friend class CnvFactory<JepRoiReadByteStreamV2Cnv<Container> >;
+
+protected:
+
+  JepRoiReadByteStreamV2Cnv(ISvcLocator* svcloc);
+
+public:
+
+  ~JepRoiReadByteStreamV2Cnv();
+
+  virtual StatusCode initialize();
+  /// Create Container from ByteStream
+  virtual StatusCode createObj(IOpaqueAddress* pAddr, DataObject*& pObj);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::JepRoiByteStreamV2Tool> m_tool;
+
+  /// Service for reading bytestream
+  ServiceHandle<IROBDataProviderSvc> m_robDataProvider;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#include "JepRoiReadByteStreamV2Cnv.icc"
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiReadByteStreamV2Cnv.icc b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiReadByteStreamV2Cnv.icc
new file mode 100755
index 0000000000000000000000000000000000000000..28db53b21cc56061409c448a7542c7f91e2f856c
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/JepRoiReadByteStreamV2Cnv.icc
@@ -0,0 +1,141 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+#include <stdint.h>
+#include <typeinfo>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "JepRoiByteStreamV2Tool.h"
+
+namespace LVL1BS {
+
+template <typename Container>
+JepRoiReadByteStreamV2Cnv<Container>::JepRoiReadByteStreamV2Cnv(
+      ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("JepRoiReadByteStreamV2Cnv"),
+      m_tool("LVL1BS::JepRoiByteStreamV2Tool/JepRoiByteStreamV2Tool"),
+      m_robDataProvider("ROBDataProviderSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+template <typename Container>
+JepRoiReadByteStreamV2Cnv<Container>::~JepRoiReadByteStreamV2Cnv()
+{
+}
+
+// CLID
+
+template <typename Container>
+const CLID& JepRoiReadByteStreamV2Cnv<Container>::classID()
+{
+  return ClassID_traits<Container>::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+template <typename Container>
+StatusCode JepRoiReadByteStreamV2Cnv<Container>::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << "<"
+                      << typeid(Container).name() << "> - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return sc;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  // Get ROBDataProvider
+  sc = m_robDataProvider.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_robDataProvider << endreq;
+    return sc ;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_robDataProvider << endreq;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// createObj should create the RDO from bytestream.
+
+template <typename Container>
+StatusCode JepRoiReadByteStreamV2Cnv<Container>::createObj( IOpaqueAddress* pAddr,
+                                                            DataObject*& pObj )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createObj() called" << endreq;
+
+  ByteStreamAddress *pBS_Addr;
+  pBS_Addr = dynamic_cast<ByteStreamAddress *>( pAddr );
+  if ( !pBS_Addr ) {
+    m_log << MSG::ERROR << " Cannot cast to ByteStreamAddress " << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = *( pBS_Addr->par() );
+
+  if (m_debug) m_log << MSG::DEBUG << " Creating Objects " << nm << endreq;
+
+  // get SourceIDs
+  const std::vector<uint32_t>& vID(m_tool->sourceIDs(nm));
+
+  // get ROB fragments
+  IROBDataProviderSvc::VROBFRAG robFrags;
+  m_robDataProvider->getROBData( vID, robFrags );
+
+  // size check
+  Container* const collection = new Container;
+  if (m_debug) {
+    m_log << MSG::DEBUG << " Number of ROB fragments is " << robFrags.size()
+          << endreq;
+  }
+  if (robFrags.size() == 0) {
+    pObj = SG::asStorable(collection) ;
+    return StatusCode::SUCCESS;
+  }
+
+  StatusCode sc = m_tool->convert(robFrags, collection);
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << " Failed to create Objects   " << nm << endreq;
+    delete collection;
+    return sc;
+  }
+
+  pObj = SG::asStorable(collection);
+
+  return sc;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloErrorByteStreamCnv.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloErrorByteStreamCnv.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..9b4b0b2c06f025535562066ce5066f9fe3fc5341
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloErrorByteStreamCnv.cxx
@@ -0,0 +1,104 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StlVectorClids.h"
+#include "SGTools/StorableConversions.h"
+
+#include "L1CaloErrorByteStreamTool.h"
+
+#include "L1CaloErrorByteStreamCnv.h"
+
+namespace LVL1BS {
+
+L1CaloErrorByteStreamCnv::L1CaloErrorByteStreamCnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("L1CaloErrorByteStreamCnv"),
+      m_tool("LVL1BS::L1CaloErrorByteStreamTool/L1CaloErrorByteStreamTool"),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+L1CaloErrorByteStreamCnv::~L1CaloErrorByteStreamCnv()
+{
+}
+
+// CLID
+
+const CLID& L1CaloErrorByteStreamCnv::classID()
+{
+  return ClassID_traits<std::vector<unsigned int> >::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode L1CaloErrorByteStreamCnv::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << " - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return sc;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  return StatusCode::SUCCESS;
+}
+
+// createObj should create the RDO from bytestream.
+
+StatusCode L1CaloErrorByteStreamCnv::createObj( IOpaqueAddress* pAddr,
+                                                DataObject*& pObj )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createObj() called" << endreq;
+
+  ByteStreamAddress *pBS_Addr;
+  pBS_Addr = dynamic_cast<ByteStreamAddress *>( pAddr );
+  if ( !pBS_Addr ) {
+    m_log << MSG::ERROR << " Can not cast to ByteStreamAddress " << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = *( pBS_Addr->par() );
+
+  if (m_debug) m_log << MSG::DEBUG << " Creating Objects " << nm << endreq;
+
+  std::vector<unsigned int>* const errCollection =
+                                             new std::vector<unsigned int>;
+
+  StatusCode sc = m_tool->errors(errCollection);
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << " Failed to create Objects   " << nm << endreq;
+    delete errCollection;
+    return sc;
+  }
+
+  pObj = SG::asStorable(errCollection);
+
+  return sc;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloErrorByteStreamCnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloErrorByteStreamCnv.h
new file mode 100644
index 0000000000000000000000000000000000000000..948634ad0c814dd72189250727aca443beb07560
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloErrorByteStreamCnv.h
@@ -0,0 +1,71 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_L1CALOERRORBYTESTREAMCNV_H
+#define TRIGT1CALOBYTESTREAM_L1CALOERRORBYTESTREAMCNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IOpaqueAddress;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class L1CaloErrorByteStreamTool;
+
+/** Returns vector of errors detected during data unpacking
+ *
+ *  @author Peter Faulkner
+ */
+
+class L1CaloErrorByteStreamCnv: public Converter {
+
+  friend class CnvFactory<L1CaloErrorByteStreamCnv>;
+
+protected:
+
+  L1CaloErrorByteStreamCnv(ISvcLocator* svcloc);
+
+public:
+
+  ~L1CaloErrorByteStreamCnv();
+
+  virtual StatusCode initialize();
+  /// Create error vector from ByteStream
+  virtual StatusCode createObj(IOpaqueAddress* pAddr, DataObject*& pObj);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::L1CaloErrorByteStreamTool> m_tool;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloErrorByteStreamTool.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloErrorByteStreamTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..9a2b7b4d8e819cd1c0e041b1e4a2ebd6f18269a0
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloErrorByteStreamTool.cxx
@@ -0,0 +1,110 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <utility>
+
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "L1CaloErrorByteStreamTool.h"
+
+namespace LVL1BS {
+
+// Interface ID
+
+static const InterfaceID IID_IL1CaloErrorByteStreamTool(
+                                           "L1CaloErrorByteStreamTool", 1, 1);
+
+const InterfaceID& L1CaloErrorByteStreamTool::interfaceID()
+{
+  return IID_IL1CaloErrorByteStreamTool;
+}
+
+// Constructor
+
+L1CaloErrorByteStreamTool::L1CaloErrorByteStreamTool(const std::string& type,
+                                                     const std::string& name,
+	    			                     const IInterface*  parent)
+                          : AthAlgTool(type, name, parent)
+{
+  declareInterface<L1CaloErrorByteStreamTool>(this);
+}
+
+// Destructor
+
+L1CaloErrorByteStreamTool::~L1CaloErrorByteStreamTool()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode L1CaloErrorByteStreamTool::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << PACKAGE_VERSION << endreq;
+
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode L1CaloErrorByteStreamTool::finalize()
+{
+  return StatusCode::SUCCESS;
+}
+
+// Set ROB status error
+
+void L1CaloErrorByteStreamTool::robError(const uint32_t robid,
+                                         const unsigned int err)
+{
+  if (err && robMap.find(robid) == robMap.end()) {
+    robMap.insert(std::make_pair(robid, err));
+  }
+  return;
+}
+
+// Set ROD unpacking error
+
+void L1CaloErrorByteStreamTool::rodError(const uint32_t robid,
+                                         const unsigned int err)
+{
+  if (err && rodMap.find(robid) == rodMap.end()) {
+    rodMap.insert(std::make_pair(robid, err));
+  }
+  return;
+}
+
+// Fill vector with accumulated errors and reset
+
+StatusCode L1CaloErrorByteStreamTool::errors(std::vector<unsigned int>*
+                                                                 const errColl)
+{
+  if (!robMap.empty() || !rodMap.empty()) {
+    errColl->push_back(robMap.size());
+    ErrorMap::const_iterator iter  = robMap.begin();
+    ErrorMap::const_iterator iterE = robMap.end();
+    for (; iter != iterE; ++iter) {
+      errColl->push_back(iter->first);
+      errColl->push_back(iter->second);
+    }
+    robMap.clear();
+    iter  = rodMap.begin();
+    iterE = rodMap.end();
+    for (; iter != iterE; ++iter) {
+      errColl->push_back(iter->first);
+      errColl->push_back(iter->second);
+    }
+    rodMap.clear();
+  }
+  return StatusCode::SUCCESS;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloErrorByteStreamTool.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloErrorByteStreamTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..9a86c98b57a458768e4a9210a92f5d1445752416
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloErrorByteStreamTool.h
@@ -0,0 +1,58 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_L1CALOERRORBYTESTREAMTOOL_H
+#define TRIGT1CALOBYTESTREAM_L1CALOERRORBYTESTREAMTOOL_H
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "AthenaBaseComps/AthAlgTool.h"
+
+class IInterface;
+class InterfaceID;
+class StatusCode;
+
+namespace LVL1BS {
+
+/** Tool to accumulate ROB/ROD unpacking errors.
+ *
+ *  @author Peter Faulkner
+ */
+
+class L1CaloErrorByteStreamTool : public AthAlgTool {
+
+ public:
+   L1CaloErrorByteStreamTool(const std::string& type, const std::string& name,
+                             const IInterface* parent);
+   virtual ~L1CaloErrorByteStreamTool();
+
+   /// AlgTool InterfaceID
+   static const InterfaceID& interfaceID();
+
+   virtual StatusCode initialize();
+   virtual StatusCode finalize();
+
+   /// Set ROB status error
+   void robError(uint32_t robid, unsigned int err);
+   /// Set ROD unpacking error
+   void rodError(uint32_t robid, unsigned int err);
+   /// Fill vector with accumulated errors and reset
+   StatusCode errors(std::vector<unsigned int>* errColl);
+
+ private:
+
+   // Maps of accumulated errors
+   typedef std::map<uint32_t, unsigned int> ErrorMap;
+   ErrorMap robMap;
+   ErrorMap rodMap;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloSrcIdMap.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloSrcIdMap.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..a74dd44723e7fbd9dd5dc8ff0c6c31f1477043ff
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloSrcIdMap.cxx
@@ -0,0 +1,89 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "L1CaloSrcIdMap.h"
+
+namespace LVL1BS {
+
+L1CaloSrcIdMap::L1CaloSrcIdMap()
+{
+}
+
+// Make a ROD Source ID
+
+uint32_t L1CaloSrcIdMap::getRodID(int crate, int slink, int daqOrRoi,
+                                             eformat::SubDetector subdet)
+{
+  // module ID = r0sscccc (ROD-spec-version1_06d, P33)
+  uint16_t moduleId = (daqOrRoi << 7) | (slink << 4) | crate;
+  eformat::helper::SourceIdentifier helpID(subdet, moduleId);
+  return helpID.code();
+}
+
+// Make a ROB Source ID from a ROD source ID
+
+uint32_t L1CaloSrcIdMap::getRobID(uint32_t rod_id)
+{
+  return rod_id;
+}
+
+// Make a ROS Source ID from a ROB source ID
+
+uint32_t L1CaloSrcIdMap::getRosID(uint32_t rob_id)
+{
+  eformat::helper::SourceIdentifier id(rob_id);
+  eformat::helper::SourceIdentifier id2(id.subdetector_id(), 0);
+  return id2.code();
+}
+
+// Make a SubDetector ID from ROS source ID
+
+uint32_t L1CaloSrcIdMap::getDetID(uint32_t ros_id)
+{
+  eformat::helper::SourceIdentifier id(ros_id);
+  eformat::helper::SourceIdentifier id2(id.subdetector_id(), 0);
+  return id2.code();
+}
+
+// Return crate from unpacked moduleID
+
+int L1CaloSrcIdMap::crate(uint32_t code)
+{
+  eformat::helper::SourceIdentifier id(code);
+  return id.module_id() & 0xf;
+}
+
+// Return daqOrRoi from unpacked moduleID
+
+int L1CaloSrcIdMap::daqOrRoi(uint32_t code)
+{
+  eformat::helper::SourceIdentifier id(code);
+  return (id.module_id() >> 7) & 0x1;
+}
+
+// Return slink from unpacked moduleID
+
+int L1CaloSrcIdMap::slink(uint32_t code)
+{
+  eformat::helper::SourceIdentifier id(code);
+  return (id.module_id() >> 4) & 0x3;
+}
+
+// Return the maximum possible number of slinks given number of
+// bits in module ID
+
+int L1CaloSrcIdMap::maxSlinks()
+{
+  return 4;
+}
+
+// Return sub-detector for given ID
+
+eformat::SubDetector L1CaloSrcIdMap::subDet(uint32_t code) {
+  eformat::helper::SourceIdentifier id(code);
+  return id.subdetector_id();
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloSrcIdMap.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloSrcIdMap.h
new file mode 100755
index 0000000000000000000000000000000000000000..329f47eabcd791298a6f7dc89aff3826ff2ee7d1
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloSrcIdMap.h
@@ -0,0 +1,65 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_L1CALOSRCIDMAP_H
+#define TRIGT1CALOBYTESTREAM_L1CALOSRCIDMAP_H
+
+#include <stdint.h>
+
+#include "eformat/SourceIdentifier.h"
+
+namespace LVL1BS {
+
+/** This class provides conversion between Lower level Source ID to
+ *  higher level source ID for L1Calo ByteStream fragments.
+ *
+ *  This is to be used in assembling the fragments from ROD fragments
+ *
+ *  @author Peter Faulkner
+ */
+
+class L1CaloSrcIdMap {
+
+public:
+  L1CaloSrcIdMap();
+
+  /// Make a ROD Source ID
+  uint32_t getRodID (int crate, int slink, int daqOrRoi,
+                                eformat::SubDetector subdet);
+
+  /// Make a ROB Source ID from a ROD source ID
+  uint32_t getRobID (uint32_t rod_id);
+
+  /// Make a ROS Source ID from a ROB source ID
+  uint32_t getRosID (uint32_t rob_id);
+
+  /// Make a SubDetector ID from ROS source ID
+  uint32_t getDetID (uint32_t ros_id);
+
+  /// Return crate from unpacked moduleID
+  int      crate(uint32_t code);
+
+  /// Return daqOrRoi from unpacked moduleID
+  int      daqOrRoi(uint32_t code);
+
+  /// Return slink from unpacked moduleID
+  int      slink(uint32_t code);
+
+  /// Return the maximum possible number of slinks
+  int      maxSlinks();
+
+  /// Return sub-detector for given ID
+  eformat::SubDetector subDet(uint32_t code);
+
+  /// Return ROD header minor version to use when writing BS
+  uint16_t minorVersion() {return 0x1004;}                     // Or may go up to 0x2000, CHECK
+
+  /// Return last ROD header minor version for pre-LS1 data
+  uint16_t minorVersionPreLS1() {return 0x1003;}
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloSubBlock.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloSubBlock.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..d6d530067a4fff5ad32e74ccff45f74334f1c222
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloSubBlock.cxx
@@ -0,0 +1,478 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "L1CaloSubBlock.h"
+
+namespace LVL1BS {
+
+// Static constant definitions
+
+const int      L1CaloSubBlock::s_headerBit;
+const int      L1CaloSubBlock::s_statusBit;
+const uint32_t L1CaloSubBlock::s_headerMask;
+const uint32_t L1CaloSubBlock::s_statusMask;
+const uint32_t L1CaloSubBlock::s_headerVal;
+const uint32_t L1CaloSubBlock::s_statusVal;
+
+const int      L1CaloSubBlock::s_ppmCrates;
+
+const int      L1CaloSubBlock::s_wordIdBit;
+const int      L1CaloSubBlock::s_versionBit;
+const int      L1CaloSubBlock::s_formatBit;
+const int      L1CaloSubBlock::s_seqnoBit;
+const int      L1CaloSubBlock::s_crateBit;
+const int      L1CaloSubBlock::s_moduleBit;
+const int      L1CaloSubBlock::s_slices2Bit;
+const int      L1CaloSubBlock::s_slices1Bit;
+const uint32_t L1CaloSubBlock::s_wordIdMask;
+const uint32_t L1CaloSubBlock::s_versionMask;
+const uint32_t L1CaloSubBlock::s_formatMask;
+const uint32_t L1CaloSubBlock::s_seqnoMask;
+const uint32_t L1CaloSubBlock::s_crateMask;
+const uint32_t L1CaloSubBlock::s_moduleMask;
+const uint32_t L1CaloSubBlock::s_slices2Mask;
+const uint32_t L1CaloSubBlock::s_slices1Mask;
+
+const int      L1CaloSubBlock::s_failingBcnBit;
+const int      L1CaloSubBlock::s_glinkTimeoutBit;
+const int      L1CaloSubBlock::s_glinkDownBit;
+const int      L1CaloSubBlock::s_upstreamErrorBit;
+const int      L1CaloSubBlock::s_daqOverflowBit;
+const int      L1CaloSubBlock::s_bcnMismatchBit;
+const int      L1CaloSubBlock::s_glinkProtocolBit;
+const int      L1CaloSubBlock::s_glinkParityBit;
+const uint32_t L1CaloSubBlock::s_failingBcnMask;
+
+const int      L1CaloSubBlock::s_maxWordBits;
+const int      L1CaloSubBlock::s_maxStreamedBits;
+const uint32_t L1CaloSubBlock::s_maxWordMask;
+const uint32_t L1CaloSubBlock::s_maxStreamedMask;
+
+const int      L1CaloSubBlock::s_maxPins;
+const uint32_t L1CaloSubBlock::s_glinkDavSet;
+
+L1CaloSubBlock::L1CaloSubBlock() : m_header(0), m_trailer(0),
+                                   m_bunchCrossing(0),
+				   m_unpackError(UNPACK_NONE),
+                                   m_bitword(0), m_currentBit(0),
+				   m_maxBits(s_maxWordBits),
+				   m_maxMask(s_maxWordMask),
+				   m_unpackerFlag(false),
+				   m_currentPinBit(s_maxPins),
+				   m_oddParity(s_maxPins, 1),
+				   m_dataWords(0)
+{
+  // Initialize unpacking masks
+  m_unpackingMasks.assign(s_maxWordBits+1, 0);
+  for (int i = 1; i <= s_maxWordBits; ++i) {
+    m_unpackingMasks[i] = (m_unpackingMasks[i-1]<<1)|0x1;
+  }
+}
+
+L1CaloSubBlock::~L1CaloSubBlock()
+{
+}
+
+// Clear all data
+
+void L1CaloSubBlock::clear()
+{
+  m_header = 0;
+  m_trailer = 0;
+  m_bunchCrossing = 0;
+  m_unpackError = UNPACK_NONE;
+  m_bitword = 0;
+  m_currentBit = 0;
+  m_unpackerFlag = false;
+  m_currentPinBit.assign(s_maxPins, 0);
+  m_oddParity.assign(s_maxPins, 1);
+  m_dataWords = 0;
+  m_data.clear();
+}
+
+// Store header data
+
+void L1CaloSubBlock::setHeader(const int wordId, const int version,
+                               const int format, const int seqno,
+                               const int crate, const int module,
+			       const int slices2, const int slices1)
+{
+  uint32_t word = 0;
+  word |= (wordId  & s_wordIdMask)  << s_wordIdBit;
+  word |= (version & s_versionMask) << s_versionBit;
+  word |= (format  & s_formatMask)  << s_formatBit;
+  word |= (seqno   & s_seqnoMask)   << s_seqnoBit;
+  word |= (crate   & s_crateMask)   << s_crateBit;
+  word |= (module  & s_moduleMask)  << s_moduleBit;
+  word |= (slices2 & s_slices2Mask) << s_slices2Bit;
+  word |= (slices1 & s_slices1Mask) << s_slices1Bit;
+  m_header = word;
+}
+
+// Input complete packed sub-block from ROD vector
+
+OFFLINE_FRAGMENTS_NAMESPACE::PointerType L1CaloSubBlock::read(
+                        const OFFLINE_FRAGMENTS_NAMESPACE::PointerType beg,
+			const OFFLINE_FRAGMENTS_NAMESPACE::PointerType end)
+{
+  m_dataWords = 0;
+  m_unpackerFlag = true;
+  OFFLINE_FRAGMENTS_NAMESPACE::PointerType pos(beg);
+  OFFLINE_FRAGMENTS_NAMESPACE::PointerType pose(end);
+  for (; pos != pose; ++pos) {
+    const uint32_t word = *pos;
+    const SubBlockWordType type = wordType(word);
+    if (type == HEADER) {
+      if (m_header) return pos;
+      m_header = word;
+    } else if (type == STATUS) {
+      // Word ID should be consistent with header
+      if (m_trailer || (wordId(word) != wordId()+1)) return pos;
+      m_trailer = word;
+    } else {
+      // Check data word IDs
+      const int id = wordId(word);
+      bool badId = false;
+      // All neutral format '0000'
+      if (format() == NEUTRAL)        badId = (id != 0);
+      // Other PPM '0xxx'
+      else if (crate() < s_ppmCrates) badId = ((id & 0x8) != 0);
+      // Other CPM/JEM '01xx' or '10xx'
+      else if (wordId() == 0xc)       badId = (((id & 0xc) != 0x4) &&
+                                               ((id & 0xc) != 0x8));
+      // Other CMM/CMX '00xx'
+      else                            badId = ((id & 0xc) != 0);
+      if (m_trailer || badId) return pos;
+      m_data.push_back(word);
+      ++m_dataWords;
+    }
+  }
+  return pose;
+}
+
+// Output complete packed sub-block to ROD vector
+
+void L1CaloSubBlock::write(
+              FullEventAssembler<L1CaloSrcIdMap>::RODDATA* const theROD) const
+{
+  theROD->push_back(m_header);
+  std::vector<uint32_t>::const_iterator pos;
+  for (pos = m_data.begin(); pos != m_data.end(); ++pos) {
+    theROD->push_back(*pos);
+  }
+  if (m_trailer) theROD->push_back(m_trailer);
+}
+
+// Store error status trailer
+
+void L1CaloSubBlock::setStatus(const uint32_t failingBCN,
+     const bool glinkTimeout, const bool glinkDown, const bool upstreamError,
+     const bool daqOverflow, const bool bcnMismatch,
+     const bool glinkProtocol, const bool glinkParity)
+{
+  uint32_t word = 0;
+  word |= (failingBCN & s_failingBcnMask) << s_failingBcnBit;
+  word |= glinkTimeout  << s_glinkTimeoutBit;
+  word |= glinkDown     << s_glinkDownBit;
+  word |= upstreamError << s_upstreamErrorBit;
+  word |= daqOverflow   << s_daqOverflowBit;
+  word |= bcnMismatch   << s_bcnMismatchBit;
+  word |= glinkProtocol << s_glinkProtocolBit;
+  word |= glinkParity   << s_glinkParityBit;
+  if (word) {
+    word |= (wordId()    & s_wordIdMask) << s_wordIdBit;
+    word |= (s_statusVal & s_statusMask) << s_statusBit;
+    word |= (seqno()     & s_seqnoMask)  << s_seqnoBit;
+    word |= (crate()     & s_crateMask)  << s_crateBit;
+    word |= (module()    & s_moduleMask) << s_moduleBit;
+  }
+  m_trailer = word;
+}
+
+// Set DAQ FIFO Overflow bit in Sub-status word
+
+void L1CaloSubBlock::setDaqOverflow(const int bit)
+{
+  if (bit) {
+    if (m_trailer) m_trailer |= (1 << s_daqOverflowBit);
+    else setStatus(0, false, false, false, true, false, false, false);
+  }
+}
+
+// Set G-Link Parity bit in Sub-status word
+
+void L1CaloSubBlock::setGlinkParity(const int bit)
+{
+  if (bit) {
+    if (m_trailer) m_trailer |= (1 << s_glinkParityBit);
+    else setStatus(0, false, false, false, false, false, false, true);
+  }
+}
+
+// Return the unpacking error message for printing
+
+std::string L1CaloSubBlock::unpackErrorMsg() const
+{
+  std::string msg;
+  switch (m_unpackError) {
+    case UNPACK_NONE:
+      msg = "No error";
+      break;
+    case UNPACK_VERSION:
+      msg = "Unsupported Data Version";
+      break;
+    case UNPACK_FORMAT:
+      msg = "Unsupported Data Format";
+      break;
+    case UNPACK_COMPRESSION_VERSION:
+      msg = "Unsupported Compression Version";
+      break;
+    case UNPACK_COMPRESSION_SLICES:
+      msg = "Unsupported Number of Slices for Compression Version";
+      break;
+    case UNPACK_DATA_TRUNCATED:
+      msg = "Premature End of Sub-block Data";
+      break;
+    case UNPACK_EXCESS_DATA:
+      msg = "Excess Data in Sub-block";
+      break;
+    case UNPACK_SOURCE_ID:
+      msg = "Invalid Source ID in Sub-block Data";
+      break;
+    case UNPACK_EXCESS_TOBS:
+      msg = "Excess TOBs in Sub-block Data";
+      break;
+    case UNPACK_DATA_ID:
+      msg = "Invalid word ID in Sub-block Data";
+      break;
+    default:
+      msg = "Unknown Error Code";
+      break;
+  }
+  return msg;
+}
+
+// Packing utilities
+
+// Return the minimum number of bits needed for given data
+
+int L1CaloSubBlock::minBits(const uint32_t datum) const
+{
+  const int maxBits = 32;
+  int nbits = maxBits;
+  for (int i = 0; i < maxBits; ++i) {
+    if ( !(datum >> i)) {
+      nbits = i;
+      break;
+    }
+  }
+  return nbits;
+}
+
+// Return the parity bit for given data
+
+int L1CaloSubBlock::parityBit(const int init, const uint32_t datum,
+                                                    const int nbits) const
+{
+  // set init to 0/1 for even/odd parity
+  int parity = init;
+  for (int bit = 0; bit < nbits; ++bit) parity ^= (datum >> bit) & 0x1;
+  return parity;
+}
+
+// Pack given data into given number of bits
+
+void L1CaloSubBlock::packer(const uint32_t datum, const int nbits)
+{
+  if (nbits > 0) {
+    uint32_t mask = m_unpackingMasks[nbits];
+    m_bitword |= (datum & mask) << m_currentBit;
+    m_currentBit += nbits;
+    if (m_currentBit >= m_maxBits) {
+      m_bitword &= m_maxMask;
+      m_data.push_back(m_bitword);
+      ++m_dataWords;
+      const int bitsLeft = m_currentBit - m_maxBits;
+      if (bitsLeft > 0) {
+        m_bitword = (datum & mask) >> (nbits - bitsLeft);
+	m_currentBit = bitsLeft;
+      } else {
+        m_bitword = 0;
+	m_currentBit = 0;
+      }
+    }
+  }
+}
+
+// Flush the current data word padded with zeros
+
+void L1CaloSubBlock::packerFlush()
+{
+  if (m_currentBit > 0) {
+    m_bitword &= m_maxMask;
+    m_data.push_back(m_bitword);
+    ++m_dataWords;
+    m_bitword = 0;
+    m_currentBit = 0;
+  }
+}
+
+// Unpack given number of bits of data
+
+uint32_t L1CaloSubBlock::unpacker(const int nbits)
+{
+  uint32_t word = 0;
+  if (nbits > 0) {
+    if (m_dataPos == m_dataPosEnd) {
+      m_unpackerFlag = false;
+      return 0;
+    }
+    int nbitsDone = nbits;
+    if (nbitsDone > m_maxBits - m_currentBit) {
+      nbitsDone = m_maxBits - m_currentBit;
+    }
+    word = (m_bitword >> m_currentBit) & m_unpackingMasks[nbitsDone];
+    m_currentBit += nbits;
+    if (m_currentBit >= m_maxBits) {
+      m_bitword = 0;
+      if (m_dataPos != m_dataPosEnd) {
+        ++m_dataPos;
+        if (m_dataPos != m_dataPosEnd) {
+          m_bitword = *m_dataPos;
+        }
+      }
+      m_currentBit = 0;
+      const int bitsLeft = nbits - nbitsDone;
+      if (bitsLeft > 0) {
+        if (m_dataPos == m_dataPosEnd) {
+          m_unpackerFlag = false;
+          return word;
+        }
+	word |= (m_bitword & m_unpackingMasks[bitsLeft]) << nbitsDone;
+	m_currentBit = bitsLeft;
+      }
+    }
+  }
+  return word;
+}
+
+// Initialise unpacker
+
+void L1CaloSubBlock::unpackerInit()
+{
+  m_bitword = 0;
+  m_currentBit = 0;
+  m_unpackerFlag = true;
+  m_dataPos = m_data.begin();
+  m_dataPosEnd = m_data.end();
+  if (m_dataPos != m_dataPosEnd) m_bitword = *m_dataPos;
+}
+
+// Pack given neutral data from given pin
+
+void L1CaloSubBlock::packerNeutral(const int pin, const uint32_t datum,
+                                                  const int nbits)
+{
+  if (pin >= 0 && pin < s_maxPins && nbits > 0) {
+    if (m_currentPinBit[pin] + nbits > m_dataWords) {
+      m_dataWords = m_currentPinBit[pin] + nbits;
+      m_data.resize(m_dataWords, s_glinkDavSet);
+    }
+    for (int bit = 0; bit < nbits; ++bit) {
+      m_data[m_currentPinBit[pin] + bit] |= ((datum >> bit) & 0x1) << pin;
+    }
+    m_currentPinBit[pin] += nbits;
+    m_oddParity[pin] = parityBit(m_oddParity[pin], datum, nbits);
+  }
+}
+
+// Pack current G-Link parity bit for given pin
+
+void L1CaloSubBlock::packerNeutralParity(const int pin)
+{
+  if (pin >= 0 && pin < s_maxPins) {
+    packerNeutral(pin, m_oddParity[pin], 1);
+    m_oddParity[pin] = 1;
+  }
+}
+
+// Unpack given number of bits of neutral data for given pin
+
+uint32_t L1CaloSubBlock::unpackerNeutral(const int pin, const int nbits)
+{
+  uint32_t word = 0;
+  if (pin >= 0 && pin < s_maxPins && nbits > 0
+               && m_currentPinBit[pin] + nbits <= m_dataWords) {
+    for (int bit = 0; bit < nbits; ++bit) {
+      word |= ((m_data[m_currentPinBit[pin] + bit] >> pin) & 0x1) << bit;
+    }
+    m_currentPinBit[pin] += nbits;
+    m_oddParity[pin] = parityBit(m_oddParity[pin], word, nbits);
+  } else m_unpackerFlag = false;
+  return word;
+}
+
+// Unpack and test G-Link parity bit for given pin
+
+bool L1CaloSubBlock::unpackerNeutralParityError(const int pin)
+{
+  bool error = true;
+  if (pin >= 0 && pin < s_maxPins) {
+    int parity = m_oddParity[pin];
+    int bit    = unpackerNeutral(pin, 1);
+    m_oddParity[pin] = 1;
+    error = !(bit == parity);
+  }
+  return error;
+}
+
+// Static function to determine word type
+
+L1CaloSubBlock::SubBlockWordType L1CaloSubBlock::wordType(const uint32_t word)
+{
+  SubBlockWordType type = DATA;
+  if (((word >> s_headerBit) & s_headerMask) == s_headerVal) {
+    if (((word >> s_statusBit) & s_statusMask) == s_statusVal) type = STATUS;
+    else type = HEADER;
+  }
+  return type;
+}
+
+// Return wordID field from given header word
+
+int L1CaloSubBlock::wordId(const uint32_t word)
+{
+  return (word >> s_wordIdBit) & s_wordIdMask;
+}
+
+// Return version number from given header word
+
+int L1CaloSubBlock::version(const uint32_t word)
+{
+  return (word >> s_versionBit) & s_versionMask;
+}
+
+// Return data format from given header word
+
+int L1CaloSubBlock::format(const uint32_t word)
+{
+  return (word >> s_formatBit) & s_formatMask;
+}
+
+// Return seqno field from given header word
+
+int L1CaloSubBlock::seqno(const uint32_t word)
+{
+  return (word >> s_seqnoBit) & s_seqnoMask;
+}
+
+// Return module field from given header word
+
+int L1CaloSubBlock::module(const uint32_t word)
+{
+  return (word >> s_moduleBit) & s_moduleMask;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloSubBlock.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloSubBlock.h
new file mode 100755
index 0000000000000000000000000000000000000000..3a38e85f3b2175b2ae4fa834da38133172b21476
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloSubBlock.h
@@ -0,0 +1,365 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_L1CALOSUBBLOCK_H
+#define TRIGT1CALOBYTESTREAM_L1CALOSUBBLOCK_H
+
+#include <stdint.h>
+#include <vector>
+
+#include "ByteStreamCnvSvcBase/FullEventAssembler.h"
+
+#include "L1CaloSrcIdMap.h"
+
+namespace LVL1BS {
+
+/** L1Calo Sub-Block base class.
+ *
+ *  Provides common functionality for all L1Calo Sub-Block derived types.
+ *
+ *  @author Peter Faulkner
+ */
+
+class L1CaloSubBlock {
+
+ public:
+   enum SubBlockWordType { HEADER, DATA, STATUS };
+   enum DataFormats      { NEUTRAL = 0, UNCOMPRESSED = 1, COMPRESSED = 2,
+                           SUPERCOMPRESSED = 3 };
+   // Errors detected before unpacking
+   enum DataErrorType    { ERROR_NONE, ERROR_DUPLICATE_ROB, ERROR_ROD_ID,
+                           ERROR_ROD_NSTATUS, ERROR_USER_HEADER,
+                           ERROR_MISSING_HEADER, ERROR_MISSING_SUBBLOCK,
+			   ERROR_CRATE_NUMBER, ERROR_MODULE_NUMBER,
+			   ERROR_SLICES, ERROR_DUPLICATE_DATA,
+			   ERROR_ROI_TYPE, ERROR_MAX };
+   // Errors detected during unpacking
+   enum UnpackErrorType  { UNPACK_NONE = ERROR_NONE, UNPACK_VERSION = ERROR_MAX,
+                           UNPACK_FORMAT, UNPACK_COMPRESSION_VERSION,
+			   UNPACK_COMPRESSION_SLICES, UNPACK_DATA_TRUNCATED,
+			   UNPACK_EXCESS_DATA, UNPACK_SOURCE_ID,
+			   UNPACK_EXCESS_TOBS, UNPACK_DATA_ID };
+
+   L1CaloSubBlock();
+   ~L1CaloSubBlock();
+
+   /// Clear all data
+   void clear();
+
+   /// Return number of data words
+   int dataWords()   const;
+
+   /// Store header data
+   void setHeader(int wordId, int version, int format, int seqno, int crate,
+                  int module, int slices2, int slices1);
+   
+   //  Return unpacked header data
+   int wordId()      const;
+   int version()     const;
+   int format()      const;
+   int seqno()       const;
+   int slice()       const;
+   int crate()       const;
+   int module()      const;
+   int slices2()     const;
+   int slices1()     const;
+
+   //  Return unpacked error status data
+   uint32_t failingBCN()     const;
+   bool     glinkTimeout()   const;
+   bool     glinkDown()      const;
+   bool     upstreamError()  const;
+   bool     daqOverflow()    const;
+   bool     bcnMismatch()    const;
+   bool     glinkProtocol()  const;
+   bool     glinkParity()    const;
+
+   /// Return Sub-status word
+   uint32_t subStatus()      const;
+
+   /// Set the Bunch Crossing number (neutral format only)
+   void setBunchCrossing(int bc);
+   /// Return the Bunch Crossing number (neutral format only)
+   int  bunchCrossing()      const;
+
+   /// Input complete packed sub-block from ROD array
+   // (OFFLINE_FRAGMENTS_NAMESPACE::PointerType = uint32_t*)
+   OFFLINE_FRAGMENTS_NAMESPACE::PointerType read(
+                      const OFFLINE_FRAGMENTS_NAMESPACE::PointerType beg,
+	              const OFFLINE_FRAGMENTS_NAMESPACE::PointerType end);
+
+   /// Output complete packed sub-block to ROD vector
+   // (FullEventAssembler<L1CaloSrcIdMap>::RODDATA = vector<uint32_t>)
+   void write(FullEventAssembler<L1CaloSrcIdMap>::RODDATA* theROD) const;
+
+   /// Store error status trailer
+   void setStatus(uint32_t failingBCN, bool glinkTimeout, bool glinkDown,
+                  bool upstreamError, bool daqOverflow, bool bcnMismatch,
+		  bool glinkProtocol, bool glinkParity);
+
+   /// Set DAQ FIFO Overflow bit in Sub-status word
+   void setDaqOverflow(int bit = 1);
+   /// Set G-Link Parity bit in Sub-status word
+   void setGlinkParity(int bit = 1);
+ 
+   /// Word identification
+   static SubBlockWordType wordType(uint32_t word);
+   /// Return wordID field from given header word
+   static int wordId(uint32_t word);
+   /// Return version number from given header word
+   static int version(uint32_t word);
+   /// Return data format from given header word
+   static int format(uint32_t word);
+   /// Return seqno field from given header word
+   static int seqno(uint32_t word);
+   /// Return module field from given header word
+   static int module(uint32_t word);
+
+   //  Unpacking error code.  Set by derived classes
+   /// Set the unpacking error code
+   void     setUnpackErrorCode(int code);
+   /// Return the unpacking error code
+   int      unpackErrorCode() const;
+   /// Return the unpacking error message for printing
+   std::string unpackErrorMsg() const;
+
+   //  Packing utilities
+   /// Return the minimum number of bits needed for given data
+   int      minBits(uint32_t datum) const;
+   /// Return the parity bit for given data
+   int      parityBit(int init, uint32_t datum, int nbits) const;
+   /// Pack given data into given number of bits
+   void     packer(uint32_t datum, int nbits);
+   /// Flush the current data word padded with zeros
+   void     packerFlush();
+   /// Set continuous bit streaming for compressed formats
+   void     setStreamed();
+   /// Unpack given number of bits of data
+   uint32_t unpacker(int nbits);
+   /// Initialise unpacker
+   void     unpackerInit();
+   /// Return unpacker success flag
+   bool     unpackerSuccess() const;
+
+   //  Neutral format packing utilities
+   /// Pack given neutral data from given pin
+   void     packerNeutral(int pin, uint32_t datum, int nbits);
+   /// Pack current G-Link parity bit for given pin
+   void     packerNeutralParity(int pin);
+   /// Unpack given number of bits of neutral data for given pin
+   uint32_t unpackerNeutral(int pin, int nbits);
+   /// Unpack and test G-Link parity bit for given pin
+   bool     unpackerNeutralParityError(int pin);
+   /// Return current pin bit for given pin
+   int      currentPinBit(int pin) const;
+
+ private:
+   //  Constants.
+   //  Header and status ID
+   static const int      s_headerBit        = 30;
+   static const int      s_statusBit        = 28;
+   static const uint32_t s_headerMask       = 0x3;
+   static const uint32_t s_statusMask       = 0x1;
+   static const uint32_t s_headerVal        = 0x3;
+   static const uint32_t s_statusVal        = 0x1;
+   //  First non-PPM crate
+   static const int      s_ppmCrates        = 8;
+   //  Header word data positions and masks
+   static const int      s_wordIdBit        = 28;
+   static const int      s_versionBit       = 25;
+   static const int      s_formatBit        = 22;
+   static const int      s_seqnoBit         = 16;
+   static const int      s_crateBit         = 12;
+   static const int      s_moduleBit        = 8;
+   static const int      s_slices2Bit       = 3;
+   static const int      s_slices1Bit       = 0;
+   static const uint32_t s_wordIdMask       = 0xf;
+   static const uint32_t s_versionMask      = 0x7;
+   static const uint32_t s_formatMask       = 0x7;
+   static const uint32_t s_seqnoMask        = 0x3f;
+   static const uint32_t s_crateMask        = 0xf;
+   static const uint32_t s_moduleMask       = 0xf;
+   static const uint32_t s_slices2Mask      = 0x1f;
+   static const uint32_t s_slices1Mask      = 0x7;
+   //  Status word data positions and masks
+   static const int      s_failingBcnBit    = 22;
+   static const int      s_glinkTimeoutBit  = 7;
+   static const int      s_glinkDownBit     = 6;
+   static const int      s_upstreamErrorBit = 4;
+   static const int      s_daqOverflowBit   = 3;
+   static const int      s_bcnMismatchBit   = 2;
+   static const int      s_glinkProtocolBit = 1;
+   static const int      s_glinkParityBit   = 0;
+   static const uint32_t s_failingBcnMask   = 0x3f;
+   //  Packing word sizes and masks
+   static const int      s_maxWordBits      = 32;
+   static const int      s_maxStreamedBits  = 31;
+   static const uint32_t s_maxWordMask      = 0xffffffff;
+   static const uint32_t s_maxStreamedMask  = 0x7fffffff;
+   //  Neutral packing
+   static const int      s_maxPins          = 20;
+   static const uint32_t s_glinkDavSet      = 0x400000;
+
+   /// Sub-Block Header
+   uint32_t m_header;
+   /// Sub-Block Status Trailer
+   uint32_t m_trailer;
+   /// Bunch Crossing number (neutral format only)
+   int m_bunchCrossing;
+   /// Unpacking error code
+   int m_unpackError;
+   //  Used for bit-packing
+   uint32_t m_bitword;
+   int      m_currentBit;
+   int      m_maxBits;
+   uint32_t m_maxMask;
+   bool     m_unpackerFlag;
+   std::vector<uint32_t>::const_iterator m_dataPos;
+   std::vector<uint32_t>::const_iterator m_dataPosEnd;
+   //  Used for neutral bit packing
+   std::vector<int> m_currentPinBit;
+   std::vector<int> m_oddParity;
+   /// Current number of data words
+   int      m_dataWords;
+   /// Sub-Block data
+   std::vector<uint32_t> m_data;
+   /// Unpacking masks
+   std::vector<uint32_t> m_unpackingMasks;
+
+};
+
+inline int L1CaloSubBlock::dataWords() const
+{
+  return m_dataWords;
+}
+
+inline int L1CaloSubBlock::wordId() const
+{
+  return (m_header >> s_wordIdBit) & s_wordIdMask;
+}
+
+inline int L1CaloSubBlock::version() const
+{
+  return (m_header >> s_versionBit) & s_versionMask;
+}
+
+inline int L1CaloSubBlock::format() const
+{
+  return (m_header >> s_formatBit) & s_formatMask;
+}
+
+inline int L1CaloSubBlock::seqno() const
+{
+  return (m_header >> s_seqnoBit) & s_seqnoMask;
+}
+
+inline int L1CaloSubBlock::slice() const
+{
+  return seqno();
+}
+
+inline int L1CaloSubBlock::crate() const
+{
+  return (m_header >> s_crateBit) & s_crateMask;
+}
+
+inline int L1CaloSubBlock::module() const
+{
+  return (m_header >> s_moduleBit) & s_moduleMask;
+}
+
+inline int L1CaloSubBlock::slices2() const
+{
+  return (m_header >> s_slices2Bit) & s_slices2Mask;
+}
+
+inline int L1CaloSubBlock::slices1() const
+{
+  return (m_header >> s_slices1Bit) & s_slices1Mask;
+}
+
+inline uint32_t L1CaloSubBlock::failingBCN() const
+{
+  return (m_trailer >> s_failingBcnBit) & s_failingBcnMask;
+}
+
+inline bool L1CaloSubBlock::glinkTimeout() const
+{
+  return m_trailer & (0x1 << s_glinkTimeoutBit);
+}
+
+inline bool L1CaloSubBlock::glinkDown() const
+{
+  return m_trailer & (0x1 << s_glinkDownBit);
+}
+
+inline bool L1CaloSubBlock::upstreamError() const
+{
+  return m_trailer & (0x1 << s_upstreamErrorBit);
+}
+
+inline bool L1CaloSubBlock::daqOverflow() const
+{
+  return m_trailer & (0x1 << s_daqOverflowBit);
+}
+
+inline bool L1CaloSubBlock::bcnMismatch() const
+{
+  return m_trailer & (0x1 << s_bcnMismatchBit);
+}
+
+inline bool L1CaloSubBlock::glinkProtocol() const
+{
+  return m_trailer & (0x1 << s_glinkProtocolBit);
+}
+
+inline bool L1CaloSubBlock::glinkParity() const
+{
+  return m_trailer & (0x1 << s_glinkParityBit);
+}
+
+inline uint32_t L1CaloSubBlock::subStatus() const
+{
+  return m_trailer;
+}
+
+inline void L1CaloSubBlock::setBunchCrossing(const int bc)
+{
+  if (bc) m_bunchCrossing = bc;
+}
+
+inline int L1CaloSubBlock::bunchCrossing() const
+{
+  return m_bunchCrossing;
+}
+
+inline void L1CaloSubBlock::setUnpackErrorCode(const int code)
+{
+  m_unpackError = code;
+}
+
+inline int L1CaloSubBlock::unpackErrorCode() const
+{
+  return m_unpackError;
+}
+
+inline void L1CaloSubBlock::setStreamed()
+{
+  m_maxBits = s_maxStreamedBits;
+  m_maxMask = s_maxStreamedMask;
+}
+
+inline bool L1CaloSubBlock::unpackerSuccess() const
+{
+  return m_unpackerFlag;
+}
+
+inline int L1CaloSubBlock::currentPinBit(int pin) const
+{
+  return m_currentPinBit[pin];
+}
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloUserHeader.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloUserHeader.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..47b65ad6bdf19f8b4a1f48090133669bb5c8d7f2
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloUserHeader.cxx
@@ -0,0 +1,41 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "L1CaloUserHeader.h"
+
+namespace LVL1BS {
+
+// Static constant definitions
+
+const int      L1CaloUserHeader::s_jepCmmBit;
+//const int      L1CaloUserHeader::s_cpCmmBit;
+const int      L1CaloUserHeader::s_jemBit;
+const int      L1CaloUserHeader::s_cpmBit;
+const int      L1CaloUserHeader::s_ppmLutBit;
+const int      L1CaloUserHeader::s_ppmFadcBit;
+const int      L1CaloUserHeader::s_lowerBoundBit;
+const int      L1CaloUserHeader::s_ppmLutBitV2;
+const int      L1CaloUserHeader::s_ppmFadcBitV2;
+const uint32_t L1CaloUserHeader::s_mask;
+const uint32_t L1CaloUserHeader::s_lowerBoundMask;
+const uint32_t L1CaloUserHeader::s_ppmLutMaskV2;
+const uint32_t L1CaloUserHeader::s_ppmFadcMaskV2;
+const int      L1CaloUserHeader::s_version1;
+
+// Constructor
+
+L1CaloUserHeader::L1CaloUserHeader(uint32_t header) : m_header(header),
+                                                      m_version2(true)
+{
+}
+
+// Test for valid header word
+
+bool L1CaloUserHeader::isValid(const uint32_t word)
+{
+  return ((word >> s_wordIdBit) & s_mask) == s_mask;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloUserHeader.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloUserHeader.h
new file mode 100755
index 0000000000000000000000000000000000000000..4b50d5d4b81f8f998486068ce4d00ff296ca252f
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/L1CaloUserHeader.h
@@ -0,0 +1,183 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_L1CALOUSERHEADER_H
+#define TRIGT1CALOBYTESTREAM_L1CALOUSERHEADER_H
+
+#include <stdint.h>
+
+namespace LVL1BS {
+
+/** L1Calo User Header class.
+ *
+ *  The User Header is the first word of the ROD data and contains
+ *  Triggered slice offsets for all the sub-detector types.
+ *
+ *  @author Peter Faulkner
+ */
+                                                    // Remove pre version 2 stuff ?
+class L1CaloUserHeader {
+
+ public:
+
+   /// Constructor - default just sets word ID and number of header words
+   L1CaloUserHeader(uint32_t header = 0xf0000001);
+
+   /// Return packed header
+   uint32_t header() const;
+
+   /// Return number of header words (should be one)
+   int words()   const;
+
+   //  Return triggered slice offsets
+   int jepCmm()  const;
+   int cpCmm()   const;
+   int jem()     const;
+   int cpm()     const;
+   int ppmLut()  const;
+   int ppmFadc() const;
+
+   /// Return FADC lower bound
+   int lowerBound() const;
+
+   //  Set triggered slice offsets
+   void setJepCmm(int offset);
+   void setCpCmm(int offset);
+   void setJem(int offset);
+   void setCpm(int offset);
+   void setPpmLut(int offset);
+   void setPpmFadc(int offset);
+
+   /// Set FADC lower bound
+   void setLowerBound(int bound);
+
+   /// Set version flag
+   void setVersion(int minorVersion);
+
+   /// Test for valid header word
+   static bool isValid(uint32_t word);
+
+ private:
+   //  Packed word bit positions version 1
+   static const int s_wordIdBit  = 28;
+   static const int s_jepCmmBit  = 24;
+   static const int s_cpCmmBit   = 20;
+   static const int s_jemBit     = 16;
+   static const int s_cpmBit     = 12;
+   static const int s_ppmLutBit  = 8;
+   static const int s_ppmFadcBit = 4;
+   //  Packed word bit positions version 2 (no cmms)
+   static const int s_lowerBoundBit = 20;
+   static const int s_ppmLutBitV2   = 9;
+   static const int s_ppmFadcBitV2  = 4;
+   /// Field mask
+   static const uint32_t s_mask  = 0xf;
+   //  Version 2 masks
+   static const uint32_t s_lowerBoundMask = 0xff;
+   static const uint32_t s_ppmLutMaskV2   = 0x7;
+   static const uint32_t s_ppmFadcMaskV2  = 0x1f;
+   /// Version 1 minor format version number
+   static const int s_version1 = 0x1001;
+   /// Packed Header
+   uint32_t m_header;
+   /// Version flag
+   bool     m_version2;
+
+};
+
+inline uint32_t L1CaloUserHeader::header() const
+{
+  return m_header;
+}
+
+inline int L1CaloUserHeader::words() const
+{
+  return m_header & s_mask;
+}
+
+inline int L1CaloUserHeader::jepCmm() const
+{
+  return (m_version2) ? (m_header >> s_jemBit   ) & s_mask
+                      : (m_header >> s_jepCmmBit) & s_mask;
+}
+
+inline int L1CaloUserHeader::cpCmm() const
+{
+  return (m_version2) ? (m_header >> s_cpmBit  ) & s_mask
+                      : (m_header >> s_cpCmmBit) & s_mask;
+}
+
+inline int L1CaloUserHeader::jem() const
+{
+  return (m_header >> s_jemBit) & s_mask;
+}
+
+inline int L1CaloUserHeader::cpm() const
+{
+  return (m_header >> s_cpmBit) & s_mask;
+}
+
+inline int L1CaloUserHeader::ppmLut() const
+{
+  return (m_version2) ? (m_header >> s_ppmLutBitV2) & s_ppmLutMaskV2
+                      : (m_header >> s_ppmLutBit  ) & s_mask;
+}
+
+inline int L1CaloUserHeader::ppmFadc() const
+{
+  return (m_version2) ? (m_header >> s_ppmFadcBitV2) & s_ppmFadcMaskV2
+                      : (m_header >> s_ppmFadcBit  ) & s_mask;
+}
+
+inline int L1CaloUserHeader::lowerBound() const
+{
+  return (m_version2) ? (m_header >> s_lowerBoundBit) & s_lowerBoundMask
+                      : 0;
+}
+
+inline void L1CaloUserHeader::setJepCmm(const int offset)
+{
+  if (!m_version2) m_header |= (s_mask & offset) << s_jepCmmBit;
+}
+
+inline void L1CaloUserHeader::setCpCmm(const int offset)
+{
+  if (!m_version2) m_header |= (s_mask & offset) << s_cpCmmBit;
+}
+
+inline void L1CaloUserHeader::setJem(const int offset)
+{
+  m_header |= (s_mask & offset) << s_jemBit;
+}
+
+inline void L1CaloUserHeader::setCpm(const int offset)
+{
+  m_header |= (s_mask & offset) << s_cpmBit;
+}
+
+inline void L1CaloUserHeader::setPpmLut(const int offset)
+{
+  m_header |= (m_version2) ? (s_ppmLutMaskV2 & offset) << s_ppmLutBitV2
+                           : (s_mask         & offset) << s_ppmLutBit;
+}
+
+inline void L1CaloUserHeader::setPpmFadc(const int offset)
+{
+  m_header |= (m_version2) ? (s_ppmFadcMaskV2 & offset) << s_ppmFadcBitV2
+                           : (s_mask          & offset) << s_ppmFadcBit;
+}
+
+inline void L1CaloUserHeader::setLowerBound(const int bound)
+{
+  if (m_version2) m_header |= (s_lowerBoundMask & bound) << s_lowerBoundBit;
+}
+
+inline void L1CaloUserHeader::setVersion(const int minorVersion)
+{
+  m_version2 = (minorVersion > s_version1);
+}
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/ModifySlices.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/ModifySlices.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..c61d284b51a28a087008fdcb3373de3300b2a638
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/ModifySlices.cxx
@@ -0,0 +1,51 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "ModifySlices.h"
+
+namespace LVL1BS {
+
+// Return new triggered slice offset
+
+int ModifySlices::peak(const int oldPeak, const int oldSlices,
+                                          const int newSlices)
+{
+  return oldPeak + (newSlices - oldSlices)/2;
+}
+
+// Return modified data vector<int>
+
+void ModifySlices::data(const std::vector<int>& oldVec,
+                              std::vector<int>& newVec, const int newSlices)
+{
+  const int oldSlices = oldVec.size();
+  const int offset = (newSlices - oldSlices)/2;
+  newVec.resize(newSlices);
+  for (int sl = 0; sl < newSlices; ++sl) {
+    const int oldSl = sl - offset;
+    if      (oldSl < 0)          newVec[sl] = oldVec[0];
+    else if (oldSl >= oldSlices) newVec[sl] = oldVec[oldSlices - 1];
+    else                         newVec[sl] = oldVec[oldSl];
+  }
+}
+
+// Return modified data vector<unsigned int>
+
+void ModifySlices::data(const std::vector<unsigned int>& oldVec,
+                              std::vector<unsigned int>& newVec,
+			      const int newSlices)
+{
+  const int oldSlices = oldVec.size();
+  const int offset = (newSlices - oldSlices)/2;
+  newVec.resize(newSlices);
+  for (int sl = 0; sl < newSlices; ++sl) {
+    const int oldSl = sl - offset;
+    if      (oldSl < 0)          newVec[sl] = oldVec[0];
+    else if (oldSl >= oldSlices) newVec[sl] = oldVec[oldSlices - 1];
+    else                         newVec[sl] = oldVec[oldSl];
+  }
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/ModifySlices.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/ModifySlices.h
new file mode 100755
index 0000000000000000000000000000000000000000..ef95f05a2f01d4ccab9155adb18e68def63b1177
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/ModifySlices.h
@@ -0,0 +1,36 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_MODIFYSLICES_H
+#define TRIGT1CALOBYTESTREAM_MODIFYSLICES_H
+
+#include <vector>
+
+namespace LVL1BS {
+
+/** Utility to modify the number of slices.
+ *
+ *  @author Peter Faulkner
+ */
+
+class ModifySlices {
+
+ public:
+   ModifySlices();
+   ~ModifySlices();
+
+   /// Return new triggered slice offset
+   static int peak(int oldPeak, int oldSlices, int newSlices);
+   /// Return modified data vector<int>
+   static void data(const std::vector<int>& oldVec,
+                          std::vector<int>& newVec, int newSlices);
+   /// Return modified data vector<unsigned int>
+   static void data(const std::vector<unsigned int>& oldVec,
+                          std::vector<unsigned int>& newVec, int newSlices);
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmByteStreamCnv.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmByteStreamCnv.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..3d69374363ec9a8ed4ab45a5f5b405314e7d0eb4
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmByteStreamCnv.cxx
@@ -0,0 +1,179 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+#include <stdint.h>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IByteStreamEventAccess.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "DataModel/DataVector.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/IRegistry.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "TrigT1CaloEvent/TriggerTower.h"
+
+#include "PpmByteStreamCnv.h"
+#include "PpmByteStreamTool.h"
+
+namespace LVL1BS {
+
+PpmByteStreamCnv::PpmByteStreamCnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("PpmByteStreamCnv"),
+      m_tool("LVL1BS::PpmByteStreamTool/PpmByteStreamTool"),
+      m_robDataProvider("ROBDataProviderSvc", m_name),
+      m_ByteStreamEventAccess("ByteStreamCnvSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+PpmByteStreamCnv::~PpmByteStreamCnv()
+{
+}
+
+// CLID
+
+const CLID& PpmByteStreamCnv::classID()
+{
+  return ClassID_traits<DataVector<LVL1::TriggerTower> >::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode PpmByteStreamCnv::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << " - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  //Get ByteStreamCnvSvc
+  sc = m_ByteStreamEventAccess.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_ByteStreamEventAccess << endreq;
+    return sc;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_ByteStreamEventAccess << endreq;
+  }
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return sc;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  // Get ROBDataProvider
+  sc = m_robDataProvider.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::WARNING << "Failed to retrieve service "
+          << m_robDataProvider << endreq;
+    // return is disabled for Write BS which does not require ROBDataProviderSvc
+    // return sc ;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_robDataProvider << endreq;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// createObj should create the RDO from bytestream.
+
+StatusCode PpmByteStreamCnv::createObj( IOpaqueAddress* pAddr,
+                                        DataObject*& pObj )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createObj() called" << endreq;
+
+  ByteStreamAddress *pBS_Addr;
+  pBS_Addr = dynamic_cast<ByteStreamAddress *>( pAddr );
+  if ( !pBS_Addr ) {
+    m_log << MSG::ERROR << " Can not cast to ByteStreamAddress " << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = *( pBS_Addr->par() );
+
+  if (m_debug) m_log << MSG::DEBUG << " Creating Objects " << nm << endreq;
+
+  // get SourceIDs
+  const std::vector<uint32_t>& vID(m_tool->sourceIDs(nm));
+
+  // get ROB fragments
+  IROBDataProviderSvc::VROBFRAG robFrags;
+  m_robDataProvider->getROBData( vID, robFrags );
+
+  // size check
+  DataVector<LVL1::TriggerTower>* const ttCollection =
+                      new DataVector<LVL1::TriggerTower>(SG::VIEW_ELEMENTS);
+  if (m_debug) {
+    m_log << MSG::DEBUG << " Number of ROB fragments is " << robFrags.size()
+          << endreq;
+  }
+  if (robFrags.size() == 0) {
+    pObj = SG::asStorable(ttCollection) ;
+    return StatusCode::SUCCESS;
+  }
+
+  StatusCode sc = m_tool->convert(robFrags, ttCollection);
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << " Failed to create Objects   " << nm << endreq;
+    delete ttCollection;
+    return sc;
+  }
+
+  pObj = SG::asStorable(ttCollection);
+
+  return sc;
+}
+
+// createRep should create the bytestream from RDOs.
+
+StatusCode PpmByteStreamCnv::createRep( DataObject* pObj,
+                                        IOpaqueAddress*& pAddr )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createRep() called" << endreq;
+
+  RawEventWrite* re = m_ByteStreamEventAccess->getRawEvent();
+
+  DataVector<LVL1::TriggerTower>* ttCollection = 0;
+  if( !SG::fromStorable( pObj, ttCollection ) ) {
+    m_log << MSG::ERROR << " Cannot cast to DataVector<TriggerTower>" << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = pObj->registry()->name();
+
+  ByteStreamAddress* addr = new ByteStreamAddress( classID(), nm, "" );
+
+  pAddr = addr;
+
+  // Convert to ByteStream
+  return m_tool->convert( ttCollection, re );
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmByteStreamCnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmByteStreamCnv.h
new file mode 100755
index 0000000000000000000000000000000000000000..7d7278050824e132464e8085b67551ff195db03c
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmByteStreamCnv.h
@@ -0,0 +1,81 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_PPMBYTESTREAMCNV_H
+#define TRIGT1CALOBYTESTREAM_PPMBYTESTREAMCNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IByteStreamEventAccess;
+class IOpaqueAddress;
+class IROBDataProviderSvc;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class PpmByteStreamTool;
+
+/** ByteStream converter for Pre-processor Module DAQ data / TriggerTowers.
+ *
+ *  @author Peter Faulkner
+ */
+
+class PpmByteStreamCnv: public Converter {
+
+  friend class CnvFactory<PpmByteStreamCnv>;
+
+protected:
+
+  PpmByteStreamCnv(ISvcLocator* svcloc);
+
+public:
+
+  ~PpmByteStreamCnv();
+
+  virtual StatusCode initialize();
+  /// Create TriggerTowers from ByteStream
+  virtual StatusCode createObj(IOpaqueAddress* pAddr, DataObject*& pObj);
+  /// Create ByteStream from TriggerTowers
+  virtual StatusCode createRep(DataObject* pObj, IOpaqueAddress*& pAddr);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::PpmByteStreamTool> m_tool;
+
+  /// Service for reading bytestream
+  ServiceHandle<IROBDataProviderSvc> m_robDataProvider;
+  /// Service for writing bytestream
+  ServiceHandle<IByteStreamEventAccess> m_ByteStreamEventAccess;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmByteStreamSubsetTool.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmByteStreamSubsetTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..946290f8cbe5cb44d6f8da55c304cbeb20f8a759
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmByteStreamSubsetTool.cxx
@@ -0,0 +1,630 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <numeric>
+#include <set>
+
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "TrigT1CaloEvent/TriggerTower.h"
+#include "TrigT1CaloUtils/DataError.h"
+#include "TrigT1CaloUtils/TriggerTowerKey.h"
+#include "TrigT1CaloMappingToolInterfaces/IL1CaloMappingTool.h"
+
+#include "L1CaloSrcIdMap.h"
+#include "L1CaloSubBlock.h"
+#include "L1CaloUserHeader.h"
+#include "PpmSubBlock.h"
+
+#include "PpmByteStreamSubsetTool.h"
+
+namespace LVL1BS {
+
+// Constructor
+
+PpmByteStreamSubsetTool::PpmByteStreamSubsetTool(const std::string& type,
+               const std::string& name, const IInterface*  parent)
+             : AthAlgTool(type, name, parent),
+	       m_ppmMaps("LVL1::PpmMappingTool/PpmMappingTool"),
+	       m_channels(64), m_crates(8), m_modules(16),
+	       m_subDetector(eformat::TDAQ_CALO_PREPROC),
+	       m_srcIdMap(0), m_towerKey(0), m_errorBlock(0)
+{
+  declareInterface<IPpmByteStreamSubsetTool>(this);
+
+  declareProperty("PpmMappingTool", m_ppmMaps);
+
+  declareProperty("ZeroSuppress", m_zeroSuppress = true,
+                  "Only make trigger towers with non-zero EM or Had energy");
+}
+
+// Destructor
+
+PpmByteStreamSubsetTool::~PpmByteStreamSubsetTool()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode PpmByteStreamSubsetTool::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << PACKAGE_VERSION << endreq;
+
+  // Retrieve mapping tool
+
+  StatusCode sc = m_ppmMaps.retrieve();
+  if ( sc.isFailure() ) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_ppmMaps << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_ppmMaps << endreq;
+
+  m_srcIdMap = new L1CaloSrcIdMap();
+  m_towerKey = new LVL1::TriggerTowerKey();
+
+  m_ttMap.clear();
+
+  // if you retrieved it nicely, build tables
+  // See that all mapping is now done by channel number
+  // leading to the unique_idx we have bellow.
+  // See also that the EM and Had parts will be a single
+  // tt pointer
+  unsigned int maxkey=0;
+  for(int crate = 0; crate<m_crates; crate++)
+  for(int module = 0; module<m_modules; module++)
+  for(int channel = 0; channel<m_channels; channel++){
+        double eta = 0;
+        double phi = 0;
+        int layer = 0;
+        LVL1::TriggerTower* tt=0;
+        if(m_ppmMaps->mapping(crate, module, channel, eta, phi, layer)){
+            tt = findTriggerTower(eta, phi);
+            if ( !tt ) {
+            const unsigned int key = m_towerKey->ttKey(phi, eta);
+            tt = new LVL1::TriggerTower(phi, eta, key);
+            m_ttMap.insert(std::make_pair(key, tt));
+            }
+        }
+        unsigned int unique_idx = channel;
+        unique_idx+=module*m_channels;
+        unique_idx+=crate*m_channels*m_modules;
+        etamap[unique_idx] = eta;
+        phimap[unique_idx] = phi;
+        layermap[unique_idx] = layer;
+        ttpool[unique_idx] = tt;
+	if (tt && tt->key()> maxkey) maxkey=tt->key();
+  }
+  ttTemp.reserve(8192);
+  m_ppmBlocks.reserve(4);
+  // Make an array (pool) of SubBlock objects to help
+  for(size_t ii=0; ii<size_t(10);++ii){
+        PpmSubBlock* const subBlock = new PpmSubBlock();
+        m_ppmBlocksPool[ii] = subBlock;
+  }
+
+  m_event = 0xffffffff;
+  m_first=true;
+  for(int i=0;i<8192;i++)
+	   uniqueness[i]=0xffffffff;
+
+
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode PpmByteStreamSubsetTool::finalize()
+{
+  for(int crate = 0; crate<m_crates; crate++)
+  for(int module = 0; module<m_modules; module++)
+  for(int channel = 0; channel<m_channels; channel++){
+	unsigned int unique_idx = channel;
+        unique_idx+=module*m_channels;
+        unique_idx+=crate*m_channels*m_modules;
+	if ( ttpool[unique_idx] && layermap[unique_idx] ){
+	  delete ttpool[unique_idx];
+	  ttpool[unique_idx] = NULL;
+	}
+  }
+  for(size_t ii=0; ii<size_t(10);++ii){
+        delete m_ppmBlocksPool[ii];
+  }
+
+
+  delete m_errorBlock;
+  delete m_towerKey;
+  delete m_srcIdMap;
+  return StatusCode::SUCCESS;
+}
+
+// Conversion bytestream to trigger towers
+
+StatusCode PpmByteStreamSubsetTool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::TriggerTower>* const ttCollection,
+			    const std::vector<unsigned int>& chanIds)
+{
+  std::vector<int> lut;
+  std::vector<int> fadc;
+  std::vector<int> bcidLut;
+  std::vector<int> bcidFadc;
+  lut     .reserve(10);
+  fadc    .reserve(10);
+  bcidLut .reserve(10);
+  bcidFadc.reserve(10);
+
+
+  const bool debug   = msgLvl(MSG::DEBUG);
+#ifndef NDEBUG
+  const bool verbose = msgLvl(MSG::VERBOSE);
+#endif
+  if (debug) msg(MSG::DEBUG);
+
+  // Index wanted channels by crate/module
+
+  std::vector<unsigned int>::const_iterator pos ;
+  std::vector<unsigned int>::const_iterator posE;
+  std::vector<unsigned int>::const_iterator posB;
+  bool full = ( chanIds.size() == 7168 ); // hardcoded number
+  if ( full && m_first ){
+  pos  = chanIds.begin();
+  posE = chanIds.end();
+  posB = pos;
+  unsigned int lastKey = (pos != posE) ? (*pos)/64 : 9999;
+  for (; pos != posE; ++pos) {
+    const unsigned int key = (*pos)/64;
+    if (key != lastKey) {
+      if (debug) msg() << "Adding key to index: " << lastKey << endreq;
+      chanMapFull.insert(std::make_pair(lastKey, std::make_pair(posB, pos)));
+      lastKey = key;
+      posB = pos;
+    }
+  }
+  if (lastKey != 9999) {
+    if (debug) msg() << "Adding key to index: " << lastKey << endreq;
+    chanMapFull.insert(std::make_pair(lastKey, std::make_pair(posB, posE)));
+  }
+  if (debug) {
+    msg() << "Number of channels wanted: " << chanIds.size()
+          << "  Channel map size: " << chanMapFull.size() << endreq;
+  }
+  m_first = false;
+  }
+  else if ( !full ) {
+  pos  = chanIds.begin();
+  posE = chanIds.end();
+  posB = pos;
+  unsigned int lastKey = (pos != posE) ? (*pos)/64 : 9999;
+  for (; pos != posE; ++pos) {
+    const unsigned int key = (*pos)/64;
+    if (key != lastKey) {
+      if (debug) msg() << "Adding key to index: " << lastKey << endreq;
+      chanMap.insert(std::make_pair(lastKey, std::make_pair(posB, pos)));
+      lastKey = key;
+      posB = pos;
+    }
+  }
+  if (lastKey != 9999) {
+    if (debug) msg() << "Adding key to index: " << lastKey << endreq;
+    chanMap.insert(std::make_pair(lastKey, std::make_pair(posB, posE)));
+  }
+  if (debug) {
+    msg() << "Number of channels wanted: " << chanIds.size()
+          << "  Channel map size: " << chanMap.size() << endreq;
+  }
+  }
+
+
+  // Loop over ROB fragments
+
+  int robCount = 0;
+  std::set<uint32_t> dupCheck;
+  ROBIterator rob    = robFrags.begin();
+  ROBIterator robEnd = robFrags.end();
+  for (; rob != robEnd; ++rob) {
+
+    if (debug) msg() << "Treating ROB fragment " << ++robCount << endreq;
+
+    // Skip fragments with ROB status errors
+
+    if ((*rob)->nstatus() > 0) {
+      ROBPointer robData;
+      (*rob)->status(robData);
+      if (*robData != 0) {
+	if (debug) msg() << "ROB status error - skipping fragment" << endreq;
+	continue;
+      }
+    }
+
+    // Skip duplicate fragments
+
+    uint32_t robid = (*rob)->source_id();
+    if (!dupCheck.insert(robid).second) {
+      if (debug) msg() << "Skipping duplicate ROB fragment" << endreq;
+      continue;
+    }
+
+    // Unpack ROD data (slinks)
+
+    RODPointer payloadBeg;
+    RODPointer payload;
+    RODPointer payloadEnd;
+    (*rob)->rod_data(payloadBeg);
+    payloadEnd = payloadBeg + (*rob)->rod_ndata();
+    payload = payloadBeg;
+    if (payload == payloadEnd) {
+      if (debug) msg() << "ROB fragment empty" << endreq;
+      continue;
+    }
+
+    // Check identifier
+    const uint32_t sourceID = (*rob)->rod_source_id();
+    if (debug) {
+      if (m_srcIdMap->subDet(sourceID) != m_subDetector ||
+          m_srcIdMap->daqOrRoi(sourceID) != 0) {
+        msg() << "Wrong source identifier in data: "
+	      << MSG::hex << sourceID << MSG::dec << endreq;
+      }
+    }
+    const int rodCrate = m_srcIdMap->crate(sourceID);
+    if (debug) {
+      msg() << "Treating crate " << rodCrate 
+            << " slink " << m_srcIdMap->slink(sourceID) << endreq;
+    }
+
+    // First word is User Header
+    L1CaloUserHeader userHeader(*payload);
+    const int minorVersion = (*rob)->rod_version() & 0xffff;
+    userHeader.setVersion(minorVersion);
+    const int headerWords = userHeader.words();
+    if (headerWords != 1 && debug) {
+      msg() << "Unexpected number of user header words: "
+            << headerWords << endreq;
+    }
+    payload+=headerWords;
+    // triggered slice offsets
+    const int trigLut  = userHeader.ppmLut();
+    const int trigFadc = userHeader.ppmFadc();
+    // FADC baseline lower bound
+    const int fadcBaseline = userHeader.lowerBound();
+    if (debug) {
+      msg() << "Minor format version number: "
+            << MSG::hex << minorVersion << MSG::dec            << endreq
+            << "LUT triggered slice offset:  " << trigLut      << endreq
+            << "FADC triggered slice offset: " << trigFadc     << endreq
+            << "FADC baseline lower bound:   " << fadcBaseline << endreq;
+    }
+    const int runNumber = (*rob)->rod_run_no() & 0xffffff;
+
+    // Find the number of channels per sub-block
+
+    int chanPerSubBlock = 0;
+    if (payload != payloadEnd) {
+      if (L1CaloSubBlock::wordType(*payload) != L1CaloSubBlock::HEADER) {
+        if (debug) msg() << "Missing Sub-block header" << endreq;
+        continue;
+      }
+      testBlock.clear();
+
+      payload = testBlock.read(payload, payloadEnd);
+      if (payload == payloadEnd) {
+        if (debug) msg() << "Keep coverity happy" << endreq;
+      }
+      chanPerSubBlock = testBlock.channelsPerSubBlock();
+      if (chanPerSubBlock == 0) {
+        if (debug) msg() << "Unsupported version/data format: "
+                         << testBlock.version() << "/"
+                         << testBlock.format()  << endreq;
+        continue;
+      }
+      if (m_channels%chanPerSubBlock != 0) {
+        if (debug) msg() << "Invalid channels per sub-block: "
+                         << chanPerSubBlock << endreq;
+        continue;
+      }
+      if (debug) {
+        msg() << "Channels per sub-block: " << chanPerSubBlock << endreq;
+      }
+    } else {
+      if (debug) msg() << "ROB fragment contains user header only" << endreq;
+      continue;
+    }
+    const int numSubBlocks = m_channels/chanPerSubBlock;
+
+    // Loop over PPMs
+
+    payload = payloadBeg;
+    payload+=headerWords;
+    bool isErr = false;
+    while (payload != payloadEnd) {
+
+      // Get all sub-blocks for one PPM
+
+      int crate = 0;
+      int module = 0;
+      m_ppmBlocks.clear();
+      int pool = 0;
+      for (int block = 0; block < numSubBlocks; ++block) {
+        if (L1CaloSubBlock::wordType(*payload) != L1CaloSubBlock::HEADER
+	                           || PpmSubBlock::errorBlock(*payload)) {
+          if (debug) msg() << "Unexpected data sequence" << endreq;
+	  isErr = true;
+	  break;
+        }
+        if (chanPerSubBlock != m_channels && 
+	    L1CaloSubBlock::seqno(*payload) != block * chanPerSubBlock) {
+	  if (debug) {
+            msg() << "Unexpected channel sequence number: "
+	          << L1CaloSubBlock::seqno(*payload) << " expected " 
+	          << block * chanPerSubBlock << endreq;
+	  }
+	  isErr = true;
+	  break;
+        }
+        PpmSubBlock* const subBlock = m_ppmBlocksPool[pool];
+        subBlock->clear();
+        m_ppmBlocks.push_back((  m_ppmBlocksPool[pool++] ));
+        payload = subBlock->read(payload, payloadEnd);
+        if (block == 0) {
+          crate  = subBlock->crate();
+	  module = subBlock->module();
+	  if (debug) {
+	    msg() << "Module " << module << endreq;
+	    if (crate != rodCrate) {
+	      msg() << "Inconsistent crate number in ROD source ID" << endreq;
+	    }
+          }
+        } else {
+          if (subBlock->crate() != crate) {
+	    if (debug) msg() << "Inconsistent crate number in sub-blocks"
+	                     << endreq;
+	    isErr = true;
+	    break;
+          }
+          if (subBlock->module() != module) {
+	    if (debug) msg() << "Inconsistent module number in sub-blocks"
+	                     << endreq;
+	    isErr = true;
+	    break;
+          }
+        }
+        if (payload == payloadEnd && block != numSubBlocks - 1) {
+          if (debug) msg() << "Premature end of data" << endreq;
+	  break;
+        }
+      }
+      if (isErr) break;
+
+      // Is there an error block?
+
+      delete m_errorBlock;
+      m_errorBlock = 0;
+      if (payload != payloadEnd) {
+        if (L1CaloSubBlock::wordType(*payload) == L1CaloSubBlock::HEADER
+	                            && PpmSubBlock::errorBlock(*payload)) {
+	  if (debug) msg() << "Error block found" << endreq;
+	  m_errorBlock = new PpmSubBlock();
+	  payload = m_errorBlock->read(payload, payloadEnd);
+          if (m_errorBlock->crate() != crate) {
+	    if (debug) msg() << "Inconsistent crate number in error block"
+	                     << endreq;
+	    isErr = true;
+	    break;
+          }
+          if (m_errorBlock->module() != module) {
+	    if (debug) msg() << "Inconsistent module number in error block"
+	                     << endreq;
+	    isErr = true;
+	    break;
+          }
+	  if (m_errorBlock->dataWords() && !m_errorBlock->unpack()) {
+	    if (debug) {
+	      std::string errMsg(m_errorBlock->unpackErrorMsg());
+	      msg() << "Unpacking error block failed: " << errMsg << endreq;
+	    }
+	    isErr = true;
+	    break;
+	  }
+        }
+      }
+
+      // Loop over wanted channels and fill trigger towers
+
+      const int actualSubBlocks = m_ppmBlocks.size();
+      int lastBlock = -1;
+      unsigned int key = crate*16 + module;
+      ChannelMap::iterator mapIter = full ? chanMapFull.find(key) : chanMap.find(key);
+      if (full && mapIter == chanMapFull.end()) {
+        if (debug) msg() << "Key not found: " << key << endreq;
+	continue;
+      }
+      if (!full && mapIter == chanMap.end()) {
+        if (debug) msg() << "Key not found: " << key << endreq;
+	continue;
+      }
+      IteratorPair ipair = mapIter->second;
+      pos  = ipair.first;
+      posE = ipair.second;
+      PpmSubBlock* subBlock = 0;
+      unsigned int cratemod_idx = module*m_channels;
+      cratemod_idx+=crate*m_channels*m_modules;
+      for (; pos != posE; ++pos) {
+        const int channel = (*pos) % 64;
+	const int block = channel/chanPerSubBlock;
+	if (block >= actualSubBlocks) {
+	  if (debug) {
+	    msg() << "channel/block/actualSubBlocks: "
+	          << channel << "/" << block << "/" << actualSubBlocks
+		  << endreq;
+	  }
+	  break;
+	}
+	if (block != lastBlock) {
+	  lastBlock = block;
+          subBlock = m_ppmBlocks[block];
+	  subBlock->setLutOffset(trigLut);
+	  subBlock->setFadcOffset(trigFadc);
+	  subBlock->setFadcBaseline(fadcBaseline);
+	  subBlock->setRunNumber(runNumber);
+          if (subBlock->dataWords() && !subBlock->unpack()) {
+	    if (debug) {
+	      std::string errMsg(subBlock->unpackErrorMsg());
+	      msg() << "Unpacking PPM sub-block failed: " << errMsg << endreq;
+	    }
+	    isErr = true;
+	    break;
+          }
+	}
+	if (!subBlock) {
+	  if (debug) msg() << "Logic error" << endreq;
+	  break;
+        }
+	subBlock->ppmData(channel, lut, fadc, bcidLut, bcidFadc);
+	int trigLutKeep  = trigLut;
+	int trigFadcKeep = trigFadc;
+	if (lut.size() < size_t(trigLut + 1)) {
+	  if (debug) {
+	    msg() << "Triggered LUT slice from header "
+	          << "inconsistent with number of slices: "
+	          << trigLut << ", " << lut.size() << ", reset to 0" << endreq;
+	  }
+	  trigLutKeep = 0;
+        }
+	if (fadc.size() < size_t(trigFadc + 1)) {
+	  if (debug) {
+	    msg() << "Triggered FADC slice from header "
+	          << "inconsistent with number of slices: "
+	          << trigFadc << ", " << fadc.size() << ", reset to 0"
+		  << endreq;
+	  }
+	  trigFadcKeep = 0;
+        }
+	LVL1::DataError errorBits(0);
+	if (m_errorBlock) {
+	  errorBits.set(LVL1::DataError::PPMErrorWord,
+	                           m_errorBlock->ppmError(channel));
+	  errorBits.set(LVL1::DataError::SubStatusWord,
+	                           m_errorBlock->subStatus());
+        } else {
+	  errorBits.set(LVL1::DataError::PPMErrorWord,
+	                           subBlock->ppmError(channel));
+	  const PpmSubBlock* const lastBlock =
+	                                    m_ppmBlocks[actualSubBlocks - 1];
+	  errorBits.set(LVL1::DataError::SubStatusWord,
+	                           lastBlock->subStatus());
+        }
+	// Wrong bit set for compressed formats 1.01 to 1.03
+	if (subBlock->format() > 1 && subBlock->seqno() < 4) {
+	  errorBits.set(LVL1::DataError::ModuleError,
+	       (errorBits.error() >> (LVL1::DataError::ModuleError+1)) & 0x1);
+	}
+	const int error = errorBits.error();
+
+	// Only save non-zero data
+
+        const bool any =
+	             std::accumulate(lut.begin(),      lut.end(),      0) ||
+	             std::accumulate(fadc.begin(),     fadc.end(),     0) ||
+		     std::accumulate(bcidLut.begin(),  bcidLut.end(),  0) ||
+		     std::accumulate(bcidFadc.begin(), bcidFadc.end(), 0);
+
+        if (any || error) {
+#ifndef NDEBUG
+	  if (verbose) {
+	    msg(MSG::VERBOSE) << "channel/LUT/FADC/bcidLUT/bcidFADC/error: "
+	                      << channel << "/";
+	    printVec(lut);
+	    printVec(fadc);
+	    printVec(bcidLut);
+	    printVec(bcidFadc);
+	    msg() << MSG::hex << error << MSG::dec << "/";
+          }
+#endif
+          // See that there is no call to m_ppmMaps->mapping here
+          // all is addressed by channel number and crate/module
+          // as prepared in the initialize. Also, no more calls to
+          // findTriggerTower in the loop
+          unsigned int unique_idx = cratemod_idx+channel;
+          LVL1::TriggerTower* tt = ttpool[unique_idx];
+          if ( !tt ) continue;
+#ifndef NDEBUG
+          if (verbose) {
+            // eta/phi are only used here
+            double eta = etamap[unique_idx];
+            double phi = phimap[unique_idx];
+            int layer = layermap[unique_idx];
+            msg() << " eta/phi/layer: " << eta << "/" << phi << "/"
+                  << layer << "/" << endreq;
+            msg(MSG::DEBUG);
+          }
+#endif
+          // tt is filled during unpacking, NOT here.
+	  if ( uniqueness[tt->key()] != m_event ){
+            ttTemp.push_back(tt);
+	    uniqueness[tt->key()] = m_event;
+	  }
+
+          if (layermap[unique_idx]) {  // EM
+	    tt->addHad(fadc, lut, bcidFadc, bcidLut, error,
+	                                         trigLutKeep, trigFadcKeep);
+          } else {           // Had
+	    tt->addEM(fadc, lut, bcidFadc, bcidLut, error,
+	                                         trigLutKeep, trigFadcKeep);
+          }
+        }
+      }
+      if (isErr) break;
+    }
+  }
+
+  // Swap wanted trigger towers into final container
+
+  const int size = ttTemp.size();
+  for (int index = 0; index < size; ++index) {
+    if ( !m_zeroSuppress || ttTemp[index]->emEnergy()
+                         || ttTemp[index]->hadEnergy() ) {
+      ttCollection->push_back(ttTemp[index]);
+    }
+  }
+  ttTemp.clear();
+  for(int i=0;i<8192;i++)
+	   uniqueness[i]=0xffffffff;
+
+  return StatusCode::SUCCESS;
+}
+
+// Find a trigger tower given eta, phi
+
+LVL1::TriggerTower* PpmByteStreamSubsetTool::findTriggerTower(const double eta,
+                                                              const double phi)
+{
+  LVL1::TriggerTower* tt = 0;
+  const unsigned int key = m_towerKey->ttKey(phi, eta);
+  TriggerTowerMap::const_iterator mapIter;
+  mapIter = m_ttMap.find(key);
+  if (mapIter != m_ttMap.end()) tt = mapIter->second;
+  return tt;
+}
+
+// Print a vector
+
+void PpmByteStreamSubsetTool::printVec(const std::vector<int>& vec) const
+{
+  std::vector<int>::const_iterator pos;
+  for (pos = vec.begin(); pos != vec.end(); ++pos) {
+    if (pos != vec.begin()) msg() << ",";
+    msg() << *pos;
+  }
+  msg() << "/";
+}
+
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmByteStreamSubsetTool.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmByteStreamSubsetTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..323364dcd95cb5fbbf3128b5a1adec70bcd7ef32
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmByteStreamSubsetTool.h
@@ -0,0 +1,130 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_PPMBYTESTREAMSUBSETTOOL_H
+#define TRIGT1CALOBYTESTREAM_PPMBYTESTREAMSUBSETTOOL_H
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+#include "ByteStreamData/RawEvent.h"
+#include "DataModel/DataVector.h"
+#include "eformat/SourceIdentifier.h"
+#include "GaudiKernel/ToolHandle.h"
+
+#include "IPpmByteStreamSubsetTool.h"
+#include "PpmSubBlock.h"
+
+class IInterface;
+class StatusCode;
+
+namespace LVL1 {
+  class IL1CaloMappingTool;
+  class TriggerTower;
+  class TriggerTowerKey;
+}
+
+namespace LVL1BS {
+
+class L1CaloSrcIdMap;
+
+/** Tool to perform ROB fragments to trigger towers conversions.
+ *
+ *  Based on ROD document version 1_09h.
+ *
+ *  @author Peter Faulkner
+ */
+
+class PpmByteStreamSubsetTool : virtual public IPpmByteStreamSubsetTool,
+                                        public AthAlgTool {
+
+ public:
+   PpmByteStreamSubsetTool(const std::string& type, const std::string& name,
+                           const IInterface* parent);
+   virtual ~PpmByteStreamSubsetTool();
+
+   virtual StatusCode initialize();
+   virtual StatusCode finalize();
+
+   /// Convert ROB fragments to trigger towers
+   virtual StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                              DataVector<LVL1::TriggerTower>* ttCollection,
+		              const std::vector<unsigned int>& chanIds);
+   virtual void eventNumber(const unsigned int eN ) { m_event=eN;};
+
+ private:
+   typedef DataVector<LVL1::TriggerTower>                TriggerTowerCollection;
+   typedef std::map<unsigned int, LVL1::TriggerTower*>   TriggerTowerMap;
+   typedef IROBDataProviderSvc::VROBFRAG::const_iterator ROBIterator;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      ROBPointer;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      RODPointer;
+   typedef std::pair<std::vector<unsigned int>::const_iterator,
+                     std::vector<unsigned int>::const_iterator> IteratorPair;
+   typedef std::map<unsigned int, IteratorPair>          ChannelMap;
+
+   /// Find a trigger tower given eta, phi
+   LVL1::TriggerTower* findTriggerTower(double eta, double phi);
+
+   /// Print a vector
+   void printVec(const std::vector<int>& vec) const;
+
+   /// Tool for mappings
+   ToolHandle<LVL1::IL1CaloMappingTool> m_ppmMaps;
+
+   /// Number of channels per module (may not all be used)
+   int m_channels;
+   /// Number of crates
+   int m_crates;
+   /// Number of modules per crate (may not all exist)
+   int m_modules;
+   /// Sub-detector type
+   eformat::SubDetector m_subDetector;
+   /// Zero suppression on input
+   bool m_zeroSuppress;
+   /// Source ID converter
+   L1CaloSrcIdMap* m_srcIdMap;
+   /// Trigger tower key provider
+   LVL1::TriggerTowerKey* m_towerKey;
+   /// Current error block
+   PpmSubBlock* m_errorBlock;
+   /// Trigger tower map for conversion from bytestream
+   TriggerTowerMap m_ttMap;
+   /// Vector for current PPM sub-blocks. Changed type
+   std::vector<PpmSubBlock*> m_ppmBlocks;
+   /// Pool for current PPM sub-blocks
+   PpmSubBlock* m_ppmBlocksPool[10];
+   // To simplify life, let's prepare maps beforehand
+   // Organized by crate (8), module (16) and channel (64)
+   // eta and phi maps could be avoided. Only used in DEBUG
+   // building of the code
+   double etamap[8192];
+   double phimap[8192];
+   int layermap [8192];
+   unsigned int uniqueness[8192];
+   // The same for new tt addresses (no mem allocation)
+   LVL1::TriggerTower* ttpool[8192];
+   // Cache temporary vector
+   std::vector<LVL1::TriggerTower*> ttTemp;
+   // Cache channel map
+   ChannelMap chanMap;
+   ChannelMap chanMapFull;
+   // Get channel maps for full calo unpack
+   bool m_first;
+   // Caching some variables
+   PpmSubBlock testBlock;
+   // counter of present event
+   unsigned int m_event;
+
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmByteStreamTool.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmByteStreamTool.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..caf837f60f9b1673d899c50dc56d59b6143b486f
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmByteStreamTool.cxx
@@ -0,0 +1,1121 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <numeric>
+#include <set>
+#include <utility>
+
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+#include "StoreGate/SegMemSvc.h"
+
+#include "ByteStreamCnvSvcBase/FullEventAssembler.h"
+
+#include "TrigT1CaloEvent/TriggerTower.h"
+#include "TrigT1CaloUtils/DataError.h"
+#include "TrigT1CaloUtils/TriggerTowerKey.h"
+#include "TrigT1CaloMappingToolInterfaces/IL1CaloMappingTool.h"
+
+#include "CmmSubBlock.h"
+#include "L1CaloErrorByteStreamTool.h"
+#include "L1CaloSrcIdMap.h"
+#include "L1CaloSubBlock.h"
+#include "L1CaloUserHeader.h"
+#include "ModifySlices.h"
+#include "PpmSubBlock.h"
+
+#include "PpmByteStreamTool.h"
+
+namespace LVL1BS {
+
+const int PpmByteStreamTool::s_crates;
+const int PpmByteStreamTool::s_modules;
+const int PpmByteStreamTool::s_channels;
+const int PpmByteStreamTool::s_dataSize;
+
+// Interface ID
+
+static const InterfaceID IID_IPpmByteStreamTool("PpmByteStreamTool", 1, 1);
+
+const InterfaceID& PpmByteStreamTool::interfaceID()
+{
+  return IID_IPpmByteStreamTool;
+}
+
+// Constructor
+
+PpmByteStreamTool::PpmByteStreamTool(const std::string& type,
+                                     const std::string& name,
+				     const IInterface*  parent)
+  : AthAlgTool(type, name, parent),
+    m_ppmMaps("LVL1::PpmMappingTool/PpmMappingTool"),
+    m_errorTool("LVL1BS::L1CaloErrorByteStreamTool/L1CaloErrorByteStreamTool"),
+    m_sms("SegMemSvc/SegMemSvc", name),
+    m_version(1), m_compVers(4),
+    m_dataChannels(true), m_spareChannels(false), m_muonChannels(false),
+    m_subDetector(eformat::TDAQ_CALO_PREPROC),
+    m_srcIdMap(0), m_towerKey(0), m_errorBlock(0), m_rodStatus(0), m_fea(0)
+{
+  declareInterface<PpmByteStreamTool>(this);
+
+  declareProperty("PpmMappingTool", m_ppmMaps,
+                  "Crate/Module/Channel to Eta/Phi/Layer mapping tool");
+  declareProperty("ErrorTool", m_errorTool,
+                  "Tool to collect errors for monitoring");
+
+  declareProperty("PrintCompStats",     m_printCompStats  = 0,
+                  "Print compressed format statistics");
+  declareProperty("SlinksPerCrate",     m_slinks          = 4,
+                  "The number of S-Links per crate");
+
+  // Properties for reading bytestream only
+  declareProperty("ZeroSuppress",       m_zeroSuppress    = 0,
+                  "Only make trigger towers with non-zero EM or Had energy");
+  declareProperty("ROBSourceIDs",       m_sourceIDs,
+                  "ROB fragment source identifiers");
+  declareProperty("PedestalValue",      m_pedestal        = 10,
+                  "Pedestal value - needed for compressed formats 0,1 only");
+
+  // Properties for writing bytestream only
+  declareProperty("DataFormat",         m_dataFormat      = 1,
+                  "Format identifier (0-3) in sub-block header");
+  declareProperty("FADCBaseline",       m_fadcBaseline    = 0,
+                  "FADC baseline lower bound for compressed formats");
+  declareProperty("FADCThreshold",      m_fadcThreshold   = 1,
+                  "FADC threshold for super-compressed format");
+  declareProperty("SimulSlicesLUT",     m_dfltSlicesLut   = 1,
+                  "The number of LUT slices in the simulation");
+  declareProperty("SimulSlicesFADC",    m_dfltSlicesFadc  = 7,
+                  "The number of FADC slices in the simulation");
+  declareProperty("ForceSlicesLUT",     m_forceSlicesLut  = 0,
+                  "If >0, the number of LUT slices in bytestream");
+  declareProperty("ForceSlicesFADC",    m_forceSlicesFadc = 0,
+                  "If >0, the number of FADC slices in bytestream");
+  declareProperty("CrateMin",       m_crateMin = 0,
+                  "Minimum crate number, allows partial output");
+  declareProperty("CrateMax",       m_crateMax = s_crates-1,
+                  "Maximum crate number, allows partial output");
+
+}
+
+// Destructor
+
+PpmByteStreamTool::~PpmByteStreamTool()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode PpmByteStreamTool::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = m_ppmMaps.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_ppmMaps << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_ppmMaps << endreq;
+
+  sc = m_errorTool.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_errorTool << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_errorTool << endreq;
+
+  sc = m_sms.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve service " << m_sms << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved service " << m_sms << endreq;
+
+  m_srcIdMap  = new L1CaloSrcIdMap();
+  m_towerKey  = new LVL1::TriggerTowerKey();
+  m_rodStatus = new std::vector<uint32_t>(2);
+  m_fea       = new FullEventAssembler<L1CaloSrcIdMap>();
+
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode PpmByteStreamTool::finalize()
+{
+  if (m_printCompStats && msgLvl(MSG::INFO)) {
+    msg(MSG::INFO);
+    printCompStats();
+  }
+  delete m_fea;
+  delete m_rodStatus;
+  delete m_errorBlock;
+  delete m_towerKey;
+  delete m_srcIdMap;
+  return StatusCode::SUCCESS;
+}
+
+// Conversion bytestream to trigger towers
+
+StatusCode PpmByteStreamTool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::TriggerTower>* const ttCollection)
+{
+  const bool debug   = msgLvl(MSG::DEBUG);
+  const bool verbose = msgLvl(MSG::VERBOSE);
+  if (debug) msg(MSG::DEBUG);
+
+  // Set up TriggerTower pool and mappings
+  // (NB. This assumes mappings won't change during the course of a job)
+  const int maxChannels = s_crates * s_modules * s_channels;
+  const int chanBitVecSize = maxChannels/32;
+  if (m_ttData.empty()) {
+    const int spareSize = maxChannels - 2*s_dataSize;
+    const int muonSize = 2*s_channels;
+    const int modBitVecSize = (s_crates * s_modules)/32;
+    m_ttData.reserve(s_dataSize);
+    m_ttSpare.reserve(spareSize);
+    m_ttMuon.reserve(muonSize);
+    m_ttPos.resize(maxChannels);
+    m_chanLayer.resize(chanBitVecSize);
+    m_dataChan.resize(chanBitVecSize);
+    m_spareChan.resize(chanBitVecSize);
+    m_muonChan.resize(chanBitVecSize);
+    m_dataMod.resize(modBitVecSize);
+    m_spareMod.resize(modBitVecSize);
+    m_muonMod.resize(modBitVecSize);
+    TriggerTowerMap ttMap;
+    TriggerTowerMap::iterator itt;
+    std::vector<int> dummyS;
+    std::vector<int> dummyL(1);
+    std::vector<int> dummyF(5);
+    int dataCount  = 0;
+    int spareCount = 0;
+    for (int crate = 0; crate < s_crates; ++crate) {
+      for (int module = 0; module < s_modules; ++module) {
+	const int index2 = (crate<<4) + module;
+	const int word2  = index2/32;
+	const int bit2   = index2%32;
+        for (int channel = 0; channel < s_channels; ++channel) {
+	  const int index = (crate<<10) + (module<<6) + channel;
+	  const int word  = index/32;
+	  const int bit   = index%32;
+	  double eta = 0.;
+	  double phi = 0.;
+	  int layer = 0;
+	  unsigned int key = 0;
+	  if (m_ppmMaps->mapping(crate, module, channel, eta, phi, layer)) {
+	    // Data channels
+	    key = m_towerKey->ttKey(phi, eta);
+	    itt = ttMap.find(key);
+	    if (itt == ttMap.end()) {
+	      LVL1::TriggerTower* tt =
+	          new (m_sms->allocate<LVL1::TriggerTower>(SegMemSvc::JOB))
+		          LVL1::TriggerTower(phi, eta, key,
+			  dummyF, dummyL, dummyF, dummyL, 0, 0, 0,
+			  dummyF, dummyL, dummyF, dummyL, 0, 0, 0);
+	      m_ttData.push_back(tt);
+	      const int count = dataCount++;
+	      m_ttPos[index] = count;
+	      ttMap.insert(std::make_pair(key,count));
+            } else {
+	      m_ttPos[index] = itt->second;
+            }
+	    m_chanLayer[word] |= (layer<<bit);
+	    m_dataChan[word]  |= (1<<bit);
+	    m_dataMod[word2]  |= (1<<bit2);
+          } else {
+	    // Spare channels
+	    const int pin  = channel%16;
+	    const int asic = channel/16;
+	    eta = 16*crate + module;
+	    phi = 4*pin + asic;
+	    layer = 0;
+	    const int type = 1;
+	    key = (crate<<24) | (type<<20) | (module<<16) | (pin<<8) | asic; // CoolID
+	    LVL1::TriggerTower* tt =
+	        new (m_sms->allocate<LVL1::TriggerTower>(SegMemSvc::JOB))
+		          LVL1::TriggerTower(phi, eta, key,
+			  dummyF, dummyL, dummyF, dummyL, 0, 0, 0,
+			  dummyS, dummyS, dummyS, dummyS, 0, 0, 0);
+	    m_ttSpare.push_back(tt);
+	    m_ttPos[index] = spareCount++;
+	    m_chanLayer[word] |= (layer<<bit);
+	    m_spareChan[word] |= (1<<bit);
+	    m_spareMod[word2] |= (1<<bit2);
+            if ((crate == 2 || crate == 3) && (module == 0)) {
+	      m_ttMuon.push_back(tt);
+	      m_muonChan[word] |= (1<<bit);
+	      m_muonMod[word2] |= (1<<bit2);
+	    }
+          }
+        }
+      }
+    }
+  }
+
+  // Set up according to the collection we want
+
+  TriggerTowerVector& ttCol((m_dataChannels) ? m_ttData
+                                             : (m_spareChannels)
+                                                ? m_ttSpare
+			  		        : m_ttMuon);
+  TriggerTowerVector& ttColRef((m_dataChannels) ? m_ttData
+                                                : m_ttSpare);
+  ChannelBitVector& colChan((m_dataChannels) ? m_dataChan
+                                             : (m_spareChannels)
+			   			? m_spareChan
+						: m_muonChan);
+  ChannelBitVector& colMod((m_dataChannels) ? m_dataMod
+                                            : (m_spareChannels)
+					       ? m_spareMod
+					       : m_muonMod);
+  const int colSize = (m_dataChannels) ? 2*ttCol.size()
+                                       : ttCol.size();
+  m_foundChan.assign(chanBitVecSize, 0);
+  int ttCount = 0;
+
+  // Vectors to unpack into
+  std::vector<int> lut;
+  std::vector<int> fadc;
+  std::vector<int> bcidLut;
+  std::vector<int> bcidFadc;
+
+  // Loop over ROB fragments
+
+  int robCount = 0;
+  std::set<uint32_t> dupCheck;
+  ROBIterator rob    = robFrags.begin();
+  ROBIterator robEnd = robFrags.end();
+  for (; rob != robEnd; ++rob) {
+
+    if (debug) {
+      ++robCount;
+      msg() << "Treating ROB fragment " << robCount << endreq;
+    }
+
+    // Skip fragments with ROB status errors
+
+    uint32_t robid = (*rob)->source_id();
+    if ((*rob)->nstatus() > 0) {
+      ROBPointer robData;
+      (*rob)->status(robData);
+      if (*robData != 0) {
+        m_errorTool->robError(robid, *robData);
+	if (debug) msg() << "ROB status error - skipping fragment" << endreq;
+	continue;
+      }
+    }
+
+    // Skip duplicate fragments
+
+    if (!dupCheck.insert(robid).second) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_DUPLICATE_ROB);
+      if (debug) msg() << "Skipping duplicate ROB fragment" << endreq;
+      continue;
+    }
+
+    // Unpack ROD data (slinks)
+
+    RODPointer payloadBeg;
+    RODPointer payload;
+    RODPointer payloadEnd;
+    (*rob)->rod_data(payloadBeg);
+    payloadEnd = payloadBeg + (*rob)->rod_ndata();
+    payload = payloadBeg;
+    if (payload == payloadEnd) {
+      if (debug) msg() << "ROB fragment empty" << endreq;
+      continue;
+    }
+
+    // Check identifier
+    const uint32_t sourceID = (*rob)->rod_source_id();
+    if (m_srcIdMap->getRobID(sourceID) != robid         ||
+        m_srcIdMap->subDet(sourceID)   != m_subDetector ||
+	m_srcIdMap->daqOrRoi(sourceID) != 0             ||
+	m_srcIdMap->slink(sourceID)    >= m_slinks      ||
+	m_srcIdMap->crate(sourceID)    >= s_crates) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_ROD_ID);
+      if (debug) {
+        msg() << "Wrong source identifier in data: ROD "
+	      << MSG::hex << sourceID << "  ROB " << robid
+	      << MSG::dec << endreq;
+      }
+      continue;
+    }
+
+    // Check minor version
+    const int minorVersion = (*rob)->rod_version() & 0xffff;
+    if (minorVersion > m_srcIdMap->minorVersionPreLS1()) {
+      if (debug) msg() << "Skipping post-LS1 data" << endreq;
+      continue;
+    }
+    const int rodCrate = m_srcIdMap->crate(sourceID);
+    if (debug) {
+      msg() << "Treating crate " << rodCrate 
+            << " slink "         << m_srcIdMap->slink(sourceID) << endreq;
+    }
+
+    // First word should be User Header
+    if ( !L1CaloUserHeader::isValid(*payload) ) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
+      if (debug) msg() << "Invalid or missing user header" << endreq;
+      continue;
+    }
+    L1CaloUserHeader userHeader(*payload);
+    userHeader.setVersion(minorVersion);
+    const int headerWords = userHeader.words();
+    if (headerWords != 1) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_USER_HEADER);
+      if (debug) {
+        msg() << "Unexpected number of user header words: "
+              << headerWords << endreq;
+      }
+      continue;
+    }
+    for (int i = 0; i < headerWords; ++i) ++payload;
+    // triggered slice offsets
+    const int trigLut  = userHeader.ppmLut();
+    const int trigFadc = userHeader.ppmFadc();
+    // FADC baseline lower bound
+    m_fadcBaseline = userHeader.lowerBound();
+    if (debug) {
+      msg() << "Minor format version number: "
+            << MSG::hex << minorVersion << MSG::dec              << endreq
+            << "LUT triggered slice offset:  " << trigLut        << endreq
+            << "FADC triggered slice offset: " << trigFadc       << endreq
+            << "FADC baseline lower bound:   " << m_fadcBaseline << endreq;
+    }
+    const int runNumber = (*rob)->rod_run_no() & 0xffffff;
+
+    // Find the number of channels per sub-block
+
+    int chanPerSubBlock = 0;
+    bool firstBlock = false;
+    uint32_t firstWord = 0;
+    RODPointer payloadFirst;
+    if (payload != payloadEnd) {
+      if (L1CaloSubBlock::wordType(*payload) != L1CaloSubBlock::HEADER
+                                   || CmmSubBlock::cmmBlock(*payload)) {
+	m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_MISSING_HEADER);
+        if (debug) msg() << "Missing Sub-block header" << endreq;
+	continue;
+      }
+      firstBlock = true;
+      firstWord = *payload;
+      if (m_ppmBlocks.empty()) {
+        m_ppmBlocks.push_back(new PpmSubBlock());
+      }
+      PpmSubBlock* const subBlock = m_ppmBlocks[0];
+      subBlock->clear();
+      payloadFirst = subBlock->read(payload, payloadEnd);
+      chanPerSubBlock = subBlock->channelsPerSubBlock();
+      if (chanPerSubBlock == 0) {
+        m_errorTool->rodError(robid, subBlock->unpackErrorCode());
+        if (debug) msg() << "Unsupported version/data format: "
+                         << subBlock->version() << "/"
+                         << subBlock->format()  << endreq;
+	continue;
+      }
+      if (debug) msg() << "Channels per sub-block: "
+                       << chanPerSubBlock << endreq;
+    } else {
+      if (debug) msg() << "ROB fragment contains user header only" << endreq;
+      continue;
+    }
+    const int numSubBlocks = s_channels/chanPerSubBlock;
+    const int size = m_ppmBlocks.size();
+    if (numSubBlocks > size) {
+      for (int i = size; i < numSubBlocks; ++i) {
+        m_ppmBlocks.push_back(new PpmSubBlock());
+      }
+    }
+
+    // Loop over PPMs
+
+    payload = payloadBeg;
+    for (int i = 0; i < headerWords; ++i) ++payload;
+    unsigned int rodErr = L1CaloSubBlock::ERROR_NONE;
+    while (payload != payloadEnd) {
+
+      // Get all sub-blocks for one PPM (first already read in above)
+
+      int crate = 0;
+      int module = 0;
+      int nPpmBlocks = 0;
+      for (int block = 0; block < numSubBlocks; ++block) {
+	const uint32_t word = (firstBlock) ? firstWord : *payload;
+        if (L1CaloSubBlock::wordType(word) != L1CaloSubBlock::HEADER
+                                   || CmmSubBlock::cmmBlock(word)
+	                           || PpmSubBlock::errorBlock(word)) {
+          if (debug) msg() << "Unexpected data sequence" << endreq;
+	  rodErr = L1CaloSubBlock::ERROR_MISSING_HEADER;
+	  break;
+        }
+        if (chanPerSubBlock != s_channels && 
+	    L1CaloSubBlock::seqno(word) != block * chanPerSubBlock) {
+	  if (debug) {
+            msg() << "Unexpected channel sequence number: "
+	          << L1CaloSubBlock::seqno(word) << " expected " 
+	          << block * chanPerSubBlock << endreq;
+	  }
+	  rodErr = L1CaloSubBlock::ERROR_MISSING_SUBBLOCK;
+	  break;
+        }
+        PpmSubBlock* const subBlock = m_ppmBlocks[block];
+	nPpmBlocks++;
+	if (firstBlock) {
+          payload = payloadFirst;
+          firstBlock = false;
+        } else {
+          subBlock->clear();
+          payload = subBlock->read(payload, payloadEnd);
+        }
+        if (block == 0) {
+          crate = subBlock->crate();
+	  module = subBlock->module();
+	  if (debug) {
+	    msg() << "Crate " << crate << "  Module " << module << endreq;
+	  }
+	  if (crate != rodCrate) {
+	    if (debug) {
+	      msg() << "Inconsistent crate number in ROD source ID" << endreq;
+	    }
+	    rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	    break;
+          }
+        } else {
+          if (subBlock->crate() != crate) {
+	    if (debug) msg() << "Inconsistent crate number in sub-blocks"
+	                     << endreq;
+	    rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	    break;
+          }
+          if (subBlock->module() != module) {
+	    if (debug) msg() << "Inconsistent module number in sub-blocks"
+	                     << endreq;
+	    rodErr = L1CaloSubBlock::ERROR_MODULE_NUMBER;
+	    break;
+          }
+        }
+        if (payload == payloadEnd && block != numSubBlocks - 1) {
+          if (debug) msg() << "Premature end of data" << endreq;
+	  rodErr = L1CaloSubBlock::ERROR_MISSING_SUBBLOCK;
+	  break;
+        }
+      }
+      if (rodErr != L1CaloSubBlock::ERROR_NONE) break;
+
+      // Is there an error block?
+
+      bool isErrBlock = false;
+      if (payload != payloadEnd) {
+        if (L1CaloSubBlock::wordType(*payload) == L1CaloSubBlock::HEADER
+                                    && !CmmSubBlock::cmmBlock(*payload)
+	                            && PpmSubBlock::errorBlock(*payload)) {
+	  if (debug) msg() << "Error block found" << endreq;
+	  if (!m_errorBlock) m_errorBlock = new PpmSubBlock();
+	  else m_errorBlock->clear();
+	  isErrBlock = true;
+	  payload = m_errorBlock->read(payload, payloadEnd);
+          if (m_errorBlock->crate() != crate) {
+	    if (debug) msg() << "Inconsistent crate number in error block"
+	                     << endreq;
+	    rodErr = L1CaloSubBlock::ERROR_CRATE_NUMBER;
+	    break;
+          }
+          if (m_errorBlock->module() != module) {
+	    if (debug) msg() << "Inconsistent module number in error block"
+	                     << endreq;
+	    rodErr = L1CaloSubBlock::ERROR_MODULE_NUMBER;
+	    break;
+          }
+	  if (m_errorBlock->dataWords() && !m_errorBlock->unpack()) {
+	    if (debug) {
+	      std::string errMsg(m_errorBlock->unpackErrorMsg());
+	      msg() << "Unpacking error block failed: " << errMsg << endreq;
+	    }
+	    rodErr = m_errorBlock->unpackErrorCode();
+	    break;
+	  }
+        }
+      }
+
+      // Don't bother unpacking modules that aren't used for required collection
+
+      const int index2 = (crate<<4) + module;
+      const int word2  = index2/32;
+      const int bit2   = index2%32;
+      if (!((colMod[word2]>>bit2)&1)) continue;
+
+      // Loop over sub-blocks and fill trigger towers
+
+      for (int block = 0; block < nPpmBlocks; ++block) {
+        PpmSubBlock* const subBlock = m_ppmBlocks[block];
+	subBlock->setLutOffset(trigLut);
+	subBlock->setFadcOffset(trigFadc);
+	subBlock->setPedestal(m_pedestal);
+	subBlock->setFadcBaseline(m_fadcBaseline);
+	subBlock->setRunNumber(runNumber);
+	if (debug) {
+	  msg() << "Unpacking sub-block version/format/seqno: "
+	        << subBlock->version() << "/" << subBlock->format() << "/"
+	        << subBlock->seqno() << endreq;
+	}
+        if (subBlock->dataWords() && !subBlock->unpack()) {
+	  if (debug) {
+	    std::string errMsg(subBlock->unpackErrorMsg());
+	    msg() << "Unpacking PPM sub-block failed: " << errMsg << endreq;
+	  }
+	  rodErr = subBlock->unpackErrorCode();
+	  break;
+        }
+	if (m_printCompStats) addCompStats(subBlock->compStats());
+        for (int chan = 0; chan < chanPerSubBlock; ++chan) {
+          const int channel = block*chanPerSubBlock + chan;
+	  const int index = (crate<<10) + (module<<6) + channel;
+	  const int word  = index/32;
+	  const int bit   = index%32;
+	  if (!((colChan[word]>>bit)&1)) continue; // skip unwanted channels
+	  if (((m_foundChan[word]>>bit)&1)) {
+	    if (debug) msg() << "Duplicate data for crate/module/channel: "
+	                     << crate << "/" << module << "/" << channel
+			     << endreq;
+	    rodErr = L1CaloSubBlock::ERROR_DUPLICATE_DATA;
+	    break;
+          }
+	  lut.clear();
+	  fadc.clear();
+	  bcidLut.clear();
+	  bcidFadc.clear();
+	  subBlock->ppmData(channel, lut, fadc, bcidLut, bcidFadc);
+	  if (lut.size() < size_t(trigLut + 1)) {
+	    if (debug) {
+	      msg() << "Triggered LUT slice from header "
+	            << "inconsistent with number of slices: "
+		    << trigLut << ", " << lut.size() << endreq;
+	    }
+	    rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    break;
+          }
+	  if (fadc.size() < size_t(trigFadc + 1)) {
+	    if (debug) {
+	      msg() << "Triggered FADC slice from header "
+	            << "inconsistent with number of slices: "
+		    << trigFadc << ", " << fadc.size() << endreq;
+	    }
+	    rodErr = L1CaloSubBlock::ERROR_SLICES;
+	    break;
+          }
+	  LVL1::DataError errorBits(0);
+	  if (isErrBlock) {
+	    errorBits.set(LVL1::DataError::PPMErrorWord,
+	                             m_errorBlock->ppmError(channel));
+	    errorBits.set(LVL1::DataError::SubStatusWord,
+	                             m_errorBlock->subStatus());
+          } else {
+	    errorBits.set(LVL1::DataError::PPMErrorWord,
+	                             subBlock->ppmError(channel));
+	    const PpmSubBlock* const lastBlock =
+	                                      m_ppmBlocks[nPpmBlocks - 1];
+	    errorBits.set(LVL1::DataError::SubStatusWord,
+	                             lastBlock->subStatus());
+          }
+	  // Wrong bit set for compressed formats 1.01 to 1.03
+	  if (subBlock->format() > 1 && subBlock->seqno() < 4) {
+	    errorBits.set(LVL1::DataError::ModuleError,
+	         (errorBits.error() >> (LVL1::DataError::ModuleError+1)) & 0x1);
+	  }
+	  const int error = errorBits.error();
+
+	  // Save to TriggerTower
+
+	  if (verbose) {
+	    msg(MSG::VERBOSE) << "channel/LUT/FADC/bcidLUT/bcidFADC/error: "
+	                      << channel << "/";
+	    printVec(lut);
+	    printVec(fadc);
+	    printVec(bcidLut);
+	    printVec(bcidFadc);
+	    msg() << MSG::hex << error << MSG::dec << "/";
+          }
+	  m_foundChan[word] |= (1<<bit);
+	  ++ttCount;
+	  LVL1::TriggerTower* tt = ttColRef[m_ttPos[index]];
+	  const int layer = ((m_chanLayer[word]>>bit)&1);
+          if (layer == 0) {  // EM
+	    tt->addEM(fadc, lut, bcidFadc, bcidLut, error, trigLut, trigFadc);
+          } else {           // Had
+	    tt->addHad(fadc, lut, bcidFadc, bcidLut, error, trigLut, trigFadc);
+          }
+        }
+        if (rodErr != L1CaloSubBlock::ERROR_NONE) break;
+      }
+      if (rodErr != L1CaloSubBlock::ERROR_NONE) break;
+    }
+    if (rodErr != L1CaloSubBlock::ERROR_NONE)
+                                          m_errorTool->rodError(robid, rodErr);
+  }
+
+  // Reset any missing channels (should be rare)
+
+  if (ttCount != colSize) {
+    if (debug) {
+      msg() << "Found " << ttCount << " channels, expected " << colSize << endreq;
+    }
+    std::vector<int> dummy(1);
+    for (int word = 0; word < chanBitVecSize; ++word) {
+      if (m_foundChan[word] != colChan[word]) {
+        for (int bit = 0; bit < 32; ++bit) {
+	  if (((m_foundChan[word]>>bit)&1) != ((colChan[word]>>bit)&1)) {
+	    const int index = word*32 + bit;
+	    LVL1::TriggerTower* tt = ttColRef[m_ttPos[index]];
+	    const int layer = ((m_chanLayer[word]>>bit)&1);
+	    if (layer == 0) {            // EM
+	      tt->addEM(dummy, dummy, dummy, dummy, 0, 0, 0);
+            } else if (m_dataChannels) { // Had
+	      tt->addHad(dummy, dummy, dummy, dummy, 0, 0, 0);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // And copy into output collection
+
+  if (m_zeroSuppress) {
+    TriggerTowerVector::iterator itr  = ttCol.begin();
+    TriggerTowerVector::iterator itrE = ttCol.end();
+    for (; itr != itrE; ++itr) {
+      if ((*itr)->emEnergy() || (m_dataChannels && (*itr)->hadEnergy())) {
+        ttCollection->push_back(*itr);
+      }
+    }
+  } else {
+    ttCollection->assign(ttCol.begin(), ttCol.end());
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Conversion of trigger towers to bytestream
+
+StatusCode PpmByteStreamTool::convert(
+                      const DataVector<LVL1::TriggerTower>* const ttCollection,
+                            RawEventWrite* const re)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Clear the event assembler
+
+  m_fea->clear();
+  const uint16_t minorVersion = m_srcIdMap->minorVersionPreLS1();
+  m_fea->setRodMinorVersion(minorVersion);
+  m_rodStatusMap.clear();
+
+  // Pointer to ROD data vector
+
+  FullEventAssembler<L1CaloSrcIdMap>::RODDATA* theROD = 0;
+
+  // Set up trigger tower maps
+
+  setupTTMaps(ttCollection);
+
+  // Create the sub-blocks to do the packing
+
+  PpmSubBlock subBlock;
+  const int chanPerSubBlock = subBlock.channelsPerSubBlock(m_version,
+                                                               m_dataFormat);
+  if (chanPerSubBlock == 0) {
+    msg(MSG::ERROR) << "Unsupported version/data format: "
+                    << m_version << "/" << m_dataFormat << endreq;
+    return StatusCode::FAILURE;
+  }
+  PpmSubBlock errorBlock;
+
+  int slicesLut  = 1;
+  int slicesFadc = 1;
+  int trigLut    = 0;
+  int trigFadc   = 0;
+  int slicesLutNew  = 1;
+  int slicesFadcNew = 1;
+  int trigLutNew    = 0;
+  int trigFadcNew   = 0;
+  const int modulesPerSlink = s_modules / m_slinks;
+  for (int crate = m_crateMin; crate <= m_crateMax; ++crate) {
+    for (int module=0; module < s_modules; ++module) {
+
+      // Pack required number of modules per slink
+
+      if (module%modulesPerSlink == 0) {
+	const int daqOrRoi = 0;
+	const int slink = module/modulesPerSlink;
+        if (debug) {
+          msg() << "Treating crate " << crate << " slink " << slink << endreq;
+        }
+	// Get number of slices and triggered slice offsets
+	// for this slink
+	if ( ! slinkSlices(crate, module, modulesPerSlink,
+	                   slicesLut, slicesFadc, trigLut, trigFadc)) {
+	  msg(MSG::ERROR) << "Inconsistent number of slices or "
+	                  << "triggered slice offsets in data for crate "
+	                  << crate << " slink " << slink << endreq;
+	  return StatusCode::FAILURE;
+        }
+	slicesLutNew  = (m_forceSlicesLut)  ? m_forceSlicesLut  : slicesLut;
+	slicesFadcNew = (m_forceSlicesFadc) ? m_forceSlicesFadc : slicesFadc;
+	trigLutNew    = ModifySlices::peak(trigLut,  slicesLut,  slicesLutNew);
+	trigFadcNew   = ModifySlices::peak(trigFadc, slicesFadc, slicesFadcNew);
+        if (debug) {
+	  msg() << "Data Version/Format: " << m_version
+	        << " " << m_dataFormat << endreq
+                << "LUT slices/offset: " << slicesLut << " " << trigLut;
+          if (slicesLut != slicesLutNew) {
+	    msg() << " modified to " << slicesLutNew << " " << trigLutNew;
+          }
+	  msg() << endreq
+                << "FADC slices/offset: " << slicesFadc << " " << trigFadc;
+          if (slicesFadc != slicesFadcNew) {
+	    msg() << " modified to " << slicesFadcNew << " " << trigFadcNew;
+          }
+	  msg() << endreq;
+        }
+        L1CaloUserHeader userHeader;
+        userHeader.setPpmLut(trigLutNew);
+        userHeader.setPpmFadc(trigFadcNew);
+	userHeader.setLowerBound(m_fadcBaseline);
+	const uint32_t rodIdPpm = m_srcIdMap->getRodID(crate, slink, daqOrRoi,
+	                                                       m_subDetector);
+	theROD = m_fea->getRodData(rodIdPpm);
+	theROD->push_back(userHeader.header());
+	m_rodStatusMap.insert(make_pair(rodIdPpm, m_rodStatus));
+      }
+      if (debug) msg() << "Module " << module << endreq;
+
+      // Find trigger towers corresponding to each eta/phi pair and fill
+      // sub-blocks
+
+      bool upstreamError = false;
+      for (int channel=0; channel < s_channels; ++channel) {
+	const int chan = channel % chanPerSubBlock;
+        if (channel == 0 && m_dataFormat == L1CaloSubBlock::UNCOMPRESSED) {
+          errorBlock.clear();
+	  errorBlock.setPpmErrorHeader(m_version, m_dataFormat, crate,
+	                               module, slicesFadcNew, slicesLutNew);
+	}
+        if (chan == 0) {
+	  subBlock.clear();
+	  if (m_dataFormat >= L1CaloSubBlock::COMPRESSED) {
+	    subBlock.setPpmHeader(m_version, m_dataFormat, m_compVers, crate,
+	                          module, slicesFadcNew, slicesLutNew);
+          } else {
+	    subBlock.setPpmHeader(m_version, m_dataFormat, channel, crate,
+	                          module, slicesFadcNew, slicesLutNew);
+	  }
+	  subBlock.setLutOffset(trigLutNew);
+	  subBlock.setFadcOffset(trigFadcNew);
+	  subBlock.setFadcBaseline(m_fadcBaseline);
+	  subBlock.setFadcThreshold(m_fadcThreshold);
+        }
+        const LVL1::TriggerTower* tt = 0;
+	double eta = 0.;
+	double phi = 0.;
+	int layer = 0;
+	if (m_ppmMaps->mapping(crate, module, channel, eta, phi, layer)) {
+	  tt = findLayerTriggerTower(eta, phi, layer);
+        }
+	if (tt ) {
+	  int err = 0;
+	  std::vector<int> lut;
+	  std::vector<int> fadc;
+	  std::vector<int> bcidLut;
+	  std::vector<int> bcidFadc;
+	  if (layer == 0) {  // em
+	    ModifySlices::data(tt->emLUT(),     lut,      slicesLutNew);
+	    ModifySlices::data(tt->emADC(),     fadc,     slicesFadcNew);
+	    ModifySlices::data(tt->emBCIDvec(), bcidLut,  slicesLutNew);
+	    ModifySlices::data(tt->emBCIDext(), bcidFadc, slicesFadcNew);
+	    err = tt->emError();
+          } else {           // had
+	    ModifySlices::data(tt->hadLUT(),     lut,      slicesLutNew);
+	    ModifySlices::data(tt->hadADC(),     fadc,     slicesFadcNew);
+	    ModifySlices::data(tt->hadBCIDvec(), bcidLut,  slicesLutNew);
+	    ModifySlices::data(tt->hadBCIDext(), bcidFadc, slicesFadcNew);
+	    err = tt->hadError();
+          }
+	  subBlock.fillPpmData(channel, lut, fadc, bcidLut, bcidFadc);
+	  if (err) {
+	    const LVL1::DataError errorBits(err);
+	    const int errpp = errorBits.get(LVL1::DataError::PPMErrorWord);
+	    if (m_dataFormat == L1CaloSubBlock::UNCOMPRESSED) {
+	      errorBlock.fillPpmError(channel, errpp);
+	    } else subBlock.fillPpmError(channel, errpp);
+	    if (errpp >> 2) upstreamError = true;
+          }
+        }
+        if (chan == chanPerSubBlock - 1) {
+	  // output the packed sub-block
+	  if ( !subBlock.pack()) {
+	    msg(MSG::ERROR) << "PPM sub-block packing failed" << endreq;
+	    return StatusCode::FAILURE;
+	  }
+	  if (m_printCompStats) addCompStats(subBlock.compStats());
+	  if (channel != s_channels - 1) {
+	    // Only put errors in last sub-block
+	    subBlock.setStatus(0, false, false, false, false,
+	                                 false, false, false);
+	    if (debug) {
+	      msg() << "PPM sub-block data words: "
+	            << subBlock.dataWords() << endreq;
+	    }
+	    subBlock.write(theROD);
+	  } else {
+	    // Last sub-block - write error block
+	    bool glinkTimeout = false;
+	    bool daqOverflow  = false;
+	    bool bcnMismatch  = false;
+	    bool glinkParity  = false;
+	    if (m_dataFormat == L1CaloSubBlock::UNCOMPRESSED) {
+	      glinkTimeout = errorBlock.mcmAbsent() ||
+	                     errorBlock.timeout();
+	      daqOverflow  = errorBlock.asicFull() ||
+	                     errorBlock.fpgaCorrupt();
+	      bcnMismatch  = errorBlock.eventMismatch() ||
+	                     errorBlock.bunchMismatch();
+	      glinkParity  = errorBlock.glinkPinParity();
+	    } else {
+	      glinkTimeout = subBlock.mcmAbsent() ||
+	                     subBlock.timeout();
+	      daqOverflow  = subBlock.asicFull() ||
+	                     subBlock.fpgaCorrupt();
+	      bcnMismatch  = subBlock.eventMismatch() ||
+	                     subBlock.bunchMismatch();
+	      glinkParity  = subBlock.glinkPinParity();
+	    }
+	    subBlock.setStatus(0, glinkTimeout, false, upstreamError,
+	                       daqOverflow, bcnMismatch, false, glinkParity);
+	    if (debug) {
+	      msg() << "PPM sub-block data words: "
+	            << subBlock.dataWords() << endreq;
+	    }
+            subBlock.write(theROD);
+	    // Only uncompressed format has a separate error block
+	    if (m_dataFormat == L1CaloSubBlock::UNCOMPRESSED) {
+	      if ( ! errorBlock.pack()) {
+	        msg(MSG::ERROR) << "PPM error block packing failed" << endreq;
+	        return StatusCode::FAILURE;
+	      }
+	      errorBlock.setStatus(0, glinkTimeout, false, upstreamError, 
+	                       daqOverflow, bcnMismatch, false, glinkParity);
+	      errorBlock.write(theROD);
+	      if (debug) {
+	        msg() << "PPM error block data words: "
+	              << errorBlock.dataWords() << endreq;
+	      }
+	    }
+          }
+        }
+      }
+    }
+  }
+
+  // Fill the raw event
+
+  m_fea->fill(re, msg());
+
+  // Set ROD status words
+
+  //L1CaloRodStatus::setStatus(re, m_rodStatusMap, m_srcIdMap);
+
+  return StatusCode::SUCCESS;
+}
+
+// Add compression stats to totals
+
+void PpmByteStreamTool::addCompStats(const std::vector<uint32_t>& stats)
+{
+  if (stats.empty()) return;
+  const int n = stats.size();
+  if (m_compStats.empty()) m_compStats.resize(n);
+  for (int i = 0; i < n; ++i) m_compStats[i] += stats[i];
+}
+
+// Print compression stats
+
+void PpmByteStreamTool::printCompStats() const
+{
+  msg() << "Compression stats format/count: ";
+  const int n = m_compStats.size();
+  for (int i = 0; i < n; ++i) {
+    msg() << " " << i << "/" << m_compStats[i];
+  }
+  msg() << endreq;
+}
+
+// Find a trigger tower using separate layer maps
+
+const LVL1::TriggerTower* PpmByteStreamTool::findLayerTriggerTower(
+            const double eta, const double phi, const int layer)
+{
+  const LVL1::TriggerTower* tt = 0;
+  const unsigned int key = m_towerKey->ttKey(phi, eta);
+  TriggerTowerMapConst::const_iterator mapIter;
+  if (layer == 0) {
+    mapIter = m_ttEmMap.find(key);
+    if (mapIter != m_ttEmMap.end()) tt = mapIter->second;
+  } else {
+    mapIter = m_ttHadMap.find(key);
+    if (mapIter != m_ttHadMap.end()) tt = mapIter->second;
+  }
+  return tt;
+}
+
+// Set up trigger tower maps
+
+void PpmByteStreamTool::setupTTMaps(const TriggerTowerCollection*
+                                                           const ttCollection)
+{
+  using std::accumulate;
+
+  m_ttEmMap.clear();
+  m_ttHadMap.clear();
+  TriggerTowerCollection::const_iterator pos  = ttCollection->begin();
+  TriggerTowerCollection::const_iterator pose = ttCollection->end();
+  for (; pos != pose; ++pos) {
+    const LVL1::TriggerTower* tt = *pos;
+    const unsigned int key = m_towerKey->ttKey(tt->phi(), tt->eta());
+    // Ignore any with zero data
+    // EM
+    if (accumulate((tt->emLUT()).begin(),     (tt->emLUT()).end(),     0) ||
+        accumulate((tt->emADC()).begin(),     (tt->emADC()).end(),     0) ||
+	accumulate((tt->emBCIDvec()).begin(), (tt->emBCIDvec()).end(), 0) ||
+	accumulate((tt->emBCIDext()).begin(), (tt->emBCIDext()).end(), 0) ||
+	tt->emError()) {
+      m_ttEmMap.insert(std::make_pair(key, tt));
+    }
+    // Had
+    if (accumulate((tt->hadLUT()).begin(),     (tt->hadLUT()).end(),     0) ||
+        accumulate((tt->hadADC()).begin(),     (tt->hadADC()).end(),     0) ||
+	accumulate((tt->hadBCIDvec()).begin(), (tt->hadBCIDvec()).end(), 0) ||
+	accumulate((tt->hadBCIDext()).begin(), (tt->hadBCIDext()).end(), 0) ||
+	tt->hadError()) {
+      m_ttHadMap.insert(std::make_pair(key, tt));
+    }
+  }
+}
+
+// Get number of slices and triggered slice offsets for next slink
+
+bool PpmByteStreamTool::slinkSlices(const int crate, const int module,
+                  const int modulesPerSlink, int& slicesLut, int& slicesFadc,
+		  int& trigLut, int& trigFadc)
+{
+  int sliceL = -1;
+  int sliceF =  m_dfltSlicesFadc;
+  int trigL  =  m_dfltSlicesLut/2;
+  int trigF  =  m_dfltSlicesFadc/2;
+  for (int mod = module; mod < module + modulesPerSlink; ++mod) {
+    for (int chan = 0; chan < s_channels; ++chan) {
+      double eta = 0.;
+      double phi = 0.;
+      int layer = 0;
+      if (!m_ppmMaps->mapping(crate, mod, chan,
+                                     eta, phi, layer)) continue;
+      const LVL1::TriggerTower* const tt = findLayerTriggerTower(
+                                                eta, phi, layer);
+      if ( !tt ) continue;
+      if (layer == 0) {
+        if (sliceL < 0) { // initialise
+	  sliceL = (tt->emLUT()).size();
+	  sliceF = (tt->emADC()).size();
+	  trigL  = tt->emPeak();
+	  trigF  = tt->emADCPeak();
+        } else { // check consistent
+	  if ((tt->emLUT()).size() != size_t(sliceL) ||
+	      (tt->emADC()).size() != size_t(sliceF) ||
+	      tt->emPeak() != trigL || tt->emADCPeak() != trigF) {
+            return false;
+          }
+        }
+      } else {
+        if (sliceL < 0) {
+	  sliceL = (tt->hadLUT()).size();
+	  sliceF = (tt->hadADC()).size();
+	  trigL  = tt->hadPeak();
+	  trigF  = tt->hadADCPeak();
+        } else {
+	  if ((tt->hadLUT()).size() != size_t(sliceL) ||
+	      (tt->hadADC()).size() != size_t(sliceF) ||
+	      tt->hadPeak() != trigL || tt->hadADCPeak() != trigF) {
+            return false;
+          }
+        }
+      }
+    }
+  }
+  if (sliceL < 0) sliceL = m_dfltSlicesLut;
+  slicesLut  = sliceL;
+  slicesFadc = sliceF;
+  trigLut    = trigL;
+  trigFadc   = trigF;
+  return true;
+}
+
+// Return reference to vector with all possible Source Identifiers
+
+const std::vector<uint32_t>& PpmByteStreamTool::sourceIDs(
+                                                 const std::string& sgKey)
+{
+  // Check if spare channels wanted
+  const std::string flag("Spare");
+  const std::string::size_type pos = sgKey.find(flag);
+  m_spareChannels =
+    (pos != std::string::npos && pos == (sgKey.length() - flag.length()));
+  // Check if Tile Muon channels wanted
+  const std::string flag2("Muon");
+  const std::string::size_type pos2 = sgKey.find(flag2);
+  m_muonChannels =
+    (pos2 != std::string::npos && pos2 == (sgKey.length() - flag2.length()));
+  m_dataChannels = (!m_spareChannels && !m_muonChannels);
+  if (m_sourceIDs.empty()) {
+    const int maxlinks = m_srcIdMap->maxSlinks();
+    for (int crate = 0; crate < s_crates; ++crate) {
+      for (int slink = 0; slink < maxlinks; ++slink) {
+        const int daqOrRoi = 0;
+        const uint32_t rodId = m_srcIdMap->getRodID(crate, slink, daqOrRoi,
+                                                            m_subDetector);
+        const uint32_t robId = m_srcIdMap->getRobID(rodId);
+        m_sourceIDs.push_back(robId);
+	if (crate > 1 && crate < 6) {
+	  m_sourceIDsSpare.push_back(robId);
+	  if (crate < 4 && slink == 0) m_sourceIDsMuon.push_back(robId);
+        }
+      }
+    }
+  }
+  if (m_spareChannels) return m_sourceIDsSpare;
+  if (m_muonChannels)  return m_sourceIDsMuon;
+  return m_sourceIDs;
+}
+
+// Print a vector
+
+void PpmByteStreamTool::printVec(const std::vector<int>& vec) const
+{
+  std::vector<int>::const_iterator pos;
+  for (pos = vec.begin(); pos != vec.end(); ++pos) {
+    if (pos != vec.begin()) msg() << ",";
+    msg() << *pos;
+  }
+  msg() << "/";
+}
+
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmByteStreamTool.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmByteStreamTool.h
new file mode 100755
index 0000000000000000000000000000000000000000..1c34d0a2b798efb50ebc33270454711daa3f19d6
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmByteStreamTool.h
@@ -0,0 +1,198 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_PPMBYTESTREAMTOOL_H
+#define TRIGT1CALOBYTESTREAM_PPMBYTESTREAMTOOL_H
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+#include "ByteStreamData/RawEvent.h"
+#include "DataModel/DataVector.h"
+#include "eformat/SourceIdentifier.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class IInterface;
+class InterfaceID;
+class StatusCode;
+class SegMemSvc;
+
+template <class T> class FullEventAssembler;
+
+namespace LVL1 {
+  class IL1CaloMappingTool;
+  class TriggerTower;
+  class TriggerTowerKey;
+}
+
+namespace LVL1BS {
+
+class L1CaloErrorByteStreamTool;
+class L1CaloSrcIdMap;
+class PpmSubBlock;
+
+/** Tool to perform ROB fragments to trigger towers and trigger towers
+ *  to raw data conversions.
+ *
+ *  Based on ROD document version 1_09h.
+ *
+ *  @author Peter Faulkner
+ */
+
+class PpmByteStreamTool : public AthAlgTool {
+
+ public:
+   PpmByteStreamTool(const std::string& type, const std::string& name,
+                     const IInterface* parent);
+   virtual ~PpmByteStreamTool();
+
+   /// AlgTool InterfaceID
+   static const InterfaceID& interfaceID();
+
+   virtual StatusCode initialize();
+   virtual StatusCode finalize();
+
+   /// Convert ROB fragments to trigger towers
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::TriggerTower>* ttCollection);
+
+   /// Convert trigger towers to bytestream
+   StatusCode convert(const DataVector<LVL1::TriggerTower>* ttCollection,
+                      RawEventWrite* re);
+
+   /// Return reference to vector with all possible Source Identifiers
+   const std::vector<uint32_t>& sourceIDs(const std::string& sgKey);
+
+ private:
+
+   typedef DataVector<LVL1::TriggerTower>                TriggerTowerCollection;
+   typedef std::vector<LVL1::TriggerTower*>              TriggerTowerVector;
+   typedef std::map<unsigned int, int>                   TriggerTowerMap;
+   typedef std::map<unsigned int, const LVL1::TriggerTower*>
+                                                         TriggerTowerMapConst;
+   typedef std::vector<uint32_t>                         ChannelBitVector;
+   typedef IROBDataProviderSvc::VROBFRAG::const_iterator ROBIterator;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      ROBPointer;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      RODPointer;
+
+   /// Add compression stats to totals
+   void addCompStats(const std::vector<uint32_t>& stats);
+   /// Print compression stats
+   void printCompStats() const;
+
+   /// Find a trigger tower using separate layer maps
+   const LVL1::TriggerTower* findLayerTriggerTower(double eta, double phi,
+                                                               int layer);
+   /// Set up separate Em and Had trigger tower maps
+   void setupTTMaps(const TriggerTowerCollection* ttCollection);
+
+   /// Get number of slices and triggered slice offsets for next slink
+   bool slinkSlices(int crate, int module, int modulesPerSlink,
+        int& slicesLut, int& slicesFadc, int& trigLut, int& trigFadc);
+
+   /// Print a vector
+   void printVec(const std::vector<int>& vec) const;
+
+   /// Channel mapping tool
+   ToolHandle<LVL1::IL1CaloMappingTool> m_ppmMaps;
+   /// Error collection tool
+   ToolHandle<LVL1BS::L1CaloErrorByteStreamTool> m_errorTool;
+   /// Memory pool service
+   ServiceHandle<SegMemSvc> m_sms;
+
+   /// Sub_block header version
+   int m_version;
+   /// Data compression format
+   int m_dataFormat;
+   /// Compression version
+   int m_compVers;
+   /// Compression statistics print flag
+   int m_printCompStats;
+   /// Number of slinks per crate when writing out bytestream
+   int m_slinks;
+   /// Default number of LUT slices in simulation
+   int m_dfltSlicesLut;
+   /// Default number of FADC slices in simulation
+   int m_dfltSlicesFadc;
+   /// Force number of LUT slices in bytestream
+   int m_forceSlicesLut;
+   /// Force number of FADC slices in bytestream
+   int m_forceSlicesFadc;
+   /// Minimum crate number when writing out bytestream
+   int m_crateMin;
+   /// Maximum crate number when writing out bytestream
+   int m_crateMax;
+   /// Pedestal value
+   int m_pedestal;
+   /// FADC baseline lower bound
+   int m_fadcBaseline;
+   /// FADC threshold for super-compressed format
+   int m_fadcThreshold;
+   /// Zero suppression on input
+   int m_zeroSuppress;
+   /// Data channel flag
+   bool m_dataChannels;
+   /// Spare channel flag
+   bool m_spareChannels;
+   /// Tile Muon channel flag
+   bool m_muonChannels;
+   /// ROB source IDs
+   std::vector<uint32_t> m_sourceIDs;
+   std::vector<uint32_t> m_sourceIDsSpare;
+   std::vector<uint32_t> m_sourceIDsMuon;
+   /// Sub-detector type
+   eformat::SubDetector m_subDetector;
+   /// Source ID converter
+   L1CaloSrcIdMap* m_srcIdMap;
+   /// Trigger tower key provider
+   LVL1::TriggerTowerKey* m_towerKey;
+   /// Current error block
+   PpmSubBlock* m_errorBlock;
+   /// Vector for current PPM sub-blocks
+   DataVector<PpmSubBlock> m_ppmBlocks;
+   /// Vector for compression statistics
+   std::vector<uint32_t> m_compStats;
+   /// Trigger tower map for conversion from bytestream
+   TriggerTowerMap m_ttMap;
+   /// Trigger tower map for conversion EM to bytestream
+   TriggerTowerMapConst m_ttEmMap;
+   /// Trigger tower map for conversion Had to bytestream
+   TriggerTowerMapConst m_ttHadMap;
+   /// ROD Status words
+   std::vector<uint32_t>* m_rodStatus;
+   /// ROD status map
+   std::map<uint32_t, std::vector<uint32_t>* > m_rodStatusMap;
+   /// Event assembler
+   FullEventAssembler<L1CaloSrcIdMap>* m_fea;
+   /// TriggerTower pool vectors
+   TriggerTowerVector m_ttData;
+   TriggerTowerVector m_ttSpare;
+   TriggerTowerVector m_ttMuon;
+   std::vector<int> m_ttPos;
+   /// Mapping vectors
+   ChannelBitVector m_chanLayer;
+   ChannelBitVector m_dataChan;
+   ChannelBitVector m_spareChan;
+   ChannelBitVector m_muonChan;
+   ChannelBitVector m_dataMod;
+   ChannelBitVector m_spareMod;
+   ChannelBitVector m_muonMod;
+   ChannelBitVector m_foundChan;
+
+   static const int s_crates   = 8;
+   static const int s_modules  = 16;
+   static const int s_channels = 64;
+   static const int s_dataSize = 3584;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmCompression.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmCompression.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..1c1bb8f4bd2842815a33ffd3621e0e9a136ef923
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmCompression.cxx
@@ -0,0 +1,659 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <stdint.h>
+#include <algorithm>
+#include <vector>
+
+#include "L1CaloSubBlock.h"
+#include "PpmCompression.h"
+#include "PpmSubBlock.h"
+
+namespace LVL1BS {
+
+// Static constants
+
+const int PpmCompression::s_formatsV0;
+const int PpmCompression::s_lowerRange;
+const int PpmCompression::s_upperRange;
+const int PpmCompression::s_formats;
+const int PpmCompression::s_fadcRange;
+const int PpmCompression::s_peakOnly;
+const int PpmCompression::s_lutDataBits;
+const int PpmCompression::s_lutBcidBits;
+const int PpmCompression::s_fadcDataBits;
+const int PpmCompression::s_glinkPins;
+const int PpmCompression::s_statusBits;
+const int PpmCompression::s_errorBits;
+const int PpmCompression::s_statusMask;
+
+// Pack data
+
+bool PpmCompression::pack(PpmSubBlock& subBlock)
+{
+  const int dataFormat = subBlock.format();
+  if (dataFormat != L1CaloSubBlock::COMPRESSED &&
+      dataFormat != L1CaloSubBlock::SUPERCOMPRESSED) return false;
+  const int sliceL = subBlock.slicesLut();
+  const int sliceF = subBlock.slicesFadc();
+  if (sliceL != 1 || sliceF != 5) return false;
+  const int trigOffset    = subBlock.fadcOffset() - subBlock.lutOffset();
+  const int fadcBaseline  = subBlock.fadcBaseline();
+  const int fadcThreshold = subBlock.fadcThreshold();
+  const int channels      = subBlock.channelsPerSubBlock();
+  subBlock.setStreamed();
+  std::vector<uint32_t> compStats(s_formats);
+  std::vector<int>      fadcDout(sliceF-1);
+  std::vector<int>      fadcLens(sliceF-1);
+  for (int chan = 0; chan < channels; ++chan) {
+    std::vector<int> lutData;
+    std::vector<int> lutBcid;
+    std::vector<int> fadcData;
+    std::vector<int> fadcBcid;
+    subBlock.ppmData(chan, lutData, fadcData, lutBcid, fadcBcid);
+    if (dataFormat == L1CaloSubBlock::SUPERCOMPRESSED) {
+      int dataPresent = lutData[0] || lutBcid[0];
+      if ( !dataPresent ) {
+        for (int sl = 0; sl < sliceF; ++sl) {
+	  if (fadcData[sl] >= fadcThreshold || fadcBcid[sl]) {
+	    dataPresent = 1;
+	    break;
+          }
+        }
+      }
+      subBlock.packer(dataPresent, 1);
+      if ( !dataPresent ) continue;
+    }
+    const int lutLen = subBlock.minBits(lutData[0]);
+    int  minFadc   = 0;
+    int  minOffset = 0;
+    bool fadcSame  = true;
+    for (int sl = 0; sl < sliceF; ++sl) {
+      if (sl == 0) minFadc = fadcData[sl];
+      if (fadcData[sl] < minFadc) {
+        minFadc   = fadcData[sl];
+	minOffset = sl;
+      }
+      if (fadcData[sl] != fadcData[0] || fadcBcid[sl] != 0) fadcSame = false;
+    }
+    if (minOffset) std::swap(fadcData[0], fadcData[minOffset]);
+
+    int format = 0;
+    if (lutData[0] == 0 && lutBcid[0] == 0 && fadcSame) { // format 6
+      const int header = 15;
+      subBlock.packer(header, 4);
+      if (fadcData[0]) {
+        subBlock.packer(1, 1);
+	subBlock.packer(fadcData[0], s_fadcDataBits);
+      } else subBlock.packer(0, 1);
+      format = 6;
+    } else {
+      const bool minFadcInRange = minFadc >= fadcBaseline &&
+                                  minFadc <= fadcBaseline + s_fadcRange;
+      int  anyFadcBcid = 0;
+      int  maxFadcLen = 0;
+      int  idx = 0;
+      for (int sl = 0; sl < sliceF; ++sl) {
+        if (sl != 0) {
+	  fadcDout[idx] = fadcData[sl] - minFadc;
+	  fadcLens[idx] = subBlock.minBits(fadcDout[idx]);
+	  if (idx == 0 || fadcLens[idx] > maxFadcLen) {
+	    maxFadcLen = fadcLens[idx];
+	  }
+	  ++idx;
+        }
+	if (sl != trigOffset) anyFadcBcid |= fadcBcid[sl];
+	else if (fadcBcid[sl] != (lutBcid[0] & 0x1)) anyFadcBcid |= 1;
+      }
+      if (lutData[0] == 0 && lutBcid[0] == 0 &&
+                          !anyFadcBcid && minFadcInRange && maxFadcLen < 4) {
+        // formats 0,1
+        int header = minOffset;
+	if (maxFadcLen == 3) header += 5;
+	subBlock.packer(header, 4);
+	minFadc -= fadcBaseline;
+	subBlock.packer(minFadc, 4);
+	if (maxFadcLen < 2) maxFadcLen = 2;
+	for (int idx = 0; idx < sliceF-1; ++idx) {
+	  subBlock.packer(fadcDout[idx], maxFadcLen);
+        }
+	format = maxFadcLen - 2;
+      } else if (lutLen <= 3 && ((lutData[0] == 0 && lutBcid[0] == 0) ||
+                                 (lutData[0]  > 0 && lutBcid[0] == s_peakOnly))
+                 && !anyFadcBcid && minFadcInRange && maxFadcLen <= 4) {
+        // format 2
+	const int header = minOffset + 10;
+	subBlock.packer(header, 4);
+	format = 2;
+	subBlock.packer(format - 2, 2);
+        if (lutData[0]) {
+	  subBlock.packer(1, 1);
+	  subBlock.packer(lutData[0], 3);
+	} else subBlock.packer(0, 1);
+	minFadc -= fadcBaseline;
+	subBlock.packer(minFadc, 4);
+	for (int idx = 0; idx < sliceF-1; ++idx) {
+	  subBlock.packer(fadcDout[idx], 4);
+        }
+      } else {
+        // formats 3,4,5
+	const int header = minOffset + 10;
+	subBlock.packer(header, 4);
+	if ( !minFadcInRange) {
+	  const int minFadcLen = subBlock.minBits(minFadc);
+	  if (minFadcLen > maxFadcLen) maxFadcLen = minFadcLen;
+	}
+	format = 5;
+	if (maxFadcLen <= 8) format = 4;
+	if (maxFadcLen <= 6) format = 3;
+        subBlock.packer(format - 2, 2);
+	if (lutData[0] || lutBcid[0]) subBlock.packer(1, 1);
+	else subBlock.packer(0, 1);
+	subBlock.packer(anyFadcBcid, 1);
+	if (lutData[0] || lutBcid[0]) {
+	  subBlock.packer(lutData[0], s_lutDataBits);
+	  subBlock.packer(lutBcid[0], s_lutBcidBits);
+	}
+	if (anyFadcBcid) {
+	  for (int idx = 0; idx < sliceF; ++idx) {
+	    subBlock.packer(fadcBcid[idx], 1);
+          }
+        }
+	if (minFadcInRange) {
+	  subBlock.packer(0, 1);
+	  minFadc -= fadcBaseline;
+	  subBlock.packer(minFadc, 4);
+        } else {
+	  subBlock.packer(1, 1);
+          subBlock.packer(minFadc, format * 2);
+        }
+	for (int idx = 0; idx < sliceF-1; ++idx) {
+	  if (fadcLens[idx] <= 4) {
+	    subBlock.packer(0, 1);
+	    subBlock.packer(fadcDout[idx], 4);
+          } else {
+	    subBlock.packer(1, 1);
+	    subBlock.packer(fadcDout[idx], format * 2);
+          }
+        }
+      }
+    }
+    ++compStats[format];
+  }
+  // Errors
+  std::vector<int> status(s_glinkPins);
+  std::vector<int> error(s_glinkPins);
+  int statusBit = 0;
+  int errorBit  = 0;
+  for (int pin = 0; pin < s_glinkPins; ++pin) {
+    const int errorWord = subBlock.ppmPinError(pin);
+    status[pin] = errorWord &  s_statusMask;
+    error[pin]  = errorWord >> s_statusBits;
+    if (status[pin]) statusBit = 1;
+    if (error[pin])  errorBit  = 1;
+  }
+  subBlock.packer(statusBit, 1);
+  subBlock.packer(errorBit,  1);
+  if (statusBit || errorBit) {
+    for (int pin = 0; pin < s_glinkPins; ++pin) {
+      if (status[pin] || error[pin]) subBlock.packer(1, 1);
+      else subBlock.packer(0, 1);
+    }
+    for (int pin = 0; pin < s_glinkPins; ++pin) {
+      if (status[pin] || error[pin]) {
+        if (statusBit) subBlock.packer(status[pin], s_statusBits);
+	if (errorBit)  subBlock.packer(error[pin],  s_errorBits);
+      }
+    }
+  }
+  subBlock.packerFlush();
+  subBlock.setCompStats(compStats);
+  return true;
+}
+
+// Unpack data
+
+bool PpmCompression::unpack(PpmSubBlock& subBlock)
+{
+  bool rc = false;
+  switch (subBlock.seqno()) {
+    case 0:
+      rc = unpackV100(subBlock);
+      break;
+    case 1:
+      rc = unpackV101(subBlock);
+      break;
+    case 2:
+    case 3:
+    case 4:
+    case 5: // runs 88701-24 only
+      rc = unpackV104(subBlock);
+      break;
+    default:
+      subBlock.setUnpackErrorCode(L1CaloSubBlock::UNPACK_COMPRESSION_VERSION);
+      break;
+  }
+  return rc;
+}
+
+// Unpack data - version 1.00
+
+bool PpmCompression::unpackV100(PpmSubBlock& subBlock)
+{
+  if (subBlock.format() != L1CaloSubBlock::COMPRESSED) {
+    subBlock.setUnpackErrorCode(L1CaloSubBlock::UNPACK_FORMAT);
+    return false;
+  }
+  const int sliceL = subBlock.slicesLut();
+  const int sliceF = subBlock.slicesFadc();
+  if (sliceL != 1 || sliceF != 5) {
+    subBlock.setUnpackErrorCode(L1CaloSubBlock::UNPACK_COMPRESSION_SLICES);
+    return false;
+  }
+  const int trigOffset = subBlock.fadcOffset();
+  const int pedestal   = subBlock.pedestal();
+  const int channels   = subBlock.channelsPerSubBlock();
+  subBlock.setStreamed();
+  subBlock.unpackerInit();
+  std::vector<uint32_t> compStats(s_formatsV0);
+  std::vector<int> lutData;
+  std::vector<int> lutBcid;
+  std::vector<int> fadcData;
+  std::vector<int> fadcBcid;
+  for (int chan = 0; chan < channels; ++chan) {
+    lutData.clear();
+    lutBcid.clear();
+    fadcData.clear();
+    fadcBcid.clear();
+    int format = 0;
+    const int header = subBlock.unpacker(4);
+    if (header < 10) {
+      // formats 0,1 - LUT zero, FADC around pedestal
+      const int minOffset = header % 5;
+                format    = header / 5;
+      // LUT = 0
+      lutData.push_back(0);
+      lutBcid.push_back(0);
+      // FADC
+      const uint32_t minFadc = subBlock.unpacker(4) + pedestal - s_lowerRange;
+      for (int sl = 0; sl < sliceF; ++sl) {
+        if (sl == minOffset) fadcData.push_back(minFadc);
+	else fadcData.push_back(subBlock.unpacker(format + 2) + minFadc);
+	fadcBcid.push_back(0);
+      }
+    } else {
+      // formats 2-5
+      const int minOffset = header - 10;
+                format = subBlock.unpacker(2) + 2;
+      const int anyLut = subBlock.unpacker(1);
+      int lut  = 0;
+      int bcid = 0;
+      if (format == 2) {
+        // LUT
+	if (anyLut) {
+	  lut  = subBlock.unpacker(3);
+	  bcid = s_peakOnly;  // just peak-finding BCID set
+        }
+	lutData.push_back(lut);
+	lutBcid.push_back(bcid);
+	// FADC as formats 0,1
+        const int minFadc = subBlock.unpacker(4) + pedestal - s_lowerRange;
+        for (int sl = 0; sl < sliceF; ++sl) {
+          if (sl == minOffset) fadcData.push_back(minFadc);
+	  else fadcData.push_back(subBlock.unpacker(format + 2) + minFadc);
+	  fadcBcid.push_back(0);
+        }
+      } else {
+        // formats 3,4,5 - full LUT word, variable FADC
+	const int anyBcid = subBlock.unpacker(1);
+	// LUT
+	if (anyLut) {
+	  lut  = subBlock.unpacker(s_lutDataBits);
+	  bcid = subBlock.unpacker(s_lutBcidBits);
+	}
+        lutData.push_back(lut);
+	lutBcid.push_back(bcid);
+	// FADC
+	for (int sl = 0; sl < sliceF; ++sl) {
+	  int fbcid = 0;
+	  if (sl == trigOffset) fbcid = bcid & 0x1; // take from LUT word
+	  else if (anyBcid) fbcid = subBlock.unpacker(1);
+	  fadcBcid.push_back(fbcid);
+        }
+	int minFadc = 0;
+	if (subBlock.unpacker(1)) minFadc = subBlock.unpacker(format * 2);
+	else minFadc = subBlock.unpacker(4) + pedestal - s_lowerRange;
+	for (int sl = 0; sl < sliceF; ++sl) {
+	  int fadc = minFadc;
+	  if (sl != minOffset) {
+	    if (subBlock.unpacker(1)) fadc += subBlock.unpacker(format * 2);
+	    else                      fadc += subBlock.unpacker(4);
+          }
+	  fadcData.push_back(fadc);
+        }
+      }
+    }
+    subBlock.fillPpmData(chan, lutData, fadcData, lutBcid, fadcBcid);
+    ++compStats[format];
+  }
+  subBlock.setCompStats(compStats);
+  const bool rc = subBlock.unpackerSuccess();
+  if (!rc) subBlock.setUnpackErrorCode(L1CaloSubBlock::UNPACK_DATA_TRUNCATED);
+  return rc;
+}
+
+// Unpack data - version 1.01
+
+bool PpmCompression::unpackV101(PpmSubBlock& subBlock)
+{
+  if (subBlock.format() != L1CaloSubBlock::COMPRESSED) {
+    subBlock.setUnpackErrorCode(L1CaloSubBlock::UNPACK_FORMAT);
+    return false;
+  }
+  const int sliceL = subBlock.slicesLut();
+  const int sliceF = subBlock.slicesFadc();
+  if (sliceL != 1 || sliceF != 5) {
+    subBlock.setUnpackErrorCode(L1CaloSubBlock::UNPACK_COMPRESSION_SLICES);
+    return false;
+  }
+  const int trigOffset = subBlock.fadcOffset();
+  const int pedestal   = subBlock.pedestal();
+  const int channels   = subBlock.channelsPerSubBlock();
+  subBlock.setStreamed();
+  subBlock.unpackerInit();
+  std::vector<uint32_t> compStats(s_formats);
+  std::vector<int> lutData;
+  std::vector<int> lutBcid;
+  std::vector<int> fadcData;
+  std::vector<int> fadcBcid;
+  for (int chan = 0; chan < channels; ++chan) {
+    lutData.clear();
+    lutBcid.clear();
+    fadcData.clear();
+    fadcBcid.clear();
+    int format = 0;
+    const int header = subBlock.unpacker(4);
+    if (header < 10) {
+      // formats 0,1 - LUT zero, FADC around pedestal
+      const int minOffset = header % 5;
+                format    = header / 5;
+      // LUT = 0
+      lutData.push_back(0);
+      lutBcid.push_back(0);
+      // FADC
+      const int minFadc = subBlock.unpacker(4) + pedestal - s_lowerRange;
+      fadcData.push_back(minFadc);
+      fadcBcid.push_back(0);
+      for (int sl = 1; sl < sliceF; ++sl) {
+	fadcData.push_back(subBlock.unpacker(format + 2) + minFadc);
+        fadcBcid.push_back(0);
+      }
+      if (minOffset) std::swap(fadcData[0], fadcData[minOffset]);
+    } else if (header < 15) {
+      // formats 2-5
+      const int minOffset = header - 10;
+                format = subBlock.unpacker(2) + 2;
+      const int anyLut = subBlock.unpacker(1);
+      int lut  = 0;
+      int bcid = 0;
+      if (format == 2) {
+        // LUT
+	if (anyLut) {
+	  lut  = subBlock.unpacker(3);
+	  bcid = s_peakOnly;  // just peak-finding BCID set
+        }
+	lutData.push_back(lut);
+	lutBcid.push_back(bcid);
+	// FADC as formats 0,1
+        const int minFadc = subBlock.unpacker(4) + pedestal - s_lowerRange;
+        fadcData.push_back(minFadc);
+        fadcBcid.push_back(0);
+        for (int sl = 1; sl < sliceF; ++sl) {
+	  fadcData.push_back(subBlock.unpacker(format + 2) + minFadc);
+          fadcBcid.push_back(0);
+        }
+        if (minOffset) std::swap(fadcData[0], fadcData[minOffset]);
+      } else {
+        // formats 3,4,5 - full LUT word, variable FADC
+	const int anyBcid = subBlock.unpacker(1);
+	// LUT
+	if (anyLut) {
+	  lut  = subBlock.unpacker(s_lutDataBits);
+	  bcid = subBlock.unpacker(s_lutBcidBits);
+	}
+        lutData.push_back(lut);
+	lutBcid.push_back(bcid);
+	// FADC
+	for (int sl = 0; sl < sliceF; ++sl) {
+	  int fbcid = 0;
+	  if (sl == trigOffset) fbcid = bcid & 0x1; // take from LUT word
+	  else if (anyBcid) fbcid = subBlock.unpacker(1);
+	  fadcBcid.push_back(fbcid);
+        }
+	int minFadc = 0;
+	if (subBlock.unpacker(1)) minFadc = subBlock.unpacker(format * 2);
+	else minFadc = subBlock.unpacker(4) + pedestal - s_lowerRange;
+        fadcData.push_back(minFadc);
+	for (int sl = 1; sl < sliceF; ++sl) {
+	  int len = 4;
+	  if (subBlock.unpacker(1)) len = format * 2;
+	  fadcData.push_back(subBlock.unpacker(len) + minFadc);
+        }
+	if (minOffset) std::swap(fadcData[0], fadcData[minOffset]);
+      }
+    } else {
+      // format 6 - LUT zero, FADC all equal
+      format = 6;
+      // LUT
+      lutData.push_back(0);
+      lutBcid.push_back(0);
+      // FADC
+      int fadc = 0;
+      if (subBlock.unpacker(1)) fadc = subBlock.unpacker(s_fadcDataBits);
+      for (int sl = 0; sl < sliceF; ++sl) {
+        fadcData.push_back(fadc);
+        fadcBcid.push_back(0);
+      }
+    }
+    subBlock.fillPpmData(chan, lutData, fadcData, lutBcid, fadcBcid);
+    ++compStats[format];
+  }
+  // Errors
+  const int statusBit = subBlock.unpacker(1);
+  const int errorBit  = subBlock.unpacker(1);
+  if (statusBit || errorBit) {
+    std::vector<int> err(s_glinkPins);
+    for (int pin = 0; pin < s_glinkPins; ++pin) {
+      err[pin] = subBlock.unpacker(1);
+    }
+    for (int pin = 0; pin < s_glinkPins; ++pin) {
+      if (err[pin]) {
+        int status = 0;
+	int error  = 0;
+	if (statusBit) status = subBlock.unpacker(s_statusBits-1);
+	if (errorBit)  error  = subBlock.unpacker(s_errorBits+1);
+	subBlock.fillPpmPinError(pin, (error << (s_statusBits-1)) | status);
+      }
+    }
+  }
+  subBlock.setCompStats(compStats);
+  const bool rc = subBlock.unpackerSuccess();
+  if (!rc) subBlock.setUnpackErrorCode(L1CaloSubBlock::UNPACK_DATA_TRUNCATED);
+  return rc;
+}
+
+// Unpack data - versions 1.02, 1.03, 1.04, 1.05 (by mistake for a few runs)
+
+bool PpmCompression::unpackV104(PpmSubBlock& subBlock)
+{
+  const int dataFormat = subBlock.format();
+  if (dataFormat != L1CaloSubBlock::COMPRESSED &&
+      dataFormat != L1CaloSubBlock::SUPERCOMPRESSED) {
+    subBlock.setUnpackErrorCode(L1CaloSubBlock::UNPACK_FORMAT);
+    return false;
+  }
+  const int compressionVersion = subBlock.seqno();
+  if (compressionVersion == 2 && dataFormat != L1CaloSubBlock::COMPRESSED) {
+    subBlock.setUnpackErrorCode(L1CaloSubBlock::UNPACK_FORMAT);
+    return false;
+  }
+  if (compressionVersion == 5) {
+    const int run = subBlock.runNumber();
+    if (run < 88701 || run > 88724) {
+      subBlock.setUnpackErrorCode(L1CaloSubBlock::UNPACK_COMPRESSION_VERSION);
+      return false;
+    }
+  }
+  const int sliceL = subBlock.slicesLut();
+  const int sliceF = subBlock.slicesFadc();
+  if (sliceL != 1 || sliceF != 5) {
+    subBlock.setUnpackErrorCode(L1CaloSubBlock::UNPACK_COMPRESSION_SLICES);
+    return false;
+  }
+  const int trigOffset   = subBlock.fadcOffset() - subBlock.lutOffset();
+  const int fadcBaseline = subBlock.fadcBaseline();
+  const int channels     = subBlock.channelsPerSubBlock();
+  subBlock.setStreamed();
+  subBlock.unpackerInit();
+  std::vector<uint32_t> compStats(s_formats);
+  std::vector<int> lutData;
+  std::vector<int> lutBcid;
+  std::vector<int> fadcData;
+  std::vector<int> fadcBcid;
+  for (int chan = 0; chan < channels; ++chan) {
+    if (dataFormat == L1CaloSubBlock::SUPERCOMPRESSED) {
+      if ( !subBlock.unpacker(1) ) continue;
+    }
+    lutData.clear();
+    lutBcid.clear();
+    fadcData.clear();
+    fadcBcid.clear();
+    int format = 0;
+    const int header = subBlock.unpacker(4);
+    if (header < 10) {
+      // formats 0,1 - LUT zero, FADC around pedestal
+      const int minOffset = header % 5;
+                format    = header / 5;
+      // LUT = 0
+      lutData.push_back(0);
+      lutBcid.push_back(0);
+      // FADC
+      const int minFadc = subBlock.unpacker(4) + fadcBaseline;
+      fadcData.push_back(minFadc);
+      fadcBcid.push_back(0);
+      for (int sl = 1; sl < sliceF; ++sl) {
+	fadcData.push_back(subBlock.unpacker(format + 2) + minFadc);
+        fadcBcid.push_back(0);
+      }
+      if (minOffset) std::swap(fadcData[0], fadcData[minOffset]);
+    } else if (header < 15) {
+      // formats 2-5
+      const int minOffset = header - 10;
+                format = subBlock.unpacker(2) + 2;
+      const int anyLut = subBlock.unpacker(1);
+      int lut  = 0;
+      int bcid = 0;
+      if (format == 2) {
+        // LUT
+	if (anyLut) {
+	  lut  = subBlock.unpacker(3);
+	  bcid = s_peakOnly;  // just peak-finding BCID set
+        }
+	lutData.push_back(lut);
+	lutBcid.push_back(bcid);
+	// FADC as formats 0,1
+        const int minFadc = subBlock.unpacker(4) + fadcBaseline;
+        fadcData.push_back(minFadc);
+        fadcBcid.push_back(0);
+        for (int sl = 1; sl < sliceF; ++sl) {
+	  fadcData.push_back(subBlock.unpacker(format + 2) + minFadc);
+          fadcBcid.push_back(0);
+        }
+        if (minOffset) std::swap(fadcData[0], fadcData[minOffset]);
+      } else {
+        // formats 3,4,5 - full LUT word, variable FADC
+	const int anyBcid = subBlock.unpacker(1);
+	// LUT
+	if (anyLut) {
+	  lut  = subBlock.unpacker(s_lutDataBits);
+	  bcid = subBlock.unpacker(s_lutBcidBits);
+	}
+        lutData.push_back(lut);
+	lutBcid.push_back(bcid);
+	// FADC
+	for (int sl = 0; sl < sliceF; ++sl) {
+	  int fbcid = 0;
+	  if (anyBcid) fbcid = subBlock.unpacker(1);
+	  else if (sl == trigOffset) fbcid = bcid & 0x1; // take from LUT word
+	  fadcBcid.push_back(fbcid);
+        }
+	int minFadc = 0;
+	if (subBlock.unpacker(1)) minFadc = subBlock.unpacker(format * 2);
+	else minFadc = subBlock.unpacker(4) + fadcBaseline;
+        fadcData.push_back(minFadc);
+	for (int sl = 1; sl < sliceF; ++sl) {
+	  int len = 4;
+	  if (subBlock.unpacker(1)) len = format * 2;
+	  fadcData.push_back(subBlock.unpacker(len) + minFadc);
+        }
+	if (minOffset) std::swap(fadcData[0], fadcData[minOffset]);
+      }
+    } else {
+      // format 6 - LUT zero, FADC all equal
+      format = 6;
+      // LUT
+      lutData.push_back(0);
+      lutBcid.push_back(0);
+      // FADC
+      int fadc = 0;
+      if (subBlock.unpacker(1)) fadc = subBlock.unpacker(s_fadcDataBits);
+      for (int sl = 0; sl < sliceF; ++sl) {
+        fadcData.push_back(fadc);
+        fadcBcid.push_back(0);
+      }
+    }
+    subBlock.fillPpmData(chan, lutData, fadcData, lutBcid, fadcBcid);
+    ++compStats[format];
+  }
+  // Errors
+  const int statusBit = subBlock.unpacker(1);
+  const int errorBit  = subBlock.unpacker(1);
+  const bool mcmAbsentIsError = compressionVersion < 4;
+  const int sBits = (mcmAbsentIsError) ? s_statusBits - 1 : s_statusBits;
+  const int eBits = (mcmAbsentIsError) ? s_errorBits  + 1 : s_errorBits;
+  if (statusBit || errorBit) {
+    std::vector<int> err(s_glinkPins);
+    for (int pin = 0; pin < s_glinkPins; ++pin) {
+      err[pin] = subBlock.unpacker(1);
+    }
+    for (int pin = 0; pin < s_glinkPins; ++pin) {
+      if (err[pin]) {
+        int status = 0;
+	int error  = 0;
+	if (statusBit) status = subBlock.unpacker(sBits);
+	if (errorBit)  error  = subBlock.unpacker(eBits);
+	subBlock.fillPpmPinError(pin, (error << sBits) | status);
+      }
+    }
+    // There is a bug in versions < 4 but if I've understood it correctly
+    // it is not possible to detect it reliably in data.
+  }
+  subBlock.setCompStats(compStats);
+  bool rc = subBlock.unpackerSuccess();
+  if (!rc) subBlock.setUnpackErrorCode(L1CaloSubBlock::UNPACK_DATA_TRUNCATED);
+  else {
+    // Check no more non-zero data - indicates corruption
+    while (subBlock.unpackerSuccess()) {
+      if (subBlock.unpacker(31)) {
+        subBlock.setUnpackErrorCode(L1CaloSubBlock::UNPACK_EXCESS_DATA);
+	rc = false;
+	break;
+      }
+    }
+  }
+  return rc;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmCompression.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmCompression.h
new file mode 100644
index 0000000000000000000000000000000000000000..e4c858a8d2f4856c144a49a4061cf4e7c8650ec3
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmCompression.h
@@ -0,0 +1,56 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_PPMCOMPRESSION_H
+#define TRIGT1CALOBYTESTREAM_PPMCOMPRESSION_H
+
+namespace LVL1BS {
+
+class PpmSubBlock;
+
+/** PPM Compressed Format Version 1.04 packing and unpacking utilities.
+ *
+ *  Based on:
+ *
+ *  "ATLAS L1Calo Pre-processor compressed S-Link data formats",
+ *   Version 1.7, D.P.C.Sankey.
+ *
+ *  @author Peter Faulkner
+ */
+
+class PpmCompression {
+
+ public:
+   PpmCompression();
+   ~PpmCompression();
+
+   /// Pack data
+   static bool pack(PpmSubBlock& subBlock);
+   /// Unpack data
+   static bool unpack(PpmSubBlock& subBlock);
+
+ private:
+   static const int s_formatsV0    = 6;
+   static const int s_lowerRange   = 12;
+   static const int s_upperRange   = 3;
+   static const int s_formats      = 7;
+   static const int s_fadcRange    = 15;
+   static const int s_peakOnly     = 4;
+   static const int s_lutDataBits  = 8;
+   static const int s_lutBcidBits  = 3;
+   static const int s_fadcDataBits = 10;
+   static const int s_glinkPins    = 16;
+   static const int s_statusBits   = 5;
+   static const int s_errorBits    = 6;
+   static const int s_statusMask   = 0x1f;
+
+   static bool unpackV100(PpmSubBlock& subBlock);
+   static bool unpackV101(PpmSubBlock& subBlock);
+   static bool unpackV104(PpmSubBlock& subBlock);
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmSubBlock.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmSubBlock.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..c3dc35fa2bd9e3801f2b25456e299933877d79b3
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmSubBlock.cxx
@@ -0,0 +1,493 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "PpmCompression.h"
+#include "PpmSubBlock.h"
+
+namespace LVL1BS {
+
+// Constant definitions
+
+const uint32_t PpmSubBlock::s_wordIdVal;
+const int      PpmSubBlock::s_errorMarker;
+
+const int      PpmSubBlock::s_wordLen;
+const int      PpmSubBlock::s_lutBit;
+const int      PpmSubBlock::s_bcidLutBit;
+const int      PpmSubBlock::s_fadcBit;
+const int      PpmSubBlock::s_bcidFadcBit;
+const uint32_t PpmSubBlock::s_lutMask;
+const uint32_t PpmSubBlock::s_bcidLutMask;
+const uint32_t PpmSubBlock::s_fadcMask;
+const uint32_t PpmSubBlock::s_bcidFadcMask;
+
+const int      PpmSubBlock::s_channels;
+const int      PpmSubBlock::s_glinkPins;
+const int      PpmSubBlock::s_asicChannels;
+const int      PpmSubBlock::s_dataBits;
+const int      PpmSubBlock::s_errorBits;
+const int      PpmSubBlock::s_bunchCrossingBits;
+
+const uint32_t PpmSubBlock::s_errorMask;
+const int      PpmSubBlock::s_glinkPinParityBit;
+const int      PpmSubBlock::s_fpgaCorruptBit;
+const int      PpmSubBlock::s_bunchMismatchBit;
+const int      PpmSubBlock::s_eventMismatchBit;
+const int      PpmSubBlock::s_asicFullBit;
+const int      PpmSubBlock::s_timeoutBit;
+const int      PpmSubBlock::s_mcmAbsentBit;
+const int      PpmSubBlock::s_channelDisabledBit;
+
+PpmSubBlock::PpmSubBlock() : m_globalError(0), m_globalDone(false),
+                             m_lutOffset(-1), m_fadcOffset(-1),
+			     m_pedestal(10), m_fadcBaseline(0),
+			     m_fadcThreshold(0), m_runNumber(0)
+{
+}
+
+PpmSubBlock::~PpmSubBlock()
+{
+}
+
+// Clear all data
+
+void PpmSubBlock::clear()
+{
+  L1CaloSubBlock::clear();
+  m_globalError   = 0;
+  m_globalDone    = false;
+  m_lutOffset     = -1;
+  m_fadcOffset    = -1;
+  m_datamap.clear();
+  m_errormap.clear();
+}
+
+// Store PPM header
+
+void PpmSubBlock::setPpmHeader(const int version, const int format,
+                               const int seqno, const int crate,
+                               const int module, const int slicesFadc,
+			       const int slicesLut)
+{
+  setHeader(s_wordIdVal, version, format, seqno, crate, module,
+                                                 slicesFadc, slicesLut);
+}
+
+// Store PPM error block header
+
+void PpmSubBlock::setPpmErrorHeader(const int version, const int format,
+                                    const int crate, const int module,
+				    const int slicesFadc, const int slicesLut)
+{
+  setHeader(s_wordIdVal, version, format, s_errorMarker, crate, module,
+                                                 slicesFadc, slicesLut);
+}
+
+// Return the number of FADC slices
+
+int PpmSubBlock::slicesFadc() const
+{
+  int slices = slices2();
+  if (slices == 0 && format() == NEUTRAL) {
+    slices = dataWords()/(s_asicChannels*s_dataBits) - slicesLut();
+  }
+  if (slices <= 0) slices = 1;
+  return slices;
+}
+
+// Return the number of LUT slices
+
+int PpmSubBlock::slicesLut() const
+{
+  int slices = slices1();
+  if (slices == 0) slices = 1;
+  return slices;
+}
+
+// Store PPM data for later packing
+
+void PpmSubBlock::fillPpmData(const int chan, const std::vector<int>& lut,
+                                              const std::vector<int>& fadc,
+				              const std::vector<int>& bcidLut,
+				              const std::vector<int>& bcidFadc)
+{
+  const int sliceL = slicesLut();
+  const int sliceF = slicesFadc();
+  const int slices = sliceL + sliceF;
+  const int chanPerSubBlock = channelsPerSubBlock();
+  int dataSize = m_datamap.size();
+  if (dataSize == 0) {
+    dataSize = slices * chanPerSubBlock;
+    m_datamap.resize(dataSize);
+  }
+  int offset = (chan % chanPerSubBlock) * slices;
+  if (offset + slices <= dataSize) {
+    for (int pos = 0; pos < sliceL; ++pos) {
+      uint32_t datum = (lut[pos] & s_lutMask) << s_lutBit;
+      datum |= (bcidLut[pos] & s_bcidLutMask) << s_bcidLutBit;
+      m_datamap[offset + pos] = datum;
+    }
+    offset += sliceL;
+    for (int pos = 0; pos < sliceF; ++pos) {
+      const int adc = (fadc[pos] > 0) ? fadc[pos] : 0;
+      uint32_t datum = (adc & s_fadcMask) << s_fadcBit;
+      datum |= (bcidFadc[pos] & s_bcidFadcMask) << s_bcidFadcBit;
+      m_datamap[offset + pos] = datum;
+    }
+  }
+}
+
+// Return unpacked data for given channel
+
+void PpmSubBlock::ppmData(const int chan, std::vector<int>& lut,
+                                          std::vector<int>& fadc,
+				          std::vector<int>& bcidLut,
+				          std::vector<int>& bcidFadc)
+{
+  lut.clear();
+  fadc.clear();
+  bcidLut.clear();
+  bcidFadc.clear();
+  const int sliceL = slicesLut();
+  const int sliceF = slicesFadc();
+  int beg = (chan % channelsPerSubBlock()) * (sliceL + sliceF);
+  int end = beg + sliceL;
+  if (size_t(end + sliceF) <= m_datamap.size()) {
+    for (int pos = beg; pos < end; ++pos) {
+      const uint32_t word = m_datamap[pos];
+      lut.push_back((word >> s_lutBit) & s_lutMask);
+      bcidLut.push_back((word >> s_bcidLutBit) & s_bcidLutMask);
+    }
+    beg += sliceL;
+    end += sliceF;
+    for (int pos = beg; pos < end; ++pos) {
+      const uint32_t word = m_datamap[pos];
+      fadc.push_back((word >> s_fadcBit) & s_fadcMask);
+      bcidFadc.push_back((word >> s_bcidFadcBit) & s_bcidFadcMask);
+    }
+  } else {
+    lut.resize(sliceL);
+    fadc.resize(sliceF);
+    bcidLut.resize(sliceL);
+    bcidFadc.resize(sliceF);
+  }
+}
+
+// Store an error word corresponding to a data channel
+
+void PpmSubBlock::fillPpmError(const int chan, const int errorWord)
+{
+  if (m_errormap.empty()) m_errormap.resize(s_glinkPins);
+  // Expand one ASIC channel disabled bit to four
+  const uint32_t chanDisabled = (errorWord & 0x1) << asic(chan);
+  m_errormap[pin(chan)] |= (((errorWord >> 1) << s_asicChannels)
+                                              | chanDisabled) & s_errorMask;
+}
+
+// Store an error word corresponding to a G-Link pin
+
+void PpmSubBlock::fillPpmPinError(const int pin, const int errorWord)
+{
+  if (m_errormap.empty()) m_errormap.resize(s_glinkPins);
+  m_errormap[pin] = errorWord & s_errorMask;
+}
+
+// Return the error word for a data channel
+
+int PpmSubBlock::ppmError(const int chan) const
+{
+  int err = 0;
+  if ( !m_errormap.empty()) {
+    // Replace the four ASIC channel disabled bits with just the one
+    // corresponding to the data channel
+    err = (((m_errormap[pin(chan)] & s_errorMask) >> s_asicChannels) << 1)
+                                                  | channelDisabled(chan);
+  }
+  return err;
+}
+
+// Return the error word for a G-Link pin
+
+int PpmSubBlock::ppmPinError(const int pin) const
+{
+  int err = 0;
+  if ( !m_errormap.empty()) err = m_errormap[pin] & s_errorMask;
+  return err;
+}
+
+// Return global error bit
+
+bool PpmSubBlock::errorBit(const int bit) const
+{
+  if ( ! m_globalDone) {
+    std::vector<uint32_t>::const_iterator pos;
+    for (pos = m_errormap.begin(); pos != m_errormap.end(); ++pos) {
+      m_globalError |= *pos;
+    }
+    m_globalDone  = true;
+  }
+  return m_globalError & (0x1 << bit);
+}
+
+// Packing/Unpacking routines
+
+bool PpmSubBlock::pack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = packNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  switch (seqno()) {
+	    case s_errorMarker:
+	      rc = packUncompressedErrors();
+	      break;
+            default:
+	      rc = packUncompressedData();
+	      break;
+          }
+	  break;
+        case COMPRESSED:
+        case SUPERCOMPRESSED:
+	  rc = PpmCompression::pack(*this);
+	  break;
+        default:
+	  break;
+      }
+      break;
+    default:
+      break;
+  }
+  return rc;
+}
+
+bool PpmSubBlock::unpack()
+{
+  bool rc = false;
+  switch (version()) {
+    case 1:
+      switch (format()) {
+        case NEUTRAL:
+	  rc = unpackNeutral();
+	  break;
+        case UNCOMPRESSED:
+	  switch (seqno()) {
+	    case s_errorMarker:
+	      rc = unpackUncompressedErrors();
+	      break;
+            default:
+	      rc = unpackUncompressedData();
+	      break;
+          }
+	  break;
+        case COMPRESSED:
+        case SUPERCOMPRESSED:
+	  rc = PpmCompression::unpack(*this);
+	  break;
+        default:
+	  setUnpackErrorCode(UNPACK_FORMAT);
+	  break;
+      }
+      break;
+    default:
+      setUnpackErrorCode(UNPACK_VERSION);
+      break;
+  }
+  return rc;
+}
+
+// Pack neutral data
+
+bool PpmSubBlock::packNeutral()
+{
+  const int slices   = slicesLut() + slicesFadc();
+  const int channels = channelsPerSubBlock();
+  if (m_datamap.empty()) m_datamap.resize(slices * channels);
+  // Bunch crossing number
+  for (int pin = 0; pin < s_glinkPins; ++pin) {
+    uint32_t bc = 0;
+    if (pin < s_bunchCrossingBits) bc = (bunchCrossing() >> pin) & 0x1;
+    packerNeutral(pin, bc, 1);
+  }
+  // Data
+  std::vector<uint32_t>::const_iterator pos = m_datamap.begin();
+  for (int asic = 0; asic < s_asicChannels; ++asic) {
+    for (int pin = 0; pin < s_glinkPins; ++pin) {
+      for (int sl = 0; sl < slices; ++sl) {
+        packerNeutral(pin, *pos, s_dataBits);
+	++pos;
+      }
+    }
+  }
+  // Errors, including GP
+  if (m_errormap.empty()) m_errormap.resize(s_glinkPins);
+  pos = m_errormap.begin();
+  for (int pin = 0; pin < s_glinkPins; ++pin) {
+    packerNeutral(pin, *pos, s_errorBits);
+    packerNeutralParity(pin);
+    ++pos;
+  }
+  return true;
+}
+
+// Pack uncompressed data
+
+bool PpmSubBlock::packUncompressedData()
+{
+  const int slices   = slicesLut() + slicesFadc();
+  const int channels = channelsPerSubBlock();
+  if (m_datamap.empty()) m_datamap.resize(slices * channels);
+  for (int sl = 0; sl < slices; ++sl) {
+    for (int chan = 0; chan < channels; ++chan) {
+      packer(m_datamap[sl + chan*slices], s_wordLen);
+    }
+  }
+  packerFlush();
+  return true;
+}
+
+// Pack uncompressed error data
+
+bool PpmSubBlock::packUncompressedErrors()
+{
+  if (m_errormap.empty()) m_errormap.resize(s_glinkPins);
+  for (int pin = 0; pin < s_glinkPins; ++pin) {
+    packer(m_errormap[pin], s_wordLen);
+  }
+  packerFlush();
+  return true;
+}
+
+// Unpack neutral data
+
+bool PpmSubBlock::unpackNeutral()
+{
+  const int slices = slicesLut() + slicesFadc();
+  m_datamap.clear();
+  // Bunch Crossing number
+  int bunchCrossing = 0;
+  for (int pin = 0; pin < s_glinkPins; ++pin) {
+    const int bc = unpackerNeutral(pin, 1);
+    if (pin < s_bunchCrossingBits) bunchCrossing |= bc << pin;
+  }
+  setBunchCrossing(bunchCrossing);
+  // Data
+  for (int asic = 0; asic < s_asicChannels; ++asic) {
+    for (int pin = 0; pin < s_glinkPins; ++pin) {
+      for (int sl = 0; sl < slices; ++sl) {
+        m_datamap.push_back(unpackerNeutral(pin, s_dataBits));
+      }
+    }
+  }
+  const bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  // Errors
+  m_errormap.clear();
+  for (int pin = 0; pin < s_glinkPins; ++pin) {
+    uint32_t error = unpackerNeutral(pin, s_errorBits);
+    error |= unpackerNeutralParityError(pin) << s_errorBits;
+    m_errormap.push_back(error);
+  }
+  return rc;
+}
+
+// Unpack uncompressed data
+
+bool PpmSubBlock::unpackUncompressedData()
+{
+  const int slices = slicesLut() + slicesFadc();
+  const int channels = channelsPerSubBlock();
+  m_datamap.resize(slices * channels);
+  unpackerInit();
+  for (int sl = 0; sl < slices; ++sl) {
+    for (int chan = 0; chan < channels; ++chan) {
+      m_datamap[sl + chan*slices] = unpacker(s_wordLen);
+    }
+  }
+  bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  else {
+    // Check no more non-zero data
+    while (unpackerSuccess()) {
+      if (unpacker(s_wordLen)) {
+        setUnpackErrorCode(UNPACK_EXCESS_DATA);
+	rc = false;
+	break;
+      }
+    }
+  }
+  return rc;
+}
+
+// Unpack uncompressed error data
+
+bool PpmSubBlock::unpackUncompressedErrors()
+{
+  unpackerInit();
+  m_errormap.clear();
+  for (int pin = 0; pin < s_glinkPins; ++pin) {
+    m_errormap.push_back(unpacker(s_wordLen));
+  }
+  bool rc = unpackerSuccess();
+  if (!rc) setUnpackErrorCode(UNPACK_DATA_TRUNCATED);
+  else {
+    while (unpackerSuccess()) {
+      if (unpacker(s_wordLen)) {
+        setUnpackErrorCode(UNPACK_EXCESS_DATA);
+	rc = false;
+	break;
+      }
+    }
+  }
+  return rc;
+}
+
+// Return the number of channels per sub-block
+
+int PpmSubBlock::channelsPerSubBlock(const int version, const int format)
+{
+  int chan = 0;
+  switch (version) {
+    case 1:
+      switch (format) {
+        case UNCOMPRESSED:
+	  chan = s_channels/s_asicChannels;
+	  break;
+        case NEUTRAL:
+        case COMPRESSED:
+        case SUPERCOMPRESSED:
+	  chan = s_channels;
+	  break;
+        default:
+	  setUnpackErrorCode(UNPACK_FORMAT);
+	  break;
+      }
+      break;
+    default:
+      setUnpackErrorCode(UNPACK_VERSION);
+      break;
+  }
+  return chan;
+}
+
+int PpmSubBlock::channelsPerSubBlock()
+{
+  return channelsPerSubBlock(version(), format());
+}
+
+// Check if a header word is for an error block
+
+bool PpmSubBlock::errorBlock(const uint32_t word)
+{
+  bool rc = false;
+  if (format(word) == UNCOMPRESSED && 
+       seqno(word) == s_errorMarker) rc = true;
+  return rc;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmSubBlock.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmSubBlock.h
new file mode 100755
index 0000000000000000000000000000000000000000..8bcb12533f97202554faab61db9eeae3fa562812
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/PpmSubBlock.h
@@ -0,0 +1,400 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_PPMSUBBLOCK_H
+#define TRIGT1CALOBYTESTREAM_PPMSUBBLOCK_H
+
+#include <stdint.h>
+#include <vector>
+
+#include "L1CaloSubBlock.h"
+
+namespace LVL1BS {
+
+/** Sub-Block class for PPM data.
+ *
+ *  @author Peter Faulkner
+ */
+
+class PpmSubBlock : public L1CaloSubBlock {
+
+ public:
+   PpmSubBlock();
+   ~PpmSubBlock();
+
+   /// Clear all data
+   void clear();
+
+   /// Store PPM header
+   void setPpmHeader(int version, int format, int seqno, int crate,
+	             int module, int slicesFadc, int slicesLut);
+   /// Store PPM error block header
+   void setPpmErrorHeader(int version, int format, int crate,
+	                  int module, int slicesFadc, int slicesLut);
+
+   //  Return PPM-specific header data
+   int  slicesFadc() const;
+   int  slicesLut()  const;
+
+   /// Store PPM data for later packing
+   void fillPpmData(int chan, const std::vector<int>& lut,
+                              const std::vector<int>& fadc,
+                              const std::vector<int>& bcidLut,
+		              const std::vector<int>& bcidFadc);
+   /// Return unpacked data for given channel
+   void ppmData(int chan, std::vector<int>& lut,
+                          std::vector<int>& fadc,
+			  std::vector<int>& bcidLut,
+			  std::vector<int>& bcidFadc);
+
+   /// Store an error word corresponding to a data channel
+   void fillPpmError(int chan, int errorWord);
+   /// Store an error word corresponding to a G-Link pin
+   void fillPpmPinError(int pin, int errorWord);
+
+   /// Return the error word for a data channel
+   int  ppmError(int chan)        const;
+   /// Return the error word for a G-Link pin
+   int  ppmPinError(int pin)      const;
+
+   //  Return individual error bits
+   bool glinkPinParity(int chan)  const;
+   bool fpgaCorrupt(int chan)     const;
+   bool bunchMismatch(int chan)   const;
+   bool eventMismatch(int chan)   const;
+   bool asicFull(int chan)        const;
+   bool timeout(int chan)         const;
+   bool mcmAbsent(int chan)       const;
+   bool channelDisabled(int chan) const;
+   bool channelDisabledA(int pin) const;
+   bool channelDisabledB(int pin) const;
+   bool channelDisabledC(int pin) const;
+   bool channelDisabledD(int pin) const;
+   //  Ditto ORed over all pins
+   bool glinkPinParity()          const;
+   bool fpgaCorrupt()             const;
+   bool bunchMismatch()           const;
+   bool eventMismatch()           const;
+   bool asicFull()                const;
+   bool timeout()                 const;
+   bool mcmAbsent()               const;
+   bool channelDisabledA()        const;
+   bool channelDisabledB()        const;
+   bool channelDisabledC()        const;
+   bool channelDisabledD()        const;
+
+   //  Set triggered slice offsets, pedestal value
+   void setLutOffset(int offset);
+   void setFadcOffset(int offset);
+   void setPedestal(int pedval);
+   void setFadcBaseline(int baseline);
+   void setFadcThreshold(int threshold);
+   void setRunNumber(int run);
+   //  Return triggered slice offsets, pedestal value
+   int  lutOffset()               const;
+   int  fadcOffset()              const;
+   int  pedestal()                const;
+   int  fadcBaseline()            const;
+   int  fadcThreshold()           const;
+   int  runNumber()               const;
+
+   /// Pack data
+   bool pack();
+   /// Unpack data
+   bool unpack();
+
+   /// Return the number of channels per sub-block
+   int channelsPerSubBlock(int version, int format);
+   int channelsPerSubBlock();
+
+   /// Check if a header word is for an error block
+   static bool errorBlock(uint32_t word);
+
+   /// Set compression stats
+   void setCompStats(const std::vector<uint32_t>& stats);
+   /// Return reference to compression stats
+   const std::vector<uint32_t>& compStats() const;
+
+ private:
+   //  Header word data
+   static const uint32_t s_wordIdVal    = 0xc;
+   static const int      s_errorMarker  = 63;
+   //  Data word positions and masks
+   static const int      s_wordLen      = 16;
+   static const int      s_lutBit       = 0;
+   static const int      s_bcidLutBit   = 8;
+   static const int      s_fadcBit      = 1;
+   static const int      s_bcidFadcBit  = 0;
+   static const uint32_t s_lutMask      = 0xff;
+   static const uint32_t s_bcidLutMask  = 0x7;
+   static const uint32_t s_fadcMask     = 0x3ff;
+   static const uint32_t s_bcidFadcMask = 0x1;
+   //  For neutral format
+   static const int      s_channels          = 64;
+   static const int      s_glinkPins         = 16;
+   static const int      s_asicChannels      = 4;
+   static const int      s_dataBits          = 11;
+   static const int      s_errorBits         = 10;
+   static const int      s_bunchCrossingBits = 12;
+   //  Error word masks and bit positions
+   static const uint32_t s_errorMask          = 0x7ff;
+   static const int      s_glinkPinParityBit  = 10;
+   static const int      s_fpgaCorruptBit     = 9;
+   static const int      s_bunchMismatchBit   = 8;
+   static const int      s_eventMismatchBit   = 7;
+   static const int      s_asicFullBit        = 6;
+   static const int      s_timeoutBit         = 5;
+   static const int      s_mcmAbsentBit       = 4;
+   static const int      s_channelDisabledBit = 0;
+
+   /// Return the ASIC channel corresponding to a data channel
+   int  asic(int chan) const;
+   /// Return the G-Link pin corresponding to a data channel
+   int  pin(int chan) const;
+
+   /// Error bit extraction
+   bool errorBit(int pin, int bit) const;
+   /// Global error bit extraction
+   bool errorBit(int bit) const;
+
+   //  Packing/unpacking for specific formats
+   /// Pack neutral data
+   bool packNeutral();
+   /// Pack uncompressed data
+   bool packUncompressedData();
+   /// Pack uncompressed error data
+   bool packUncompressedErrors();
+   /// Unpack neutral data
+   bool unpackNeutral();
+   /// Unpack uncompressed data
+   bool unpackUncompressedData();
+   /// Unpack uncompressed error data
+   bool unpackUncompressedErrors();
+
+   //  Global error flags
+   mutable uint32_t m_globalError;
+   mutable bool     m_globalDone;
+
+   //  Triggered slice offsets, pedestal value
+   int m_lutOffset;
+   int m_fadcOffset;
+   int m_pedestal;
+   int m_fadcBaseline;
+   int m_fadcThreshold;
+   int m_runNumber;
+
+   /// Vector for compression statistics
+   std::vector<uint32_t> m_compStats;
+
+   /// Vector for intermediate data
+   std::vector<uint32_t> m_datamap;
+
+   /// Vector for intermediate error data
+   std::vector<uint32_t> m_errormap;
+
+};
+
+inline bool PpmSubBlock::glinkPinParity(const int chan) const
+{
+  return errorBit(pin(chan), s_glinkPinParityBit);
+}
+
+inline bool PpmSubBlock::fpgaCorrupt(const int chan) const
+{
+  return errorBit(pin(chan), s_fpgaCorruptBit);
+}
+
+inline bool PpmSubBlock::bunchMismatch(const int chan) const
+{
+  return errorBit(pin(chan), s_bunchMismatchBit);
+}
+
+inline bool PpmSubBlock::eventMismatch(const int chan) const
+{
+  return errorBit(pin(chan), s_eventMismatchBit);
+}
+
+inline bool PpmSubBlock::asicFull(const int chan) const
+{
+  return errorBit(pin(chan), s_asicFullBit);
+}
+
+inline bool PpmSubBlock::timeout(const int chan) const
+{
+  return errorBit(pin(chan), s_timeoutBit);
+}
+
+inline bool PpmSubBlock::mcmAbsent(const int chan) const
+{
+  return errorBit(pin(chan), s_mcmAbsentBit);
+}
+
+inline bool PpmSubBlock::channelDisabled(const int chan) const
+{
+  return errorBit(pin(chan), s_channelDisabledBit + asic(chan));
+}
+
+inline bool PpmSubBlock::channelDisabledA(const int pin) const
+{
+  return errorBit(pin, s_channelDisabledBit);
+}
+
+inline bool PpmSubBlock::channelDisabledB(const int pin) const
+{
+  return errorBit(pin, s_channelDisabledBit + 1);
+}
+
+inline bool PpmSubBlock::channelDisabledC(const int pin) const
+{
+  return errorBit(pin, s_channelDisabledBit + 2);
+}
+
+inline bool PpmSubBlock::channelDisabledD(const int pin) const
+{
+  return errorBit(pin, s_channelDisabledBit + 3);
+}
+
+inline bool PpmSubBlock::glinkPinParity() const
+{
+  return errorBit(s_glinkPinParityBit);
+}
+
+inline bool PpmSubBlock::fpgaCorrupt() const
+{
+  return errorBit(s_fpgaCorruptBit);
+}
+
+inline bool PpmSubBlock::bunchMismatch() const
+{
+  return errorBit(s_bunchMismatchBit);
+}
+
+inline bool PpmSubBlock::eventMismatch() const
+{
+  return errorBit(s_eventMismatchBit);
+}
+
+inline bool PpmSubBlock::asicFull() const
+{
+  return errorBit(s_asicFullBit);
+}
+
+inline bool PpmSubBlock::timeout() const
+{
+  return errorBit(s_timeoutBit);
+}
+
+inline bool PpmSubBlock::mcmAbsent() const
+{
+  return errorBit(s_mcmAbsentBit);
+}
+
+inline bool PpmSubBlock::channelDisabledA() const
+{
+  return errorBit(s_channelDisabledBit);
+}
+
+inline bool PpmSubBlock::channelDisabledB() const
+{
+  return errorBit(s_channelDisabledBit + 1);
+}
+
+inline bool PpmSubBlock::channelDisabledC() const
+{
+  return errorBit(s_channelDisabledBit + 2);
+}
+
+inline bool PpmSubBlock::channelDisabledD() const
+{
+  return errorBit(s_channelDisabledBit + 3);
+}
+
+inline void PpmSubBlock::setLutOffset(const int offset)
+{
+  m_lutOffset = offset;
+}
+
+inline void PpmSubBlock::setFadcOffset(const int offset)
+{
+  m_fadcOffset = offset;
+}
+
+inline void PpmSubBlock::setPedestal(const int pedval)
+{
+  m_pedestal = pedval;
+}
+
+inline void PpmSubBlock::setFadcBaseline(const int baseline)
+{
+  m_fadcBaseline = baseline;
+}
+
+inline void PpmSubBlock::setFadcThreshold(const int threshold)
+{
+  m_fadcThreshold = threshold;
+}
+
+inline void PpmSubBlock::setRunNumber(const int run)
+{
+  m_runNumber = run;
+}
+
+inline int PpmSubBlock::lutOffset() const
+{
+  return (m_lutOffset < 0) ? slicesLut()/2 : m_lutOffset;
+}
+
+inline int PpmSubBlock::fadcOffset() const
+{
+  return (m_fadcOffset < 0) ? slicesFadc()/2 : m_fadcOffset;
+}
+
+inline int PpmSubBlock::pedestal() const
+{
+  return m_pedestal;
+}
+
+inline int PpmSubBlock::fadcBaseline() const
+{
+  return m_fadcBaseline;
+}
+
+inline int PpmSubBlock::fadcThreshold() const
+{
+  return m_fadcThreshold;
+}
+
+inline int PpmSubBlock::runNumber() const
+{
+  return m_runNumber;
+}
+
+inline const std::vector<uint32_t>& PpmSubBlock::compStats() const
+{
+  return m_compStats;
+}
+
+inline void PpmSubBlock::setCompStats(const std::vector<uint32_t>& stats)
+{
+  m_compStats = stats;
+}
+
+inline int  PpmSubBlock::asic(const int chan) const
+{
+  return chan / s_glinkPins;
+}
+
+inline int  PpmSubBlock::pin(const int chan) const
+{
+  return chan % s_glinkPins;
+}
+
+inline bool PpmSubBlock::errorBit(const int pin, const int bit) const
+{
+  return m_errormap[pin] & (0x1 << bit);
+}
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/RodHeaderByteStreamCnv.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/RodHeaderByteStreamCnv.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..083d4f28a99fbc7a904644db282c4db29acada4b
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/RodHeaderByteStreamCnv.cxx
@@ -0,0 +1,140 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <vector>
+#include <stdint.h>
+
+#include "ByteStreamCnvSvcBase/ByteStreamAddress.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+
+#include "ByteStreamData/RawEvent.h"
+#include "ByteStreamData/ROBData.h"
+
+#include "DataModel/DataVector.h"
+
+#include "GaudiKernel/CnvFactory.h"
+#include "GaudiKernel/DataObject.h"
+#include "GaudiKernel/IOpaqueAddress.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "SGTools/ClassID_traits.h"
+#include "SGTools/StorableConversions.h"
+
+#include "TrigT1CaloEvent/RODHeader.h"
+
+#include "RodHeaderByteStreamTool.h"
+
+#include "RodHeaderByteStreamCnv.h"
+
+namespace LVL1BS {
+
+RodHeaderByteStreamCnv::RodHeaderByteStreamCnv( ISvcLocator* svcloc )
+    : Converter( ByteStream_StorageType, classID(), svcloc ),
+      m_name("RodHeaderByteStreamCnv"),
+      m_tool("LVL1BS::RodHeaderByteStreamTool/RodHeaderByteStreamTool"),
+      m_robDataProvider("ROBDataProviderSvc", m_name),
+      m_log(msgSvc(), m_name), m_debug(false)
+{
+}
+
+RodHeaderByteStreamCnv::~RodHeaderByteStreamCnv()
+{
+}
+
+// CLID
+
+const CLID& RodHeaderByteStreamCnv::classID()
+{
+  return ClassID_traits<DataVector<LVL1::RODHeader> >::ID();
+}
+
+//  Init method gets all necessary services etc.
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode RodHeaderByteStreamCnv::initialize()
+{
+  m_debug = msgSvc()->outputLevel(m_name) <= MSG::DEBUG;
+  m_log << MSG::DEBUG << "Initializing " << m_name << " - package version "
+                      << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = Converter::initialize();
+  if ( sc.isFailure() )
+    return sc;
+
+  // Retrieve Tool
+  sc = m_tool.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve tool " << m_tool << endreq;
+    return sc;
+  } else m_log << MSG::DEBUG << "Retrieved tool " << m_tool << endreq;
+
+  // Get ROBDataProvider
+  sc = m_robDataProvider.retrieve();
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << "Failed to retrieve service "
+          << m_robDataProvider << endreq;
+    return sc ;
+  } else {
+    m_log << MSG::DEBUG << "Retrieved service "
+          << m_robDataProvider << endreq;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// createObj should create the RDO from bytestream.
+
+StatusCode RodHeaderByteStreamCnv::createObj( IOpaqueAddress* pAddr,
+                                        DataObject*& pObj )
+{
+  if (m_debug) m_log << MSG::DEBUG << "createObj() called" << endreq;
+
+  ByteStreamAddress *pBS_Addr;
+  pBS_Addr = dynamic_cast<ByteStreamAddress *>( pAddr );
+  if ( !pBS_Addr ) {
+    m_log << MSG::ERROR << " Can not cast to ByteStreamAddress " << endreq;
+    return StatusCode::FAILURE;
+  }
+
+  const std::string nm = *( pBS_Addr->par() );
+
+  if (m_debug) m_log << MSG::DEBUG << " Creating Objects " << nm << endreq;
+
+  // get SourceIDs
+  const std::vector<uint32_t>& vID(m_tool->sourceIDs(nm));
+
+  // get ROB fragments
+  IROBDataProviderSvc::VROBFRAG robFrags;
+  m_robDataProvider->getROBData( vID, robFrags );
+
+  // size check
+  DataVector<LVL1::RODHeader>* const rhCollection =
+                                           new DataVector<LVL1::RODHeader>;
+  if (m_debug) {
+    m_log << MSG::DEBUG << " Number of ROB fragments is " << robFrags.size()
+          << endreq;
+  }
+  if (robFrags.size() == 0) {
+    pObj = SG::asStorable(rhCollection) ;
+    return StatusCode::SUCCESS;
+  }
+
+  StatusCode sc = m_tool->convert(robFrags, rhCollection);
+  if ( sc.isFailure() ) {
+    m_log << MSG::ERROR << " Failed to create Objects   " << nm << endreq;
+    delete rhCollection;
+    return sc;
+  }
+
+  pObj = SG::asStorable(rhCollection);
+
+  return sc;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/RodHeaderByteStreamCnv.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/RodHeaderByteStreamCnv.h
new file mode 100644
index 0000000000000000000000000000000000000000..26d9c875d48793739f00b080edaeaec996ac2107
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/RodHeaderByteStreamCnv.h
@@ -0,0 +1,76 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_RODHEADERBYTESTREAMCNV_H
+#define TRIGT1CALOBYTESTREAM_RODHEADERBYTESTREAMCNV_H
+
+#include <string>
+
+#include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/Converter.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+class DataObject;
+class IOpaqueAddress;
+class IROBDataProviderSvc;
+class ISvcLocator;
+class StatusCode;
+
+template <typename> class CnvFactory;
+
+// Externals
+extern long ByteStream_StorageType;
+
+namespace LVL1BS {
+
+class RodHeaderByteStreamTool;
+
+/** ByteStream converter for L1Calo ROD header info
+ *
+ *  @author Peter Faulkner
+ */
+
+class RodHeaderByteStreamCnv: public Converter {
+
+  friend class CnvFactory<RodHeaderByteStreamCnv>;
+
+protected:
+
+  RodHeaderByteStreamCnv(ISvcLocator* svcloc);
+
+public:
+
+  ~RodHeaderByteStreamCnv();
+
+  virtual StatusCode initialize();
+  /// Create RodHeaders from ByteStream
+  virtual StatusCode createObj(IOpaqueAddress* pAddr, DataObject*& pObj);
+
+  //  Storage type and class ID
+  virtual long repSvcType() const { return ByteStream_StorageType;}
+  static  long storageType(){ return ByteStream_StorageType; }
+  static const CLID& classID();
+
+private:
+
+  /// Converter name
+  std::string m_name;
+
+  /// Tool that does the actual work
+  ToolHandle<LVL1BS::RodHeaderByteStreamTool> m_tool;
+
+  /// Service for reading bytestream
+  ServiceHandle<IROBDataProviderSvc> m_robDataProvider;
+
+  /// Message log
+  mutable MsgStream m_log;
+  bool m_debug;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/RodHeaderByteStreamTool.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/RodHeaderByteStreamTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..1e6ddcd30306ddadc1b7c8e18418e874db873ec8
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/RodHeaderByteStreamTool.cxx
@@ -0,0 +1,300 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <algorithm>
+#include <set>
+
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "TrigT1CaloEvent/RODHeader.h"
+
+#include "L1CaloErrorByteStreamTool.h"
+#include "L1CaloSrcIdMap.h"
+#include "L1CaloSubBlock.h"
+
+#include "RodHeaderByteStreamTool.h"
+
+namespace LVL1BS {
+
+// Interface ID
+
+static const InterfaceID IID_IRodHeaderByteStreamTool("RodHeaderByteStreamTool", 1, 1);
+
+const InterfaceID& RodHeaderByteStreamTool::interfaceID()
+{
+  return IID_IRodHeaderByteStreamTool;
+}
+
+// Constructor
+
+RodHeaderByteStreamTool::RodHeaderByteStreamTool(const std::string& type,
+                                                 const std::string& name,
+	    			                 const IInterface*  parent)
+  : AthAlgTool(type, name, parent),
+    m_errorTool("LVL1BS::L1CaloErrorByteStreamTool/L1CaloErrorByteStreamTool"),
+    m_srcIdMap(0)
+{
+  declareInterface<RodHeaderByteStreamTool>(this);
+
+  declareProperty("ErrorTool", m_errorTool,
+                  "Tool to collect errors for monitoring");
+  declareProperty("ROBSourceIDs",        m_sourceIDs,
+                  "ROB fragment source identifiers - All except RoIB");
+  declareProperty("ROBSourceIDsPP",      m_sourceIDsPP,
+                  "ROB fragment source identifiers - PP only");
+  declareProperty("ROBSourceIDsCP",      m_sourceIDsCP,
+                  "ROB fragment source identifiers - CP DAQ only");
+  declareProperty("ROBSourceIDsJEP",     m_sourceIDsJEP,
+                  "ROB fragment source identifiers - JEP DAQ only");
+  declareProperty("ROBSourceIDsCPRoI",   m_sourceIDsCPRoI,
+                  "ROB fragment source identifiers - CP RoI only");
+  declareProperty("ROBSourceIDsJEPRoI",  m_sourceIDsJEPRoI,
+                  "ROB fragment source identifiers - JEP RoI only");
+  declareProperty("ROBSourceIDsCPRoIB",  m_sourceIDsCPRoIB,
+                  "ROB fragment source identifiers - CP RoIB only");
+  declareProperty("ROBSourceIDsJEPRoIB", m_sourceIDsJEPRoIB,
+                  "ROB fragment source identifiers - JEP RoIB only");
+
+}
+
+// Destructor
+
+RodHeaderByteStreamTool::~RodHeaderByteStreamTool()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode RodHeaderByteStreamTool::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << PACKAGE_VERSION << endreq;
+
+  StatusCode sc = m_errorTool.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_errorTool << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_errorTool << endreq;
+
+  m_srcIdMap = new L1CaloSrcIdMap();
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode RodHeaderByteStreamTool::finalize()
+{
+  delete m_srcIdMap;
+  return StatusCode::SUCCESS;
+}
+
+// Conversion bytestream to RODHeaders
+
+StatusCode RodHeaderByteStreamTool::convert(
+                            const IROBDataProviderSvc::VROBFRAG& robFrags,
+                            DataVector<LVL1::RODHeader>* const rhCollection)
+{
+  const bool debug = msgLvl(MSG::DEBUG);
+  if (debug) msg(MSG::DEBUG);
+
+  // Loop over ROB fragments
+
+  int robCount = 0;
+  std::set<uint32_t> dupCheck;
+  ROBIterator rob    = robFrags.begin();
+  ROBIterator robEnd = robFrags.end();
+  for (; rob != robEnd; ++rob) {
+
+    if (debug) {
+      ++robCount;
+      msg() << "Treating ROB fragment " << robCount << endreq;
+    }
+
+    // Skip fragments with ROB status errors
+
+    uint32_t robid = (*rob)->source_id();
+    if ((*rob)->nstatus() > 0) {
+      ROBPointer robData;
+      (*rob)->status(robData);
+      if (*robData != 0) {
+        m_errorTool->robError(robid, *robData);
+	if (debug) msg() << "ROB status error - skipping fragment" << endreq;
+	continue;
+      }
+    }
+
+    // Skip duplicate fragments
+
+    if (!dupCheck.insert(robid).second) {
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_DUPLICATE_ROB);
+      if (debug) msg() << "Skipping duplicate ROB fragment" << endreq;
+      continue;
+    }
+
+    // Unpack ROD header info
+
+    const uint32_t version  = (*rob)->rod_version();
+    const uint32_t sourceId = (*rob)->rod_source_id();
+    const uint32_t run      = (*rob)->rod_run_no();
+    const uint32_t lvl1Id   = (*rob)->rod_lvl1_id();
+    const uint32_t bcId     = (*rob)->rod_bc_id();
+    const uint32_t trigType = (*rob)->rod_lvl1_trigger_type();
+    const uint32_t detType  = (*rob)->rod_detev_type();
+    const uint32_t nData    = (*rob)->rod_ndata();
+
+    // Unpack status words
+
+    std::vector<uint32_t> statusWords;
+    unsigned int nstatus = (*rob)->rod_nstatus();
+    if (nstatus <= 2) {
+      RODPointer status;
+      RODPointer statusEnd;
+      (*rob)->rod_status(status);
+      statusEnd = status + nstatus;
+      for (; status != statusEnd; ++status) statusWords.push_back(*status);
+    } else { // Likely corruption
+      m_errorTool->rodError(robid, L1CaloSubBlock::ERROR_ROD_NSTATUS);
+      continue;
+    }
+
+    // Save
+
+    rhCollection->push_back(new LVL1::RODHeader(version, sourceId, run, lvl1Id,
+                                bcId, trigType, detType, statusWords, nData));
+    if (debug) {
+      msg() << MSG::hex
+            << "ROD Header version/sourceId/run/lvl1Id/bcId/trigType/detType/nData: "
+	    << version << "/" << sourceId << "/" << run << "/" << lvl1Id << "/"
+	    << bcId << "/" << trigType << "/" << detType << "/" << nData
+	    << endreq << "ROD Status Words:";
+      std::vector<uint32_t>::const_iterator pos  = statusWords.begin();
+      std::vector<uint32_t>::const_iterator pose = statusWords.end();
+      for (; pos != pose; ++pos) msg() << " " << *pos;
+      msg() << MSG::dec << endreq;
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Return reference to vector with all possible Source Identifiers
+
+const std::vector<uint32_t>& RodHeaderByteStreamTool::sourceIDs(
+                                                      const std::string& sgKey)
+{
+  const bool pp      = isAppended(sgKey, "PP");
+  const bool cp      = isAppended(sgKey, "CP");
+  const bool jep     = isAppended(sgKey, "JEP");
+  const bool cpRoi   = isAppended(sgKey, "CPRoI");
+  const bool jepRoi  = isAppended(sgKey, "JEPRoI");
+  const bool cpRoib  = isAppended(sgKey, "CPRoIB");
+  const bool jepRoib = isAppended(sgKey, "JEPRoIB");
+  const bool all = !(pp || cp || jep || cpRoi || jepRoi || cpRoib || jepRoib);
+  if (all     && !m_sourceIDs.empty())        return m_sourceIDs;
+  if (pp      && !m_sourceIDsPP.empty())      return m_sourceIDsPP;
+  if (cp      && !m_sourceIDsCP.empty())      return m_sourceIDsCP;
+  if (jep     && !m_sourceIDsJEP.empty())     return m_sourceIDsJEP;
+  if (cpRoi   && !m_sourceIDsCPRoI.empty())   return m_sourceIDsCPRoI;
+  if (jepRoi  && !m_sourceIDsJEPRoI.empty())  return m_sourceIDsJEPRoI;
+  if (cpRoib  && !m_sourceIDsCPRoIB.empty())  return m_sourceIDsCPRoIB;
+  if (jepRoib && !m_sourceIDsJEPRoIB.empty()) return m_sourceIDsJEPRoIB;
+  // PP
+  if (all || pp) {
+    std::vector<int> slinks(4);
+    for (int i = 1; i < 4; ++i) slinks[i] = i;
+    fillRobIds(all, 8, 0, slinks, 0, eformat::TDAQ_CALO_PREPROC,
+                                                  m_sourceIDsPP);
+    if (pp) return m_sourceIDsPP;
+  }
+  // CP
+  if (all || cp) {
+    std::vector<int> slinks(2);
+    slinks[1] = 2;
+    fillRobIds(all, 4, 8, slinks, 0, eformat::TDAQ_CALO_CLUSTER_PROC_DAQ,
+                                                           m_sourceIDsCP);
+    if (cp) return m_sourceIDsCP;
+  }
+  // CP RoI
+  if (all || cpRoi) {
+    const std::vector<int> slinks(1);
+    fillRobIds(all, 4, 8, slinks, 1, eformat::TDAQ_CALO_CLUSTER_PROC_ROI,
+                                                        m_sourceIDsCPRoI);
+    if (cpRoi) return m_sourceIDsCPRoI;
+  }
+  // JEP
+  if (all || jep) {
+    std::vector<int> slinks(4);
+    for (int i = 1; i < 4; ++i) slinks[i] = i;
+    fillRobIds(all, 2, 12, slinks, 0, eformat::TDAQ_CALO_JET_PROC_DAQ,
+                                                       m_sourceIDsJEP);
+    if (jep) return m_sourceIDsJEP;
+  }
+  // JEP RoI
+  if (all || jepRoi) {
+    const std::vector<int> slinks(1);
+    fillRobIds(all, 2, 12, slinks, 1, eformat::TDAQ_CALO_JET_PROC_ROI,
+                                                    m_sourceIDsJEPRoI);
+    if (jepRoi) return m_sourceIDsJEPRoI;
+  }
+  // Don't include RoIBs (LVL2) in complete set
+  // CP RoIB
+  if (cpRoib) {
+    const std::vector<int> slinks(1, 2);
+    fillRobIds(false, 4, 8, slinks, 1, eformat::TDAQ_CALO_CLUSTER_PROC_ROI,
+                                                         m_sourceIDsCPRoIB);
+    return m_sourceIDsCPRoIB;
+  }
+  // JEP RoIB
+  if (jepRoib) {
+    const std::vector<int> slinks(1, 2);
+    fillRobIds(false, 2, 12, slinks, 1, eformat::TDAQ_CALO_JET_PROC_ROI,
+                                                     m_sourceIDsJEPRoIB);
+    return m_sourceIDsJEPRoIB;
+  }
+  return m_sourceIDs;
+}
+
+// Fill vector with ROB IDs for given sub-detector
+
+void RodHeaderByteStreamTool::fillRobIds(const bool all, const int numCrates,
+                                         const int crateOffset,
+				         const std::vector<int>& slinks,
+				 	 const int daqOrRoi,
+				         const eformat::SubDetector subdet,
+				         std::vector<uint32_t>& detSourceIDs)
+{
+  if (all && !detSourceIDs.empty()) {
+    std::copy(detSourceIDs.begin(), detSourceIDs.end(),
+                                    std::back_inserter(m_sourceIDs));
+  } else {
+    for (int crate = 0; crate < numCrates; ++crate) {
+      const int numSlinks = slinks.size();
+      for (int i = 0; i < numSlinks; ++i) {
+        const uint32_t rodId = m_srcIdMap->getRodID(crate + crateOffset,
+	                                   slinks[i], daqOrRoi, subdet);
+        const uint32_t robId = m_srcIdMap->getRobID(rodId);
+	if (all) m_sourceIDs.push_back(robId);
+	else     detSourceIDs.push_back(robId);
+      }
+    }
+  }
+}
+
+// Return true if StoreGate key ends in given string
+
+bool RodHeaderByteStreamTool::isAppended(const std::string& sgKey,
+                                         const std::string& flag) const
+{
+  const std::string::size_type pos = sgKey.find(flag);
+  return (pos != std::string::npos && pos == sgKey.length() - flag.length());
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/RodHeaderByteStreamTool.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/RodHeaderByteStreamTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..3f68df5ffa80f447eb218d1ffa866786e1619281
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/RodHeaderByteStreamTool.h
@@ -0,0 +1,95 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_RODHEADERBYTESTREAMTOOL_H
+#define TRIGT1CALOBYTESTREAM_RODHEADERBYTESTREAMTOOL_H
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "eformat/SourceIdentifier.h"
+#include "GaudiKernel/ToolHandle.h"
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+#include "ByteStreamData/RawEvent.h"
+#include "DataModel/DataVector.h"
+
+class IInterface;
+class InterfaceID;
+class StatusCode;
+
+namespace LVL1 {
+  class RODHeader;
+}
+
+namespace LVL1BS {
+
+class L1CaloErrorByteStreamTool;
+class L1CaloSrcIdMap;
+
+/** Tool to perform ROB fragments to ROD Header conversions.
+ *
+ *  Based on ROD document version 1_09h.
+ *
+ *  @author Peter Faulkner
+ */
+
+class RodHeaderByteStreamTool : public AthAlgTool {
+
+ public:
+   RodHeaderByteStreamTool(const std::string& type, const std::string& name,
+                           const IInterface* parent);
+   virtual ~RodHeaderByteStreamTool();
+
+   /// AlgTool InterfaceID
+   static const InterfaceID& interfaceID();
+
+   virtual StatusCode initialize();
+   virtual StatusCode finalize();
+
+   /// Convert ROB fragments to RODHeaders
+   StatusCode convert(const IROBDataProviderSvc::VROBFRAG& robFrags,
+                      DataVector<LVL1::RODHeader>* rhCollection);
+
+   /// Return reference to vector with all possible Source Identifiers
+   const std::vector<uint32_t>& sourceIDs(const std::string& sgKey);
+
+ private:
+   typedef DataVector<LVL1::RODHeader>                   RodHeaderCollection;
+   typedef IROBDataProviderSvc::VROBFRAG::const_iterator ROBIterator;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      ROBPointer;
+   typedef OFFLINE_FRAGMENTS_NAMESPACE::PointerType      RODPointer;
+
+   /// Fill vector with ROB IDs for given sub-detector
+   void fillRobIds(bool all, int numCrates, int crateOffset,
+                   const std::vector<int>& slinks, int daqOrRoi,
+		   eformat::SubDetector subdet,
+		   std::vector<uint32_t>& detSourceIDs);
+
+   /// Return true if StoreGate key ends in given string
+   bool isAppended(const std::string& sgKey, const std::string& flag) const;
+
+   /// Error collection tool
+   ToolHandle<LVL1BS::L1CaloErrorByteStreamTool> m_errorTool;
+
+   /// ROB source IDs
+   std::vector<uint32_t> m_sourceIDs;
+   std::vector<uint32_t> m_sourceIDsPP;
+   std::vector<uint32_t> m_sourceIDsCP;
+   std::vector<uint32_t> m_sourceIDsJEP;
+   std::vector<uint32_t> m_sourceIDsCPRoI;
+   std::vector<uint32_t> m_sourceIDsJEPRoI;
+   std::vector<uint32_t> m_sourceIDsCPRoIB;
+   std::vector<uint32_t> m_sourceIDsJEPRoIB;
+   /// Source ID converter
+   L1CaloSrcIdMap* m_srcIdMap;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/TrigT1CaloDataAccess.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/TrigT1CaloDataAccess.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..44f9f7ab8a1ed01b3155d56f9733888a21ef772a
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/TrigT1CaloDataAccess.cxx
@@ -0,0 +1,234 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <stdint.h>
+
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "ByteStreamCnvSvcBase/IROBDataProviderSvc.h"
+#include "TrigT1CaloEvent/TriggerTower.h"
+#include "TrigT1CaloEvent/JetElement.h"
+
+#include "IPpmByteStreamSubsetTool.h"
+#include "JepByteStreamV2Tool.h"
+#include "ITriggerTowerSelectionTool.h"
+
+#include "TrigT1CaloDataAccess.h"
+
+namespace LVL1BS {
+
+// Constructor
+
+TrigT1CaloDataAccess::TrigT1CaloDataAccess(const std::string& type,
+                      const std::string& name, const IInterface*  parent)
+                    : AthAlgTool(type, name, parent),
+ m_robDataProvider("ROBDataProviderSvc/ROBDataProviderSvc", name),
+ m_selectionTool("LVL1BS::TriggerTowerSelectionTool/TriggerTowerSelectionTool"),
+ m_ppmBSConverter("LVL1BS::PpmByteStreamSubsetTool/PpmByteStreamSubsetTool"),
+ m_JetConverter("LVL1BS::JepByteStreamV2Tool/JepByteStreamV2Tool"),
+ m_ttCol(0), m_jetCol(0)
+{
+  declareInterface<ITrigT1CaloDataAccess>(this);
+
+  declareProperty("ROBDataProviderSvc",        m_robDataProvider);
+  declareProperty("TriggerTowerSelectionTool", m_selectionTool);
+  declareProperty("PpmByteStreamSubsetTool",   m_ppmBSConverter);
+  declareProperty("JepByteStreamV2Tool",   m_JetConverter);
+
+}
+
+// Destructor
+
+TrigT1CaloDataAccess::~TrigT1CaloDataAccess()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode TrigT1CaloDataAccess::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << PACKAGE_VERSION << endreq;
+
+  // Retrieve data provider service
+
+  StatusCode sc = m_robDataProvider.retrieve();
+  if ( sc.isFailure() ) {
+    msg(MSG::ERROR) << "Failed to retrieve service " << m_robDataProvider
+                    << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved service " << m_robDataProvider << endreq;
+
+  // Retrieve selection tool
+
+  sc = m_selectionTool.retrieve();
+  if ( sc.isFailure() ) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_selectionTool << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_selectionTool << endreq;
+
+  // Retrieve PPM converter tool
+
+  sc = m_ppmBSConverter.retrieve();
+  if ( sc.isFailure() ) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_ppmBSConverter << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_ppmBSConverter << endreq;
+
+  sc = m_JetConverter.retrieve();
+  if ( sc.isFailure() ) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_JetConverter << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_JetConverter << endreq;
+
+  IIncidentSvc* p_incSvc;
+  if ( service("IncidentSvc",p_incSvc, true).isFailure() ) {
+          msg(MSG::ERROR) << "Unable to get the IncidentSvc" << endreq;
+  }else{
+  // Will use the new event for caching purposes
+  p_incSvc->addListener(this, "BeginEvent",100);
+  }
+
+
+  m_robs_full_je.push_back(0x0074000c); m_robs_full_je.push_back(0x0074000d);
+  m_robs_full_je.push_back(0x0074001c); m_robs_full_je.push_back(0x0074001d);
+  m_robs_full_je.push_back(0x0074002c); m_robs_full_je.push_back(0x0074002d);
+  m_robs_full_je.push_back(0x0074003c); m_robs_full_je.push_back(0x0074003d);
+
+  m_ttCol = new DataVector<LVL1::TriggerTower>(SG::VIEW_ELEMENTS);
+  // Better reserve the space. Some objects will be used for full
+  // unpacking and will be build in the first full event request
+  m_ttCol->reserve(5300);
+  m_chanIds.reserve(100);
+  m_chanIds_full.reserve(7400);
+  m_robs.reserve(36);
+  m_robs_full.reserve(36);
+  // First event selection
+  m_jetCol = new DataVector<LVL1::JetElement>;
+  m_jetCol->reserve(150);
+  m_first=true;
+
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode TrigT1CaloDataAccess::finalize()
+{
+  m_chanIds.clear();
+  m_chanIds_full.clear();
+  m_robs_full_je.clear();
+  m_robs_full.clear();
+  m_robs.clear();
+  m_ttCol->clear(); 
+  delete m_ttCol;
+  m_jetCol->clear(); 
+  delete m_jetCol;
+  return StatusCode::SUCCESS;
+}
+
+// Return iterators to required trigger towers
+
+StatusCode TrigT1CaloDataAccess::loadCollection(
+                     DataVector<LVL1::TriggerTower>::const_iterator& beg,
+	             DataVector<LVL1::TriggerTower>::const_iterator& end,
+	             const double etaMin, const double etaMax,
+	             const double phiMin, const double phiMax, const bool full=true)
+{
+
+  beg=end;
+  // It is possible to save good time for full scan using special
+  // pre-prepared tables. You have to state full=true
+  if ( !full ) {  m_chanIds.clear(); m_robs.clear(); }
+  // Get PPM sub-block channel IDs for wanted TriggerTowers
+  if ( m_first && full )
+  m_selectionTool->channelIDs(-5., 5., 0, 2*M_PI, m_chanIds_full);
+  if ( ! full )
+  m_selectionTool->channelIDs(etaMin, etaMax, phiMin, phiMax, m_chanIds);
+
+  // Get ROB IDs for wanted TriggerTowers
+  if ( m_first && full )
+  m_selectionTool->robIDs(m_chanIds_full, m_robs_full);
+  if ( ! full )
+  m_selectionTool->robIDs(m_chanIds, m_robs);
+
+  // Get data
+  m_robFrags.clear();
+
+  if ( full ){
+  m_robDataProvider->addROBData(m_robs_full);
+  m_robDataProvider->getROBData(m_robs_full, m_robFrags);
+  }else{
+  m_robDataProvider->addROBData(m_robs);
+  m_robDataProvider->getROBData(m_robs, m_robFrags);
+  }
+
+  // Convert to TriggerTowers
+  m_ttCol->clear();
+  StatusCode sc;
+  if ( full ) sc = m_ppmBSConverter->convert(m_robFrags, m_ttCol, m_chanIds_full);
+  else sc = m_ppmBSConverter->convert(m_robFrags, m_ttCol, m_chanIds);
+  if (sc.isFailure() ) {
+    msg(MSG::ERROR) << "PPM bytestream conversion failed" << endreq;
+    m_ttCol->clear();
+  }
+  beg = m_ttCol->begin();
+  end = m_ttCol->end();
+
+  if ( full && m_first ) m_first=false;
+  return sc;
+
+
+}
+
+StatusCode TrigT1CaloDataAccess::loadCollection(
+                      DataVector<LVL1::JetElement>::const_iterator& beg,
+		      DataVector<LVL1::JetElement>::const_iterator& end){
+
+  beg=end;
+  if ( m_present_event != m_lC_Jet ){
+  // Get data
+  m_robFrags.clear();
+  m_robDataProvider->addROBData(m_robs_full_je);
+  m_robDataProvider->getROBData(m_robs_full_je, m_robFrags);
+
+  m_jetCol->clear();
+  StatusCode sc = m_JetConverter->convert(m_robFrags, m_jetCol);
+  if (sc.isFailure() ) {
+    msg(MSG::ERROR) << "JET bytestream conversion failed" << endreq;
+    return sc;
+  }
+  m_lC_Jet = m_present_event;
+  }
+  beg = m_jetCol->begin();
+  end = m_jetCol->end();
+  
+
+  return StatusCode::SUCCESS;
+}
+
+void TrigT1CaloDataAccess::handle(const Incident & inc ) {
+         const EventIncident* eventInc  = dynamic_cast<const EventIncident*>(&inc);
+          if(!eventInc) {
+              std::cout << " Unable to get EventInfo from either EventStore or BeginRun incident" << std::endl;
+              return;
+          }
+          else {
+		const EventInfo* evt;
+		evt = &eventInc->eventInfo();
+		m_present_event = (evt->event_ID()->event_number());
+		m_ppmBSConverter->eventNumber(m_present_event);
+          }
+}
+
+
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/TrigT1CaloDataAccess.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/TrigT1CaloDataAccess.h
new file mode 100644
index 0000000000000000000000000000000000000000..51557ce07ba83b34121d6b5166f5d7c74ef028c7
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/TrigT1CaloDataAccess.h
@@ -0,0 +1,112 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_TRIGT1CALODATAACCESS_H
+#define TRIGT1CALOBYTESTREAM_TRIGT1CALODATAACCESS_H
+
+#include <string>
+#include <vector>
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "ByteStreamData/RawEvent.h"
+#include "DataModel/DataVector.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+#include "TrigT1CaloByteStream/ITrigT1CaloDataAccess.h"
+#include "GaudiKernel/IIncidentSvc.h"
+#include "GaudiKernel/IIncidentListener.h"
+#include "GaudiKernel/Incident.h"
+#include "EventInfo/EventInfo.h"
+#include "EventInfo/EventID.h"
+#include "EventInfo/EventIncident.h"
+
+class IInterface;
+class IROBDataProviderSvc;
+class StatusCode;
+
+namespace LVL1 {
+  class TriggerTower;
+  class JetElement;
+}
+
+namespace LVL1BS {
+
+class JepByteStreamV2Tool;
+class IPpmByteStreamSubsetTool;
+class ITriggerTowerSelectionTool;
+
+/** Tool to retrieve TriggerTowers corresponding to a given eta/phi range
+ *  from bytestream.
+ *
+ *  @author Peter Faulkner
+ */
+
+class TrigT1CaloDataAccess : virtual public ITrigT1CaloDataAccess,
+              public AthAlgTool, virtual public IIncidentListener {
+
+ public:
+   TrigT1CaloDataAccess(const std::string& type, const std::string& name,
+                        const IInterface* parent);
+   virtual ~TrigT1CaloDataAccess();
+
+   virtual StatusCode initialize();
+   virtual StatusCode finalize();
+
+   /// Return iterators to required trigger towers
+   virtual StatusCode loadCollection(
+                      DataVector<LVL1::TriggerTower>::const_iterator& beg,
+		      DataVector<LVL1::TriggerTower>::const_iterator& end,
+		      double etaMin, double etaMax,
+		      double phiMin, double phiMax,const bool full);
+
+   virtual StatusCode loadCollection(
+                      DataVector<LVL1::JetElement>::const_iterator& beg,
+		      DataVector<LVL1::JetElement>::const_iterator& end);
+
+   /** handle to init a new event */
+   void handle (const Incident& );
+
+ private:
+
+   /// Data fetching service
+   ServiceHandle<IROBDataProviderSvc>             m_robDataProvider;
+   /// Tool for selections
+   ToolHandle<LVL1BS::ITriggerTowerSelectionTool> m_selectionTool;
+   /// Tool for bytestream conversion
+   ToolHandle<LVL1BS::IPpmByteStreamSubsetTool>   m_ppmBSConverter;
+   /// Tool for Jep bytestream conversion
+   ToolHandle<LVL1BS::JepByteStreamV2Tool>   m_JetConverter;
+
+   /// ROB fragment pointers
+   std::vector<const OFFLINE_FRAGMENTS_NAMESPACE::ROBFragment*> m_robFrags;
+   /// Current TriggerTower sub-collection
+   DataVector<LVL1::TriggerTower>* m_ttCol;
+   /// Current JetElements sub-collection
+   DataVector<LVL1::JetElement>* m_jetCol;
+   // save loads of memory for re-use
+   // Get PPM sub-block channel IDs for wanted TriggerTowers
+   std::vector<unsigned int> m_chanIds;
+   // Get ROB IDs for wanted TriggerTowers
+   std::vector<uint32_t> m_robs;
+   // Get PPM sub-block channel IDs for wanted TriggerTowers
+   std::vector<unsigned int> m_chanIds_full;
+   // Get ROB IDs for wanted TriggerTowers
+   std::vector<uint32_t> m_robs_full;
+   // Get ROB IDs for wanted JetElements
+   std::vector<uint32_t> m_robs_full_je;
+   // First time full runs, we should cache everything
+   bool m_first;
+   // Help caching
+   unsigned int m_lC_Jet;
+   // Present Event Number 
+   unsigned int m_present_event;
+
+
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/TriggerTowerSelectionTool.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/TriggerTowerSelectionTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..57d0d0b9abd3055a34a1c930572052201efc7b71
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/TriggerTowerSelectionTool.cxx
@@ -0,0 +1,156 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include <cmath>
+#include <set>
+
+#include "eformat/SourceIdentifier.h"
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "TrigT1CaloUtils/TriggerTowerKey.h"
+#include "TrigT1CaloMappingToolInterfaces/IL1CaloMappingTool.h"
+
+#include "L1CaloSrcIdMap.h"
+
+#include "TriggerTowerSelectionTool.h"
+
+namespace LVL1BS {
+
+TriggerTowerSelectionTool::TriggerTowerSelectionTool(const std::string& type,
+                                         const std::string& name,
+					 const IInterface*  parent)
+ : AthAlgTool(type, name, parent),
+   m_mappingTool("LVL1::PpmMappingTool/PpmMappingTool"),
+   m_srcIdMap(0)
+{
+  declareInterface<ITriggerTowerSelectionTool>(this);
+
+  declareProperty("PpmMappingTool", m_mappingTool);
+
+  // Initialise m_etaBins
+  double base = -4.9;
+  const double width = 0.425;
+  const double offset1 = width/2.;
+  for (int i = 0; i < 4; ++i) {
+    m_etaBins.push_back(base + double(i)*width + offset1);
+  }
+  m_etaBins.push_back(-3.15);
+  m_etaBins.push_back(-3.0);
+  m_etaBins.push_back(-2.8);
+  m_etaBins.push_back(-2.6);
+  const double offset2 = 0.05;
+  for (int i = -25; i < 25; ++i) {
+    m_etaBins.push_back(double(i)/10. + offset2);
+  }
+  m_etaBins.push_back(2.6);
+  m_etaBins.push_back(2.8);
+  m_etaBins.push_back(3.0);
+  m_etaBins.push_back(3.15);
+  base = 3.2;
+  for (int i = 0; i < 4; ++i) {
+    m_etaBins.push_back(base + double(i)*width + offset1);
+  }
+}
+
+TriggerTowerSelectionTool::~TriggerTowerSelectionTool()
+{
+}
+
+// Initialisation
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode TriggerTowerSelectionTool::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << PACKAGE_VERSION << endreq;
+
+  // Retrieve mapping tool
+
+  StatusCode sc = m_mappingTool.retrieve();
+  if ( sc.isFailure() ) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_mappingTool << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_mappingTool << endreq;
+
+  m_srcIdMap = new L1CaloSrcIdMap();
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode TriggerTowerSelectionTool::finalize()
+{
+  delete m_srcIdMap;
+
+  return StatusCode::SUCCESS;
+}
+
+// Return a list of TT channel IDs for given eta/phi range
+
+void TriggerTowerSelectionTool::channelIDs(const double etaMin,
+                                           const double etaMax,
+                                           const double phiMin,
+					   const double phiMax,
+					   std::vector<unsigned int>& chanIds)
+{
+  std::set<unsigned int> chanSet;
+  std::vector<double>::const_iterator etaPos  = m_etaBins.begin();
+  std::vector<double>::const_iterator etaPosE = m_etaBins.end();
+  for (; etaPos != etaPosE; ++etaPos) {
+    const double eta = *etaPos;
+    if (eta < etaMin) continue;
+    if (eta > etaMax) break;
+    const double absEta = (eta < 0.) ? -eta : eta;
+    const int phiBins = (absEta > 3.2) ? 16 : (absEta > 2.5) ? 32 : 64;
+    const double phiGran = 2.*M_PI/phiBins;
+    for (int bin = 0; bin < phiBins; ++bin) {
+      const double phi = phiGran*(double(bin) + 0.5);
+      if (phi < phiMin) continue;
+      if (phi > phiMax) break;
+      int crate, module, channel;
+      m_mappingTool->mapping(eta, phi, 0, crate, module, channel);
+      unsigned int id = (crate * 16 + module) * 64 + channel;
+      chanSet.insert(id);
+      m_mappingTool->mapping(eta, phi, 1, crate, module, channel);
+      id = (crate * 16 + module) * 64 + channel;
+      chanSet.insert(id);
+    }
+  }
+  std::set<unsigned int>::const_iterator setPos  = chanSet.begin();
+  std::set<unsigned int>::const_iterator setPosE = chanSet.end();
+  for (; setPos != setPosE; ++setPos) chanIds.push_back(*setPos);
+}
+
+// Return a list of ROB IDs for given list of TT channel IDs
+
+void TriggerTowerSelectionTool::robIDs(const std::vector<unsigned int>& chanIds,
+                                             std::vector<uint32_t>& robs)
+{
+  const int channels = 64;
+  const int modules  = 16;
+  const int slinks   = 4;
+  const int daqOrRoi = 0;
+  std::set<uint32_t> robSet;
+  std::vector<unsigned int>::const_iterator pos  = chanIds.begin();
+  std::vector<unsigned int>::const_iterator posE = chanIds.end();
+  for (; pos != posE; ++pos) {
+    const int chanId = *pos;
+    const int crate  = chanId / (channels * modules);
+    const int module = (chanId / channels) % modules;
+    const int slink  = module / slinks;
+    const uint32_t rodId = m_srcIdMap->getRodID(crate, slink, daqOrRoi,
+                                                eformat::TDAQ_CALO_PREPROC);
+    const uint32_t robId = m_srcIdMap->getRobID(rodId);
+    robSet.insert(robId);
+  }
+  std::set<uint32_t>::const_iterator setPos  = robSet.begin();
+  std::set<uint32_t>::const_iterator setPosE = robSet.end();
+  for (; setPos != setPosE; ++setPos) robs.push_back(*setPos);
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/TriggerTowerSelectionTool.h b/Trigger/TrigT1/TrigT1CaloByteStream/src/TriggerTowerSelectionTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..e8ca8151b0d547d1d21895eded4bbd28aa74099d
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/TriggerTowerSelectionTool.h
@@ -0,0 +1,65 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_TRIGGERTOWERSELECTIONTOOL_H
+#define TRIGT1CALOBYTESTREAM_TRIGGERTOWERSELECTIONTOOL_H
+
+#include <stdint.h>
+#include <vector>
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "GaudiKernel/ToolHandle.h"
+
+#include "ITriggerTowerSelectionTool.h"
+
+class IInterface;
+class StatusCode;
+
+namespace LVL1 {
+  class IL1CaloMappingTool;
+}
+
+namespace LVL1BS {
+
+class L1CaloSrcIdMap;
+
+/** TriggerTower subset selection for bytestream.
+ *
+ *  @author Peter Faulkner
+ */
+
+class TriggerTowerSelectionTool : virtual public ITriggerTowerSelectionTool,
+                                          public AthAlgTool {
+
+ public:
+
+   TriggerTowerSelectionTool(const std::string& type, const std::string& name,
+                                                const IInterface* parent);
+   virtual ~TriggerTowerSelectionTool();
+
+   virtual StatusCode initialize();
+   virtual StatusCode finalize();
+
+   /// Return a list of TT channel IDs for given eta/phi range
+   virtual void channelIDs(double etaMin, double etaMax,
+                           double phiMin, double phiMax,
+			   std::vector<unsigned int>& chanIds);
+   /// Return a list of ROB IDs for given list of TT channel IDs
+   virtual void robIDs(const std::vector<unsigned int>& chanIds,
+                             std::vector<uint32_t>& robs);
+
+ private:
+
+   /// Tool for mappings
+   ToolHandle<LVL1::IL1CaloMappingTool> m_mappingTool;
+   /// Source ID converter
+   L1CaloSrcIdMap* m_srcIdMap;
+   /// TT eta bins
+   std::vector<double> m_etaBins;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/components/TrigT1CaloByteStream_entries.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/components/TrigT1CaloByteStream_entries.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..a77649f788ff4793fd633564e97870d91d76e703
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/components/TrigT1CaloByteStream_entries.cxx
@@ -0,0 +1,215 @@
+
+#include "DataModel/DataVector.h"
+#include "GaudiKernel/DeclareFactoryEntries.h"
+
+// Post-LS1
+#include "TrigT1CaloEvent/CMXCPTob.h"
+#include "TrigT1CaloEvent/CMXCPHits.h"
+#include "TrigT1CaloEvent/CMXEtSums.h"
+#include "TrigT1CaloEvent/CMXJetTob.h"
+#include "TrigT1CaloEvent/CMXJetHits.h"
+#include "TrigT1CaloEvent/CMXRoI.h"
+#include "TrigT1CaloEvent/JEMTobRoI.h"
+// Pre-LS1
+#include "TrigT1CaloEvent/CPMHits.h"
+#include "TrigT1CaloEvent/CMMCPHits.h"
+#include "TrigT1CaloEvent/CMMEtSums.h"
+#include "TrigT1CaloEvent/CMMJetHits.h"
+#include "TrigT1CaloEvent/CMMRoI.h"
+#include "TrigT1CaloEvent/JEMHits.h"
+#include "TrigT1CaloEvent/JEMRoI.h"
+// Both
+#include "TrigT1CaloEvent/CPMTower.h"
+#include "TrigT1CaloEvent/JEMEtSums.h"
+#include "TrigT1CaloEvent/JetElement.h"
+
+// Post-LS1
+#include "../CpByteStreamV2Cnv.h"
+#include "../CpmRoiByteStreamV2Cnv.h"
+#include "../CpReadByteStreamV2Cnv.h"
+#include "../JepByteStreamV2Cnv.h"
+#include "../JepReadByteStreamV2Cnv.h"
+#include "../JepRoiByteStreamV2Cnv.h"
+#include "../JepRoiReadByteStreamV2Cnv.h"
+// Pre-LS1
+#include "../CpByteStreamV1Cnv.h"
+#include "../CpmRoiByteStreamV1Cnv.h"
+#include "../CpReadByteStreamV1Cnv.h"
+#include "../JepByteStreamV1Cnv.h"
+#include "../JepReadByteStreamV1Cnv.h"
+#include "../JepRoiByteStreamV1Cnv.h"
+#include "../JepRoiReadByteStreamV1Cnv.h"
+// Both
+#include "../CpReadByteStreamV1V2Cnv.h"
+#include "../JepReadByteStreamV1V2Cnv.h"
+#include "../PpmByteStreamCnv.h"
+#include "../RodHeaderByteStreamCnv.h"
+#include "../L1CaloErrorByteStreamCnv.h"
+
+// Post-LS1
+#include "../CpByteStreamV2Tool.h"
+#include "../CpmRoiByteStreamV2Tool.h"
+#include "../JepByteStreamV2Tool.h"
+#include "../JepRoiByteStreamV2Tool.h"
+// Pre-LS1
+#include "../CpByteStreamV1Tool.h"
+#include "../CpmRoiByteStreamV1Tool.h"
+#include "../JepByteStreamV1Tool.h"
+#include "../JepRoiByteStreamV1Tool.h"
+// Both
+#include "../PpmByteStreamTool.h"
+#include "../RodHeaderByteStreamTool.h"
+#include "../L1CaloErrorByteStreamTool.h"
+
+#include "../PpmByteStreamSubsetTool.h"
+#include "../TriggerTowerSelectionTool.h"
+#include "../TrigT1CaloDataAccess.h"
+
+namespace LVL1BS {
+
+// Post-LS1
+typedef DataVector<LVL1::CMXCPTob>   CMXCPTobCollection;
+typedef DataVector<LVL1::CMXCPHits>  CMXCPHitsCollection;
+typedef DataVector<LVL1::CMXJetTob>  CMXJetTobCollection;
+typedef DataVector<LVL1::CMXJetHits> CMXJetHitsCollection;
+typedef DataVector<LVL1::CMXEtSums>  CMXEtSumsCollection;
+typedef DataVector<LVL1::JEMTobRoI>  JEMTobRoICollection;
+// Pre-LS1
+typedef DataVector<LVL1::CPMHits>    CPMHitsCollection;
+typedef DataVector<LVL1::CMMCPHits>  CMMCPHitsCollection;
+typedef DataVector<LVL1::CMMJetHits> CMMJetHitsCollection;
+typedef DataVector<LVL1::CMMEtSums>  CMMEtSumsCollection;
+typedef DataVector<LVL1::JEMHits>    JEMHitsCollection;
+typedef DataVector<LVL1::JEMRoI>     JEMRoICollection;
+// Both
+typedef DataVector<LVL1::CPMTower>   CPMTowerCollection;
+typedef DataVector<LVL1::JetElement> JetElementCollection;
+typedef DataVector<LVL1::JEMEtSums>  JEMEtSumsCollection;
+
+// Post-LS1
+typedef CpReadByteStreamV2Cnv<CMXCPTobCollection>      CpReadCRByteStreamV2CnvT;
+typedef CpReadByteStreamV2Cnv<CMXCPHitsCollection>     CpReadCCByteStreamV2CnvT;
+typedef JepReadByteStreamV2Cnv<CMXJetTobCollection>    JepReadCTByteStreamV2CnvT;
+typedef JepReadByteStreamV2Cnv<CMXJetHitsCollection>   JepReadCJByteStreamV2CnvT;
+typedef JepReadByteStreamV2Cnv<CMXEtSumsCollection>    JepReadCEByteStreamV2CnvT;
+typedef JepRoiReadByteStreamV2Cnv<JEMTobRoICollection> JepRoiReadJRByteStreamV2CnvT;
+typedef JepRoiReadByteStreamV2Cnv<LVL1::CMXRoI>        JepRoiReadCRByteStreamV2CnvT;
+// Pre-LS1
+typedef CpReadByteStreamV1Cnv<CPMHitsCollection>       CpReadCHByteStreamV1CnvT;
+typedef CpReadByteStreamV1Cnv<CMMCPHitsCollection>     CpReadCCByteStreamV1CnvT;
+typedef JepReadByteStreamV1Cnv<CMMJetHitsCollection>   JepReadCJByteStreamV1CnvT;
+typedef JepReadByteStreamV1Cnv<CMMEtSumsCollection>    JepReadCEByteStreamV1CnvT;
+typedef JepReadByteStreamV1Cnv<JEMHitsCollection>      JepReadJHByteStreamV1CnvT;
+typedef JepRoiReadByteStreamV1Cnv<JEMRoICollection>    JepRoiReadJRByteStreamV1CnvT;
+typedef JepRoiReadByteStreamV1Cnv<LVL1::CMMRoI>        JepRoiReadCRByteStreamV1CnvT;
+// Both
+typedef JepReadByteStreamV1V2Cnv<JetElementCollection> JepReadJEByteStreamV1V2CnvT;
+typedef JepReadByteStreamV1V2Cnv<JEMEtSumsCollection>  JepReadESByteStreamV1V2CnvT;
+}
+
+// declare 
+// Post-LS1
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, CpByteStreamV2Cnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, CpmRoiByteStreamV2Cnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, CpReadCRByteStreamV2CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, CpReadCCByteStreamV2CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepByteStreamV2Cnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepRoiByteStreamV2Cnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepReadCTByteStreamV2CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepReadCJByteStreamV2CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepReadCEByteStreamV2CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepRoiReadJRByteStreamV2CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepRoiReadCRByteStreamV2CnvT )
+// Pre-LS1
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, CpByteStreamV1Cnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, CpmRoiByteStreamV1Cnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, CpReadCHByteStreamV1CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, CpReadCCByteStreamV1CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepByteStreamV1Cnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepRoiByteStreamV1Cnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepReadCJByteStreamV1CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepReadCEByteStreamV1CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepReadJHByteStreamV1CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepRoiReadJRByteStreamV1CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepRoiReadCRByteStreamV1CnvT )
+// Both
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, CpReadByteStreamV1V2Cnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepReadJEByteStreamV1V2CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepReadESByteStreamV1V2CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, PpmByteStreamCnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, RodHeaderByteStreamCnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, L1CaloErrorByteStreamCnv )
+
+// Post-LS1
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, CpByteStreamV2Tool )
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, CpmRoiByteStreamV2Tool )
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, JepByteStreamV2Tool )
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, JepRoiByteStreamV2Tool )
+// Pre-LS1
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, CpByteStreamV1Tool )
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, CpmRoiByteStreamV1Tool )
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, JepByteStreamV1Tool )
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, JepRoiByteStreamV1Tool )
+// Both
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, PpmByteStreamTool )
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, RodHeaderByteStreamTool )
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, L1CaloErrorByteStreamTool )
+
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, PpmByteStreamSubsetTool )
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, TriggerTowerSelectionTool )
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, TrigT1CaloDataAccess )
+
+DECLARE_FACTORY_ENTRIES( TrigT1CaloByteStream )
+{
+  // Post-LS1
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, CpByteStreamV2Cnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, CpmRoiByteStreamV2Cnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, CpReadCRByteStreamV2CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, CpReadCCByteStreamV2CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepByteStreamV2Cnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepRoiByteStreamV2Cnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepReadCTByteStreamV2CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepReadCJByteStreamV2CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepReadCEByteStreamV2CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepRoiReadJRByteStreamV2CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepRoiReadCRByteStreamV2CnvT )
+  // Pre-LS1
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, CpByteStreamV1Cnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, CpmRoiByteStreamV1Cnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, CpReadCHByteStreamV1CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, CpReadCCByteStreamV1CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepByteStreamV1Cnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepRoiByteStreamV1Cnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepReadCJByteStreamV1CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepReadCEByteStreamV1CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepReadJHByteStreamV1CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepRoiReadJRByteStreamV1CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepRoiReadCRByteStreamV1CnvT )
+  // Both
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, CpReadByteStreamV1V2Cnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepReadJEByteStreamV1V2CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepReadESByteStreamV1V2CnvT )
+  // Both
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, PpmByteStreamCnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, RodHeaderByteStreamCnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, L1CaloErrorByteStreamCnv )
+
+  // Post-LS1
+  DECLARE_NAMESPACE_TOOL( LVL1BS, CpByteStreamV2Tool )
+  DECLARE_NAMESPACE_TOOL( LVL1BS, CpmRoiByteStreamV2Tool )
+  DECLARE_NAMESPACE_TOOL( LVL1BS, JepByteStreamV2Tool )
+  DECLARE_NAMESPACE_TOOL( LVL1BS, JepRoiByteStreamV2Tool )
+  // Pre-LS1
+  DECLARE_NAMESPACE_TOOL( LVL1BS, CpByteStreamV1Tool )
+  DECLARE_NAMESPACE_TOOL( LVL1BS, CpmRoiByteStreamV1Tool )
+  DECLARE_NAMESPACE_TOOL( LVL1BS, JepByteStreamV1Tool )
+  DECLARE_NAMESPACE_TOOL( LVL1BS, JepRoiByteStreamV1Tool )
+  // Both
+  DECLARE_NAMESPACE_TOOL( LVL1BS, PpmByteStreamTool )
+  DECLARE_NAMESPACE_TOOL( LVL1BS, RodHeaderByteStreamTool )
+  DECLARE_NAMESPACE_TOOL( LVL1BS, L1CaloErrorByteStreamTool )
+
+  DECLARE_NAMESPACE_TOOL( LVL1BS, PpmByteStreamSubsetTool )
+  DECLARE_NAMESPACE_TOOL( LVL1BS, TriggerTowerSelectionTool )
+  DECLARE_NAMESPACE_TOOL( LVL1BS, TrigT1CaloDataAccess )
+}
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/src/components/TrigT1CaloByteStream_load.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/src/components/TrigT1CaloByteStream_load.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..f713087d5d67558f722ea8b708eaa09389bc6958
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/src/components/TrigT1CaloByteStream_load.cxx
@@ -0,0 +1,5 @@
+
+#include "GaudiKernel/LoadFactoryEntries.h"
+
+LOAD_FACTORY_ENTRIES(TrigT1CaloByteStream)
+
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmErrors.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmErrors.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..9e6f65544d37cffc604dfee4a51047206597a290
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmErrors.cxx
@@ -0,0 +1,483 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "StoreGate/StoreGateSvc.h"
+#include "EventInfo/EventInfo.h"
+#include "EventInfo/EventID.h"
+
+#include "TrigT1CaloEvent/CMXCPTob.h"
+#include "TrigT1CaloEvent/CMXCPHits.h"
+#include "TrigT1CaloEvent/CPMTobRoI.h"
+#include "TrigT1CaloEvent/CPMTower.h"
+#include "TrigT1CaloUtils/CoordToHardware.h"
+#include "TrigT1CaloUtils/DataError.h"
+#include "TrigT1Interfaces/Coordinate.h"
+#include "TrigT1Interfaces/TrigT1CaloDefs.h"
+
+#include "CpmErrors.h"
+
+namespace LVL1BS {
+
+CpmErrors::CpmErrors(const std::string& name, ISvcLocator* pSvcLocator)
+  : AthAlgorithm(name, pSvcLocator),
+    m_doneThisEvent(false),
+    m_doneEmParity(false),
+    m_doneHadParity(false),
+    m_doneEmLinkDown(false),
+    m_doneHadLinkDown(false),
+    m_doneEmReadoutA(false),
+    m_doneEmReadoutB(false),
+    m_doneEmReadoutC(false),
+    m_doneHadReadoutA(false),
+    m_doneHadReadoutB(false),
+    m_doneHadReadoutC(false),
+    m_doneCmxTobParityM(false),
+    m_doneCmxTobParity0(false),
+    m_doneCmxTobParity1(false),
+    m_doneCmxTobParity2(false),
+    m_doneCmxTobParity3(false),
+    m_doneTobEnReadoutA(false),
+    m_doneTobEnReadoutB(false),
+    m_doneTobEnReadoutC(false),
+    m_doneTobIsReadoutA(false),
+    m_doneTobIsReadoutB(false),
+    m_doneTobIsReadoutC(false),
+    m_doneCmxHitsParity0(false),
+    m_doneCmxHitsParity1(false)
+
+{
+  declareProperty("CPMTowerLocation",
+           m_cpmTowerLocation  = LVL1::TrigT1CaloDefs::CPMTowerLocation);
+  declareProperty("CMXCPTobLocation",
+           m_cmxCpTobLocation  = LVL1::TrigT1CaloDefs::CMXCPTobLocation);
+  declareProperty("CMXCPHitsLocation",
+           m_cmxCpHitsLocation = LVL1::TrigT1CaloDefs::CMXCPHitsLocation);
+  declareProperty("CPMTobRoILocation",
+           m_cpmTobRoiLocation = LVL1::TrigT1CaloDefs::CPMTobRoILocation);
+  declareProperty("CPMTowerLocationOut",
+           m_cpmTowerLocationOut  = LVL1::TrigT1CaloDefs::CPMTowerLocation+"Errors");
+  declareProperty("CMXCPTobLocationOut",
+           m_cmxCpTobLocationOut  = LVL1::TrigT1CaloDefs::CMXCPTobLocation+"Errors");
+  declareProperty("CMXCPHitsLocationOut",
+           m_cmxCpHitsLocationOut = LVL1::TrigT1CaloDefs::CMXCPHitsLocation+"Errors");
+  declareProperty("CPMTobRoILocationOut",
+           m_cpmTobRoiLocationOut = LVL1::TrigT1CaloDefs::CPMTobRoILocation+"Errors");
+
+  declareProperty("CPMTowerErrors",  m_cpmTowerErrors  = 0xffff);
+  declareProperty("CMXCPTobErrors",  m_cmxTobErrors    = 0xffff);
+  declareProperty("CMXCPHitsErrors", m_cmxHitsErrors   = 0xffff);
+  declareProperty("CPMTobRoIErrors", m_cpmTobRoiErrors = 0xffff);
+
+}
+
+CpmErrors::~CpmErrors()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode CpmErrors::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << /* version() */ PACKAGE_VERSION << endreq;
+
+  // Initialise individual error flags
+
+  m_doneEmParity     = ((m_cpmTowerErrors&Error1) == 0);
+  m_doneHadParity    = ((m_cpmTowerErrors&Error2) == 0);
+  m_doneEmLinkDown   = ((m_cpmTowerErrors&Error3) == 0);
+  m_doneHadLinkDown  = ((m_cpmTowerErrors&Error4) == 0);
+  m_doneEmReadoutA   = ((m_cpmTowerErrors&Error5) == 0);
+  m_doneEmReadoutB   = ((m_cpmTowerErrors&Error6) == 0);
+  m_doneEmReadoutC   = ((m_cpmTowerErrors&Error7) == 0);
+  m_doneHadReadoutA  = ((m_cpmTowerErrors&Error8) == 0);
+  m_doneHadReadoutB  = ((m_cpmTowerErrors&Error9) == 0);
+  m_doneHadReadoutC  = ((m_cpmTowerErrors&Error10) == 0);
+  m_doneCpmSubStatus = ((m_cpmTowerErrors&Error11) == 0);
+
+  m_doneCmxTobParityM = ((m_cmxTobErrors&Error1) == 0);
+  m_doneCmxTobParity0 = ((m_cmxTobErrors&Error2) == 0);
+  m_doneCmxTobParity1 = ((m_cmxTobErrors&Error3) == 0);
+  m_doneCmxTobParity2 = ((m_cmxTobErrors&Error4) == 0);
+  m_doneCmxTobParity3 = ((m_cmxTobErrors&Error5) == 0);
+  m_doneTobEnReadoutA = ((m_cmxTobErrors&Error6) == 0);
+  m_doneTobEnReadoutB = ((m_cmxTobErrors&Error7) == 0);
+  m_doneTobEnReadoutC = ((m_cmxTobErrors&Error8) == 0);
+  m_doneTobIsReadoutA = ((m_cmxTobErrors&Error9) == 0);
+  m_doneTobIsReadoutB = ((m_cmxTobErrors&Error10) == 0);
+  m_doneTobIsReadoutC = ((m_cmxTobErrors&Error11) == 0);
+
+  m_doneCmxHitsParity0 = ((m_cmxHitsErrors&Error1) == 0);
+  m_doneCmxHitsParity1 = ((m_cmxHitsErrors&Error2) == 0);
+
+  return StatusCode::SUCCESS;
+}
+
+// Execute
+
+StatusCode CpmErrors::execute()
+{
+  if ( !msgLvl(MSG::INFO) ) return StatusCode::SUCCESS;
+  msg(MSG::INFO);
+
+  m_doneThisEvent = false;
+
+  if (m_cpmTowerErrors) {
+
+    // Find CPM towers
+
+    const CpmTowerCollection* cpCollection = 0;
+    StatusCode sc = evtStore()->retrieve(cpCollection, m_cpmTowerLocation);
+    if (sc.isFailure() || !cpCollection || cpCollection->empty()) {
+      msg() << "No CP Elements found" << endreq;
+      cpCollection = 0;
+    }
+
+    // Generate CPM tower errors
+
+    CpmTowerCollection* errCollection = new CpmTowerCollection;
+    if (cpCollection) {
+      cpmTowerErrors(cpCollection, errCollection);
+    }
+
+    // Save error collection
+
+    sc = evtStore()->record(errCollection, m_cpmTowerLocationOut);
+    if (sc.isFailure()) {
+      msg(MSG::ERROR) << "Error recording CPMTower container in TDS " << endreq;
+      return sc;
+    }
+  }
+
+  if (m_cmxTobErrors) {
+
+    // Find CMX TOBs
+
+    const CmxCpTobCollection* tobCollection = 0;
+    StatusCode sc = evtStore()->retrieve(tobCollection, m_cmxCpTobLocation);
+    if (sc.isFailure() || !tobCollection || tobCollection->empty()) {
+      msg() << "No CMX TOBs found" << endreq;
+      tobCollection = 0;
+    }
+
+    // Generate CMX TOB errors
+
+    CmxCpTobCollection* errCollection = new CmxCpTobCollection;
+    if (tobCollection) {
+      cmxTobErrors(tobCollection, errCollection);
+    }
+
+    // Save error collection
+
+    sc = evtStore()->record(errCollection, m_cmxCpTobLocationOut);
+    if (sc.isFailure()) {
+      msg(MSG::ERROR) << "Error recording CMXCPTob container in TDS " << endreq;
+      return sc;
+    }
+  }
+
+  if (m_cmxHitsErrors) {
+
+    // Find CMX hits
+
+    const CmxCpHitsCollection* hitCollection = 0;
+    StatusCode sc = evtStore()->retrieve(hitCollection, m_cmxCpHitsLocation);
+    if (sc.isFailure() || !hitCollection || hitCollection->empty()) {
+      msg() << "No CMX Hits found" << endreq;
+      hitCollection = 0;
+    }
+
+    // Generate CMX Hits errors
+
+    CmxCpHitsCollection* errCollection = new CmxCpHitsCollection;
+    if (hitCollection) {
+      cmxHitsErrors(hitCollection, errCollection);
+    }
+
+    // Save error collection
+
+    sc = evtStore()->record(errCollection, m_cmxCpHitsLocationOut);
+    if (sc.isFailure()) {
+      msg(MSG::ERROR) << "Error recording CMXCPHits container in TDS " << endreq;
+      return sc;
+    }
+  }
+/*
+  if (m_cpmTobRoiErrors) {
+
+    // Find CPM TOB RoIs
+
+    const CpmTobRoiCollection* crCollection = 0;
+    StatusCode sc = evtStore()->retrieve(crCollection, m_cpmTobRoiLocation);
+    if (sc.isFailure() || !jcCollection || crCollection->empty()) {
+      msg() << "No CPM TOB RoIs found" << endreq;
+      crCollection = 0;
+    }
+
+    // Generate CPM RoI errors
+
+    CpmTobRoiCollection* errCollection = new CpmTobRoiCollection;
+    if (crCollection) {
+      cpmTobRoiErrors(crCollection, errCollection);
+    }
+
+    // Save error collection
+
+    sc = evtStore()->record(errCollection, m_cpmTobRoiLocationOut);
+    if (sc.isFailure()) {
+      msg(MSG::ERROR) << "Error recording CPMTobRoI container in TDS " << endreq;
+      return sc;
+    }
+  }
+*/
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode CpmErrors::finalize()
+{
+  return StatusCode::SUCCESS;
+}
+
+// Generate CPMTower errors
+
+void CpmErrors::cpmTowerErrors(const CpmTowerCollection* cpCollection,
+                                     CpmTowerCollection* errCollection)
+{
+  LVL1::CoordToHardware converter;
+  CpmTowerCollection::const_iterator iter    = cpCollection->begin();
+  CpmTowerCollection::const_iterator iterEnd = cpCollection->end();
+  for (; iter != iterEnd; ++iter) {
+    const LVL1::CPMTower* const cp = *iter;
+    const double eta = cp->eta();
+    const double phi = cp->phi();
+    const LVL1::Coordinate coord(phi, eta);
+    const bool overlap = (converter.cpCrateOverlap(coord) <= 3);
+    const int peak = cp->peak();
+    std::vector<int> emEnergy(cp->emEnergyVec());
+    std::vector<int> hadEnergy(cp->hadEnergyVec());
+    std::vector<int> emError(cp->emErrorVec());
+    std::vector<int> hadError(cp->hadErrorVec());
+    if (!m_doneThisEvent && !overlap) {
+      std::string errorType = "";
+      if (!m_doneEmParity) {
+        emError[peak] |= (1 << LVL1::DataError::Parity);
+        errorType = "EM Parity";
+        m_doneEmParity = true;
+      } else if (!m_doneHadParity) {
+        hadError[peak] |= (1 << LVL1::DataError::Parity);
+        errorType = "Had Parity";
+        m_doneHadParity = true;
+      } else if (!m_doneEmLinkDown) {
+        emError[peak] |= (1 << LVL1::DataError::LinkDown);
+        errorType = "EM Link Down";
+        m_doneEmLinkDown = true;
+      } else if (!m_doneHadLinkDown) {
+        hadError[peak] |= (1 << LVL1::DataError::LinkDown);
+        errorType = "Had Link Down";
+        m_doneHadLinkDown = true;
+      } else if (!m_doneEmReadoutA && emEnergy[peak] != 0 &&
+                 emEnergy[peak] < (s_saturation-1)) {
+        emEnergy[peak] += 1;
+        errorType = "EM readout non-zero mismatch";
+        m_doneEmReadoutA = true;
+      } else if (!m_doneEmReadoutB && emEnergy[peak] == 0) {
+        emEnergy[peak] = 1;
+        errorType = "EM readout sim zero mismatch";
+        m_doneEmReadoutB = true;
+      } else if (!m_doneEmReadoutC && emEnergy[peak] != 0 &&
+                                      hadEnergy[peak] != 0) {
+        emEnergy[peak] = 0;
+        errorType = "EM readout data zero mismatch";
+        m_doneEmReadoutC = true;
+      } else if (!m_doneHadReadoutA && hadEnergy[peak] != 0 &&
+                 hadEnergy[peak] < (s_saturation-1)) {
+        hadEnergy[peak] += 1;
+        errorType = "Had readout non-zero mismatch";
+        m_doneHadReadoutA = true;
+      } else if (!m_doneHadReadoutB && hadEnergy[peak] == 0) {
+        hadEnergy[peak] = 1;
+        errorType = "Had readout sim zero mismatch";
+        m_doneHadReadoutB = true;
+      } else if (!m_doneHadReadoutC && hadEnergy[peak] != 0 &&
+                                       emEnergy[peak] != 0) {
+        hadEnergy[peak] = 0;
+        errorType = "Had readout data zero mismatch";
+        m_doneHadReadoutC = true;
+      } else if (!m_doneCpmSubStatus) {
+        emError[peak]  |= (1 << LVL1::DataError::BCNMismatch);
+        hadError[peak] |= (1 << LVL1::DataError::BCNMismatch);
+        errorType = "SubStatus";
+        m_doneCpmSubStatus = true;
+      }
+      if (errorType != "") {
+        errorMessage("CPMTowers " + errorType);
+        m_doneThisEvent = true;
+      }
+    }
+    LVL1::CPMTower* err = new LVL1::CPMTower(phi, eta, emEnergy, emError,
+                                             hadEnergy, hadError, peak);
+    errCollection->push_back(err);
+  }
+}
+
+// Generate CMXCPTob errors
+
+void CpmErrors::cmxTobErrors(const CmxCpTobCollection* tobCollection,
+                                   CmxCpTobCollection* errCollection)
+{
+  CmxCpTobCollection::const_iterator iter    = tobCollection->begin();
+  CmxCpTobCollection::const_iterator iterEnd = tobCollection->end();
+  for (; iter != iterEnd; ++iter) {
+    const LVL1::CMXCPTob* const tob = *iter;
+    const int crate = tob->crate();
+    const int cmx   = tob->cmx();
+    const int cpm   = tob->cpm();
+          int chip  = tob->chip();
+          int loc   = tob->location();
+    const int peak  = tob->peak();
+    std::vector<int> energy(tob->energyVec());
+    std::vector<int> isolation(tob->isolationVec());
+    std::vector<int> error(tob->errorVec());
+    std::vector<unsigned int> presence(tob->presenceMapVec());
+    if (!m_doneThisEvent) {
+      std::string errorType = "";
+      if (!m_doneCmxTobParityM && chip != 0) {
+	// NB. Changes real data path
+        error[peak] |= (1 << LVL1::DataError::ParityMerge);
+	chip = 0;
+	loc  = 0;
+	energy[peak] = 0;
+	isolation[peak] = 0;
+        errorType = "CMX-CP TOB Parity Merge";
+	m_doneCmxTobParityM = true;
+      } else if (!m_doneCmxTobParity0) {
+        error[peak] |= (1 << LVL1::DataError::ParityPhase0);
+        errorType = "CMX-CP TOB Parity Phase 0";
+        m_doneCmxTobParity0 = true;
+      } else if (!m_doneCmxTobParity1) {
+        error[peak] |= (1 << LVL1::DataError::ParityPhase1);
+        errorType = "CMX-CP TOB Parity Phase 1";
+        m_doneCmxTobParity1 = true;
+      } else if (!m_doneCmxTobParity2) {
+        error[peak] |= (1 << LVL1::DataError::ParityPhase2);
+        errorType = "CMX-CP TOB Parity Phase 2";
+        m_doneCmxTobParity2 = true;
+      } else if (!m_doneCmxTobParity3) {
+        error[peak] |= (1 << LVL1::DataError::ParityPhase3);
+        errorType = "CMX-CP TOB Parity Phase 3";
+        m_doneCmxTobParity3 = true;
+      } else if (!m_doneTobEnReadoutA && energy[peak] != 0) {
+        energy[peak] += 1;
+        errorType = "CMX-CP TOB Energy readout non-zero mismatch";
+        m_doneTobEnReadoutA = true;
+      } else if (!m_doneTobEnReadoutB && energy[peak] == 0) { // never happen?
+        energy[peak] = 1;
+        errorType = "CMX-CP TOB Energy readout sim zero mismatch";
+        m_doneTobEnReadoutB = true;
+      } else if (!m_doneTobEnReadoutC && energy[peak] != 0) {
+        energy[peak] = 0;
+        errorType = "CMX-CP TOB Energy readout data zero mismatch";
+        m_doneTobEnReadoutC = true;
+      } else if (!m_doneTobIsReadoutA && isolation[peak] != 0) {
+        isolation[peak] += 1;
+        errorType = "CMX-CP TOB Isolation readout non-zero mismatch";
+        m_doneTobIsReadoutA = true;
+      } else if (!m_doneTobIsReadoutB && isolation[peak] == 0) {
+        isolation[peak] = 1;
+        errorType = "CMX-CP TOB Isolation readout sim zero mismatch";
+        m_doneTobIsReadoutB = true;
+      } else if (!m_doneTobIsReadoutC && isolation[peak] != 0) {
+        isolation[peak] = 0;
+        errorType = "CMX-CP TOB Isolation readout data zero mismatch";
+        m_doneTobIsReadoutC = true;
+      }
+      if (errorType != "") {
+        errorMessage("CMXCPTob " + errorType);
+        m_doneThisEvent = true;
+      }
+    }
+    LVL1::CMXCPTob* err = new LVL1::CMXCPTob(crate, cmx, cpm, chip, loc,
+                                             energy, isolation, error,
+					     presence, peak);
+    errCollection->push_back(err);
+  }
+}
+
+// Generate CMXCPHits errors
+
+void CpmErrors::cmxHitsErrors(const CmxCpHitsCollection* hitCollection,
+                                    CmxCpHitsCollection* errCollection)
+{
+  CmxCpHitsCollection::const_iterator iter    = hitCollection->begin();
+  CmxCpHitsCollection::const_iterator iterEnd = hitCollection->end();
+  for (; iter != iterEnd; ++iter) {
+    const LVL1::CMXCPHits* const hits = *iter;
+    const int crate  = hits->crate();
+    const int cmx    = hits->cmx();
+    const int source = hits->source();
+    const int peak   = hits->peak();
+    std::vector<unsigned int> hits0(hits->hitsVec0());
+    std::vector<unsigned int> hits1(hits->hitsVec1());
+    std::vector<int> error0(hits->errorVec0());
+    std::vector<int> error1(hits->errorVec1());
+    if (!m_doneThisEvent) {
+      std::string errorType = "";
+      if (!m_doneCmxHitsParity0 && source == LVL1::CMXCPHits::REMOTE_0) {
+        error0[peak] |= (1 << LVL1::DataError::Parity);
+        errorType = "CMX-CP Hits Parity Phase 0";
+        m_doneCmxHitsParity0 = true;
+      } else if (!m_doneCmxHitsParity1 && source == LVL1::CMXCPHits::REMOTE_2) {
+        error1[peak] |= (1 << LVL1::DataError::Parity);
+        errorType = "CMX-CP Hits Parity Phase 1";
+        m_doneCmxHitsParity1 = true;
+      }
+      /*
+       * Add hit readout errors here
+      */
+      if (errorType != "") {
+        errorMessage("CMXCPHits " + errorType);
+        m_doneThisEvent = true;
+      }
+    }
+    LVL1::CMXCPHits* err = new LVL1::CMXCPHits(crate, cmx, source,
+                                               hits0, hits1, error0, error1,
+					       peak);
+    errCollection->push_back(err);
+  }
+}
+
+// Generate CPM TOB RoI errors
+
+void CpmErrors::cpmTobRoiErrors(const CpmTobRoiCollection* /*crCollection*/,
+                                      CpmTobRoiCollection* /*errCollection*/)
+{
+}
+
+// Print a message when error generated
+
+void CpmErrors::errorMessage(const std::string& errmsg)
+{
+  int eventNumber = 0;
+  const EventInfo* evInfo = 0;
+  StatusCode sc = evtStore()->retrieve(evInfo);
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "No EventInfo found" << endreq;
+  } else {
+    const EventID* evID = evInfo->event_ID();
+    if (evID) eventNumber = evID->event_number();
+  }
+  msg(MSG::INFO) << "Event " << eventNumber
+                 << " has error " << errmsg << endreq;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmErrors.h b/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmErrors.h
new file mode 100755
index 0000000000000000000000000000000000000000..07dad1749eb68bc3d7ebe13db56e2091d923c629
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmErrors.h
@@ -0,0 +1,137 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPMERRORS_H
+#define TRIGT1CALOBYTESTREAM_CPMERRORS_H
+
+#include <string>
+#include <stdint.h>
+
+#include "AthenaBaseComps/AthAlgorithm.h"
+#include "DataModel/DataVector.h"
+
+class ISvcLocator;
+class StatusCode;
+
+namespace LVL1 {
+  class CPMTower;
+  class CPMTobRoI;
+  class CMXCPTob;
+  class CMXCPHits;
+}
+
+namespace LVL1BS {
+
+/** Algorithm to add errors to CPM data for testing bytestream converters
+ *  and monitoring.
+ *
+ *  NB. Doesn't pretent to be rigorous so read back data may not be identical
+ *  to written (eg. SubStatus errors in single CPMTower on output will be in
+ *  multiple CPMTowers on subsequent input, ie. all from the relevant sub-block).
+ *
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpmErrors : public AthAlgorithm {
+
+ public:
+   CpmErrors(const std::string& name, ISvcLocator* pSvcLocator);
+   virtual ~CpmErrors();
+
+   virtual StatusCode initialize();
+   virtual StatusCode execute();
+   virtual StatusCode finalize();
+
+ private:
+   enum ErrorBits { Error1 = 0x1, Error2 = 0x2, Error3 = 0x4, Error4 = 0x8,
+		    Error5 = 0x10, Error6 = 0x20, Error7 = 0x40, Error8 = 0x80,
+		    Error9 = 0x100, Error10 = 0x200, Error11 = 0x400,
+		    Error12 = 0x800, Error13 = 0x1000, Error14 = 0x2000,
+		    Error15 = 0x4000, Error16 = 0x8000 };
+
+   typedef DataVector<LVL1::CPMTower>  CpmTowerCollection;
+   typedef DataVector<LVL1::CMXCPTob>  CmxCpTobCollection;
+   typedef DataVector<LVL1::CMXCPHits> CmxCpHitsCollection;
+   typedef DataVector<LVL1::CPMTobRoI> CpmTobRoiCollection;
+   
+   /// Generate CPMTowers errors
+   void cpmTowerErrors(const CpmTowerCollection* cpCollection,
+                             CpmTowerCollection* errCollection);
+   /// Generate CMXCPTob errors
+   void cmxTobErrors(const CmxCpTobCollection* tobCollection,
+                           CmxCpTobCollection* errCollection);
+   /// Generate CMXCPHits errors
+   void cmxHitsErrors(const CmxCpHitsCollection* hitCollection,
+                            CmxCpHitsCollection* errCollection);
+   /// Generate CPM TOB RoI errors
+   void cpmTobRoiErrors(const CpmTobRoiCollection* crCollection,
+                              CpmTobRoiCollection* errCollection);
+
+   /// Print a message when error generated
+   void errorMessage(const std::string& msg);
+
+   /// CP element container input StoreGate key
+   std::string m_cpmTowerLocation;
+   /// CMX TOB container input StoreGate key
+   std::string m_cmxCpTobLocation;
+   /// CMX hits container input StoreGate key
+   std::string m_cmxCpHitsLocation;
+   /// CPM TOB RoI container input StoreGate key
+   std::string m_cpmTobRoiLocation;
+   /// CP element container output StoreGate key
+   std::string m_cpmTowerLocationOut;
+   /// CMX TOB container output StoreGate key
+   std::string m_cmxCpTobLocationOut;
+   /// CMX hits container output StoreGate key
+   std::string m_cmxCpHitsLocationOut;
+   /// CPM TOB RoI container output StoreGate key
+   std::string m_cpmTobRoiLocationOut;
+   /// CPM towers errors flag
+   int m_cpmTowerErrors;
+   /// CMX TOB errors flag
+   int m_cmxTobErrors;
+   /// CMX hits errors flag
+   int m_cmxHitsErrors;
+   /// CPM TOB RoI errors flag
+   int m_cpmTobRoiErrors;
+
+   /// Individual error done flags
+   bool m_doneThisEvent;
+   /// CPMTowers
+   bool m_doneEmParity;
+   bool m_doneHadParity;
+   bool m_doneEmLinkDown;
+   bool m_doneHadLinkDown;
+   bool m_doneEmReadoutA;
+   bool m_doneEmReadoutB;
+   bool m_doneEmReadoutC;
+   bool m_doneHadReadoutA;
+   bool m_doneHadReadoutB;
+   bool m_doneHadReadoutC;
+   bool m_doneCpmSubStatus;
+   /// CMXCPTob
+   bool m_doneCmxTobParityM;
+   bool m_doneCmxTobParity0;
+   bool m_doneCmxTobParity1;
+   bool m_doneCmxTobParity2;
+   bool m_doneCmxTobParity3;
+   bool m_doneTobEnReadoutA;
+   bool m_doneTobEnReadoutB;
+   bool m_doneTobEnReadoutC;
+   bool m_doneTobIsReadoutA;
+   bool m_doneTobIsReadoutB;
+   bool m_doneTobIsReadoutC;
+   /// CMXCPHits
+   bool m_doneCmxHitsParity0;
+   bool m_doneCmxHitsParity1;
+   /// CPMTobRoI
+
+   static const int s_saturation = 255;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmTester.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmTester.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..c0f79adda93c626791302dd2bbdbcd6175352bb5
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmTester.cxx
@@ -0,0 +1,437 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <utility>
+
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+#include "StoreGate/StoreGateSvc.h"
+
+#include "TrigT1CaloEvent/CMMCPHits.h"
+#include "TrigT1CaloEvent/CPMHits.h"
+#include "TrigT1CaloEvent/CPMTower.h"
+#include "TrigT1CaloEvent/CPMRoI.h"
+#include "TrigT1CaloUtils/TriggerTowerKey.h"
+#include "TrigT1Interfaces/TrigT1CaloDefs.h"
+
+#include "CpmTester.h"
+#include "../src/ModifySlices.h"
+
+namespace LVL1BS {
+
+CpmTester::CpmTester(const std::string& name, ISvcLocator* pSvcLocator)
+                     : AthAlgorithm(name, pSvcLocator),
+		       m_towerKey(0)
+{
+  declareProperty("CPMTowerLocation",
+           m_cpmTowerLocation   = LVL1::TrigT1CaloDefs::CPMTowerLocation);
+  declareProperty("CPMHitsLocation",
+           m_cpmHitsLocation    = LVL1::TrigT1CaloDefs::CPMHitsLocation);
+  declareProperty("CMMCPHitsLocation",
+           m_cmmCpHitsLocation  = LVL1::TrigT1CaloDefs::CMMCPHitsLocation);
+  declareProperty("CPMRoILocation",
+           m_cpmRoiLocation     = LVL1::TrigT1CaloDefs::CPMRoILocation);
+  declareProperty("CPMRoILocationRoIB",
+           m_cpmRoiLocationRoib =
+	                   LVL1::TrigT1CaloDefs::CPMRoILocation + "RoIB");
+  declareProperty("CPMTowerLocationOverlap",
+           m_cpmTowerLocationOverlap =
+	                   LVL1::TrigT1CaloDefs::CPMTowerLocation + "Overlap");
+
+  declareProperty("ForceSlicesCPM", m_forceSlicesCpm = 0);
+  declareProperty("ForceSlicesCMM", m_forceSlicesCmm = 0);
+
+  // By default print everything except RoIB RoIs and Tower overlap
+  declareProperty("CPMTowerPrint",   m_cpmTowerPrint   = 1);
+  declareProperty("CPMHitsPrint",    m_cpmHitsPrint    = 1);
+  declareProperty("CMMCPHitsPrint",  m_cmmCpHitsPrint  = 1);
+  declareProperty("CPMRoIPrint",     m_cpmRoiPrint     = 1);
+  declareProperty("CPMRoIPrintRoIB", m_cpmRoiPrintRoib = 0);
+  declareProperty("CPMTowerPrintOverlap", m_cpmTowerPrintOverlap = 0);
+}
+
+CpmTester::~CpmTester()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode CpmTester::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << /* version() */ PACKAGE_VERSION << endreq;
+
+  m_towerKey = new LVL1::TriggerTowerKey();
+
+  return StatusCode::SUCCESS;
+}
+
+// Execute
+
+StatusCode CpmTester::execute()
+{
+  if ( !msgLvl(MSG::INFO) ) return StatusCode::SUCCESS;
+  msg(MSG::INFO);
+
+  if (m_cpmTowerPrint) {
+
+    // Find CPM towers
+
+    const CpmTowerCollection* ttCollection = 0;
+    StatusCode sc = evtStore()->retrieve(ttCollection, m_cpmTowerLocation);
+    if (sc.isFailure() || !ttCollection || ttCollection->empty()) {
+      msg() << "No core CPM towers found" << endreq;
+    } else {
+
+      // Order by eta, phi
+
+      setupCpmTowerMap(ttCollection);
+
+      // Print the CPM towers
+
+      printCpmTowers("core");
+    }
+  }
+
+  if (m_cpmTowerPrintOverlap) {
+
+    // Find overlap CPM towers
+
+    const CpmTowerCollection* ttCollection = 0;
+    StatusCode sc = evtStore()->retrieve(ttCollection, m_cpmTowerLocationOverlap);
+    if (sc.isFailure() || !ttCollection || ttCollection->empty()) {
+      msg() << "No overlap CPM towers found" << endreq;
+    } else {
+
+      // Order by eta, phi
+
+      setupCpmTowerMap(ttCollection);
+
+      // Print the overlap CPM towers
+
+      printCpmTowers("overlap");
+    }
+  }
+
+  if (m_cpmHitsPrint) {
+
+    // Find CPM hits
+
+    const CpmHitsCollection* hitCollection = 0;
+    StatusCode sc = evtStore()->retrieve(hitCollection, m_cpmHitsLocation);
+    if (sc.isFailure() || !hitCollection || hitCollection->empty()) {
+      msg() << "No CPM Hits found" << endreq;
+    } else {
+
+      // Order by crate, module
+
+      setupCpmHitsMap(hitCollection);
+
+      // Print the CPM hits
+
+      printCpmHits();
+    }
+  }
+
+  if (m_cmmCpHitsPrint) {
+
+    // Find CMM-CP hits
+
+    const CmmCpHitsCollection* hitCollection = 0;
+    StatusCode sc = evtStore()->retrieve(hitCollection, m_cmmCpHitsLocation);
+    if (sc.isFailure() || !hitCollection || hitCollection->empty()) {
+      msg() << "No CMM-CP Hits found" << endreq;
+    } else {
+
+      // Order by crate, dataID
+
+      setupCmmCpHitsMap(hitCollection);
+
+      // Print the CMM-CP hits
+
+      printCmmCpHits();
+    }
+  }
+
+  if (m_cpmRoiPrint) {
+
+    // Find CPM RoIs
+
+    const CpmRoiCollection* roiCollection = 0;
+    StatusCode sc = evtStore()->retrieve(roiCollection, m_cpmRoiLocation);
+    if (sc.isFailure() || !roiCollection || roiCollection->empty()) {
+      msg() << "No CPM RoIs found" << endreq;
+    } else {
+
+      // Order by RoI word
+
+      setupCpmRoiMap(roiCollection);
+
+      // Print the CPM RoIs
+
+      printCpmRois("DAQ");
+    }
+  }
+
+  if (m_cpmRoiPrintRoib) {
+
+    // Find CPM RoIs from RoIB
+
+    const CpmRoiCollection* roiCollection = 0;
+    StatusCode sc = evtStore()->retrieve(roiCollection, m_cpmRoiLocationRoib);
+    if (sc.isFailure() || !roiCollection || roiCollection->empty()) {
+      msg() << "No CPM RoIs from RoIB found" << endreq;
+    } else {
+
+      // Order by RoI word
+
+      setupCpmRoiMap(roiCollection);
+
+      // Print the CPM RoIs from RoIB
+
+      printCpmRois("RoIB");
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode CpmTester::finalize()
+{
+  delete m_towerKey;
+
+  return StatusCode::SUCCESS;
+}
+
+// Print the CPM towers
+
+void CpmTester::printCpmTowers(const std::string& source) const
+{
+  msg() << "Number of " << source << " CPM towers = "
+        << m_ttMap.size() << endreq;
+  CpmTowerMap::const_iterator mapIter = m_ttMap.begin();
+  CpmTowerMap::const_iterator mapEnd  = m_ttMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::CPMTower* const tt = mapIter->second;
+    int peak   = tt->peak();
+    int slices = (tt->emEnergyVec()).size();
+    if (m_forceSlicesCpm) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesCpm);
+      slices = m_forceSlicesCpm;
+    }
+    msg() << "key/eta/phi/peak/em/had/emErr/hadErr: "
+          << mapIter->first << "/" << tt->eta() << "/" << tt->phi() << "/"
+	  << peak << "/";
+
+    std::vector<int> emEnergy;
+    std::vector<int> hadEnergy;
+    std::vector<int> emError;
+    std::vector<int> hadError;
+    ModifySlices::data(tt->emEnergyVec(),  emEnergy,  slices);
+    ModifySlices::data(tt->hadEnergyVec(), hadEnergy, slices);
+    ModifySlices::data(tt->emErrorVec(),   emError,   slices);
+    ModifySlices::data(tt->hadErrorVec(),  hadError,  slices);
+    printVec(emEnergy);
+    printVec(hadEnergy);
+    msg() << MSG::hex;
+    printVec(emError);
+    printVec(hadError);
+    msg() << MSG::dec << endreq;
+  }
+}
+
+// Print the CPM hits
+
+void CpmTester::printCpmHits() const
+{
+  msg() << "Number of CPM Hits = " << m_hitsMap.size() << endreq;
+  CpmHitsMap::const_iterator mapIter = m_hitsMap.begin();
+  CpmHitsMap::const_iterator mapEnd  = m_hitsMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::CPMHits* const ch = mapIter->second;
+    int peak   = ch->peak();
+    int slices = (ch->HitsVec0()).size();
+    if (m_forceSlicesCpm) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesCpm);
+      slices = m_forceSlicesCpm;
+    }
+    msg() << "crate/module/peak/hits0/hits1: "
+	  << ch->crate() << "/" << ch->module() << "/" << peak << "/";
+
+    std::vector<unsigned int> hits0;
+    std::vector<unsigned int> hits1;
+    ModifySlices::data(ch->HitsVec0(), hits0, slices);
+    ModifySlices::data(ch->HitsVec1(), hits1, slices);
+    printVecH(hits0);
+    printVecH(hits1);
+    msg() << endreq;
+  }
+}
+
+// Print the CMM-CP hits
+
+void CpmTester::printCmmCpHits() const
+{
+  msg() << "Number of CMM-CP Hits = " << m_cmmHitsMap.size() << endreq;
+  CmmCpHitsMap::const_iterator mapIter = m_cmmHitsMap.begin();
+  CmmCpHitsMap::const_iterator mapEnd  = m_cmmHitsMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::CMMCPHits* const ch = mapIter->second;
+    int peak   = ch->peak();
+    int slices = (ch->HitsVec0()).size();
+    if (m_forceSlicesCmm) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesCmm);
+      slices = m_forceSlicesCmm;
+    }
+    msg() << "crate/dataID/peak/hits0/hits1/err0/err1: "
+	  << ch->crate() << "/" << ch->dataID() << "/" << peak << "/";
+
+    std::vector<unsigned int> hits0;
+    std::vector<unsigned int> hits1;
+    std::vector<int> err0;
+    std::vector<int> err1;
+    ModifySlices::data(ch->HitsVec0(), hits0, slices);
+    ModifySlices::data(ch->HitsVec1(), hits1, slices);
+    ModifySlices::data(ch->ErrorVec0(), err0, slices);
+    ModifySlices::data(ch->ErrorVec1(), err1, slices);
+    printVecH(hits0);
+    printVecH(hits1);
+    msg() << MSG::hex;
+    printVec(err0);
+    printVec(err1);
+    msg() << MSG::dec << endreq;
+  }
+}
+
+// Print the CPM RoIs
+
+void CpmTester::printCpmRois(const std::string& source) const
+{
+  msg() << "Number of CPM RoIs (" << source << ") = " << m_roiMap.size()
+        << endreq;
+  CpmRoiMap::const_iterator mapIter = m_roiMap.begin();
+  CpmRoiMap::const_iterator mapEnd  = m_roiMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::CPMRoI* const roi = mapIter->second;
+    msg() << "crate/cpm/chip/loc/hits/error: "
+	  << roi->crate()    << "/" << roi->cpm() << "/" << roi->chip() << "/"
+	  << roi->location() << "/";
+    int hits = roi->hits();
+    for (int i = 0; i < 16; ++i) {
+      if (i == 8)     msg() << ";";
+      else if (i > 0) msg() << ":";
+      msg() << ((hits >> i) & 0x1);
+    }
+    msg() << "/" << roi->error() << "/" << endreq;
+  }
+}
+
+// Print a vector
+
+void CpmTester::printVec(const std::vector<int>& vec) const
+{
+  std::vector<int>::const_iterator pos;
+  for (pos = vec.begin(); pos != vec.end(); ++pos) {
+    if (pos != vec.begin()) msg() << ",";
+    msg() << *pos;
+  }
+  msg() << "/";
+}
+
+// Print a vector of hits
+
+void CpmTester::printVecH(const std::vector<unsigned int>& vec) const
+{
+  const int words = 8;
+  const int bits  = 3;
+  const unsigned int mask = 0x7;
+  std::vector<unsigned int>::const_iterator pos;
+  std::vector<unsigned int>::const_iterator posb = vec.begin();
+  std::vector<unsigned int>::const_iterator pose = vec.end();
+  for (pos = posb; pos != pose; ++pos) {
+    if (pos != posb) msg() << ",";
+    const unsigned int hits = *pos;
+    for (int i = 0; i < words; ++i) {
+      if (i != 0) msg() << ":";
+      const unsigned int thr = (hits >> (bits*i)) & mask;
+      msg() << thr;
+    }
+  }
+  msg() << "/";
+}
+
+// Set up CPM tower map
+
+void CpmTester::setupCpmTowerMap(const CpmTowerCollection* const ttCollection)
+{
+  m_ttMap.clear();
+  if (ttCollection) {
+    CpmTowerCollection::const_iterator pos  = ttCollection->begin();
+    CpmTowerCollection::const_iterator pose = ttCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CPMTower* const tt = *pos;
+      const unsigned int key = m_towerKey->ttKey(tt->phi(), tt->eta());
+      m_ttMap.insert(std::make_pair(key, tt));
+    }
+  }
+}
+
+// Set up CPM hits map
+
+void CpmTester::setupCpmHitsMap(const CpmHitsCollection* const hitCollection)
+{
+  m_hitsMap.clear();
+  if (hitCollection) {
+    CpmHitsCollection::const_iterator pos  = hitCollection->begin();
+    CpmHitsCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CPMHits* const hits = *pos;
+      const int key = hits->crate()*100 + hits->module() - 1;
+      m_hitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Set up CMM-CP hits map
+
+void CpmTester::setupCmmCpHitsMap(const CmmCpHitsCollection*
+                                                       const hitCollection)
+{
+  m_cmmHitsMap.clear();
+  if (hitCollection) {
+    CmmCpHitsCollection::const_iterator pos  = hitCollection->begin();
+    CmmCpHitsCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CMMCPHits* const hits = *pos;
+      const int key = hits->crate()*100 + hits->dataID();
+      m_cmmHitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Set up CPM RoI map
+
+void CpmTester::setupCpmRoiMap(const CpmRoiCollection* const roiCollection)
+{
+  m_roiMap.clear();
+  if (roiCollection) {
+    CpmRoiCollection::const_iterator pos  = roiCollection->begin();
+    CpmRoiCollection::const_iterator pose = roiCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CPMRoI* const roi = *pos;
+      const uint32_t key = roi->roiWord();
+      m_roiMap.insert(std::make_pair(key, roi));
+    }
+  }
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmTester.h b/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmTester.h
new file mode 100755
index 0000000000000000000000000000000000000000..0dd651e6e4420442564062df75549e250848d59d
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmTester.h
@@ -0,0 +1,126 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPMTESTER_H
+#define TRIGT1CALOBYTESTREAM_CPMTESTER_H
+
+#include <map>
+#include <string>
+#include <stdint.h>
+
+#include "AthenaBaseComps/AthAlgorithm.h"
+#include "DataModel/DataVector.h"
+
+class ISvcLocator;
+class StatusCode;
+
+namespace LVL1 {
+  class CMMCPHits;
+  class CPMHits;
+  class CPMTower;
+  class CPMRoI;
+  class TriggerTowerKey;
+}
+
+namespace LVL1BS {
+
+/** Algorithm to test CPM component bytestream conversions.
+ *
+ *  Just prints out the contents of the CPMTower objects
+ *  and CPMHits objects.
+ *  Also includes CMMCPHits and CPMRoIs.
+ *
+ *  Run before writing bytestream and after reading it and compare.
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpmTester : public AthAlgorithm {
+
+ public:
+   CpmTester(const std::string& name, ISvcLocator* pSvcLocator);
+   virtual ~CpmTester();
+
+   virtual StatusCode initialize();
+   virtual StatusCode execute();
+   virtual StatusCode finalize();
+
+ private:
+   typedef DataVector<LVL1::CPMTower>                     CpmTowerCollection;
+   typedef DataVector<LVL1::CPMHits>                      CpmHitsCollection;
+   typedef DataVector<LVL1::CMMCPHits>                    CmmCpHitsCollection;
+   typedef DataVector<LVL1::CPMRoI>                       CpmRoiCollection;
+   typedef std::map<unsigned int, const LVL1::CPMTower*>  CpmTowerMap;
+   typedef std::map<int,          const LVL1::CPMHits*>   CpmHitsMap;
+   typedef std::map<int,          const LVL1::CMMCPHits*> CmmCpHitsMap;
+   typedef std::map<uint32_t,     const LVL1::CPMRoI*>    CpmRoiMap;
+
+   /// Print the CPM towers
+   void printCpmTowers(const std::string& source) const;
+   /// Print the CPM hits
+   void printCpmHits()   const;
+   /// Print the CMM-CP hits
+   void printCmmCpHits() const;
+   /// Print the CPM RoIs
+   void printCpmRois(const std::string& source)   const;
+
+   /// Print a vector
+   void printVec(const std::vector<int>& vec) const;
+   /// Print a vector of hits
+   void printVecH(const std::vector<unsigned int>& vec) const;
+
+   /// Set up CPM tower map
+   void setupCpmTowerMap(const CpmTowerCollection* ttCollection);
+   /// Set up CPM hits map
+   void setupCpmHitsMap(const CpmHitsCollection* hitCollection);
+   /// Set up CMM-CP hits map
+   void setupCmmCpHitsMap(const CmmCpHitsCollection* hitCollection);
+   /// Set up CPM RoI map
+   void setupCpmRoiMap(const CpmRoiCollection* roiCollection);
+
+   /// CPM tower key provider
+   LVL1::TriggerTowerKey* m_towerKey;
+   /// CPM tower container StoreGate key
+   std::string m_cpmTowerLocation;
+   /// CPM hits container StoreGate key
+   std::string m_cpmHitsLocation;
+   /// CMM-CP hits container StoreGate key
+   std::string m_cmmCpHitsLocation;
+   /// CPM RoI container StoreGate key
+   std::string m_cpmRoiLocation;
+   /// CPM RoI from RoIB container StoreGate key
+   std::string m_cpmRoiLocationRoib;
+   /// CPM tower overlap container StoreGate key
+   std::string m_cpmTowerLocationOverlap;
+   /// Force number of CPM slices
+   int m_forceSlicesCpm;
+   /// Force number of CMM slices
+   int m_forceSlicesCmm;
+   /// CPM tower print flag
+   int m_cpmTowerPrint;
+   /// CPM hits print flag
+   int m_cpmHitsPrint;
+   /// CMM-CP hits print flag
+   int m_cmmCpHitsPrint;
+   /// CPM RoI print flag
+   int m_cpmRoiPrint;
+   /// CPM RoI from RoIB print flag
+   int m_cpmRoiPrintRoib;
+   /// CPM tower overlap print flag
+   int m_cpmTowerPrintOverlap;
+
+   /// CPM tower map
+   CpmTowerMap  m_ttMap;
+   /// CPM hits map
+   CpmHitsMap   m_hitsMap;
+   /// CMM-CP hits map
+   CmmCpHitsMap m_cmmHitsMap;
+   /// CPM RoI map
+   CpmRoiMap    m_roiMap;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmTesterV1.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmTesterV1.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..be1142fe5a370a31b2f178a972e3c66ee735e5cc
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmTesterV1.cxx
@@ -0,0 +1,437 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <utility>
+
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+#include "StoreGate/StoreGateSvc.h"
+
+#include "TrigT1CaloEvent/CMMCPHits.h"
+#include "TrigT1CaloEvent/CPMHits.h"
+#include "TrigT1CaloEvent/CPMTower.h"
+#include "TrigT1CaloEvent/CPMRoI.h"
+#include "TrigT1CaloUtils/TriggerTowerKey.h"
+#include "TrigT1Interfaces/TrigT1CaloDefs.h"
+
+#include "CpmTesterV1.h"
+#include "../src/ModifySlices.h"
+
+namespace LVL1BS {
+
+CpmTesterV1::CpmTesterV1(const std::string& name, ISvcLocator* pSvcLocator)
+                     : AthAlgorithm(name, pSvcLocator),
+		       m_towerKey(0)
+{
+  declareProperty("CPMTowerLocation",
+           m_cpmTowerLocation   = LVL1::TrigT1CaloDefs::CPMTowerLocation);
+  declareProperty("CPMHitsLocation",
+           m_cpmHitsLocation    = LVL1::TrigT1CaloDefs::CPMHitsLocation);
+  declareProperty("CMMCPHitsLocation",
+           m_cmmCpHitsLocation  = LVL1::TrigT1CaloDefs::CMMCPHitsLocation);
+  declareProperty("CPMRoILocation",
+           m_cpmRoiLocation     = LVL1::TrigT1CaloDefs::CPMRoILocation);
+  declareProperty("CPMRoILocationRoIB",
+           m_cpmRoiLocationRoib =
+	                   LVL1::TrigT1CaloDefs::CPMRoILocation + "RoIB");
+  declareProperty("CPMTowerLocationOverlap",
+           m_cpmTowerLocationOverlap =
+	                   LVL1::TrigT1CaloDefs::CPMTowerLocation + "Overlap");
+
+  declareProperty("ForceSlicesCPM", m_forceSlicesCpm = 0);
+  declareProperty("ForceSlicesCMM", m_forceSlicesCmm = 0);
+
+  // By default print everything except RoIB RoIs and Tower overlap
+  declareProperty("CPMTowerPrint",   m_cpmTowerPrint   = 1);
+  declareProperty("CPMHitsPrint",    m_cpmHitsPrint    = 1);
+  declareProperty("CMMCPHitsPrint",  m_cmmCpHitsPrint  = 1);
+  declareProperty("CPMRoIPrint",     m_cpmRoiPrint     = 1);
+  declareProperty("CPMRoIPrintRoIB", m_cpmRoiPrintRoib = 0);
+  declareProperty("CPMTowerPrintOverlap", m_cpmTowerPrintOverlap = 0);
+}
+
+CpmTesterV1::~CpmTesterV1()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode CpmTesterV1::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << /* version() */ PACKAGE_VERSION << endreq;
+
+  m_towerKey = new LVL1::TriggerTowerKey();
+
+  return StatusCode::SUCCESS;
+}
+
+// Execute
+
+StatusCode CpmTesterV1::execute()
+{
+  if ( !msgLvl(MSG::INFO) ) return StatusCode::SUCCESS;
+  msg(MSG::INFO);
+
+  if (m_cpmTowerPrint) {
+
+    // Find CPM towers
+
+    const CpmTowerCollection* ttCollection = 0;
+    StatusCode sc = evtStore()->retrieve(ttCollection, m_cpmTowerLocation);
+    if (sc.isFailure() || !ttCollection || ttCollection->empty()) {
+      msg() << "No core CPM towers found" << endreq;
+    } else {
+
+      // Order by eta, phi
+
+      setupCpmTowerMap(ttCollection);
+
+      // Print the CPM towers
+
+      printCpmTowers("core");
+    }
+  }
+
+  if (m_cpmTowerPrintOverlap) {
+
+    // Find overlap CPM towers
+
+    const CpmTowerCollection* ttCollection = 0;
+    StatusCode sc = evtStore()->retrieve(ttCollection, m_cpmTowerLocationOverlap);
+    if (sc.isFailure() || !ttCollection || ttCollection->empty()) {
+      msg() << "No overlap CPM towers found" << endreq;
+    } else {
+
+      // Order by eta, phi
+
+      setupCpmTowerMap(ttCollection);
+
+      // Print the overlap CPM towers
+
+      printCpmTowers("overlap");
+    }
+  }
+
+  if (m_cpmHitsPrint) {
+
+    // Find CPM hits
+
+    const CpmHitsCollection* hitCollection = 0;
+    StatusCode sc = evtStore()->retrieve(hitCollection, m_cpmHitsLocation);
+    if (sc.isFailure() || !hitCollection || hitCollection->empty()) {
+      msg() << "No CPM Hits found" << endreq;
+    } else {
+
+      // Order by crate, module
+
+      setupCpmHitsMap(hitCollection);
+
+      // Print the CPM hits
+
+      printCpmHits();
+    }
+  }
+
+  if (m_cmmCpHitsPrint) {
+
+    // Find CMM-CP hits
+
+    const CmmCpHitsCollection* hitCollection = 0;
+    StatusCode sc = evtStore()->retrieve(hitCollection, m_cmmCpHitsLocation);
+    if (sc.isFailure() || !hitCollection || hitCollection->empty()) {
+      msg() << "No CMM-CP Hits found" << endreq;
+    } else {
+
+      // Order by crate, dataID
+
+      setupCmmCpHitsMap(hitCollection);
+
+      // Print the CMM-CP hits
+
+      printCmmCpHits();
+    }
+  }
+
+  if (m_cpmRoiPrint) {
+
+    // Find CPM RoIs
+
+    const CpmRoiCollection* roiCollection = 0;
+    StatusCode sc = evtStore()->retrieve(roiCollection, m_cpmRoiLocation);
+    if (sc.isFailure() || !roiCollection || roiCollection->empty()) {
+      msg() << "No CPM RoIs found" << endreq;
+    } else {
+
+      // Order by RoI word
+
+      setupCpmRoiMap(roiCollection);
+
+      // Print the CPM RoIs
+
+      printCpmRois("DAQ");
+    }
+  }
+
+  if (m_cpmRoiPrintRoib) {
+
+    // Find CPM RoIs from RoIB
+
+    const CpmRoiCollection* roiCollection = 0;
+    StatusCode sc = evtStore()->retrieve(roiCollection, m_cpmRoiLocationRoib);
+    if (sc.isFailure() || !roiCollection || roiCollection->empty()) {
+      msg() << "No CPM RoIs from RoIB found" << endreq;
+    } else {
+
+      // Order by RoI word
+
+      setupCpmRoiMap(roiCollection);
+
+      // Print the CPM RoIs from RoIB
+
+      printCpmRois("RoIB");
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode CpmTesterV1::finalize()
+{
+  delete m_towerKey;
+
+  return StatusCode::SUCCESS;
+}
+
+// Print the CPM towers
+
+void CpmTesterV1::printCpmTowers(const std::string& source) const
+{
+  msg() << "Number of " << source << " CPM towers = "
+        << m_ttMap.size() << endreq;
+  CpmTowerMap::const_iterator mapIter = m_ttMap.begin();
+  CpmTowerMap::const_iterator mapEnd  = m_ttMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::CPMTower* const tt = mapIter->second;
+    int peak   = tt->peak();
+    int slices = (tt->emEnergyVec()).size();
+    if (m_forceSlicesCpm) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesCpm);
+      slices = m_forceSlicesCpm;
+    }
+    msg() << "key/eta/phi/peak/em/had/emErr/hadErr: "
+          << mapIter->first << "/" << tt->eta() << "/" << tt->phi() << "/"
+	  << peak << "/";
+
+    std::vector<int> emEnergy;
+    std::vector<int> hadEnergy;
+    std::vector<int> emError;
+    std::vector<int> hadError;
+    ModifySlices::data(tt->emEnergyVec(),  emEnergy,  slices);
+    ModifySlices::data(tt->hadEnergyVec(), hadEnergy, slices);
+    ModifySlices::data(tt->emErrorVec(),   emError,   slices);
+    ModifySlices::data(tt->hadErrorVec(),  hadError,  slices);
+    printVec(emEnergy);
+    printVec(hadEnergy);
+    msg() << MSG::hex;
+    printVec(emError);
+    printVec(hadError);
+    msg() << MSG::dec << endreq;
+  }
+}
+
+// Print the CPM hits
+
+void CpmTesterV1::printCpmHits() const
+{
+  msg() << "Number of CPM Hits = " << m_hitsMap.size() << endreq;
+  CpmHitsMap::const_iterator mapIter = m_hitsMap.begin();
+  CpmHitsMap::const_iterator mapEnd  = m_hitsMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::CPMHits* const ch = mapIter->second;
+    int peak   = ch->peak();
+    int slices = (ch->HitsVec0()).size();
+    if (m_forceSlicesCpm) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesCpm);
+      slices = m_forceSlicesCpm;
+    }
+    msg() << "crate/module/peak/hits0/hits1: "
+	  << ch->crate() << "/" << ch->module() << "/" << peak << "/";
+
+    std::vector<unsigned int> hits0;
+    std::vector<unsigned int> hits1;
+    ModifySlices::data(ch->HitsVec0(), hits0, slices);
+    ModifySlices::data(ch->HitsVec1(), hits1, slices);
+    printVecH(hits0);
+    printVecH(hits1);
+    msg() << endreq;
+  }
+}
+
+// Print the CMM-CP hits
+
+void CpmTesterV1::printCmmCpHits() const
+{
+  msg() << "Number of CMM-CP Hits = " << m_cmmHitsMap.size() << endreq;
+  CmmCpHitsMap::const_iterator mapIter = m_cmmHitsMap.begin();
+  CmmCpHitsMap::const_iterator mapEnd  = m_cmmHitsMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::CMMCPHits* const ch = mapIter->second;
+    int peak   = ch->peak();
+    int slices = (ch->HitsVec0()).size();
+    if (m_forceSlicesCmm) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesCmm);
+      slices = m_forceSlicesCmm;
+    }
+    msg() << "crate/dataID/peak/hits0/hits1/err0/err1: "
+	  << ch->crate() << "/" << ch->dataID() << "/" << peak << "/";
+
+    std::vector<unsigned int> hits0;
+    std::vector<unsigned int> hits1;
+    std::vector<int> err0;
+    std::vector<int> err1;
+    ModifySlices::data(ch->HitsVec0(), hits0, slices);
+    ModifySlices::data(ch->HitsVec1(), hits1, slices);
+    ModifySlices::data(ch->ErrorVec0(), err0, slices);
+    ModifySlices::data(ch->ErrorVec1(), err1, slices);
+    printVecH(hits0);
+    printVecH(hits1);
+    msg() << MSG::hex;
+    printVec(err0);
+    printVec(err1);
+    msg() << MSG::dec << endreq;
+  }
+}
+
+// Print the CPM RoIs
+
+void CpmTesterV1::printCpmRois(const std::string& source) const
+{
+  msg() << "Number of CPM RoIs (" << source << ") = " << m_roiMap.size()
+        << endreq;
+  CpmRoiMap::const_iterator mapIter = m_roiMap.begin();
+  CpmRoiMap::const_iterator mapEnd  = m_roiMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::CPMRoI* const roi = mapIter->second;
+    msg() << "crate/cpm/chip/loc/hits/error: "
+	  << roi->crate()    << "/" << roi->cpm() << "/" << roi->chip() << "/"
+	  << roi->location() << "/";
+    int hits = roi->hits();
+    for (int i = 0; i < 16; ++i) {
+      if (i == 8)     msg() << ";";
+      else if (i > 0) msg() << ":";
+      msg() << ((hits >> i) & 0x1);
+    }
+    msg() << "/" << roi->error() << "/" << endreq;
+  }
+}
+
+// Print a vector
+
+void CpmTesterV1::printVec(const std::vector<int>& vec) const
+{
+  std::vector<int>::const_iterator pos;
+  for (pos = vec.begin(); pos != vec.end(); ++pos) {
+    if (pos != vec.begin()) msg() << ",";
+    msg() << *pos;
+  }
+  msg() << "/";
+}
+
+// Print a vector of hits
+
+void CpmTesterV1::printVecH(const std::vector<unsigned int>& vec) const
+{
+  const int words = 8;
+  const int bits  = 3;
+  const unsigned int mask = 0x7;
+  std::vector<unsigned int>::const_iterator pos;
+  std::vector<unsigned int>::const_iterator posb = vec.begin();
+  std::vector<unsigned int>::const_iterator pose = vec.end();
+  for (pos = posb; pos != pose; ++pos) {
+    if (pos != posb) msg() << ",";
+    const unsigned int hits = *pos;
+    for (int i = 0; i < words; ++i) {
+      if (i != 0) msg() << ":";
+      const unsigned int thr = (hits >> (bits*i)) & mask;
+      msg() << thr;
+    }
+  }
+  msg() << "/";
+}
+
+// Set up CPM tower map
+
+void CpmTesterV1::setupCpmTowerMap(const CpmTowerCollection* const ttCollection)
+{
+  m_ttMap.clear();
+  if (ttCollection) {
+    CpmTowerCollection::const_iterator pos  = ttCollection->begin();
+    CpmTowerCollection::const_iterator pose = ttCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CPMTower* const tt = *pos;
+      const unsigned int key = m_towerKey->ttKey(tt->phi(), tt->eta());
+      m_ttMap.insert(std::make_pair(key, tt));
+    }
+  }
+}
+
+// Set up CPM hits map
+
+void CpmTesterV1::setupCpmHitsMap(const CpmHitsCollection* const hitCollection)
+{
+  m_hitsMap.clear();
+  if (hitCollection) {
+    CpmHitsCollection::const_iterator pos  = hitCollection->begin();
+    CpmHitsCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CPMHits* const hits = *pos;
+      const int key = hits->crate()*100 + hits->module() - 1;
+      m_hitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Set up CMM-CP hits map
+
+void CpmTesterV1::setupCmmCpHitsMap(const CmmCpHitsCollection*
+                                                       const hitCollection)
+{
+  m_cmmHitsMap.clear();
+  if (hitCollection) {
+    CmmCpHitsCollection::const_iterator pos  = hitCollection->begin();
+    CmmCpHitsCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CMMCPHits* const hits = *pos;
+      const int key = hits->crate()*100 + hits->dataID();
+      m_cmmHitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Set up CPM RoI map
+
+void CpmTesterV1::setupCpmRoiMap(const CpmRoiCollection* const roiCollection)
+{
+  m_roiMap.clear();
+  if (roiCollection) {
+    CpmRoiCollection::const_iterator pos  = roiCollection->begin();
+    CpmRoiCollection::const_iterator pose = roiCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CPMRoI* const roi = *pos;
+      const uint32_t key = roi->roiWord();
+      m_roiMap.insert(std::make_pair(key, roi));
+    }
+  }
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmTesterV1.h b/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmTesterV1.h
new file mode 100755
index 0000000000000000000000000000000000000000..a08d05f35ebcf084eab350c55ccb550d53403b7d
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmTesterV1.h
@@ -0,0 +1,126 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPMTESTERV1_H
+#define TRIGT1CALOBYTESTREAM_CPMTESTERV1_H
+
+#include <map>
+#include <string>
+#include <stdint.h>
+
+#include "AthenaBaseComps/AthAlgorithm.h"
+#include "DataModel/DataVector.h"
+
+class ISvcLocator;
+class StatusCode;
+
+namespace LVL1 {
+  class CMMCPHits;
+  class CPMHits;
+  class CPMTower;
+  class CPMRoI;
+  class TriggerTowerKey;
+}
+
+namespace LVL1BS {
+
+/** Algorithm to test CPM component bytestream conversions.
+ *
+ *  Just prints out the contents of the CPMTower objects
+ *  and CPMHits objects.
+ *  Also includes CMMCPHits and CPMRoIs.
+ *
+ *  Run before writing bytestream and after reading it and compare.
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpmTesterV1 : public AthAlgorithm {
+
+ public:
+   CpmTesterV1(const std::string& name, ISvcLocator* pSvcLocator);
+   virtual ~CpmTesterV1();
+
+   virtual StatusCode initialize();
+   virtual StatusCode execute();
+   virtual StatusCode finalize();
+
+ private:
+   typedef DataVector<LVL1::CPMTower>                     CpmTowerCollection;
+   typedef DataVector<LVL1::CPMHits>                      CpmHitsCollection;
+   typedef DataVector<LVL1::CMMCPHits>                    CmmCpHitsCollection;
+   typedef DataVector<LVL1::CPMRoI>                       CpmRoiCollection;
+   typedef std::map<unsigned int, const LVL1::CPMTower*>  CpmTowerMap;
+   typedef std::map<int,          const LVL1::CPMHits*>   CpmHitsMap;
+   typedef std::map<int,          const LVL1::CMMCPHits*> CmmCpHitsMap;
+   typedef std::map<uint32_t,     const LVL1::CPMRoI*>    CpmRoiMap;
+
+   /// Print the CPM towers
+   void printCpmTowers(const std::string& source) const;
+   /// Print the CPM hits
+   void printCpmHits()   const;
+   /// Print the CMM-CP hits
+   void printCmmCpHits() const;
+   /// Print the CPM RoIs
+   void printCpmRois(const std::string& source)   const;
+
+   /// Print a vector
+   void printVec(const std::vector<int>& vec) const;
+   /// Print a vector of hits
+   void printVecH(const std::vector<unsigned int>& vec) const;
+
+   /// Set up CPM tower map
+   void setupCpmTowerMap(const CpmTowerCollection* ttCollection);
+   /// Set up CPM hits map
+   void setupCpmHitsMap(const CpmHitsCollection* hitCollection);
+   /// Set up CMM-CP hits map
+   void setupCmmCpHitsMap(const CmmCpHitsCollection* hitCollection);
+   /// Set up CPM RoI map
+   void setupCpmRoiMap(const CpmRoiCollection* roiCollection);
+
+   /// CPM tower key provider
+   LVL1::TriggerTowerKey* m_towerKey;
+   /// CPM tower container StoreGate key
+   std::string m_cpmTowerLocation;
+   /// CPM hits container StoreGate key
+   std::string m_cpmHitsLocation;
+   /// CMM-CP hits container StoreGate key
+   std::string m_cmmCpHitsLocation;
+   /// CPM RoI container StoreGate key
+   std::string m_cpmRoiLocation;
+   /// CPM RoI from RoIB container StoreGate key
+   std::string m_cpmRoiLocationRoib;
+   /// CPM tower overlap container StoreGate key
+   std::string m_cpmTowerLocationOverlap;
+   /// Force number of CPM slices
+   int m_forceSlicesCpm;
+   /// Force number of CMM slices
+   int m_forceSlicesCmm;
+   /// CPM tower print flag
+   int m_cpmTowerPrint;
+   /// CPM hits print flag
+   int m_cpmHitsPrint;
+   /// CMM-CP hits print flag
+   int m_cmmCpHitsPrint;
+   /// CPM RoI print flag
+   int m_cpmRoiPrint;
+   /// CPM RoI from RoIB print flag
+   int m_cpmRoiPrintRoib;
+   /// CPM tower overlap print flag
+   int m_cpmTowerPrintOverlap;
+
+   /// CPM tower map
+   CpmTowerMap  m_ttMap;
+   /// CPM hits map
+   CpmHitsMap   m_hitsMap;
+   /// CMM-CP hits map
+   CmmCpHitsMap m_cmmHitsMap;
+   /// CPM RoI map
+   CpmRoiMap    m_roiMap;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmTesterV2.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmTesterV2.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..cc65885467bad8c6115a6ceeb9d87b5c25a7fb3a
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmTesterV2.cxx
@@ -0,0 +1,474 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <utility>
+
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+#include "StoreGate/StoreGateSvc.h"
+
+#include "TrigT1CaloEvent/CMXCPHits.h"
+#include "TrigT1CaloEvent/CMXCPTob.h"
+#include "TrigT1CaloEvent/CPMTower.h"
+#include "TrigT1CaloEvent/CPMTobRoI.h"
+#include "TrigT1CaloUtils/DataError.h"
+#include "TrigT1CaloUtils/TriggerTowerKey.h"
+#include "TrigT1Interfaces/TrigT1CaloDefs.h"
+
+#include "CpmTesterV2.h"
+#include "../src/ModifySlices.h"
+
+namespace LVL1BS {
+
+CpmTesterV2::CpmTesterV2(const std::string& name, ISvcLocator* pSvcLocator)
+                       : AthAlgorithm(name, pSvcLocator),
+		         m_towerKey(0)
+{
+  declareProperty("CPMTowerLocation",
+           m_cpmTowerLocation   = LVL1::TrigT1CaloDefs::CPMTowerLocation);
+  declareProperty("CMXCPTobLocation",
+           m_cmxCpTobLocation   = LVL1::TrigT1CaloDefs::CMXCPTobLocation);
+  declareProperty("CMXCPHitsLocation",
+           m_cmxCpHitsLocation  = LVL1::TrigT1CaloDefs::CMXCPHitsLocation);
+  declareProperty("CPMTobRoILocation",
+           m_cpmRoiLocation     = LVL1::TrigT1CaloDefs::CPMTobRoILocation);
+  declareProperty("CPMTobRoILocationRoIB",
+           m_cpmRoiLocationRoib = LVL1::TrigT1CaloDefs::CPMTobRoILocation + "RoIB");
+  declareProperty("CPMTowerLocationOverlap",
+           m_cpmTowerLocationOverlap =
+	                   LVL1::TrigT1CaloDefs::CPMTowerLocation + "Overlap");
+
+  declareProperty("ForceSlicesCPM", m_forceSlicesCpm = 0);
+  declareProperty("ForceSlicesCMX", m_forceSlicesCmx = 0);
+
+  // By default print everything except RoIB RoIs and Tower overlap
+  declareProperty("CPMTowerPrint",   m_cpmTowerPrint   = 1);
+  declareProperty("CMXCPTobPrint",   m_cmxCpTobPrint   = 1,
+                  "Set to 2 to print presence maps (Neutral format only)");
+  declareProperty("CMXCPHitsPrint",  m_cmxCpHitsPrint  = 1,
+                  "Set to 2 to print overflow bit (Neutral format only)");
+  declareProperty("CPMTobRoIPrint",     m_cpmRoiPrint     = 1);
+  declareProperty("CPMTobRoIPrintRoIB", m_cpmRoiPrintRoib = 0);
+  declareProperty("CPMTowerPrintOverlap", m_cpmTowerPrintOverlap = 0);
+}
+
+CpmTesterV2::~CpmTesterV2()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode CpmTesterV2::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << /* version() */ PACKAGE_VERSION << endreq;
+
+  m_towerKey = new LVL1::TriggerTowerKey();
+
+  return StatusCode::SUCCESS;
+}
+
+// Execute
+
+StatusCode CpmTesterV2::execute()
+{
+  if ( !msgLvl(MSG::INFO) ) return StatusCode::SUCCESS;
+  msg(MSG::INFO);
+
+  if (m_cpmTowerPrint) {
+
+    // Find CPM towers
+
+    const CpmTowerCollection* ttCollection = 0;
+    StatusCode sc = evtStore()->retrieve(ttCollection, m_cpmTowerLocation);
+    if (sc.isFailure() || !ttCollection || ttCollection->empty()) {
+      msg() << "No core CPM towers found" << endreq;
+    } else {
+
+      // Order by eta, phi
+
+      setupCpmTowerMap(ttCollection);
+
+      // Print the CPM towers
+
+      printCpmTowers("core");
+    }
+  }
+
+  if (m_cpmTowerPrintOverlap) {
+
+    // Find overlap CPM towers
+
+    const CpmTowerCollection* ttCollection = 0;
+    StatusCode sc = evtStore()->retrieve(ttCollection, m_cpmTowerLocationOverlap);
+    if (sc.isFailure() || !ttCollection || ttCollection->empty()) {
+      msg() << "No overlap CPM towers found" << endreq;
+    } else {
+
+      // Order by eta, phi
+
+      setupCpmTowerMap(ttCollection);
+
+      // Print the overlap CPM towers
+
+      printCpmTowers("overlap");
+    }
+  }
+
+  if (m_cmxCpTobPrint) {
+
+    // Find CMX-CP TOBs
+
+    const CmxCpTobCollection* tobCollection = 0;
+    StatusCode sc = evtStore()->retrieve(tobCollection, m_cmxCpTobLocation);
+    if (sc.isFailure() || !tobCollection || tobCollection->empty()) {
+      msg() << "No CMX-CP TOBs found" << endreq;
+    } else {
+
+      // Order by crate, cmx, cpm, chip
+
+      setupCmxCpTobMap(tobCollection);
+
+      // Print the CMX-CP TOBs
+
+      printCmxCpTobs();
+    }
+  }
+
+  if (m_cmxCpHitsPrint) {
+
+    // Find CMX-CP hits
+
+    const CmxCpHitsCollection* hitCollection = 0;
+    StatusCode sc = evtStore()->retrieve(hitCollection, m_cmxCpHitsLocation);
+    if (sc.isFailure() || !hitCollection || hitCollection->empty()) {
+      msg() << "No CMX-CP Hits found" << endreq;
+    } else {
+
+      // Order by crate, cmx, source
+
+      setupCmxCpHitsMap(hitCollection);
+
+      // Print the CMX-CP hits
+
+      printCmxCpHits();
+    }
+  }
+
+  if (m_cpmRoiPrint) {
+
+    // Find CPM RoIs
+
+    const CpmRoiCollection* roiCollection = 0;
+    StatusCode sc = evtStore()->retrieve(roiCollection, m_cpmRoiLocation);
+    if (sc.isFailure() || !roiCollection || roiCollection->empty()) {
+      msg() << "No CPM RoIs found" << endreq;
+    } else {
+
+      // Order by RoI word
+
+      setupCpmRoiMap(roiCollection);
+
+      // Print the CPM RoIs
+
+      printCpmRois("DAQ");
+    }
+  }
+
+  if (m_cpmRoiPrintRoib) {
+
+    // Find CPM RoIs from RoIB
+
+    const CpmRoiCollection* roiCollection = 0;
+    StatusCode sc = evtStore()->retrieve(roiCollection, m_cpmRoiLocationRoib);
+    if (sc.isFailure() || !roiCollection || roiCollection->empty()) {
+      msg() << "No CPM RoIs from RoIB found" << endreq;
+    } else {
+
+      // Order by RoI word
+
+      setupCpmRoiMap(roiCollection);
+
+      // Print the CPM RoIs from RoIB
+
+      printCpmRois("RoIB");
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode CpmTesterV2::finalize()
+{
+  delete m_towerKey;
+
+  return StatusCode::SUCCESS;
+}
+
+// Print the CPM towers
+
+void CpmTesterV2::printCpmTowers(const std::string& source) const
+{
+  msg() << "Number of " << source << " CPM towers = "
+        << m_ttMap.size() << endreq;
+  CpmTowerMap::const_iterator mapIter = m_ttMap.begin();
+  CpmTowerMap::const_iterator mapEnd  = m_ttMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::CPMTower* const tt = mapIter->second;
+    int peak   = tt->peak();
+    int slices = (tt->emEnergyVec()).size();
+    if (m_forceSlicesCpm) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesCpm);
+      slices = m_forceSlicesCpm;
+    }
+    msg() << "key/eta/phi/peak/em/had/emErr/hadErr: "
+          << mapIter->first << "/" << tt->eta() << "/" << tt->phi() << "/"
+	  << peak << "/";
+
+    std::vector<int> emEnergy;
+    std::vector<int> hadEnergy;
+    std::vector<int> emError;
+    std::vector<int> hadError;
+    ModifySlices::data(tt->emEnergyVec(),  emEnergy,  slices);
+    ModifySlices::data(tt->hadEnergyVec(), hadEnergy, slices);
+    ModifySlices::data(tt->emErrorVec(),   emError,   slices);
+    ModifySlices::data(tt->hadErrorVec(),  hadError,  slices);
+    printVec(emEnergy);
+    printVec(hadEnergy);
+    msg() << MSG::hex;
+    printVec(emError);
+    printVec(hadError);
+    msg() << MSG::dec << endreq;
+  }
+}
+
+// Print the CMX-CP TOBs
+
+void CpmTesterV2::printCmxCpTobs() const
+{
+  msg() << "Number of CMX-CP TOBs = " << m_tobMap.size() << endreq;
+  CmxCpTobMap::const_iterator mapIter = m_tobMap.begin();
+  CmxCpTobMap::const_iterator mapEnd  = m_tobMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::CMXCPTob* const tb = mapIter->second;
+    int peak   = tb->peak();
+    int slices = (tb->energyVec()).size();
+    if (m_forceSlicesCmx) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesCmx);
+      slices = m_forceSlicesCmx;
+    }
+    if (m_cmxCpTobPrint == 2) {
+      msg() << "crate/cmx/cpm/chip/loc/peak/energy/isolation/error/presenceMap: ";
+    } else {
+      msg() << "crate/cmx/cpm/chip/loc/peak/energy/isolation/error: ";
+    }
+    msg() << tb->crate() << "/" << tb->cmx() << "/" << tb->cpm() << "/"
+	  << tb->chip() << "/" << tb->location() << "/" << peak << "/";
+
+    std::vector<int> energy;
+    std::vector<int> isolation;
+    std::vector<int> error;
+    ModifySlices::data(tb->energyVec(), energy, slices);
+    ModifySlices::data(tb->isolationVec(), isolation, slices);
+    ModifySlices::data(tb->errorVec(), error, slices);
+    printVec(energy);
+    msg() << MSG::hex;
+    printVec(isolation);
+    printVec(error);
+    if (m_cmxCpTobPrint == 2) {
+      std::vector<unsigned int> presence;
+      ModifySlices::data(tb->presenceMapVec(), presence, slices);
+      printVecH(presence, 1, 16);
+    }
+    msg() << MSG::dec << endreq;
+  }
+}
+
+// Print the CMX-CP hits
+
+void CpmTesterV2::printCmxCpHits() const
+{
+  msg() << "Number of CMX-CP Hits = " << m_hitsMap.size() << endreq;
+  CmxCpHitsMap::const_iterator mapIter = m_hitsMap.begin();
+  CmxCpHitsMap::const_iterator mapEnd  = m_hitsMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::CMXCPHits* const ch = mapIter->second;
+    int peak   = ch->peak();
+    int slices = (ch->hitsVec0()).size();
+    if (m_forceSlicesCmx) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesCmx);
+      slices = m_forceSlicesCmx;
+    }
+    msg() << "crate/cmx/source/peak/hits0/hits1/err0/err1: "
+	  << ch->crate() << "/" << ch->cmx() << "/" << ch->source() << "/"
+	  << peak << "/";
+
+    std::vector<unsigned int> hits0;
+    std::vector<unsigned int> hits1;
+    std::vector<int> err0;
+    std::vector<int> err1;
+    ModifySlices::data(ch->hitsVec0(), hits0, slices);
+    ModifySlices::data(ch->hitsVec1(), hits1, slices);
+    ModifySlices::data(ch->errorVec0(), err0, slices);
+    ModifySlices::data(ch->errorVec1(), err1, slices);
+    int bits = 3;
+    int words = 8;
+    if (ch->source() == LVL1::CMXCPHits::TOPO_CHECKSUM) {
+      bits = 16;
+      words = 1;
+    } else if (ch->source() == LVL1::CMXCPHits::TOPO_OCCUPANCY_MAP) {
+      bits = 1;
+      words = 14;
+    } else if (ch->source() == LVL1::CMXCPHits::TOPO_OCCUPANCY_COUNTS) {
+      words = 7;
+    }
+    printVecH(hits0, bits, words);
+    printVecH(hits1, bits, words);
+    msg() << MSG::hex;
+    if (m_cmxCpHitsPrint != 2) { // Suppress overflow bit
+      for (int sl = 0; sl < slices; ++sl) {
+        LVL1::DataError d0(err0[sl]);
+	d0.set(LVL1::DataError::Overflow, 0);
+	err0[sl] = d0.error();
+        LVL1::DataError d1(err1[sl]);
+	d1.set(LVL1::DataError::Overflow, 0);
+	err1[sl] = d1.error();
+      }
+    }
+    printVec(err0);
+    printVec(err1);
+    msg() << MSG::dec << endreq;
+  }
+}
+
+// Print the CPM RoIs
+
+void CpmTesterV2::printCpmRois(const std::string& source) const
+{
+  msg() << "Number of CPM RoIs (" << source << ") = " << m_roiMap.size()
+        << endreq;
+  CpmRoiMap::const_iterator mapIter = m_roiMap.begin();
+  CpmRoiMap::const_iterator mapEnd  = m_roiMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::CPMTobRoI* const roi = mapIter->second;
+    msg() << "crate/cpm/chip/loc/type/energy/isolation: "
+	  << roi->crate()    << "/" << roi->cpm() << "/" << roi->chip() << "/"
+	  << roi->location() << "/" << roi->type() << "/"
+	  << roi->energy() << "/" << MSG::hex << roi->isolation() << MSG::dec
+	  << endreq;
+  }
+}
+
+// Print a vector
+
+void CpmTesterV2::printVec(const std::vector<int>& vec) const
+{
+  std::vector<int>::const_iterator pos;
+  for (pos = vec.begin(); pos != vec.end(); ++pos) {
+    if (pos != vec.begin()) msg() << ",";
+    msg() << *pos;
+  }
+  msg() << "/";
+}
+
+// Print a vector of hits
+
+void CpmTesterV2::printVecH(const std::vector<unsigned int>& vec,
+                          const int bits, const int words) const
+{
+  const unsigned int mask = (1<<bits)-1;
+  std::vector<unsigned int>::const_iterator pos;
+  std::vector<unsigned int>::const_iterator posb = vec.begin();
+  std::vector<unsigned int>::const_iterator pose = vec.end();
+  for (pos = posb; pos != pose; ++pos) {
+    if (pos != posb) msg() << ",";
+    const unsigned int hits = *pos;
+    for (int i = 0; i < words; ++i) {
+      if (i != 0) msg() << ":";
+      const unsigned int thr = (hits >> (bits*i)) & mask;
+      msg() << thr;
+    }
+  }
+  msg() << "/";
+}
+
+// Set up CPM tower map
+
+void CpmTesterV2::setupCpmTowerMap(const CpmTowerCollection* const ttCollection)
+{
+  m_ttMap.clear();
+  if (ttCollection) {
+    CpmTowerCollection::const_iterator pos  = ttCollection->begin();
+    CpmTowerCollection::const_iterator pose = ttCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CPMTower* const tt = *pos;
+      const unsigned int key = m_towerKey->ttKey(tt->phi(), tt->eta());
+      m_ttMap.insert(std::make_pair(key, tt));
+    }
+  }
+}
+
+// Set up CMX-CP TOB map
+
+void CpmTesterV2::setupCmxCpTobMap(const CmxCpTobCollection* const tobCollection)
+{ 
+  m_tobMap.clear();
+  if (tobCollection) { 
+    CmxCpTobCollection::const_iterator pos  = tobCollection->begin();
+    CmxCpTobCollection::const_iterator pose = tobCollection->end();
+    for (; pos != pose; ++pos) {
+      LVL1::CMXCPTob* const tob = *pos;
+      const int crate = tob->crate();
+      const int cmx = tob->cmx(); 
+      const int cpm = tob->cpm();
+      const int chip = tob->chip();
+      const int loc = tob->location();
+      const int key = (((((((crate<<1)|cmx)<<4)|cpm)<<4)|chip)<<2)|loc;
+      m_tobMap.insert(std::make_pair(key, tob));
+    }
+  }
+}
+
+// Set up CMX-CP hits map
+
+void CpmTesterV2::setupCmxCpHitsMap(const CmxCpHitsCollection*
+                                                       const hitCollection)
+{
+  m_hitsMap.clear();
+  if (hitCollection) {
+    CmxCpHitsCollection::const_iterator pos  = hitCollection->begin();
+    CmxCpHitsCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CMXCPHits* const hits = *pos;
+      const int key = (((hits->crate()<<1)|hits->cmx())<<3)|hits->source();
+      m_hitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Set up CPM RoI map
+
+void CpmTesterV2::setupCpmRoiMap(const CpmRoiCollection* const roiCollection)
+{
+  m_roiMap.clear();
+  if (roiCollection) {
+    CpmRoiCollection::const_iterator pos  = roiCollection->begin();
+    CpmRoiCollection::const_iterator pose = roiCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CPMTobRoI* const roi = *pos;
+      const uint32_t key = roi->roiWord();
+      m_roiMap.insert(std::make_pair(key, roi));
+    }
+  }
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmTesterV2.h b/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmTesterV2.h
new file mode 100755
index 0000000000000000000000000000000000000000..dd479fd56d61b7c659734402869c6ce645a3094e
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/CpmTesterV2.h
@@ -0,0 +1,126 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_CPMTESTERV2_H
+#define TRIGT1CALOBYTESTREAM_CPMTESTERV2_H
+
+#include <map>
+#include <string>
+#include <stdint.h>
+
+#include "AthenaBaseComps/AthAlgorithm.h"
+#include "DataModel/DataVector.h"
+
+class ISvcLocator;
+class StatusCode;
+
+namespace LVL1 {
+  class CMXCPHits;
+  class CMXCPTob;
+  class CPMTower;
+  class CPMTobRoI;
+  class TriggerTowerKey;
+}
+
+namespace LVL1BS {
+
+/** Algorithm to test CPM component bytestream conversions.
+ *
+ *  Just prints out the contents of the CPMTower objects
+ *  Also includes CMXCPTob, CMXCPHits and CPMTobRoIs.
+ *
+ *  Run before writing bytestream and after reading it and compare.
+ *
+ *  @author Peter Faulkner
+ */
+
+class CpmTesterV2 : public AthAlgorithm {
+
+ public:
+   CpmTesterV2(const std::string& name, ISvcLocator* pSvcLocator);
+   virtual ~CpmTesterV2();
+
+   virtual StatusCode initialize();
+   virtual StatusCode execute();
+   virtual StatusCode finalize();
+
+ private:
+   typedef DataVector<LVL1::CPMTower>                     CpmTowerCollection;
+   typedef DataVector<LVL1::CMXCPTob>                     CmxCpTobCollection;
+   typedef DataVector<LVL1::CMXCPHits>                    CmxCpHitsCollection;
+   typedef DataVector<LVL1::CPMTobRoI>                       CpmRoiCollection;
+   typedef std::map<unsigned int, const LVL1::CPMTower*>  CpmTowerMap;
+   typedef std::map<int,          const LVL1::CMXCPTob*>  CmxCpTobMap;
+   typedef std::map<int,          const LVL1::CMXCPHits*> CmxCpHitsMap;
+   typedef std::map<uint32_t,     const LVL1::CPMTobRoI*> CpmRoiMap;
+
+   /// Print the CPM towers
+   void printCpmTowers(const std::string& source) const;
+   /// Print the CMX-CP TOBs
+   void printCmxCpTobs()   const;
+   /// Print the CMX-CP hits
+   void printCmxCpHits() const;
+   /// Print the CPM RoIs
+   void printCpmRois(const std::string& source)   const;
+
+   /// Print a vector
+   void printVec(const std::vector<int>& vec) const;
+   /// Print a vector of hits
+   void printVecH(const std::vector<unsigned int>& vec, int bits,
+                                                   int words) const;
+
+   /// Set up CPM tower map
+   void setupCpmTowerMap(const CpmTowerCollection* ttCollection);
+   /// Set up CMX-CP TOB map
+   void setupCmxCpTobMap(const CmxCpTobCollection* tobCollection);
+   /// Set up CMX-CP hits map
+   void setupCmxCpHitsMap(const CmxCpHitsCollection* hitCollection);
+   /// Set up CPM RoI map
+   void setupCpmRoiMap(const CpmRoiCollection* roiCollection);
+
+   /// CPM tower key provider
+   LVL1::TriggerTowerKey* m_towerKey;
+   /// CPM tower container StoreGate key
+   std::string m_cpmTowerLocation;
+   /// CMX-CP TOB container StoreGate key
+   std::string m_cmxCpTobLocation;
+   /// CMX-CP hits container StoreGate key
+   std::string m_cmxCpHitsLocation;
+   /// CPM RoI container StoreGate key
+   std::string m_cpmRoiLocation;
+   /// CPM RoI from RoIB container StoreGate key
+   std::string m_cpmRoiLocationRoib;
+   /// CPM tower overlap container StoreGate key
+   std::string m_cpmTowerLocationOverlap;
+   /// Force number of CPM slices
+   int m_forceSlicesCpm;
+   /// Force number of CMX slices
+   int m_forceSlicesCmx;
+   /// CPM tower print flag
+   int m_cpmTowerPrint;
+   /// CMX-CP TOB print flag
+   int m_cmxCpTobPrint;
+   /// CMX-CP hits print flag
+   int m_cmxCpHitsPrint;
+   /// CPM RoI print flag
+   int m_cpmRoiPrint;
+   /// CPM RoI from RoIB print flag
+   int m_cpmRoiPrintRoib;
+   /// CPM tower overlap print flag
+   int m_cpmTowerPrintOverlap;
+
+   /// CPM tower map
+   CpmTowerMap  m_ttMap;
+   /// CMX-CP TOB map
+   CmxCpTobMap  m_tobMap;
+   /// CMX-CP hits map
+   CmxCpHitsMap m_hitsMap;
+   /// CPM RoI map
+   CpmRoiMap    m_roiMap;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/ErrorTester.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/test/ErrorTester.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..7f3119c7bd9db16c84d0e0edd27b76fb57d982a5
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/ErrorTester.cxx
@@ -0,0 +1,88 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+#include "StoreGate/StoreGateSvc.h"
+#include "SGTools/StlVectorClids.h"
+
+#include "ErrorTester.h"
+
+namespace LVL1BS {
+
+ErrorTester::ErrorTester(const std::string& name, ISvcLocator* pSvcLocator)
+                     : AthAlgorithm(name, pSvcLocator)
+{
+  declareProperty("L1CaloErrorLocation",
+                   m_errorVectorLocation = "L1CaloUnpackingErrors");
+}
+
+ErrorTester::~ErrorTester()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode ErrorTester::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << /* version() */ PACKAGE_VERSION << endreq;
+
+  return StatusCode::SUCCESS;
+}
+
+// Execute
+
+StatusCode ErrorTester::execute()
+{
+  if ( !msgLvl(MSG::INFO) ) return StatusCode::SUCCESS;
+  msg(MSG::INFO);
+
+  const ErrorCollection* errVec = 0;
+  StatusCode sc = evtStore()->retrieve(errVec, m_errorVectorLocation);
+  if (sc.isFailure() || !errVec) {
+    msg() << "No error vector found for " << m_errorVectorLocation << endreq;
+  } else {
+    if (errVec->empty()) {
+      msg() << "Error vector empty" << endreq;
+    } else {
+      ErrorCollection::const_iterator iter  = errVec->begin();
+      ErrorCollection::const_iterator iterE = errVec->end();
+      unsigned int size1 = *iter;
+      ++iter;
+      msg() << "Number of ROB status errors: " << size1 << endreq;
+      while (iter != iterE) {
+	msg() << "ROB ID: " << MSG::hex << *iter << MSG::dec;
+	++iter;
+	if (iter != iterE) {
+	  if (size1 > 0) {
+	    msg() << "  status error: " << MSG::hex << *iter << MSG::dec
+	          << endreq;
+	    size1--;
+	  } else msg() << "  unpacking error: " << *iter << endreq;
+	  ++iter;
+        } else {
+	  msg() << "  vector truncated, no error value" << endreq;
+        }
+      }
+    }
+  }
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode ErrorTester::finalize()
+{
+
+  return StatusCode::SUCCESS;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/ErrorTester.h b/Trigger/TrigT1/TrigT1CaloByteStream/test/ErrorTester.h
new file mode 100644
index 0000000000000000000000000000000000000000..674da867a2a080c5a32f984401af3b9be141db28
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/ErrorTester.h
@@ -0,0 +1,45 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_ERRORTESTER_H
+#define TRIGT1CALOBYTESTREAM_ERRORTESTER_H
+
+#include <string>
+#include <vector>
+
+#include "AthenaBaseComps/AthAlgorithm.h"
+
+class ISvcLocator;
+class StatusCode;
+
+namespace LVL1BS {
+
+/** Algorithm to test unpacking error bytestream conversions.
+ *
+ *  Just prints out the contents of the Error vector
+ *
+ *  @author Peter Faulkner
+ */
+
+class ErrorTester : public AthAlgorithm {
+
+ public:
+   ErrorTester(const std::string& name, ISvcLocator* pSvcLocator);
+   virtual ~ErrorTester();
+
+   virtual StatusCode initialize();
+   virtual StatusCode execute();
+   virtual StatusCode finalize();
+
+ private:
+   typedef std::vector<unsigned int> ErrorCollection;
+
+   /// Error vector StoreGate key
+   std::string m_errorVectorLocation;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/JemErrors.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/test/JemErrors.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..d9be8df272766248a09270b17d185756a3da534f
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/JemErrors.cxx
@@ -0,0 +1,649 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "StoreGate/StoreGateSvc.h"
+#include "EventInfo/EventInfo.h"
+#include "EventInfo/EventID.h"
+
+#include "TrigT1CaloEvent/CMXEtSums.h"
+#include "TrigT1CaloEvent/CMXJetTob.h"
+#include "TrigT1CaloEvent/CMXJetHits.h"
+#include "TrigT1CaloEvent/CMXRoI.h"
+#include "TrigT1CaloEvent/JEMEtSums.h"
+#include "TrigT1CaloEvent/JEMTobRoI.h"
+#include "TrigT1CaloEvent/JetElement.h"
+#include "TrigT1CaloUtils/CoordToHardware.h"
+#include "TrigT1CaloUtils/DataError.h"
+#include "TrigT1Interfaces/Coordinate.h"
+#include "TrigT1Interfaces/TrigT1CaloDefs.h"
+
+#include "JemErrors.h"
+
+namespace LVL1BS {
+
+JemErrors::JemErrors(const std::string& name, ISvcLocator* pSvcLocator)
+  : AthAlgorithm(name, pSvcLocator),
+    m_doneThisEvent(false),
+    m_doneEmParity(false),
+    m_doneHadParity(false),
+    m_doneEmLinkDown(false),
+    m_doneHadLinkDown(false),
+    m_doneEmReadoutA(false),
+    m_doneEmReadoutB(false),
+    m_doneEmReadoutC(false),
+    m_doneHadReadoutA(false),
+    m_doneHadReadoutB(false),
+    m_doneHadReadoutC(false),
+    m_doneJeSubStatus(false),
+    m_doneJemEtReadoutA(false),
+    m_doneJemEtReadoutB(false),
+    m_doneJemEtReadoutC(false),
+    m_doneJemExReadoutA(false),
+    m_doneJemExReadoutB(false),
+    m_doneJemExReadoutC(false),
+    m_doneJemEyReadoutA(false),
+    m_doneJemEyReadoutB(false),
+    m_doneJemEyReadoutC(false),
+    m_doneCmxTobParity(false),
+    m_doneTobLgReadoutA(false),
+    m_doneTobLgReadoutB(false),
+    m_doneTobLgReadoutC(false),
+    m_doneTobSmReadoutA(false),
+    m_doneTobSmReadoutB(false),
+    m_doneTobSmReadoutC(false),
+    m_doneCmxHitsParity0(false),
+    m_doneCmxHitsParity1(false),
+    m_doneCmxHitsParity2(false),
+    m_doneCmxHitsParity3(false)
+
+{
+  declareProperty("JetElementLocation",
+           m_jetElementLocation = LVL1::TrigT1CaloDefs::JetElementLocation);
+  declareProperty("JEMEtSumsLocation",
+           m_jemEtSumsLocation  = LVL1::TrigT1CaloDefs::JEMEtSumsLocation);
+  declareProperty("CMXJetTobLocation",
+           m_cmxJetTobLocation  = LVL1::TrigT1CaloDefs::CMXJetTobLocation);
+  declareProperty("CMXJetHitsLocation",
+           m_cmxJetHitsLocation = LVL1::TrigT1CaloDefs::CMXJetHitsLocation);
+  declareProperty("CMXEtSumsLocation",
+           m_cmxEnergyLocation  = LVL1::TrigT1CaloDefs::CMXEtSumsLocation);
+  declareProperty("JEMTobRoILocation",
+           m_jemRoiLocation     = LVL1::TrigT1CaloDefs::JEMTobRoILocation);
+  declareProperty("CMXRoILocation",
+           m_cmxRoiLocation     = LVL1::TrigT1CaloDefs::CMXRoILocation);
+  declareProperty("JetElementLocationOut",
+           m_jetElementLocationOut = LVL1::TrigT1CaloDefs::JetElementLocation+"Errors");
+  declareProperty("JEMEtSumsLocationOut",
+           m_jemEtSumsLocationOut  = LVL1::TrigT1CaloDefs::JEMEtSumsLocation+"Errors");
+  declareProperty("CMXJetTobLocationOut",
+           m_cmxJetTobLocationOut  = LVL1::TrigT1CaloDefs::CMXJetTobLocation+"Errors");
+  declareProperty("CMXJetHitsLocationOut",
+           m_cmxJetHitsLocationOut = LVL1::TrigT1CaloDefs::CMXJetHitsLocation+"Errors");
+  declareProperty("CMXEtSumsLocationOut",
+           m_cmxEnergyLocationOut  = LVL1::TrigT1CaloDefs::CMXEtSumsLocation+"Errors");
+  declareProperty("JEMTobRoILocationOut",
+           m_jemRoiLocationOut     = LVL1::TrigT1CaloDefs::JEMTobRoILocation+"Errors");
+  declareProperty("CMXRoILocationOut",
+           m_cmxRoiLocationOut     = LVL1::TrigT1CaloDefs::CMXRoILocation+"Errors");
+
+  declareProperty("JetElementErrors", m_jetElementErrors = 0xffff);
+  declareProperty("JEMEtSumsErrors",  m_jemEtSumsErrors  = 0xffff);
+  declareProperty("CMXJetTobErrors",  m_cmxTobErrors     = 0xffff);
+  declareProperty("CMXJetHitsErrors", m_cmxHitsErrors    = 0xffff);
+  declareProperty("CMXEtSumsErrors",  m_cmxEtSumsErrors  = 0xffff);
+  declareProperty("JEMTobRoIErrors",  m_jemRoiErrors     = 0xffff);
+  declareProperty("CMXRoIErrors",     m_cmxRoiErrors     = 0xffff);
+}
+
+JemErrors::~JemErrors()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode JemErrors::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << /* version() */ PACKAGE_VERSION << endreq;
+
+  // Initialise individual error flags
+
+  m_doneEmParity    = ((m_jetElementErrors&Error1) == 0);
+  m_doneHadParity   = ((m_jetElementErrors&Error2) == 0);
+  m_doneEmLinkDown  = ((m_jetElementErrors&Error3) == 0);
+  m_doneHadLinkDown = ((m_jetElementErrors&Error4) == 0);
+  m_doneEmReadoutA  = ((m_jetElementErrors&Error5) == 0);
+  m_doneEmReadoutB  = ((m_jetElementErrors&Error6) == 0);
+  m_doneEmReadoutC  = ((m_jetElementErrors&Error7) == 0);
+  m_doneHadReadoutA = ((m_jetElementErrors&Error8) == 0);
+  m_doneHadReadoutB = ((m_jetElementErrors&Error9) == 0);
+  m_doneHadReadoutC = ((m_jetElementErrors&Error10) == 0);
+  m_doneJeSubStatus = ((m_jetElementErrors&Error11) == 0);
+
+  m_doneJemEtReadoutA = ((m_jemEtSumsErrors&Error1) == 0);
+  m_doneJemEtReadoutB = ((m_jemEtSumsErrors&Error2) == 0);
+  m_doneJemEtReadoutC = ((m_jemEtSumsErrors&Error3) == 0);
+  m_doneJemExReadoutA = ((m_jemEtSumsErrors&Error4) == 0);
+  m_doneJemExReadoutB = ((m_jemEtSumsErrors&Error5) == 0);
+  m_doneJemExReadoutC = ((m_jemEtSumsErrors&Error6) == 0);
+  m_doneJemEyReadoutA = ((m_jemEtSumsErrors&Error7) == 0);
+  m_doneJemEyReadoutB = ((m_jemEtSumsErrors&Error8) == 0);
+  m_doneJemEyReadoutC = ((m_jemEtSumsErrors&Error9) == 0);
+
+  m_doneCmxTobParity  = ((m_cmxTobErrors&Error1) == 0);
+  m_doneTobLgReadoutA = ((m_cmxTobErrors&Error2) == 0);
+  m_doneTobLgReadoutB = ((m_cmxTobErrors&Error3) == 0);
+  m_doneTobLgReadoutC = ((m_cmxTobErrors&Error4) == 0);
+  m_doneTobSmReadoutA = ((m_cmxTobErrors&Error5) == 0);
+  m_doneTobSmReadoutB = ((m_cmxTobErrors&Error6) == 0);
+  m_doneTobSmReadoutC = ((m_cmxTobErrors&Error7) == 0);
+
+  m_doneCmxHitsParity0 = ((m_cmxHitsErrors&Error1) == 0);
+  m_doneCmxHitsParity1 = ((m_cmxHitsErrors&Error2) == 0);
+  m_doneCmxHitsParity2 = ((m_cmxHitsErrors&Error3) == 0);
+  m_doneCmxHitsParity3 = ((m_cmxHitsErrors&Error4) == 0);
+
+  return StatusCode::SUCCESS;
+}
+
+// Execute
+
+StatusCode JemErrors::execute()
+{
+  if ( !msgLvl(MSG::INFO) ) return StatusCode::SUCCESS;
+  msg(MSG::INFO);
+
+  m_doneThisEvent = false;
+
+  if (m_jetElementErrors) {
+
+    // Find jet elements
+
+    const JetElementCollection* jeCollection = 0;
+    StatusCode sc = evtStore()->retrieve(jeCollection, m_jetElementLocation);
+    if (sc.isFailure() || !jeCollection || jeCollection->empty()) {
+      msg() << "No Jet Elements found" << endreq;
+      jeCollection = 0;
+    }
+
+    // Generate jet element errors
+
+    JetElementCollection* errCollection = new JetElementCollection;
+    if (jeCollection) {
+      jetElementErrors(jeCollection, errCollection);
+    }
+
+    // Save error collection
+
+    sc = evtStore()->record(errCollection, m_jetElementLocationOut);
+    if (sc.isFailure()) {
+      msg(MSG::ERROR) << "Error recording JetElement container in TDS " << endreq;
+      return sc;
+    }
+  }
+
+  if (m_jemEtSumsErrors) {
+
+    // Find energy sums
+
+    const EnergySumsCollection* etCollection = 0;
+    StatusCode sc = evtStore()->retrieve(etCollection, m_jemEtSumsLocation);
+    if (sc.isFailure() || !etCollection || etCollection->empty()) {
+      msg() << "No Energy Sums found" << endreq;
+      etCollection = 0;
+    }
+
+    // Generate energy sum errors
+
+    EnergySumsCollection* errCollection = new EnergySumsCollection;
+    if (etCollection) {
+      jemEtSumsErrors(etCollection, errCollection);
+    }
+
+    // Save error collection
+
+    sc = evtStore()->record(errCollection, m_jemEtSumsLocationOut);
+    if (sc.isFailure()) {
+      msg(MSG::ERROR) << "Error recording JEMEtSums container in TDS " << endreq;
+      return sc;
+    }
+  }
+
+  if (m_cmxTobErrors) {
+
+    // Find CMX TOBs
+
+    const CmxJetTobCollection* tobCollection = 0;
+    StatusCode sc = evtStore()->retrieve(tobCollection, m_cmxJetTobLocation);
+    if (sc.isFailure() || !tobCollection || tobCollection->empty()) {
+      msg() << "No CMX TOBs found" << endreq;
+      tobCollection = 0;
+    }
+
+    // Generate CMX TOB errors
+
+    CmxJetTobCollection* errCollection = new CmxJetTobCollection;
+    if (tobCollection) {
+      cmxTobErrors(tobCollection, errCollection);
+    }
+
+    // Save error collection
+
+    sc = evtStore()->record(errCollection, m_cmxJetTobLocationOut);
+    if (sc.isFailure()) {
+      msg(MSG::ERROR) << "Error recording CMXJetTob container in TDS " << endreq;
+      return sc;
+    }
+  }
+
+  if (m_cmxHitsErrors) {
+
+    // Find CMX hits
+
+    const CmxJetHitsCollection* hitCollection = 0;
+    StatusCode sc = evtStore()->retrieve(hitCollection, m_cmxJetHitsLocation);
+    if (sc.isFailure() || !hitCollection || hitCollection->empty()) {
+      msg() << "No CMX Hits found" << endreq;
+      hitCollection = 0;
+    }
+
+    // Generate CMX Hits errors
+
+    CmxJetHitsCollection* errCollection = new CmxJetHitsCollection;
+    if (hitCollection) {
+      cmxHitsErrors(hitCollection, errCollection);
+    }
+
+    // Save error collection
+
+    sc = evtStore()->record(errCollection, m_cmxJetHitsLocationOut);
+    if (sc.isFailure()) {
+      msg(MSG::ERROR) << "Error recording CMXJetHits container in TDS " << endreq;
+      return sc;
+    }
+  }
+/*
+  if (m_cmxEtSumsErrors) {
+
+    // Find CMX energy sums
+
+    const CmxEnergyCollection* etCollection = 0;
+    StatusCode sc = evtStore()->retrieve(etCollection, m_cmxEnergyLocation);
+    if (sc.isFailure() || !etCollection || etCollection->empty()) {
+      msg() << "No CMX Energy Sums found" << endreq;
+      etCollection = 0;
+    }
+
+    // Generate CMX energy sums errors
+
+    CmxEnergyCollection* errCollection = new CmxEnergyCollection;
+    if (etCollection) {
+      cmxEtSumsErrors(etCollection, errCollection);
+    }
+
+    // Save error collection
+
+    sc = evtStore()->record(errCollection, m_cmxEnergyLocationOut);
+    if (sc.isFailure()) {
+      msg(MSG::ERROR) << "Error recording CMXEtSums container in TDS " << endreq;
+      return sc;
+    }
+  }
+
+  if (m_jemRoiErrors) {
+
+    // Find JEM RoIs
+
+    const JemRoiCollection* jrCollection = 0;
+    StatusCode sc = evtStore()->retrieve(jrCollection, m_jemRoiLocation);
+    if (sc.isFailure() || !jrCollection || jrCollection->empty()) {
+      msg() << "No JEM RoIs found" << endreq;
+      jrCollection = 0;
+    }
+
+    // Generate JEM RoI errors
+
+    JemRoiCollection* errCollection = new JemRoiCollection;
+    if (jrCollection) {
+      jemRoiErrors(jrCollection, errCollection);
+    }
+
+    // Save error collection
+
+    sc = evtStore()->record(errCollection, m_jemRoiLocationOut);
+    if (sc.isFailure()) {
+      msg(MSG::ERROR) << "Error recording JEMTobRoI container in TDS " << endreq;
+      return sc;
+    }
+  }
+
+  if (m_cmxRoiErrors) {
+
+    // Find CMX RoIs
+
+    const LVL1::CMXRoI* crCollection = 0;
+    StatusCode sc = evtStore()->retrieve(crCollection, m_cmxRoiLocation);
+    if (sc.isFailure() || !crCollection || (!crCollection->roiWord(0) &&
+                                            !crCollection->roiWord(1) &&
+                                            !crCollection->roiWord(2) &&
+                                            !crCollection->roiWord(3) &&
+                                            !crCollection->roiWord(4) &&
+                                            !crCollection->roiWord(5))) {
+      msg() << "No CMX RoIs found" << endreq;
+      crCollection = 0;
+    }
+
+    // Generate CMX RoI errors
+
+    CMXRoI* errCollection = 0;
+    if (crCollection) {
+      cmxRoiErrors(crCollection, errCollection);
+    } else errCollection = new CMXRoI();
+
+    // Save error collection
+
+    sc = evtStore()->record(errCollection, m_cmxRoiLocationOut);
+    if (sc.isFailure()) {
+      msg(MSG::ERROR) << "Error recording CMXRoI container in TDS " << endreq;
+      return sc;
+    }
+  }
+*/
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode JemErrors::finalize()
+{
+  return StatusCode::SUCCESS;
+}
+
+// Generate JetElement errors
+
+void JemErrors::jetElementErrors(const JetElementCollection* jeCollection,
+                                       JetElementCollection* errCollection)
+{
+  LVL1::CoordToHardware converter;
+  JetElementCollection::const_iterator iter    = jeCollection->begin();
+  JetElementCollection::const_iterator iterEnd = jeCollection->end();
+  for (; iter != iterEnd; ++iter) {
+    const LVL1::JetElement* const je = *iter;
+    const double eta = je->eta();
+    const double phi = je->phi();
+    const LVL1::Coordinate coord(phi, eta);
+    const bool overlap = (converter.jepCrateOverlap(coord) <= 1);
+    unsigned int key = je->key();
+    const int peak = je->peak();
+    std::vector<int> emEnergy(je->emEnergyVec());
+    std::vector<int> hadEnergy(je->hadEnergyVec());
+    std::vector<int> emError(je->emErrorVec());
+    std::vector<int> hadError(je->hadErrorVec());
+    std::vector<int> linkError(je->linkErrorVec());
+    if (!m_doneThisEvent && !overlap) {
+      std::string errorType = "";
+      if (!m_doneEmParity) {
+        emError[peak] |= (1 << LVL1::DataError::Parity);
+        errorType = "EM Parity";
+        m_doneEmParity = true;
+      } else if (!m_doneHadParity) {
+        hadError[peak] |= (1 << LVL1::DataError::Parity);
+        errorType = "Had Parity";
+        m_doneHadParity = true;
+      } else if (!m_doneEmLinkDown) {
+        emError[peak] |= (1 << LVL1::DataError::LinkDown);
+	linkError[peak] |= 0x1;
+        errorType = "EM Link Down";
+        m_doneEmLinkDown = true;
+      } else if (!m_doneHadLinkDown) {
+        hadError[peak] |= (1 << LVL1::DataError::LinkDown);
+	linkError[peak] |= 0x2;
+        errorType = "Had Link Down";
+        m_doneHadLinkDown = true;
+      } else if (!m_doneEmReadoutA && emEnergy[peak] != 0 &&
+                 !je->isEmSaturated()) {
+        emEnergy[peak] += 1;
+        errorType = "EM readout non-zero mismatch";
+        m_doneEmReadoutA = true;
+      } else if (!m_doneEmReadoutB && emEnergy[peak] == 0) {
+        emEnergy[peak] = 1;
+        errorType = "EM readout sim zero mismatch";
+        m_doneEmReadoutB = true;
+      } else if (!m_doneEmReadoutC && emEnergy[peak] != 0 &&
+                                      hadEnergy[peak] != 0) {
+        emEnergy[peak] = 0;
+        errorType = "EM readout data zero mismatch";
+        m_doneEmReadoutC = true;
+      } else if (!m_doneHadReadoutA && hadEnergy[peak] != 0 &&
+                 !je->isHadSaturated()) {
+        hadEnergy[peak] += 1;
+        errorType = "Had readout non-zero mismatch";
+        m_doneHadReadoutA = true;
+      } else if (!m_doneHadReadoutB && hadEnergy[peak] == 0) {
+        hadEnergy[peak] = 1;
+        errorType = "Had readout sim zero mismatch";
+        m_doneHadReadoutB = true;
+      } else if (!m_doneHadReadoutC && hadEnergy[peak] != 0 &&
+                                       emEnergy[peak] != 0) {
+        hadEnergy[peak] = 0;
+        errorType = "Had readout data zero mismatch";
+        m_doneHadReadoutC = true;
+      } else if (!m_doneJeSubStatus) {
+        emError[peak]  |= (1 << LVL1::DataError::BCNMismatch);
+        hadError[peak] |= (1 << LVL1::DataError::BCNMismatch);
+        errorType = "SubStatus";
+        m_doneJeSubStatus = true;
+      }
+      if (errorType != "") {
+        errorMessage("JetElements " + errorType);
+        m_doneThisEvent = true;
+      }
+    }
+    LVL1::JetElement* err = new LVL1::JetElement(phi, eta, emEnergy, hadEnergy,
+                            key, emError, hadError, linkError, peak);
+    errCollection->push_back(err);
+  }
+}
+
+// Generate JEMEtSums errors
+
+void JemErrors::jemEtSumsErrors(const EnergySumsCollection* etCollection,
+                                      EnergySumsCollection* errCollection)
+{
+  EnergySumsCollection::const_iterator iter    = etCollection->begin();
+  EnergySumsCollection::const_iterator iterEnd = etCollection->end();
+  for (; iter != iterEnd; ++iter) {
+    const LVL1::JEMEtSums* const es = *iter;
+    const int crate = es->crate();
+    const int module = es->module();
+    const int peak = es->peak();
+    std::vector<unsigned int> et(es->EtVec());
+    std::vector<unsigned int> ex(es->ExVec());
+    std::vector<unsigned int> ey(es->EyVec());
+    if (!m_doneThisEvent) {
+      std::string errorType = "";
+      if (!m_doneJemEtReadoutA && et[peak] != 0) {
+        et[peak] += 1;
+        errorType = "Et readout non-zero mismatch";
+        m_doneJemEtReadoutA = true;
+      } else if (!m_doneJemEtReadoutB && et[peak] == 0) {
+        et[peak] = 1;
+        errorType = "Et readout sim zero mismatch";
+        m_doneJemEtReadoutB = true;
+      } else if (!m_doneJemEtReadoutC && et[peak] != 0) {
+        et[peak] = 0;
+        errorType = "Et readout data zero mismatch";
+        m_doneJemEtReadoutC = true;
+      } else if (!m_doneJemExReadoutA && ex[peak] != 0) {
+        ex[peak] += 1;
+        errorType = "Ex readout non-zero mismatch";
+        m_doneJemExReadoutA = true;
+      } else if (!m_doneJemExReadoutB && ex[peak] == 0) {
+        ex[peak] = 1;
+        errorType = "Ex readout sim zero mismatch";
+        m_doneJemExReadoutB = true;
+      } else if (!m_doneJemExReadoutC && ex[peak] != 0) {
+        ex[peak] = 0;
+        errorType = "Ex readout data zero mismatch";
+        m_doneJemExReadoutC = true;
+      } else if (!m_doneJemEyReadoutA && ey[peak] != 0) {
+        ey[peak] += 1;
+        errorType = "Ey readout non-zero mismatch";
+        m_doneJemEyReadoutA = true;
+      } else if (!m_doneJemEyReadoutB && ey[peak] == 0) {
+        ey[peak] = 1;
+        errorType = "Ey readout sim zero mismatch";
+        m_doneJemEyReadoutB = true;
+      } else if (!m_doneJemEyReadoutC && ey[peak] != 0) {
+        ey[peak] = 0;
+        errorType = "Ey readout data zero mismatch";
+        m_doneJemEyReadoutC = true;
+      }
+      if (errorType != "") {
+        errorMessage("JEMEtSums " + errorType);
+        m_doneThisEvent = true;
+      }
+    }
+    LVL1::JEMEtSums* err = new LVL1::JEMEtSums(crate, module,
+                                               et, ex, ey, peak);
+    errCollection->push_back(err);
+  }
+}
+
+// Generate CMXJetTob errors
+
+void JemErrors::cmxTobErrors(const CmxJetTobCollection* tobCollection,
+                                   CmxJetTobCollection* errCollection)
+{
+  CmxJetTobCollection::const_iterator iter    = tobCollection->begin();
+  CmxJetTobCollection::const_iterator iterEnd = tobCollection->end();
+  for (; iter != iterEnd; ++iter) {
+    const LVL1::CMXJetTob* const tob = *iter;
+    const int crate = tob->crate();
+    const int jem   = tob->jem();
+    const int frame = tob->frame();
+    const int loc   = tob->location();
+    const int peak  = tob->peak();
+    std::vector<int> energyLg(tob->energyLgVec());
+    std::vector<int> energySm(tob->energySmVec());
+    std::vector<int> error(tob->errorVec());
+    std::vector<unsigned int> presence(tob->presenceMapVec());
+    if (!m_doneThisEvent) {
+      std::string errorType = "";
+      if (!m_doneCmxTobParity) {
+        error[peak] |= (1 << LVL1::DataError::Parity);
+        error[peak] |= (1 << LVL1::DataError::ParityPhase1);
+        errorType = "CMX-Jet TOB Parity";
+        m_doneCmxTobParity = true;
+      } else if (!m_doneTobLgReadoutA && energyLg[peak] != 0) {
+        energyLg[peak] += 1;
+        errorType = "CMX-Jet TOB Energy Large readout non-zero mismatch";
+        m_doneTobLgReadoutA = true;
+      } else if (!m_doneTobLgReadoutB && energyLg[peak] == 0) { // never happen?
+        energyLg[peak] = 1;
+        errorType = "CMX-Jet TOB Energy Large readout sim zero mismatch";
+        m_doneTobLgReadoutB = true;
+      } else if (!m_doneTobLgReadoutC && energyLg[peak] != 0) {
+        energyLg[peak] = 0;
+        errorType = "CMX-Jet TOB Energy Large readout data zero mismatch";
+        m_doneTobLgReadoutC = true;
+      } else if (!m_doneTobSmReadoutA && energySm[peak] != 0) {
+        energySm[peak] += 1;
+        errorType = "CMX-Jet TOB Energy Small readout non-zero mismatch";
+        m_doneTobSmReadoutA = true;
+      } else if (!m_doneTobSmReadoutB && energySm[peak] == 0) {
+        energySm[peak] = 1;
+        errorType = "CMX-Jet TOB Energy Small readout sim zero mismatch";
+        m_doneTobSmReadoutB = true;
+      } else if (!m_doneTobSmReadoutC && energySm[peak] != 0) {
+        energySm[peak] = 0;
+        errorType = "CMX-Jet TOB Energy Small readout data zero mismatch";
+        m_doneTobSmReadoutC = true;
+      }
+      if (errorType != "") {
+        errorMessage("CMXJetTob " + errorType);
+        m_doneThisEvent = true;
+      }
+    }
+    LVL1::CMXJetTob* err = new LVL1::CMXJetTob(crate, jem, frame, loc,
+                                               energyLg, energySm, error,
+					       presence, peak);
+    errCollection->push_back(err);
+  }
+}
+
+// Generate CMXJetHits errors
+
+void JemErrors::cmxHitsErrors(const CmxJetHitsCollection* hitCollection,
+                                    CmxJetHitsCollection* errCollection)
+{
+  CmxJetHitsCollection::const_iterator iter    = hitCollection->begin();
+  CmxJetHitsCollection::const_iterator iterEnd = hitCollection->end();
+  for (; iter != iterEnd; ++iter) {
+    const LVL1::CMXJetHits* const hits = *iter;
+    const int crate  = hits->crate();
+    const int source = hits->source();
+    const int peak   = hits->peak();
+    std::vector<unsigned int> hits0(hits->hitsVec0());
+    std::vector<unsigned int> hits1(hits->hitsVec1());
+    std::vector<int> error0(hits->errorVec0());
+    std::vector<int> error1(hits->errorVec1());
+    if (!m_doneThisEvent) {
+      std::string errorType = "";
+      if (!m_doneCmxHitsParity0 && source == LVL1::CMXJetHits::REMOTE_MAIN) {
+        error0[peak] |= (1 << LVL1::DataError::Parity);
+        errorType = "CMX-Jet Hits Parity Cable 0 Phase 0";
+        m_doneCmxHitsParity0 = true;
+      } else if (!m_doneCmxHitsParity1 && source == LVL1::CMXJetHits::REMOTE_MAIN) {
+        error1[peak] |= (1 << LVL1::DataError::Parity);
+        errorType = "CMX-Jet Hits Parity Cable 0 Phase 1";
+        m_doneCmxHitsParity1 = true;
+      } else if (!m_doneCmxHitsParity2 && source == LVL1::CMXJetHits::REMOTE_FORWARD) {
+        error0[peak] |= (1 << LVL1::DataError::Parity);
+        errorType = "CMX-Jet Hits Parity Cable 1 Phase 0";
+        m_doneCmxHitsParity2 = true;
+      } else if (!m_doneCmxHitsParity3 && source == LVL1::CMXJetHits::REMOTE_FORWARD) {
+        error1[peak] |= (1 << LVL1::DataError::Parity);
+        errorType = "CMX-Jet Hits Parity Cable 1 Phase 1";
+        m_doneCmxHitsParity3 = true;
+      /*
+       * To be continued?
+      */
+      }
+      if (errorType != "") {
+        errorMessage("CMXJetHits " + errorType);
+        m_doneThisEvent = true;
+      }
+    }
+    LVL1::CMXJetHits* err = new LVL1::CMXJetHits(crate, source, hits0, hits1,
+						 error0, error1, peak);
+    errCollection->push_back(err);
+  }
+}
+
+// Print a message when error generated
+
+void JemErrors::errorMessage(const std::string& errmsg)
+{
+  int eventNumber = 0;
+  const EventInfo* evInfo = 0;
+  StatusCode sc = evtStore()->retrieve(evInfo);
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "No EventInfo found" << endreq;
+  } else {
+    const EventID* evID = evInfo->event_ID();
+    if (evID) eventNumber = evID->event_number();
+  }
+  msg(MSG::INFO) << "Event " << eventNumber
+                 << " has error " << errmsg << endreq;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/JemErrors.h b/Trigger/TrigT1/TrigT1CaloByteStream/test/JemErrors.h
new file mode 100755
index 0000000000000000000000000000000000000000..1a2a6686d09fe63c5d8bf6ec80fc7261f41c9dec
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/JemErrors.h
@@ -0,0 +1,161 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEMERRORS_H
+#define TRIGT1CALOBYTESTREAM_JEMERRORS_H
+
+#include <string>
+#include <stdint.h>
+
+#include "AthenaBaseComps/AthAlgorithm.h"
+#include "DataModel/DataVector.h"
+
+class ISvcLocator;
+class StatusCode;
+
+namespace LVL1 {
+  class CMXEtSums;
+  class CMXJetTob;
+  class CMXJetHits;
+  class CMXRoI;
+  class JEMEtSums;
+  class JEMTobRoI;
+  class JetElement;
+}
+
+namespace LVL1BS {
+
+/** Algorithm to add errors to JEM data for testing bytestream converters
+ *  and monitoring.
+ *
+ *
+ *  @author Peter Faulkner
+ */
+
+class JemErrors : public AthAlgorithm {
+
+ public:
+   JemErrors(const std::string& name, ISvcLocator* pSvcLocator);
+   virtual ~JemErrors();
+
+   virtual StatusCode initialize();
+   virtual StatusCode execute();
+   virtual StatusCode finalize();
+
+ private:
+   enum ErrorBits { Error1 = 0x1, Error2 = 0x2, Error3 = 0x4, Error4 = 0x8,
+                    Error5 = 0x10, Error6 = 0x20, Error7 = 0x40, Error8 = 0x80,
+                    Error9 = 0x100, Error10 = 0x200, Error11 = 0x400,
+                    Error12 = 0x800, Error13 = 0x1000, Error14 = 0x2000,
+                    Error15 = 0x4000, Error16 = 0x8000 };
+
+   typedef DataVector<LVL1::JetElement> JetElementCollection;
+   typedef DataVector<LVL1::JEMEtSums>  EnergySumsCollection;
+   typedef DataVector<LVL1::CMXJetTob>  CmxJetTobCollection;
+   typedef DataVector<LVL1::CMXJetHits> CmxJetHitsCollection;
+   typedef DataVector<LVL1::CMXEtSums>  CmxEnergyCollection;
+   typedef DataVector<LVL1::JEMTobRoI>  JemRoiCollection;
+
+   /// Generate JetElement errors
+   void jetElementErrors(const JetElementCollection* jeCollection,
+                               JetElementCollection* errCollection);
+   /// Generate JEMEtSums errors
+   void jemEtSumsErrors(const EnergySumsCollection* etCollection,
+                              EnergySumsCollection* errCollection);
+   /// Generate CMXJetTob errors
+   void cmxTobErrors(const CmxJetTobCollection* tobCollection,
+                           CmxJetTobCollection* errCollection);
+   /// Generate CMXJetHits errors
+   void cmxHitsErrors(const CmxJetHitsCollection* hitCollection,
+                            CmxJetHitsCollection* errCollection);
+
+   /// Print a message when error generated
+   void errorMessage(const std::string& msg);
+
+   /// Jet element container input StoreGate key
+   std::string m_jetElementLocation;
+   /// Energy sums container input StoreGate key
+   std::string m_jemEtSumsLocation;
+   /// CMX TOB container input StoreGate key
+   std::string m_cmxJetTobLocation;
+   /// CMX hits container input StoreGate key
+   std::string m_cmxJetHitsLocation;
+   /// CMX energy sums container input StoreGate key
+   std::string m_cmxEnergyLocation;
+   /// JEM RoI container input StoreGate key
+   std::string m_jemRoiLocation;
+   /// CMX RoI container input StoreGate key
+   std::string m_cmxRoiLocation;
+   /// Jet element container output StoreGate key
+   std::string m_jetElementLocationOut;
+   /// Energy sums container output StoreGate key
+   std::string m_jemEtSumsLocationOut;
+   /// CMX TOB container output StoreGate key
+   std::string m_cmxJetTobLocationOut;
+   /// CMX hits container output StoreGate key
+   std::string m_cmxJetHitsLocationOut;
+   /// CMX energy sums container output StoreGate key
+   std::string m_cmxEnergyLocationOut;
+   /// JEM RoI container output StoreGate key
+   std::string m_jemRoiLocationOut;
+   /// CMX RoI container output StoreGate key
+   std::string m_cmxRoiLocationOut;
+   /// Jet element errors flag
+   int m_jetElementErrors;
+   /// Energy sums errors flag
+   int m_jemEtSumsErrors;
+   /// CMX TOB errors flag
+   int m_cmxTobErrors;
+   /// CMX hits errors flag
+   int m_cmxHitsErrors;
+   /// CMX energy sums errors flag
+   int m_cmxEtSumsErrors;
+   /// JEM RoI errors flag
+   int m_jemRoiErrors;
+   /// CMX RoI errors flag
+   int m_cmxRoiErrors;
+
+   /// Individual error done flags
+   bool m_doneThisEvent;
+   /// JetElement
+   bool m_doneEmParity;
+   bool m_doneHadParity;
+   bool m_doneEmLinkDown;
+   bool m_doneHadLinkDown;
+   bool m_doneEmReadoutA;
+   bool m_doneEmReadoutB;
+   bool m_doneEmReadoutC;
+   bool m_doneHadReadoutA;
+   bool m_doneHadReadoutB;
+   bool m_doneHadReadoutC;
+   bool m_doneJeSubStatus;
+   /// JEMEtSums
+   bool m_doneJemEtReadoutA;
+   bool m_doneJemEtReadoutB;
+   bool m_doneJemEtReadoutC;
+   bool m_doneJemExReadoutA;
+   bool m_doneJemExReadoutB;
+   bool m_doneJemExReadoutC;
+   bool m_doneJemEyReadoutA;
+   bool m_doneJemEyReadoutB;
+   bool m_doneJemEyReadoutC;
+   /// CMXJetTob
+   bool m_doneCmxTobParity;
+   bool m_doneTobLgReadoutA;
+   bool m_doneTobLgReadoutB;
+   bool m_doneTobLgReadoutC;
+   bool m_doneTobSmReadoutA;
+   bool m_doneTobSmReadoutB;
+   bool m_doneTobSmReadoutC;
+   /// CMXJetHits
+   bool m_doneCmxHitsParity0;
+   bool m_doneCmxHitsParity1;
+   bool m_doneCmxHitsParity2;
+   bool m_doneCmxHitsParity3;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/JemTester.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/test/JemTester.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..026aa89a9d4e42921e336c71849d125a671f0a9a
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/JemTester.cxx
@@ -0,0 +1,694 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <numeric>
+#include <utility>
+
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "StoreGate/StoreGateSvc.h"
+
+#include "TrigT1CaloEvent/CMMEtSums.h"
+#include "TrigT1CaloEvent/CMMJetHits.h"
+#include "TrigT1CaloEvent/CMMRoI.h"
+#include "TrigT1CaloEvent/JEMEtSums.h"
+#include "TrigT1CaloEvent/JEMHits.h"
+#include "TrigT1CaloEvent/JEMRoI.h"
+#include "TrigT1CaloEvent/JetElement.h"
+#include "TrigT1CaloUtils/JetElementKey.h"
+#include "TrigT1Interfaces/TrigT1CaloDefs.h"
+
+#include "JemTester.h"
+#include "../src/ModifySlices.h"
+
+namespace LVL1BS {
+
+JemTester::JemTester(const std::string& name, ISvcLocator* pSvcLocator)
+                     : AthAlgorithm(name, pSvcLocator),
+		       m_elementKey(0)
+{
+  declareProperty("JetElementLocation",
+           m_jetElementLocation = LVL1::TrigT1CaloDefs::JetElementLocation);
+  declareProperty("JEMHitsLocation",
+           m_jemHitsLocation    = LVL1::TrigT1CaloDefs::JEMHitsLocation);
+  declareProperty("JEMEtSumsLocation",
+           m_jemEtSumsLocation  = LVL1::TrigT1CaloDefs::JEMEtSumsLocation);
+  declareProperty("CMMJetHitsLocation",
+           m_cmmJetLocation     = LVL1::TrigT1CaloDefs::CMMJetHitsLocation);
+  declareProperty("CMMEtSumsLocation",
+           m_cmmEnergyLocation  = LVL1::TrigT1CaloDefs::CMMEtSumsLocation);
+  declareProperty("JEMRoILocation",
+           m_jemRoiLocation     = LVL1::TrigT1CaloDefs::JEMRoILocation);
+  declareProperty("CMMRoILocation",
+           m_cmmRoiLocation     = LVL1::TrigT1CaloDefs::CMMRoILocation);
+  declareProperty("JEMRoILocationRoIB",
+           m_jemRoiLocationRoib =
+	                 LVL1::TrigT1CaloDefs::JEMRoILocation + "RoIB");
+  declareProperty("CMMRoILocationRoIB",
+           m_cmmRoiLocationRoib =
+	                 LVL1::TrigT1CaloDefs::CMMRoILocation + "RoIB");
+  declareProperty("JetElementLocationOverlap",
+           m_jetElementLocationOverlap =
+	                 LVL1::TrigT1CaloDefs::JetElementLocation + "Overlap");
+
+  declareProperty("ForceSlicesJEM",  m_forceSlicesJem  = 0);
+  declareProperty("ForceSlicesCMM",  m_forceSlicesCmm  = 0);
+
+  // By default print everything except RoIB RoIs and jet element overlap
+  declareProperty("JetElementPrint", m_jetElementPrint = 1);
+  declareProperty("JEMHitsPrint",    m_jemHitsPrint    = 1);
+  declareProperty("JEMEtSumsPrint",  m_jemEtSumsPrint  = 1);
+  declareProperty("CMMJetHitsPrint", m_cmmHitsPrint    = 1);
+  declareProperty("CMMEtSumsPrint",  m_cmmEtSumsPrint  = 1);
+  declareProperty("JEMRoIPrint",     m_jemRoiPrint     = 1);
+  declareProperty("CMMRoIPrint",     m_cmmRoiPrint     = 1);
+  declareProperty("JEMRoIPrintRoIB", m_jemRoiPrintRoib = 0);
+  declareProperty("CMMRoIPrintRoIB", m_cmmRoiPrintRoib = 0);
+  declareProperty("JetElementPrintOverlap", m_jetElementPrintOverlap = 0);
+}
+
+JemTester::~JemTester()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode JemTester::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << /* version() */ PACKAGE_VERSION << endreq;
+
+  m_elementKey = new LVL1::JetElementKey();
+
+  return StatusCode::SUCCESS;
+}
+
+// Execute
+
+StatusCode JemTester::execute()
+{
+  if ( !msgLvl(MSG::INFO) ) return StatusCode::SUCCESS;
+  msg(MSG::INFO);
+
+  if (m_jetElementPrint) {
+
+    // Find core jet elements
+
+    const JetElementCollection* jeCollection = 0;
+    StatusCode sc = evtStore()->retrieve(jeCollection, m_jetElementLocation);
+    if (sc.isFailure() || !jeCollection || jeCollection->empty()) {
+      msg() << "No core Jet Elements found" << endreq;
+    } else {
+
+      // Order by eta, phi
+
+      setupJeMap(jeCollection);
+
+      // Print the jet elements
+
+      printJetElements("core");
+    }
+  }
+
+  if (m_jetElementPrintOverlap) {
+
+    // Find overlap jet elements
+
+    const JetElementCollection* jeCollection = 0;
+    StatusCode sc = evtStore()->retrieve(jeCollection,
+                                      m_jetElementLocationOverlap);
+    if (sc.isFailure() || !jeCollection || jeCollection->empty()) {
+      msg() << "No overlap Jet Elements found" << endreq;
+    } else {
+
+      // Order by eta, phi
+
+      setupJeMap(jeCollection);
+
+      // Print the jet elements
+
+      printJetElements("overlap");
+    }
+  }
+
+  if (m_jemHitsPrint) {
+
+    // Find jet hits
+
+    const JetHitsCollection* hitCollection = 0;
+    StatusCode sc = evtStore()->retrieve(hitCollection, m_jemHitsLocation);
+    if (sc.isFailure() || !hitCollection || hitCollection->empty()) {
+      msg() << "No Jet Hits found" << endreq;
+    } else {
+
+      // Order by crate, module
+
+      setupHitsMap(hitCollection);
+
+      // Print the jet hits
+
+      printJetHits();
+    }
+  }
+
+  if (m_jemEtSumsPrint) {
+
+    // Find energy sums
+
+    const EnergySumsCollection* etCollection = 0;
+    StatusCode sc = evtStore()->retrieve(etCollection, m_jemEtSumsLocation);
+    if (sc.isFailure() || !etCollection || etCollection->empty()) {
+      msg() << "No Energy Sums found" << endreq;
+    } else {
+
+      // Order by crate, module
+
+      setupEtMap(etCollection);
+
+      // Print the energy sums
+
+      printEnergySums();
+    }
+  }
+
+  if (m_cmmHitsPrint) {
+
+    // Find CMM hits
+
+    const CmmJetCollection* hitCollection = 0;
+    StatusCode sc = evtStore()->retrieve(hitCollection, m_cmmJetLocation);
+    if (sc.isFailure() || !hitCollection || hitCollection->empty()) {
+      msg() << "No CMM Hits found" << endreq;
+    } else {
+
+      // Order by crate, dataID
+
+      setupCmmHitsMap(hitCollection);
+
+      // Print the CMM hits
+
+      printCmmHits();
+    }
+  }
+
+  if (m_cmmEtSumsPrint) {
+
+    // Find CMM energy sums
+
+    const CmmEnergyCollection* etCollection = 0;
+    StatusCode sc = evtStore()->retrieve(etCollection, m_cmmEnergyLocation);
+    if (sc.isFailure() || !etCollection || etCollection->empty()) {
+      msg() << "No CMM Energy Sums found" << endreq;
+    } else {
+
+      // Order by crate, dataID
+
+      setupCmmEtMap(etCollection);
+
+      // Print the CMM energy sums
+
+      printCmmSums();
+    }
+  }
+
+  if (m_jemRoiPrint) {
+
+    // Find JEM RoIs
+
+    const JemRoiCollection* jrCollection = 0;
+    StatusCode sc = evtStore()->retrieve(jrCollection, m_jemRoiLocation);
+    if (sc.isFailure() || !jrCollection || jrCollection->empty()) {
+      msg() << "No JEM RoIs found" << endreq;
+    } else {
+
+      // Order by RoI word
+
+      setupJemRoiMap(jrCollection);
+
+      // Print the JEM RoIs
+
+      printJemRois("DAQ");
+    }
+  }
+
+  if (m_jemRoiPrintRoib) {
+
+    // Find JEM RoIs from RoIB
+
+    const JemRoiCollection* jrCollection = 0;
+    StatusCode sc = evtStore()->retrieve(jrCollection, m_jemRoiLocationRoib);
+    if (sc.isFailure() || !jrCollection || jrCollection->empty()) {
+      msg() << "No JEM RoIs from RoIB found" << endreq;
+    } else {
+
+      // Order by RoI word
+
+      setupJemRoiMap(jrCollection);
+
+      // Print the JEM RoIs from RoIB
+
+      printJemRois("RoIB");
+    }
+  }
+
+  if (m_cmmRoiPrint) {
+
+    // Find CMM RoIs
+
+    const LVL1::CMMRoI* crCollection = 0;
+    StatusCode sc = evtStore()->retrieve(crCollection, m_cmmRoiLocation);
+    if (sc.isFailure() || !crCollection || (!crCollection->jetEtRoiWord()   &&
+                                            !crCollection->energyRoiWord0() &&
+					    !crCollection->energyRoiWord1() &&
+					    !crCollection->energyRoiWord2())) {
+      msg() << "No CMM RoIs found" << endreq;
+    } else {
+
+      // Print the CMM RoIs
+
+      printCmmRois("DAQ", crCollection);
+    }
+  }
+
+  if (m_cmmRoiPrintRoib) {
+
+    // Find CMM RoIs from RoIB
+
+    const LVL1::CMMRoI* crCollection = 0;
+    StatusCode sc = evtStore()->retrieve(crCollection, m_cmmRoiLocationRoib);
+    if (sc.isFailure() || !crCollection || (!crCollection->jetEtRoiWord()   &&
+                                            !crCollection->energyRoiWord0() &&
+					    !crCollection->energyRoiWord1() &&
+					    !crCollection->energyRoiWord2())) {
+      msg() << "No CMM RoIs from RoIB found" << endreq;
+    } else {
+
+      // Print the CMM RoIs from RoIB
+
+      printCmmRois("RoIB", crCollection);
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode JemTester::finalize()
+{
+  delete m_elementKey;
+
+  return StatusCode::SUCCESS;
+}
+
+// Print the jet elements
+
+void JemTester::printJetElements(const std::string& source) const
+{
+  msg() << "Number of " << source << " Jet Elements = "
+        << m_jeMap.size() << endreq;
+  JetElementMap::const_iterator mapIter = m_jeMap.begin();
+  JetElementMap::const_iterator mapEnd  = m_jeMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::JetElement* const je = mapIter->second;
+    int peak   = je->peak();
+    int slices = (je->emEnergyVec()).size();
+    if (m_forceSlicesJem) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesJem);
+      slices = m_forceSlicesJem;
+    }
+    msg() << "key/eta/phi/peak/em/had/emErr/hadErr/linkErr: "
+          << mapIter->first << "/" << je->eta() << "/" << je->phi() << "/"
+	  << peak << "/";
+
+    std::vector<int> emEnergy;
+    std::vector<int> hadEnergy;
+    std::vector<int> emError;
+    std::vector<int> hadError;
+    std::vector<int> linkError;
+    ModifySlices::data(je->emEnergyVec(),  emEnergy,  slices);
+    ModifySlices::data(je->hadEnergyVec(), hadEnergy, slices);
+    ModifySlices::data(je->emErrorVec(),   emError,   slices);
+    ModifySlices::data(je->hadErrorVec(),  hadError,  slices);
+    ModifySlices::data(je->linkErrorVec(), linkError, slices);
+    printVec(emEnergy);
+    printVec(hadEnergy);
+    msg() << MSG::hex;
+    printVec(emError);
+    printVec(hadError);
+    printVec(linkError);
+    msg() << MSG::dec << endreq;
+  }
+}
+
+// Print the jet hits
+
+void JemTester::printJetHits() const
+{
+  msg() << "Number of Jet Hits = " << m_hitsMap.size() << endreq;
+  JetHitsMap::const_iterator mapIter = m_hitsMap.begin();
+  JetHitsMap::const_iterator mapEnd  = m_hitsMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::JEMHits* const jh = mapIter->second;
+    int peak   = jh->peak();
+    int slices = (jh->JetHitsVec()).size();
+    if (m_forceSlicesJem) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesJem);
+      slices = m_forceSlicesJem;
+    }
+    msg() << "crate/module/peak/hits: "
+	  << jh->crate() << "/" << jh->module() << "/" << peak << "/";
+    int words = 8;
+    int bits  = 3;
+    unsigned int mask = 0x7;
+    if (jh->forward()) {
+      words = 12;
+      bits  = 2;
+      mask  = 0x3;
+    }
+    std::vector<unsigned int> jetHits;
+    ModifySlices::data(jh->JetHitsVec(), jetHits, slices);
+    std::vector<unsigned int>::const_iterator pos;
+    std::vector<unsigned int>::const_iterator posb = jetHits.begin();
+    std::vector<unsigned int>::const_iterator pose = jetHits.end();
+    for (pos = posb; pos != pose; ++pos) {
+      if (pos != posb) msg() << ",";
+      const unsigned int hits = *pos;
+      for (int i = 0; i < words; ++i) {
+        if (i != 0) msg() << ":";
+	const unsigned int thr = (hits >> bits*i) & mask;
+        msg() << thr;
+      }
+    }
+    msg() << "/" << endreq;
+  }
+}
+
+// Print energy sums
+
+void JemTester::printEnergySums() const
+{
+  msg() << "Number of Energy Sums = " << m_etMap.size() << endreq;
+  EnergySumsMap::const_iterator mapIter = m_etMap.begin();
+  EnergySumsMap::const_iterator mapEnd  = m_etMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::JEMEtSums* const et = mapIter->second;
+    int peak   = et->peak();
+    int slices = (et->ExVec()).size();
+    if (m_forceSlicesJem) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesJem);
+      slices = m_forceSlicesJem;
+    }
+    msg() << "crate/module/peak/Ex/Ey/Et: "
+	  << et->crate() << "/" << et->module() << "/" << peak << "/";
+    std::vector<unsigned int> exVec;
+    std::vector<unsigned int> eyVec;
+    std::vector<unsigned int> etVec;
+    ModifySlices::data(et->ExVec(), exVec, slices);
+    ModifySlices::data(et->EyVec(), eyVec, slices);
+    ModifySlices::data(et->EtVec(), etVec, slices);
+    printVecU(exVec);
+    printVecU(eyVec);
+    printVecU(etVec);
+    msg() << endreq;
+  }
+}
+
+// Print the CMM hits
+
+void JemTester::printCmmHits() const
+{
+  msg() << "Number of CMM Hits = " << m_cmmHitsMap.size() << endreq;
+  CmmHitsMap::const_iterator mapIter = m_cmmHitsMap.begin();
+  CmmHitsMap::const_iterator mapEnd  = m_cmmHitsMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::CMMJetHits* const jh = mapIter->second;
+    const int dataID = jh->dataID();
+    int peak   = jh->peak();
+    int slices = (jh->HitsVec()).size();
+    if (m_forceSlicesCmm) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesCmm);
+      slices = m_forceSlicesCmm;
+    }
+    msg() << "crate/dataID/peak/hits/error: "
+	  << jh->crate() << "/" << dataID << "/" << peak << "/";
+    int words = 8;
+    int bits  = 3;
+    unsigned int mask = 0x7;
+    if (dataID == 0 || dataID == 7 || dataID == 8 || dataID == 15) {
+      words = 12;
+      bits  = 2;
+      mask  = 0x3;
+    } else if (dataID == LVL1::CMMJetHits::REMOTE_FORWARD ||
+               dataID == LVL1::CMMJetHits::LOCAL_FORWARD  ||
+	       dataID == LVL1::CMMJetHits::TOTAL_FORWARD) {
+      bits  = 2;
+      mask  = 0x3;
+    } else if (dataID == LVL1::CMMJetHits::ET_MAP) {
+      words = 1;
+      bits  = 4;
+      mask  = 0xf;
+    }
+    std::vector<unsigned int> hitsVec;
+    ModifySlices::data(jh->HitsVec(), hitsVec, slices);
+    std::vector<unsigned int>::const_iterator pos;
+    std::vector<unsigned int>::const_iterator posb = hitsVec.begin();
+    std::vector<unsigned int>::const_iterator pose = hitsVec.end();
+    for (pos = posb; pos != pose; ++pos) {
+      if (pos != posb) msg() << ",";
+      const unsigned int hits = *pos;
+      for (int i = 0; i < words; ++i) {
+        if (i != 0) msg() << ":";
+	const unsigned int thr = (hits >> bits*i) & mask;
+        msg() << thr;
+      }
+    }
+    msg() << "/" << MSG::hex;
+    std::vector<int> errorVec;
+    ModifySlices::data(jh->ErrorVec(), errorVec, slices);
+    printVec(errorVec);
+    msg() << MSG::dec << endreq;
+  }
+}
+
+// Print CMM energy sums
+
+void JemTester::printCmmSums() const
+{
+  msg() << "Number of CMM Energy Sums = " << m_cmmEtMap.size() << endreq;
+  CmmSumsMap::const_iterator mapIter = m_cmmEtMap.begin();
+  CmmSumsMap::const_iterator mapEnd  = m_cmmEtMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::CMMEtSums* const et = mapIter->second;
+    int peak   = et->peak();
+    int slices = (et->ExVec()).size();
+    if (m_forceSlicesCmm) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesCmm);
+      slices = m_forceSlicesCmm;
+    }
+    msg() << "crate/dataID/peak/Ex/Ey/Et/ExErr/EyErr/EtErr: "
+	  << et->crate() << "/" << et->dataID() << "/" << peak << "/";
+    std::vector<unsigned int> exVec;
+    std::vector<unsigned int> eyVec;
+    std::vector<unsigned int> etVec;
+    std::vector<int> exError;
+    std::vector<int> eyError;
+    std::vector<int> etError;
+    ModifySlices::data(et->ExVec(), exVec, slices);
+    ModifySlices::data(et->EyVec(), eyVec, slices);
+    ModifySlices::data(et->EtVec(), etVec, slices);
+    ModifySlices::data(et->ExErrorVec(), exError, slices);
+    ModifySlices::data(et->EyErrorVec(), eyError, slices);
+    ModifySlices::data(et->EtErrorVec(), etError, slices);
+    printVecU(exVec);
+    printVecU(eyVec);
+    printVecU(etVec);
+    msg() << MSG::hex;
+    printVec(exError);
+    printVec(eyError);
+    printVec(etError);
+    msg() << MSG::dec << endreq;
+  }
+}
+
+// Print the JEM RoIs
+
+void JemTester::printJemRois(const std::string& source) const
+{
+  msg() << "Number of JEM RoIs (" << source << ") = " << m_roiMap.size()
+        << endreq;
+  JemRoiMap::const_iterator mapIter = m_roiMap.begin();
+  JemRoiMap::const_iterator mapEnd  = m_roiMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::JEMRoI* const roi = mapIter->second;
+    msg() << "crate/jem/frame/loc/fwd/hits/error: "
+          << roi->crate() << "/" << roi->jem() << "/" << roi->frame() << "/"
+	  << roi->location() << "/" << roi->forward() << "/";
+    const int hits = roi->hits();
+    const int numHits = (roi->forward()) ? 4 : 8;
+    for (int i = 0; i < numHits; ++i) {
+      if (i > 0) msg() << ":";
+      msg() << ((hits >> i) & 0x1);
+    }
+    msg() << "/" << roi->error() << "/" << endreq;
+  }
+}
+
+// Print the CMM RoIs
+
+void JemTester::printCmmRois(const std::string& source,
+                             const LVL1::CMMRoI* const roi) const
+{
+  msg() << "CMMRoI(" << source
+        << "):jetEtHits/sumEtHits/missingEtHits/missingEtSigHits/Ex/Ey/Et;error: "
+        << MSG::hex << roi->jetEtHits() << ";" << roi->jetEtError() << "/"
+        << roi->sumEtHits() << ";" << roi->sumEtError() << "/"
+        << roi->missingEtHits() << ";" << roi->missingEtError() << "/"
+        << roi->missingEtSigHits() << ";" << roi->missingEtSigError() << "/"
+        << MSG::dec << roi->ex() << ";" << roi->exError() << "/"
+        << roi->ey() << ";" << roi->eyError() << "/"
+        << roi->et() << ";" << roi->etError() << "/" << endreq;
+}
+
+// Print a vector
+
+void JemTester::printVec(const std::vector<int>& vec) const
+{
+  std::vector<int>::const_iterator pos;
+  for (pos = vec.begin(); pos != vec.end(); ++pos) {
+    if (pos != vec.begin()) msg() << ",";
+    msg() << *pos;
+  }
+  msg() << "/";
+}
+
+// Print a vector (unsigned)
+
+void JemTester::printVecU(const std::vector<unsigned int>& vec) const
+{
+  std::vector<unsigned int>::const_iterator pos;
+  for (pos = vec.begin(); pos != vec.end(); ++pos) {
+    if (pos != vec.begin()) msg() << ",";
+    msg() << *pos;
+  }
+  msg() << "/";
+}
+
+// Set up jet element map
+
+void JemTester::setupJeMap(const JetElementCollection* const jeCollection)
+{
+  m_jeMap.clear();
+  if (jeCollection) {
+    JetElementCollection::const_iterator pos = jeCollection->begin();
+    JetElementCollection::const_iterator pose = jeCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::JetElement* const je = *pos;
+      const unsigned int key = m_elementKey->jeKey(je->phi(), je->eta());
+      m_jeMap.insert(std::make_pair(key, je));
+    }
+  }
+}
+
+// Set up jet hits map
+
+void JemTester::setupHitsMap(const JetHitsCollection* const hitCollection)
+{
+  m_hitsMap.clear();
+  if (hitCollection) {
+    JetHitsCollection::const_iterator pos  = hitCollection->begin();
+    JetHitsCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::JEMHits* const hits = *pos;
+      const int key = hits->crate()*100 + hits->module();
+      m_hitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Set up energy sums map
+
+void JemTester::setupEtMap(const EnergySumsCollection* const etCollection)
+{
+  m_etMap.clear();
+  if (etCollection) {
+    EnergySumsCollection::const_iterator pos  = etCollection->begin();
+    EnergySumsCollection::const_iterator pose = etCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::JEMEtSums* const sums = *pos;
+      // Sim doesn't zero suppress properly, do it here
+      if (std::accumulate((sums->ExVec()).begin(), (sums->ExVec()).end(), 0) ||
+          std::accumulate((sums->EyVec()).begin(), (sums->EyVec()).end(), 0) ||
+          std::accumulate((sums->EtVec()).begin(), (sums->EtVec()).end(), 0)) {
+        const int key = sums->crate()*100 + sums->module();
+        m_etMap.insert(std::make_pair(key, sums));
+      }
+    }
+  }
+}
+
+// Set up CMM hits map
+
+void JemTester::setupCmmHitsMap(const CmmJetCollection* const hitCollection)
+{
+  m_cmmHitsMap.clear();
+  if (hitCollection) {
+    CmmJetCollection::const_iterator pos  = hitCollection->begin();
+    CmmJetCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CMMJetHits* const hits = *pos;
+      const int key = hits->crate()*100 + hits->dataID();
+      m_cmmHitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Set up CMM energy sums map
+
+void JemTester::setupCmmEtMap(const CmmEnergyCollection* const etCollection)
+{
+  m_cmmEtMap.clear();
+  if (etCollection) {
+    CmmEnergyCollection::const_iterator pos  = etCollection->begin();
+    CmmEnergyCollection::const_iterator pose = etCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CMMEtSums* const sums = *pos;
+      if (std::accumulate((sums->ExVec()).begin(), (sums->ExVec()).end(), 0) ||
+          std::accumulate((sums->EyVec()).begin(), (sums->EyVec()).end(), 0) ||
+          std::accumulate((sums->EtVec()).begin(), (sums->EtVec()).end(), 0) ||
+          std::accumulate((sums->ExErrorVec()).begin(),
+	                                      (sums->ExErrorVec()).end(), 0) ||
+          std::accumulate((sums->EyErrorVec()).begin(),
+	                                      (sums->EyErrorVec()).end(), 0) ||
+          std::accumulate((sums->EtErrorVec()).begin(),
+	                                      (sums->EtErrorVec()).end(), 0)) {
+        const int key = sums->crate()*100 + sums->dataID();
+        m_cmmEtMap.insert(std::make_pair(key, sums));
+      }
+    }
+  }
+}
+
+// Set up JEM RoI map
+
+void JemTester::setupJemRoiMap(const JemRoiCollection* const jrCollection)
+{
+  m_roiMap.clear();
+  if (jrCollection) {
+    JemRoiCollection::const_iterator pos  = jrCollection->begin();
+    JemRoiCollection::const_iterator pose = jrCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::JEMRoI* const roi = *pos;
+      const uint32_t key = roi->roiWord();
+      m_roiMap.insert(std::make_pair(key, roi));
+    }
+  }
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/JemTester.h b/Trigger/TrigT1/TrigT1CaloByteStream/test/JemTester.h
new file mode 100755
index 0000000000000000000000000000000000000000..5fe76c76733057cfa1678cc9b287bc23d6bf2171
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/JemTester.h
@@ -0,0 +1,164 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEMTESTER_H
+#define TRIGT1CALOBYTESTREAM_JEMTESTER_H
+
+#include <map>
+#include <string>
+#include <stdint.h>
+
+#include "AthenaBaseComps/AthAlgorithm.h"
+#include "DataModel/DataVector.h"
+
+class ISvcLocator;
+class StatusCode;
+
+namespace LVL1 {
+  class CMMEtSums;
+  class CMMJetHits;
+  class CMMRoI;
+  class JEMEtSums;
+  class JEMHits;
+  class JEMRoI;
+  class JetElement;
+  class JetElementKey;
+}
+
+namespace LVL1BS {
+
+/** Algorithm to test JEM component bytestream conversions.
+ *
+ *  Just prints out the contents of the JetElement objects,
+ *  JEMHits objects and JEMEtSums objects.
+ *  Now also includes CMMJetHits, CMMEtSums, JEMRoI and CMMRoI.
+ *
+ *  Run before writing bytestream and after reading it and compare.
+ *
+ *  @author Peter Faulkner
+ */
+
+class JemTester : public AthAlgorithm {
+
+ public:
+   JemTester(const std::string& name, ISvcLocator* pSvcLocator);
+   virtual ~JemTester();
+
+   virtual StatusCode initialize();
+   virtual StatusCode execute();
+   virtual StatusCode finalize();
+
+ private:
+   typedef DataVector<LVL1::JetElement>              JetElementCollection;
+   typedef DataVector<LVL1::JEMHits>                 JetHitsCollection;
+   typedef DataVector<LVL1::JEMEtSums>               EnergySumsCollection;
+   typedef DataVector<LVL1::CMMJetHits>              CmmJetCollection;
+   typedef DataVector<LVL1::CMMEtSums>               CmmEnergyCollection;
+   typedef DataVector<LVL1::JEMRoI>                  JemRoiCollection;
+   typedef std::map<unsigned int, const LVL1::JetElement*> JetElementMap;
+   typedef std::map<int, const LVL1::JEMHits*>       JetHitsMap;
+   typedef std::map<int, const LVL1::JEMEtSums*>     EnergySumsMap;
+   typedef std::map<int, const LVL1::CMMJetHits*>    CmmHitsMap;
+   typedef std::map<int, const LVL1::CMMEtSums*>     CmmSumsMap;
+   typedef std::map<uint32_t, const LVL1::JEMRoI*>   JemRoiMap;
+
+   /// Print the jet elements
+   void printJetElements(const std::string& source) const;
+   /// Print the jet hits
+   void printJetHits()    const;
+   /// Print the energy sums
+   void printEnergySums() const;
+   /// Print the CMM hits
+   void printCmmHits()    const;
+   /// Print the CMM energy sums
+   void printCmmSums()    const;
+   /// Print the JEM RoIs
+   void printJemRois(const std::string& source) const;
+   /// Print the CMM RoIs
+   void printCmmRois(const std::string& source,
+                     const LVL1::CMMRoI* roi)   const;
+
+   /// Print a vector
+   void printVec(const std::vector<int>& vec) const;
+   /// Print a vector (unsigned)
+   void printVecU(const std::vector<unsigned int>& vec) const;
+
+   /// Set up jet element map
+   void setupJeMap(const JetElementCollection* jeCollection);
+   /// Set up jet hits map
+   void setupHitsMap(const JetHitsCollection* hitCollection);
+   /// Set up energy sums map
+   void setupEtMap(const EnergySumsCollection* etCollection);
+   /// Set up CMM hits map
+   void setupCmmHitsMap(const CmmJetCollection* hitCollection);
+   /// Set up CMM energy sums map
+   void setupCmmEtMap(const CmmEnergyCollection* etCollection);
+   /// Set up JEM RoI map
+   void setupJemRoiMap(const JemRoiCollection* jrCollection);
+
+   /// Jet element key provider
+   LVL1::JetElementKey* m_elementKey;
+   /// Jet element container StoreGate key
+   std::string m_jetElementLocation;
+   /// Jet hits container StoreGate key
+   std::string m_jemHitsLocation;
+   /// Energy sums container StoreGate key
+   std::string m_jemEtSumsLocation;
+   /// CMM hits container StoreGate key
+   std::string m_cmmJetLocation;
+   /// CMM energy sums container StoreGate key
+   std::string m_cmmEnergyLocation;
+   /// JEM RoI container StoreGate key
+   std::string m_jemRoiLocation;
+   /// CMM RoI container StoreGate key
+   std::string m_cmmRoiLocation;
+   /// JEM RoI from RoIB container StoreGate key
+   std::string m_jemRoiLocationRoib;
+   /// CMM RoI from RoIB container StoreGate key
+   std::string m_cmmRoiLocationRoib;
+   /// Jet element overlap container StoreGate key
+   std::string m_jetElementLocationOverlap;
+   /// Force number of JEM slices
+   int m_forceSlicesJem;
+   /// Force number of CMM slices
+   int m_forceSlicesCmm;
+   /// Jet element print flag
+   int m_jetElementPrint;
+   /// Jet hits print flag
+   int m_jemHitsPrint;
+   /// Energy sums print flag
+   int m_jemEtSumsPrint;
+   /// CMM hits print flag
+   int m_cmmHitsPrint;
+   /// CMM energy sums print flag
+   int m_cmmEtSumsPrint;
+   /// JEM RoI print flag
+   int m_jemRoiPrint;
+   /// CMM RoI print flag
+   int m_cmmRoiPrint;
+   /// JEM RoI from RoIB print flag
+   int m_jemRoiPrintRoib;
+   /// CMM RoI from RoIB print flag
+   int m_cmmRoiPrintRoib;
+   /// Jet element overlap print flag
+   int m_jetElementPrintOverlap;
+
+   /// Jet element map
+   JetElementMap m_jeMap;
+   /// Jet hits map
+   JetHitsMap    m_hitsMap;
+   /// Energy sums map
+   EnergySumsMap m_etMap;
+   /// CMM hits map
+   CmmHitsMap    m_cmmHitsMap;
+   /// CMM energy sums map
+   CmmSumsMap    m_cmmEtMap;
+   /// JEM RoI map
+   JemRoiMap     m_roiMap;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/JemTesterV1.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/test/JemTesterV1.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..62a58e175f35b12004659b920ef2fbf070686aea
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/JemTesterV1.cxx
@@ -0,0 +1,694 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <numeric>
+#include <utility>
+
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "StoreGate/StoreGateSvc.h"
+
+#include "TrigT1CaloEvent/CMMEtSums.h"
+#include "TrigT1CaloEvent/CMMJetHits.h"
+#include "TrigT1CaloEvent/CMMRoI.h"
+#include "TrigT1CaloEvent/JEMEtSums.h"
+#include "TrigT1CaloEvent/JEMHits.h"
+#include "TrigT1CaloEvent/JEMRoI.h"
+#include "TrigT1CaloEvent/JetElement.h"
+#include "TrigT1CaloUtils/JetElementKey.h"
+#include "TrigT1Interfaces/TrigT1CaloDefs.h"
+
+#include "JemTesterV1.h"
+#include "../src/ModifySlices.h"
+
+namespace LVL1BS {
+
+JemTesterV1::JemTesterV1(const std::string& name, ISvcLocator* pSvcLocator)
+                       : AthAlgorithm(name, pSvcLocator),
+		         m_elementKey(0)
+{
+  declareProperty("JetElementLocation",
+           m_jetElementLocation = LVL1::TrigT1CaloDefs::JetElementLocation);
+  declareProperty("JEMHitsLocation",
+           m_jemHitsLocation    = LVL1::TrigT1CaloDefs::JEMHitsLocation);
+  declareProperty("JEMEtSumsLocation",
+           m_jemEtSumsLocation  = LVL1::TrigT1CaloDefs::JEMEtSumsLocation);
+  declareProperty("CMMJetHitsLocation",
+           m_cmmJetLocation     = LVL1::TrigT1CaloDefs::CMMJetHitsLocation);
+  declareProperty("CMMEtSumsLocation",
+           m_cmmEnergyLocation  = LVL1::TrigT1CaloDefs::CMMEtSumsLocation);
+  declareProperty("JEMRoILocation",
+           m_jemRoiLocation     = LVL1::TrigT1CaloDefs::JEMRoILocation);
+  declareProperty("CMMRoILocation",
+           m_cmmRoiLocation     = LVL1::TrigT1CaloDefs::CMMRoILocation);
+  declareProperty("JEMRoILocationRoIB",
+           m_jemRoiLocationRoib =
+	                 LVL1::TrigT1CaloDefs::JEMRoILocation + "RoIB");
+  declareProperty("CMMRoILocationRoIB",
+           m_cmmRoiLocationRoib =
+	                 LVL1::TrigT1CaloDefs::CMMRoILocation + "RoIB");
+  declareProperty("JetElementLocationOverlap",
+           m_jetElementLocationOverlap =
+	                 LVL1::TrigT1CaloDefs::JetElementLocation + "Overlap");
+
+  declareProperty("ForceSlicesJEM",  m_forceSlicesJem  = 0);
+  declareProperty("ForceSlicesCMM",  m_forceSlicesCmm  = 0);
+
+  // By default print everything except RoIB RoIs and jet element overlap
+  declareProperty("JetElementPrint", m_jetElementPrint = 1);
+  declareProperty("JEMHitsPrint",    m_jemHitsPrint    = 1);
+  declareProperty("JEMEtSumsPrint",  m_jemEtSumsPrint  = 1);
+  declareProperty("CMMJetHitsPrint", m_cmmHitsPrint    = 1);
+  declareProperty("CMMEtSumsPrint",  m_cmmEtSumsPrint  = 1);
+  declareProperty("JEMRoIPrint",     m_jemRoiPrint     = 1);
+  declareProperty("CMMRoIPrint",     m_cmmRoiPrint     = 1);
+  declareProperty("JEMRoIPrintRoIB", m_jemRoiPrintRoib = 0);
+  declareProperty("CMMRoIPrintRoIB", m_cmmRoiPrintRoib = 0);
+  declareProperty("JetElementPrintOverlap", m_jetElementPrintOverlap = 0);
+}
+
+JemTesterV1::~JemTesterV1()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode JemTesterV1::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << /* version() */ PACKAGE_VERSION << endreq;
+
+  m_elementKey = new LVL1::JetElementKey();
+
+  return StatusCode::SUCCESS;
+}
+
+// Execute
+
+StatusCode JemTesterV1::execute()
+{
+  if ( !msgLvl(MSG::INFO) ) return StatusCode::SUCCESS;
+  msg(MSG::INFO);
+
+  if (m_jetElementPrint) {
+
+    // Find core jet elements
+
+    const JetElementCollection* jeCollection = 0;
+    StatusCode sc = evtStore()->retrieve(jeCollection, m_jetElementLocation);
+    if (sc.isFailure() || !jeCollection || jeCollection->empty()) {
+      msg() << "No core Jet Elements found" << endreq;
+    } else {
+
+      // Order by eta, phi
+
+      setupJeMap(jeCollection);
+
+      // Print the jet elements
+
+      printJetElements("core");
+    }
+  }
+
+  if (m_jetElementPrintOverlap) {
+
+    // Find overlap jet elements
+
+    const JetElementCollection* jeCollection = 0;
+    StatusCode sc = evtStore()->retrieve(jeCollection,
+                                      m_jetElementLocationOverlap);
+    if (sc.isFailure() || !jeCollection || jeCollection->empty()) {
+      msg() << "No overlap Jet Elements found" << endreq;
+    } else {
+
+      // Order by eta, phi
+
+      setupJeMap(jeCollection);
+
+      // Print the jet elements
+
+      printJetElements("overlap");
+    }
+  }
+
+  if (m_jemHitsPrint) {
+
+    // Find jet hits
+
+    const JetHitsCollection* hitCollection = 0;
+    StatusCode sc = evtStore()->retrieve(hitCollection, m_jemHitsLocation);
+    if (sc.isFailure() || !hitCollection || hitCollection->empty()) {
+      msg() << "No Jet Hits found" << endreq;
+    } else {
+
+      // Order by crate, module
+
+      setupHitsMap(hitCollection);
+
+      // Print the jet hits
+
+      printJetHits();
+    }
+  }
+
+  if (m_jemEtSumsPrint) {
+
+    // Find energy sums
+
+    const EnergySumsCollection* etCollection = 0;
+    StatusCode sc = evtStore()->retrieve(etCollection, m_jemEtSumsLocation);
+    if (sc.isFailure() || !etCollection || etCollection->empty()) {
+      msg() << "No Energy Sums found" << endreq;
+    } else {
+
+      // Order by crate, module
+
+      setupEtMap(etCollection);
+
+      // Print the energy sums
+
+      printEnergySums();
+    }
+  }
+
+  if (m_cmmHitsPrint) {
+
+    // Find CMM hits
+
+    const CmmJetCollection* hitCollection = 0;
+    StatusCode sc = evtStore()->retrieve(hitCollection, m_cmmJetLocation);
+    if (sc.isFailure() || !hitCollection || hitCollection->empty()) {
+      msg() << "No CMM Hits found" << endreq;
+    } else {
+
+      // Order by crate, dataID
+
+      setupCmmHitsMap(hitCollection);
+
+      // Print the CMM hits
+
+      printCmmHits();
+    }
+  }
+
+  if (m_cmmEtSumsPrint) {
+
+    // Find CMM energy sums
+
+    const CmmEnergyCollection* etCollection = 0;
+    StatusCode sc = evtStore()->retrieve(etCollection, m_cmmEnergyLocation);
+    if (sc.isFailure() || !etCollection || etCollection->empty()) {
+      msg() << "No CMM Energy Sums found" << endreq;
+    } else {
+
+      // Order by crate, dataID
+
+      setupCmmEtMap(etCollection);
+
+      // Print the CMM energy sums
+
+      printCmmSums();
+    }
+  }
+
+  if (m_jemRoiPrint) {
+
+    // Find JEM RoIs
+
+    const JemRoiCollection* jrCollection = 0;
+    StatusCode sc = evtStore()->retrieve(jrCollection, m_jemRoiLocation);
+    if (sc.isFailure() || !jrCollection || jrCollection->empty()) {
+      msg() << "No JEM RoIs found" << endreq;
+    } else {
+
+      // Order by RoI word
+
+      setupJemRoiMap(jrCollection);
+
+      // Print the JEM RoIs
+
+      printJemRois("DAQ");
+    }
+  }
+
+  if (m_jemRoiPrintRoib) {
+
+    // Find JEM RoIs from RoIB
+
+    const JemRoiCollection* jrCollection = 0;
+    StatusCode sc = evtStore()->retrieve(jrCollection, m_jemRoiLocationRoib);
+    if (sc.isFailure() || !jrCollection || jrCollection->empty()) {
+      msg() << "No JEM RoIs from RoIB found" << endreq;
+    } else {
+
+      // Order by RoI word
+
+      setupJemRoiMap(jrCollection);
+
+      // Print the JEM RoIs from RoIB
+
+      printJemRois("RoIB");
+    }
+  }
+
+  if (m_cmmRoiPrint) {
+
+    // Find CMM RoIs
+
+    const LVL1::CMMRoI* crCollection = 0;
+    StatusCode sc = evtStore()->retrieve(crCollection, m_cmmRoiLocation);
+    if (sc.isFailure() || !crCollection || (!crCollection->jetEtRoiWord()   &&
+                                            !crCollection->energyRoiWord0() &&
+					    !crCollection->energyRoiWord1() &&
+					    !crCollection->energyRoiWord2())) {
+      msg() << "No CMM RoIs found" << endreq;
+    } else {
+
+      // Print the CMM RoIs
+
+      printCmmRois("DAQ", crCollection);
+    }
+  }
+
+  if (m_cmmRoiPrintRoib) {
+
+    // Find CMM RoIs from RoIB
+
+    const LVL1::CMMRoI* crCollection = 0;
+    StatusCode sc = evtStore()->retrieve(crCollection, m_cmmRoiLocationRoib);
+    if (sc.isFailure() || !crCollection || (!crCollection->jetEtRoiWord()   &&
+                                            !crCollection->energyRoiWord0() &&
+					    !crCollection->energyRoiWord1() &&
+					    !crCollection->energyRoiWord2())) {
+      msg() << "No CMM RoIs from RoIB found" << endreq;
+    } else {
+
+      // Print the CMM RoIs from RoIB
+
+      printCmmRois("RoIB", crCollection);
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode JemTesterV1::finalize()
+{
+  delete m_elementKey;
+
+  return StatusCode::SUCCESS;
+}
+
+// Print the jet elements
+
+void JemTesterV1::printJetElements(const std::string& source) const
+{
+  msg() << "Number of " << source << " Jet Elements = "
+        << m_jeMap.size() << endreq;
+  JetElementMap::const_iterator mapIter = m_jeMap.begin();
+  JetElementMap::const_iterator mapEnd  = m_jeMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::JetElement* const je = mapIter->second;
+    int peak   = je->peak();
+    int slices = (je->emEnergyVec()).size();
+    if (m_forceSlicesJem) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesJem);
+      slices = m_forceSlicesJem;
+    }
+    msg() << "key/eta/phi/peak/em/had/emErr/hadErr/linkErr: "
+          << mapIter->first << "/" << je->eta() << "/" << je->phi() << "/"
+	  << peak << "/";
+
+    std::vector<int> emEnergy;
+    std::vector<int> hadEnergy;
+    std::vector<int> emError;
+    std::vector<int> hadError;
+    std::vector<int> linkError;
+    ModifySlices::data(je->emEnergyVec(),  emEnergy,  slices);
+    ModifySlices::data(je->hadEnergyVec(), hadEnergy, slices);
+    ModifySlices::data(je->emErrorVec(),   emError,   slices);
+    ModifySlices::data(je->hadErrorVec(),  hadError,  slices);
+    ModifySlices::data(je->linkErrorVec(), linkError, slices);
+    printVec(emEnergy);
+    printVec(hadEnergy);
+    msg() << MSG::hex;
+    printVec(emError);
+    printVec(hadError);
+    printVec(linkError);
+    msg() << MSG::dec << endreq;
+  }
+}
+
+// Print the jet hits
+
+void JemTesterV1::printJetHits() const
+{
+  msg() << "Number of Jet Hits = " << m_hitsMap.size() << endreq;
+  JetHitsMap::const_iterator mapIter = m_hitsMap.begin();
+  JetHitsMap::const_iterator mapEnd  = m_hitsMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::JEMHits* const jh = mapIter->second;
+    int peak   = jh->peak();
+    int slices = (jh->JetHitsVec()).size();
+    if (m_forceSlicesJem) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesJem);
+      slices = m_forceSlicesJem;
+    }
+    msg() << "crate/module/peak/hits: "
+	  << jh->crate() << "/" << jh->module() << "/" << peak << "/";
+    int words = 8;
+    int bits  = 3;
+    unsigned int mask = 0x7;
+    if (jh->forward()) {
+      words = 12;
+      bits  = 2;
+      mask  = 0x3;
+    }
+    std::vector<unsigned int> jetHits;
+    ModifySlices::data(jh->JetHitsVec(), jetHits, slices);
+    std::vector<unsigned int>::const_iterator pos;
+    std::vector<unsigned int>::const_iterator posb = jetHits.begin();
+    std::vector<unsigned int>::const_iterator pose = jetHits.end();
+    for (pos = posb; pos != pose; ++pos) {
+      if (pos != posb) msg() << ",";
+      const unsigned int hits = *pos;
+      for (int i = 0; i < words; ++i) {
+        if (i != 0) msg() << ":";
+	const unsigned int thr = (hits >> bits*i) & mask;
+        msg() << thr;
+      }
+    }
+    msg() << "/" << endreq;
+  }
+}
+
+// Print energy sums
+
+void JemTesterV1::printEnergySums() const
+{
+  msg() << "Number of Energy Sums = " << m_etMap.size() << endreq;
+  EnergySumsMap::const_iterator mapIter = m_etMap.begin();
+  EnergySumsMap::const_iterator mapEnd  = m_etMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::JEMEtSums* const et = mapIter->second;
+    int peak   = et->peak();
+    int slices = (et->ExVec()).size();
+    if (m_forceSlicesJem) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesJem);
+      slices = m_forceSlicesJem;
+    }
+    msg() << "crate/module/peak/Ex/Ey/Et: "
+	  << et->crate() << "/" << et->module() << "/" << peak << "/";
+    std::vector<unsigned int> exVec;
+    std::vector<unsigned int> eyVec;
+    std::vector<unsigned int> etVec;
+    ModifySlices::data(et->ExVec(), exVec, slices);
+    ModifySlices::data(et->EyVec(), eyVec, slices);
+    ModifySlices::data(et->EtVec(), etVec, slices);
+    printVecU(exVec);
+    printVecU(eyVec);
+    printVecU(etVec);
+    msg() << endreq;
+  }
+}
+
+// Print the CMM hits
+
+void JemTesterV1::printCmmHits() const
+{
+  msg() << "Number of CMM Hits = " << m_cmmHitsMap.size() << endreq;
+  CmmHitsMap::const_iterator mapIter = m_cmmHitsMap.begin();
+  CmmHitsMap::const_iterator mapEnd  = m_cmmHitsMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::CMMJetHits* const jh = mapIter->second;
+    const int dataID = jh->dataID();
+    int peak   = jh->peak();
+    int slices = (jh->HitsVec()).size();
+    if (m_forceSlicesCmm) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesCmm);
+      slices = m_forceSlicesCmm;
+    }
+    msg() << "crate/dataID/peak/hits/error: "
+	  << jh->crate() << "/" << dataID << "/" << peak << "/";
+    int words = 8;
+    int bits  = 3;
+    unsigned int mask = 0x7;
+    if (dataID == 0 || dataID == 7 || dataID == 8 || dataID == 15) {
+      words = 12;
+      bits  = 2;
+      mask  = 0x3;
+    } else if (dataID == LVL1::CMMJetHits::REMOTE_FORWARD ||
+               dataID == LVL1::CMMJetHits::LOCAL_FORWARD  ||
+	       dataID == LVL1::CMMJetHits::TOTAL_FORWARD) {
+      bits  = 2;
+      mask  = 0x3;
+    } else if (dataID == LVL1::CMMJetHits::ET_MAP) {
+      words = 1;
+      bits  = 4;
+      mask  = 0xf;
+    }
+    std::vector<unsigned int> hitsVec;
+    ModifySlices::data(jh->HitsVec(), hitsVec, slices);
+    std::vector<unsigned int>::const_iterator pos;
+    std::vector<unsigned int>::const_iterator posb = hitsVec.begin();
+    std::vector<unsigned int>::const_iterator pose = hitsVec.end();
+    for (pos = posb; pos != pose; ++pos) {
+      if (pos != posb) msg() << ",";
+      const unsigned int hits = *pos;
+      for (int i = 0; i < words; ++i) {
+        if (i != 0) msg() << ":";
+	const unsigned int thr = (hits >> bits*i) & mask;
+        msg() << thr;
+      }
+    }
+    msg() << "/" << MSG::hex;
+    std::vector<int> errorVec;
+    ModifySlices::data(jh->ErrorVec(), errorVec, slices);
+    printVec(errorVec);
+    msg() << MSG::dec << endreq;
+  }
+}
+
+// Print CMM energy sums
+
+void JemTesterV1::printCmmSums() const
+{
+  msg() << "Number of CMM Energy Sums = " << m_cmmEtMap.size() << endreq;
+  CmmSumsMap::const_iterator mapIter = m_cmmEtMap.begin();
+  CmmSumsMap::const_iterator mapEnd  = m_cmmEtMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::CMMEtSums* const et = mapIter->second;
+    int peak   = et->peak();
+    int slices = (et->ExVec()).size();
+    if (m_forceSlicesCmm) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesCmm);
+      slices = m_forceSlicesCmm;
+    }
+    msg() << "crate/dataID/peak/Ex/Ey/Et/ExErr/EyErr/EtErr: "
+	  << et->crate() << "/" << et->dataID() << "/" << peak << "/";
+    std::vector<unsigned int> exVec;
+    std::vector<unsigned int> eyVec;
+    std::vector<unsigned int> etVec;
+    std::vector<int> exError;
+    std::vector<int> eyError;
+    std::vector<int> etError;
+    ModifySlices::data(et->ExVec(), exVec, slices);
+    ModifySlices::data(et->EyVec(), eyVec, slices);
+    ModifySlices::data(et->EtVec(), etVec, slices);
+    ModifySlices::data(et->ExErrorVec(), exError, slices);
+    ModifySlices::data(et->EyErrorVec(), eyError, slices);
+    ModifySlices::data(et->EtErrorVec(), etError, slices);
+    printVecU(exVec);
+    printVecU(eyVec);
+    printVecU(etVec);
+    msg() << MSG::hex;
+    printVec(exError);
+    printVec(eyError);
+    printVec(etError);
+    msg() << MSG::dec << endreq;
+  }
+}
+
+// Print the JEM RoIs
+
+void JemTesterV1::printJemRois(const std::string& source) const
+{
+  msg() << "Number of JEM RoIs (" << source << ") = " << m_roiMap.size()
+        << endreq;
+  JemRoiMap::const_iterator mapIter = m_roiMap.begin();
+  JemRoiMap::const_iterator mapEnd  = m_roiMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::JEMRoI* const roi = mapIter->second;
+    msg() << "crate/jem/frame/loc/fwd/hits/error: "
+          << roi->crate() << "/" << roi->jem() << "/" << roi->frame() << "/"
+	  << roi->location() << "/" << roi->forward() << "/";
+    const int hits = roi->hits();
+    const int numHits = (roi->forward()) ? 4 : 8;
+    for (int i = 0; i < numHits; ++i) {
+      if (i > 0) msg() << ":";
+      msg() << ((hits >> i) & 0x1);
+    }
+    msg() << "/" << roi->error() << "/" << endreq;
+  }
+}
+
+// Print the CMM RoIs
+
+void JemTesterV1::printCmmRois(const std::string& source,
+                               const LVL1::CMMRoI* const roi) const
+{
+  msg() << "CMMRoI(" << source
+        << "):jetEtHits/sumEtHits/missingEtHits/missingEtSigHits/Ex/Ey/Et;error: "
+        << MSG::hex << roi->jetEtHits() << ";" << roi->jetEtError() << "/"
+        << roi->sumEtHits() << ";" << roi->sumEtError() << "/"
+        << roi->missingEtHits() << ";" << roi->missingEtError() << "/"
+        << roi->missingEtSigHits() << ";" << roi->missingEtSigError() << "/"
+        << MSG::dec << roi->ex() << ";" << roi->exError() << "/"
+        << roi->ey() << ";" << roi->eyError() << "/"
+        << roi->et() << ";" << roi->etError() << "/" << endreq;
+}
+
+// Print a vector
+
+void JemTesterV1::printVec(const std::vector<int>& vec) const
+{
+  std::vector<int>::const_iterator pos;
+  for (pos = vec.begin(); pos != vec.end(); ++pos) {
+    if (pos != vec.begin()) msg() << ",";
+    msg() << *pos;
+  }
+  msg() << "/";
+}
+
+// Print a vector (unsigned)
+
+void JemTesterV1::printVecU(const std::vector<unsigned int>& vec) const
+{
+  std::vector<unsigned int>::const_iterator pos;
+  for (pos = vec.begin(); pos != vec.end(); ++pos) {
+    if (pos != vec.begin()) msg() << ",";
+    msg() << *pos;
+  }
+  msg() << "/";
+}
+
+// Set up jet element map
+
+void JemTesterV1::setupJeMap(const JetElementCollection* const jeCollection)
+{
+  m_jeMap.clear();
+  if (jeCollection) {
+    JetElementCollection::const_iterator pos = jeCollection->begin();
+    JetElementCollection::const_iterator pose = jeCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::JetElement* const je = *pos;
+      const unsigned int key = m_elementKey->jeKey(je->phi(), je->eta());
+      m_jeMap.insert(std::make_pair(key, je));
+    }
+  }
+}
+
+// Set up jet hits map
+
+void JemTesterV1::setupHitsMap(const JetHitsCollection* const hitCollection)
+{
+  m_hitsMap.clear();
+  if (hitCollection) {
+    JetHitsCollection::const_iterator pos  = hitCollection->begin();
+    JetHitsCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::JEMHits* const hits = *pos;
+      const int key = hits->crate()*100 + hits->module();
+      m_hitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Set up energy sums map
+
+void JemTesterV1::setupEtMap(const EnergySumsCollection* const etCollection)
+{
+  m_etMap.clear();
+  if (etCollection) {
+    EnergySumsCollection::const_iterator pos  = etCollection->begin();
+    EnergySumsCollection::const_iterator pose = etCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::JEMEtSums* const sums = *pos;
+      // Sim doesn't zero suppress properly, do it here
+      if (std::accumulate((sums->ExVec()).begin(), (sums->ExVec()).end(), 0) ||
+          std::accumulate((sums->EyVec()).begin(), (sums->EyVec()).end(), 0) ||
+          std::accumulate((sums->EtVec()).begin(), (sums->EtVec()).end(), 0)) {
+        const int key = sums->crate()*100 + sums->module();
+        m_etMap.insert(std::make_pair(key, sums));
+      }
+    }
+  }
+}
+
+// Set up CMM hits map
+
+void JemTesterV1::setupCmmHitsMap(const CmmJetCollection* const hitCollection)
+{
+  m_cmmHitsMap.clear();
+  if (hitCollection) {
+    CmmJetCollection::const_iterator pos  = hitCollection->begin();
+    CmmJetCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CMMJetHits* const hits = *pos;
+      const int key = hits->crate()*100 + hits->dataID();
+      m_cmmHitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Set up CMM energy sums map
+
+void JemTesterV1::setupCmmEtMap(const CmmEnergyCollection* const etCollection)
+{
+  m_cmmEtMap.clear();
+  if (etCollection) {
+    CmmEnergyCollection::const_iterator pos  = etCollection->begin();
+    CmmEnergyCollection::const_iterator pose = etCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CMMEtSums* const sums = *pos;
+      if (std::accumulate((sums->ExVec()).begin(), (sums->ExVec()).end(), 0) ||
+          std::accumulate((sums->EyVec()).begin(), (sums->EyVec()).end(), 0) ||
+          std::accumulate((sums->EtVec()).begin(), (sums->EtVec()).end(), 0) ||
+          std::accumulate((sums->ExErrorVec()).begin(),
+	                                      (sums->ExErrorVec()).end(), 0) ||
+          std::accumulate((sums->EyErrorVec()).begin(),
+	                                      (sums->EyErrorVec()).end(), 0) ||
+          std::accumulate((sums->EtErrorVec()).begin(),
+	                                      (sums->EtErrorVec()).end(), 0)) {
+        const int key = sums->crate()*100 + sums->dataID();
+        m_cmmEtMap.insert(std::make_pair(key, sums));
+      }
+    }
+  }
+}
+
+// Set up JEM RoI map
+
+void JemTesterV1::setupJemRoiMap(const JemRoiCollection* const jrCollection)
+{
+  m_roiMap.clear();
+  if (jrCollection) {
+    JemRoiCollection::const_iterator pos  = jrCollection->begin();
+    JemRoiCollection::const_iterator pose = jrCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::JEMRoI* const roi = *pos;
+      const uint32_t key = roi->roiWord();
+      m_roiMap.insert(std::make_pair(key, roi));
+    }
+  }
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/JemTesterV1.h b/Trigger/TrigT1/TrigT1CaloByteStream/test/JemTesterV1.h
new file mode 100755
index 0000000000000000000000000000000000000000..3ab152fc80aad8ebd194f550c66df0810e9b136b
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/JemTesterV1.h
@@ -0,0 +1,164 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEMTESTERV1_H
+#define TRIGT1CALOBYTESTREAM_JEMTESTERV1_H
+
+#include <map>
+#include <string>
+#include <stdint.h>
+
+#include "AthenaBaseComps/AthAlgorithm.h"
+#include "DataModel/DataVector.h"
+
+class ISvcLocator;
+class StatusCode;
+
+namespace LVL1 {
+  class CMMEtSums;
+  class CMMJetHits;
+  class CMMRoI;
+  class JEMEtSums;
+  class JEMHits;
+  class JEMRoI;
+  class JetElement;
+  class JetElementKey;
+}
+
+namespace LVL1BS {
+
+/** Algorithm to test JEM component bytestream conversions.
+ *
+ *  Just prints out the contents of the JetElement objects,
+ *  JEMHits objects and JEMEtSums objects.
+ *  Now also includes CMMJetHits, CMMEtSums, JEMRoI and CMMRoI.
+ *
+ *  Run before writing bytestream and after reading it and compare.
+ *
+ *  @author Peter Faulkner
+ */
+
+class JemTesterV1 : public AthAlgorithm {
+
+ public:
+   JemTesterV1(const std::string& name, ISvcLocator* pSvcLocator);
+   virtual ~JemTesterV1();
+
+   virtual StatusCode initialize();
+   virtual StatusCode execute();
+   virtual StatusCode finalize();
+
+ private:
+   typedef DataVector<LVL1::JetElement>              JetElementCollection;
+   typedef DataVector<LVL1::JEMHits>                 JetHitsCollection;
+   typedef DataVector<LVL1::JEMEtSums>               EnergySumsCollection;
+   typedef DataVector<LVL1::CMMJetHits>              CmmJetCollection;
+   typedef DataVector<LVL1::CMMEtSums>               CmmEnergyCollection;
+   typedef DataVector<LVL1::JEMRoI>                  JemRoiCollection;
+   typedef std::map<unsigned int, const LVL1::JetElement*> JetElementMap;
+   typedef std::map<int, const LVL1::JEMHits*>       JetHitsMap;
+   typedef std::map<int, const LVL1::JEMEtSums*>     EnergySumsMap;
+   typedef std::map<int, const LVL1::CMMJetHits*>    CmmHitsMap;
+   typedef std::map<int, const LVL1::CMMEtSums*>     CmmSumsMap;
+   typedef std::map<uint32_t, const LVL1::JEMRoI*>   JemRoiMap;
+
+   /// Print the jet elements
+   void printJetElements(const std::string& source) const;
+   /// Print the jet hits
+   void printJetHits()    const;
+   /// Print the energy sums
+   void printEnergySums() const;
+   /// Print the CMM hits
+   void printCmmHits()    const;
+   /// Print the CMM energy sums
+   void printCmmSums()    const;
+   /// Print the JEM RoIs
+   void printJemRois(const std::string& source) const;
+   /// Print the CMM RoIs
+   void printCmmRois(const std::string& source,
+                     const LVL1::CMMRoI* roi)   const;
+
+   /// Print a vector
+   void printVec(const std::vector<int>& vec) const;
+   /// Print a vector (unsigned)
+   void printVecU(const std::vector<unsigned int>& vec) const;
+
+   /// Set up jet element map
+   void setupJeMap(const JetElementCollection* jeCollection);
+   /// Set up jet hits map
+   void setupHitsMap(const JetHitsCollection* hitCollection);
+   /// Set up energy sums map
+   void setupEtMap(const EnergySumsCollection* etCollection);
+   /// Set up CMM hits map
+   void setupCmmHitsMap(const CmmJetCollection* hitCollection);
+   /// Set up CMM energy sums map
+   void setupCmmEtMap(const CmmEnergyCollection* etCollection);
+   /// Set up JEM RoI map
+   void setupJemRoiMap(const JemRoiCollection* jrCollection);
+
+   /// Jet element key provider
+   LVL1::JetElementKey* m_elementKey;
+   /// Jet element container StoreGate key
+   std::string m_jetElementLocation;
+   /// Jet hits container StoreGate key
+   std::string m_jemHitsLocation;
+   /// Energy sums container StoreGate key
+   std::string m_jemEtSumsLocation;
+   /// CMM hits container StoreGate key
+   std::string m_cmmJetLocation;
+   /// CMM energy sums container StoreGate key
+   std::string m_cmmEnergyLocation;
+   /// JEM RoI container StoreGate key
+   std::string m_jemRoiLocation;
+   /// CMM RoI container StoreGate key
+   std::string m_cmmRoiLocation;
+   /// JEM RoI from RoIB container StoreGate key
+   std::string m_jemRoiLocationRoib;
+   /// CMM RoI from RoIB container StoreGate key
+   std::string m_cmmRoiLocationRoib;
+   /// Jet element overlap container StoreGate key
+   std::string m_jetElementLocationOverlap;
+   /// Force number of JEM slices
+   int m_forceSlicesJem;
+   /// Force number of CMM slices
+   int m_forceSlicesCmm;
+   /// Jet element print flag
+   int m_jetElementPrint;
+   /// Jet hits print flag
+   int m_jemHitsPrint;
+   /// Energy sums print flag
+   int m_jemEtSumsPrint;
+   /// CMM hits print flag
+   int m_cmmHitsPrint;
+   /// CMM energy sums print flag
+   int m_cmmEtSumsPrint;
+   /// JEM RoI print flag
+   int m_jemRoiPrint;
+   /// CMM RoI print flag
+   int m_cmmRoiPrint;
+   /// JEM RoI from RoIB print flag
+   int m_jemRoiPrintRoib;
+   /// CMM RoI from RoIB print flag
+   int m_cmmRoiPrintRoib;
+   /// Jet element overlap print flag
+   int m_jetElementPrintOverlap;
+
+   /// Jet element map
+   JetElementMap m_jeMap;
+   /// Jet hits map
+   JetHitsMap    m_hitsMap;
+   /// Energy sums map
+   EnergySumsMap m_etMap;
+   /// CMM hits map
+   CmmHitsMap    m_cmmHitsMap;
+   /// CMM energy sums map
+   CmmSumsMap    m_cmmEtMap;
+   /// JEM RoI map
+   JemRoiMap     m_roiMap;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/JemTesterV2.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/test/JemTesterV2.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..8ad28a0e8c0747a6efd66ee3e99ee0133bf99123
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/JemTesterV2.cxx
@@ -0,0 +1,752 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <numeric>
+#include <utility>
+
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "StoreGate/StoreGateSvc.h"
+
+#include "TrigT1CaloEvent/CMXEtSums.h"
+#include "TrigT1CaloEvent/CMXJetTob.h"
+#include "TrigT1CaloEvent/CMXJetHits.h"
+#include "TrigT1CaloEvent/CMXRoI.h"
+#include "TrigT1CaloEvent/JEMEtSums.h"
+#include "TrigT1CaloEvent/JEMTobRoI.h"
+#include "TrigT1CaloEvent/JetElement.h"
+#include "TrigT1CaloUtils/JetElementKey.h"
+#include "TrigT1CaloUtils/DataError.h"
+#include "TrigT1Interfaces/TrigT1CaloDefs.h"
+
+#include "JemTesterV2.h"
+#include "../src/ModifySlices.h"
+
+namespace LVL1BS {
+
+JemTesterV2::JemTesterV2(const std::string& name, ISvcLocator* pSvcLocator)
+                       : AthAlgorithm(name, pSvcLocator),
+		         m_elementKey(0)
+{
+  declareProperty("JetElementLocation",
+           m_jetElementLocation = LVL1::TrigT1CaloDefs::JetElementLocation);
+  declareProperty("JEMEtSumsLocation",
+           m_jemEtSumsLocation  = LVL1::TrigT1CaloDefs::JEMEtSumsLocation);
+  declareProperty("CMXJetTobLocation",
+           m_cmxTobLocation     = LVL1::TrigT1CaloDefs::CMXJetTobLocation);
+  declareProperty("CMXJetHitsLocation",
+           m_cmxJetLocation     = LVL1::TrigT1CaloDefs::CMXJetHitsLocation);
+  declareProperty("CMXEtSumsLocation",
+           m_cmxEnergyLocation  = LVL1::TrigT1CaloDefs::CMXEtSumsLocation);
+  declareProperty("JEMTobRoILocation",
+           m_jemRoiLocation     = LVL1::TrigT1CaloDefs::JEMTobRoILocation);
+  declareProperty("CMXRoILocation",
+           m_cmxRoiLocation     = LVL1::TrigT1CaloDefs::CMXRoILocation);
+  declareProperty("JEMTobRoILocationRoIB",
+           m_jemRoiLocationRoib =
+	                 LVL1::TrigT1CaloDefs::JEMTobRoILocation + "RoIB");
+  declareProperty("CMXRoILocationRoIB",
+           m_cmxRoiLocationRoib =
+	                 LVL1::TrigT1CaloDefs::CMXRoILocation + "RoIB");
+  declareProperty("JetElementLocationOverlap",
+           m_jetElementLocationOverlap =
+	                 LVL1::TrigT1CaloDefs::JetElementLocation + "Overlap");
+
+  declareProperty("ForceSlicesJEM",  m_forceSlicesJem  = 0);
+  declareProperty("ForceSlicesCMX",  m_forceSlicesCmx  = 0);
+
+  // By default print everything except RoIB RoIs and jet element overlap
+  declareProperty("JetElementPrint", m_jetElementPrint = 1);
+  declareProperty("JEMEtSumsPrint",  m_jemEtSumsPrint  = 1);
+  declareProperty("CMXJetTobPrint",  m_cmxTobPrint     = 1,
+                  "Set to 2 to print presence maps (Neutral format only)");
+  declareProperty("CMXJetHitsPrint", m_cmxHitsPrint    = 1);
+  declareProperty("CMXEtSumsPrint",  m_cmxEtSumsPrint  = 1);
+  declareProperty("JEMRoIPrint",     m_jemRoiPrint     = 1);
+  declareProperty("CMXRoIPrint",     m_cmxRoiPrint     = 1);
+  declareProperty("JEMRoIPrintRoIB", m_jemRoiPrintRoib = 0);
+  declareProperty("CMXRoIPrintRoIB", m_cmxRoiPrintRoib = 0);
+  declareProperty("JetElementPrintOverlap", m_jetElementPrintOverlap = 0);
+}
+
+JemTesterV2::~JemTesterV2()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode JemTesterV2::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << /* version() */ PACKAGE_VERSION << endreq;
+
+  m_elementKey = new LVL1::JetElementKey();
+
+  return StatusCode::SUCCESS;
+}
+
+// Execute
+
+StatusCode JemTesterV2::execute()
+{
+  if ( !msgLvl(MSG::INFO) ) return StatusCode::SUCCESS;
+  msg(MSG::INFO);
+
+  if (m_jetElementPrint) {
+
+    // Find core jet elements
+
+    const JetElementCollection* jeCollection = 0;
+    StatusCode sc = evtStore()->retrieve(jeCollection, m_jetElementLocation);
+    if (sc.isFailure() || !jeCollection || jeCollection->empty()) {
+      msg() << "No core Jet Elements found" << endreq;
+    } else {
+
+      // Order by eta, phi
+
+      setupJeMap(jeCollection);
+
+      // Print the jet elements
+
+      printJetElements("core");
+    }
+  }
+
+  if (m_jetElementPrintOverlap) {
+
+    // Find overlap jet elements
+
+    const JetElementCollection* jeCollection = 0;
+    StatusCode sc = evtStore()->retrieve(jeCollection,
+                                      m_jetElementLocationOverlap);
+    if (sc.isFailure() || !jeCollection || jeCollection->empty()) {
+      msg() << "No overlap Jet Elements found" << endreq;
+    } else {
+
+      // Order by eta, phi
+
+      setupJeMap(jeCollection);
+
+      // Print the jet elements
+
+      printJetElements("overlap");
+    }
+  }
+
+  if (m_jemEtSumsPrint) {
+
+    // Find energy sums
+
+    const EnergySumsCollection* etCollection = 0;
+    StatusCode sc = evtStore()->retrieve(etCollection, m_jemEtSumsLocation);
+    if (sc.isFailure() || !etCollection || etCollection->empty()) {
+      msg() << "No Energy Sums found" << endreq;
+    } else {
+
+      // Order by crate, module
+
+      setupEtMap(etCollection);
+
+      // Print the energy sums
+
+      printEnergySums();
+    }
+  }
+
+  if (m_cmxTobPrint) {
+
+    // Find CMX TOBs
+
+    const CmxTobCollection* tobCollection = 0;
+    StatusCode sc = evtStore()->retrieve(tobCollection, m_cmxTobLocation);
+    if (sc.isFailure() || !tobCollection || tobCollection->empty()) {
+      msg() << "No CMX TOBs found" << endreq;
+    } else {
+
+      // Order by crate, jem, frame, loc
+
+      setupCmxTobMap(tobCollection);
+
+      // Print the CMX TOBs
+
+      printCmxTobs();
+    }
+  }
+
+  if (m_cmxHitsPrint) {
+
+    // Find CMX hits
+
+    const CmxJetCollection* hitCollection = 0;
+    StatusCode sc = evtStore()->retrieve(hitCollection, m_cmxJetLocation);
+    if (sc.isFailure() || !hitCollection || hitCollection->empty()) {
+      msg() << "No CMX Hits found" << endreq;
+    } else {
+
+      // Order by crate, source
+
+      setupCmxHitsMap(hitCollection);
+
+      // Print the CMX hits
+
+      printCmxHits();
+    }
+  }
+
+  if (m_cmxEtSumsPrint) {
+
+    // Find CMX energy sums
+
+    const CmxEnergyCollection* etCollection = 0;
+    StatusCode sc = evtStore()->retrieve(etCollection, m_cmxEnergyLocation);
+    if (sc.isFailure() || !etCollection || etCollection->empty()) {
+      msg() << "No CMX Energy Sums found" << endreq;
+    } else {
+
+      // Order by crate, source
+
+      setupCmxEtMap(etCollection);
+
+      // Print the CMX energy sums
+
+      printCmxSums();
+    }
+  }
+
+  if (m_jemRoiPrint) {
+
+    // Find JEM RoIs
+
+    const JemRoiCollection* jrCollection = 0;
+    StatusCode sc = evtStore()->retrieve(jrCollection, m_jemRoiLocation);
+    if (sc.isFailure() || !jrCollection || jrCollection->empty()) {
+      msg() << "No JEM RoIs found" << endreq;
+    } else {
+
+      // Order by RoI word
+
+      setupJemRoiMap(jrCollection);
+
+      // Print the JEM RoIs
+
+      printJemRois("DAQ");
+    }
+  }
+
+  if (m_jemRoiPrintRoib) {
+
+    // Find JEM RoIs from RoIB
+
+    const JemRoiCollection* jrCollection = 0;
+    StatusCode sc = evtStore()->retrieve(jrCollection, m_jemRoiLocationRoib);
+    if (sc.isFailure() || !jrCollection || jrCollection->empty()) {
+      msg() << "No JEM RoIs from RoIB found" << endreq;
+    } else {
+
+      // Order by RoI word
+
+      setupJemRoiMap(jrCollection);
+
+      // Print the JEM RoIs from RoIB
+
+      printJemRois("RoIB");
+    }
+  }
+
+  if (m_cmxRoiPrint) {
+
+    // Find CMX RoIs
+
+    const LVL1::CMXRoI* crCollection = 0;
+    StatusCode sc = evtStore()->retrieve(crCollection, m_cmxRoiLocation);
+    if (sc.isFailure() || !crCollection || (!crCollection->roiWord(0) &&
+                                            !crCollection->roiWord(1) &&
+                                            !crCollection->roiWord(2) &&
+                                            !crCollection->roiWord(3) &&
+                                            !crCollection->roiWord(4) &&
+                                            !crCollection->roiWord(5))) {
+      msg() << "No CMX RoIs found" << endreq;
+    } else {
+
+      // Print the CMX RoIs
+
+      printCmxRois("DAQ", crCollection);
+    }
+  }
+
+  if (m_cmxRoiPrintRoib) {
+
+    // Find CMX RoIs from RoIB
+
+    const LVL1::CMXRoI* crCollection = 0;
+    StatusCode sc = evtStore()->retrieve(crCollection, m_cmxRoiLocationRoib);
+    if (sc.isFailure() || !crCollection || (!crCollection->roiWord(0) &&
+                                            !crCollection->roiWord(1) &&
+                                            !crCollection->roiWord(2) &&
+                                            !crCollection->roiWord(3) &&
+                                            !crCollection->roiWord(4) &&
+                                            !crCollection->roiWord(5))) {
+      msg() << "No CMX RoIs from RoIB found" << endreq;
+    } else {
+
+      // Print the CMX RoIs from RoIB
+
+      printCmxRois("RoIB", crCollection);
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode JemTesterV2::finalize()
+{
+  delete m_elementKey;
+
+  return StatusCode::SUCCESS;
+}
+
+// Print the jet elements
+
+void JemTesterV2::printJetElements(const std::string& source) const
+{
+  msg() << "Number of " << source << " Jet Elements = "
+        << m_jeMap.size() << endreq;
+  JetElementMap::const_iterator mapIter = m_jeMap.begin();
+  JetElementMap::const_iterator mapEnd  = m_jeMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::JetElement* const je = mapIter->second;
+    int peak   = je->peak();
+    int slices = (je->emEnergyVec()).size();
+    if (m_forceSlicesJem) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesJem);
+      slices = m_forceSlicesJem;
+    }
+    std::vector<int> emEnergy;
+    std::vector<int> hadEnergy;
+    std::vector<int> emError;
+    std::vector<int> hadError;
+    std::vector<int> linkError;
+    ModifySlices::data(je->emEnergyVec(),  emEnergy,  slices);
+    ModifySlices::data(je->hadEnergyVec(), hadEnergy, slices);
+    ModifySlices::data(je->emErrorVec(),   emError,   slices);
+    ModifySlices::data(je->hadErrorVec(),  hadError,  slices);
+    ModifySlices::data(je->linkErrorVec(), linkError, slices);
+    msg() << "key/eta/phi/peak/em/had/emErr/hadErr/linkErr: "
+          << mapIter->first << "/" << je->eta() << "/" << je->phi() << "/"
+	  << peak << "/";
+    printVec(emEnergy);
+    printVec(hadEnergy);
+    msg() << MSG::hex;
+    printVec(emError);
+    printVec(hadError);
+    printVec(linkError);
+    msg() << MSG::dec << endreq;
+  }
+}
+
+// Print energy sums
+
+void JemTesterV2::printEnergySums() const
+{
+  msg() << "Number of Energy Sums = " << m_etMap.size() << endreq;
+  EnergySumsMap::const_iterator mapIter = m_etMap.begin();
+  EnergySumsMap::const_iterator mapEnd  = m_etMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::JEMEtSums* const et = mapIter->second;
+    int peak   = et->peak();
+    int slices = (et->ExVec()).size();
+    if (m_forceSlicesJem) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesJem);
+      slices = m_forceSlicesJem;
+    }
+    msg() << "crate/module/peak/Ex/Ey/Et: "
+	  << et->crate() << "/" << et->module() << "/" << peak << "/";
+    std::vector<unsigned int> exVec;
+    std::vector<unsigned int> eyVec;
+    std::vector<unsigned int> etVec;
+    ModifySlices::data(et->ExVec(), exVec, slices);
+    ModifySlices::data(et->EyVec(), eyVec, slices);
+    ModifySlices::data(et->EtVec(), etVec, slices);
+    printVecU(exVec);
+    printVecU(eyVec);
+    printVecU(etVec);
+    msg() << endreq;
+  }
+}
+
+// Print the CMX-Jet TOBs
+
+void JemTesterV2::printCmxTobs() const
+{
+  msg() << "Number of CMX-Jet TOBs = " << m_cmxTobMap.size() << endreq;
+  CmxTobMap::const_iterator mapIter = m_cmxTobMap.begin();
+  CmxTobMap::const_iterator mapEnd  = m_cmxTobMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::CMXJetTob* const tb = mapIter->second;
+    int peak   = tb->peak();
+    int slices = (tb->energyLgVec()).size();
+    if (m_forceSlicesCmx) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesCmx);
+      slices = m_forceSlicesCmx;
+    }
+    if (m_cmxTobPrint == 2) {
+      msg() << "crate/jem/frame/loc/peak/energyLg/energySm/error/presenceMap: ";
+    } else {
+      msg() << "crate/jem/frame/loc/peak/energyLg/energySm/error: ";
+    }
+    msg() << tb->crate() << "/" << tb->jem() << "/" << tb->frame() << "/"
+          << tb->location() << "/" << peak << "/";
+
+    std::vector<int> energyLg;
+    std::vector<int> energySm;
+    std::vector<int> error;
+    ModifySlices::data(tb->energyLgVec(), energyLg, slices);
+    ModifySlices::data(tb->energySmVec(), energySm, slices);
+    ModifySlices::data(tb->errorVec(), error, slices);
+    printVec(energyLg);
+    printVec(energySm);
+    msg() << MSG::hex;
+    printVec(error);
+    if (m_cmxTobPrint == 2) {
+      std::vector<unsigned int> presence;
+      ModifySlices::data(tb->presenceMapVec(), presence, slices);
+      printVecH(presence, 1, 16);
+    }
+    msg() << MSG::dec << endreq;
+  }
+}
+
+// Print the CMX hits
+
+void JemTesterV2::printCmxHits() const
+{
+  msg() << "Number of CMX Hits = " << m_cmxHitsMap.size() << endreq;
+  CmxHitsMap::const_iterator mapIter = m_cmxHitsMap.begin();
+  CmxHitsMap::const_iterator mapEnd  = m_cmxHitsMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::CMXJetHits* const jh = mapIter->second;
+    const int source = jh->source();
+    int peak   = jh->peak();
+    int slices = (jh->hitsVec0()).size();
+    if (m_forceSlicesCmx) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesCmx);
+      slices = m_forceSlicesCmx;
+    }
+    msg() << "crate/source/peak/hits0/hits1/error0/error1: "
+	  << jh->crate() << "/" << source << "/" << peak << "/";
+    int words1 = 5;
+    int words2 = 5;
+    int bits   = 3;
+    unsigned int mask = 0x7;
+    if (source == LVL1::CMXJetHits::REMOTE_FORWARD ||
+        source == LVL1::CMXJetHits::LOCAL_FORWARD  ||
+        source == LVL1::CMXJetHits::TOTAL_FORWARD) {
+      words1 = 8;
+      words2 = 7;
+      bits   = 2;
+      mask   = 0x3;
+    } else if (source == LVL1::CMXJetHits::TOPO_CHECKSUM) {
+      words1 = 1;
+      words2 = 0;
+      bits   = 16;
+      mask   = 0xffff;
+    } else if (source == LVL1::CMXJetHits::TOPO_OCCUPANCY_MAP) {
+      words1 = 16;
+      words2 = 0;
+      bits   = 1;
+      mask   = 0x1;
+    } else if (source == LVL1::CMXJetHits::TOPO_OCCUPANCY_COUNTS) {
+      words1 = 8;
+      words2 = 8;
+    }
+    std::vector<unsigned int> hitsVec0;
+    std::vector<unsigned int> hitsVec1;
+    ModifySlices::data(jh->hitsVec0(), hitsVec0, slices);
+    ModifySlices::data(jh->hitsVec1(), hitsVec1, slices);
+    std::vector<unsigned int>::const_iterator pos;
+    std::vector<unsigned int>::const_iterator posb = hitsVec0.begin();
+    std::vector<unsigned int>::const_iterator pose = hitsVec0.end();
+    for (pos = posb; pos != pose; ++pos) {
+      if (pos != posb) msg() << ",";
+      const unsigned int hits = *pos;
+      for (int i = 0; i < words1; ++i) {
+        if (i != 0) msg() << ":";
+	const unsigned int thr = (hits >> bits*i) & mask;
+        msg() << thr;
+      }
+    }
+    msg() << "/";
+    posb = hitsVec1.begin();
+    pose = hitsVec1.end();
+    for (pos = posb; pos != pose; ++pos) {
+      if (pos != posb) msg() << ",";
+      const unsigned int hits = *pos;
+      for (int i = 0; i < words2; ++i) {
+        if (i != 0) msg() << ":";
+	const unsigned int thr = (hits >> bits*i) & mask;
+        msg() << thr;
+      }
+    }
+    msg() << "/" << MSG::hex;
+    std::vector<int> errorVec0;
+    std::vector<int> errorVec1;
+    ModifySlices::data(jh->errorVec0(), errorVec0, slices);
+    ModifySlices::data(jh->errorVec1(), errorVec1, slices);
+    printVec(errorVec0);
+    printVec(errorVec1);
+    msg() << MSG::dec << endreq;
+  }
+}
+
+// Print CMX energy sums
+
+void JemTesterV2::printCmxSums() const
+{
+  msg() << "Number of CMX Energy Sums = " << m_cmxEtMap.size() << endreq;
+  CmxSumsMap::const_iterator mapIter = m_cmxEtMap.begin();
+  CmxSumsMap::const_iterator mapEnd  = m_cmxEtMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::CMXEtSums* const et = mapIter->second;
+    int peak   = et->peak();
+    int slices = (et->ExVec()).size();
+    if (m_forceSlicesCmx) {
+      peak   = ModifySlices::peak(peak, slices, m_forceSlicesCmx);
+      slices = m_forceSlicesCmx;
+    }
+    msg() << "crate/source/peak/Ex/Ey/Et/ExErr/EyErr/EtErr: "
+	  << et->crate() << "/" << et->source() << "/" << peak << "/";
+    std::vector<unsigned int> exVec;
+    std::vector<unsigned int> eyVec;
+    std::vector<unsigned int> etVec;
+    std::vector<int> exError;
+    std::vector<int> eyError;
+    std::vector<int> etError;
+    ModifySlices::data(et->ExVec(), exVec, slices);
+    ModifySlices::data(et->EyVec(), eyVec, slices);
+    ModifySlices::data(et->EtVec(), etVec, slices);
+    ModifySlices::data(et->ExErrorVec(), exError, slices);
+    ModifySlices::data(et->EyErrorVec(), eyError, slices);
+    ModifySlices::data(et->EtErrorVec(), etError, slices);
+    printVecU(exVec);
+    printVecU(eyVec);
+    printVecU(etVec);
+    msg() << MSG::hex;
+    printVec(exError);
+    printVec(eyError);
+    printVec(etError);
+    msg() << MSG::dec << endreq;
+  }
+}
+
+// Print the JEM RoIs
+
+void JemTesterV2::printJemRois(const std::string& source) const
+{
+  msg() << "Number of JEM RoIs (" << source << ") = " << m_roiMap.size()
+        << endreq;
+  JemRoiMap::const_iterator mapIter = m_roiMap.begin();
+  JemRoiMap::const_iterator mapEnd  = m_roiMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::JEMTobRoI* const roi = mapIter->second;
+    msg() << "crate/jem/frame/loc/energyLg/energySm: "
+          << roi->crate() << "/" << roi->jem() << "/" << roi->frame() << "/"
+	  << roi->location() << "/" << roi->energyLarge() << "/"
+	  << roi->energySmall() << "/" << endreq;
+  }
+}
+
+// Print the CMX RoIs
+
+void JemTesterV2::printCmxRois(const std::string& source,
+                               const LVL1::CMXRoI* const roi) const
+{
+  msg() << "CMXRoI(" << source
+        << "):Normal:sumEtHits/missingEtHits/missingEtSigHits/Ex/Ey/Et;error: "
+        << MSG::hex
+	<< roi->sumEtHits()        << "/"
+        << roi->missingEtHits()    << "/"
+        << roi->missingEtSigHits() << "/"
+        << MSG::dec
+	<< roi->ex() << ";" << roi->exError() << "/"
+        << roi->ey() << ";" << roi->eyError() << "/"
+        << roi->et() << ";" << roi->etError() << "/" << endreq;
+  msg() << "CMXRoI(" << source
+        << "):Masked:sumEtHits/missingEtHits/Ex/Ey/Et;error: "
+        << MSG::hex
+	<< roi->sumEtHits(LVL1::CMXRoI::MASKED)     << "/"
+        << roi->missingEtHits(LVL1::CMXRoI::MASKED) << "/"
+        << MSG::dec
+	<< roi->ex(LVL1::CMXRoI::MASKED)            << ";"
+	<< roi->exError(LVL1::CMXRoI::MASKED)       << "/"
+        << roi->ey(LVL1::CMXRoI::MASKED)            << ";"
+	<< roi->eyError(LVL1::CMXRoI::MASKED)       << "/"
+        << roi->et(LVL1::CMXRoI::MASKED)            << ";"
+	<< roi->etError(LVL1::CMXRoI::MASKED)       << "/" << endreq;
+}
+
+// Print a vector
+
+void JemTesterV2::printVec(const std::vector<int>& vec) const
+{
+  std::vector<int>::const_iterator pos;
+  for (pos = vec.begin(); pos != vec.end(); ++pos) {
+    if (pos != vec.begin()) msg() << ",";
+    msg() << *pos;
+  }
+  msg() << "/";
+}
+
+// Print a vector (unsigned)
+
+void JemTesterV2::printVecU(const std::vector<unsigned int>& vec) const
+{
+  std::vector<unsigned int>::const_iterator pos;
+  for (pos = vec.begin(); pos != vec.end(); ++pos) {
+    if (pos != vec.begin()) msg() << ",";
+    msg() << *pos;
+  }
+  msg() << "/";
+}
+
+// Print a vector of hits
+
+void JemTesterV2::printVecH(const std::vector<unsigned int>& vec,
+                            const int bits, const int words) const
+{
+  const unsigned int mask = (1<<bits)-1;
+  std::vector<unsigned int>::const_iterator pos;
+  std::vector<unsigned int>::const_iterator posb = vec.begin();
+  std::vector<unsigned int>::const_iterator pose = vec.end();
+  for (pos = posb; pos != pose; ++pos) {
+    if (pos != posb) msg() << ",";
+    const unsigned int hits = *pos;
+    for (int i = 0; i < words; ++i) {
+      if (i != 0) msg() << ":";
+      const unsigned int thr = (hits >> (bits*i)) & mask;
+      msg() << thr;
+    }
+  }
+  msg() << "/";
+}
+
+// Set up jet element map
+
+void JemTesterV2::setupJeMap(const JetElementCollection* const jeCollection)
+{
+  m_jeMap.clear();
+  if (jeCollection) {
+    JetElementCollection::const_iterator pos = jeCollection->begin();
+    JetElementCollection::const_iterator pose = jeCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::JetElement* const je = *pos;
+      const unsigned int key = m_elementKey->jeKey(je->phi(), je->eta());
+      m_jeMap.insert(std::make_pair(key, je));
+    }
+  }
+}
+
+// Set up energy sums map
+
+void JemTesterV2::setupEtMap(const EnergySumsCollection* const etCollection)
+{
+  m_etMap.clear();
+  if (etCollection) {
+    EnergySumsCollection::const_iterator pos  = etCollection->begin();
+    EnergySumsCollection::const_iterator pose = etCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::JEMEtSums* const sums = *pos;
+      // Sim doesn't zero suppress properly, do it here
+      if (std::accumulate((sums->ExVec()).begin(), (sums->ExVec()).end(), 0) ||
+          std::accumulate((sums->EyVec()).begin(), (sums->EyVec()).end(), 0) ||
+          std::accumulate((sums->EtVec()).begin(), (sums->EtVec()).end(), 0)) {
+        const int key = sums->crate()*100 + sums->module();
+        m_etMap.insert(std::make_pair(key, sums));
+      }
+    }
+  }
+}
+
+// Set up CMX TOBs map
+
+void JemTesterV2::setupCmxTobMap(const CmxTobCollection* const tobCollection)
+{
+  m_cmxTobMap.clear();
+  if (tobCollection) {
+    CmxTobCollection::const_iterator pos  = tobCollection->begin();
+    CmxTobCollection::const_iterator pose = tobCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CMXJetTob* const tob = *pos;
+      const int key = ((((((tob->crate()<<4)+tob->jem())<<3)+tob->frame())<<2)
+                                                            +tob->location());
+      m_cmxTobMap.insert(std::make_pair(key, tob));
+    }
+  }
+}
+
+// Set up CMX hits map
+
+void JemTesterV2::setupCmxHitsMap(const CmxJetCollection* const hitCollection)
+{
+  m_cmxHitsMap.clear();
+  if (hitCollection) {
+    CmxJetCollection::const_iterator pos  = hitCollection->begin();
+    CmxJetCollection::const_iterator pose = hitCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CMXJetHits* const hits = *pos;
+      const int key = hits->crate()*100 + hits->source();
+      m_cmxHitsMap.insert(std::make_pair(key, hits));
+    }
+  }
+}
+
+// Set up CMX energy sums map
+
+void JemTesterV2::setupCmxEtMap(const CmxEnergyCollection* const etCollection)
+{
+  m_cmxEtMap.clear();
+  if (etCollection) {
+    CmxEnergyCollection::const_iterator pos  = etCollection->begin();
+    CmxEnergyCollection::const_iterator pose = etCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::CMXEtSums* const sums = *pos;
+      if (std::accumulate((sums->ExVec()).begin(), (sums->ExVec()).end(), 0) ||
+          std::accumulate((sums->EyVec()).begin(), (sums->EyVec()).end(), 0) ||
+          std::accumulate((sums->EtVec()).begin(), (sums->EtVec()).end(), 0) ||
+          std::accumulate((sums->ExErrorVec()).begin(),
+	                                      (sums->ExErrorVec()).end(), 0) ||
+          std::accumulate((sums->EyErrorVec()).begin(),
+	                                      (sums->EyErrorVec()).end(), 0) ||
+          std::accumulate((sums->EtErrorVec()).begin(),
+	                                      (sums->EtErrorVec()).end(), 0)) {
+        const int key = sums->crate()*100 + sums->source();
+        m_cmxEtMap.insert(std::make_pair(key, sums));
+      }
+    }
+  }
+}
+
+// Set up JEM RoI map
+
+void JemTesterV2::setupJemRoiMap(const JemRoiCollection* const jrCollection)
+{
+  m_roiMap.clear();
+  if (jrCollection) {
+    JemRoiCollection::const_iterator pos  = jrCollection->begin();
+    JemRoiCollection::const_iterator pose = jrCollection->end();
+    for (; pos != pose; ++pos) {
+      const LVL1::JEMTobRoI* const roi = *pos;
+      const uint32_t key = roi->roiWord();
+      m_roiMap.insert(std::make_pair(key, roi));
+    }
+  }
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/JemTesterV2.h b/Trigger/TrigT1/TrigT1CaloByteStream/test/JemTesterV2.h
new file mode 100755
index 0000000000000000000000000000000000000000..ea12e247880a533b9bf1431e1c32459bd89480a7
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/JemTesterV2.h
@@ -0,0 +1,166 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_JEMTESTERV2_H
+#define TRIGT1CALOBYTESTREAM_JEMTESTERV2_H
+
+#include <map>
+#include <string>
+#include <stdint.h>
+
+#include "AthenaBaseComps/AthAlgorithm.h"
+#include "DataModel/DataVector.h"
+
+class ISvcLocator;
+class StatusCode;
+
+namespace LVL1 {
+  class CMXEtSums;
+  class CMXJetTob;
+  class CMXJetHits;
+  class CMXRoI;
+  class JEMEtSums;
+  class JEMTobRoI;
+  class JetElement;
+  class JetElementKey;
+}
+
+namespace LVL1BS {
+
+/** Algorithm to test JEM component bytestream conversions.
+ *
+ *  Just prints out the contents of the JetElement objects,
+ *  JEMEtSums, CMXJetTob, CMXJetHits, CMXEtSums, JEMTobRoI and CMXRoI.
+ *
+ *  Run before writing bytestream and after reading it and compare.
+ *
+ *  @author Peter Faulkner
+ */
+
+class JemTesterV2 : public AthAlgorithm {
+
+ public:
+   JemTesterV2(const std::string& name, ISvcLocator* pSvcLocator);
+   virtual ~JemTesterV2();
+
+   virtual StatusCode initialize();
+   virtual StatusCode execute();
+   virtual StatusCode finalize();
+
+ private:
+   typedef DataVector<LVL1::JetElement>              JetElementCollection;
+   typedef DataVector<LVL1::JEMEtSums>               EnergySumsCollection;
+   typedef DataVector<LVL1::CMXJetTob>               CmxTobCollection;
+   typedef DataVector<LVL1::CMXJetHits>              CmxJetCollection;
+   typedef DataVector<LVL1::CMXEtSums>               CmxEnergyCollection;
+   typedef DataVector<LVL1::JEMTobRoI>               JemRoiCollection;
+   typedef std::map<unsigned int, const LVL1::JetElement*> JetElementMap;
+   typedef std::map<int, const LVL1::JEMEtSums*>     EnergySumsMap;
+   typedef std::map<int, const LVL1::CMXJetTob*>     CmxTobMap;
+   typedef std::map<int, const LVL1::CMXJetHits*>    CmxHitsMap;
+   typedef std::map<int, const LVL1::CMXEtSums*>     CmxSumsMap;
+   typedef std::map<uint32_t, const LVL1::JEMTobRoI*>   JemRoiMap;
+
+   /// Print the jet elements
+   void printJetElements(const std::string& source) const;
+   /// Print the energy sums
+   void printEnergySums() const;
+   /// Print the CMX TOBs
+   void printCmxTobs()    const;
+   /// Print the CMX hits
+   void printCmxHits()    const;
+   /// Print the CMX energy sums
+   void printCmxSums()    const;
+   /// Print the JEM RoIs
+   void printJemRois(const std::string& source) const;
+   /// Print the CMX RoIs
+   void printCmxRois(const std::string& source,
+                     const LVL1::CMXRoI* roi)   const;
+
+   /// Print a vector
+   void printVec(const std::vector<int>& vec) const;
+   /// Print a vector (unsigned)
+   void printVecU(const std::vector<unsigned int>& vec) const;
+   /// Print a vector of hits
+   void printVecH(const std::vector<unsigned int>& vec, int bits,
+                                                        int words) const;
+
+   /// Set up jet element map
+   void setupJeMap(const JetElementCollection* jeCollection);
+   /// Set up energy sums map
+   void setupEtMap(const EnergySumsCollection* etCollection);
+   /// Set up CMX TOB map
+   void setupCmxTobMap(const CmxTobCollection* tobCollection);
+   /// Set up CMX hits map
+   void setupCmxHitsMap(const CmxJetCollection* hitCollection);
+   /// Set up CMX energy sums map
+   void setupCmxEtMap(const CmxEnergyCollection* etCollection);
+   /// Set up JEM RoI map
+   void setupJemRoiMap(const JemRoiCollection* jrCollection);
+
+   /// Jet element key provider
+   LVL1::JetElementKey* m_elementKey;
+   /// Jet element container StoreGate key
+   std::string m_jetElementLocation;
+   /// Energy sums container StoreGate key
+   std::string m_jemEtSumsLocation;
+   /// CMX TOB container StoreGate key
+   std::string m_cmxTobLocation;
+   /// CMX hits container StoreGate key
+   std::string m_cmxJetLocation;
+   /// CMX energy sums container StoreGate key
+   std::string m_cmxEnergyLocation;
+   /// JEM RoI container StoreGate key
+   std::string m_jemRoiLocation;
+   /// CMX RoI container StoreGate key
+   std::string m_cmxRoiLocation;
+   /// JEM RoI from RoIB container StoreGate key
+   std::string m_jemRoiLocationRoib;
+   /// CMX RoI from RoIB container StoreGate key
+   std::string m_cmxRoiLocationRoib;
+   /// Jet element overlap container StoreGate key
+   std::string m_jetElementLocationOverlap;
+   /// Force number of JEM slices
+   int m_forceSlicesJem;
+   /// Force number of CMX slices
+   int m_forceSlicesCmx;
+   /// Jet element print flag
+   int m_jetElementPrint;
+   /// Energy sums print flag
+   int m_jemEtSumsPrint;
+   /// CMX TOB print flag
+   int m_cmxTobPrint;
+   /// CMX hits print flag
+   int m_cmxHitsPrint;
+   /// CMX energy sums print flag
+   int m_cmxEtSumsPrint;
+   /// JEM RoI print flag
+   int m_jemRoiPrint;
+   /// CMX RoI print flag
+   int m_cmxRoiPrint;
+   /// JEM RoI from RoIB print flag
+   int m_jemRoiPrintRoib;
+   /// CMX RoI from RoIB print flag
+   int m_cmxRoiPrintRoib;
+   /// Jet element overlap print flag
+   int m_jetElementPrintOverlap;
+
+   /// Jet element map
+   JetElementMap m_jeMap;
+   /// Energy sums map
+   EnergySumsMap m_etMap;
+   /// CMX TOB map
+   CmxTobMap     m_cmxTobMap;
+   /// CMX hits map
+   CmxHitsMap    m_cmxHitsMap;
+   /// CMX energy sums map
+   CmxSumsMap    m_cmxEtMap;
+   /// JEM RoI map
+   JemRoiMap     m_roiMap;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/PpmMappingTester.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/test/PpmMappingTester.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..1280c454e68d18824bcf6a07fc815090fcb2b31d
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/PpmMappingTester.cxx
@@ -0,0 +1,225 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include <cmath>
+
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "TrigT1CaloMappingToolInterfaces/IL1CaloMappingTool.h"
+
+#include "PpmMappingTester.h"
+
+namespace LVL1BS {
+
+PpmMappingTester::PpmMappingTester(const std::string& name,
+                                   ISvcLocator* pSvcLocator)
+ : AthAlgorithm(name, pSvcLocator),
+   m_tool1("LVL1::PpmMappingTool/PpmMappingTool"),
+   m_tool2("LVL1::PpmCoolMappingTool/PpmCoolMappingTool")
+{
+
+  declareProperty("PpmMappingTool1", m_tool1);
+  declareProperty("PpmMappingTool2", m_tool2);
+
+  declareProperty("Timing", m_timing = -1);
+
+  // Initialise m_etaBins
+  double base = -4.9;
+  const double width = 0.425;
+  const double offset1 = width/2.;
+  for (int i = 0; i < 4; ++i) {
+    m_etaBins.push_back(base + double(i)*width + offset1);
+  }
+  m_etaBins.push_back(-3.15);
+  m_etaBins.push_back(-3.0);
+  m_etaBins.push_back(-2.8);
+  m_etaBins.push_back(-2.6);
+  const double offset2 = 0.05;
+  for (int i = -25; i < 25; ++i) {
+    m_etaBins.push_back(double(i)/10. + offset2);
+  }
+  m_etaBins.push_back(2.6);
+  m_etaBins.push_back(2.8);
+  m_etaBins.push_back(3.0);
+  m_etaBins.push_back(3.15);
+  base = 3.2;
+  for (int i = 0; i < 4; ++i) {
+    m_etaBins.push_back(base + double(i)*width + offset1);
+  }
+}
+
+PpmMappingTester::~PpmMappingTester()
+{
+}
+
+// Initialisation
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode PpmMappingTester::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << /* version() */ PACKAGE_VERSION << endreq;
+
+  // Retrieve mapping tools
+
+  StatusCode sc = m_tool1.retrieve();
+  if ( sc.isFailure() ) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_tool1 << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_tool1 << endreq;
+
+  sc = m_tool2.retrieve();
+  if ( sc.isFailure() ) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_tool2 << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_tool2 << endreq;
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode PpmMappingTester::finalize()
+{
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode PpmMappingTester::execute()
+{
+  // Timing tests
+
+  std::vector<unsigned int> chanIds;
+  std::vector<double> etas;
+  std::vector<double> phis;
+  std::vector<int>    layers;
+  if      (m_timing == 0) etaPhiToChannel(m_tool1, chanIds);
+  else if (m_timing == 1) etaPhiToChannel(m_tool1, chanIds);
+  else if (m_timing == 2) etaPhiToChannel(m_tool2, chanIds);
+  else if (m_timing == 3) channelToEtaPhi(m_tool1, etas, phis, layers);
+  else if (m_timing == 4) channelToEtaPhi(m_tool2, etas, phis, layers);
+  else {
+
+  // Comparison tests
+  
+    etaPhiToChannel(m_tool1, chanIds);
+    std::vector<unsigned int> chanIds2;
+    etaPhiToChannel(m_tool2, chanIds2);
+    unsigned int size1 = chanIds.size();
+    unsigned int size2 = chanIds2.size();
+    msg(MSG::INFO) << "Vector sizes: " << size1 << ", " << size2 << endreq;
+    if (size1 == size2) {
+      for (unsigned int i = 0; i < size1; ++i) {
+        if (chanIds[i] != chanIds2[i]) {
+	  msg(MSG::INFO) << "ChanID mismatch: " << chanIds[i] << ", "
+	                 << chanIds2[i] << endreq;
+        }
+      }
+    }
+    channelToEtaPhi(m_tool1, etas, phis, layers);
+    std::vector<double> etas2;
+    std::vector<double> phis2;
+    std::vector<int>    layers2;
+    channelToEtaPhi(m_tool2, etas2, phis2, layers2);
+    size1 = etas.size();
+    size2 = etas2.size();
+    msg(MSG::INFO) << "Vector sizes: " << size1 << ", " << size2 << endreq;
+    if (size1 == size2) {
+      for (unsigned int i = 0; i < size1; ++i) {
+        if (notEqual(etas[i], etas2[i]) ||
+	    notEqual(phis[i], phis2[i]) ||
+	    notEqual(layers[i], layers2[i])) {
+	  msg(MSG::INFO) << "ChanID mismatch: eta/phi/layer "
+	                 << etas[i] << "/" << phis[i] << "/"
+			 << layers[i] << "/" 
+	                 << etas2[i] << "/" << phis2[i] << "/"
+			 << layers2[i]      << endreq;
+        }
+      }
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Return a list of TT channel IDs for all possible eta/phi/layers
+
+void PpmMappingTester::etaPhiToChannel(
+                       ToolHandle<LVL1::IL1CaloMappingTool>& tool,
+				  std::vector<unsigned int>& chanIds)
+{
+  const double etaMin = -4.9;
+  const double etaMax = 4.9;
+  const double phiMin = 0.;
+  const double phiMax = 2.*M_PI;
+  std::vector<double>::const_iterator etaPos  = m_etaBins.begin();
+  std::vector<double>::const_iterator etaPosE = m_etaBins.end();
+  for (; etaPos != etaPosE; ++etaPos) {
+    const double eta = *etaPos;
+    if (eta < etaMin) continue;
+    if (eta > etaMax) break;
+    const double absEta = (eta < 0.) ? -eta : eta;
+    const int phiBins = (absEta > 3.2) ? 16 : (absEta > 2.5) ? 32 : 64;
+    const double phiGran = 2.*M_PI/phiBins;
+    for (int bin = 0; bin < phiBins; ++bin) {
+      const double phi = phiGran*(double(bin) + 0.5);
+      if (phi < phiMin) continue;
+      if (phi > phiMax) break;
+      int crate, module, channel;
+      if (m_timing) {
+        if (tool->mapping(eta, phi, 0, crate, module, channel)) {
+          const unsigned int id = (crate * 16 + module) * 64 + channel;
+          chanIds.push_back(id);
+        }
+      } else chanIds.push_back(0);
+      if (m_timing) {
+        if (tool->mapping(eta, phi, 1, crate, module, channel)) {
+          const unsigned int id = (crate * 16 + module) * 64 + channel;
+	  chanIds.push_back(id);
+        }
+      } else chanIds.push_back(0);
+    }
+  }
+}
+
+void PpmMappingTester::channelToEtaPhi(
+                       ToolHandle<LVL1::IL1CaloMappingTool>& tool,
+                       std::vector<double>& etas, std::vector<double>& phis,
+                       std::vector<int>& layers)
+{
+  const int ncrates = 8;
+  const int nmodules = 16;
+  const int nchannels = 64;
+  for (int crate = 0; crate < ncrates; ++crate) {
+    for (int module = 0; module < nmodules; ++module) {
+      for (int channel = 0; channel < nchannels; ++channel) {
+	double eta = 0.;
+	double phi = 0.;
+	int layer = 0;
+        if (m_timing) {
+	  if (tool->mapping(crate, module, channel, eta, phi, layer)) {
+	    etas.push_back(eta);
+	    phis.push_back(phi);
+	    layers.push_back(layer);
+          }
+        } else {
+	  etas.push_back(0);
+	  phis.push_back(0);
+	  layers.push_back(0);
+        }
+      }
+    }
+  }
+}
+
+bool PpmMappingTester::notEqual(double item1, double item2)
+{
+  const double tolerance = 0.00001;
+  return std::abs(item1 - item2) > tolerance;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/PpmMappingTester.h b/Trigger/TrigT1/TrigT1CaloByteStream/test/PpmMappingTester.h
new file mode 100644
index 0000000000000000000000000000000000000000..cb5777f34d971fca51547ae572555a12c99abbc8
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/PpmMappingTester.h
@@ -0,0 +1,60 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_PPMMAPPINGTESTER_H
+#define TRIGT1CALOBYTESTREAM_PPMMAPPINGTESTER_H
+
+#include <string>
+#include <vector>
+
+#include "GaudiKernel/ToolHandle.h"
+
+#include "AthenaBaseComps/AthAlgorithm.h"
+#include "DataModel/DataVector.h"
+
+class ISvcLocator;
+class StatusCode;
+
+namespace LVL1 {
+  class IL1CaloMappingTool;
+}
+
+namespace LVL1BS {
+
+/** Algorithm to compare Trigger tower mapping tools.
+ *
+ *  @author Peter Faulkner
+ */
+
+class PpmMappingTester : public AthAlgorithm {
+
+ public:
+   PpmMappingTester(const std::string& name, ISvcLocator* pSvcLocator);
+   virtual ~PpmMappingTester();
+
+   virtual StatusCode initialize();
+   virtual StatusCode execute();
+   virtual StatusCode finalize();
+
+ private:
+   void etaPhiToChannel(ToolHandle<LVL1::IL1CaloMappingTool>& tool,
+                                   std::vector<unsigned int>& chanIds);
+   void channelToEtaPhi(ToolHandle<LVL1::IL1CaloMappingTool>& tool,
+                        std::vector<double>& etas, std::vector<double>& phis,
+			std::vector<int>& layers);
+   bool notEqual(double item1, double item2);
+
+   /// Mapping tool 1
+   ToolHandle<LVL1::IL1CaloMappingTool> m_tool1;
+   /// Mapping tool 2
+   ToolHandle<LVL1::IL1CaloMappingTool> m_tool2;
+
+   int m_timing;
+   std::vector<double> m_etaBins;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/PpmSubsetTester.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/test/PpmSubsetTester.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..8b921a33f63f482fbaabdfc02b79f7a9b4a73844
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/PpmSubsetTester.cxx
@@ -0,0 +1,236 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <cmath>
+#include <utility>
+
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+#include "StoreGate/StoreGateSvc.h"
+
+#include "TrigT1CaloEvent/TriggerTower.h"
+#include "TrigT1CaloUtils/TriggerTowerKey.h"
+#include "TrigT1Interfaces/TrigT1CaloDefs.h"
+
+#include "TrigT1CaloByteStream/ITrigT1CaloDataAccess.h"
+
+#include "../src/ModifySlices.h"
+
+#include "PpmSubsetTester.h"
+
+namespace LVL1BS {
+
+PpmSubsetTester::PpmSubsetTester(const std::string& name,
+                                 ISvcLocator* pSvcLocator)
+         : AthAlgorithm(name, pSvcLocator),
+	   m_dataAccess("LVL1BS::TrigT1CaloDataAccess/TrigT1CaloDataAccess"),
+	   m_towerKey(0)
+{
+  declareProperty("TrigT1CaloDataAccess", m_dataAccess,
+                                          "TriggerTower access tool");
+  declareProperty("TriggerTowerLocation",
+         m_triggerTowerLocation = LVL1::TrigT1CaloDefs::TriggerTowerLocation);
+
+  declareProperty("ForceSlicesLUT",  m_forceSlicesLut  = 0);
+  declareProperty("ForceSlicesFADC", m_forceSlicesFadc = 0);
+}
+
+PpmSubsetTester::~PpmSubsetTester()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode PpmSubsetTester::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << /* version() */ PACKAGE_VERSION << endreq;
+
+  StatusCode sc = m_dataAccess.retrieve();
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Failed to retrieve tool " << m_dataAccess << endreq;
+    return sc;
+  } else msg(MSG::INFO) << "Retrieved tool " << m_dataAccess << endreq;
+
+  m_towerKey = new LVL1::TriggerTowerKey();
+
+  return StatusCode::SUCCESS;
+}
+
+// Execute
+
+StatusCode PpmSubsetTester::execute()
+{
+  if ( !msgLvl(MSG::INFO) ) return StatusCode::SUCCESS;
+  msg(MSG::INFO);
+
+  const double etaMin = -4.9;
+  const double etaMax = 4.9;
+  const double phiMin = 0.;
+  const double phiMax = M_PI*2.;
+
+  // Find trigger towers
+
+  const TriggerTowerCollection* ttCollection = 0;
+  StatusCode sc = evtStore()->retrieve(ttCollection, m_triggerTowerLocation);
+  if (sc.isFailure() || !ttCollection || ttCollection->empty()) {
+    msg() << "No Trigger Towers found" << endreq;
+    return StatusCode::SUCCESS;
+  }
+
+  // Order by key
+
+  setupTTMap(ttCollection, etaMin, etaMax, phiMin, phiMax);
+
+  // Print the trigger towers
+
+  printTriggerTowers();
+
+  // Now use subset tool
+
+  TriggerTowerCollection::const_iterator beg;
+  TriggerTowerCollection::const_iterator end;
+  sc = m_dataAccess->loadCollection(beg, end, etaMin, etaMax, phiMin, phiMax, false);
+  if (sc.isFailure()) {
+    msg(MSG::ERROR) << "Error accessing data" << endreq;
+    return sc;
+  }
+
+  // Order by key
+
+  setupTTMap(beg, end);
+
+  // Print the trigger towers
+
+  printTriggerTowers();
+
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode PpmSubsetTester::finalize()
+{
+  delete m_towerKey;
+
+  return StatusCode::SUCCESS;
+}
+
+// Print the trigger towers
+
+void PpmSubsetTester::printTriggerTowers() const
+{
+  msg() << "Number of Trigger Towers = " << m_ttMap.size() << endreq;
+  TriggerTowerMap::const_iterator mapIter = m_ttMap.begin();
+  TriggerTowerMap::const_iterator mapEnd  = m_ttMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::TriggerTower* const tt = mapIter->second;
+    int emPeak       = tt->emPeak();
+    int hadPeak      = tt->hadPeak();
+    int emADCPeak    = tt->emADCPeak();
+    int hadADCPeak   = tt->hadADCPeak();
+    int emLUTSlices  = (tt->emLUT()).size();
+    int emADCSlices  = (tt->emADC()).size();
+    int hadLUTSlices = (tt->hadLUT()).size();
+    int hadADCSlices = (tt->hadADC()).size();
+    if (m_forceSlicesLut) {
+      emPeak  = ModifySlices::peak(emPeak, emLUTSlices, m_forceSlicesLut);
+      hadPeak = ModifySlices::peak(hadPeak, hadLUTSlices, m_forceSlicesLut);
+      emLUTSlices  = m_forceSlicesLut;
+      hadLUTSlices = m_forceSlicesLut;
+    }
+    if (m_forceSlicesFadc) {
+      emADCPeak  = ModifySlices::peak(emADCPeak, emADCSlices,
+                                                            m_forceSlicesFadc);
+      hadADCPeak = ModifySlices::peak(hadADCPeak, hadADCSlices,
+                                                            m_forceSlicesFadc);
+      emADCSlices  = m_forceSlicesFadc;
+      hadADCSlices = m_forceSlicesFadc;
+    }
+    msg() << " EM:key/eta/phi/LUTpeak/FADCpeak/LUT/FADC/bcidLUT/bcidFADC/error: "
+          << mapIter->first << "/" << tt->eta() << "/" << tt->phi() << "/"
+	  << emPeak << "/" << emADCPeak << "/";
+    std::vector<int> lut;
+    std::vector<int> fadc;
+    std::vector<int> bcidLut;
+    std::vector<int> bcidFadc;
+    ModifySlices::data(tt->emLUT(),     lut,      emLUTSlices);
+    ModifySlices::data(tt->emADC(),     fadc,     emADCSlices);
+    ModifySlices::data(tt->emBCIDvec(), bcidLut,  emLUTSlices);
+    ModifySlices::data(tt->emBCIDext(), bcidFadc, emADCSlices);
+    printVec(lut);
+    printVec(fadc);
+    printVec(bcidLut);
+    printVec(bcidFadc);
+    msg() << MSG::hex << tt->emError() << MSG::dec << "/" << endreq
+          << "HAD:key/eta/phi/LUTpeak/FADCpeak/LUT/FADC/bcidLUT/bcidFADC/error: "
+          << mapIter->first << "/" << tt->eta() << "/" << tt->phi() << "/"
+	  << hadPeak << "/" << hadADCPeak << "/";
+    ModifySlices::data(tt->hadLUT(),     lut,      hadLUTSlices);
+    ModifySlices::data(tt->hadADC(),     fadc,     hadADCSlices);
+    ModifySlices::data(tt->hadBCIDvec(), bcidLut,  hadLUTSlices);
+    ModifySlices::data(tt->hadBCIDext(), bcidFadc, hadADCSlices);
+    printVec(lut);
+    printVec(fadc);
+    printVec(bcidLut);
+    printVec(bcidFadc);
+    msg() << MSG::hex << tt->hadError() << MSG::dec << "/" << endreq;
+  }
+}
+
+// Print a vector
+
+void PpmSubsetTester::printVec(const std::vector<int>& vec) const
+{
+  std::vector<int>::const_iterator pos;
+  for (pos = vec.begin(); pos != vec.end(); ++pos) {
+    if (pos != vec.begin()) msg() << ",";
+    msg() << *pos;
+  }
+  msg() << "/";
+}
+
+// Set up trigger tower map
+
+void PpmSubsetTester::setupTTMap(const TriggerTowerCollection*
+                                 const ttCollection,
+                                 const double etaMin, const double etaMax,
+				 const double phiMin, const double phiMax)
+{
+  m_ttMap.clear();
+  TriggerTowerCollection::const_iterator pos  = ttCollection->begin();
+  TriggerTowerCollection::const_iterator pose = ttCollection->end();
+  for (; pos != pose; ++pos) {
+    const LVL1::TriggerTower* const tt = *pos;
+    const double eta = tt->eta();
+    if (eta < etaMin || eta > etaMax) continue;
+    const double phi = tt->phi();
+    if (phi < phiMin || phi > phiMax) continue;
+    const unsigned int key = m_towerKey->ttKey(phi, eta);
+    m_ttMap.insert(std::make_pair(key, tt));
+  }
+}
+
+// Set up trigger tower map
+
+void PpmSubsetTester::setupTTMap(TriggerTowerCollection::const_iterator& beg,
+                                 TriggerTowerCollection::const_iterator& end)
+{
+  m_ttMap.clear();
+  TriggerTowerCollection::const_iterator pos  = beg;
+  TriggerTowerCollection::const_iterator pose = end;
+  for (; pos != pose; ++pos) {
+    const LVL1::TriggerTower* const tt = *pos;
+    const unsigned int key = m_towerKey->ttKey(tt->phi(), tt->eta());
+    m_ttMap.insert(std::make_pair(key, tt));
+  }
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/PpmSubsetTester.h b/Trigger/TrigT1/TrigT1CaloByteStream/test/PpmSubsetTester.h
new file mode 100644
index 0000000000000000000000000000000000000000..9122e48e5910b8da55a0fb1262736037037231d4
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/PpmSubsetTester.h
@@ -0,0 +1,83 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_PPMSUBSETTESTER_H
+#define TRIGT1CALOBYTESTREAM_PPMSUBSETTESTER_H
+
+#include <map>
+#include <string>
+
+#include "GaudiKernel/ToolHandle.h"
+
+#include "AthenaBaseComps/AthAlgorithm.h"
+#include "DataModel/DataVector.h"
+
+class ISvcLocator;
+class StatusCode;
+
+namespace LVL1 {
+  class TriggerTower;
+  class TriggerTowerKey;
+}
+
+namespace LVL1BS {
+
+class ITrigT1CaloDataAccess;
+
+/** Algorithm to test Trigger tower bytestream conversions.
+ *
+ *  Just prints out the contents of the TriggerTower objects.
+ *  Run before writing bytestream and after reading it and compare.
+ *
+ *  @author Peter Faulkner
+ */
+
+class PpmSubsetTester : public AthAlgorithm {
+
+ public:
+   PpmSubsetTester(const std::string& name, ISvcLocator* pSvcLocator);
+   virtual ~PpmSubsetTester();
+
+   virtual StatusCode initialize();
+   virtual StatusCode execute();
+   virtual StatusCode finalize();
+
+ private:
+   /// Trigger tower container
+   typedef DataVector<LVL1::TriggerTower> TriggerTowerCollection;
+   /// Trigger tower map
+   typedef std::map<unsigned int, const LVL1::TriggerTower*> TriggerTowerMap;
+
+   /// Print the trigger towers
+   void printTriggerTowers() const;
+
+   /// Print a vector
+   void printVec(const std::vector<int>& vec) const;
+
+   /// Set up trigger tower map
+   void setupTTMap(const TriggerTowerCollection* ttCollection,
+                   double etaMin, double etaMax, double phiMin, double phiMax);
+   /// Set up trigger tower map
+   void setupTTMap(TriggerTowerCollection::const_iterator& beg,
+                   TriggerTowerCollection::const_iterator& end);
+
+   /// TT subset access tool
+   ToolHandle<LVL1BS::ITrigT1CaloDataAccess> m_dataAccess;
+   /// Trigger tower key provider
+   LVL1::TriggerTowerKey* m_towerKey;
+   /// Trigger tower container StoreGate key
+   std::string m_triggerTowerLocation;
+   /// Force number of LUT slices
+   int m_forceSlicesLut;
+   /// Force number of FADC slices
+   int m_forceSlicesFadc;
+
+   /// Trigger tower map
+   TriggerTowerMap m_ttMap;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/PpmTester.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/test/PpmTester.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..e062118d45b40f31be15ba9dbe905d46916812a7
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/PpmTester.cxx
@@ -0,0 +1,180 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <utility>
+
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+#include "StoreGate/StoreGateSvc.h"
+
+#include "TrigT1CaloEvent/TriggerTower.h"
+#include "TrigT1CaloUtils/TriggerTowerKey.h"
+#include "TrigT1Interfaces/TrigT1CaloDefs.h"
+
+#include "../src/ModifySlices.h"
+
+#include "PpmTester.h"
+
+namespace LVL1BS {
+
+PpmTester::PpmTester(const std::string& name, ISvcLocator* pSvcLocator)
+                     : AthAlgorithm(name, pSvcLocator),
+		       m_towerKey(0)
+{
+  declareProperty("TriggerTowerLocation",
+         m_triggerTowerLocation = LVL1::TrigT1CaloDefs::TriggerTowerLocation);
+
+  declareProperty("ForceSlicesLUT",  m_forceSlicesLut  = 0);
+  declareProperty("ForceSlicesFADC", m_forceSlicesFadc = 0);
+}
+
+PpmTester::~PpmTester()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode PpmTester::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << /* version() */ PACKAGE_VERSION << endreq;
+
+  m_towerKey = new LVL1::TriggerTowerKey();
+
+  return StatusCode::SUCCESS;
+}
+
+// Execute
+
+StatusCode PpmTester::execute()
+{
+  if ( !msgLvl(MSG::INFO) ) return StatusCode::SUCCESS;
+  msg(MSG::INFO);
+
+  // Find trigger towers
+
+  const TriggerTowerCollection* ttCollection = 0;
+  StatusCode sc = evtStore()->retrieve(ttCollection, m_triggerTowerLocation);
+  if (sc.isFailure() || !ttCollection || ttCollection->empty()) {
+    msg() << "No Trigger Towers found" << endreq;
+    return StatusCode::SUCCESS;
+  }
+
+  // Order by key
+
+  setupTTMap(ttCollection);
+
+  // Print the trigger towers
+
+  printTriggerTowers();
+
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode PpmTester::finalize()
+{
+  delete m_towerKey;
+
+  return StatusCode::SUCCESS;
+}
+
+// Print the trigger towers
+
+void PpmTester::printTriggerTowers() const
+{
+  msg() << "Number of Trigger Towers = " << m_ttMap.size() << endreq;
+  TriggerTowerMap::const_iterator mapIter = m_ttMap.begin();
+  TriggerTowerMap::const_iterator mapEnd  = m_ttMap.end();
+  for (; mapIter != mapEnd; ++mapIter) {
+    const LVL1::TriggerTower* const tt = mapIter->second;
+    int emPeak       = tt->emPeak();
+    int hadPeak      = tt->hadPeak();
+    int emADCPeak    = tt->emADCPeak();
+    int hadADCPeak   = tt->hadADCPeak();
+    int emLUTSlices  = (tt->emLUT()).size();
+    int emADCSlices  = (tt->emADC()).size();
+    int hadLUTSlices = (tt->hadLUT()).size();
+    int hadADCSlices = (tt->hadADC()).size();
+    if (m_forceSlicesLut) {
+      emPeak  = ModifySlices::peak(emPeak, emLUTSlices, m_forceSlicesLut);
+      hadPeak = ModifySlices::peak(hadPeak, hadLUTSlices, m_forceSlicesLut);
+      emLUTSlices  = m_forceSlicesLut;
+      hadLUTSlices = m_forceSlicesLut;
+    }
+    if (m_forceSlicesFadc) {
+      emADCPeak  = ModifySlices::peak(emADCPeak, emADCSlices,
+                                                            m_forceSlicesFadc);
+      hadADCPeak = ModifySlices::peak(hadADCPeak, hadADCSlices,
+                                                            m_forceSlicesFadc);
+      emADCSlices  = m_forceSlicesFadc;
+      hadADCSlices = m_forceSlicesFadc;
+    }
+    msg() << " EM:key/eta/phi/LUTpeak/FADCpeak/LUT/FADC/bcidLUT/bcidFADC/error: "
+          << mapIter->first << "/" << tt->eta() << "/" << tt->phi() << "/"
+	  << emPeak << "/" << emADCPeak << "/";
+    std::vector<int> lut;
+    std::vector<int> fadc;
+    std::vector<int> bcidLut;
+    std::vector<int> bcidFadc;
+    ModifySlices::data(tt->emLUT(),     lut,      emLUTSlices);
+    ModifySlices::data(tt->emADC(),     fadc,     emADCSlices);
+    ModifySlices::data(tt->emBCIDvec(), bcidLut,  emLUTSlices);
+    ModifySlices::data(tt->emBCIDext(), bcidFadc, emADCSlices);
+    printVec(lut);
+    printVec(fadc);
+    printVec(bcidLut);
+    printVec(bcidFadc);
+    msg() << MSG::hex << tt->emError() << MSG::dec << "/" << endreq;
+    msg() << "HAD:key/eta/phi/LUTpeak/FADCpeak/LUT/FADC/bcidLUT/bcidFADC/error: "
+          << mapIter->first << "/" << tt->eta() << "/" << tt->phi() << "/"
+	  << hadPeak << "/" << hadADCPeak << "/";
+    ModifySlices::data(tt->hadLUT(),     lut,      hadLUTSlices);
+    ModifySlices::data(tt->hadADC(),     fadc,     hadADCSlices);
+    ModifySlices::data(tt->hadBCIDvec(), bcidLut,  hadLUTSlices);
+    ModifySlices::data(tt->hadBCIDext(), bcidFadc, hadADCSlices);
+    printVec(lut);
+    printVec(fadc);
+    printVec(bcidLut);
+    printVec(bcidFadc);
+    msg() << MSG::hex << tt->hadError() << MSG::dec << "/" << endreq;
+  }
+}
+
+// Print a vector
+
+void PpmTester::printVec(const std::vector<int>& vec) const
+{
+  std::vector<int>::const_iterator pos;
+  for (pos = vec.begin(); pos != vec.end(); ++pos) {
+    if (pos != vec.begin()) msg() << ",";
+    msg() << *pos;
+  }
+  msg() << "/";
+}
+
+// Set up trigger tower map
+
+void PpmTester::setupTTMap(const TriggerTowerCollection* const ttCollection)
+{
+  m_ttMap.clear();
+  TriggerTowerCollection::const_iterator pos  = ttCollection->begin();
+  TriggerTowerCollection::const_iterator pose = ttCollection->end();
+  for (; pos != pose; ++pos) {
+    const LVL1::TriggerTower* const tt = *pos;
+    // doesn't work for spare channels (unphysical eta/phi):
+    //const unsigned int key = m_towerKey->ttKey(tt->phi(), tt->eta());
+    const unsigned int key = tt->key();
+    m_ttMap.insert(std::make_pair(key, tt));
+  }
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/PpmTester.h b/Trigger/TrigT1/TrigT1CaloByteStream/test/PpmTester.h
new file mode 100755
index 0000000000000000000000000000000000000000..abe56cf03dd8833a5c2372961743f2420a0de4ae
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/PpmTester.h
@@ -0,0 +1,73 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_PPMTESTER_H
+#define TRIGT1CALOBYTESTREAM_PPMTESTER_H
+
+#include <map>
+#include <string>
+
+#include "AthenaBaseComps/AthAlgorithm.h"
+#include "DataModel/DataVector.h"
+
+class ISvcLocator;
+class StatusCode;
+
+namespace LVL1 {
+  class TriggerTower;
+  class TriggerTowerKey;
+}
+
+namespace LVL1BS {
+
+/** Algorithm to test Trigger tower bytestream conversions.
+ *
+ *  Just prints out the contents of the TriggerTower objects.
+ *  Run before writing bytestream and after reading it and compare.
+ *
+ *  @author Peter Faulkner
+ */
+
+class PpmTester : public AthAlgorithm {
+
+ public:
+   PpmTester(const std::string& name, ISvcLocator* pSvcLocator);
+   virtual ~PpmTester();
+
+   virtual StatusCode initialize();
+   virtual StatusCode execute();
+   virtual StatusCode finalize();
+
+ private:
+   /// Trigger tower container
+   typedef DataVector<LVL1::TriggerTower> TriggerTowerCollection;
+   /// Trigger tower map
+   typedef std::map<unsigned int, const LVL1::TriggerTower*> TriggerTowerMap;
+
+   /// Print the trigger towers
+   void printTriggerTowers() const;
+
+   /// Print a vector
+   void printVec(const std::vector<int>& vec) const;
+
+   /// Set up trigger tower map
+   void setupTTMap(const TriggerTowerCollection* jeCollection);
+
+   /// Trigger tower key provider
+   LVL1::TriggerTowerKey* m_towerKey;
+   /// Trigger tower container StoreGate key
+   std::string m_triggerTowerLocation;
+   /// Force number of LUT slices
+   int m_forceSlicesLut;
+   /// Force number of FADC slices
+   int m_forceSlicesFadc;
+
+   /// Trigger tower map
+   TriggerTowerMap m_ttMap;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/RodTester.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/test/RodTester.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..dd6a6abb9fd476c748e4ccf8daa2ff3aeacafca9
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/RodTester.cxx
@@ -0,0 +1,139 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include <stdint.h>
+
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/StatusCode.h"
+#include "StoreGate/StoreGateSvc.h"
+
+#include "TrigT1Interfaces/TrigT1CaloDefs.h"
+#include "TrigT1CaloEvent/RODHeader.h"
+
+#include "RodTester.h"
+
+namespace LVL1BS {
+
+RodTester::RodTester(const std::string& name, ISvcLocator* pSvcLocator)
+                     : AthAlgorithm(name, pSvcLocator)
+{
+  declareProperty("RodHeaderLocation", m_rodHeaderLocation =
+                   LVL1::TrigT1CaloDefs::RODHeaderLocation);
+
+  m_flags.push_back("");
+  m_flags.push_back("PP");
+  m_flags.push_back("CP");
+  m_flags.push_back("CPRoI");
+  m_flags.push_back("JEP");
+  m_flags.push_back("JEPRoI");
+  m_flags.push_back("CPRoIB");
+  m_flags.push_back("JEPRoIB");
+}
+
+RodTester::~RodTester()
+{
+}
+
+// Initialize
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "unknown"
+#endif
+
+StatusCode RodTester::initialize()
+{
+  msg(MSG::INFO) << "Initializing " << name() << " - package version "
+                 << /* version() */ PACKAGE_VERSION << endreq;
+
+  return StatusCode::SUCCESS;
+}
+
+// Execute
+
+StatusCode RodTester::execute()
+{
+  if ( !msgLvl(MSG::INFO) ) return StatusCode::SUCCESS;
+  msg(MSG::INFO);
+
+  // Loop over all possible collections
+
+  std::vector<std::string>::const_iterator flagIter = m_flags.begin();
+  std::vector<std::string>::const_iterator flagEnd  = m_flags.end();
+  for (; flagIter != flagEnd; ++flagIter) {
+    msg() << "================================================="
+          << endreq;
+    const std::string location(m_rodHeaderLocation + *flagIter);
+    const RodHeaderCollection* rods = 0;
+    StatusCode sc = evtStore()->retrieve(rods, location);
+    if (sc.isFailure() || !rods || rods->empty()) {
+      msg() << "No ROD headers found for " << location << endreq;
+    } else {
+
+      // Print the ROD headers
+
+      msg() << "Number of ROD headers in collection "
+            << location << " is " << rods->size() << MSG::hex << endreq;
+      RodHeaderCollection::const_iterator rodIter = rods->begin();
+      RodHeaderCollection::const_iterator rodEnd  = rods->end();
+      for (; rodIter != rodEnd; ++rodIter) {
+        const LVL1::RODHeader* const header = *rodIter;
+	msg() << "-------------------------------------------------"
+	      << endreq
+	      << "version/majorVersion/minorVersion: "
+	      << header->version() << "/" << header->majorVersion() << "/"
+	      << header->minorVersion() << endreq
+              << "sourceID/subDetectorID/moduleID/crate/sLink/dataType: "
+	      << header->sourceID() << "/" << header->subDetectorID() << "/"
+	      << header->moduleID() << "/" << header->crate() << "/"
+	      << header->sLink() << "/" << header->dataType() << endreq
+              << "run/runType/runNumber: "
+	      << header->run() << "/" << header->runType() << "/"
+	      << header->runNumber() << endreq
+              << "extendedL1ID/ecrID/l1ID: "
+	      << header->extendedL1ID() << "/" << header->ecrID() << "/"
+	      << header->l1ID() << endreq
+              << "bunchCrossing/l1TriggerType: "
+	      << header->bunchCrossing() << "/" << header->l1TriggerType() << "/"
+	      << endreq
+              << "detEventType/orbitCount/stepNumber/stepType: "
+	      << header->detEventType() << "/" << header->orbitCount() << "/"
+	      << header->stepNumber() << "/" << header->stepType() << endreq
+	      << "payloadSize: " << header->payloadSize() << endreq;
+	const std::vector<uint32_t>& words(header->statusWords());
+        msg() << "statusWords(" << words.size() << "): ";
+	std::vector<uint32_t>::const_iterator wordIter = words.begin();
+	std::vector<uint32_t>::const_iterator wordEnd  = words.end();
+	for (; wordIter != wordEnd; ++wordIter) {
+	  msg() << *wordIter << " ";
+        }
+	msg() << endreq
+	      << "bcnMismatch/gLinkTimeout/dataTransportError/rodFifoOverflow: "
+	      << header->bcnMismatch() << "/" << header->gLinkTimeout() << "/"
+	      << header->dataTransportError() << "/" << header->rodFifoOverflow()
+	      << endreq
+              << "lvdsLinkError/cmmParityError/gLinkError: "
+	      << header->lvdsLinkError() << "/" << header->cmmParityError() << "/"
+	      << header->gLinkError() << endreq
+              << "limitedRoISet/triggerTypeTimeout: "
+	      << header->limitedRoISet() << "/" << header->triggerTypeTimeout()
+	      << endreq;
+      }
+      msg() << MSG::dec;
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// Finalize
+
+StatusCode RodTester::finalize()
+{
+
+  return StatusCode::SUCCESS;
+}
+
+} // end namespace
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/RodTester.h b/Trigger/TrigT1/TrigT1CaloByteStream/test/RodTester.h
new file mode 100644
index 0000000000000000000000000000000000000000..41f03ac3001e1918a9f2fd5577d6984b08236928
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/RodTester.h
@@ -0,0 +1,52 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGT1CALOBYTESTREAM_RODTESTER_H
+#define TRIGT1CALOBYTESTREAM_RODTESTER_H
+
+#include <string>
+#include <vector>
+
+#include "AthenaBaseComps/AthAlgorithm.h"
+#include "DataModel/DataVector.h"
+
+class ISvcLocator;
+class StatusCode;
+
+namespace LVL1 {
+  class RODHeader;
+}
+
+namespace LVL1BS {
+
+/** Algorithm to test ROD header bytestream conversions.
+ *
+ *  Just prints out the contents of the RODHeader objects
+ *
+ *  @author Peter Faulkner
+ */
+
+class RodTester : public AthAlgorithm {
+
+ public:
+   RodTester(const std::string& name, ISvcLocator* pSvcLocator);
+   virtual ~RodTester();
+
+   virtual StatusCode initialize();
+   virtual StatusCode execute();
+   virtual StatusCode finalize();
+
+ private:
+   typedef DataVector<LVL1::RODHeader> RodHeaderCollection;
+
+   /// RODHeader container StoreGate key
+   std::string m_rodHeaderLocation;
+   /// Variant collection flags
+   std::vector<std::string> m_flags;
+
+};
+
+} // end namespace
+
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/TrigT1CaloByteStream_entries.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/test/TrigT1CaloByteStream_entries.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..b202aaedede40602bfac87d378b7dc5a68b781fb
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/TrigT1CaloByteStream_entries.cxx
@@ -0,0 +1,264 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "DataModel/DataVector.h"
+#include "GaudiKernel/DeclareFactoryEntries.h"
+
+// Post-LS1
+#include "TrigT1CaloEvent/CMXCPTob.h"
+#include "TrigT1CaloEvent/CMXCPHits.h"
+#include "TrigT1CaloEvent/CMXEtSums.h"
+#include "TrigT1CaloEvent/CMXJetTob.h"
+#include "TrigT1CaloEvent/CMXJetHits.h"
+#include "TrigT1CaloEvent/CMXRoI.h"
+#include "TrigT1CaloEvent/JEMTobRoI.h"
+// Pre-LS1
+#include "TrigT1CaloEvent/CPMHits.h"
+#include "TrigT1CaloEvent/CMMCPHits.h"
+#include "TrigT1CaloEvent/CMMEtSums.h"
+#include "TrigT1CaloEvent/CMMJetHits.h"
+#include "TrigT1CaloEvent/CMMRoI.h"
+#include "TrigT1CaloEvent/JEMHits.h"
+#include "TrigT1CaloEvent/JEMRoI.h"
+// Both
+#include "TrigT1CaloEvent/CPMTower.h"
+#include "TrigT1CaloEvent/JEMEtSums.h"
+#include "TrigT1CaloEvent/JetElement.h"
+
+// Post-LS1
+#include "../src/CpByteStreamV2Cnv.h"
+#include "../src/CpmRoiByteStreamV2Cnv.h"
+#include "../src/CpReadByteStreamV2Cnv.h"
+#include "../src/JepByteStreamV2Cnv.h"
+#include "../src/JepReadByteStreamV2Cnv.h"
+#include "../src/JepRoiByteStreamV2Cnv.h"
+#include "../src/JepRoiReadByteStreamV2Cnv.h"
+// Pre-LS1
+#include "../src/CpByteStreamV1Cnv.h"
+#include "../src/CpmRoiByteStreamV1Cnv.h"
+#include "../src/CpReadByteStreamV1Cnv.h"
+#include "../src/JepByteStreamV1Cnv.h"
+#include "../src/JepReadByteStreamV1Cnv.h"
+#include "../src/JepRoiByteStreamV1Cnv.h"
+#include "../src/JepRoiReadByteStreamV1Cnv.h"
+// Both
+#include "../src/CpReadByteStreamV1V2Cnv.h"
+#include "../src/JepReadByteStreamV1V2Cnv.h"
+#include "../src/PpmByteStreamCnv.h"
+#include "../src/RodHeaderByteStreamCnv.h"
+#include "../src/L1CaloErrorByteStreamCnv.h"
+
+// Post-LS1
+#include "../src/CpByteStreamV2Tool.h"
+#include "../src/CpmRoiByteStreamV2Tool.h"
+#include "../src/JepByteStreamV2Tool.h"
+#include "../src/JepRoiByteStreamV2Tool.h"
+// Pre-LS1
+#include "../src/CpByteStreamV1Tool.h"
+#include "../src/CpmRoiByteStreamV1Tool.h"
+#include "../src/JepByteStreamV1Tool.h"
+#include "../src/JepRoiByteStreamV1Tool.h"
+// Both
+#include "../src/PpmByteStreamTool.h"
+#include "../src/RodHeaderByteStreamTool.h"
+#include "../src/L1CaloErrorByteStreamTool.h"
+
+#include "../src/PpmByteStreamSubsetTool.h"
+#include "../src/TriggerTowerSelectionTool.h"
+#include "../src/TrigT1CaloDataAccess.h"
+
+// Post-LS1
+#include "CpmTesterV2.h"
+#include "JemTesterV2.h"
+#include "CpmErrors.h"
+#include "JemErrors.h"
+// Pre-LS1
+#include "CpmTesterV1.h"
+#include "JemTesterV1.h"
+// Both
+#include "PpmMappingTester.h"
+#include "PpmSubsetTester.h"
+#include "PpmTester.h"
+#include "RodTester.h"
+#include "ErrorTester.h"
+
+namespace LVL1BS {
+
+// Post-LS1
+typedef DataVector<LVL1::CMXCPTob>   CMXCPTobCollection;
+typedef DataVector<LVL1::CMXCPHits>  CMXCPHitsCollection;
+typedef DataVector<LVL1::CMXJetTob>  CMXJetTobCollection;
+typedef DataVector<LVL1::CMXJetHits> CMXJetHitsCollection;
+typedef DataVector<LVL1::CMXEtSums>  CMXEtSumsCollection;
+typedef DataVector<LVL1::JEMTobRoI>  JEMTobRoICollection;
+// Pre-LS1
+typedef DataVector<LVL1::CPMHits>    CPMHitsCollection;
+typedef DataVector<LVL1::CMMCPHits>  CMMCPHitsCollection;
+typedef DataVector<LVL1::CMMJetHits> CMMJetHitsCollection;
+typedef DataVector<LVL1::CMMEtSums>  CMMEtSumsCollection;
+typedef DataVector<LVL1::JEMHits>    JEMHitsCollection;
+typedef DataVector<LVL1::JEMRoI>     JEMRoICollection;
+// Both
+typedef DataVector<LVL1::CPMTower>   CPMTowerCollection;
+typedef DataVector<LVL1::JetElement> JetElementCollection;
+typedef DataVector<LVL1::JEMEtSums>  JEMEtSumsCollection;
+
+// Post-LS1
+typedef CpReadByteStreamV2Cnv<CMXCPTobCollection>      CpReadCRByteStreamV2CnvT;
+typedef CpReadByteStreamV2Cnv<CMXCPHitsCollection>     CpReadCCByteStreamV2CnvT;
+typedef JepReadByteStreamV2Cnv<CMXJetTobCollection>    JepReadCTByteStreamV2CnvT;
+typedef JepReadByteStreamV2Cnv<CMXJetHitsCollection>   JepReadCJByteStreamV2CnvT;
+typedef JepReadByteStreamV2Cnv<CMXEtSumsCollection>    JepReadCEByteStreamV2CnvT;
+typedef JepRoiReadByteStreamV2Cnv<JEMTobRoICollection> JepRoiReadJRByteStreamV2CnvT;
+typedef JepRoiReadByteStreamV2Cnv<LVL1::CMXRoI>        JepRoiReadCRByteStreamV2CnvT;
+// Pre-LS1
+typedef CpReadByteStreamV1Cnv<CPMHitsCollection>       CpReadCHByteStreamV1CnvT;
+typedef CpReadByteStreamV1Cnv<CMMCPHitsCollection>     CpReadCCByteStreamV1CnvT;
+typedef JepReadByteStreamV1Cnv<CMMJetHitsCollection>   JepReadCJByteStreamV1CnvT;
+typedef JepReadByteStreamV1Cnv<CMMEtSumsCollection>    JepReadCEByteStreamV1CnvT;
+typedef JepReadByteStreamV1Cnv<JEMHitsCollection>      JepReadJHByteStreamV1CnvT;
+typedef JepRoiReadByteStreamV1Cnv<JEMRoICollection>    JepRoiReadJRByteStreamV1CnvT;
+typedef JepRoiReadByteStreamV1Cnv<LVL1::CMMRoI>        JepRoiReadCRByteStreamV1CnvT;
+// Both
+typedef JepReadByteStreamV1V2Cnv<JetElementCollection> JepReadJEByteStreamV1V2CnvT;
+typedef JepReadByteStreamV1V2Cnv<JEMEtSumsCollection>  JepReadESByteStreamV1V2CnvT;
+}
+
+// declare 
+// Post-LS1
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, CpByteStreamV2Cnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, CpmRoiByteStreamV2Cnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, CpReadCRByteStreamV2CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, CpReadCCByteStreamV2CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepByteStreamV2Cnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepRoiByteStreamV2Cnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepReadCTByteStreamV2CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepReadCJByteStreamV2CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepReadCEByteStreamV2CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepRoiReadJRByteStreamV2CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepRoiReadCRByteStreamV2CnvT )
+// Pre-LS1
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, CpByteStreamV1Cnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, CpmRoiByteStreamV1Cnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, CpReadCHByteStreamV1CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, CpReadCCByteStreamV1CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepByteStreamV1Cnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepRoiByteStreamV1Cnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepReadCJByteStreamV1CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepReadCEByteStreamV1CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepReadJHByteStreamV1CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepRoiReadJRByteStreamV1CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepRoiReadCRByteStreamV1CnvT )
+// Both
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, CpReadByteStreamV1V2Cnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepReadJEByteStreamV1V2CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, JepReadESByteStreamV1V2CnvT )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, PpmByteStreamCnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, RodHeaderByteStreamCnv )
+DECLARE_NAMESPACE_CONVERTER_FACTORY( LVL1BS, L1CaloErrorByteStreamCnv )
+
+// Post-LS1
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, CpByteStreamV2Tool )
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, CpmRoiByteStreamV2Tool )
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, JepByteStreamV2Tool )
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, JepRoiByteStreamV2Tool )
+// Pre-LS1
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, CpByteStreamV1Tool )
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, CpmRoiByteStreamV1Tool )
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, JepByteStreamV1Tool )
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, JepRoiByteStreamV1Tool )
+// Both
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, PpmByteStreamTool )
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, RodHeaderByteStreamTool )
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, L1CaloErrorByteStreamTool )
+
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, PpmByteStreamSubsetTool )
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, TriggerTowerSelectionTool )
+DECLARE_NAMESPACE_TOOL_FACTORY( LVL1BS, TrigT1CaloDataAccess )
+
+// Post-LS1
+DECLARE_NAMESPACE_ALGORITHM_FACTORY( LVL1BS, CpmTesterV2 )
+DECLARE_NAMESPACE_ALGORITHM_FACTORY( LVL1BS, JemTesterV2 )
+DECLARE_NAMESPACE_ALGORITHM_FACTORY( LVL1BS, CpmErrors )
+DECLARE_NAMESPACE_ALGORITHM_FACTORY( LVL1BS, JemErrors )
+// Pre-LS1
+DECLARE_NAMESPACE_ALGORITHM_FACTORY( LVL1BS, CpmTesterV1 )
+DECLARE_NAMESPACE_ALGORITHM_FACTORY( LVL1BS, JemTesterV1 )
+// Both
+DECLARE_NAMESPACE_ALGORITHM_FACTORY( LVL1BS, PpmTester )
+DECLARE_NAMESPACE_ALGORITHM_FACTORY( LVL1BS, RodTester )
+DECLARE_NAMESPACE_ALGORITHM_FACTORY( LVL1BS, ErrorTester )
+DECLARE_NAMESPACE_ALGORITHM_FACTORY( LVL1BS, PpmSubsetTester )
+DECLARE_NAMESPACE_ALGORITHM_FACTORY( LVL1BS, PpmMappingTester )
+
+DECLARE_FACTORY_ENTRIES( TrigT1CaloByteStream )
+{
+  // Post-LS1
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, CpByteStreamV2Cnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, CpmRoiByteStreamV2Cnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, CpReadCRByteStreamV2CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, CpReadCCByteStreamV2CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepByteStreamV2Cnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepRoiByteStreamV2Cnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepReadCTByteStreamV2CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepReadCJByteStreamV2CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepReadCEByteStreamV2CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepRoiReadJRByteStreamV2CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepRoiReadCRByteStreamV2CnvT )
+  // Pre-LS1
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, CpByteStreamV1Cnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, CpmRoiByteStreamV1Cnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, CpReadCHByteStreamV1CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, CpReadCCByteStreamV1CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepByteStreamV1Cnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepRoiByteStreamV1Cnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepReadCJByteStreamV1CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepReadCEByteStreamV1CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepReadJHByteStreamV1CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepRoiReadJRByteStreamV1CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepRoiReadCRByteStreamV1CnvT )
+  // Both
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, CpReadByteStreamV1V2Cnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepReadJEByteStreamV1V2CnvT )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, JepReadESByteStreamV1V2CnvT )
+  // Both
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, PpmByteStreamCnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, RodHeaderByteStreamCnv )
+  DECLARE_NAMESPACE_CONVERTER( LVL1BS, L1CaloErrorByteStreamCnv )
+
+  // Post-LS1
+  DECLARE_NAMESPACE_TOOL( LVL1BS, CpByteStreamV2Tool )
+  DECLARE_NAMESPACE_TOOL( LVL1BS, CpmRoiByteStreamV2Tool )
+  DECLARE_NAMESPACE_TOOL( LVL1BS, JepByteStreamV2Tool )
+  DECLARE_NAMESPACE_TOOL( LVL1BS, JepRoiByteStreamV2Tool )
+  // Pre-LS1
+  DECLARE_NAMESPACE_TOOL( LVL1BS, CpByteStreamV1Tool )
+  DECLARE_NAMESPACE_TOOL( LVL1BS, CpmRoiByteStreamV1Tool )
+  DECLARE_NAMESPACE_TOOL( LVL1BS, JepByteStreamV1Tool )
+  DECLARE_NAMESPACE_TOOL( LVL1BS, JepRoiByteStreamV1Tool )
+  // Both
+  DECLARE_NAMESPACE_TOOL( LVL1BS, PpmByteStreamTool )
+  DECLARE_NAMESPACE_TOOL( LVL1BS, RodHeaderByteStreamTool )
+  DECLARE_NAMESPACE_TOOL( LVL1BS, L1CaloErrorByteStreamTool )
+
+  DECLARE_NAMESPACE_TOOL( LVL1BS, PpmByteStreamSubsetTool )
+  DECLARE_NAMESPACE_TOOL( LVL1BS, TriggerTowerSelectionTool )
+  DECLARE_NAMESPACE_TOOL( LVL1BS, TrigT1CaloDataAccess )
+
+  // Post-LS1
+  DECLARE_NAMESPACE_ALGORITHM( LVL1BS, CpmTesterV2 )
+  DECLARE_NAMESPACE_ALGORITHM( LVL1BS, JemTesterV2 )
+  DECLARE_NAMESPACE_ALGORITHM( LVL1BS, CpmErrors )
+  DECLARE_NAMESPACE_ALGORITHM( LVL1BS, JemErrors )
+  // Pre-LS1
+  DECLARE_NAMESPACE_ALGORITHM( LVL1BS, CpmTesterV1 )
+  DECLARE_NAMESPACE_ALGORITHM( LVL1BS, JemTesterV1 )
+  // Both
+  DECLARE_NAMESPACE_ALGORITHM( LVL1BS, PpmTester )
+  DECLARE_NAMESPACE_ALGORITHM( LVL1BS, RodTester )
+  DECLARE_NAMESPACE_ALGORITHM( LVL1BS, ErrorTester )
+  DECLARE_NAMESPACE_ALGORITHM( LVL1BS, PpmSubsetTester )
+  DECLARE_NAMESPACE_ALGORITHM( LVL1BS, PpmMappingTester )
+}
diff --git a/Trigger/TrigT1/TrigT1CaloByteStream/test/TrigT1CaloByteStream_load.cxx b/Trigger/TrigT1/TrigT1CaloByteStream/test/TrigT1CaloByteStream_load.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..caf9bbc06f9f48f587d01be9020adc2cda56aca0
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloByteStream/test/TrigT1CaloByteStream_load.cxx
@@ -0,0 +1,9 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "GaudiKernel/LoadFactoryEntries.h"
+
+LOAD_FACTORY_ENTRIES(TrigT1CaloByteStream)
+