diff --git a/Trigger/TrigT1/TrigT1CaloMonitoring/python/LVL1CaloMonitoringConfig.py b/Trigger/TrigT1/TrigT1CaloMonitoring/python/LVL1CaloMonitoringConfig.py
index 62ed3b98a2f583a388db21125f5bd44f8b139750..4cf615e6d1e13a1d70adbf3cd694e3148d413885 100644
--- a/Trigger/TrigT1/TrigT1CaloMonitoring/python/LVL1CaloMonitoringConfig.py
+++ b/Trigger/TrigT1/TrigT1CaloMonitoring/python/LVL1CaloMonitoringConfig.py
@@ -20,7 +20,9 @@ def LVL1CaloMonitoringConfig(flags):
     # do not run on MC or  RAW->ESD(tier0), or AOD-only
     if isData and flags.DQ.Environment not in ('tier0Raw', 'AOD'):
         from TrigT1CaloMonitoring.CpmMonitorAlgorithm import CpmMonitoringConfig
+        from TrigT1CaloMonitoring.PprMonitorAlgorithm import PprMonitoringConfig
 
         result.merge(CpmMonitoringConfig(flags))
+        result.merge(PprMonitoringConfig(flags))
 
     return result
diff --git a/Trigger/TrigT1/TrigT1CaloMonitoring/python/PprMonitorAlgorithm.py b/Trigger/TrigT1/TrigT1CaloMonitoring/python/PprMonitorAlgorithm.py
new file mode 100644
index 0000000000000000000000000000000000000000..e45055c60fd38d68c7325a3e925f72e1bf23c6f0
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloMonitoring/python/PprMonitorAlgorithm.py
@@ -0,0 +1,157 @@
+#
+#  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+#
+
+def PprMonitoringConfig(inputFlags):
+    '''Function to configure LVL1 Ppr algorithm in the monitoring system.'''
+
+    import math 
+    # get the component factory - used for getting the algorithms
+    from AthenaConfiguration.ComponentFactory import CompFactory
+    from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
+    result = ComponentAccumulator()
+
+    # any things that need setting up for job e.g.
+    #from AtlasGeoModel.AtlasGeoModelConfig import AtlasGeometryCfg
+    #result.merge(AtlasGeometryCfg(inputFlags))
+
+    # make the athena monitoring helper
+    from AthenaMonitoring import AthMonitorCfgHelper
+    helper = AthMonitorCfgHelper(inputFlags,'PprMonitoringCfg')
+
+    # get any algorithms
+    PprMonAlg = helper.addAlgorithm(CompFactory.PprMonitorAlgorithm,'PprMonAlg')
+
+    # add any steering
+    groupName = 'PprMonitor' # the monitoring group name is also used for the package name
+    PprMonAlg.PackageName = groupName
+
+    # Steering properties 
+    threshADC = 50
+    PprMonAlg.TT_ADC_HitMap_Thresh = threshADC  # ADC cut for hit maps
+ 
+    # Histogram paths
+    mainDir = 'L1Calo'
+    trigPath = 'PPM/'
+
+    # add monitoring algorithm to group, with group name and main directory 
+    myGroup = helper.addGroup(PprMonAlg, groupName , mainDir)
+
+
+    # Trigger tower plots: eta-phi granularity
+    etabins = [-4.9,-4.475,-4.050,-3.625,-3.2,-3.1,-2.9,
+                     -2.7,-2.5,-2.4,-2.3,-2.2,-2.1,-2.0,-1.9,
+                     -1.8,-1.7,-1.6,-1.5,-1.4,-1.3,-1.2,-1.1,
+                     -1.0,-0.9,-0.8,-0.7,-0.6,-0.5,-0.4,-0.3,
+                     -0.2,-0.1,0.0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,
+                     0.8,0.9,1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7,
+                     1.8,1.9,2.0,2.1,2.2,2.3,2.4,2.5,2.7,2.9,
+                     3.1,3.2,3.625,4.050,4.475,4.9]
+    phibins = 64
+    phimin = 0
+    phimax_2d = 64
+    phimax_1d = 2.*math.pi 
+    maxEnergyRange = 256
+
+   
+    #######################   
+    # PPM inputs (LUT-CP) #
+    #######################
+    histPath = trigPath+'/LUT-CP'
+    
+    # EM distributions
+    myGroup.defineHistogram('etaTT_EM;h_ppm_em_1d_tt_lutcp_Eta', title='EM LUT-CP: Distribution of peak in #eta; #eta', type='TH1F', path=histPath+'/Distributions', xbins=etabins, cutmask='mask_EM_cpET_0_noPhi')
+
+    myGroup.defineHistogram('phiTT_1d_EM;h_ppm_em_1d_tt_lutcp_Phi', title='EM LUT-CP: Distribution of peak in #phi; #phi', type='TH1F', path=histPath+'/Distributions', xbins=phibins, xmin=phimin, xmax=phimax_1d, cutmask='mask_EM_cpET_0_phiBins')
+ 
+    myGroup.defineHistogram('cpET_EM;h_ppm_em_1d_tt_lutcp_Et', title='EM LUT-CP: Distribution of peak; EM LUT peak [GeV/2]', type='TH1F', path=histPath+'/Distributions', xbins=maxEnergyRange-1, xmin=1, xmax=maxEnergyRange, cutmask='mask_EM_cpET_0_noPhi')
+
+
+    # Eta-phi maps
+    myGroup.defineHistogram('etaTT_EM,phiTT_2d_EM;h_ppm_em_2d_etaPhi_tt_lutcp_AverageEt', title='EM Average LUT-CP Et for Et > 5 GeV/2', type='TH2F', path=histPath+'/EtaPhiMaps', xbins=etabins, ybins=phibins, ymin=phimin, ymax=phimax_2d, cutmask='mask_EM_cpET_5_phiBins', weight='cpET_EM')
+    
+ 
+    ########################   
+    # PPM inputs (LUT-JEP) #
+    ########################
+    histPath = trigPath+'/LUT-JEP'
+    
+    # EM distributions
+    myGroup.defineHistogram('etaTT_EM;h_ppm_em_1d_tt_lutjep_Eta', title='EM LUT-JEP: Distribution of peak in #eta', type='TH1F', path=histPath+'/Distributions', xbins=etabins, cutmask='mask_EM_jepET_0_noPhi')
+
+    myGroup.defineHistogram('phiTT_1d_EM;h_ppm_em_1d_tt_lutjep_Phi', title='EM LUT-JEP: Distribution of peak in #phi; #phi', type='TH1F', path=histPath+'/Distributions', xbins=phibins, xmin=phimin, xmax=phimax_1d, cutmask='mask_EM_jepET_0_phiBins')
+
+    myGroup.defineHistogram('jepET_EM;h_ppm_em_1d_tt_lutjep_Et', title='EM LUT-JEP: Distribution of peak; EM LUT peak [GeV]', type='TH1F', path=histPath+'/Distributions', xbins=maxEnergyRange-1, xmin=1, xmax=maxEnergyRange, cutmask='mask_EM_jepET_0_noPhi')   
+
+    # Eta-phi maps
+    myGroup.defineHistogram('etaTT_EM,phiTT_2d_EM;h_ppm_em_2d_etaPhi_tt_lutjep_AverageEt', title='EM Average LUT-JEP Et for Et > 5 GeV', type='TH2F', path=histPath+'/EtaPhiMaps', xbins=etabins, ybins=phibins, ymin=phimin, ymax=phimax_2d, cutmask='mask_EM_jepET_5_phiBins', weight='jepET_EM')
+
+    
+
+    ####################
+    # PPM inputs (ADC) #
+    ####################
+    histPath = trigPath+'/ADC'
+    
+    # EM tower maps 
+    myGroup.defineHistogram('etaTT_EM,phiTT_2d_EM;h_ppm_em_2d_etaPhi_tt_adc_HitMap', title='#eta - #phi map of EM FADC > ' +str(threshADC)+ ' for triggered timeslice; Tower #eta; Tower #phi', type='TH2F', path=histPath+'/EtaPhiMaps', xbins=etabins, ybins=phibins, ymin=phimin, ymax=phimax_2d, cutmask='mask_EM_timeslice') 
+   
+    myGroup.defineHistogram('etaTT_EM,phiTT_2d_EM;h_ppm_em_2d_etaPhi_tt_adc_ProfileHitMap', title='#eta - #phi profile map of EM FADC > ' +str(threshADC)+ ' for triggered timeslice; Tower #eta; Tower #phi', type='TH2F', path=histPath+'/EtaPhiMaps', xbins=etabins, ybins=phibins, ymin=phimin, ymax=phimax_2d, cutmask='mask_EM_timeslice', weight='emTT_ADC') 
+
+ 
+    # HAD tower maps 
+    myGroup.defineHistogram('etaTT_HAD,phiTT_2d_HAD;h_ppm_had_2d_etaPhi_tt_adc_HitMap', title='#eta - #phi map of HAD FADC > ' +str(threshADC)+ ' for triggered timeslice; Tower #eta; Tower #phi', type='TH2F', path=histPath+'/EtaPhiMaps', xbins=etabins, ybins=phibins, ymin=phimin, ymax=phimax_2d, cutmask='mask_HAD_timeslice')
+ 
+    myGroup.defineHistogram('etaTT_HAD,phiTT_2d_HAD;h_ppm_had_2d_etaPhi_tt_adc_ProfileHitMap', title='#eta - #phi profile map of HAD FADC > ' +str(threshADC)+ ' for triggered timeslice; Tower #eta; Tower #phi', type='TH2F', path=histPath+'/EtaPhiMaps', xbins=etabins, ybins=phibins, ymin=phimin, ymax=phimax_2d, cutmask='mask_HAD_timeslice', weight='hadTT_ADC')
+ 
+  
+    
+    acc = helper.result()
+    result.merge(acc)
+    return result
+
+
+if __name__=='__main__':
+    # For direct tests
+    from AthenaCommon.Configurable import Configurable
+    Configurable.configurableRun3Behavior = 1
+
+    # set debug level for whole job
+    from AthenaCommon.Logging import log
+    from AthenaCommon.Constants import INFO #DEBUG
+    log.setLevel(INFO)
+
+    # set input file and config options
+    from AthenaConfiguration.AllConfigFlags import ConfigFlags
+    import glob
+
+    inputs = glob.glob('/eos/atlas/atlascerngroupdisk/data-art/build-output/master/Athena/x86_64-centos7-gcc8-opt/2020-04-06T2139/TrigP1Test/test_trigP1_v1PhysP1_T0Mon_build/ESD.pool.root')
+
+    ConfigFlags.Input.Files = inputs
+    ConfigFlags.Output.HISTFileName = 'ExampleMonitorOutput_LVL1.root'
+
+    ConfigFlags.lock()
+    ConfigFlags.dump() # print all the configs
+
+    from AthenaCommon.AppMgr import ServiceMgr
+    ServiceMgr.Dump = False
+
+    from AthenaConfiguration.MainServicesConfig import MainServicesSerialCfg 
+    from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg
+    cfg = MainServicesSerialCfg()
+    cfg.merge(PoolReadCfg(ConfigFlags))
+
+    PprMonitorCfg = PprMonitoringConfig(ConfigFlags)
+    cfg.merge(PprMonitorCfg)
+
+    # message level for algorithm
+    PprMonitorCfg.getEventAlgo('PprMonAlg').OutputLevel = 2 # 1/2 INFO/DEBUG
+    # options - print all details of algorithms, very short summary 
+    cfg.printConfig(withDetails=False, summariseProps = True)
+
+    nevents=-1
+    status = cfg.run(nevents)
+    if status.isFailure():
+        import sys
+        sys.exit(-1)
+
diff --git a/Trigger/TrigT1/TrigT1CaloMonitoring/src/PprMonitorAlgorithm.cxx b/Trigger/TrigT1/TrigT1CaloMonitoring/src/PprMonitorAlgorithm.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..9ec864201c7db8aa10af47dda8fef25717b3a0be
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloMonitoring/src/PprMonitorAlgorithm.cxx
@@ -0,0 +1,307 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+
+// L1 objects
+#include "TrigT1CaloEvent/TriggerTowerCollection.h"
+#include "TrigT1CaloEvent/TriggerTower_ClassDEF.h"
+#include "TrigT1CaloUtils/DataError.h"
+#include "TrigT1Interfaces/TrigT1CaloDefs.h"
+
+// xAOD
+#include "xAODTrigL1Calo/TriggerTowerContainer.h"
+// =============================================
+
+#include "PprMonitorAlgorithm.h"
+
+
+PprMonitorAlgorithm::PprMonitorAlgorithm( const std::string& name, ISvcLocator* pSvcLocator )
+  : AthMonitorAlgorithm(name,pSvcLocator)
+{
+}
+
+StatusCode PprMonitorAlgorithm::initialize() {
+
+  ATH_MSG_DEBUG("PprMonitorAlgorith::initialize");
+  ATH_MSG_DEBUG("Package Name "<< m_packageName);
+  ATH_MSG_DEBUG("m_xAODTriggerTowerContainerName "<< m_xAODTriggerTowerContainerName); 
+  ATH_MSG_DEBUG("m_TT_ADC_HitMap_Thresh " << m_TT_ADC_HitMap_Thresh);
+
+  // we initialise all the containers that we need
+  ATH_CHECK(m_xAODTriggerTowerContainerName.initialize());
+
+  // retrieve any tools if needed
+  //ATH_CHECK(myTool.retrieve());
+  
+ 
+  return AthMonitorAlgorithm::initialize();
+}
+
+StatusCode PprMonitorAlgorithm::fillHistograms( const EventContext& ctx ) const {
+
+  ATH_MSG_DEBUG("PprMonitorAlgorithm::fillHistograms");
+
+
+  // Contains the variables to monitor
+  std::vector<std::reference_wrapper<Monitored::IMonitoredVariable>> variables;
+
+
+  //Retrieve Trigger Towers from SG
+  SG::ReadHandle<xAOD::TriggerTowerContainer> triggerTowerTES(m_xAODTriggerTowerContainerName, ctx);
+  if(!triggerTowerTES.isValid()){
+    ATH_MSG_ERROR("No Trigger Tower container found in TES  "<< m_xAODTriggerTowerContainerName); 
+    return StatusCode::FAILURE;
+  }
+
+
+  // Create a vector of trigger towers with quantities to be monitored
+  std::vector<MonitorTT> vecMonTT_EM;  // EM towers
+  std::vector<MonitorTT> vecMonTT_HAD; // HAD towers
+  std::vector<MonitorTT> vecMonTT;     // All towers
+
+  // Loop over trigger tower container
+  for (const auto& tt : *triggerTowerTES) {
+   
+    // Create the trigger tower objects and calculate scaled phi
+    // Fill the required number of phi bins based on tower granularity
+    ATH_CHECK( fillPPMTowerEtaPhi(tt, vecMonTT_EM, vecMonTT_HAD, vecMonTT) );     
+        
+  } // End of loop over triggerTowerTES container    
+
+
+  // Variables to monitor 
+  
+  // EM towers
+  // eta for 2-d eta-phi maps (contains "duplicate" entries to account for granularity of multiple phi bins in forward region)
+  auto etaTT_EM = Monitored::Collection("etaTT_EM", vecMonTT_EM, []( const auto &emTower ){return emTower.tower->eta();});
+  variables.push_back(etaTT_EM);
+
+  auto phiTT_2d_EM = Monitored::Collection("phiTT_2d_EM", vecMonTT_EM, []( const auto &emTower ){return emTower.phiScaled;});
+  variables.push_back(phiTT_2d_EM); 
+
+  auto phiTT_1d_EM = Monitored::Collection("phiTT_1d_EM", vecMonTT_EM, []( const auto &emTower ){return emTower.phi1d;});
+  variables.push_back(phiTT_1d_EM);  
+
+  auto cpET_EM = Monitored::Collection("cpET_EM", vecMonTT_EM, []( const auto &emTower ){return emTower.tower->cpET();});
+  variables.push_back(cpET_EM);  
+
+  auto jepET_EM = Monitored::Collection("jepET_EM", vecMonTT_EM, []( const auto &emTower ){return emTower.jepET;});
+  variables.push_back(jepET_EM);
+
+  // HAD towers
+  auto etaTT_HAD = Monitored::Collection("etaTT_HAD", vecMonTT_HAD, []( const auto &hadTower ){return hadTower.tower->eta();});
+  variables.push_back(etaTT_HAD);
+
+  auto phiTT_2d_HAD = Monitored::Collection("phiTT_2d_HAD", vecMonTT_HAD, []( const auto &hadTower ){return hadTower.phiScaled;});
+  variables.push_back(phiTT_2d_HAD);
+
+  auto phiTT_1d_HAD = Monitored::Collection("phiTT_1d_HAD", vecMonTT_HAD, []( const auto &hadTower ){return hadTower.phi1d;});
+  variables.push_back(phiTT_1d_HAD);
+
+  // Cutmasks
+  std::vector<int> vec_EM_timeslice = {};
+  std::vector<int> vec_EM_cpET_0 = {};             // includes "duplicate" towers for phi maps
+  std::vector<int> vec_EM_cpET_0_noDuplicates = {}; // no duplicates: for plots not binned in phi
+  std::vector<int> vec_EM_cpET_5 = {};
+  std::vector<int> vec_EM_cpET_5_noDuplicates = {};
+  std::vector<int> vec_EM_jepET_0 = {};
+  std::vector<int> vec_EM_jepET_0_noDuplicates = {};
+  std::vector<int> vec_EM_jepET_5 = {};
+  std::vector<int> vec_EM_jepET_5_noDuplicates = {};
+
+  // Weights
+  std::vector<int> vec_EM_ADC = {};
+
+
+  for (auto& emTower : vecMonTT_EM) {
+  
+  
+    
+    // -------- LUT --------
+    int cpET = (emTower.tower)->cpET();
+    int jepET = emTower.jepET;
+    bool isDuplicate = emTower.isDuplicate;
+
+    ATH_MSG_DEBUG("cpET: " << cpET << " jepET: " << jepET);
+
+    // Fill the cutmasks for EM LUT-CP and LUT-JEP energy distributions 
+    vec_EM_cpET_0.push_back(cpET > 0);                                  // For phi distributions / maps
+    vec_EM_cpET_0_noDuplicates.push_back((cpET > 0) && !isDuplicate);   // For plots not binned in phi
+    vec_EM_cpET_5.push_back(cpET > 5);
+    vec_EM_cpET_5_noDuplicates.push_back((cpET > 5) && !isDuplicate);
+    vec_EM_jepET_0.push_back(jepET > 0);
+    vec_EM_jepET_0_noDuplicates.push_back((jepET > 0) && !isDuplicate);
+    vec_EM_jepET_5.push_back(jepET > 5);
+    vec_EM_jepET_5_noDuplicates.push_back((jepET > 5) && !isDuplicate); 
+      
+
+    // -------- ADC hitmaps per timeslice --------
+    unsigned int tslice = (emTower.tower)->adcPeak();
+    unsigned int adcSize = ((emTower.tower)->adc()).size();
+    
+    ATH_MSG_DEBUG("tslice: " << tslice << " adcSize: " << adcSize);
+
+    if (tslice < adcSize) {
+      const int ADC = ((emTower.tower)->adc())[tslice];
+      vec_EM_timeslice.push_back(ADC > m_TT_ADC_HitMap_Thresh);
+      vec_EM_ADC.push_back(ADC);
+      ATH_MSG_DEBUG("ADC: " << ADC);
+    }
+    else {
+      vec_EM_timeslice.push_back(0);
+      vec_EM_ADC.push_back(0);
+    }
+    ATH_MSG_DEBUG(" mask: " << vec_EM_timeslice.back());
+   
+  } // End loop over vector of EM towers 
+
+  // HAD cutmasks 
+  std::vector<int> vec_HAD_timeslice = {};
+
+  // HAD weights 
+  std::vector<int> vec_HAD_ADC = {};
+
+  for (auto& hadTower : vecMonTT_HAD) {
+
+    // Timeslice 
+    unsigned int tslice = (hadTower.tower)->adcPeak();
+    unsigned int adcSize = ((hadTower.tower)->adc()).size();
+
+    ATH_MSG_DEBUG("tslice: " << tslice << " adcSize: " << adcSize);
+
+    if (tslice < adcSize) {
+      const int ADC = ((hadTower.tower)->adc())[tslice];
+      vec_HAD_timeslice.push_back(ADC > m_TT_ADC_HitMap_Thresh);
+      vec_HAD_ADC.push_back(ADC);
+      ATH_MSG_DEBUG("ADC: " << ADC);
+    }
+    else {
+      vec_HAD_timeslice.push_back(0);
+      vec_HAD_ADC.push_back(0);
+    }
+    ATH_MSG_DEBUG(" mask: " << vec_HAD_timeslice.back());
+
+  } // End loop over vector of HAD towers 
+  
+
+  // Define the cutmasks (EM)
+  auto mask_EM_timeslice = Monitored::Collection("mask_EM_timeslice", vec_EM_timeslice);
+  variables.push_back(mask_EM_timeslice);
+  
+  auto emTT_ADC = Monitored::Collection("emTT_ADC", vec_EM_ADC);
+  variables.push_back(emTT_ADC);
+
+  auto mask_EM_cpET_0_phiBins = Monitored::Collection("mask_EM_cpET_0_phiBins", vec_EM_cpET_0);
+  variables.push_back(mask_EM_cpET_0_phiBins);
+
+  auto mask_EM_cpET_0_noPhi = Monitored::Collection("mask_EM_cpET_0_noPhi", vec_EM_cpET_0_noDuplicates);
+  variables.push_back(mask_EM_cpET_0_noPhi);
+
+  auto mask_EM_cpET_5_phiBins = Monitored::Collection("mask_EM_cpET_5_phiBins", vec_EM_cpET_5);
+  variables.push_back(mask_EM_cpET_5_phiBins);
+
+  auto mask_EM_cpET_5_noPhi = Monitored::Collection("mask_EM_cpET_5_noPhi", vec_EM_cpET_5_noDuplicates);
+  variables.push_back(mask_EM_cpET_5_noPhi);
+
+  auto mask_EM_jepET_0_phiBins = Monitored::Collection("mask_EM_jepET_0_phiBins", vec_EM_jepET_0);
+  variables.push_back(mask_EM_jepET_0_phiBins);
+
+  auto mask_EM_jepET_0_noPhi = Monitored::Collection("mask_EM_jepET_0_noPhi", vec_EM_jepET_0_noDuplicates);
+  variables.push_back(mask_EM_jepET_0_noPhi);
+
+  auto mask_EM_jepET_5_phiBins = Monitored::Collection("mask_EM_jepET_5_phiBins", vec_EM_jepET_5);
+  variables.push_back(mask_EM_jepET_5_phiBins);
+  
+  auto mask_EM_jepET_5_noPhi = Monitored::Collection("mask_EM_jepET_5_noPhi", vec_EM_jepET_5_noDuplicates);
+  variables.push_back(mask_EM_jepET_5_noPhi);
+
+
+  // Define the cutmasks (HAD)
+  auto mask_HAD_timeslice = Monitored::Collection("mask_HAD_timeslice", vec_HAD_timeslice);
+  variables.push_back(mask_HAD_timeslice);
+
+  auto hadTT_ADC = Monitored::Collection("hadTT_ADC", vec_HAD_ADC);
+  variables.push_back(hadTT_ADC);
+
+  fill(m_packageName, variables);
+  variables.clear();
+  return StatusCode::SUCCESS;
+}
+
+StatusCode PprMonitorAlgorithm::fillPPMTowerEtaPhi( const xAOD::TriggerTower_v2* tt, 
+                                                    std::vector<MonitorTT> &vecMonTT_EM,  
+                                                    std::vector<MonitorTT> &vecMonTT_HAD,  
+                                                    std::vector<MonitorTT> &vecMonTT) const
+{
+
+  const int layer = tt->layer(); // 0 = EM, 1 = HAD
+  const double eta = tt->eta();
+  const double absEta = std::fabs(eta);
+  const double phi = tt->phi();
+  double phiMod = phi * m_phiScaleTT;
+  int jepET = 0;
+  const std::vector<uint_least8_t> jepETvec = tt->lut_jep();
+  if (jepETvec.size() > 0) jepET = tt->jepET();
+  bool isDuplicate = false; 
+ 
+  // Offsets for filling multiple phi bins depending on TT granularity in eta
+  std::vector<double> offset32 = {1.5, 0.5, -0.5, -1.5};
+  std::vector<double> offset25 = {0.5, -0.5};  
+
+  // Offsets for filling 1D phi plots
+  double phi1d = phi;
+
+  if (absEta > 3.2) {
+   
+    phiMod = std::floor(phiMod/4)*4. + 2.;
+    // Fill four bins in phi  
+    for (int i = 0; i < 4; i++) {
+      MonitorTT monTT;
+      monTT.tower = tt;
+      monTT.phiScaled = phiMod + offset32[i];
+      monTT.phi1d = phi + offset32[i]/m_phiScaleTT;
+      monTT.jepET = jepET;
+      if(i != 0) isDuplicate = true;
+      monTT.isDuplicate = isDuplicate;   
+      
+      vecMonTT.push_back(monTT);
+      if (layer == 0) vecMonTT_EM.push_back(monTT);
+      if (layer == 1) vecMonTT_HAD.push_back(monTT);
+      
+      ATH_MSG_DEBUG("layer: " << layer << " eta: " << eta << " phi: " << phi << " scaled phi: " << monTT.phiScaled << " 1d phi: " << phi1d << " duplicate: " << monTT.isDuplicate);
+    }
+  }
+  else if (absEta > 2.5) {
+    phiMod = std::floor(phiMod/2)*2. + 1.;
+    // Fill two bins in phi
+    for (int i = 0; i < 2; i++) {
+      MonitorTT monTT;
+      monTT.tower = tt;
+      monTT.phiScaled = phiMod + offset25[i];
+      monTT.phi1d = phi + offset25[i] / m_phiScaleTT;
+      monTT.jepET = jepET;
+      if(i != 0) isDuplicate = true;
+      monTT.isDuplicate = isDuplicate;
+
+      vecMonTT.push_back(monTT);
+      if (layer == 0) vecMonTT_EM.push_back(monTT);
+      if (layer == 1) vecMonTT_HAD.push_back(monTT);
+      ATH_MSG_DEBUG("layer: " << layer << " eta: " << eta << " phi: " << phi << " scaled phi: " << monTT.phiScaled << " 1d phi: " << phi1d << " duplicate: " << monTT.isDuplicate);
+    }
+  }
+  else {
+    // Fill one phi bin
+    MonitorTT monTT;
+    monTT.tower = tt;
+    monTT.phiScaled = phiMod;
+    monTT.phi1d = phi;
+    monTT.jepET = jepET;
+    monTT.isDuplicate = false;
+    vecMonTT.push_back(monTT);
+    if (layer == 0) vecMonTT_EM.push_back(monTT);
+    if (layer == 1) vecMonTT_HAD.push_back(monTT);
+    ATH_MSG_DEBUG("layer: " << layer << " eta: " << eta << " phi: " << phi << " scaled phi: " << monTT.phiScaled << " 1d phi: " << phi1d << " duplicate: " << monTT.isDuplicate);
+  }   
+    
+  return StatusCode::SUCCESS; 
+}
diff --git a/Trigger/TrigT1/TrigT1CaloMonitoring/src/PprMonitorAlgorithm.h b/Trigger/TrigT1/TrigT1CaloMonitoring/src/PprMonitorAlgorithm.h
new file mode 100644
index 0000000000000000000000000000000000000000..8e2d10fc6acc76a1e17b6911a71fe901e71e092b
--- /dev/null
+++ b/Trigger/TrigT1/TrigT1CaloMonitoring/src/PprMonitorAlgorithm.h
@@ -0,0 +1,47 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+#ifndef TRIGT1CALOMONITORING_PPRMONITORALGORITHM_H
+#define TRIGT1CALOMONITORING_PPRMONITORALGORITHM_H
+
+#include "AthenaMonitoring/AthMonitorAlgorithm.h"
+#include "AthenaMonitoringKernel/Monitored.h"
+#include "StoreGate/ReadHandleKey.h"
+
+class PprMonitorAlgorithm : public AthMonitorAlgorithm {
+public:PprMonitorAlgorithm( const std::string& name, ISvcLocator* pSvcLocator );
+  virtual ~PprMonitorAlgorithm()=default;
+  virtual StatusCode initialize() override;
+  virtual StatusCode fillHistograms( const EventContext& ctx ) const override;
+ 
+  /// Struct to contain PPM trigger tower info
+  struct MonitorTT { 
+    const xAOD::TriggerTower_v2* tower;
+    double phiScaled; /// phi for 2d maps with integer bins (taking into account granularity in eta)
+    double phi1d;     /// phi for 1d phi distributions (taking into account granularity in eta) 
+    int jepET;
+    bool isDuplicate; /// Bookkeeping of multiple bins in phi for a given eta bin in the forward region 
+  };
+
+
+private:
+
+  StringProperty m_packageName{this,"PackageName","PprMonitor","group name for histogramming"};
+
+  /// container keys including steering parameter and description
+  SG::ReadHandleKey<xAOD::TriggerTowerContainer> m_xAODTriggerTowerContainerName{this, "BS_xAODTriggerTowerContainer",LVL1::TrigT1CaloDefs::xAODTriggerTowerLocation,"Trigger Tower Container"};
+  
+
+  /// Properties
+  Gaudi::Property<double> m_phiScaleTT{this, "phiScaleTT", 32./M_PI, "Scale factor to convert trigger tower phi to integer binning"};
+  Gaudi::Property<int> m_TT_ADC_HitMap_Thresh{this, "TT_ADC_HitMap_Thresh", 50, "ADC cut for hit maps"};
+
+
+  /// Helper functions
+  StatusCode fillPPMTowerEtaPhi( const xAOD::TriggerTower_v2* tt, 
+                               std::vector<MonitorTT> &vecMonTT_EM, 
+                               std::vector<MonitorTT> &vecMonTT_HAD,  
+                               std::vector<MonitorTT> &vecMonTT) const;
+
+};
+#endif
diff --git a/Trigger/TrigT1/TrigT1CaloMonitoring/src/components/TrigT1CaloMonitoring_entries.cxx b/Trigger/TrigT1/TrigT1CaloMonitoring/src/components/TrigT1CaloMonitoring_entries.cxx
index 22a5bd4ca7d188b0dd987abf1bad225ba17b6bfd..a9dbe5a125b43c3189d7146b073bf3cd8abe47f4 100644
--- a/Trigger/TrigT1/TrigT1CaloMonitoring/src/components/TrigT1CaloMonitoring_entries.cxx
+++ b/Trigger/TrigT1/TrigT1CaloMonitoring/src/components/TrigT1CaloMonitoring_entries.cxx
@@ -2,7 +2,7 @@
 #include "../CpmMonitorAlgorithm.h"
 //#include "../CpmSimMonitorAlgorithm.h"
 //#include "../PpmSimBsMonitorAlgorithm.h"
-//#include "../PprMonitorAlgorithm.h"
+#include "../PprMonitorAlgorithm.h"
 //#include "../PprSpareMonitorAlgorithm.h"
 //#include "../PprStabilityMonitorAlgorithm.h"
 
@@ -37,7 +37,7 @@
 DECLARE_COMPONENT( CpmMonitorAlgorithm )
 //DECLARE_COMPONENT( CpmSimMonitorAlgorithm )
 //DECLARE_COMPONENT( PpmSimBsMonitorAlgorithm )
-//DECLARE_COMPONENT( PprMonitorAlgorithm )
+DECLARE_COMPONENT( PprMonitorAlgorithm )
 //DECLARE_COMPONENT( PprSpareMonitorAlgorithm )
 //DECLARE_COMPONENT( PprStabilityMonitorAlgorithm )