diff --git a/InnerDetector/InDetEventCnv/SCT_RawDataByteStreamCnv/CMakeLists.txt b/InnerDetector/InDetEventCnv/SCT_RawDataByteStreamCnv/CMakeLists.txt
index 20d2ee9f4e0205e0e70d4a250c58a1999b2fc85b..a615b6093f2d075517957cbad06f5843670aff43 100644
--- a/InnerDetector/InDetEventCnv/SCT_RawDataByteStreamCnv/CMakeLists.txt
+++ b/InnerDetector/InDetEventCnv/SCT_RawDataByteStreamCnv/CMakeLists.txt
@@ -28,6 +28,9 @@ atlas_add_test( TestSCTEncodeNewConf
                 SCRIPT python -m SCT_RawDataByteStreamCnv.testSCTEncodeNewConf
                 POST_EXEC_SCRIPT noerror.sh
                 PROPERTIES TIMEOUT 600 )
+                
+atlas_add_executable( DecodeSCT
+                utilities/*.cxx )
 
 # Install files from the package:
 atlas_install_python_modules( python/*.py POST_BUILD_CMD ${ATLAS_FLAKE8} )
diff --git a/InnerDetector/InDetEventCnv/SCT_RawDataByteStreamCnv/utilities/DecodeSCT.cxx b/InnerDetector/InDetEventCnv/SCT_RawDataByteStreamCnv/utilities/DecodeSCT.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..6ff6c7cd06a64e09b11405522714abcb2a5dba7b
--- /dev/null
+++ b/InnerDetector/InDetEventCnv/SCT_RawDataByteStreamCnv/utilities/DecodeSCT.cxx
@@ -0,0 +1,462 @@
+/*
+  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include <sys/mman.h> //PROT_READ, MAP_PRIVATE
+#include <fcntl.h> //O_RDONLY
+#include <unistd.h> //close
+
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <string>
+#include <utility>
+#include <cstdint>
+#include <filesystem>
+
+
+namespace fs = std::filesystem;
+namespace {
+//check the filename exists and return it as a string, or return empty string.
+fs::path
+getFilename(const char * filenameArg);
+
+void 
+decodeEventWord(uint16_t currWord, int errorType, bool &linkCondensed, 
+                std::string &type, std::vector<std::pair<std::string, int> > &arguments, 
+                std::vector<std::string> &errors);
+
+std::vector<std::string> 
+decodeEventFlags(uint32_t flags);
+
+bool 
+findNextEvent(uint32_t* &start, uint32_t* &finish, uint32_t *end, uint32_t startWord);
+
+void 
+decodeEvent(uint32_t *buffer, int length);
+
+void 
+decodeSubFragment(uint32_t *buffer, int length);
+}
+
+inline std::string 
+str(const char * word){
+  return word ? word : "";
+} 
+
+void
+helpMessage(){
+  std::cout<< "'DecodeSCT' takes exactly one argument, the bytestream filename." <<std::endl;
+}
+
+
+int main(int argc, char **argv) {
+  if (argc < 2){
+    helpMessage();
+    return 1;
+  } 
+  fs::path input_file = getFilename(argv[1]);
+  if (input_file.empty()) {
+    std::cerr << argv[1] <<" does not exist."<<std::endl;
+    return 2;
+  }
+  auto length = fs::file_size(input_file);
+  printf("Length: %lu\n", length);
+  auto fd = open(input_file.c_str(), O_RDONLY);
+  if(fd<0) {
+    perror("File open failed");
+    return 3;
+  }
+  uint32_t *buffer = (uint32_t *) mmap(0, length, PROT_READ, MAP_PRIVATE, fd, 0);
+  if(buffer == MAP_FAILED) {
+    close(fd);
+    perror("mmap failed");
+    return 4;
+  }
+  uint32_t *endBuffer = &buffer[length/4];
+  uint32_t *begin = buffer;
+  uint32_t *end{};
+  while(findNextEvent(begin, end, endBuffer, 0xee1234ee)) {
+    //cppcheck-suppress invalidPrintfArgType_sint
+    printf("Event from %li to %li\n", begin-buffer, end-buffer);
+    int length = static_cast<int>(end - 1l - begin);
+    decodeEvent(begin, length);
+  }
+  munmap(buffer, length);
+  close(fd);
+}
+
+namespace {
+
+  bool findNextEvent(uint32_t* &start, uint32_t* &finish, uint32_t *end, uint32_t startWord) {
+    start ++;
+    if(start >= end) return false;
+    while(*start != startWord) {
+      start++;
+      if(start >= end) return false;
+    }
+    finish = start+1;
+    // ignore sub-ROD fragments
+    while((!( ((*finish & 0x00ffff00) == 0x00123400) || ((*finish & 0xffff0000) == 0x12340000))) ||  ((*finish) == 0xff1234ff)) {
+      // Last one, return length
+      if(finish >= end) break;
+      finish++;
+    }
+    return true;
+  }
+
+  bool findNextSubFragment(uint32_t* &start, uint32_t* &finish, uint32_t *end) {
+    start ++;
+    if(start >= end) return false;
+    while(*start != 0xff1234ff) {
+      start++;
+      if(start >= end) return false;
+    }
+    finish = start+1;
+    while((!( ((*finish & 0x00ffff00) == 0x00123400) || ((*finish & 0xffff0000) == 0x12340000))) ) {
+      // Last one, return length
+      if(finish >= end) break;
+      finish++;
+    }
+    return true;
+  }
+
+  inline uint32_t 
+  eventWord(const uint32_t * const buffer, const int index, const int length) {
+    if (index > length) {
+      std::cout << "Attempt to read from the end of the buffer " << std::dec << index << " " << length << std::hex << std::endl;
+      return 0;
+    } else {
+      return buffer[index-1];
+    }
+  }
+
+  void decodeEvent(uint32_t *frameBuffer, int length) {
+    if(frameBuffer[0] == 0xee1234ee) {
+      std::cout << "Valid header\n";
+    } else {
+      std::cout << "Bad check 0x" << std::hex << frameBuffer[0] << std::dec << "\n";
+      std::cout << "Skipping decode as SCT ROD fragment\n";
+      return;
+    }
+    uint32_t headerLength = 9;     // Including bof
+    uint32_t trailerLength = 5;    // Excluding eof
+    // The below assumes the old definition of headerLength (ie including 0xbof)
+    headerLength=10;
+    // 2 is to correct for the bof and eof that aren't in the s-link format
+    int eventLength = length - headerLength - trailerLength + 2;
+    uint32_t rawOffset = headerLength;
+    if(frameBuffer[1] != headerLength) {
+      std::cout << "Unknown header length (" << frameBuffer[1] << ")\n";
+    } 
+    std::cout << "New formatter version:\n";
+    std::cout << "Version: 0x" << std::hex << eventWord(frameBuffer, 3, length) << std::dec
+      << " ID: 0x" <<std::hex  << eventWord(frameBuffer, 4, length) <<std::dec
+      << " Run number = " << eventWord(frameBuffer, 5, length) << "\n";
+      std::cout << "L1ID = 0x" << std::hex << eventWord(frameBuffer, 6, length) << std::dec 
+      << " BCID = 0x" << std::hex << eventWord(frameBuffer, 7, length) << std::dec 
+      << " TType = " << eventWord(frameBuffer, 8, length) 
+      << " det type = " << eventWord(frameBuffer, 9, length) << "\n";
+    uint32_t SubDetID = (eventWord(frameBuffer, 4, length) & 0xff0000) >> 16 ;
+    std::cout << "SubDetID = " << std::hex << SubDetID <<std::dec;
+    switch (SubDetID){
+      case 0x20:
+      case 0x21:
+      case 0x22:
+      case 0x23:
+      case 0x24:
+      {
+        std::cout << " Found SCT fragment [" << SubDetID << "]. Decoding...\n";
+        
+        // Something different to the first word
+        uint16_t lastWord = 1 + ((eventWord(frameBuffer, rawOffset, length) & 0xffff0000) >> 16);
+        uint32_t repeats = 0;
+        
+        bool linkCondensed = true;
+        
+        for(int i=0; i<eventLength * 2; i++) {
+          uint16_t currWord;
+          {
+            uint32_t rawWord = eventWord(frameBuffer, rawOffset + i/2, length);
+            if(i&1) {
+              currWord = rawWord & 0x00ffff;
+            } else {
+              currWord = (rawWord & 0xffff0000) >> 16;
+            }
+          }
+          
+          if(currWord == lastWord) {
+            repeats ++;
+            continue;
+          } else if(repeats) {
+            std::cout << "        Repeated " << repeats << " times\n";
+            repeats = 0;
+          }
+          lastWord = currWord;
+          std::cout.width(4);
+          std::cout.fill('0');
+          std::cout << std::hex << currWord << std::dec;
+          std::cout.fill(' ');
+          std::string type;
+          std::vector<std::pair<std::string, int> > arguments;
+          std::vector<std::string> errors;
+          decodeEventWord(currWord, 0, linkCondensed,type, arguments, errors);
+          std::cout << " " << type << " ";
+          for(auto iter = arguments.begin();iter != arguments.end();++iter ) {
+            std::cout << "  " << iter->first << ": " << iter->second;
+          }
+          if(errors.size() > 0) {
+            std::cout << "  ERRORS: ";
+          }
+          for(auto iter = errors.begin();iter != errors.end();++iter) {
+            std::cout << " " << *iter;
+          }
+          std::cout << std::endl;
+        }
+        if(repeats) {
+          std::cout << "        Repeated " << repeats << " times\n";
+          repeats = 0;
+        }
+        std::cout << "Error count = 0x" << std::hex << eventWord(frameBuffer, length-trailerLength + 2, length) << std::dec << "\n";
+        std::cout << "Error flags = 0x" << std::hex << eventWord(frameBuffer, length-trailerLength + 3, length) << std::dec << "\n";
+        int flags = eventWord(frameBuffer, length-trailerLength + 3, length);
+        std::vector<std::string> flagStrings = decodeEventFlags(flags);
+        for(auto iter = flagStrings.begin();iter != flagStrings.end();++iter) {
+          std::cout << *iter << " ";
+        }
+        if(flagStrings.size() > 0) 
+          std::cout << std::endl;
+        std::cout << "nData = " << eventWord(frameBuffer, length-trailerLength + 5, length) << " words found = " << (length - (headerLength + trailerLength) + 2) << std::endl;
+        return;
+      }
+      case 0x70: {
+        std::cout << " TDAQ [" << SubDetID << "] beam crate fragment: " << std::endl;
+        uint32_t *endBuffer = &frameBuffer[length];
+        uint32_t *begin = frameBuffer;
+        uint32_t *end = frameBuffer;
+        bool found = findNextSubFragment(begin, end, endBuffer);
+        //cppcheck-suppress invalidPrintfArgType_sint   
+        printf("SubFragment from %ld to %ld\n", begin-frameBuffer, end-frameBuffer);
+        while (found) {
+          int length = static_cast<int>(end-begin);
+          decodeSubFragment(begin, length);
+          found = findNextSubFragment(begin, end, endBuffer);
+          //cppcheck-suppress invalidPrintfArgType_sint    
+          if (found) printf("SubFragment from %ld to %ld\n", begin-frameBuffer, end-frameBuffer);
+        }
+        return;
+      }
+      case 0x30:
+      case 0x31:
+      case 0x32:
+      case 0x33:
+      case 0x34: {
+        std::cout << " TRT fragment [" << SubDetID << "] ... skipping" << std::endl;
+        return;
+      }
+      default: {
+        std::cout << " Not an known fragment [" << SubDetID << "] ... skipping" << std::endl;
+        return;
+      }
+    }
+  }
+
+  void 
+  decodeSubFragment(uint32_t *frameBuffer, int length) {
+    uint32_t SubFragID = eventWord(frameBuffer, 3, length) ;
+    std::cout << "SubFragmentID = "  << SubFragID ;
+    std::cout <<  " Length =  " << length; 
+    std::string Type[8]={"data","res ","head","res ","eob ","res ","inv ","res "};
+    switch(SubFragID) {
+      case 0x1: {
+        std::cout << " Found TDC fragment. Decoding..."<<  std::endl;
+        for (int i =0; i<length-3;i++) {
+          uint32_t data = eventWord(frameBuffer, i+4 ,length);
+          std::cout << "data[" << i << "]=0x" << std::hex <<(data&0xFFF) << std::dec << " ";
+          std::cout << "type=" << Type[(data>>24)&0x07] << " ";
+          std::cout << "chan=" << ((data>>16)&0x001f) << " ";
+          std::cout << "un=" << ((data>>13)&1) << " ";
+          std::cout << "ov=" << ((data>>12)&1) << " ";
+          std::cout << "value=" << ((data&0x0fff)) << " ";
+          std::cout << std::endl;
+        }
+        return;
+      }
+      case 0x2: {
+        std::cout << " Found ADC fragment. Decoding..." << std::endl;
+        for (int i =0; i<length-5;i++) {
+          uint32_t data = eventWord(frameBuffer, i+4 ,length);
+          std::cout << "data[" << i << "]=0x" << std::hex <<data << std::dec << " ";
+          std::cout << "type=" << Type[(data>>24)&0x07] << " ";
+          std::cout << "chan=" << ((data>>16)&0x001f) << " ";
+          std::cout << "un=" << ((data>>13)&1) << " ";
+          std::cout << "ov=" << ((data>>12)&1) << " ";
+          std::cout << "value=" << ((data&0x0fff)) << " ";
+          std::cout << std::endl;  
+        }
+        return;
+      }
+      default: {
+        std::cout << " Not an known SubFragment [" << SubFragID << "] ... skipping" << std::endl;
+        return;
+      }
+    }
+  }
+
+  void 
+  decodeEventWord(uint16_t currWord, int errorType, bool &linkCondensed, 
+                  std::string &type, std::vector<std::pair<std::string, int> > &arguments, 
+                  std::vector<std::string> &errors) {
+  type = "UNKNOWN";
+  arguments.clear();
+  errors.clear();
+  
+  switch((currWord & 0xe000) >> 13) {
+    case 0:
+      {
+        if(currWord & 0x1f80) 
+          type = "INVALID";
+        else {
+          // Flagged error
+          type = "Flagged error";
+          errors.push_back("FLAGGED");
+          arguments.push_back(std::make_pair("chip", (currWord & 0x78) >> 3));
+          arguments.push_back(std::make_pair("value", currWord & 0x7));
+        }
+      }
+      break;
+    case 1:
+      {
+        // Header
+        type = "Header";
+        if(errorType) {
+          // No indication of whether its condensed...??
+          arguments.push_back(std::make_pair("L1", ((currWord & 0x0f00) >> 8)));
+          arguments.push_back(std::make_pair("BCID", ((currWord & 0xff))));
+          if(currWord & 0x1000) {
+            errors.push_back("Preamble err");
+          }
+        } else {
+          int link = (currWord & 0x7f);
+          arguments.push_back(std::make_pair("Link", link));
+          if(currWord & 0x100) {
+            arguments.push_back(std::make_pair("Condensed mode", 1));
+            linkCondensed = true;
+          } else {
+            linkCondensed = false;
+          }
+          if(currWord & 0x200) 
+            errors.push_back("BC err");
+          if(currWord & 0x400)
+            errors.push_back("L1 err");
+          if(currWord & 0x800)
+            errors.push_back("Time out err");
+          if(currWord & 0x1000)
+            errors.push_back("Preamble err");
+        }
+      }
+    break;
+    case 2:
+      {
+        if(currWord & 0x3ff) 
+          type = "INVALID";
+        else {
+          // Trailer
+          type = "Trailer";
+          if(currWord & 0x400)
+            errors.push_back("Data overflow err");
+          if(currWord & 0x800)
+            errors.push_back("H/T limit err");
+          if(currWord & 0x1000)
+            errors.push_back("Trailer bit err");
+        }
+      }
+      break;
+    case 3:
+      {
+        if(currWord & 0x300) 
+          type = "INVALID";
+        else {
+          // Raw data
+          type = "Raw";
+          int bits = ((currWord & 0x1c00) >> 10) + 1;
+          arguments.push_back(std::make_pair("bits", bits));
+          int value = ((currWord<<(8-bits)) & 0xff) >> (8-bits);
+          arguments.push_back(std::make_pair("value", value));
+          // Construct decimal number that looks like binary...
+          int binValue = 0;
+          for(int b=8-bits; b<8; b++) {
+            binValue = binValue * 10 + ((value>>(7-b)) & 1);
+          }
+          // Unfortunately leading zeros are supressed when it's printed as an int...
+          arguments.push_back(std::make_pair("binValue", binValue));
+        }
+      }
+      break;
+    default:
+      // Everything else (hits)
+      {
+        if((currWord & 0x2) == 0) {
+          // Check if it should be condensed
+          if(linkCondensed || errorType) {
+            arguments.push_back(std::make_pair("Chip", ((currWord & 0x7800) >> 11)));
+            arguments.push_back(std::make_pair("Channel", ((currWord & 0x7f0) >> 4)));
+            if(currWord & 1) {
+              type = "Condensed double hit";
+              if(currWord & 0x4) errors.push_back("Error in hit1");
+              if(currWord & 0x8) errors.push_back("Error in hit2");
+            } else {
+              type = "Condensed hit";
+              if(currWord & 0x4) errors.push_back("Error in hit");
+            }
+          }
+        }
+      
+        // Only check if expanded is a posibility
+        if(!linkCondensed || errorType) {
+          if((currWord & 0x8) == 0) {
+            type = "1st hit clust exp";
+            arguments.push_back(std::make_pair("Chip", ((currWord & 0x7800) >> 11)));
+            arguments.push_back(std::make_pair("Channel", ((currWord & 0x7f0) >> 4)));
+            arguments.push_back(std::make_pair("hits", currWord & 0x7));
+          } else {
+            if((currWord & 0x7f00) == 0) {
+              type = "Clust exp";
+              arguments.push_back(std::make_pair("hits", currWord & 0x7));
+              if(currWord & 0x80) {
+                arguments.push_back(std::make_pair("hits2", (currWord & 0x70) >> 4));
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  std::vector<std::string> 
+  decodeEventFlags(uint32_t flags) {
+    std::vector<std::string> result;
+    if(flags & 0x1) result.push_back("HEADER");
+    if(flags & 0x2) result.push_back("TRAILER");
+    if(flags & 0x4) result.push_back("FLAGGED");
+    if(flags & 0x8) result.push_back("\"HIT PATTERN\"");
+    if(flags & 0x10) result.push_back("SYNC");
+    if(flags & 0x20) result.push_back("L1ID");
+    if(flags & 0x40) result.push_back("BCID");
+    if(flags & 0x80) result.push_back("TIMEOUT");
+    if(flags & 0x100) result.push_back("\"ALMOST FULL\"");
+    if(flags & 0x200) result.push_back("OVERFLOW");
+    if(flags & 0x400) result.push_back("\"CHIP SEQ\"");
+    if(flags & 0x800) result.push_back("\"BAD CHIP\"");
+    return result;
+  }
+
+  fs::path
+  getFilename(const char * filenameArg){
+    std::string result;
+    const fs::path file(filenameArg);
+    if (fs::exists(file)) result = filenameArg;
+    return fs::path(result);
+  }
+} // Close null namespace
+