diff --git a/.clang-format b/.clang-format
index 99bf475e5cfd40d9a84d7e21d28b1f0380870114..7a1683f2af9fefd476987996b8e6507cc4cb57b0 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,116 +1,73 @@
----
-Language:        Cpp
-# BasedOnStyle:  LLVM
+# Generated from CLion C/C++ Code Style settings
+BasedOnStyle: LLVM
 AccessModifierOffset: -4
 AlignAfterOpenBracket: Align
-AlignConsecutiveAssignments: false
-AlignConsecutiveDeclarations: false
-AlignEscapedNewlines: Right
-AlignOperands:   true
-AlignTrailingComments: false
-AllowAllParametersOfDeclarationOnNextLine: true
-AllowShortBlocksOnASingleLine: false
-AllowShortCaseLabelsOnASingleLine: false
-AllowShortFunctionsOnASingleLine: Empty
-AllowShortIfStatementsOnASingleLine: true
-AllowShortLoopsOnASingleLine: false
-AlwaysBreakAfterDefinitionReturnType: None
+AlignConsecutiveAssignments: None
+AlignOperands: Align
+AllowAllArgumentsOnNextLine: false
+AllowAllConstructorInitializersOnNextLine: false
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortBlocksOnASingleLine: Always
+AllowShortCaseLabelsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: All
+AllowShortIfStatementsOnASingleLine: Always
+AllowShortLambdasOnASingleLine: All
+AllowShortLoopsOnASingleLine: true
 AlwaysBreakAfterReturnType: None
-AlwaysBreakBeforeMultilineStrings: false
 AlwaysBreakTemplateDeclarations: Yes
-BinPackArguments: true
-BinPackParameters: true
-BreakBeforeBinaryOperators: None
 BreakBeforeBraces: Custom
 BraceWrapping:
+  AfterCaseLabel: false
   AfterClass: false
-  AfterControlStatement: false
+  AfterControlStatement: Never
   AfterEnum: false
   AfterFunction: false
-  AfterNamespace: true
-  AfterStruct: false
+  AfterNamespace: false
   AfterUnion: false
-  AfterExternBlock: false
   BeforeCatch: false
   BeforeElse: false
+  IndentBraces: false
   SplitEmptyFunction: false
-  SplitEmptyRecord: false
-  SplitEmptyNamespace: false
-BreakBeforeInheritanceComma: false
-BreakInheritanceList: BeforeColon
+  SplitEmptyRecord: true
+BreakBeforeBinaryOperators: None
 BreakBeforeTernaryOperators: true
-BreakConstructorInitializersBeforeComma: false
 BreakConstructorInitializers: BeforeColon
-BreakAfterJavaFieldAnnotations: false
-BreakStringLiterals: true
-ColumnLimit:     120
-CommentPragmas:  '^ IWYU pragma:'
+BreakInheritanceList: BeforeColon
+ColumnLimit: 0
 CompactNamespaces: false
-ConstructorInitializerAllOnOneLineOrOnePerLine: false
-ConstructorInitializerIndentWidth: 4
 ContinuationIndentWidth: 4
-Cpp11BracedListStyle: true
-DerivePointerAlignment: false
-DisableFormat:   false
-ExperimentalAutoDetectBinPacking: false
-FixNamespaceComments: true
-ForEachMacros:
-  - foreach
-  - Q_FOREACH
-  - BOOST_FOREACH
-IncludeBlocks:   Merge
 IncludeCategories:
   - Regex:           '^"'
-    Priority:        2
-  - Regex:           '^<'
     Priority:        3
+  - Regex:           '^<'
+    Priority:        2
   - Regex:           '.*'
     Priority:        1
-IncludeIsMainRegex: '(Test)?$'
 IndentCaseLabels: true
 IndentPPDirectives: None
-IndentWidth:     4
-IndentWrappedFunctionNames: false
-JavaScriptQuotes: Leave
-JavaScriptWrapImports: true
-KeepEmptyLinesAtTheStartOfBlocks: false
-MacroBlockBegin: ''
-MacroBlockEnd:   ''
-MaxEmptyLinesToKeep: 1
-NamespaceIndentation: None
-ObjCBinPackProtocolList: Auto
-ObjCBlockIndentWidth: 2
+IndentWidth: 4
+KeepEmptyLinesAtTheStartOfBlocks: true
+MaxEmptyLinesToKeep: 2
+NamespaceIndentation: All
 ObjCSpaceAfterProperty: false
 ObjCSpaceBeforeProtocolList: true
-PenaltyBreakAssignment: 2
-PenaltyBreakBeforeFirstCallParameter: 19
-PenaltyBreakComment: 300
-PenaltyBreakFirstLessLess: 120
-PenaltyBreakString: 1000
-PenaltyBreakTemplateDeclaration: 10
-PenaltyExcessCharacter: 1000000
-PenaltyReturnTypeOnItsOwnLine: 60
 PointerAlignment: Left
-ReflowComments:  true
-SortIncludes:    false
-SortUsingDeclarations: true
-SpaceAfterCStyleCast: false
+ReflowComments: false
+SpaceAfterCStyleCast: true
+SpaceAfterLogicalNot: false
 SpaceAfterTemplateKeyword: true
 SpaceBeforeAssignmentOperators: true
 SpaceBeforeCpp11BracedList: false
 SpaceBeforeCtorInitializerColon: true
 SpaceBeforeInheritanceColon: true
 SpaceBeforeParens: ControlStatements
-SpaceBeforeRangeBasedForLoopColon: true
+SpaceBeforeRangeBasedForLoopColon: false
 SpaceInEmptyParentheses: false
 SpacesBeforeTrailingComments: 1
-SpacesInAngles:  false
-SpacesInContainerLiterals: true
+SpacesInAngles: false
 SpacesInCStyleCastParentheses: false
+SpacesInContainerLiterals: false
 SpacesInParentheses: false
 SpacesInSquareBrackets: false
-Standard:        Cpp11
-TabWidth:        4
-UseTab:          ForIndentation
-...
-
+TabWidth: 4
+UseTab: ForIndentation
diff --git a/.gitignore b/.gitignore
index 051810d3f334d430727a431c6daba6ae1a37e196..a15b9233fb1eaf13a108d3a161c2d72f9c52a510 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,10 @@
 # Build files
 build
 cmake-build-debug
-docs
+
+# Documentation output
+/docs/html
+/docs/latex
 
 # Dump and continuous integration files
 *.dump
@@ -74,4 +77,4 @@ __pycache__
 .idea/**/markdown-*
 
 # IDEs
-.vscode
\ No newline at end of file
+.vscode
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7c7becceea2926b617fdd2632e9fa20ce5d4cae4..0b3ef569bb6f779875aacc94788e471b2e836135 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -72,10 +72,10 @@ doxygen:
   stage: analyze
   script:
     - cd $CI_PROJECT_DIR
-    - doxygen doxygen.conf
+    - doxygen
   artifacts:
     paths:
-      - ./docs
+      - ./docs/html
 
 ikos:
   image: spacedot/ikos:3.0
@@ -120,10 +120,13 @@ pages:
   stage: deploy
   script:
     - mkdir .public
-    - cp -r ./{docs,gcovr,ikos-report,scan-build-html-report,clang-tidy-html-report,cppcheck-html-report} ./.public/
+    - cp -r ./docs/html ./.public/docs
+    - cp -r ./{gcovr,ikos-report,scan-build-html-report,clang-tidy-html-report,cppcheck-html-report} ./.public/
     - ls ./.public
     - find ./.public
     - mv .public public
+  after_script:
+    - "echo Base page for this branch: ${CI_PAGES_URL}"
   artifacts:
     paths:
       - public # Upload the resulting website
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 391b583de03bfac21f4c2b44e6da378d7be3a068..8caf2586e93d15c7539d6084ff9f32dc699067b1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -29,8 +29,7 @@ add_library(common OBJECT
         src/MessageParser.cpp
         src/ServicePool.cpp
         src/Helpers/CRCHelper.cpp
-        src/Helpers/TimeAndDate.cpp
-        src/Helpers/TimeHelper.cpp
+        src/Time/UTCTimestamp.cpp
         src/Services/EventReportService.cpp
         src/Services/MemoryManagementService.cpp
         src/Services/ParameterService.cpp
@@ -42,13 +41,14 @@ add_library(common OBJECT
         src/Services/FunctionManagementService.cpp
         src/Services/HousekeepingService.cpp
         src/Services/ParameterStatisticsService.cpp
+        src/Services/OnBoardMonitoringService.cpp
         src/Helpers/Statistic.cpp
         src/Services/RealTimeForwardingControlService.cpp
+        src/Helpers/PMONBase.cpp
         )
 
 # Specify the .cpp files for the executables
-file(GLOB x86_main_SRC "src/Platform/x86/*.cpp" "src/Platform/x86/Services/*.cpp"
-        "lib/logger/src/Platform/x86/*.cpp")
+file(GLOB_RECURSE x86_main_SRC "src/Platform/x86/*.cpp" "lib/logger/src/Platform/x86/*.cpp")
 
 add_executable(ecss_services
         $<TARGET_OBJECTS:common>
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 5d89f392d3d1e9e841f887a9f80e9c4d6350792f..0000000000000000000000000000000000000000
--- a/Dockerfile
+++ /dev/null
@@ -1,38 +0,0 @@
-# This is the Dockerfile for the lightspot21/acubesat-ci:latest
-# Docker image used at the pipeline. Please take care to generate 
-# and push a new image to the lightspot21/acubesat-ci repo every 
-# 1-2 weeks in order to ensure that the tools are at the latest version.
-# 
-# P.S. Tag properly your images with --tag lightspot21/acubesat-ci when
-# building.
-
-FROM alpine:latest
-
-# Set a new work directory. DO NOT DELETE THE LINE BELOW
-WORKDIR /root/
-
-# Set up clang-tidy version 8
-RUN echo "@testing http://dl-cdn.alpinelinux.org/alpine/edge/testing" \
-    >> /etc/apk/repositories && apk update && \
-    apk add --no-cache --virtual git-deps git && \
-    apk add --no-cache build-base cmake && \
-    apk add --no-cache python3 && \
-    git clone --depth=1 https://github.com/llvm/llvm-project.git -b release/8.x && \
-    cmake \
-    -DLLVM_ENABLE_PROJECTS="clang-tools-extra;clang" \
-    -DCMAKE_BUILD_TYPE=MinSizeRel \
-    -DLLVM_TARGETS_TO_BUILD="host" \
-    -G "Unix Makefiles" ./llvm-project/llvm && \
-    make -j$(nproc) clang-tidy && mv bin/clang-tidy /usr/bin/clang-tidy && \
-    rm -rf * && apk del git-deps
-
-# Update package lists and install cmake, cppcheck, doxygen, vera++, 
-# gcc and lcov with their dependencies
-RUN apk add --no-cache findutils python3-dev \
-    cppcheck doxygen vera++@testing lcov@testing
-
-# Install gcovr
-RUN python3 -m pip install gcovr
-
-# Start a new shell
-ENTRYPOINT ["/bin/sh", "-c"]
diff --git a/doxygen.conf b/Doxyfile
similarity index 99%
rename from doxygen.conf
rename to Doxyfile
index a723c79ce42474d250494a18605c120eefa4f235..419b742a3ebf4acbc6929bfea78c59b610269b4d 100644
--- a/doxygen.conf
+++ b/Doxyfile
@@ -32,7 +32,7 @@ DOXYFILE_ENCODING      = UTF-8
 # title of most generated pages and in a few other places.
 # The default value is: My Project.
 
-PROJECT_NAME           = "ASAT CubeSat ECSS PUS Implementation"
+PROJECT_NAME           = "AcubeSAT ECSS PUS Implementation"
 
 # The PROJECT_NUMBER tag can be used to enter a project or revision number. This
 # could be handy for archiving the generated documentation or if some version
@@ -310,7 +310,7 @@ MARKDOWN_SUPPORT       = YES
 # Minimum value: 0, maximum value: 99, default value: 0.
 # This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
 
-TOC_INCLUDE_HEADINGS   = 0
+TOC_INCLUDE_HEADINGS   = 5
 
 # When enabled doxygen tries to link words that correspond to documented
 # classes, or namespaces to their corresponding documentation. Such a link can
@@ -703,7 +703,7 @@ FILE_VERSION_FILTER    =
 # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
 # tag is left empty.
 
-LAYOUT_FILE            =
+LAYOUT_FILE            = docs/DoxygenLayout.xml
 
 # The CITE_BIB_FILES tag can be used to specify one or more bib files containing
 # the reference definitions. This must be a list of .bib files. The .bib
@@ -790,7 +790,7 @@ WARN_LOGFILE           =
 # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  = README.md inc/ src/ test/Services/ServiceTests.hpp
+INPUT                  = docs/ inc/ src/ test/Services/ServiceTests.hpp
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -889,7 +889,7 @@ EXCLUDE_SYMLINKS       = NO
 # Note that the wildcards are matched against the file with absolute path, so to
 # exclude all test directories for example use the pattern */test/*
 
-EXCLUDE_PATTERNS       =
+EXCLUDE_PATTERNS       = README.md
 
 # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
 # (namespaces, classes, functions, etc.) that should be excluded from the
@@ -1008,7 +1008,7 @@ INLINE_SOURCES         = NO
 # Fortran comments will always remain visible.
 # The default value is: YES.
 
-STRIP_CODE_COMMENTS    = YES
+STRIP_CODE_COMMENTS    = NO
 
 # If the REFERENCED_BY_RELATION tag is set to YES then for each documented
 # function all documented functions referencing it will be listed.
@@ -1192,7 +1192,8 @@ HTML_STYLESHEET        =
 # list). For an example see the documentation.
 # This tag requires that the tag GENERATE_HTML is set to YES.
 
-HTML_EXTRA_STYLESHEET  = "/root/doxygen-awesome-css/doxygen-awesome.css"
+HTML_EXTRA_STYLESHEET  = "../doxygen-awesome-css/doxygen-awesome.css"
+HTML_EXTRA_STYLESHEET  = "ci/page_style/doxygen_dark_theme/custom_dark_theme.css"
 
 # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
 # other source files which should be copied to the HTML output directory. Note
diff --git a/LICENSE b/LICENSE
index f288702d2fa16d3cdf0035b15a9fcbc552cd88e7..b10fb75b0ab408fc992240c6e01a80c15b5ff857 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,674 +1,18 @@
-                    GNU GENERAL PUBLIC LICENSE
-                       Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-                            Preamble
-
-  The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
-  The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works.  By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users.  We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors.  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
-  To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights.  Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received.  You must make sure that they, too, receive
-or can get the source code.  And you must show them these terms so they
-know their rights.
-
-  Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
-  For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software.  For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
-  Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so.  This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software.  The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable.  Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products.  If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
-  Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary.  To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-                       TERMS AND CONDITIONS
-
-  0. Definitions.
-
-  "This License" refers to version 3 of the GNU General Public License.
-
-  "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
-  "The Program" refers to any copyrightable work licensed under this
-License.  Each licensee is addressed as "you".  "Licensees" and
-"recipients" may be individuals or organizations.
-
-  To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy.  The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
-  A "covered work" means either the unmodified Program or a work based
-on the Program.
-
-  To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy.  Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
-  To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies.  Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
-  An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License.  If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
-  1. Source Code.
-
-  The "source code" for a work means the preferred form of the work
-for making modifications to it.  "Object code" means any non-source
-form of a work.
-
-  A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
-  The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form.  A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
-  The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities.  However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work.  For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
-  The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
-  The Corresponding Source for a work in source code form is that
-same work.
-
-  2. Basic Permissions.
-
-  All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met.  This License explicitly affirms your unlimited
-permission to run the unmodified Program.  The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work.  This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
-  You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force.  You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright.  Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
-  Conveying under any other circumstances is permitted solely under
-the conditions stated below.  Sublicensing is not allowed; section 10
-makes it unnecessary.
-
-  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
-  No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
-  When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
-  4. Conveying Verbatim Copies.
-
-  You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
-  You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
-  5. Conveying Modified Source Versions.
-
-  You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
-    a) The work must carry prominent notices stating that you modified
-    it, and giving a relevant date.
-
-    b) The work must carry prominent notices stating that it is
-    released under this License and any conditions added under section
-    7.  This requirement modifies the requirement in section 4 to
-    "keep intact all notices".
-
-    c) You must license the entire work, as a whole, under this
-    License to anyone who comes into possession of a copy.  This
-    License will therefore apply, along with any applicable section 7
-    additional terms, to the whole of the work, and all its parts,
-    regardless of how they are packaged.  This License gives no
-    permission to license the work in any other way, but it does not
-    invalidate such permission if you have separately received it.
-
-    d) If the work has interactive user interfaces, each must display
-    Appropriate Legal Notices; however, if the Program has interactive
-    interfaces that do not display Appropriate Legal Notices, your
-    work need not make them do so.
-
-  A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit.  Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
-  6. Conveying Non-Source Forms.
-
-  You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
-    a) Convey the object code in, or embodied in, a physical product
-    (including a physical distribution medium), accompanied by the
-    Corresponding Source fixed on a durable physical medium
-    customarily used for software interchange.
-
-    b) Convey the object code in, or embodied in, a physical product
-    (including a physical distribution medium), accompanied by a
-    written offer, valid for at least three years and valid for as
-    long as you offer spare parts or customer support for that product
-    model, to give anyone who possesses the object code either (1) a
-    copy of the Corresponding Source for all the software in the
-    product that is covered by this License, on a durable physical
-    medium customarily used for software interchange, for a price no
-    more than your reasonable cost of physically performing this
-    conveying of source, or (2) access to copy the
-    Corresponding Source from a network server at no charge.
-
-    c) Convey individual copies of the object code with a copy of the
-    written offer to provide the Corresponding Source.  This
-    alternative is allowed only occasionally and noncommercially, and
-    only if you received the object code with such an offer, in accord
-    with subsection 6b.
-
-    d) Convey the object code by offering access from a designated
-    place (gratis or for a charge), and offer equivalent access to the
-    Corresponding Source in the same way through the same place at no
-    further charge.  You need not require recipients to copy the
-    Corresponding Source along with the object code.  If the place to
-    copy the object code is a network server, the Corresponding Source
-    may be on a different server (operated by you or a third party)
-    that supports equivalent copying facilities, provided you maintain
-    clear directions next to the object code saying where to find the
-    Corresponding Source.  Regardless of what server hosts the
-    Corresponding Source, you remain obligated to ensure that it is
-    available for as long as needed to satisfy these requirements.
-
-    e) Convey the object code using peer-to-peer transmission, provided
-    you inform other peers where the object code and Corresponding
-    Source of the work are being offered to the general public at no
-    charge under subsection 6d.
-
-  A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
-  A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling.  In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage.  For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product.  A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
-  "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source.  The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
-  If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information.  But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
-  The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed.  Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
-  Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
-  7. Additional Terms.
-
-  "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law.  If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
-  When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it.  (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.)  You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
-  Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
-    a) Disclaiming warranty or limiting liability differently from the
-    terms of sections 15 and 16 of this License; or
-
-    b) Requiring preservation of specified reasonable legal notices or
-    author attributions in that material or in the Appropriate Legal
-    Notices displayed by works containing it; or
-
-    c) Prohibiting misrepresentation of the origin of that material, or
-    requiring that modified versions of such material be marked in
-    reasonable ways as different from the original version; or
-
-    d) Limiting the use for publicity purposes of names of licensors or
-    authors of the material; or
-
-    e) Declining to grant rights under trademark law for use of some
-    trade names, trademarks, or service marks; or
-
-    f) Requiring indemnification of licensors and authors of that
-    material by anyone who conveys the material (or modified versions of
-    it) with contractual assumptions of liability to the recipient, for
-    any liability that these contractual assumptions directly impose on
-    those licensors and authors.
-
-  All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10.  If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term.  If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
-  If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
-  Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
-  8. Termination.
-
-  You may not propagate or modify a covered work except as expressly
-provided under this License.  Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
-  However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
-  Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
-  Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License.  If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
-  9. Acceptance Not Required for Having Copies.
-
-  You are not required to accept this License in order to receive or
-run a copy of the Program.  Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance.  However,
-nothing other than this License grants you permission to propagate or
-modify any covered work.  These actions infringe copyright if you do
-not accept this License.  Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
-  10. Automatic Licensing of Downstream Recipients.
-
-  Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License.  You are not responsible
-for enforcing compliance by third parties with this License.
-
-  An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations.  If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
-  You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License.  For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
-  11. Patents.
-
-  A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based.  The
-work thus licensed is called the contributor's "contributor version".
-
-  A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version.  For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
-  Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
-  In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement).  To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
-  If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients.  "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
-  If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
-  A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License.  You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
-  Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
-  12. No Surrender of Others' Freedom.
-
-  If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all.  For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
-  13. Use with the GNU Affero General Public License.
-
-  Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work.  The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
-  14. Revised Versions of this License.
-
-  The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-  Each version is given a distinguishing version number.  If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation.  If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
-  If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
-  Later license versions may give you additional or different
-permissions.  However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
-  15. Disclaimer of Warranty.
-
-  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-  16. Limitation of Liability.
-
-  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
-  17. Interpretation of Sections 15 and 16.
-
-  If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
-                     END OF TERMS AND CONDITIONS
-
-            How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <https://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
-  If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
-    <program>  Copyright (C) <year>  <name of author>
-    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
-  You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-<https://www.gnu.org/licenses/>.
-
-  The GNU General Public License does not permit incorporating your program
-into proprietary programs.  If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library.  If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.  But first, please read
-<https://www.gnu.org/licenses/why-not-lgpl.html>.
+MIT License
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index af9d33d97cdac80138dee87ff8c9d231010148fa..42ee0c7793aba48ee85df7c0fa41b5cb94d5d567 100644
--- a/README.md
+++ b/README.md
@@ -4,12 +4,33 @@ In this repository you can find the implementation of the ECSS services, based
 on the [ECSS-E-ST-70-41C](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/)
 standard.
 
+## Documentation
+You can read the complete documentation of this project at https://acubesat.gitlab.io/obc/ecss-services/docs/.
+This includes installation instructions, a usage guide, and detailed descriptions of all public functions and definitions.
+
 ## Directories
 - **ci**: GitLab CI configuration files
+- **docs**: Source code usage and development documentation
 - **inc**: All headers and libraries used in the code
 - **lib**: External libraries used in the code
 - **src**: All source files are included here
 - **test**: Unit test implementation
 
-## Documentation
-Full documentation about this project based on code comments is available at https://acubesat.gitlab.io/obc/ecss-services/docs/.
+## Implementation status
+
+| Service Type | Service Name                   | Implementation   |
+|--------------|--------------------------------|------------------|
+| ST[01]       | Request Verification           | Full             |
+| ST[03]       | Housekeeping                   | Partial          |
+| ST[04]       | Parameter Statistics Reporting | Partial          |
+| ST[05]       | Event Reporting                | Partial          |
+| ST[06]       | Memory Management              | Partial          |
+| ST[08]       | Function Management            | Full             |
+| ST[11]       | Time-based Scheduling          | Partial          |
+| ST[12]       | On-board Monitoring            | Work in progress |
+| ST[13]       | Large Packer Transfer          | Partial          |
+| ST[15]       | On-board Storage and Retrieval | Work in progress |
+| ST[17]       | Test                           | Partial          |
+| ST[19]       | Event-action                   | Partial          |
+| ST[20]       | Parameter Management           | Partial          |
+| ST[23]       | File Management                | Work in progress |
diff --git a/ci/clang-format.sh b/ci/clang-format.sh
index d8718f524a3230ced99febabba5cdd08b1e72bf0..911b4f5f58efbcf71b67f6d4181ba0d1e3b78ffc 100755
--- a/ci/clang-format.sh
+++ b/ci/clang-format.sh
@@ -9,6 +9,6 @@
 
 echo -e "\033[0;34mRunning clang-format...\033[0m"
 
-cd "$(dirname "$0")"
-clang-format-7 -i `find ../src/ ../inc/ ../test/ -type f -regextype posix-egrep -regex '.*\.(cpp|hpp|c|h)'` \
-    -verbose $@
+cd "$(dirname "$0")" || exit
+clang-format -style=file -i "$(find ../src/ ../inc/ ../test/ -type f -regextype posix-egrep -regex '.*\.(cpp|hpp|tpp|c|h)')" \
+    -verbose "$@"
diff --git a/ci/vera.profile b/ci/vera.profile
deleted file mode 100644
index 2382359f944d16804f95c08abf35c78c0d758ae8..0000000000000000000000000000000000000000
--- a/ci/vera.profile
+++ /dev/null
@@ -1,25 +0,0 @@
-
-set rules {
-  F001
-  F002
-  L001
-  L003
-  L004
-  L005
-  L006
-  T001
-  T002
-  T004
-  T005
-  T006
-  T007
-  T008
-  T009
-  T010
-  T012
-  T015
-  T016
-  T017
-  T018
-  T019
-}
diff --git a/ci/vera.sh b/ci/vera.sh
deleted file mode 100755
index 2ece81da50bfefdefba08effef4bbadc3f7d415f..0000000000000000000000000000000000000000
--- a/ci/vera.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env bash
-
-#
-# Code style checks using vera++
-#
-# Usage:
-# $ ci/vera.sh
-#
-
-echo -e "\033[0;34mRunning vera++...\033[0m"
-
-cd "$(dirname "$0")/.."
-vera++ --error --parameter max-line-length=120 --profile custom \
-    `find src inc test -type f -regextype posix-egrep -regex '.*\.(cpp|hpp|c|h)' -not -wholename 'inc/Logger.hpp'`
diff --git a/docs/DoxygenLayout.xml b/docs/DoxygenLayout.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b4ce28e401ca5d2f41336898ee7675542d0af31f
--- /dev/null
+++ b/docs/DoxygenLayout.xml
@@ -0,0 +1,39 @@
+<doxygenlayout version="1.0">
+  <!-- Generated by doxygen 1.8.17 -->
+  <!-- Navigation index tabs for HTML output -->
+  <navindex>
+    <tab type="mainpage" visible="yes" title=""/>
+    <tab type="pages" visible="yes" title="Pages" intro=""/>
+    <tab type="user" url="@ref Services" title="Services"/>
+    <tab type="modules" visible="yes" title="" intro=""/>
+    <tab type="namespaces" visible="yes" title="">
+      <tab type="namespacelist" visible="yes" title="" intro=""/>
+      <tab type="namespacemembers" visible="yes" title="" intro=""/>
+    </tab>
+    <tab type="interfaces" visible="yes" title="">
+      <tab type="interfacelist" visible="yes" title="" intro=""/>
+      <tab type="interfaceindex" visible="$ALPHABETICAL_INDEX" title=""/>
+      <tab type="interfacehierarchy" visible="yes" title="" intro=""/>
+    </tab>
+    <tab type="classes" visible="yes" title="">
+      <tab type="classlist" visible="yes" title="" intro=""/>
+      <tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
+      <tab type="hierarchy" visible="yes" title="" intro=""/>
+      <tab type="classmembers" visible="yes" title="" intro=""/>
+    </tab>
+    <tab type="structs" visible="yes" title="">
+      <tab type="structlist" visible="yes" title="" intro=""/>
+      <tab type="structindex" visible="$ALPHABETICAL_INDEX" title=""/>
+    </tab>
+    <tab type="exceptions" visible="yes" title="">
+      <tab type="exceptionlist" visible="yes" title="" intro=""/>
+      <tab type="exceptionindex" visible="$ALPHABETICAL_INDEX" title=""/>
+      <tab type="exceptionhierarchy" visible="yes" title="" intro=""/>
+    </tab>
+    <tab type="files" visible="yes" title="">
+      <tab type="filelist" visible="yes" title="" intro=""/>
+      <tab type="globals" visible="yes" title="" intro=""/>
+    </tab>
+    <tab type="examples" visible="yes" title="" intro=""/>
+  </navindex>
+</doxygenlayout>
diff --git a/docs/design.md b/docs/design.md
new file mode 100644
index 0000000000000000000000000000000000000000..e08bea1267c2f1c192cb9c9aaab9a8d00467b7cc
--- /dev/null
+++ b/docs/design.md
@@ -0,0 +1,115 @@
+# Design
+
+@tableofcontents
+
+The main building block of this library are the @ref Services and the @ref Message.
+
+@dot
+digraph G {
+  rankdir=LR;
+
+  space [shape=box,label="Spacecraft"]
+  ground [shape=box,label="Ground Station"]
+
+  space:se -> ground:sw [label="Response (TM)",color="#039be5"]
+  ground:nw -> space:ne  [label="Request (TC)",color="#f4511e"]
+}
+@enddot
+
+Each Service is represented by a class. All Services reside inside the @ref ServicePool. Messages handle Telecommands
+and Telemetry in the form of @ref Message "Messages". A Message represents a _Space Packet_, as defined by [CCSDS
+133.0-B-2](https://public.ccsds.org/Pubs/133x0b2e1.pdf). Messages are managed and distributed by the @ref MessageParser class.
+
+## Reading and Writing Messages
+
+The Message is the simplest unit of information that is transferred between space and ground. A Message contains its
+payload data in a _binary string_ format, combined by some essential header information.
+
+As the message data is in a binary format, special functions have been written to facilitate reading from and writing to
+the Message:
+```cpp
+// Reading data
+message.readUint32();
+message.readFloat();
+
+// Writing data
+message.appendUint32();
+message.appendFloat();
+
+// You can also use templated functions, when the type is obvious and unambiguous
+message.append<int16_t>(10);
+message.append(someVariable);
+```
+
+You can visit @ref Message for a complete description of all functions available for data manipulation.
+
+You will typically need access to `.read()` and `.append()` functions only when developing a Service. Any manipulation
+of Messages will typically be done only inside the Service classes.
+
+## The Lifetime of a Message
+@dot
+digraph H {
+    rankdir=LR
+
+    Message [label="TM Message",URL="@ref Message",shape=box,style=filled,color="#039be5"]
+    D [label="storeMessage()",fontname="monospace"]
+
+    subgraph cluster_0 {
+        style=filled
+        color=lightgrey
+        node [style=filled,color=white]
+
+        A [label="@ref MessageParser",fontname="monospace"]
+        B [label="Service::function()",fontname="monospace"]
+
+        A -> B
+
+        label = "Telemetry (TM) processing";
+    }
+
+    subgraph cluster_1 {
+        style=filled
+        color=lightgrey
+        node [style=filled,color=white]
+
+        C [label="Service::function()",fontname="monospace"]
+        TCMessage [label="TC Message",shape=box,style=filled,color="#f4511e"]
+
+        C -> TCMessage
+
+        label = "Telecommand (TC) generation";
+    }
+
+    Message -> A
+    B -> C
+    TCMessage -> D
+}
+@enddot
+
+ecss-services can receive and transmit ECSS messages from and to a ground segment or other subsystems. The codebase is
+responsible for the parsing, execution and responses to those messages. Additionally, it provides the tools to tailor
+the actions of each service to your subsystem.
+
+### Message reception
+
+When any Telemetry is received, it is first sent in raw format to the @ref MessageParser class. The MessageParser is
+responsible for reading the headers of the data, converting it to a @ref Message class, and sending it to the relevant
+service.
+
+Inside every Service, each TM message type is tied to a single function that receives the Message as an argument. This
+function is responsible for parsing and starting the execution of the contents of the message. It will also generate the
+relevant TC, if needed.
+
+### Message generation
+
+After creating a Message and populating it with data, it can then be sent on its way. The @ref Service::storeMessage
+function is responsible for propagating the TC message to the next layer.
+
+The next step can be:
+- Storage of the message for later retransmission (ST[15] service)
+- Immediate transmission of the message to its recipient (e.g. via an antenna, ST[14] service)
+- Transmission of the message to other observers (e.g. logging for debugging purposes)
+
+Sometimes, you may want to generate TC without any external stimulus. For example, when an event occurs (e.g. antenna
+deployed), you will want to notify the ecss-services library to generate the corresponding TC and take any other actions
+required (e.g. event-action definitions). To generate arbitrary TC, just call the relevant functions on each Service class.
diff --git a/docs/ecss_overview.md b/docs/ecss_overview.md
new file mode 100644
index 0000000000000000000000000000000000000000..82e0011181d26b321d3163e2f5bb1d947e97eb54
--- /dev/null
+++ b/docs/ecss_overview.md
@@ -0,0 +1,203 @@
+# Overview to the PUS standard {#pus-overview}
+
+@tableofcontents
+
+The `ecss-services` library implements the ECSS-E-ST-70-41C **Telemetry and telecommand packet utilization** standard,
+released by the European Cooperation for Space Standardization. The complete text of the standard is
+[available after a free registration](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/).
+This page summarises the core concepts of the standard, and how it can be useful to a typical space mission.
+
+The ECSS-E-ST-70-41C standard (also called **Packet Utilisation Standard**, or **PUS** for short) lists a number of
+**services** that a spacecraft can support. These services define basic operational functions of a spacecraft, such as
+file storage, statistics generation, responding to events and many others. Each Service is also accompanied with
+different Message types. These messages can be:
+- **Telecommand (TC) requests**: Messages from a Ground Station to the Spacecraft. These messages often request specific
+  information, or command the spacecraft to take specific actions.
+- **Telemetry (TM) reports**: These can be responses to other commands, or they can inform the Ground Station about
+  interesting events and information happening on the spacecraft.
+
+The standard clearly defines the **byte-level structure** of each Message, as well as the **capabilities** that each
+  Service provides.
+
+## Important entities
+A few entities defined in ECSS-E-ST-70-41C are used throughout the standard and are useful to keep in mind:
+1. **@ref Parameter "Parameters"**. A parameter is a value (often numerical) that represents a small piece of data which
+   can be sent to or received from the satellite. Parameters can represent sensor outputs, configuration values, status
+   indicators, or everything else.
+
+   Parameters are mainly handled by the @ref ParameterService, but they are regularly used throughout the code when
+   dealing with satellite data.
+
+   Parameters can be numbers of different types, strings or (rarely) more complex entities.
+2. **Events**. Events represent expected or unexpected occurences on the spacecraft.
+
+   The @ref EventReportService is mainly responsible for management of on-board events. Other services provide the
+   capability of generating or responding to on-board events.
+
+3. **Application Processes** (AP). An Application Process is any physical (hardware) or logical (software) entity that
+   can handle PUS messages.
+
+   In most cases, an Application Process will be a single microcontroller or subsystem. For example, the
+   <span title="On-Board Computer">OBC</span>, <span title="Attitude Determination and Control Subsystem">ADCS</span> and Ground
+   Station may be different Application Processes.
+
+   Typically, a message will have an AP as a source, and another AP as its destination. For example, if the Ground
+   Station wants to send a message to the OBC, it can send a Telecommand where the OBC Application Process is listed as
+   the destination.
+
+   @todo Application Process handling is not yet implemented in this library. See
+   [#55](https://gitlab.com/acubesat/obc/ecss-services/-/issues/55).
+
+## The services
+
+This section provides a very short overview of each ECSS service, and how it can be typically used in a space mission.
+
+All services are designed to be **highly configurable during flight**. While they can have a pre-defined configuration
+at launch, the Ground Station can send simple telecommands (TC) to modify the services.
+
+The standard aptly defines itself as a _"menu"_ from which services are chosen. The following services do not need to be
+fully implemented for each mission, but need to be _tailored_ according to the requirements and needs of the spacecraft
+and its environment.
+
+Each service is further divided into some **subservices** that are logical groupings of a service's functionality. A
+large space mission may split every subservice into different parts of the hardware. However, this implementation _makes
+no distinction between subservices_.
+
+<b>`ST[01]`: Request verification</b>
+
+Provides acknowledgement or failure reports for executed commands. This service essentially informs the operators about
+the status of TCs sent to the spacecraft, and reports any occurred errors during parsing or execution.
+
+See @ref #error-handling for more information regarding error handling on this library.
+
+<b>`ST[02]`: Device access</b>
+
+Allows toggling, controlling and configuring on-board peripherals that rely on simple protocols to communicate
+(such as I2C).
+
+<b>`ST[03]`: Housekeeping</b>
+
+Produces periodic TM reports with values of parameters. This service is the one providing the most essential information
+about the status of the spacecraft.
+
+ST[03] is also highly configurable during flight. For example, it allows:
+- Selecting which parameters we want to be periodically transmitted
+- How often the parameters should be transmitted
+- Enabling/disabling specific types of periodic reports at will
+
+<b>`ST[04]`: Parameter statistics reporting</b>
+
+Allows reporting statistics (min, max, mean, standard deviation) for specific parameters over specified durations. This
+is a data-efficient alternative to the `ST[03]` housekeeping service.
+
+<b>`ST[05]`: Event reporting</b>
+
+Generates reports when notable occurrences take place on-board, such as:
+
+- Autonomous on-board actions
+- Detected failures or anomalies
+- Predefined steps during an operation
+
+<b>`ST[06]`: Memory management</b>
+
+Allows writing and reading directly from an on-board memory unit. This can be useful for debugging and investigative
+purposes, fetching mission data, or uploading new software to the spacecraft's memories. The service also allows
+downlinking and uplinking files in a file system.
+
+<b>`ST[08]`: Function management</b>
+
+Provides the capability of running predefined actions that can receive further parameters. These actions can correspond
+to payload, platform, or any other functionality.
+
+<b>`ST[09]`: Time management</b>
+
+Allows periodic reporting of the current spacecraft time for observability and correlation purposes.
+
+<b>`ST[11]`: Time-based scheduling</b>
+
+Allows the operators to <b>"time-tag"</b> telecommands for execution at future timestamps, instead of immediately. Its use
+is essential when communication with your spacecraft is not guaranteed for the entirety of its orbit.
+
+This is one of the most complicated services to use, as it allows complex functionality such as:
+- Running a command after some duration of time
+- Running a command at a specified time
+- Running a command after successful execution of another command
+- Running a command only if another command fails
+
+<b>`ST[12]`: On-board monitoring</b>
+
+This service allows checking parameter values to ensure that they remain within configurable limits. Whenever a
+violation occurs, an `ST[05]` event can be optionally generated for further processing.
+
+<b>`ST[13]`: Large packet transfer</b>
+
+Provides a method of message segmentation, for message payloads that are too large to fit within the maximum allowed
+length for TC or TM.
+
+The maximum limits are usually imposed by the lower communication layers. For example, the Communications subsystem of a
+satellite may use a transceiver that can only send packets up to 64 bytes long.
+
+<b>`ST[14]`: Real-time forwarding control</b>
+
+This service is responsible of controlling which types of generated reports are immediately transmitted to the ground.
+
+<b>`ST[15]`: On-board storage and retrieval</b>
+
+Allows storing generated TM reports on-board.
+
+If your spacecraft does not have constant communication with the Ground Station (e.g. a low-earth orbit satellite), it
+will be necessary to store all messages until they can be downlinked safely. The ST[15] service can be used to:
+- Downlink all packets that haven't yet been transmitted
+- Filter packets by their type
+- Filter packets by the time of their creation
+
+<b>`ST[17]`: Test</b>
+
+Allows performing on-board connection checks, similar to "pinging" a machine and asking if it is alive.
+
+<b>`ST[18]`: On-board operations procedure</b>
+
+Allows loading, controlling (start, suspend, resume, abort) and configuring On-Board Control Procedures, which are
+sequences of commands written in an application-specific language.
+
+<b>`ST[19]`: Event-action</b>
+
+Provides the capability of automatically executing arbitrary actions when an `ST[05]` event is triggered. This service
+can be an essential component when planning the autonomous functionalities of a spacecraft.
+
+<b>`ST[20]`: On-board parameter management</b>
+
+Provides the capability of reading and setting on-board parameters.
+
+<b>`ST[21]`: Request sequencing</b>
+
+Allows operators to load series of TCs to be executed in a sequential order.
+
+<b>`ST[22]`: Position-based scheduling</b>
+
+Provides the capability of executing TCs when the spacecraft reaches a specific point in its orbit.
+
+<b>`ST[23]`: File management</b>
+
+Provides the capability of managing on-board file systems, with functions such as *copy*, *move*, *delete*, or *create
+directory*.
+
+The capability also exists to define _custom_ services and message types, if needed for each mission.
+
+## Trivia
+- Version C of the standard contains 656 pages, often filled with verbose requirements and difficult definitions. Every
+  requirement is identified by its requirement ID. [Version
+  A](https://ecss.nl/standard/ecss-e-70-41a-ground-systems-and-operations-telemetry-and-telecommand-packet-utilization/)
+  contains a simpler but outdated and less rigorous version of the standard.
+- This standard is not optimised for heavy communication, e.g. real-time audio/video, large files, etc. Other protocols
+  and solutions can be used for this purpose (e.g. [CFDP](https://public.ccsds.org/Pubs/720x1g4.pdf)).
+- While the standard is mainly designed for ground-space communication, it can theoretically be used to coordinate
+  communication between different subsytems onboard a spacecraft.
+- This repository _does not implement the entire standard_. A subset has been selected that should be useful to our
+  mission, or to any CubeSat that requires some basic functionality. ECSS characteristically mentions that this standard
+  serves as a "menu" from which to select all relevant services.
+- While all services are designed in a modular manner, there are often various relationships between them (e.g. ST[05]
+  event reporting — ST[19] event-action). Additionally, some services provide _observable_ parameters (e.g. ST[23] may
+  provide the available memory space as an observable)
+- Each service can be implemented by a single microcontroller in a spacecraft, or by multiple different units. The
+  standard does not define any implementation restrictions on this regard.
diff --git a/docs/error_handling.md b/docs/error_handling.md
new file mode 100644
index 0000000000000000000000000000000000000000..511b361b950e9c6a44677bbe8cb8a2aea1104dc2
--- /dev/null
+++ b/docs/error_handling.md
@@ -0,0 +1,140 @@
+# Error Handling {#error_handling}
+
+@tableofcontents
+
+Errors can show up in almost any location on this library, for example:
+- When an incorrect Message is received, containing invalid information (due to a faulty channel or operator error)
+- When an unexpected bug occurs
+- When radiation or a hardware issue leads to an unexpected software state
+
+This library incorporates an error handling framework to allow proper identification and recovery from various errors,
+without violating the performance and memory constraints of an embedded system.
+
+As [exceptions are not used](docs/writing_code.md), this library uses a slightly different approach to testing and
+reporting errors from what is standard in C++.
+
+@note Errors do not have to be fatal or lead to a loss of the subsystem. Most errors should be recoverable with minimal impact
+to the rest of the system.
+
+## The ErrorHandler class
+
+The processing of errors in the `ecss-services` library is managed by the @ref ErrorHandler class. The ErrorHandler
+provides a number of convenience functions to report any errors that occur using a single line of code (demonstrated below).
+
+In order to convey the most information using the minimal amount of data, all **error types** are encoded into different
+enumerations inside the @ref ErrorHandler.
+
+When deemed necessary, the developer can add additional **auxiliary data** to each error, to provide more observability
+to the ground station if needed.
+
+@todo Auxiliary data not implemented yet, see [#28](https://gitlab.com/acubesat/obc/ecss-services/-/issues/28)
+
+### The ST[01] service
+
+The `ST[01] service request verification` service is responsible for generating error notifications when an issue is
+found in a **received Telemetry message**. It does not cover errors generated by internal functions.
+
+For example, the following incidents **will** trigger an ST[01] error:
+- Ground Station asks for Parameter #205, but Parameter #205 does not exist
+- Ground Station asks to disable Temperature Sensor, but Temperature Sensor is already disabled
+- Ground Station sends a corrupted message that makes no sense
+
+The following incidents **will not** trigger an ST[01] error:
+- A division by zero happens
+- A loop that should run 5 times runs 101 times
+- A temperature sensor experiences a failure
+
+Any ST[01] failure report (i.e. a TM message generated by ST[01] to inform us about a problem that was caused by a TC
+message) is tied to **the TM that caused it**. That means that every ST[01] report contains information about the
+problematic TM, such as its packet type and counter. The report also contains the **error type** and any **auxiliary
+data** available for this error.
+
+To report an error tied to a Message, you can use @ref ErrorHandler::reportError.
+```cpp
+ErrorHandler::reportError(message, errorType);
+```
+
+You can also use convenience functions like @ref Message::assertType, @ref Message::assertTM, or @ref Message::assertTC.
+These will check if your Message is of a specific type, and call `reportError` on their own if it is not.
+
+### Internal errors
+
+Errors that are not tied to a specific TC and are caused independently by a microcontroller are termed _internal
+errors_. While they are still managed by the @ref ErrorHandler, they are not part of the standard.
+
+To report an internal error, you can use @ref ErrorHandler::reportInternalError.
+```cpp
+ErrorHandler::reportInternalError(errorType);
+```
+
+You can also use the convenience @ref ASSERT_INTERNAL macro, which checks if a condition is true, and calls @ref
+ErrorHandler::reportInternalError automatically if not:
+```cpp
+ASSERT_INTERNAL(5 > 3, ErrorHandler::MathematicsIsBroken);
+```
+
+## Function results
+An important question that comes to mind when disabling extensions is how errors are propagated through the code. When a
+function is executed, it must be clear when the result of the function is correct and when it is not. Additionally, any
+parts of the code that depend on a successful result may need to avoid being executed when an error occurs.
+
+While there are different available approaches to failure propagation (e.g.
+[errno](https://www.tutorialspoint.com/cprogramming/c_error_handling.htm), error pointers as arguments etc.), the
+`ecss-services` library indicates the error in the **return value** of each function, through the @ref Result class.
+
+The @ref Result class can contain **either**:
+- A successful result (e.g. an `int` or a `string` or a @ref Parameter), or
+- An error, explaining why it failed to find a result
+
+By calling the relevant functions of a @ref Result, the user can access its contents on success, or abort the operation
+in case of failure.
+
+### Example usage
+
+The simplest usage of a `Result` might be as follows:
+```cpp
+Result<T,E> something = ...
+
+if (something.error()) {
+    // No! An error happened! We cannot continue!
+    return;
+}
+
+// No errors, we can happily use the result
+something->coolFunction();
+```
+
+### Error propagation
+The @ref Result class offers many convenience functions to aid in common, yet complex situations.
+
+For example, we may want a function that takes the average of many parameters. However, this function will fail if one
+of those parameters does not exist.
+
+```cpp
+Result<float, Error> getAverage(ParameterArray parameterIds) {
+    // Don't allow entering an empty array, by throwing the relevant error
+    if (parameterIds.empty()) {
+        // Error automatically gets converted to Result<float, Error>
+        return ErrorHandler::EmptyInputError;
+    }
+
+    float sum = 0;
+
+    for (auto parameterId : parameterIds) {
+        // You should use `auto` here. We keep the full type for demonstration purposes
+        Result<Parameter, Error> parameter = ParameterService::getParameterById(parameterId);
+
+        if (parameter.error()) {
+            // Result<anything, Error> automatically gets converted to Result<float, Error>
+            return parameter;
+        }
+
+        sum += parameter.getValue();
+    }
+
+    // float automatically gets converted to Result<float, Error>
+    return sum / parameterIds.count();
+}
+```
+
+@todo Result class not implemented and not used. See [#134](https://gitlab.com/acubesat/obc/ecss-services/-/issues/134)
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000000000000000000000000000000000000..ace2f3aa86c7a9a4c82171ec17e7de57ac7ee955
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,23 @@
+# AcubeSAT ECSS Services {#mainpage}
+
+This repository contains the implementation of the [ECSS-E-ST-70-41C](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/) standard, as used in the AcubeSAT mission.
+
+## Installation
+
+Read [Installation](docs/installation.md) to see how to download, compile and install ecss-services.
+
+Read [Usage with Microcontroller](docs/usage_with_microcontroller.md) to see how to integrate ecss-services with your
+satellite or embedded system.
+
+## How to read this documentation
+
+This documentation contains the technical documentation for the implementation of the ECSS documentation. For a description of the requirements see [version A](https://ecss.nl/standard/ecss-e-70-41a-ground-systems-and-operations-telemetry-and-telecommand-packet-utilization/) and [version C](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/) of the standard.
+
+To get into the documentation you can read:
+1. A list of [pages](pages.html) that contain generic information on the philosophy, design and usage of the services
+2. A list of [modules](modules.html) that group interesting functionality with the relevant instructions
+3. A list of [classes](annotated.html) that contain the most interesting entities and objects in the code
+4. A list of [namespaces](namespaces.html) that contain grouped utility functions or variables
+
+A good starting point would be the @ref Services module. You can browse through each ECSS Service based on the functionality you desire.
+
diff --git a/docs/installation.md b/docs/installation.md
new file mode 100644
index 0000000000000000000000000000000000000000..d6f5916ba8842160b8f7c21054ed8e860c86e0d9
--- /dev/null
+++ b/docs/installation.md
@@ -0,0 +1,68 @@
+# Installation
+
+@tableofcontents
+
+The ECSS Services library was is developed and tested on Linux. It is possible to use Mac OS or Windows, if the relevant
+tools are installed.
+
+## Required software {#required-software}
+Before getting started, you will need to make sure that you have the required compilation tools installed. We use the
+[CMake](https://cmake.org/) build system for all the instrumentation required for compiling.
+
+On Debian-based systems, a command like the following should suffice:
+```bash
+sudo apt install git cmake make binutils build-essential
+```
+
+If you want to run the **optional tools**, including documentation generators and code checkers, you will need to
+install them as well:
+```bash
+sudo apt install doxygen clang-tidy clang-format gcovr lcov cppcheck
+```
+
+## Compiling {#compiling}
+To download and build the ecss-services repo, use the following terminal commands:
+
+1. Clone the repository, along with its submodules
+   ```bash
+   git clone --recursive https://gitlab.com/acubesat/obc/ecss-services.git
+   ```
+
+2. Create the `build` directory. This is where the executables and outputs will be stored.
+   ```bash
+   cd ecss-services
+   mkdir build
+   ```
+
+3. Create the CMake project and Makefile
+   ```bash
+   cmake ..
+   ```
+
+4. Run the build!
+   ```bash
+   make -j${nproc}
+   ```
+
+You will be able to run the `./ecss-services` script to see how the output of the services may look like, or `./tests`
+to run some unit tests and verify that the code works correctly.
+
+## Other tasks {#other}
+
+Apart from compiling the services, you can also generate other outputs if you want.
+
+### Integrating with your code {#integrating}
+While ecss-services can run independently, the repo is built as a library. Follow the instructions in
+[Usage with a microcontroller](docs/usage_with_microcontroller.md) to integrate with your space or ground segment code.
+
+
+### Creating the documentation {#documentation}
+To create the documentation files, make sure you have `doxygen` installed. Afterwords, you can just run from the root
+directory:
+```bash
+doxygen
+```
+
+### Running static analysis scripts {#static-analysis}
+To run the static analysis scripts, you can open `.gitlab-ci.yml` and find the script you are interested in. Make sure
+to have the relevant static analysis tool installed beforehand!
diff --git a/docs/protocols.md b/docs/protocols.md
new file mode 100644
index 0000000000000000000000000000000000000000..dc64877bd34f34bd423a6cf4667e27c9f4e56fb4
--- /dev/null
+++ b/docs/protocols.md
@@ -0,0 +1,42 @@
+# Protocols and Standards
+
+@tableofcontents
+
+This library implements the application layer as defined in the following protocols:
+- [ECSS-E-ST-70-41C](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/)
+  (Packet Utilisation Standard)
+- [CCSDS 133.0-B-2](https://public.ccsds.org/Pubs/133x0b2e1.pdf) (Space Packet)
+
+<table>
+<tr>
+<td colspan="4" align="center">Space Packet</td>
+</tr>
+<tr>
+<td colspan="1" align="center">Space Packet Primary Header</td>
+<td colspan="3" align="center">Packet Data Field</td>
+</tr>
+<tr>
+<td colspan="1" align="center">Space Packet Primary Header</td>
+<td colspan="1" align="center">ECSS Secondary Header</td>
+<td colspan="1" align="center">ECSS Data Field</td>
+<td colspan="1" align="center">ECSS Error Control</td>
+</tr>
+</table>
+
+These protocols are supposed to be encapsulated in protocols representing the higher layers. For example, an RF message
+may be placed in a CCSDS 132.0-B-3 frame, while a message transmitted via wire may be placed inside a CAN bus frame.
+The user of this library must specify how a message is received and transmitted (see @ref usage-mcu).
+
+The headers defined by the different protocols define different attributes of the message, such as:
+- Its length
+- Its source
+- Its destination
+- A counter to identify lost messages
+- The time it was generated
+- Whether a response is expected on success
+- The number of the ECSS service of the message
+
+The **error control** field embeds an **Error-Detecting Code** to investigate if the message was transmitted and
+received correctly, and was not corrupted by faults in the communication channel. It is managed by @ref
+Message::finalize.
+
diff --git a/docs/usage_with_microcontroller.md b/docs/usage_with_microcontroller.md
new file mode 100644
index 0000000000000000000000000000000000000000..05f09b6f1f8e3e714846e0a0eab2e618b4d81c35
--- /dev/null
+++ b/docs/usage_with_microcontroller.md
@@ -0,0 +1,172 @@
+# Usage with a microcontroller {#usage-mcu}
+
+@tableofcontents
+
+ecss-services relies on many functions that may not be readily available to a microcontroller, such as printing,
+getting the time, transmitting packets etc. That's why you will have to implement a few simple functions by yourself
+when porting this library to a microcontroller.
+
+The library is also attempting to be modular enough to support the needs of your own spacecraft. After setting it up,
+you will be able to specify your own parameters, functions, events, and other definitions. All limits and numeric
+constants are not hard-coded, but can be modified in @ref ECSSDefinitions.
+
+@attention The ecss-services repository is not designed to be used as a static or dynamic library. Due to the embedded
+nature of the project, ecss-services may have a different binary output for every different usecase. You will need to
+compile this library independently for different projects.
+
+## Examples
+
+Some people have already integrated this library with their own code:
+- [AcubeSAT OBC Software](https://gitlab.com/acubesat/obc/obc-software), integration with ATSAMV71Q21 and FreeRTOS
+- [AcubeSAT FDIR Thesis](https://github.com/kongr45gpen/fdir-demo), integration with ATSAMV71Q21 and FreeRTOS
+- [AcubeSAT OBC Mockup](https://gitlab.com/acubesat/obc/mockup-4), integration with STM32L4S9 and FreeRTOS
+
+## The Platform directory
+
+ecss-services declares some basic functions, but does not implement them. You, the user of this library, must write the
+code for these functions that will make them work on your platform.
+
+The `inc/Platform` and `src/Platform` directories contain some platform-specific code that has already been prepared.
+The **`x86`** subdirectory includes all the code necessary to run on a Linux platform, i.e. your computer. This code is
+only used when building for a desktop environment, and is not compiled when building for a microcontroller.
+
+## Integration with CMake
+
+While `ecss-services` is a typical C++ project, it is built and based around the [CMake build
+system](https://cmake.org/). If you are using CMake in your project, you can easily integrate `ecss-services` in your
+`CMakeLists.txt` file, just by adding these lines:
+
+```cmake
+# ecss-services include directories
+include_directories(lib/ecss-services/inc)
+include_directories(lib/ecss-services/inc/Platform/x86)
+include_directories(lib/ecss-services/lib/etl/include)
+include_directories(lib/ecss-services/lib/logger/inc)
+
+# Build ecss-services and link it to the project
+add_subdirectory(lib/ecss-services EXCLUDE_FROM_ALL)
+target_link_libraries(<project_name> common)
+```
+
+Assuming your project is called `<project_name>` and the library is installed in `lib/ecss-services`, the above lines
+will automatically compile ecss-services for you using the compiler applicable to your application, and link them to
+your project.
+
+## Functions to implement
+
+The following sections list and explain the functions that need to be implemented by library users. It is enough to
+place the function body inside a compiled `.cpp` file. Otherwise, the linker will show errors about undefined functions.
+
+For more details on the requirements, arguments and return values of each function, refer to their respective documentation.
+
+### Logger
+
+The logger is responsible for outputting messages through a serial interface used for debugging purposes.
+
+You need to define the following function:
+```cpp
+void Logger::log(Logger::LogLevel level, etl::istring & message);
+```
+
+This function is responsible for getting a log level and log message, and outputting them through a serial port that can
+be inspected by the developers. It is suggested to add any extra information that is available, e.g. current time or
+execution thread.
+
+An example definition can be as follows:
+```cpp
+void Logger::log(Logger::LogLevel level, etl::istring &message) {
+    etl::string<20> time;
+    etl::to_string(MCU_Get_Time(), time);
+
+    etl::string<250> output;
+    output.append(time);
+    output.append(" ");
+    output.append(message);
+
+    MCU_Serial_Write(output);
+}
+```
+
+#### Setting the log level
+You will also need to set the **minimum log level** of your application by setting the relevant `LOGLEVEL` constants.
+
+For example, you can add this to your `CMakeLists.txt` to log all messages:
+```cmake
+add_compile_definitions(LOGLEVEL_TRACE)
+```
+
+For a list of all possible log levels, refer to the documentation of the Logger.
+
+@note If you want to have different log levels for different parts of your application, you can use [`target_compile_definitions`](https://cmake.org/cmake/help/latest/command/target_compile_definitions.html).
+
+@note All logs with a level lower than the specified one will not be compiled at all, and will not be included in any
+form in the resulting binary. This means that disabled log messages _will not_ have any negative impact on the
+performance and size of your program.
+
+### Message transmission
+
+Whenever PUS telemetry is generated, it needs to be transmitted or sent to a receiver. This is the responsibility of the
+@ref Service::storeMessage function.
+
+In this function, you can transmit the message via an antenna, send it through an interface for debugging, or both.
+
+An example definition can be as follows:
+```cpp
+void Service::storeMessage(Message& message) {
+	message.finalize();
+
+	MCU_Antenna_Transmit(message.data, message.dataSize);
+
+	LOG_DEBUG << "Just sent ST[" << static_cast<int>(message.serviceType) << "] message";
+}
+```
+
+### Error handling
+
+The @ref ErrorHandler::logError is responsible for logging errors for debugging purposes.
+
+@note Any generated errors are already transmitted to the ground segment via @ref Service::storeMessage. The @ref
+ErrorHandler::logError function is used strictly for debugging purposes, and can be left empty if desired.
+
+An example definition can be as follows:
+```cpp
+template <typename ErrorType>
+void ErrorHandler::logError(const Message& message, ErrorType errorType) {
+	LOG_ERROR << "Error in ["
+        << static_cast<uint16_t>(message.serviceType)
+        << ", " << static_cast<uint16_t>(message.messageType)
+        << "]: " << errorType;
+}
+
+template <typename ErrorType>
+void ErrorHandler::logError(ErrorType errorType) {
+	LOG_ERROR << "Error: " << errorType;
+}
+```
+
+## Service initialisation
+
+Platform-specific code also gives a chance to every Service to use pre-initialised entities (e.g. parameters, monitoring
+definitions etc.) during boot. The following functions are called at initialisation:
+1. @ref ParameterService::initializeParameterMap
+
+An example definition can be as follows:
+```cpp
+Parameter<uint8_t> parameter1(200);
+Parameter<uint8_t> parameter2(150);
+Parameter<uint8_t> parameter3(100);
+
+void ParameterService::initializeParameterMap() {
+	parameters = {{0, parameter1}, {1, parameter2}, {2, parameter3}};
+}
+```
+
+## Receiving messages
+
+After making sure that your code compiles, you need to provide a way of feeding received TC into the services. This can
+be done easily:
+```cpp
+MessageParser::parse(string, size);
+```
+
+You can use the rest of the @ref MessageParser functions if you have a more specific use-case.
diff --git a/docs/writing_code.md b/docs/writing_code.md
new file mode 100644
index 0000000000000000000000000000000000000000..57b878c6fd70f89de405b7d7ed4b9472746741e4
--- /dev/null
+++ b/docs/writing_code.md
@@ -0,0 +1,91 @@
+# Writing code
+@tableofcontents
+
+The ECSS-Services repository is based on C++17 and is compiled by gcc. The nature of the project means that there are a number of limitations and guidelines that need to be followed before code is accepted to the default branch.
+
+## Restrictions {#restrictions}
+
+This repository contains mission-critical software that is destined for use in space. As such, a number of restrictions are in place to ensure high performance, bounded memory and determinism.
+
+The following C++ features are **forbidden** from use in flight-ready software:
+1. **Dynamic memory allocation**.
+
+   This prohibits use of `malloc`/`free`, `new`/`delete` and almost most `std::` containers.
+2. **Run-Time Type Inference** (RTTI).
+
+   This prohibits use of `dynamic_cast`, `typeid` and `std::type_info`.
+3. **Exceptions**
+4. **Multiple inheritance**
+5. Some features only available in [hosted implementations](https://en.cppreference.com/w/cpp/freestanding).
+
+   If a C++ or compiler feature is not available in a bare-metal microcontroller, then it cannot be used in this repository.
+
+   This prohibits use of libraries such as `<ctime>`, `<pthreads>`, `<iostream>` and others.
+
+@see [DDJF_OBSW](https://gitlab.com/acubesat/documentation/cdr-public/-/blob/master/DDJF/DDJF_OBSW.pdf)
+
+There are no strict requirements about compiler portability: ecss-services is built around modern versions of `gcc` and
+`clang`. While no unexpected behaviour mush be invoked, and the code must remain portable across different
+architectures, we occasionally use compiler-specific features to aid performance or memory management.
+
+## Code standards {#seandards}
+
+The ECSS-Services repository uses a number of tools to perform static code analysis, which are part of the automated CI/CD pipeline.
+
+Due to the lack of available free static analysis tools for embedded standards, only the checks mentioned above are executed. However, performed static analysis checks and rules loosely follow the following guidelines:
+- [The Power of 10: Rules for Developing Safety-Critical Code](https://spinroot.com/gerard/pdf/P10.pdf)
+- [MISRA C++](https://www.misra.org.uk/misra-c-plus-plus/)
+- [Joint Strike Fighter C++ Coding Standards](https://www.stroustrup.com/JSF-AV-rules.pdf)
+- [C++ Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines)
+
+## Code style {#code-style}
+
+This repository typically follows the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) with the following notable modifications:
+1. The code is indented using **tabs**
+2. A soft column limit of `120` characters is followed
+3. Variable names are in `camelCase`
+4. Constant names are in `PascalCase`
+
+Code style can be enforced automatically:
+- By using the CLion _Reformat Code_ function
+- By using [`clang-format`](https://clang.llvm.org/docs/ClangFormat.html) and the accompanying `.clang-format` file.  \
+  You can use the `ci/clang-format.sh` script for automated code reformatting across the repository.
+
+Note that code style is not enforced authoritatively; sometimes deviations may exist between the two tools and the current code. Occasional deviations are accepted if needed for better readability and maintainability.
+
+## Documentation {#documentation}
+
+We use [Doxygen](https://www.doxygen.nl/index.html) to document our code and its interfaces. All classes, functions and most variables should be documented using doxygen.
+
+We use javadoc-style comments, for example:
+```cpp
+/**
+ * This class represents an object that exists.
+ *
+ * Represents a [physical object](https://en.wikipedia.org/wiki/Thing) and its location in our universe.
+ * Coordinates are given according to XYZ. This is similar to the @ref Item class, but implemented dynamically.
+ *
+ * @warning Functions in this class are not re-entrant and should be used carefully when combined with an RTOS.
+ */
+class Something
+```
+
+## Resources {#resources}
+
+Contributing to ecss-services requires using modern C++ that is not often seen in the wild. There are various resources
+available online, covering beginner to advanced topics, such as:
+1. Introductory
+   - [A Tour of C++](https://isocpp.org/tour)
+   - [learncpp.com](https://www.learncpp.com/)
+   - [Introduction to C++, MIT 6.096](https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-096-introduction-to-c-january-iap-2011/index.htm)
+   - [Wikiversity Introduction to C++](https://en.wikiversity.org/wiki/C%2B%2B/Introduction)
+3. Advanced
+   - [C++ Notes for Professionals](https://goalkicker.com/CPlusPlusBook/)
+4. Specific to modern C++
+   - [Modern C++ Tutorial: C++11/14/17/20 On the Fly](https://github.com/changkun/modern-cpp-tutorial)
+   - [C++ Super-FAQ](https://isocpp.org/faq)
+   - [C++ Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines)
+5. References
+   - [cppreference.com](https://en.cppreference.com/w/), contains advanced descriptions of pretty much every feature of C++
+6. Tools
+   - [Compiler Explorer](https://godbolt.org/)
diff --git a/inc/ECSS_Definitions.hpp b/inc/ECSS_Definitions.hpp
index 866213d2fd6442baa9b43341c0b3521570c86dbe..b17d891f8a8bf08c25931788df4631ee13529e8c 100644
--- a/inc/ECSS_Definitions.hpp
+++ b/inc/ECSS_Definitions.hpp
@@ -191,6 +191,12 @@ inline const uint8_t ECSSMaxServiceTypeDefinitions = 10;
  */
 inline const uint8_t ECSSMaxEventDefinitionIDs = 15;
 
+/**
+ * Maximum number of ST[12] Parameter Monitoring Definitions.
+ */
+inline const uint8_t ECSSMaxMonitoringDefinitions = 4;
+
+
 /** @} */
 
 #endif // ECSS_SERVICES_ECSS_DEFINITIONS_H
diff --git a/inc/ErrorHandler.hpp b/inc/ErrorHandler.hpp
index feab4ebeb5fada3bc0a5f74db6a75dc41b60dc71..98a98a78a934b5d77991737009b5375eede4dd70 100644
--- a/inc/ErrorHandler.hpp
+++ b/inc/ErrorHandler.hpp
@@ -1,8 +1,8 @@
 #ifndef PROJECT_ERRORHANDLER_HPP
 #define PROJECT_ERRORHANDLER_HPP
 
-#include <type_traits>
 #include <stdint.h> // for the uint_8t stepID
+#include <type_traits>
 
 // Forward declaration of the class, since its header file depends on the ErrorHandler
 class Message;
@@ -85,6 +85,10 @@ public:
 		 * Attempt to access an invalid parameter in ST[03]
 		 */
 		NonExistentParameter = 13,
+		/**
+		 * Invalid TimeStamp parameters at creation
+		 */
+		InvalidTimeStampInput = 14
 	};
 
 	/**
@@ -154,77 +158,122 @@ public:
 		/**
 		 * Attempt to add definition to the struct map but its already full. (ST[19])
 		 */
-		EventActionDefinitionsMapIsFull = 11,
+		EventActionDefinitionsMapIsFull = 9,
 		/**
 		 * Attempt to report/delete non existing housekeeping structure (ST[03])
 		 */
-		RequestedNonExistingStructure = 12,
+		RequestedNonExistingStructure = 10,
 		/**
 		 * Attempt to create already created structure (ST[03])
 		 */
-		RequestedAlreadyExistingStructure = 13,
+		RequestedAlreadyExistingStructure = 11,
 		/**
 		 * Attempt to delete structure which has the periodic reporting status enabled (ST[03]) as per 6.3.3.5.2(d-2)
 		 */
-		RequestedDeletionOfEnabledHousekeeping = 14,
+		RequestedDeletionOfEnabledHousekeeping = 12,
 		/**
 		 * Attempt to append a new parameter ID to a housekeeping structure, but the ID is already in the structure
 		 * (ST[03])
 		 */
-		AlreadyExistingParameter = 15,
+		AlreadyExistingParameter = 13,
 		/**
 		 * Attempt to append a new parameter id to a housekeeping structure, but the periodic generation status is
 		 * enabled (ST[03])
 		 */
-		RequestedAppendToEnabledHousekeeping = 16,
+		RequestedAppendToEnabledHousekeeping = 14,
 		/**
 		 * Attempt to create a new housekeeping structure in Housekeeping Service, when the maximum number of
 		 * housekeeping structures is already reached (ST[03])
 		 */
-		ExceededMaxNumberOfHousekeepingStructures = 17,
+		ExceededMaxNumberOfHousekeepingStructures = 15,
 		/**
 		 * Attempt to add a new simply commutated parameter in a specific housekeeping structure, but the maximum
 		 * number of simply commutated parameters for this structure is already reached (ST[03])
 		 */
-		ExceededMaxNumberOfSimplyCommutatedParameters = 18,
+		ExceededMaxNumberOfSimplyCommutatedParameters = 16,
 		/* Attempt to set a reporting rate which is smaller than the parameter sampling rate.
 		 * ST[04]
 		 */
-		InvalidReportingRateError = 19,
+		InvalidReportingRateError = 17,
 		/**
 		 * Attempt to set a sampling rate which is greater than the parameter reporting rate.
 		 * ST[04]
 		 */
-		InvalidSamplingRateError = 20,
+		InvalidSamplingRateError = 18,
 		/**
 		 * Attempt to add new statistic definition but the maximum number is already reached (ST[04])
 		 */
-		MaxStatisticDefinitionsReached = 21,
+		MaxStatisticDefinitionsReached = 19,
 		/**
 		 * Attempt to add a new report type, when the addition of all report types is already enabled in the
 		 * Application Process configuration (ST[14])
 		 */
-		AllReportTypesAlreadyAllowed = 22,
+		AllReportTypesAlreadyAllowed = 20,
 		/**
 		 * Attempt to add a new service type, when the addition of all service types is already enabled in the
 		 * Application Process configuration (ST[14])
 		 */
-		AllServiceTypesAlreadyAllowed = 23,
+		AllServiceTypesAlreadyAllowed = 21,
 		/**
 		 * Attempt to add a new report type, when the max number of reports types allowed per service type
 		 * definition in the Application Process configuration is already reached (ST[14])
 		 */
-		MaxReportTypesReached = 24,
+		MaxReportTypesReached = 22,
 		/**
 		 * Attempt to add a new service type, when the max number of service types allowed per application process
 		 * definition in the Application Process configuration is already reached (ST[14])
 		 */
-		MaxServiceTypesReached = 25,
+		MaxServiceTypesReached = 23,
 		/**
 		 * Attempt to add a report/event definition/housekeeping report type, when the specified application process
 		 * ID is not controlled by the Service (ST[14])
 		 */
-		NotControlledApplication = 26,
+		NotControlledApplication = 24,
+		/**
+		 * Attempt to delete all parameter monitoring definitions but the Parameter Monitoring Function Status is
+		 * enabled.
+		 */
+		InvalidRequestToDeleteAllParameterMonitoringDefinitions = 25,
+		/**
+		 * Attempt to delete one parameter monitoring definition but its Parameter Monitoring Status is
+		 * enabled.
+		 */
+		InvalidRequestToDeleteParameterMonitoringDefinition = 26,
+		/**
+		 * Attempt to add a parameter that already exists to the Parameter Monitoring List.
+		 */
+		AddAlreadyExistingParameter = 27,
+		/**
+		 * Attempt to add a parameter in the Parameter Monitoring List but it's full
+		 */
+		ParameterMonitoringListIsFull = 28,
+		/**
+		 * Attempt to add or modify a limit check parameter monitoring definition, but the high limit is lower than
+		 * the low limit.
+		 */
+		HighLimitIsLowerThanLowLimit = 29,
+		/**
+		 * Attempt to add or modify a delta check parameter monitoring definition, but the high threshold is lower than
+		 * the low threshold.
+		 */
+		HighThresholdIsLowerThanLowThreshold = 30,
+		/**
+		 * Attempt to modify a non existent Parameter Monitoring definition.
+		 */
+		ModifyParameterNotInTheParameterMonitoringList = 31,
+		/**
+		 * Attempt to modify a parameter monitoring definition, but the instruction refers to a monitored parameter
+		 * that is not the one used in that parameter monitoring definition.
+		 */
+		DifferentParameterMonitoringDefinitionAndMonitoredParameter = 32,
+		/**
+		 * Attempt to get a parameter monitoring definition that does not exist.
+		 */
+		GetNonExistingParameterMonitoringDefinition = 33,
+		/**
+		 * Request to report a non existent parameter monitoring definition.
+		 */
+		ReportParameterNotInTheParameterMonitoringList = 34
 	};
 
 	/**
diff --git a/inc/Helpers/HousekeepingStructure.hpp b/inc/Helpers/HousekeepingStructure.hpp
index 1e75430829b003bf49b83002a59dfddc068879f6..98271aa37526f9b4254d357b454e9b871ed96a64 100644
--- a/inc/Helpers/HousekeepingStructure.hpp
+++ b/inc/Helpers/HousekeepingStructure.hpp
@@ -14,20 +14,22 @@
  */
 class HousekeepingStructure {
 public:
-	uint8_t structureId;
-	/**
-	 * Defined as integer multiples of the minimum sampling interval as per 6.3.3.2.c.5 #NOTE-2.
-	 */
-	uint32_t collectionInterval = 0;
-	/**
-	 * Indicates whether the periodic generation of housekeeping reports is enabled.
-	 */
-	bool periodicGenerationActionStatus = false;
+    uint8_t structureId;
 
-	/**
-	 * Vector containing the IDs of the simply commutated parameters, contained in the housekeeping structure.
-	 */
-	etl::vector<uint16_t, ECSSMaxSimplyCommutatedParameters> simplyCommutatedParameterIds;
+    /**
+     * Defined as integer multiples of the minimum sampling interval as per 6.3.3.2.c.5 #NOTE-2.
+     */
+    uint32_t collectionInterval = 0;
+
+    /**
+     * Indicates whether the periodic generation of housekeeping reports is enabled.
+     */
+    bool periodicGenerationActionStatus = false;
+
+    /**
+     * Vector containing the IDs of the simply commutated parameters, contained in the housekeeping structure.
+     */
+    etl::vector<uint16_t, ECSSMaxSimplyCommutatedParameters> simplyCommutatedParameterIds;
 
 	HousekeepingStructure() = default;
 };
diff --git a/inc/Helpers/PMONBase.hpp b/inc/Helpers/PMONBase.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..66c00e58ff877d4b3c4f11b718ef6c83aeb7860e
--- /dev/null
+++ b/inc/Helpers/PMONBase.hpp
@@ -0,0 +1,102 @@
+#ifndef ECSS_SERVICES_PMONBASE_HPP
+#define ECSS_SERVICES_PMONBASE_HPP
+#include <cstdint>
+#include "Message.hpp"
+#include "etl/array.h"
+#include "Service.hpp"
+#include "Helpers/Parameter.hpp"
+#include "etl/map.h"
+#include "ECSS_Definitions.hpp"
+#include "etl/list.h"
+
+/**
+ * Base class for Parameter Monitoring definitions. Contains the common variables of all check types.
+ */
+class PMONBase {
+public:
+	enum CheckingStatus : uint8_t {
+		Unchecked = 1,
+		Invalid = 2,
+		ExpectedValue = 3,
+		UnexpectedValue = 4,
+		WithinLimits = 5,
+		BelowLowLimit = 6,
+		AboveHighLimit = 7,
+		WithinThreshold = 8,
+		BelowLowThreshold = 9,
+		AboveHighThreshold = 10
+	};
+
+	uint16_t monitoredParameterId;
+
+	std::reference_wrapper<ParameterBase> monitoredParameter;
+	/**
+	 * The number of checks that need to be conducted in order to set a new Parameter Monitoring Status.
+	 */
+	uint16_t repetitionNumber;
+	/**
+	 * The number of checks that have been conducted so far.
+	 */
+	uint16_t repetitionCounter = 0;
+	bool monitoringEnabled = false;
+	CheckingStatus checkingStatus = Unchecked;
+	etl::array<CheckingStatus, 2> checkTransitionList = {};
+
+protected:
+	/**
+	 * @param monitoredParameterId is assumed to be correct and not checked.
+	 */
+	PMONBase(uint16_t monitoredParameterId, uint16_t repetitionNumber);
+};
+
+/**
+ * Contains the variables specific to Parameter Monitoring definitions of expected value check type.
+ */
+class PMONExpectedValueCheck : public PMONBase {
+public:
+	double expectedValue;
+	uint64_t mask;
+	uint16_t unexpectedValueEvent;
+
+	explicit PMONExpectedValueCheck(uint16_t monitoredParameterId, uint16_t repetitionNumber, double expectedValue,
+	                                uint64_t mask, uint16_t unexpectedValueEvent)
+	    : expectedValue(expectedValue), mask(mask), unexpectedValueEvent(unexpectedValueEvent),
+	      PMONBase(monitoredParameterId, repetitionNumber){};
+};
+
+/**
+ * Contains the variables specific to Parameter Monitoring definitions of limit check type.
+ */
+class PMONLimitCheck : public PMONBase {
+public:
+	double lowLimit;
+	uint16_t belowLowLimitEvent;
+	double highLimit;
+	uint16_t aboveHighLimitEvent;
+
+	explicit PMONLimitCheck(uint16_t monitoredParameterId, uint16_t repetitionNumber, double lowLimit,
+	                        uint16_t belowLowLimitEvent, double highLimit, uint16_t aboveHighLimitEvent)
+	    : lowLimit(lowLimit), belowLowLimitEvent(belowLowLimitEvent), highLimit(highLimit),
+	      aboveHighLimitEvent(aboveHighLimitEvent), PMONBase(monitoredParameterId, repetitionNumber){};
+};
+
+/**
+ * Contains the variables specific to Parameter Monitoring definitions of delta check type.
+ */
+class PMONDeltaCheck : public PMONBase {
+public:
+	uint16_t numberOfConsecutiveDeltaChecks;
+	double lowDeltaThreshold;
+	uint16_t belowLowThresholdEvent;
+	double highDeltaThreshold;
+	uint16_t aboveHighThresholdEvent;
+
+	explicit PMONDeltaCheck(uint16_t monitoredParameterId, uint16_t repetitionNumber,
+	                        uint16_t numberOfConsecutiveDeltaChecks, double lowDeltaThreshold,
+	                        uint16_t belowLowThresholdEvent, double highDeltaThreshold,
+	                        uint16_t aboveHighThresholdEvent)
+	    : numberOfConsecutiveDeltaChecks(numberOfConsecutiveDeltaChecks), lowDeltaThreshold(lowDeltaThreshold),
+	      belowLowThresholdEvent(belowLowThresholdEvent), highDeltaThreshold(highDeltaThreshold),
+	      aboveHighThresholdEvent(aboveHighThresholdEvent), PMONBase(monitoredParameterId, repetitionNumber){};
+};
+#endif // ECSS_SERVICES_PMONBASE_HPP
diff --git a/inc/Helpers/TimeAndDate.hpp b/inc/Helpers/TimeAndDate.hpp
deleted file mode 100644
index 03fb0833d3d542b81d48b0f9d31e707220a88fa7..0000000000000000000000000000000000000000
--- a/inc/Helpers/TimeAndDate.hpp
+++ /dev/null
@@ -1,78 +0,0 @@
-#ifndef ECSS_SERVICES_TIMEANDDATE_HPP
-#define ECSS_SERVICES_TIMEANDDATE_HPP
-
-#include <cstdint>
-#include "macros.hpp"
-
-/**
- * A class that represents the time and date.
- *
- * @note
- * This class represents UTC (Coordinated Universal Time) date
- */
-class TimeAndDate {
-public:
-	uint16_t year;
-	uint8_t month;
-	uint8_t day;
-	uint8_t hour;
-	uint8_t minute;
-	uint8_t second;
-
-	/**
-	 * Assign the instances with the Unix epoch 1/1/1970 00:00:00
-	 */
-	TimeAndDate();
-
-	/**
-	 * @param year the year as it used in Gregorian calendar
-	 * @param month the month as it used in Gregorian calendar
-	 * @param day the day as it used in Gregorian calendar
-	 * @param hour UTC hour in 24 format
-	 * @param minute UTC minutes
-	 * @param second UTC seconds
-	 */
-	TimeAndDate(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second);
-
-	/**
-	 * Compare two timestamps.
-	 *
-	 * @param Date the date that will be compared with the pointer `this`
-	 * @return true if the pointer `this` is smaller than \p Date
-	 */
-	bool operator<(const TimeAndDate& Date);
-
-	/**
-	 * Compare two timestamps.
-	 *
-	 * @param Date the date that will be compared with the pointer `this`
-	 * @return true if the pointer `this` is greater than \p Date
-	 */
-	bool operator>(const TimeAndDate& Date);
-
-	/**
-	 * Compare two timestamps.
-	 *
-	 * @param Date the date that will be compared with the pointer `this`
-	 * @return true if the pointer `this` is equal to \p Date
-	 */
-	bool operator==(const TimeAndDate& Date);
-
-	/**
-	 * Compare two timestamps.
-	 *
-	 * @param Date the date that will be compared with the pointer `this`
-	 * @return true if the pointer `this` is smaller than or equal to \p Date
-	 */
-	bool operator<=(const TimeAndDate& Date);
-
-	/**
-	 * Compare two timestamps.
-	 *
-	 * @param Date the date that will be compared with the pointer `this`
-	 * @return true if the pointer `this` is greater than or equal to \p Date
-	 */
-	bool operator>=(const TimeAndDate& Date);
-};
-
-#endif // ECSS_SERVICES_TIMEANDDATE_HPP
diff --git a/inc/Helpers/TimeHelper.hpp b/inc/Helpers/TimeHelper.hpp
deleted file mode 100644
index 42b5baf1204b01c58b3e964c5d413a6a973ea26f..0000000000000000000000000000000000000000
--- a/inc/Helpers/TimeHelper.hpp
+++ /dev/null
@@ -1,139 +0,0 @@
-#ifndef ECSS_SERVICES_TIMEHELPER_HPP
-#define ECSS_SERVICES_TIMEHELPER_HPP
-
-#include <cstdint>
-#include <Message.hpp>
-#include "TimeAndDate.hpp"
-
-inline const uint8_t SecondsPerMinute = 60U;
-inline const uint16_t SecondsPerHour = 3600U;
-inline const uint32_t SecondsPerDay = 86400U;
-
-/**
- * @todo If we use CUC time format then we should keep leap seconds up to date. Leap seconds are added in undefined
- * periods of time, so we should find a way to adjust to these changes either in runtime using GPS or sending a new
- * compiled code (changing the defined leap seconds) from the ground segment
- */
-inline const uint8_t LeapSeconds = 27;
-
-
-/**
- * This class formats the spacecraft time and cooperates closely with the ST[09] time management.
- *
- * The ECSS standard supports two time formats: the CUC and CSD that are described in CCSDS
- * 301.0-B-4 standard.
- *
- * The CDS is UTC-based (UTC: Coordinated Universal Time). It consists of two main fields: the
- * time code preamble field (P-field) and the time specification field (T-field). The P-Field is the metadata for the
- * T-Field. The T-Field is consisted of two segments: 1) the `DAY` and the 2) `ms of day` segments. The P-field won't
- * be included in the code, because as the ECSS standards claims, it can be just implicitly declared.
- *
- * The CUC is TAI-based (TAI: International Atomic Time). It consists of two main fields: the time code preamble field
- * (P-field) and the time specification field(T-field). The T-Field contains the value of the time unit and the designer
- * decides what the time unit will be, so this is a subject for discussion. The recommended time unit from the
- * standard is the second and it is probably the best solution for accuracy.
- *
- * @note
- * The defined epoch for both time formats is 1 January 1958 00:00:00
- *
- * Since CDS format is UTC-based, the leap second correction must be made. The leap seconds that
- * have been occurred between timestamps should be considered if a critical time-difference is
- * needed
- *
- */
-class TimeHelper {
-public:
-	static constexpr uint8_t DaysOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
-
-	TimeHelper() = default;
-
-	/**
-	 * @param year The year that will be examined if it is a leap year (366 days)
-	 * @return if the \p year is a leap year returns true and if it isn't returns false
-	 */
-	static bool IsLeapYear(uint16_t year);
-
-	/**
-	 * Convert UTC date to elapsed seconds since Unix epoch (1/1/1970 00:00:00).
-	 *
-	 * This is a reimplemented `mktime()` of the `<ctime>` library in an embedded compatible way
-	 *
-	 * @note
-	 * This function can convert UTC dates after 1 January 2019 to elapsed seconds since Unix epoch
-	 *
-	 * @param TimeInfo the time information/data from the RTC (UTC format)
-	 * @return the elapsed seconds between a given UTC date (after the Unix epoch) and Unix epoch
-	 * @todo check if we need to change the epoch to the recommended one from the standard, 1
-	 * January 1958
-	 */
-	static uint32_t utcToSeconds(const TimeAndDate& TimeInfo);
-
-	/**
-	 * Convert elapsed seconds since Unix epoch to UTC date.
-	 *
-	 * This is a reimplemented `gmtime()` of the `<ctime>` library in an embedded compatible way
-	 *
-	 * @note
-	 * This function can convert elapsed seconds since Unix epoch to UTC dates after 1 January 2019
-	 *
-	 * @param seconds elapsed seconds since Unix epoch
-	 * @return the UTC date based on the \p seconds
-	 * @todo check if we need to change the epoch to the recommended one from the standard, 1
-	 * January 1958
-	 */
-	static TimeAndDate secondsToUTC(uint32_t seconds);
-
-	/**
-	 * Generate the CDS time format (3.3 in CCSDS 301.0-B-4 standard).
-	 *
-	 * Converts a UTC date to CDS time format.
-	 *
-	 * @param TimeInfo is the data provided from RTC (UTC)
-	 * @return TimeFormat the CDS time format. More specific, 48 bits are used for the T-field
-	 * (16 for the `DAY` and 32 for the `ms of day`)
-	 * @todo time security for critical time operations
-	 * @todo declare the implicit P-field
-	 */
-	static uint64_t generateCDSTimeFormat(const struct TimeAndDate& TimeInfo);
-
-	/**
-	 * Parse the CDS time format (3.3 in CCSDS 301.0-B-4 standard)
-	 *
-	 * @param data time information provided from the ground segment. The length of the data is a
-	 * fixed size of 48 bits
-	 * @return the UTC date
-	 */
-	static TimeAndDate parseCDStimeFormat(const uint8_t* data);
-
-	/**
-	 * Generate the CUC time format (3.3 in CCSDS 301.0-B-4 standard).
-	 *
-	 * Converts a UTC date to CUC time format.
-	 *
-	 * @note
-	 * The T-field is specified for the seconds passed from the defined epoch 1 January 1958. We use 4 octets(32
-	 * bits) for the time unit (seconds) because 32 bits for the seconds are enough to count 136 years! But if we use 24
-	 * bits for the seconds then it will count 0,5 years and this isn't enough. Remember we can use only integers
-	 * numbers of octets for the time unit (second)
-	 *
-	 * The CUC time format doesn't include leap seconds, so we need to add them because we assume that
-	 * the RTC will provide UTC format.
-	 *
-	 * @param TimeInfo is the data provided from RTC (UTC)
-	 * @return TimeFormat the CUC time format. More specific, 32 bits are used for the T-field (seconds since 1/1/1958)
-	 * @todo time security for critical time operations
-	 * @todo declare the implicit P-field
-	 */
-	static uint32_t generateCUCTimeFormat(const struct TimeAndDate& TimeInfo);
-
-	/**
-	 * Parse the CUC time format (3.3 in CCSDS 301.0-B-4 standard)
-	 *
-	 * @param data time information provided from the ground segment. The length of the data is a
-	 * fixed size of 32 bits
-	 * @return the UTC date
-	 */
-	static TimeAndDate parseCUCTimeFormat(const uint8_t* data);
-};
-
-#endif // ECSS_SERVICES_TIMEHELPER_HPP
diff --git a/inc/Platform/x86/ECSS_Configuration.hpp b/inc/Platform/x86/ECSS_Configuration.hpp
index a8aba07c492158ed30807f7b6cb9905805535c1a..bebc56cc91cc77b2fba9cf3fb0d258cf2ebac9a8 100644
--- a/inc/Platform/x86/ECSS_Configuration.hpp
+++ b/inc/Platform/x86/ECSS_Configuration.hpp
@@ -27,6 +27,7 @@
 #define SERVICE_HOUSEKEEPING              	///<  Compile ST[03] housekeeping
 #define SERVICE_LARGEPACKET         		///<  Compile ST[13] large packet transfer
 #define SERVICE_MEMORY              		///<  Compile ST[06] memory management
+#define SERVICE_ONBOARDMONITORING 			///<  Compile ST[12] on-board monitoring
 #define SERVICE_PARAMETER           		///<  Compile ST[20] parameter management
 #define SERVICE_PARAMETERSTATISTICS 		///<  Compile ST[04] parameter statistics
 #define SERVICE_REALTIMEFORWARDINGCONTROL   ///< Compile ST[14] real time forwarding control
@@ -34,7 +35,8 @@
 #define SERVICE_TEST                		///<  Compile ST[17] test
 #define SERVICE_TIME                		///<  Compile ST[09] time management
 #define SERVICE_TIMESCHEDULING              ///<  Compile ST[11] time-based scheduling
+
 #endif
 /** @} */
 
-#endif //ECSS_SERVICES_ECSS_CONFIGURATION_HPP
+#endif // ECSS_SERVICES_ECSS_CONFIGURATION_HPP
diff --git a/inc/Platform/x86/Helpers/UTCTimestamp.hpp b/inc/Platform/x86/Helpers/UTCTimestamp.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d6b5eed4059e48f8c713158be277191bdb4c923e
--- /dev/null
+++ b/inc/Platform/x86/Helpers/UTCTimestamp.hpp
@@ -0,0 +1,5 @@
+#include <Time/UTCTimestamp.hpp>
+#include <ostream>
+#include <iomanip>
+
+std::ostream& operator<<(std::ostream& o, UTCTimestamp const& date);
diff --git a/inc/ServicePool.hpp b/inc/ServicePool.hpp
index 20fc4ab559a68fbdb02a6381b5e280186247f52e..fcdc7b19376149b6012b48d7c8a8dbd2a305e434 100644
--- a/inc/ServicePool.hpp
+++ b/inc/ServicePool.hpp
@@ -14,6 +14,7 @@
 #include "Services/HousekeepingService.hpp"
 #include "Services/ParameterStatisticsService.hpp"
 #include "Services/RealTimeForwardingControlService.hpp"
+#include "Services/OnBoardMonitoringService.hpp"
 
 /**
  * Defines a class that contains instances of all Services.
@@ -37,10 +38,6 @@ class ServicePool {
 	uint16_t packetSequenceCounter = 0;
 
 public:
-#ifdef SERVICE_PARAMETERSTATISTICS
-	ParameterStatisticsService parameterStatistics;
-#endif
-
 #ifdef SERVICE_EVENTACTION
 	EventActionService eventAction;
 #endif
@@ -65,6 +62,10 @@ public:
 	MemoryManagementService memoryManagement;
 #endif
 
+#ifdef SERVICE_ONBOARDMONITORING
+	OnBoardMonitoringService onBoardMonitoringService;
+#endif
+
 #ifdef SERVICE_PARAMETER
 	ParameterService parameterManagement;
 #endif
@@ -73,6 +74,10 @@ public:
 	RealTimeForwardingControlService realTimeForwarding;
 #endif
 
+#ifdef SERVICE_PARAMETERSTATISTICS
+	ParameterStatisticsService parameterStatistics;
+#endif
+
 #ifdef SERVICE_REQUESTVERIFICATION
 	RequestVerificationService requestVerification;
 #endif
diff --git a/inc/Services/EventActionService.hpp b/inc/Services/EventActionService.hpp
index f0c33e1702a664a851ae91053d6e7cb579b575fd..6ed438d8f07834d475c6ada312d2c3ec8d1555d8 100644
--- a/inc/Services/EventActionService.hpp
+++ b/inc/Services/EventActionService.hpp
@@ -12,18 +12,18 @@
  * ECSS 8.19 && 6.19
  *
  * @ingroup Services
- * @note: Make sure to check the note in the addEventActionDefinition()
- * @note: A third variable was added, the eventActionDefinitionID. This was added for the purpose of identifying
+ * @note Make sure to check the note in the addEventActionDefinition()
+ * @note A third variable was added, the eventActionDefinitionID. This was added for the purpose of identifying
  * various eventActionDefinitions that correspond to the same eventDefinitionID. The goal is to have multiple actions
  * be executed when one event takes place. This defies the standard.
- * @note: The application ID was decided to be abolished as an identifier of the event-action
+ * @note The application ID was decided to be abolished as an identifier of the event-action
  * definition
- * @attention: Every event action definition ID should be different, regardless of the application ID
+ * @attention Every event action definition ID should be different, regardless of the application ID
  *
- * @todo: Since there are multiple actions per event and in delete/enable/disable functions are
+ * @todo Since there are multiple actions per event and in delete/enable/disable functions are
  * multiple instances are accessed, should I find a more efficient way to access them?
- * @todo: check if eventActionFunctionStatus should be private or not
- * @todo: check if eventAction map of definitions should be private or not
+ * @todo check if eventActionFunctionStatus should be private or not
+ * @todo check if eventAction map of definitions should be private or not
  */
 class EventActionService : public Service {
 private:
diff --git a/inc/Services/EventReportService.hpp b/inc/Services/EventReportService.hpp
index dc24b92c15fe50ae6688cba87dc88259bc022e1e..839dff8bc3ca013acee789fbb9ad257b730fa18e 100644
--- a/inc/Services/EventReportService.hpp
+++ b/inc/Services/EventReportService.hpp
@@ -8,8 +8,8 @@
  * Implementation of ST[05] event reporting service
  *
  * @ingroup Services
- * @todo: add more enums event IDs
- * @todo: Make sure there isn't an event ID == 0, because there's a confliction with another service
+ * @todo add more enums event IDs
+ * @todo Make sure there isn't an event ID == 0, because there's a confliction with another service
  * Note: enum IDs are these just for test purposes
  *
  */
diff --git a/inc/Services/FunctionManagementService.hpp b/inc/Services/FunctionManagementService.hpp
index 6f2bc55b34a202f86124690ce2a55059701aaf29..4dd0922560dc4d550045ade585dc3b5d4e2dd942 100644
--- a/inc/Services/FunctionManagementService.hpp
+++ b/inc/Services/FunctionManagementService.hpp
@@ -7,6 +7,9 @@
 #include "Service.hpp"
 #include "ErrorHandler.hpp"
 
+typedef String<ECSSFunctionNameLength> functionName;
+typedef etl::map<functionName, void (*)(String<ECSSFunctionMaxArgLength>), ECSSFunctionMapSize> FunctionMap;
+
 /**
  * Implementation of the ST[08] function management service
  *
@@ -24,34 +27,6 @@
  * @ingroup Services
  * @author Grigoris Pavlakis <grigpavl@ece.auth.gr>
  */
-
-/**
- * Usage of the include() function:
- *
- * @code
- * void foo(String<MAX_ARG_LENGTH> b) {
- * 		std::cout << "SPAAAACE!" << std::endl;
- * 	}
- *
- * void bar(String<MAX_ARG_LENGTH> b) {
- * 		std::cout << "I HAZ A CUBESAT THAT SNAPS PIX!" << std::endl;
- * 	}
- *
- * void baz(String<MAX_ARG_LENGTH> b) {
- * 		std::cout << "QWERTYUIOP" << std::endl;
- * 	}
- *
- * 	FunctionManagementService::FunctionManagementService() {
- * 		include(String<FUNC_NAME_LENGTH>("foo"), &foo);
- * 		include(String<FUNC_NAME_LENGTH>("bar"), &bar);
- * 		include(String<FUNC_NAME_LENGTH>("baz"), &baz);
- * 	}
- * @endcode
- */
-
-typedef String<ECSSFunctionNameLength> functionName;
-typedef etl::map<functionName, void (*)(String<ECSSFunctionMaxArgLength>), ECSSFunctionMapSize> FunctionMap;
-
 class FunctionManagementService : public Service {
 	/**
 	 * Map of the function names to their respective pointers. Size controlled by FUNC_MAP_SIZE
@@ -84,6 +59,28 @@ public:
 	 * Includes a new function in the pointer map. This enables it to be called by way of a valid
 	 * TC[8,1] message.
 	 *
+	 * Usage of the include() function:
+	 *
+	 * @code
+	 * void foo(String<MAX_ARG_LENGTH> b) {
+	 * 		std::cout << "SPAAAACE!" << std::endl;
+	 * 	}
+	 *
+	 * void bar(String<MAX_ARG_LENGTH> b) {
+	 * 		std::cout << "I HAZ A CUBESAT THAT SNAPS PIX!" << std::endl;
+	 * 	}
+	 *
+	 * void baz(String<MAX_ARG_LENGTH> b) {
+	 * 		std::cout << "QWERTYUIOP" << std::endl;
+	 * 	}
+	 *
+	 * 	FunctionManagementService::FunctionManagementService() {
+	 * 		include(String<FUNC_NAME_LENGTH>("foo"), &foo);
+	 * 		include(String<FUNC_NAME_LENGTH>("bar"), &bar);
+	 * 		include(String<FUNC_NAME_LENGTH>("baz"), &baz);
+	 * 	}
+	 * @endcode
+	 *
 	 * @param funcName the function's name. Max. length is FUNC_NAME_LENGTH bytes.
 	 * @param ptr pointer to a function of void return type and a MAX_ARG_LENGTH-lengthed byte
 	 * string as argument (which contains the actual arguments of the function)
diff --git a/inc/Services/HousekeepingService.hpp b/inc/Services/HousekeepingService.hpp
index 4ee886911f9a2b72a917f92c9d5e0cb82eb7c2fa..2336f094e8e15a0afee5f28927502541494fee86 100644
--- a/inc/Services/HousekeepingService.hpp
+++ b/inc/Services/HousekeepingService.hpp
@@ -11,6 +11,7 @@
  * Implementation of the ST[03] Housekeeping Reporting Service. The job of the Housekeeping Service is to store
  * parameters in the housekeeping structures so that it can generate housekeeping reports periodically.
  *
+ * @ingroup Services
  * @author Petridis Konstantinos <petridkon@gmail.com>
  */
 class HousekeepingService : Service {
@@ -30,6 +31,12 @@ private:
 	static bool existsInVector(const etl::vector<uint16_t, ECSSMaxSimplyCommutatedParameters>& ids,
 	                           uint16_t parameterId);
 
+    /**
+     * Initializes Housekeeping Structures with the Parameters found in the obc-software.
+     * The function definition is also found in the obc-software repo.
+     */
+    void initializeHousekeepingStructures();
+
 public:
 	inline static const uint8_t ServiceType = 3;
 
@@ -53,7 +60,9 @@ public:
 		HousekeepingPeriodicPropertiesReport = 35,
 	};
 
-	HousekeepingService() = default;
+    HousekeepingService() {
+        initializeHousekeepingStructures();
+    };
 
 	/**
 	 * Implementation of TC[3,1]. Request to create a housekeeping parameters report structure.
@@ -120,6 +129,19 @@ public:
 	 */
 	void reportHousekeepingPeriodicProperties(Message& request);
 
+	/**
+	 * This function calculates the time needed to pass until the next periodic report for each housekeeping 
+	 * structure. The function also calls the housekeeping reporting functions as needed.
+	 * 
+	 * @note Three arguments are needed for resiliency in case the function doesn't execute at the exact time that is expected
+	 * 
+	 * @param currentTime The current system time, in milliseconds.
+	 * @param previousTime The system time of the previous call of the function.
+	 * @param expectedDelay The output of this function after its last execution.
+	 * @return uint32_t The minimum amount of time until the next periodic housekeeping report, in milliseconds.
+	 */
+	uint32_t reportPendingStructures(uint32_t currentTime, uint32_t previousTime, uint32_t expectedDelay);
+
 	/**
 	 * It is responsible to call the suitable function that executes a TC packet. The source of that packet
 	 * is the ground station.
diff --git a/inc/Services/OnBoardMonitoringService.hpp b/inc/Services/OnBoardMonitoringService.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1ef954f2f6f4356436047b1909bc75dcd95d45b4
--- /dev/null
+++ b/inc/Services/OnBoardMonitoringService.hpp
@@ -0,0 +1,96 @@
+#ifndef ECSS_SERVICES_ONBOARDMONITORINGSERVICE_HPP
+#define ECSS_SERVICES_ONBOARDMONITORINGSERVICE_HPP
+#include <cstdint>
+#include "Message.hpp"
+#include "etl/array.h"
+#include "Service.hpp"
+#include "Helpers/Parameter.hpp"
+#include "etl/map.h"
+#include "ECSS_Definitions.hpp"
+#include "etl/list.h"
+#include "Helpers/PMONBase.hpp"
+
+/**
+ * Implementation of the ST[12] parameter statistics reporting service, as defined in ECSS-E-ST-70-41C.
+ * @ingroup Services
+ * @author Konstantinos Michopoulos <konstantinos.michopoulos@gmail.com>
+ */
+class OnBoardMonitoringService : public Service {
+private:
+	/**
+	 * Map storing the parameter monitoring definitions.
+	 */
+	etl::map<uint16_t, std::reference_wrapper<PMONBase>, ECSSMaxMonitoringDefinitions> parameterMonitoringList;
+
+public:
+	inline static const uint8_t ServiceType = 12;
+	enum MessageType : uint8_t {
+		EnableParameterMonitoringDefinitions = 1,
+		DisableParameterMonitoringDefinitions = 2,
+		ChangeMaximumTransitionReportingDelay = 3,
+		DeleteAllParameterMonitoringDefinitions = 4,
+		AddParameterMonitoringDefinitions = 5,
+		DeleteParameterMonitoringDefinitions = 6,
+		ModifyParameterMonitoringDefinitions = 7,
+		ReportParameterMonitoringDefinitions = 8,
+		ParameterMonitoringDefinitionReport = 9,
+		ReportOutOfLimits = 10,
+		OutOfLimitsReport = 11,
+		CheckTransitionReport = 12,
+		ReportStatusOfParameterMonitoringDefinition = 13,
+		ParameterMonitoringDefinitionStatusReport = 14
+	};
+	/**
+	 * The maximum time between two transition reports.
+	 * Measured in "on-board parameter minimum sampling interval" units (see 5.4.3.2c in ECSS-E-ST-70-41C).
+	 */
+	uint16_t maximumTransitionReportingDelay = 0;
+	/**
+	 * If true, parameter monitoring is enabled
+	 */
+	bool parameterMonitoringFunctionStatus = false;
+	/*
+	 * Adds a new Parameter Monitoring definition to the parameter monitoring list.
+	 */
+	void addPMONDefinition(uint16_t PMONId, std::reference_wrapper<PMONBase> PMONDefinition) {
+		parameterMonitoringList.insert({PMONId, PMONDefinition});
+	}
+	/**
+	 * @param PMONId
+	 * @return Parameter Monitoring definition
+	 */
+	std::reference_wrapper<PMONBase> getPMONDefinition(uint16_t PMONId) {
+		return parameterMonitoringList.at(PMONId);
+	}
+	/**
+	 * @return true if PMONList is empty.
+	 */
+	bool isPMONListEmpty() {
+		return parameterMonitoringList.empty();
+	}
+	/**
+	 * Enables the PMON definitions which correspond to the ids in TC[12,1].
+	 */
+	void enableParameterMonitoringDefinitions(Message& message);
+
+	/**
+	 * Disables the PMON definitions which correspond to the ids in TC[12,2].
+	 */
+	void disableParameterMonitoringDefinitions(Message& message);
+
+	/**
+	 * TC[12,3]
+	 * Changes the maximum time between two transition reports.
+	 */
+	void changeMaximumTransitionReportingDelay(Message& message);
+
+	/**
+	 * TC[12,4]
+	 * Deletes all the PMON definitions in the PMON list.
+	 */
+	void deleteAllParameterMonitoringDefinitions(Message& message);
+
+	void execute(Message& message);
+};
+
+#endif // ECSS_SERVICES_ONBOARDMONITORINGSERVICE_HPP
diff --git a/inc/Services/ParameterService.hpp b/inc/Services/ParameterService.hpp
index c932f3c6239a25344317aac0e257a028cb3ad252..437bd395ad06cffe0a97ef1d2959d4f2c531ee2f 100644
--- a/inc/Services/ParameterService.hpp
+++ b/inc/Services/ParameterService.hpp
@@ -12,16 +12,15 @@
  * Implementation of the ST[20] parameter management service,
  * as defined in ECSS-E-ST-70-41C
  *
- * @author Grigoris Pavlakis <grigpavl@ece.auth.gr>
- * @author Athanasios Theocharis <athatheoc@gmail.com>
- */
-
-/**
  * Parameter manager - ST[20]
  *
  * The purpose of this class is to handle functions regarding the access and modification
  * of the various parameters of the CubeSat.
  * The parameters to be managed are initialized and kept in \ref SystemParameters.
+ *
+ * @ingroup Services
+ * @author Grigoris Pavlakis <grigpavl@ece.auth.gr>
+ * @author Athanasios Theocharis <athatheoc@gmail.com>
  */
 class ParameterService : public Service {
 private:
diff --git a/inc/Services/ParameterStatisticsService.hpp b/inc/Services/ParameterStatisticsService.hpp
index d4bcd8a65e8f1e119de34859c30a76bfd8c56aa8..b6f98580fca55357a6968d90fb7c4e9342e05db4 100644
--- a/inc/Services/ParameterStatisticsService.hpp
+++ b/inc/Services/ParameterStatisticsService.hpp
@@ -10,6 +10,8 @@
 
 /**
  * Implementation of the ST[04] parameter statistics reporting service, as defined in ECSS-E-ST-70-41C.
+ *
+ * @ingroup Services
  * @author Konstantinos Petridis <petridkon@gmail.com>
  */
 class ParameterStatisticsService : public Service {
diff --git a/inc/Services/TimeBasedSchedulingService.hpp b/inc/Services/TimeBasedSchedulingService.hpp
index 751b6438dac6109d05245340cdb23ac8a19d1c0e..f49d8067a02a738a521c71ffcbee1d7b4718fc19 100644
--- a/inc/Services/TimeBasedSchedulingService.hpp
+++ b/inc/Services/TimeBasedSchedulingService.hpp
@@ -6,7 +6,6 @@
 #include "ErrorHandler.hpp"
 #include "MessageParser.hpp"
 #include "Helpers/CRCHelper.hpp"
-#include "Helpers/TimeHelper.hpp"
 
 // Include platform specific files
 #include "Platform/x86/TimeGetter.hpp"
diff --git a/inc/Time/Time.hpp b/inc/Time/Time.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..55dbaa16ec64dfac8a811d7b05ea719296c1e7b4
--- /dev/null
+++ b/inc/Time/Time.hpp
@@ -0,0 +1,268 @@
+#pragma once
+#include <cstdint>
+#include <Message.hpp>
+#include "macros.hpp"
+#include "etl/String.hpp"
+
+/**
+ * @defgroup Time Time
+ *
+ * Group of classes, functions and variables to represent and operate on time
+ *
+ * The implementation of the ECSS services requires storing and handling timestamps quite often. However, in an embedded
+ * system with mission-critical tasks, it is wide to avoid the complexity of a full time management system, as the one
+ * that comes with Linux or contains [large timezone databases](https://en.wikipedia.org/wiki/Tz_database).
+ *
+ * This repository provides a set of classes and utilities that allow handling of on-board spacecraft time. Most formats
+ * are compatible to the [CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf) specification, and are
+ * customisable to support different precisions, lengths and durations. Additionally, a number of formats and headers
+ * are provided so that timestamps can be transmitted and received from the Ground Station.
+ *
+ * The base timestamp class representing an instant in time is @ref TimeStamp. This is used as the internal
+ * representation of all moments in this repository. Additional formats are provided for convenience and compatibility
+ * with other systems:
+ *  - @ref UTCTimestamp
+ *
+ * You can convert between different types of timestamp, either by using constructors, or conversion functions
+ * defined in each timestamp class.
+ *
+ * Note that you should try sticking to the default @ref TimeStamp class unless needed, as it provides the best
+ * precision and performance for the least amount of memory, and is directly compatible with internal timestamps
+ * used, without loss of precision.
+ *
+ * @section Epoch Epoch
+ *
+ * Internal timestamp representations can use a single point in time referring to $t=0$. This point is referred to as
+ * the **Epoch** and is stored in @ref Time::Epoch. CSSDS suggests using _1 January 1958_ as the Epoch. UNIX uses _1
+ * January 1970_. In this repository however, each implementor can choose a different epoch (e.g. the start of the
+ * mission).
+ *
+ * @section DevUtils Developer utilities
+ * Timestamp classes are designed to make the developer's life easier.
+ *
+ * You can easily compare timestamps without having to call any other functions:
+ * @code
+ * if (timestamp1 < timestamp2)
+ * @endcode
+ *
+ * If you are building on x86, you can directly print UTC timestamps:
+ * @code
+ * std::cout << utcTimestamp << std::endl;
+ * @endcode
+ *
+ * @section UTC UTC and Leap seconds
+ * All internal timestamps are represented in the GMT+00:00 timezone due to the high expense of timezone
+ * calculations.
+ *
+ * This implementation uses **TAI (International Atomic Time)** instead of UTC (Coordinated Universal Time).
+ * [TAI](https://en.wikipedia.org/wiki/International_Atomic_Time) is equivalent to UTC, with the exception of leap
+ * seconds, which are occasionally added to match terrestrial and astronomical time. As leap seconds are added after
+ * human intervention to UTC, it is impossible for a satellite to know the exact UTC time without ground station
+ * intervention.
+ */
+
+/**
+ * An armada of utilities regarding timekeeping, timestamps and conversion between different internal and string
+ * time formats. This file implements [CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf).
+ *
+ * @ingroup Time
+ * @author Baptiste Fournier
+ */
+namespace Time
+{
+inline constexpr uint8_t SecondsPerMinute = 60;
+inline constexpr uint16_t SecondsPerHour = 3600;
+inline constexpr uint32_t SecondsPerDay = 86400;
+static constexpr uint8_t DaysOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/**
+ * Number of bytes used for the basic time units of the CUC header for this mission
+ */
+inline constexpr uint8_t CUCSecondsBytes = 2;
+
+/**
+ * Number of bytes used for the fractional time units of the CUC header for this mission
+ */
+inline constexpr uint8_t CUCFractionalBytes = 2;
+
+/**
+ * The system epoch (clock measurement starting time)
+ * All timestamps emitted by the ECSS services will show the elapsed time (seconds, days etc.) from this epoch.
+ */
+inline constexpr struct {
+	uint16_t year;
+	uint8_t month;
+	uint8_t day;
+} Epoch{
+    2020,
+    1,
+    1,
+};
+
+/**
+ * Number of seconds elapsed between the UNIX epoch (1 January 1970) and the system epoch.
+ *
+ * The system epoch is defined by @ref Epoch.
+ * This constant is used for conversion between Unix and other timestamps.
+ * Leap seconds are not taken into account here.
+ *
+ * @warning This value MUST be updated after every change of the system @ref Epoch. You can use utilities such as
+ * https://www.unixtimestamp.com/ to obtain a correct result.
+ */
+inline constexpr uint32_t EpochSecondsFromUnix = 1577836800;
+
+/**
+ * The maximum theoretical size in bytes of a CUC timestamp, including headers (P-field and T-field)
+ */
+inline constexpr uint8_t CUCTimestampMaximumSize = 9;
+
+static_assert(Epoch.year >= 2019);
+static_assert(Epoch.month < 11 && Epoch.month >= 0);
+static_assert(Epoch.day < DaysOfMonth[Epoch.month]);
+
+/**
+ * Builds the short P-field of the CUC (CCSDS Unsegmented Time Code) format, as defined in CCSDS 301.0-B-4.
+ *
+ * The short P-field contains only one byte. It is used when many octets are used to represent the basic or fractional
+ * time units.
+ *
+ * @see CCSDS 301.0-B-4, Section 3.2.2
+ * @tparam secondsBytes The number of octets used to represent the basic time units
+ * @tparam fractionalBytes The number of octets used to represent the fractional time units
+ * @return A single byte, representing the P-field contents
+ */
+template <int secondsBytes, int fractionalBytes>
+inline constexpr uint8_t buildShortCUCHeader() {
+	static_assert(secondsBytes <= 4, "Use buildLongCUCHeader instead");
+	static_assert(fractionalBytes <= 3, "Use buildLongCUCHeader instead");
+
+	uint8_t header = 0;
+
+	// P-Field extension is 0, CUC header is not extended
+	header += 0;
+
+	// We are using a custom TAI epoch ("agency-defined epoch")
+	header <<= 3U;
+	header += 0b010;
+
+	// Number of bytes in the basic time unit
+	header <<= 2U;
+	header += secondsBytes - 1;
+
+	// Number of bytes in the fractional unit
+	header <<= 2U;
+	header += fractionalBytes;
+
+	return header;
+}
+
+/**
+ * Builds the long P-field of the CUC (CCSDS Unsegmented Time Code) format, as defined in CCSDS 301.0-B-4.
+ *
+ * The long P-field contains two bytes. The 2nd byte is used to define the size of the additional octets added to the
+ * timestamp, which could not fit in a short P-field.
+ *
+ * @see CCSDS 301.0-B-4, Section 3.2.2
+ * @tparam secondsBytes The number of octets used to represent the basic time units
+ * @tparam fractionalBytes The number of octets used to represent the fractional time units
+ * @return Two bytes, representing the P-field contents
+ */
+template <int secondsBytes, int fractionalBytes>
+inline constexpr uint16_t buildLongCUCHeader() {
+	// cppcheck-suppress redundantCondition
+	static_assert(secondsBytes > 4 || fractionalBytes > 3, "Use buildShortCUCHeader instead");
+	static_assert(secondsBytes <= 7, "Number of bytes for seconds over maximum number of octets allowed by CCSDS");
+	static_assert(fractionalBytes <= 6, "Number of bytes for seconds over maximum number of octets allowed by CCSDS");
+
+	uint16_t header = 0;
+
+	uint8_t octet1secondsBytes = std::min(4, secondsBytes);
+	uint8_t octet2secondsBytes = secondsBytes - octet1secondsBytes;
+
+	uint8_t octet1fractionalBytes = std::min(3, fractionalBytes);
+	uint8_t octet2fractionalBytes = fractionalBytes - octet1fractionalBytes;
+
+	// P-Field extension is 1, CUC header is extended
+	header += 1;
+
+	// We are using custom a TAI epoch
+	header <<= 3U;
+	header += 0b010;
+
+	// Number of bytes in the basic time unit
+	header <<= 2U;
+	header += octet1secondsBytes - 1;
+
+	// Number of bytes in the fractional unit
+	header <<= 2U;
+	header += octet1fractionalBytes;
+
+	// P-Field extension is 1, CUC header was extended
+	header <<= 1U;
+	header += 1;
+
+	// Number of bytes in the extended basic time unit
+	header <<= 2U;
+	header += octet2secondsBytes;
+
+	// Number of bytes in the extended fractional unit
+	header <<= 3U;
+	header += octet2fractionalBytes;
+
+	// Last 3 LSB are reserved for custom mission use
+	header <<= 2U;
+	header += 0;
+
+	return header;
+}
+
+/**
+ * Builds the entire P-field of the CUC (CCSDS Unsegmented Time Code) format, as defined in CCSDS 301.0-B-4.
+ *
+ * The P-field contains the metadata of the timestamp, including information about its size and epoch. This function
+ * is implemented for arbitrary sizes (_octet count_) for the basic and fractional time units.
+ *
+ * The following options cannot be changed:
+ *   - Time-code identification is set to an _agency-defined epoch_. This is represented by @ref Epoch.
+ *   - Bits 6-7 of octet 2 (_reserved_) are set to `0`
+ *
+ * @note The P-field (header) does not contain the timestamp information, but only the description of the timestamp's
+ * structure. It may be entirely omitted if the structure is known beforehand.
+ *
+ * @see CCSDS 301.0-B-4, Section 3.2.2
+ * @tparam T An arbitrary `uint` return type of the header
+ * @tparam secondsBytes The number of octets used to represent the basic time units
+ * @tparam fractionalBytes The number of octets used to represent the fractional time units
+ * @return One or two bytes representing the header
+ */
+template <typename T, int secondsBytes, int fractionalBytes>
+inline constexpr T buildCUCHeader() {
+	// TODO: Gitlab issue #106
+	static_assert((secondsBytes + fractionalBytes) <= 8, "Complete arbitrary precision not supported");
+	// cppcheck-suppress syntaxError
+	// cppcheck-suppress redundantCondition
+	if constexpr (secondsBytes <= 4 && fractionalBytes <= 3) {
+		return buildShortCUCHeader<secondsBytes, fractionalBytes>();
+	} else {
+		return buildLongCUCHeader<secondsBytes, fractionalBytes>();
+	}
+}
+
+/**
+ * Returns whether a year is a leap year according to the Gregorian calendar
+ */
+constexpr bool isLeapYear(uint16_t year) {
+	if ((year % 4) != 0) {
+		return false;
+	}
+	if ((year % 100) != 0) {
+		return true;
+	}
+	return (year % 400) == 0;
+}
+
+typedef struct {
+	uint64_t elapsed100msTicks = 0;
+} CustomCUC_t;
+
+} // namespace Time
diff --git a/inc/Time/TimeStamp.hpp b/inc/Time/TimeStamp.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..499fb3c66ac9869be384e9fde471e4eb903d5c05
--- /dev/null
+++ b/inc/Time/TimeStamp.hpp
@@ -0,0 +1,168 @@
+#ifndef ECSS_SERVICES_TIME_HPP
+#define ECSS_SERVICES_TIME_HPP
+
+#include <cstdint>
+#include <algorithm>
+#include "macros.hpp"
+#include <etl/array.h>
+#include "Time.hpp"
+#include "UTCTimestamp.hpp"
+
+/**
+ * A class that represents an instant in time, with convenient conversion
+ * to and from usual time and date representations
+ *
+ * @note
+ * This class uses internally TAI time, and handles UTC leap seconds at conversion to and
+ * from UTC time system.
+ *
+ * @ingroup Time
+ * @author Baptiste Fournier
+ * @see [CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
+ */
+template <uint8_t secondsBytes, uint8_t fractionalBytes>
+class TimeStamp {
+private:
+	static_assert(secondsBytes + fractionalBytes <= 8,
+	              "Currently, this class is not suitable for storage on internal counter larger than uint64_t");
+	typedef typename std::conditional<(secondsBytes < 4 && fractionalBytes < 3), uint8_t, uint16_t>::type CUCHeader_t;
+	typedef typename std::conditional<(secondsBytes + fractionalBytes < 4), uint32_t, uint64_t>::type TAICounter_t;
+
+	/**
+	 * Integer counter of time units since the @ref Time::Epoch. This number essentially represents the timestamp.
+	 *
+	 * The unit represented by this variable depends on `secondsBytes` and `fractionalBytes`. The fractional
+	 * part is included as the least significant bits of this variable, and the base part follows.
+	 */
+	TAICounter_t taiCounter;
+
+	/**
+	 * The constant header ("P-value") of the timestamp, if needed to be attached to any message
+	 */
+	static constexpr CUCHeader_t CUCHeader = Time::buildCUCHeader<CUCHeader_t, secondsBytes, fractionalBytes>();
+
+	/**
+	 * The maximum value that can fit in @ref taiCounter, or the maximum number of seconds since epoch that can be
+	 * represented in this base class
+	 */
+	static constexpr uint64_t maxSecondCounterValue = (1U << (8U * secondsBytes)) - 1;
+
+	/**
+	 * Returns whether the amount of `seconds` can be represented by this TimeStamp.
+	 * If `seconds` is too large, the number of `secondsByte` may not be enough to represent this timestamp.
+	 *
+	 * @param seconds The amount of seconds from @ref Time::Epoch
+	 */
+	static constexpr bool areSecondsValid(TAICounter_t seconds);
+
+public:
+	/**
+	 * Initialize the TimeStamp at @ref Time::Epoch
+	 */
+	TimeStamp() : taiCounter(0){};
+
+	/**
+	 * Initialize the TimeStamp from a duration from epoch in TAI (leap seconds not accounted)
+	 *
+	 * @param taiSecondsFromEpoch An integer number of seconds from the custom @ref Time::Epoch
+	 */
+	explicit TimeStamp(uint64_t taiSecondsFromEpoch);
+
+	/**
+	 * Initialize the TimeStamp from a count of 100ms ticks from epoch in TAI (leap seconds not accounted)
+	 *
+	 * @param customCUCTimestamp An struct containing a 64 bit unsigned number of 100ms
+	 * ticks from the custom @ref Time::Epoch
+	 */
+	explicit TimeStamp(Time::CustomCUC_t customCUCTimestamp);
+
+	/**
+	 * Initialize the TimeStamp from the bytes of a CUC time stamp
+	 *
+	 * @param timestamp A complete CUC timestamp including header, of the maximum possible size, zero padded to the
+	 * right
+	 */
+	explicit TimeStamp(etl::array<uint8_t, Time::CUCTimestampMaximumSize> timestamp);
+
+	/**
+	 * Initialize the Timestamp from a UTC timestamp struct
+	 *
+	 * @param timestamp a UTC timestamp, from Unix Epoch
+	 */
+	explicit TimeStamp(const UTCTimestamp& timestamp);
+
+	/**
+	 * Get the representation as seconds from epoch in TAI
+	 *
+	 * @return The seconds elapsed in TAI since @ref Time::Epoch. This function is explicitly defined
+	 */
+	TAICounter_t asTAIseconds();
+
+	/**
+	 * Get the representation as a struct containing 100ms ticks from epoch in TAI
+	 *
+	 * @return An struct containing a 64 bit unsigned number of 100ms
+	 * ticks from the custom @ref Time::Epoch. This function is explicitly defined.
+	 */
+	Time::CustomCUC_t asCustomCUCTimestamp();
+
+	/**
+	 * Get the representation as seconds from epoch in TAI, for a floating-point representation.
+	 * For an integer result, see the overloaded @ref asTAIseconds function.
+	 *
+	 * @todo Implement integer seconds in this function
+	 * @tparam T The return type of the seconds (float or double).
+	 * @return The seconds elapsed in TAI since @ref Time::Epoch
+	 */
+	template <typename T>
+	T asTAIseconds();
+
+	/**
+	 * Get the representation as CUC formatted bytes
+	 *
+	 * @return The TimeStamp, represented in the CCSDS CUC format
+	 */
+	etl::array<uint8_t, Time::CUCTimestampMaximumSize> toCUCtimestamp();
+
+	/**
+	 * Get the representation as a UTC timestamp
+	 *
+	 * @return The TimeStamp, represented in the structure that holds UTC timestamps
+	 */
+	UTCTimestamp toUTCtimestamp();
+
+	/**
+	 * Compare two timestamps.
+	 *
+	 * @param timestamp the date that will be compared with the pointer `this`
+	 * @return true if the condition is satisfied
+	 */
+	bool operator<(const TimeStamp<secondsBytes, fractionalBytes>& timestamp) const {
+		return taiCounter < timestamp.taiCounter;
+	}
+
+	bool operator>(const TimeStamp<secondsBytes, fractionalBytes>& timestamp) const {
+		return taiCounter > timestamp.taiCounter;
+	}
+
+	bool operator==(const TimeStamp<secondsBytes, fractionalBytes>& timestamp) const {
+		return taiCounter == timestamp.taiCounter;
+	}
+
+	bool operator!=(const TimeStamp<secondsBytes, fractionalBytes>& timestamp) const {
+		return taiCounter != timestamp.taiCounter;
+	}
+
+	bool operator<=(const TimeStamp<secondsBytes, fractionalBytes>& timestamp) const {
+		return taiCounter <= timestamp.taiCounter;
+	}
+
+	bool operator>=(const TimeStamp<secondsBytes, fractionalBytes>& timestamp) const {
+		return taiCounter >= timestamp.taiCounter;
+	}
+};
+
+#include "TimeStamp.tpp"
+typedef TimeStamp<Time::CUCSecondsBytes, Time::CUCFractionalBytes> AcubeSATTimeStamp_t;
+
+#endif
diff --git a/inc/Time/TimeStamp.tpp b/inc/Time/TimeStamp.tpp
new file mode 100644
index 0000000000000000000000000000000000000000..34edf5a2c291153b1a38778ebc0462ec661d9fd6
--- /dev/null
+++ b/inc/Time/TimeStamp.tpp
@@ -0,0 +1,187 @@
+template <uint8_t secondsBytes, uint8_t fractionalBytes>
+constexpr bool TimeStamp<secondsBytes, fractionalBytes>::areSecondsValid(TimeStamp::TAICounter_t seconds) {
+	return seconds < maxSecondCounterValue;
+}
+
+template <uint8_t secondsBytes, uint8_t fractionalBytes>
+TimeStamp<secondsBytes, fractionalBytes>::TimeStamp(uint64_t taiSecondsFromEpoch) {
+	ASSERT_INTERNAL(areSecondsValid((taiSecondsFromEpoch)), ErrorHandler::InternalErrorType::InvalidTimeStampInput);
+
+	taiCounter = static_cast<TAICounter_t>(taiSecondsFromEpoch) << 8 * fractionalBytes;
+}
+
+template <uint8_t secondsBytes, uint8_t fractionalBytes>
+TimeStamp<secondsBytes, fractionalBytes>::TimeStamp(Time::CustomCUC_t customCUCTimestamp) {
+	ASSERT_INTERNAL(areSecondsValid((customCUCTimestamp.elapsed100msTicks / 10)),
+	                ErrorHandler::InternalErrorType::InvalidTimeStampInput);
+	taiCounter = static_cast<TAICounter_t>(customCUCTimestamp.elapsed100msTicks / 10);
+	if (fractionalBytes > 0) {
+		TAICounter_t fractionalPart = static_cast<TAICounter_t>(customCUCTimestamp.elapsed100msTicks) - 10 * taiCounter;
+		taiCounter = taiCounter << 8;
+		taiCounter += fractionalPart * 256 / 10;
+		taiCounter = taiCounter << 8 * (fractionalBytes - 1);
+	}
+}
+
+template <uint8_t secondsCounter, uint8_t fractionalBytes>
+TimeStamp<secondsCounter, fractionalBytes>::TimeStamp(etl::array<uint8_t, Time::CUCTimestampMaximumSize> timestamp) {
+	// process header
+	uint8_t headerSize = 1;
+	if ((timestamp[0] & 0b10000000U) != 0) {
+		headerSize = 2;
+	}
+
+	uint8_t inputSecondsBytes = ((timestamp[0] & 0b00001100U) >> 2U) + 1U;
+	uint8_t inputFractionalBytes = (timestamp[0] & 0b00000011U) >> 0U;
+
+	if (headerSize == 2) {
+		inputSecondsBytes += (timestamp[1] & 0b01100000U) >> 5U;
+		inputFractionalBytes += (timestamp[1] & 0b00011100U) >> 2U;
+	}
+
+	// check input validity (useless bytes set to 0)
+	for (int i = headerSize + inputSecondsBytes + inputFractionalBytes; i < Time::CUCTimestampMaximumSize; i++) {
+		if (timestamp[i] != 0) {
+			ErrorHandler::reportInternalError(ErrorHandler::InternalErrorType::InvalidTimeStampInput);
+			break;
+		}
+	}
+
+	// do checks wrt template precision parameters
+	ASSERT_INTERNAL(inputSecondsBytes <= secondsCounter, ErrorHandler::InternalErrorType::InvalidTimeStampInput);
+	ASSERT_INTERNAL(inputFractionalBytes <= fractionalBytes, ErrorHandler::InternalErrorType::InvalidTimeStampInput);
+
+	// put timestamp into internal counter
+	taiCounter = 0;
+	// add seconds until run out of bytes on input array
+	for (auto i = 0; i < inputSecondsBytes + inputFractionalBytes; i++) {
+		taiCounter = taiCounter << 8;
+		taiCounter += timestamp[headerSize + i];
+	}
+	// pad rightmost bytes to full length
+	taiCounter = taiCounter << 8 * (fractionalBytes - inputFractionalBytes);
+}
+
+template <uint8_t seconds_counter_bytes, uint8_t fractional_counter_bytes>
+TimeStamp<seconds_counter_bytes, fractional_counter_bytes>::TimeStamp(const UTCTimestamp& timestamp) {
+	TAICounter_t seconds = 0;
+	for (int year = Time::Epoch.year; year < timestamp.year; ++year) {
+		seconds += (Time::isLeapYear(year) ? 366 : 365) * Time::SecondsPerDay;
+	}
+	for (int month = Time::Epoch.month; month < timestamp.month; ++month) {
+		seconds += Time::DaysOfMonth[month - 1] * Time::SecondsPerDay;
+		if ((month == 2U) && Time::isLeapYear(timestamp.year)) {
+			seconds += Time::SecondsPerDay;
+		}
+	}
+	seconds += (timestamp.day - Time::Epoch.day) * Time::SecondsPerDay;
+	seconds += timestamp.hour * Time::SecondsPerHour;
+	seconds += timestamp.minute * Time::SecondsPerMinute;
+	seconds += timestamp.second;
+	// TODO: Add check that `seconds` is within bounds (?)
+	taiCounter = static_cast<TAICounter_t>(seconds) << 8 * fractional_counter_bytes;
+}
+
+template <uint8_t secondsBytes, uint8_t fractionalBytes>
+typename TimeStamp<secondsBytes, fractionalBytes>::TAICounter_t
+TimeStamp<secondsBytes, fractionalBytes>::asTAIseconds() {
+	return taiCounter >> (8 * fractionalBytes);
+}
+
+template <uint8_t secondsBytes, uint8_t fractionalBytes>
+Time::CustomCUC_t TimeStamp<secondsBytes, fractionalBytes>::asCustomCUCTimestamp() {
+	TAICounter_t temp = taiCounter;
+	Time::CustomCUC_t return_s = {0};
+	if (fractionalBytes > 0) {
+		temp = temp >> 8 * (fractionalBytes - 1);
+		return_s.elapsed100msTicks += temp * 10 / 256;
+	} else {
+		return_s.elapsed100msTicks += temp * 10;
+	}
+	return return_s;
+}
+
+template <uint8_t secondsBytes, uint8_t fractionalBytes>
+template <typename T>
+T TimeStamp<secondsBytes, fractionalBytes>::asTAIseconds() {
+	static_assert(std::is_floating_point_v<T>, "TimeStamp::asTAIseconds() only accepts numeric types.");
+	static_assert(std::numeric_limits<T>::max() >= maxSecondCounterValue);
+
+	TAICounter_t decimalPart = taiCounter >> (8 * fractionalBytes);
+
+	T fractionalPart = taiCounter - (decimalPart << (8 * fractionalBytes));
+	T fractionalPartMax = (1U << (8U * fractionalBytes)) - 1U;
+
+	return decimalPart + fractionalPart / fractionalPartMax;
+}
+
+template <uint8_t secondsBytes, uint8_t fractionalBytes>
+etl::array<uint8_t, Time::CUCTimestampMaximumSize> TimeStamp<secondsBytes, fractionalBytes>::toCUCtimestamp() {
+	etl::array<uint8_t, Time::CUCTimestampMaximumSize> returnArray = {0};
+
+	static constexpr uint8_t headerBytes = (secondsBytes < 4 && fractionalBytes < 3) ? 1 : 2;
+
+	if (headerBytes == 1) {
+		returnArray[0] = static_cast<uint8_t>(CUCHeader);
+	} else {
+		returnArray[1] = static_cast<uint8_t>(CUCHeader);
+		returnArray[0] = static_cast<uint8_t>(CUCHeader >> 8);
+	}
+
+	for (auto byte = 0; byte < secondsBytes + fractionalBytes; byte++) {
+		uint8_t taiCounterIndex = 8 * (secondsBytes + fractionalBytes - byte - 1);
+		returnArray[headerBytes + byte] = taiCounter >> taiCounterIndex;
+	}
+
+	return returnArray;
+}
+
+template <uint8_t secondsBytes, uint8_t fractionalBytes>
+UTCTimestamp TimeStamp<secondsBytes, fractionalBytes>::toUTCtimestamp() {
+	using namespace Time;
+
+	uint32_t totalSeconds = asTAIseconds();
+
+	uint16_t yearUTC = Epoch.year;
+	uint8_t monthUTC = Epoch.month;
+	uint8_t dayUTC = Epoch.day;
+	uint8_t hour = 0;
+	uint8_t minute = 0;
+	uint8_t second = 0;
+
+	// calculate years
+	while (totalSeconds >= (isLeapYear(yearUTC) ? 366 : 365) * SecondsPerDay) {
+		totalSeconds -= (isLeapYear(yearUTC) ? 366 : 365) * SecondsPerDay;
+		yearUTC++;
+	}
+
+	// calculate months
+	int currentMonth = 0;
+	while (totalSeconds >= (DaysOfMonth[currentMonth] * SecondsPerDay)) {
+		monthUTC++;
+		totalSeconds -= (DaysOfMonth[currentMonth] * SecondsPerDay);
+		currentMonth++;
+		if ((currentMonth == 1U) && isLeapYear(yearUTC)) {
+			if (totalSeconds <= (28 * SecondsPerDay)) {
+				break;
+			}
+			monthUTC++;
+			totalSeconds -= 29 * SecondsPerDay;
+			currentMonth++;
+		}
+	}
+
+	dayUTC = totalSeconds / SecondsPerDay;
+	totalSeconds -= dayUTC * SecondsPerDay;
+	dayUTC++; // add 1 day because we start count from 1 January (and not 0 January!)
+
+	hour = totalSeconds / SecondsPerHour;
+	totalSeconds -= hour * SecondsPerHour;
+
+	minute = totalSeconds / SecondsPerMinute;
+	totalSeconds -= minute * SecondsPerMinute;
+
+	second = totalSeconds;
+
+	return {yearUTC, monthUTC, dayUTC, hour, minute, second};
+}
diff --git a/inc/Time/UTCTimestamp.hpp b/inc/Time/UTCTimestamp.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..062402df0926efcf9d739cadd7182ca1710a8fe2
--- /dev/null
+++ b/inc/Time/UTCTimestamp.hpp
@@ -0,0 +1,60 @@
+#pragma once
+
+#include <cstdint>
+#include <etl/String.hpp>
+
+/**
+ * A class that represents a UTC time and date according to ISO 8601
+ *
+ * This class contains a human-readable representation of a timestamp, accurate down to 1 second. It is not used
+ * for timestamp storage in the satellite due to its high performance and memory cost, but it can be used for
+ * debugging and logging purposes.
+ *
+ * @ingroup Time
+ * @note
+ * This class represents UTC (Coordinated Universal Time) date
+ */
+class UTCTimestamp {
+public:
+	uint16_t year;
+	uint8_t month;
+	uint8_t day;
+	uint8_t hour;
+	uint8_t minute;
+	uint8_t second;
+
+	/**
+	 * Initialise a timestamp with the Unix epoch 1/1/1970 00:00:00
+	 */
+	UTCTimestamp();
+
+	/**
+	 *
+	 * @todo See if this implements leap seconds
+	 * @todo Implement leap seconds as ST[20] parameter
+	 * @param year the year as it used in Gregorian calendar
+	 * @param month the month as it used in Gregorian calendar (1-12 inclusive)
+	 * @param day the day as it used in Gregorian calendar (1-31 inclusive)
+	 * @param hour UTC hour in 24-hour format
+	 * @param minute UTC minutes
+	 * @param second UTC seconds
+	 */
+	UTCTimestamp(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second);
+
+	/**
+	 * @param textTimestamp the timestamp to parse into a UTC date
+	 * @todo Too expensive to implement (?). It is better to remove this and open it as another issue, or create
+	 * a platform-specific converter that will be only used in x86.
+	 */
+	explicit UTCTimestamp(etl::string<32> textTimestamp);
+
+	/**
+	 * Compare two timestamps.
+	 * @param Date the date that will be compared with the pointer `this`
+	 */
+	bool operator<(const UTCTimestamp& Date) const;
+	bool operator>(const UTCTimestamp& Date) const; ///< @copydoc UTCTimestamp::operator<
+	bool operator==(const UTCTimestamp& Date) const; ///< @copydoc UTCTimestamp::operator<
+	bool operator<=(const UTCTimestamp& Date) const; ///< @copydoc UTCTimestamp::operator<
+	bool operator>=(const UTCTimestamp& Date) const; ///< @copydoc UTCTimestamp::operator<
+};
diff --git a/src/Helpers/PMONBase.cpp b/src/Helpers/PMONBase.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..85f163fd24029d46f6dc3300b57a4dc79049d6e6
--- /dev/null
+++ b/src/Helpers/PMONBase.cpp
@@ -0,0 +1,9 @@
+#include "Helpers/PMONBase.hpp"
+#include "ServicePool.hpp"
+
+
+PMONBase::PMONBase(uint16_t monitoredParameterId, uint16_t repetitionNumber)
+    : monitoredParameter(monitoredParameter), monitoredParameterId(monitoredParameterId),
+      repetitionNumber(repetitionNumber) {
+	monitoredParameter = Services.parameterManagement.getParameter(monitoredParameterId)->get();
+}
\ No newline at end of file
diff --git a/src/Helpers/TimeHelper.cpp b/src/Helpers/TimeHelper.cpp
deleted file mode 100644
index 942635140f585f3bb9d9e11d0bae32516610683c..0000000000000000000000000000000000000000
--- a/src/Helpers/TimeHelper.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-#include "Helpers/TimeHelper.hpp"
-
-bool TimeHelper::IsLeapYear(uint16_t year) {
-	if ((year % 4) != 0) {
-		return false;
-	}
-	if ((year % 100) != 0) {
-		return true;
-	}
-	return (year % 400) == 0;
-}
-
-uint32_t TimeHelper::utcToSeconds(const TimeAndDate& TimeInfo) {
-	// the date, that \p TimeInfo represents, should be greater than or equal to 1/1/2019 and the
-	// date should be valid according to Gregorian calendar
-	ASSERT_INTERNAL(TimeInfo.year >= 2019, ErrorHandler::InternalErrorType::InvalidDate);
-	ASSERT_INTERNAL((1 <= TimeInfo.month) && (TimeInfo.month <= 12), ErrorHandler::InternalErrorType::InvalidDate);
-	ASSERT_INTERNAL((1 <= TimeInfo.day) && (TimeInfo.day <= 31), ErrorHandler::InternalErrorType::InvalidDate);
-	ASSERT_INTERNAL(TimeInfo.hour <= 24, ErrorHandler::InternalErrorType::InvalidDate);
-	ASSERT_INTERNAL(TimeInfo.minute <= 60, ErrorHandler::InternalErrorType::InvalidDate);
-	ASSERT_INTERNAL(TimeInfo.second <= 60, ErrorHandler::InternalErrorType::InvalidDate);
-
-	uint32_t secs = 1546300800; // elapsed seconds from Unix epoch until 1/1/2019 00:00:00 (UTC)
-	for (uint16_t y = 2019; y < TimeInfo.year; ++y) {
-		secs += (IsLeapYear(y) ? 366 : 365) * SecondsPerDay;
-	}
-	for (uint16_t m = 1; m < TimeInfo.month; ++m) {
-		secs += DaysOfMonth[m - 1U] * SecondsPerDay;
-		if ((m == 2U) && IsLeapYear(TimeInfo.year)) {
-			secs += SecondsPerDay;
-		}
-	}
-	secs += (TimeInfo.day - 1) * SecondsPerDay;
-	secs += TimeInfo.hour * SecondsPerHour;
-	secs += TimeInfo.minute * SecondsPerMinute;
-	secs += TimeInfo.second;
-	return secs;
-}
-
-struct TimeAndDate TimeHelper::secondsToUTC(uint32_t seconds) {
-	// elapsed seconds should be between dates, that are after 1/1/2019 and Unix epoch
-	ASSERT_INTERNAL(seconds >= 1546300800, ErrorHandler::InternalErrorType::InvalidDate);
-
-	seconds -= 1546300800; // elapsed seconds from Unix epoch until 1/1/2019 00:00:00 (UTC)
-	TimeAndDate TimeInfo;
-	TimeInfo.year = 2019;
-	TimeInfo.month = 1;
-	TimeInfo.day = 0;
-	TimeInfo.hour = 0;
-	TimeInfo.minute = 0;
-	TimeInfo.second = 0;
-
-	// calculate years
-	while (seconds >= (IsLeapYear(TimeInfo.year) ? 366 : 365) * SecondsPerDay) {
-		seconds -= (IsLeapYear(TimeInfo.year) ? 366 : 365) * SecondsPerDay;
-		TimeInfo.year++;
-	}
-
-	// calculate months
-	uint8_t i = 0;
-	while (seconds >= (DaysOfMonth[i] * SecondsPerDay)) {
-		TimeInfo.month++;
-		seconds -= (DaysOfMonth[i] * SecondsPerDay);
-		i++;
-		if ((i == 1U) && IsLeapYear(TimeInfo.year)) {
-			if (seconds <= (28 * SecondsPerDay)) {
-				break;
-			}
-			TimeInfo.month++;
-			seconds -= 29 * SecondsPerDay;
-			i++;
-		}
-	}
-
-	// calculate days
-	TimeInfo.day = seconds / SecondsPerDay;
-	seconds -= TimeInfo.day * SecondsPerDay;
-	TimeInfo.day++; // add 1 day because we start count from 1 January (and not 0 January!)
-
-	// calculate hours
-	TimeInfo.hour = seconds / SecondsPerHour;
-	seconds -= TimeInfo.hour * SecondsPerHour;
-
-	// calculate minutes
-	TimeInfo.minute = seconds / SecondsPerMinute;
-	seconds -= TimeInfo.minute * SecondsPerMinute;
-
-	// calculate seconds
-	TimeInfo.second = seconds;
-
-	return TimeInfo;
-}
-
-uint64_t TimeHelper::generateCDSTimeFormat(const TimeAndDate& TimeInfo) {
-	/**
-	 * Define the T-field. The total number of octets for the implementation of T-field is 6(2 for
-	 * the `DAY` and 4 for the `ms of day`
-	 */
-
-	uint32_t seconds = utcToSeconds(TimeInfo);
-
-	/**
-	 * The `DAY` segment, 16 bits as defined from standard. Actually the days passed since Unix
-	 * epoch
-	 */
-	auto elapsedDays = static_cast<uint16_t>(seconds / SecondsPerDay);
-
-	/**
-	 * The `ms of day` segment, 32 bits as defined in standard. The `ms of the day` and DAY`
-	 * should give the time passed since Unix epoch
-	 */
-	auto msOfDay = static_cast<uint32_t>((seconds % SecondsPerDay) * 1000);
-
-	uint64_t timeFormat = (static_cast<uint64_t>(elapsedDays) << 32) | msOfDay;
-
-	return timeFormat;
-}
-
-TimeAndDate TimeHelper::parseCDStimeFormat(const uint8_t* data) {
-	uint16_t elapsedDays = ((static_cast<uint16_t>(data[0])) << 8) | (static_cast<uint16_t>(data[1]));
-	uint32_t msOfDay = ((static_cast<uint32_t>(data[2])) << 24) | ((static_cast<uint32_t>(data[3]))) << 16 |
-	                   ((static_cast<uint32_t>(data[4]))) << 8 | (static_cast<uint32_t>(data[5]));
-
-	uint32_t seconds = (elapsedDays * SecondsPerDay) + (msOfDay / 1000U);
-
-	return secondsToUTC(seconds);
-}
-
-uint32_t TimeHelper::generateCUCTimeFormat(const struct TimeAndDate& TimeInfo) {
-	return (utcToSeconds(TimeInfo) + LeapSeconds);
-}
-
-TimeAndDate TimeHelper::parseCUCTimeFormat(const uint8_t* data) {
-	uint32_t seconds = ((static_cast<uint32_t>(data[0])) << 24) | ((static_cast<uint32_t>(data[1]))) << 16 |
-	                   ((static_cast<uint32_t>(data[2]))) << 8 | (static_cast<uint32_t>(data[3]));
-	seconds -= LeapSeconds;
-
-	return secondsToUTC(seconds);
-}
diff --git a/src/MessageParser.cpp b/src/MessageParser.cpp
index 2b164eaad5193d8c0528032e2c929b5e5d92e2fc..c54e00aa772b4331990297f2610a5b54f3491a1d 100644
--- a/src/MessageParser.cpp
+++ b/src/MessageParser.cpp
@@ -7,6 +7,7 @@
 
 void MessageParser::execute(Message& message) {
 	switch (message.serviceType) {
+
 #ifdef SERVICE_HOUSEKEEPING
 		case HousekeepingService::ServiceType:
 			Services.housekeeping.execute(message);
@@ -43,6 +44,12 @@ void MessageParser::execute(Message& message) {
 			break;
 #endif
 
+#ifdef SERVICE_ONBOARDMONITORING
+		case OnBoardMonitoringService::ServiceType:
+			Services.onBoardMonitoringService.execute(message);
+			break;
+#endif
+
 #ifdef SERVICE_TEST
 		case TestService::ServiceType:
 			Services.testService.execute(message);
diff --git a/src/Platform/x86/HousekeepingService.cpp b/src/Platform/x86/HousekeepingService.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..218f2223c8d6e0171fc437238d0a0088e8455121
--- /dev/null
+++ b/src/Platform/x86/HousekeepingService.cpp
@@ -0,0 +1,17 @@
+#include "ECSS_Configuration.hpp"
+
+#ifdef SERVICE_HOUSEKEEPING
+
+#include "Services/HousekeepingService.hpp"
+#include "Parameters/PlatformParameters.hpp"
+
+void HousekeepingService::initializeHousekeepingStructures() {
+    HousekeepingStructure structure;
+    structure.structureId = 1;
+    structure.periodicGenerationActionStatus = true;
+    structure.collectionInterval = 500;
+    structure.simplyCommutatedParameterIds = {PlatformParameters::parameter1.getValue()};
+    housekeepingStructures.insert({structure.structureId, structure});
+}
+
+#endif
\ No newline at end of file
diff --git a/src/Platform/x86/Time/UTCTimestamp.cpp b/src/Platform/x86/Time/UTCTimestamp.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c48aecd4f00d8e2a356c443bf933236d15c8052c
--- /dev/null
+++ b/src/Platform/x86/Time/UTCTimestamp.cpp
@@ -0,0 +1,12 @@
+#include <Time/UTCTimestamp.hpp>
+#include <ostream>
+#include <iomanip>
+
+std::ostream& operator<<(std::ostream& o, UTCTimestamp const& date) {
+	// YYYY-MM-DDTHH:mm:ss.sssZ
+	o.fill('0');
+	o << static_cast<int>(date.year) << "-" << std::setw(2) << static_cast<int>(date.month) << "-" << std::setw(2)
+	  << static_cast<int>(date.day) << "T" << std::setw(2) << static_cast<int>(date.hour) << ":" << std::setw(2)
+	  << static_cast<int>(date.minute) << ":" << std::setw(2) << static_cast<int>(date.second) << ":000Z" << std::endl;
+	return o;
+}
diff --git a/src/Platform/x86/main.cpp b/src/Platform/x86/main.cpp
index 52cdd073d92de1bae7b19804365e74fcf6ffec73..016647fda0cf1e2142123300802652a6b2899222 100644
--- a/src/Platform/x86/main.cpp
+++ b/src/Platform/x86/main.cpp
@@ -1,7 +1,8 @@
 #include <iostream>
 #include <Logger.hpp>
+#include <Time/UTCTimestamp.hpp>
+#include <Platform/x86/Helpers/UTCTimestamp.hpp>
 #include "Helpers/CRCHelper.hpp"
-#include "Helpers/TimeHelper.hpp"
 #include "Services/TestService.hpp"
 #include "Services/ParameterService.hpp"
 #include "Services/RequestVerificationService.hpp"
@@ -361,5 +362,7 @@ int main() {
 	timeBasedSchedulingService.summaryReportActivitiesByID(receivedMsg);
 
 	LOG_NOTICE << "ECSS Services test complete";
+
+	std::cout << UTCTimestamp() << std::endl;
 	return 0;
 }
diff --git a/src/Services/HousekeepingService.cpp b/src/Services/HousekeepingService.cpp
index 1eefcbffc86c4c75956fc6b6a55377fa3e6a2be3..35beaa2ab3163197ebd778e805a408c739beeaea 100644
--- a/src/Services/HousekeepingService.cpp
+++ b/src/Services/HousekeepingService.cpp
@@ -259,3 +259,28 @@ bool HousekeepingService::existsInVector(const etl::vector<uint16_t, ECSSMaxSimp
                                          uint16_t parameterId) {
 	return std::find(std::begin(ids), std::end(ids), parameterId) != std::end(ids);
 }
+
+uint32_t
+HousekeepingService::reportPendingStructures(uint32_t currentTime, uint32_t previousTime, uint32_t expectedDelay) {
+    uint32_t nextCollection = std::numeric_limits<uint32_t>::max();
+
+    for (auto &housekeepingStructure: housekeepingStructures) {
+        if (housekeepingStructure.second.collectionInterval == 0) {
+            housekeepingParametersReport(housekeepingStructure.second.structureId);
+            nextCollection = 0;
+            continue;
+        }
+        if (currentTime != 0 and (currentTime % housekeepingStructure.second.collectionInterval == 0 or
+                                  (previousTime + expectedDelay) % housekeepingStructure.second.collectionInterval ==
+                                  0)) {
+            housekeepingParametersReport(housekeepingStructure.second.structureId);
+        }
+        uint32_t structureTimeToCollection = housekeepingStructure.second.collectionInterval -
+                                             currentTime % housekeepingStructure.second.collectionInterval;
+        if (nextCollection > structureTimeToCollection) {
+            nextCollection = structureTimeToCollection;
+        }
+    }
+
+    return nextCollection;
+}
diff --git a/src/Services/OnBoardMonitoringService.cpp b/src/Services/OnBoardMonitoringService.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f644546db72bc8f8bfc59ec14352064893258cb1
--- /dev/null
+++ b/src/Services/OnBoardMonitoringService.cpp
@@ -0,0 +1,75 @@
+#include "ECSS_Configuration.hpp"
+#ifdef SERVICE_ONBOARDMONITORING
+#include "Message.hpp"
+#include "Services/OnBoardMonitoringService.hpp"
+#include "etl/map.h"
+
+void OnBoardMonitoringService::enableParameterMonitoringDefinitions(Message& message) {
+	message.assertTC(ServiceType, EnableParameterMonitoringDefinitions);
+
+	uint16_t numberOfPMONDefinitions = message.readUint16();
+	for (uint16_t i = 0; i < numberOfPMONDefinitions; i++) {
+		uint16_t currentId = message.readEnum16();
+		auto definition = parameterMonitoringList.find(currentId);
+		if (definition == parameterMonitoringList.end()) {
+			ErrorHandler::reportError(
+			    message, ErrorHandler::ExecutionStartErrorType::GetNonExistingParameterMonitoringDefinition);
+			continue;
+		}
+		definition->second.get().repetitionNumber = 0;
+		definition->second.get().monitoringEnabled = true;
+	}
+}
+
+void OnBoardMonitoringService::disableParameterMonitoringDefinitions(Message& message) {
+	message.assertTC(ServiceType, DisableParameterMonitoringDefinitions);
+
+	uint16_t numberOfPMONDefinitions = message.readUint16();
+	for (uint16_t i = 0; i < numberOfPMONDefinitions; i++) {
+		uint16_t currentId = message.readEnum16();
+		auto definition = parameterMonitoringList.find(currentId);
+		if (definition == parameterMonitoringList.end()) {
+			ErrorHandler::reportError(
+			    message, ErrorHandler::ExecutionStartErrorType::GetNonExistingParameterMonitoringDefinition);
+			continue;
+		}
+		definition->second.get().monitoringEnabled = false;
+		definition->second.get().checkingStatus = PMONBase::Unchecked;
+	}
+}
+
+void OnBoardMonitoringService::changeMaximumTransitionReportingDelay(Message& message) {
+	message.assertTC(ServiceType, ChangeMaximumTransitionReportingDelay);
+	maximumTransitionReportingDelay = message.readUint16();
+}
+
+void OnBoardMonitoringService::deleteAllParameterMonitoringDefinitions(Message& message) {
+	message.assertTC(ServiceType, DeleteAllParameterMonitoringDefinitions);
+	if (parameterMonitoringFunctionStatus) {
+		ErrorHandler::reportError(
+		    message, ErrorHandler::ExecutionStartErrorType::InvalidRequestToDeleteAllParameterMonitoringDefinitions);
+		return;
+	}
+	parameterMonitoringList.clear();
+}
+
+void OnBoardMonitoringService::execute(Message& message) {
+	switch (message.messageType) {
+		case EnableParameterMonitoringDefinitions:
+			enableParameterMonitoringDefinitions(message);
+			break;
+		case DisableParameterMonitoringDefinitions:
+			disableParameterMonitoringDefinitions(message);
+			break;
+		case ChangeMaximumTransitionReportingDelay:
+			changeMaximumTransitionReportingDelay(message);
+			break;
+		case DeleteAllParameterMonitoringDefinitions:
+			deleteAllParameterMonitoringDefinitions(message);
+			break;
+		default:
+			ErrorHandler::reportInternalError(ErrorHandler::OtherMessageType);
+	}
+}
+
+#endif
\ No newline at end of file
diff --git a/src/Helpers/TimeAndDate.cpp b/src/Time/UTCTimestamp.cpp
similarity index 68%
rename from src/Helpers/TimeAndDate.cpp
rename to src/Time/UTCTimestamp.cpp
index 54a17b6b17f0ca2838f741222858fc97777f25ae..072d6b67ff32e1db02d1a835a6ae61c3d43d9a1f 100644
--- a/src/Helpers/TimeAndDate.cpp
+++ b/src/Time/UTCTimestamp.cpp
@@ -1,18 +1,11 @@
-#include "Helpers/TimeHelper.hpp"
+#include "Time/Time.hpp"
+#include "Time/UTCTimestamp.hpp"
+#include <iomanip>
 
-TimeAndDate::TimeAndDate() {
-	// Unix epoch 1/1/1970
-	year = 1970;
-	month = 1;
-	day = 1;
-	hour = 0;
-	minute = 0;
-	second = 0;
-}
+UTCTimestamp::UTCTimestamp() : year(1970), month(1), second(0), minute(0), hour(0), day(1) {}
 
-TimeAndDate::TimeAndDate(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) {
-	// check if the parameters make sense
-	ASSERT_INTERNAL(2019 <= year, ErrorHandler::InternalErrorType::InvalidDate);
+UTCTimestamp::UTCTimestamp(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) {
+	ASSERT_INTERNAL(1970 <= year, ErrorHandler::InternalErrorType::InvalidDate);
 	ASSERT_INTERNAL((1 <= month) && (month <= 12), ErrorHandler::InternalErrorType::InvalidDate);
 	ASSERT_INTERNAL((1 <= day) && (day <= 31), ErrorHandler::InternalErrorType::InvalidDate);
 	ASSERT_INTERNAL(hour < 24, ErrorHandler::InternalErrorType::InvalidDate);
@@ -28,8 +21,7 @@ TimeAndDate::TimeAndDate(uint16_t year, uint8_t month, uint8_t day, uint8_t hour
 	this->second = second;
 }
 
-bool TimeAndDate::operator<(const TimeAndDate& Date) {
-	// compare years
+bool UTCTimestamp::operator<(const UTCTimestamp& Date) const {
 	if (this->year < Date.year) {
 		return true;
 	}
@@ -37,7 +29,6 @@ bool TimeAndDate::operator<(const TimeAndDate& Date) {
 		return false;
 	}
 
-	// compare months
 	if (this->month < Date.month) {
 		return true;
 	}
@@ -45,7 +36,6 @@ bool TimeAndDate::operator<(const TimeAndDate& Date) {
 		return false;
 	}
 
-	// compare days
 	if (this->day < Date.day) {
 		return true;
 	}
@@ -53,7 +43,6 @@ bool TimeAndDate::operator<(const TimeAndDate& Date) {
 		return false;
 	}
 
-	// compare hours
 	if (this->hour < Date.hour) {
 		return true;
 	}
@@ -61,7 +50,6 @@ bool TimeAndDate::operator<(const TimeAndDate& Date) {
 		return false;
 	}
 
-	// compare minutes
 	if (this->minute < Date.minute) {
 		return true;
 	}
@@ -69,7 +57,6 @@ bool TimeAndDate::operator<(const TimeAndDate& Date) {
 		return false;
 	}
 
-	// compare seconds
 	if (this->second < Date.second) {
 		return true;
 	}
@@ -77,48 +64,37 @@ bool TimeAndDate::operator<(const TimeAndDate& Date) {
 	return false;
 }
 
-bool TimeAndDate::operator>(const TimeAndDate& Date) {
-	// compare years
+bool UTCTimestamp::operator>(const UTCTimestamp& Date) const {
 	if (this->year > Date.year) {
 		return true;
 	}
 	if (this->year < Date.year) {
 		return false;
 	}
-
-	// compare months
 	if (this->month > Date.month) {
 		return true;
 	}
 	if (this->month < Date.month) {
 		return false;
 	}
-
-	// compare days
 	if (this->day > Date.day) {
 		return true;
 	}
 	if (this->day < Date.day) {
 		return false;
 	}
-
-	// compare hours
 	if (this->hour > Date.hour) {
 		return true;
 	}
 	if (this->hour < Date.hour) {
 		return false;
 	}
-
-	// compare minutes
 	if (this->minute > Date.minute) {
 		return true;
 	}
 	if (this->minute < Date.minute) {
 		return false;
 	}
-
-	// compare seconds
 	if (this->second > Date.second) {
 		return true;
 	}
@@ -126,44 +102,32 @@ bool TimeAndDate::operator>(const TimeAndDate& Date) {
 	return false;
 }
 
-bool TimeAndDate::operator==(const TimeAndDate& Date) {
-	// compare years
+bool UTCTimestamp::operator==(const UTCTimestamp& Date) const {
 	if (this->year != Date.year) {
 		return false;
 	}
-
-	// compare months
 	if (this->month != Date.month) {
 		return false;
 	}
-
-	// compare days
 	if (this->day != Date.day) {
 		return false;
 	}
-
-	// compare hours
 	if (this->hour != Date.hour) {
 		return false;
 	}
-
-	// compare minutes
 	if (this->minute != Date.minute) {
 		return false;
 	}
-
-	// compare seconds
 	if (this->second != Date.second) {
 		return false;
 	}
-
 	return true;
 }
 
-bool TimeAndDate::operator<=(const TimeAndDate& Date) {
+bool UTCTimestamp::operator<=(const UTCTimestamp& Date) const {
 	return ((*this < Date) || (*this == Date));
 }
 
-bool TimeAndDate::operator>=(const TimeAndDate& Date) {
+bool UTCTimestamp::operator>=(const UTCTimestamp& Date) const {
 	return ((*this > Date) || (*this == Date));
 }
diff --git a/test/Helpers/TimeAndDate.cpp b/test/Helpers/TimeAndDate.cpp
deleted file mode 100644
index 13866f29ebff1432aa88562fdb7f4020690fc37a..0000000000000000000000000000000000000000
--- a/test/Helpers/TimeAndDate.cpp
+++ /dev/null
@@ -1,235 +0,0 @@
-#include "catch2/catch.hpp"
-#include "Helpers/TimeAndDate.hpp"
-#include "../Services/ServiceTests.hpp"
-
-TEST_CASE("Date comparison", "[operands]") {
-	SECTION("Invalid date") {
-		TimeAndDate InvalidDate0(1900, 2, 2, 4, 5, 6); // error in year
-		TimeAndDate InvalidDate1(2030, 70, 2, 4, 5, 6); // error in month
-		TimeAndDate InvalidDate2(2030, 2, 73, 4, 5, 6); // error in day
-		TimeAndDate InvalidDate3(2030, 2, 2, 74, 5, 6); // error in hour
-		TimeAndDate InvalidDate4(2030, 2, 2, 4, 75, 6); // error in minute
-		TimeAndDate InvalidDate5(2030, 2, 2, 4, 5, 76); // error in seconds
-
-		CHECK(ServiceTests::countErrors() == 6);
-		CHECK(ServiceTests::thrownError(ErrorHandler::InvalidDate));
-	}
-
-	SECTION("Different year") {
-		TimeAndDate Now;
-		// 10/04/2021 10:15:00
-		Now.year = 2021;
-		Now.month = 4;
-		Now.day = 10;
-		Now.hour = 10;
-		Now.minute = 15;
-		Now.second = 0;
-
-		TimeAndDate Date;
-		// 10/04/2020 10:15:00
-		Date.year = 2020;
-		Date.month = 4;
-		Date.day = 10;
-		Date.hour = 10;
-		Date.minute = 15;
-		Date.second = 0;
-
-		CHECK((Now < Date) == false);
-		CHECK((Now > Date) == true);
-		CHECK((Now > Date) == true);
-		CHECK((Now < Date) == false);
-
-		CHECK((Now <= Date) == false);
-		CHECK((Now >= Date) == true);
-		CHECK((Now >= Date) == true);
-		CHECK((Now <= Date) == false);
-
-		CHECK((Now == Date) == false);
-	}
-
-	SECTION("Different month") {
-		TimeAndDate Now;
-		// 10/05/2020 10:15:00
-		Now.year = 2020;
-		Now.month = 5;
-		Now.day = 10;
-		Now.hour = 10;
-		Now.minute = 15;
-		Now.second = 0;
-
-		TimeAndDate Date;
-		// 10/04/2020 10:15:00
-		Date.year = 2020;
-		Date.month = 4;
-		Date.day = 10;
-		Date.hour = 10;
-		Date.minute = 15;
-		Date.second = 0;
-
-		CHECK((Now < Date) == false);
-		CHECK((Now > Date) == true);
-		CHECK((Now > Date) == true);
-		CHECK((Now < Date) == false);
-
-		CHECK((Now <= Date) == false);
-		CHECK((Now >= Date) == true);
-		CHECK((Now >= Date) == true);
-		CHECK((Now <= Date) == false);
-
-		CHECK((Now == Date) == false);
-	}
-
-	SECTION("Different day") {
-		TimeAndDate Now;
-		// 11/04/2020 10:15:00
-		Now.year = 2020;
-		Now.month = 5;
-		Now.day = 11;
-		Now.hour = 10;
-		Now.minute = 15;
-		Now.second = 0;
-
-		TimeAndDate Date;
-		// 10/04/2020 10:15:00
-		Date.year = 2020;
-		Date.month = 4;
-		Date.day = 10;
-		Date.hour = 10;
-		Date.minute = 15;
-		Date.second = 0;
-
-		CHECK((Now < Date) == false);
-		CHECK((Now > Date) == true);
-		CHECK((Now > Date) == true);
-		CHECK((Now < Date) == false);
-
-		CHECK((Now <= Date) == false);
-		CHECK((Now >= Date) == true);
-		CHECK((Now >= Date) == true);
-		CHECK((Now <= Date) == false);
-
-		CHECK((Now == Date) == false);
-	}
-
-	SECTION("Different hour") {
-		TimeAndDate Now;
-		// 10/04/2020 11:15:00
-		Now.year = 2020;
-		Now.month = 4;
-		Now.day = 10;
-		Now.hour = 11;
-		Now.minute = 15;
-		Now.second = 0;
-
-		TimeAndDate Date;
-		// 10/04/2020 10:15:00
-		Date.year = 2020;
-		Date.month = 4;
-		Date.day = 10;
-		Date.hour = 10;
-		Date.minute = 15;
-		Date.second = 0;
-
-		CHECK((Now < Date) == false);
-		CHECK((Now > Date) == true);
-		CHECK((Now > Date) == true);
-		CHECK((Now < Date) == false);
-
-		CHECK((Now <= Date) == false);
-		CHECK((Now >= Date) == true);
-		CHECK((Now >= Date) == true);
-		CHECK((Now <= Date) == false);
-
-		CHECK((Now == Date) == false);
-	}
-
-	SECTION("Different minute") {
-		TimeAndDate Now;
-		// 10/04/2020 10:16:00
-		Now.year = 2020;
-		Now.month = 4;
-		Now.day = 10;
-		Now.hour = 10;
-		Now.minute = 16;
-		Now.second = 0;
-
-		TimeAndDate Date;
-		// 10/04/2020 10:15:00
-		Date.year = 2020;
-		Date.month = 4;
-		Date.day = 10;
-		Date.hour = 10;
-		Date.minute = 15;
-		Date.second = 0;
-
-		CHECK((Now < Date) == false);
-		CHECK((Now > Date) == true);
-		CHECK((Now > Date) == true);
-		CHECK((Now < Date) == false);
-
-		CHECK((Now <= Date) == false);
-		CHECK((Now >= Date) == true);
-		CHECK((Now >= Date) == true);
-		CHECK((Now <= Date) == false);
-
-		CHECK((Now == Date) == false);
-	}
-
-	SECTION("Different second") {
-		TimeAndDate Now;
-		// 10/04/2020 10:15:01
-		Now.year = 2020;
-		Now.month = 4;
-		Now.day = 10;
-		Now.hour = 10;
-		Now.minute = 15;
-		Now.second = 1;
-
-		TimeAndDate Date;
-		// 10/04/2020 10:15:00
-		Date.year = 2020;
-		Date.month = 4;
-		Date.day = 10;
-		Date.hour = 10;
-		Date.minute = 15;
-		Date.second = 0;
-
-		CHECK((Now < Date) == false);
-		CHECK((Now > Date) == true);
-		CHECK((Now > Date) == true);
-		CHECK((Now < Date) == false);
-
-		CHECK((Now <= Date) == false);
-		CHECK((Now >= Date) == true);
-		CHECK((Now >= Date) == true);
-		CHECK((Now <= Date) == false);
-
-		CHECK((Now == Date) == false);
-	}
-
-	SECTION("Same date") {
-		TimeAndDate Now;
-		// 10/04/2020 10:15:01
-		Now.year = 2020;
-		Now.month = 4;
-		Now.day = 10;
-		Now.hour = 10;
-		Now.minute = 15;
-		Now.second = 0;
-
-		TimeAndDate Date;
-		// 10/04/2020 10:15:00
-		Date.year = 2020;
-		Date.month = 4;
-		Date.day = 10;
-		Date.hour = 10;
-		Date.minute = 15;
-		Date.second = 0;
-
-		CHECK((Now == Date) == true);
-		CHECK((Now <= Date) == true);
-		CHECK((Now >= Date) == true);
-		CHECK((Now >= Date) == true);
-		CHECK((Now <= Date) == true);
-	}
-}
diff --git a/test/Helpers/TimeHelper.cpp b/test/Helpers/TimeHelper.cpp
deleted file mode 100644
index e9d0fd1f22d532553de2925a1cb6ccf3657522ec..0000000000000000000000000000000000000000
--- a/test/Helpers/TimeHelper.cpp
+++ /dev/null
@@ -1,268 +0,0 @@
-#include "catch2/catch.hpp"
-#include "Helpers/TimeHelper.hpp"
-#include "../Services/ServiceTests.hpp"
-
-TEST_CASE("Time format implementation for CDS", "[CDS]") {
-	SECTION("Invalid date") {
-		TimeAndDate TimeInfo;
-
-		// invalid year
-		TimeInfo.year = 2018;
-		TimeInfo.month = 4;
-		TimeInfo.day = 10;
-		TimeInfo.hour = 10;
-		TimeInfo.minute = 15;
-		TimeInfo.second = 0;
-
-		TimeHelper::utcToSeconds(TimeInfo);
-
-		// invalid month
-		TimeInfo.year = 2019;
-		TimeInfo.month = 60;
-		TimeInfo.day = 10;
-		TimeInfo.hour = 10;
-		TimeInfo.minute = 15;
-		TimeInfo.second = 0;
-
-		TimeHelper::utcToSeconds(TimeInfo);
-
-		// invalid day
-		TimeInfo.year = 2019;
-		TimeInfo.month = 4;
-		TimeInfo.day = 35;
-		TimeInfo.hour = 10;
-		TimeInfo.minute = 15;
-		TimeInfo.second = 0;
-
-		TimeHelper::utcToSeconds(TimeInfo);
-
-		// invalid hour
-		TimeInfo.year = 2019;
-		TimeInfo.month = 4;
-		TimeInfo.day = 10;
-		TimeInfo.hour = 100;
-		TimeInfo.minute = 15;
-		TimeInfo.second = 0;
-
-		TimeHelper::utcToSeconds(TimeInfo);
-
-		// invalid minute
-		TimeInfo.year = 2019;
-		TimeInfo.month = 4;
-		TimeInfo.day = 10;
-		TimeInfo.hour = 10;
-		TimeInfo.minute = 200;
-		TimeInfo.second = 0;
-
-		TimeHelper::utcToSeconds(TimeInfo);
-
-		// invalid second
-		TimeInfo.year = 2019;
-		TimeInfo.month = 4;
-		TimeInfo.day = 10;
-		TimeInfo.hour = 10;
-		TimeInfo.minute = 15;
-		TimeInfo.second = 122;
-
-		TimeHelper::utcToSeconds(TimeInfo);
-
-		CHECK(ServiceTests::countErrors() == 6);
-		CHECK(ServiceTests::thrownError(ErrorHandler::InvalidDate));
-	}
-
-	SECTION("Convert UTC date to elapsed seconds since Unix epoch") {
-		TimeAndDate TimeInfo;
-		// 10/04/2020 10:15:00
-		TimeInfo.year = 2020;
-		TimeInfo.month = 4;
-		TimeInfo.day = 10;
-		TimeInfo.hour = 10;
-		TimeInfo.minute = 15;
-		TimeInfo.second = 0;
-
-		uint32_t currTime = TimeHelper::utcToSeconds(TimeInfo);
-
-		uint16_t elapsedDays = currTime / 86400;
-		uint32_t msOfDay = currTime % 86400 * 1000;
-		uint64_t timeFormat = (static_cast<uint64_t>(elapsedDays) << 32 | msOfDay);
-		CHECK(TimeHelper::generateCDSTimeFormat(TimeInfo) == timeFormat);
-
-		// 1/1/2019 00:00:00
-		TimeInfo.year = 2019;
-		TimeInfo.month = 1;
-		TimeInfo.day = 1;
-		TimeInfo.hour = 0;
-		TimeInfo.minute = 0;
-		TimeInfo.second = 0;
-
-		currTime = TimeHelper::utcToSeconds(TimeInfo);
-
-		elapsedDays = currTime / 86400;
-		msOfDay = currTime % 86400 * 1000;
-		timeFormat = (static_cast<uint64_t>(elapsedDays) << 32 | msOfDay);
-		CHECK(TimeHelper::generateCDSTimeFormat(TimeInfo) == timeFormat);
-
-		// 5/12/2020 00:00:00
-		TimeInfo.year = 2020;
-		TimeInfo.month = 12;
-		TimeInfo.day = 5;
-		TimeInfo.hour = 0;
-		TimeInfo.minute = 0;
-		TimeInfo.second = 0;
-
-		currTime = TimeHelper::utcToSeconds(TimeInfo);
-		CHECK(currTime == 1607126400);
-
-		// 10/12/2020 00:00:00
-		TimeInfo.year = 2020;
-		TimeInfo.month = 12;
-		TimeInfo.day = 10;
-		TimeInfo.hour = 0;
-		TimeInfo.minute = 0;
-		TimeInfo.second = 0;
-
-		currTime = TimeHelper::utcToSeconds(TimeInfo);
-		CHECK(currTime == 1607558400);
-
-		// 15/12/2020 00:00:00
-		TimeInfo.year = 2020;
-		TimeInfo.month = 12;
-		TimeInfo.day = 15;
-		TimeInfo.hour = 0;
-		TimeInfo.minute = 0;
-		TimeInfo.second = 0;
-
-		currTime = TimeHelper::utcToSeconds(TimeInfo);
-		CHECK(currTime == 1607990400);
-
-		// 20/12/2020 00:00:00
-		TimeInfo.year = 2020;
-		TimeInfo.month = 12;
-		TimeInfo.day = 20;
-		TimeInfo.hour = 0;
-		TimeInfo.minute = 0;
-		TimeInfo.second = 0;
-
-		currTime = TimeHelper::utcToSeconds(TimeInfo);
-		CHECK(currTime == 1608422400);
-	}
-
-	SECTION("Convert elapsed seconds since Unix epoch to UTC date") {
-		uint32_t seconds = 1586513700; // elapsed seconds between 10/04/2020 10:15:00 and Unix epoch
-
-		TimeAndDate TimeInfo = TimeHelper::secondsToUTC(seconds);
-		CHECK(TimeInfo.year == 2020);
-		CHECK(TimeInfo.month == 4);
-		CHECK(TimeInfo.day == 10);
-		CHECK(TimeInfo.hour == 10);
-		CHECK(TimeInfo.minute == 15);
-		CHECK(TimeInfo.second == 0);
-
-		seconds = 1546300800; // elapsed seconds between 1/1/2019 00:00:00 and Unix epoch
-
-		TimeInfo = TimeHelper::secondsToUTC(seconds);
-		CHECK(TimeInfo.year == 2019);
-		CHECK(TimeInfo.month == 1);
-		CHECK(TimeInfo.day == 1);
-		CHECK(TimeInfo.hour == 0);
-		CHECK(TimeInfo.minute == 0);
-		CHECK(TimeInfo.second == 0);
-
-		seconds = 1550966400; // elapsed seconds between 24/2/2019 00:00:00 and Unix epoch
-
-		TimeInfo = TimeHelper::secondsToUTC(seconds);
-		CHECK(TimeInfo.year == 2019);
-		CHECK(TimeInfo.month == 2);
-		CHECK(TimeInfo.day == 24);
-		CHECK(TimeInfo.hour == 0);
-		CHECK(TimeInfo.minute == 0);
-		CHECK(TimeInfo.second == 0);
-
-		seconds = 1551571200; // elapsed seconds between 3/3/2019 00:00:00 and Unix epoch
-
-		TimeInfo = TimeHelper::secondsToUTC(seconds);
-		CHECK(TimeInfo.year == 2019);
-		CHECK(TimeInfo.month == 3);
-		CHECK(TimeInfo.day == 3);
-		CHECK(TimeInfo.hour == 0);
-		CHECK(TimeInfo.minute == 0);
-		CHECK(TimeInfo.second == 0);
-
-		seconds = 1742907370; // elapsed seconds between 25/3/2025 12:56:10 and Unix epoch
-
-		TimeInfo = TimeHelper::secondsToUTC(seconds);
-		CHECK(TimeInfo.year == 2025);
-		CHECK(TimeInfo.month == 3);
-		CHECK(TimeInfo.day == 25);
-		CHECK(TimeInfo.hour == 12);
-		CHECK(TimeInfo.minute == 56);
-		CHECK(TimeInfo.second == 10);
-
-		seconds = 1583020800; // elapsed seconds between 1/3/2020 00:00:00 and Unix epoch
-
-		TimeInfo = TimeHelper::secondsToUTC(seconds);
-		CHECK(TimeInfo.year == 2020);
-		CHECK(TimeInfo.month == 3);
-		CHECK(TimeInfo.day == 1);
-		CHECK(TimeInfo.hour == 0);
-		CHECK(TimeInfo.minute == 0);
-		CHECK(TimeInfo.second == 0);
-
-		seconds = 1582934400; // elapsed seconds between 2/29/2020 00:00:00 and Unix epoch
-
-		TimeInfo = TimeHelper::secondsToUTC(seconds);
-		CHECK(TimeInfo.year == 2020);
-		CHECK(TimeInfo.month == 2);
-		CHECK(TimeInfo.day == 29);
-		CHECK(TimeInfo.hour == 0);
-		CHECK(TimeInfo.minute == 0);
-		CHECK(TimeInfo.second == 0);
-
-		seconds = 1577923200; // elapsed seconds between 2/1/2020 00:00:00 and Unix epoch
-
-		TimeInfo = TimeHelper::secondsToUTC(seconds);
-		CHECK(TimeInfo.year == 2020);
-		CHECK(TimeInfo.month == 1);
-		CHECK(TimeInfo.day == 2);
-		CHECK(TimeInfo.hour == 0);
-		CHECK(TimeInfo.minute == 0);
-		CHECK(TimeInfo.second == 0);
-	}
-}
-
-TEST_CASE("Time format implementation for CUC", "[CUC]") {
-	TimeAndDate TimeInfo;
-	// 10/04/2020 10:15:00
-	TimeInfo.year = 2020;
-	TimeInfo.month = 4;
-	TimeInfo.day = 10;
-	TimeInfo.hour = 10;
-	TimeInfo.minute = 15;
-	TimeInfo.second = 0;
-
-	uint32_t currTime = TimeHelper::utcToSeconds(TimeInfo);
-
-	uint32_t timeFormat = currTime + LeapSeconds;
-	CHECK(TimeHelper::generateCUCTimeFormat(TimeInfo) == timeFormat);
-
-	Message message = Message(9, 128, Message::TC, 3);
-	message.appendWord(timeFormat);
-	CHECK((TimeHelper::parseCUCTimeFormat(message.data) == TimeInfo) == true);
-
-	// 1/1/2019 00:00:00
-	TimeInfo.year = 2019;
-	TimeInfo.month = 1;
-	TimeInfo.day = 1;
-	TimeInfo.hour = 0;
-	TimeInfo.minute = 0;
-	TimeInfo.second = 0;
-
-	currTime = TimeHelper::utcToSeconds(TimeInfo);
-
-	timeFormat = currTime + LeapSeconds; // TAI format
-	CHECK(TimeHelper::generateCUCTimeFormat(TimeInfo) == timeFormat);
-
-	message = Message(9, 128, Message::TC, 3);
-	message.appendWord(timeFormat);
-	CHECK((TimeHelper::parseCUCTimeFormat(message.data) == TimeInfo) == true);
-}
diff --git a/test/Services/HousekeepingService.cpp b/test/Services/HousekeepingService.cpp
index 37848827d3ff4f71b3eb5994f5fc72692a30bd7c..c4fec918d3564e717b230f50fe7679b97280f655 100644
--- a/test/Services/HousekeepingService.cpp
+++ b/test/Services/HousekeepingService.cpp
@@ -23,6 +23,12 @@ void buildRequest(Message& request, uint8_t idToCreate) {
 	}
 }
 
+/**
+ * Stub function to define the HousekeepingService constructor during tests
+ */
+void HousekeepingService::initializeHousekeepingStructures() {
+}
+
 /**
  * Initializes 3 housekeeping structures with IDs = {0, 4, 6}
  */
@@ -615,3 +621,67 @@ TEST_CASE("Reporting of housekeeping structure periodic properties") {
 		Services.reset();
 	}
 }
+
+TEST_CASE("Periodically reporting Housekeeping Structures") {
+    uint32_t nextCollection = 0;
+	uint32_t currentTime = 0;
+	uint32_t previousTime = 0;
+    SECTION("Non existent structures") {
+        nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection);
+        CHECK(ServiceTests::count() == 0);
+        CHECK(nextCollection == std::numeric_limits<uint32_t>::max());
+    }
+    SECTION("Collection Intervals set to max") {
+        initializeHousekeepingStructures();
+        for (auto &housekeepingStructure: housekeepingService.housekeepingStructures) {
+            housekeepingStructure.second.collectionInterval = std::numeric_limits<uint32_t>::max();
+        }
+        nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection);
+        CHECK(ServiceTests::count() == 0);
+        CHECK(nextCollection == std::numeric_limits<uint32_t>::max());
+    }
+    SECTION("Calculating properly defined collection intervals") {
+        housekeepingService.housekeepingStructures.at(0).collectionInterval = 900;
+        housekeepingService.housekeepingStructures.at(4).collectionInterval = 1000;
+        housekeepingService.housekeepingStructures.at(6).collectionInterval = 2700;
+        nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection);
+		previousTime = currentTime;
+		currentTime += nextCollection;
+        CHECK(currentTime == 900);
+        CHECK(ServiceTests::count() == 0);
+        nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection);
+		previousTime = currentTime;
+		currentTime += nextCollection;
+        CHECK(currentTime == 1000);
+        CHECK(ServiceTests::count() == 1);
+		currentTime += 6;
+        nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection);
+		previousTime = currentTime;
+		currentTime += nextCollection;
+        CHECK(currentTime == 1800);
+        CHECK(ServiceTests::count() == 2);
+        nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection);
+		previousTime = currentTime;
+		currentTime += nextCollection;
+        CHECK(ServiceTests::count() == 3);
+        CHECK(currentTime == 2000);
+		currentTime += 15;
+        nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection);
+		previousTime = currentTime;
+		currentTime += nextCollection;
+        CHECK(ServiceTests::count() == 4);
+        CHECK(currentTime == 2700);
+        nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection);
+		previousTime = currentTime;
+		currentTime += nextCollection;
+        CHECK(ServiceTests::count() == 6);
+        CHECK(currentTime == 3000);
+    }
+    SECTION("Collection Intervals set to 0") {
+        for (auto &housekeepingStructure: housekeepingService.housekeepingStructures) {
+            housekeepingStructure.second.collectionInterval = 0;
+        }
+        nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection);
+        CHECK(nextCollection == 0);
+    }
+}
\ No newline at end of file
diff --git a/test/Services/OnBoardMonitoringService.cpp b/test/Services/OnBoardMonitoringService.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..548cf83969d2fe97e21987e877dc6b147e7b3147
--- /dev/null
+++ b/test/Services/OnBoardMonitoringService.cpp
@@ -0,0 +1,193 @@
+#include <catch2/catch.hpp>
+#include <Services/OnBoardMonitoringService.hpp>
+#include <Message.hpp>
+#include "ServiceTests.hpp"
+#include <etl/array.h>
+#include <etl/String.hpp>
+#include <ServicePool.hpp>
+
+OnBoardMonitoringService& onBoardMonitoringService = Services.onBoardMonitoringService;
+
+struct Fixtures {
+	PMONExpectedValueCheck monitoringDefinition1 = PMONExpectedValueCheck(7, 5, 10, 8, 0);
+	PMONLimitCheck monitoringDefinition2 = PMONLimitCheck(7, 5, 2, 1, 9, 2);
+	PMONDeltaCheck monitoringDefinition3 = PMONDeltaCheck(7, 5, 5, 3, 3, 11, 4);
+	PMONDeltaCheck monitoringDefinition4 = PMONDeltaCheck(7, 5, 5, 3, 3, 11, 4);
+
+	/*
+	 * Constructor to modify monitoring definitions if needed
+	 */
+	Fixtures() {
+		monitoringDefinition1.monitoringEnabled = true;
+	}
+};
+
+Fixtures fixtures;
+
+void initialiseParameterMonitoringDefinitions() {
+	// Reset fixtures to the defaults set up by the constructor
+	new (&fixtures) Fixtures();
+
+	onBoardMonitoringService.addPMONDefinition(0, fixtures.monitoringDefinition1);
+	onBoardMonitoringService.addPMONDefinition(1, fixtures.monitoringDefinition2);
+	onBoardMonitoringService.addPMONDefinition(2, fixtures.monitoringDefinition3);
+	onBoardMonitoringService.addPMONDefinition(3, fixtures.monitoringDefinition4);
+}
+
+TEST_CASE("Enable Parameter Monitoring Definitions") {
+	SECTION("3 valid requests to enable Parameter Monitoring Definitions") {
+		initialiseParameterMonitoringDefinitions();
+
+		Message request =
+		    Message(OnBoardMonitoringService::ServiceType,
+		            OnBoardMonitoringService::MessageType::EnableParameterMonitoringDefinitions, Message::TC, 0);
+		uint16_t numberOfIds = 3;
+		request.appendUint16(numberOfIds);
+		etl::array<uint16_t, 3> PMONIds = {0, 1, 2};
+		request.appendEnum16(PMONIds[0]);
+		request.appendEnum16(PMONIds[1]);
+		request.appendEnum16(PMONIds[2]);
+
+		MessageParser::execute(request);
+		CHECK(ServiceTests::count() == 0);
+
+		CHECK((onBoardMonitoringService.getPMONDefinition(PMONIds[0]).get().monitoringEnabled == true));
+		CHECK((onBoardMonitoringService.getPMONDefinition(PMONIds[1]).get().monitoringEnabled == true));
+		CHECK((onBoardMonitoringService.getPMONDefinition(PMONIds[2]).get().monitoringEnabled == true));
+		CHECK(onBoardMonitoringService.getPMONDefinition(PMONIds[0]).get().repetitionCounter == 0);
+		CHECK(onBoardMonitoringService.getPMONDefinition(PMONIds[1]).get().repetitionCounter == 0);
+		CHECK(onBoardMonitoringService.getPMONDefinition(PMONIds[2]).get().repetitionCounter == 0);
+
+		ServiceTests::reset();
+		Services.reset();
+	}
+	SECTION("2 valid requests to enable Parameter Monitoring Definitions and 1 invalid") {
+		initialiseParameterMonitoringDefinitions();
+
+		Message request =
+		    Message(OnBoardMonitoringService::ServiceType,
+		            OnBoardMonitoringService::MessageType::EnableParameterMonitoringDefinitions, Message::TC, 0);
+		uint16_t numberOfIds = 4;
+		request.appendUint16(numberOfIds);
+		etl::array<uint16_t, 4> PMONIds = {0, 10, 1};
+		request.appendEnum16(PMONIds[0]);
+		request.appendEnum16(PMONIds[1]);
+		request.appendEnum16(PMONIds[2]);
+
+		MessageParser::execute(request);
+		CHECK(ServiceTests::count() == 1);
+		CHECK(ServiceTests::countThrownErrors(ErrorHandler::GetNonExistingParameterMonitoringDefinition) == 1);
+		CHECK((onBoardMonitoringService.getPMONDefinition(PMONIds[0]).get().monitoringEnabled == true));
+		CHECK((onBoardMonitoringService.getPMONDefinition(PMONIds[2]).get().monitoringEnabled == true));
+		CHECK(onBoardMonitoringService.getPMONDefinition(PMONIds[0]).get().repetitionCounter == 0);
+		CHECK(onBoardMonitoringService.getPMONDefinition(PMONIds[2]).get().repetitionCounter == 0);
+
+		ServiceTests::reset();
+		Services.reset();
+	}
+}
+
+TEST_CASE("Disable Parameter Monitoring Definitions") {
+	SECTION("3 valid requests to disable Parameter Monitoring Definitions") {
+		initialiseParameterMonitoringDefinitions();
+		Message request =
+		    Message(OnBoardMonitoringService::ServiceType,
+		            OnBoardMonitoringService::MessageType::DisableParameterMonitoringDefinitions, Message::TC, 0);
+		uint16_t numberOfIds = 3;
+		request.appendUint16(numberOfIds);
+		etl::array<uint16_t, 3> PMONIds = {0, 1, 2};
+		request.appendEnum16(PMONIds[0]);
+		request.appendEnum16(PMONIds[1]);
+		request.appendEnum16(PMONIds[2]);
+		onBoardMonitoringService.getPMONDefinition(PMONIds[0]).get().monitoringEnabled = true;
+		onBoardMonitoringService.getPMONDefinition(PMONIds[1]).get().monitoringEnabled = true;
+		onBoardMonitoringService.getPMONDefinition(PMONIds[2]).get().monitoringEnabled = true;
+
+		MessageParser::execute(request);
+		CHECK(ServiceTests::count() == 0);
+		CHECK(onBoardMonitoringService.getPMONDefinition(PMONIds[0]).get().monitoringEnabled == false);
+		CHECK(onBoardMonitoringService.getPMONDefinition(PMONIds[1]).get().monitoringEnabled == false);
+		CHECK(onBoardMonitoringService.getPMONDefinition(PMONIds[2]).get().monitoringEnabled == false);
+		CHECK(onBoardMonitoringService.getPMONDefinition(PMONIds[0]).get().checkingStatus == PMONBase::Unchecked);
+		CHECK(onBoardMonitoringService.getPMONDefinition(PMONIds[1]).get().checkingStatus == PMONBase::Unchecked);
+		CHECK(onBoardMonitoringService.getPMONDefinition(PMONIds[2]).get().checkingStatus == PMONBase::Unchecked);
+
+		ServiceTests::reset();
+		Services.reset();
+	}
+	SECTION("3 valid requests to disable Parameter Monitoring Definitions and 1 invalid") {
+		initialiseParameterMonitoringDefinitions();
+		Message request =
+		    Message(OnBoardMonitoringService::ServiceType,
+		            OnBoardMonitoringService::MessageType::DisableParameterMonitoringDefinitions, Message::TC, 0);
+		uint16_t numberOfIds = 4;
+		request.appendUint16(numberOfIds);
+		etl::array<uint16_t, 4> PMONIds = {0, 10, 1, 2};
+		request.appendEnum16(PMONIds[0]);
+		request.appendEnum16(PMONIds[1]);
+		request.appendEnum16(PMONIds[2]);
+		request.appendEnum16(PMONIds[3]);
+		onBoardMonitoringService.getPMONDefinition(PMONIds[0]).get().monitoringEnabled = true;
+		onBoardMonitoringService.getPMONDefinition(PMONIds[2]).get().monitoringEnabled = true;
+		onBoardMonitoringService.getPMONDefinition(PMONIds[3]).get().monitoringEnabled = true;
+		MessageParser::execute(request);
+		CHECK(ServiceTests::count() == 1);
+		CHECK(ServiceTests::countThrownErrors(ErrorHandler::GetNonExistingParameterMonitoringDefinition) == 1);
+		CHECK(onBoardMonitoringService.getPMONDefinition(PMONIds[0]).get().monitoringEnabled == false);
+		CHECK(onBoardMonitoringService.getPMONDefinition(PMONIds[2]).get().monitoringEnabled == false);
+		CHECK(onBoardMonitoringService.getPMONDefinition(PMONIds[3]).get().monitoringEnabled == false);
+		CHECK(onBoardMonitoringService.getPMONDefinition(PMONIds[0]).get().checkingStatus == PMONBase::Unchecked);
+		CHECK(onBoardMonitoringService.getPMONDefinition(PMONIds[2]).get().checkingStatus == PMONBase::Unchecked);
+		CHECK(onBoardMonitoringService.getPMONDefinition(PMONIds[3]).get().checkingStatus == PMONBase::Unchecked);
+
+		ServiceTests::reset();
+		Services.reset();
+	}
+}
+
+TEST_CASE("Change Maximum Transition Reporting Delay") {
+	initialiseParameterMonitoringDefinitions();
+	Message request =
+	    Message(OnBoardMonitoringService::ServiceType,
+	            OnBoardMonitoringService::MessageType::ChangeMaximumTransitionReportingDelay, Message::TC, 0);
+	uint16_t newMaximumTransitionReportingDelay = 10;
+	request.appendUint16(newMaximumTransitionReportingDelay);
+	MessageParser::execute(request);
+	CHECK(ServiceTests::count() == 0);
+	CHECK(onBoardMonitoringService.maximumTransitionReportingDelay == newMaximumTransitionReportingDelay);
+
+	ServiceTests::reset();
+	Services.reset();
+}
+
+TEST_CASE("Delete all Parameter Monitoring Definitions") {
+	SECTION("Valid request to delete all Parameter Monitoring Definitions") {
+		initialiseParameterMonitoringDefinitions();
+		onBoardMonitoringService.parameterMonitoringFunctionStatus = false;
+		Message request =
+		    Message(OnBoardMonitoringService::ServiceType,
+		            OnBoardMonitoringService::MessageType::DeleteAllParameterMonitoringDefinitions, Message::TC, 0);
+		MessageParser::execute(request);
+		CHECK(ServiceTests::count() == 0);
+		CHECK(onBoardMonitoringService.isPMONListEmpty());
+
+		ServiceTests::reset();
+		Services.reset();
+	}
+	SECTION("Invalid request to delete all Parameter Monitoring Definitions") {
+		initialiseParameterMonitoringDefinitions();
+		onBoardMonitoringService.parameterMonitoringFunctionStatus = true;
+		Message request =
+		    Message(OnBoardMonitoringService::ServiceType,
+		            OnBoardMonitoringService::MessageType::DeleteAllParameterMonitoringDefinitions, Message::TC, 0);
+		MessageParser::execute(request);
+		CHECK(ServiceTests::count() == 1);
+		CHECK(ServiceTests::countThrownErrors(ErrorHandler::InvalidRequestToDeleteAllParameterMonitoringDefinitions) ==
+		      1);
+
+		CHECK(!onBoardMonitoringService.isPMONListEmpty());
+
+		ServiceTests::reset();
+		Services.reset();
+	}
+}
diff --git a/test/Time/TimeFormats.cpp b/test/Time/TimeFormats.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..883af362b940848bb07bfe4cae5a117fb5178098
--- /dev/null
+++ b/test/Time/TimeFormats.cpp
@@ -0,0 +1,27 @@
+#include "catch2/catch.hpp"
+#include "Time/Time.hpp"
+#include "Time/UTCTimestamp.hpp"
+#include "../Services/ServiceTests.hpp"
+
+TEST_CASE("UTC timestamps") {
+	// invalid year
+	UTCTimestamp Timestamp0{1950, 4, 10, 10, 15, 0};
+
+	// invalid month
+	UTCTimestamp Timestamp1{2020, 60, 10, 10, 15, 0};
+
+	// invalid day
+	UTCTimestamp Timestamp2{2020, 4, 35, 10, 15, 0};
+
+	// invalid hour
+	UTCTimestamp Timestamp3{2020, 4, 10, 100, 15, 0};
+
+	// invalid minute
+	UTCTimestamp Timestamp4{2020, 4, 10, 10, 200, 0};
+
+	// invalid second
+	UTCTimestamp Timestamp5{2020, 4, 10, 10, 15, 100};
+
+	CHECK(ServiceTests::countErrors() == 6);
+	CHECK(ServiceTests::thrownError(ErrorHandler::InvalidDate));
+}
diff --git a/test/Time/TimeStamp.cpp b/test/Time/TimeStamp.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ac99522ba966f817f7a048a615ab4782975007bd
--- /dev/null
+++ b/test/Time/TimeStamp.cpp
@@ -0,0 +1,190 @@
+#include "catch2/catch.hpp"
+#include "Time/TimeStamp.hpp"
+
+using namespace Time;
+
+TEST_CASE("TimeStamp class construction") {
+	// SECTION("Initialize with excessive precision, breaks at compile time"){
+	//   TimeStamp<5, 10> Epoch3;
+	//   TimeStamp<4, 4> Epoch4;
+	// }
+}
+
+TEST_CASE("CUC headers generation") {
+	auto cuc_header1 = buildCUCHeader<uint8_t, 2, 2>();
+	CHECK(cuc_header1 == 0b00100110);
+
+	auto cuc_header2 = buildCUCHeader<uint8_t, 4, 1>();
+	CHECK(cuc_header2 == 0b00101101);
+
+	auto cuc_header3 = buildCUCHeader<uint8_t, 1, 1>();
+	CHECK(cuc_header3 == 0b00100001);
+
+	auto cuc_header4 = buildCUCHeader<uint16_t, 5, 1>();
+	CHECK(cuc_header4 == 0b1010110110100000);
+
+	auto cuc_header5 = buildCUCHeader<uint16_t, 1, 6>();
+	CHECK(cuc_header5 == 0b1010001110001100);
+
+	auto cuc_header6 = buildCUCHeader<uint16_t, 7, 1>();
+	CHECK(cuc_header6 == 0b1010110111100000);
+}
+
+TEST_CASE("TAI idempotence") {
+	int input_time = 1000;
+	TimeStamp<CUCSecondsBytes, CUCFractionalBytes> time(input_time);
+
+	REQUIRE(time.asTAIseconds() == input_time);
+}
+
+TEST_CASE("CUC idempotence") {
+	etl::array<uint8_t, 9> input1 = {0b00101010, 0, 1, 1, 3, 0, 0, 0, 0};
+	TimeStamp<3, 2> time1(input1);
+	etl::array<uint8_t, 9> cuc1 = time1.toCUCtimestamp();
+
+	for (uint8_t i = 0; i < 9; i++) {
+		CHECK(input1[i] == cuc1[i]);
+	}
+
+	etl::array<uint8_t, 9> input2 = {0b10101101, 0b10100000, 218, 103, 11, 0, 3, 23, 0};
+	TimeStamp<5, 1> time2(input2);
+	etl::array<uint8_t, 9> cuc2 = time2.toCUCtimestamp();
+
+	for (auto i = 0; i < 9; i++) {
+		CHECK(input2[i] == cuc2[i]);
+	}
+
+	etl::array<uint8_t, 9> input3 = {0b10100011, 0b10001100, 218, 103, 11, 0, 3, 23, 2};
+	TimeStamp<1, 6> time3(input3);
+	etl::array<uint8_t, 9> cuc3 = time3.toCUCtimestamp();
+
+	for (auto i = 0; i < 9; i++) {
+		CHECK(input3[i] == cuc3[i]);
+	}
+}
+
+TEST_CASE("Conversion between CUC formats") {
+	SECTION("Base unit conversion") {
+		TimeStamp<2, 2> time1(20123);
+		TimeStamp<5, 2> time2(time1.toCUCtimestamp());
+		CHECK(time1.asTAIseconds() == time2.asTAIseconds());
+	}
+
+	SECTION("Floating unit conversion") {
+		etl::array<uint8_t, 9> timeInput = {0b00101010, 0, 1, 1, 3, 0, 0, 0, 0};
+		TimeStamp<3, 2> time1(timeInput);
+
+		TimeStamp<3, 5> time2(time1.toCUCtimestamp());
+		CHECK(time1.asTAIseconds() == time2.asTAIseconds());
+	}
+
+	SECTION("All units conversion") {
+		etl::array<uint8_t, 9> timeInput = {0b00101010, 0, 1, 1, 3, 0, 0, 0, 0};
+		TimeStamp<3, 2> time1(timeInput);
+
+		TimeStamp<4, 4> time2(time1.toCUCtimestamp());
+		CHECK(time1.asTAIseconds() == time2.asTAIseconds());
+	}
+}
+
+TEST_CASE("Use of custom Acubesat CUC format") {
+	SECTION("Check forward conversion") {
+		Time::CustomCUC_t customCUC1 = {1001};
+		TimeStamp<3, 0> time1(customCUC1);
+		CHECK(time1.asTAIseconds() == 100);
+		CHECK(time1.asCustomCUCTimestamp().elapsed100msTicks == 1000);
+		TimeStamp<3, 2> time2(customCUC1);
+		CHECK(time2.asTAIseconds() == 100);
+		CHECK(time2.asCustomCUCTimestamp().elapsed100msTicks == 1000);
+
+		// check rounding errors
+		Time::CustomCUC_t customCUC2 = {1004};
+		TimeStamp<3, 0> time3(customCUC2);
+		CHECK(time3.asTAIseconds() == 100);
+		CHECK(time3.asCustomCUCTimestamp().elapsed100msTicks == 1000);
+		TimeStamp<3, 2> time4(customCUC2);
+		CHECK(time4.asTAIseconds() == 100);
+		CHECK(time4.asCustomCUCTimestamp().elapsed100msTicks == 1003);
+
+		// check rounding errors
+		Time::CustomCUC_t customCUC3 = {1005};
+		TimeStamp<3, 0> time5(customCUC3);
+		CHECK(time5.asTAIseconds() == 100);
+		CHECK(time5.asCustomCUCTimestamp().elapsed100msTicks == 1000);
+		TimeStamp<3, 2> time6(customCUC3);
+		CHECK(time6.asTAIseconds() == 100);
+		CHECK(time6.asCustomCUCTimestamp().elapsed100msTicks == 1005);
+	}
+
+	SECTION("Check idempotence") {
+		Time::CustomCUC_t customCUC1 = {1000};
+		TimeStamp<3, 3> time1(customCUC1);
+		Time::CustomCUC_t customCUC2 = time1.asCustomCUCTimestamp();
+		CHECK(customCUC1.elapsed100msTicks == customCUC2.elapsed100msTicks);
+	}
+}
+
+TEST_CASE("UTC idempotence") {
+	{
+		UTCTimestamp timestamp1(2020, 4, 10, 10, 15, 0); // 10 Apr 2020, 10:15:00;
+		TimeStamp<CUCSecondsBytes, CUCFractionalBytes> time(timestamp1);
+		UTCTimestamp timestamp2 = time.toUTCtimestamp();
+		bool cond = (timestamp2 == timestamp1);
+		REQUIRE(cond);
+	}
+	{
+		UTCTimestamp timestamp1(2035, 1, 1, 0, 0, 1); // 1 Jan 2035 midnight passed;
+		TimeStamp<CUCSecondsBytes, CUCFractionalBytes> time(timestamp1);
+		UTCTimestamp timestamp2 = time.toUTCtimestamp();
+		bool cond = (timestamp2 == timestamp1);
+		REQUIRE(cond);
+	}
+}
+
+TEST_CASE("UTC conversion to and from seconds timestamps") {
+	{
+		UTCTimestamp timestamp1(2020, 12, 5, 0, 0, 0);
+		TimeStamp<CUCSecondsBytes, CUCFractionalBytes> time(timestamp1);
+		REQUIRE(time.asTAIseconds() == 29289600);
+	}
+	{
+		UTCTimestamp timestamp1(2020, 2, 29, 0, 0, 0);
+		TimeStamp<CUCSecondsBytes, CUCFractionalBytes> time(timestamp1);
+		REQUIRE(time.asTAIseconds() == 5097600);
+	}
+	{
+		UTCTimestamp timestamp1(2025, 3, 10, 0, 0, 0);
+		TimeStamp<CUCSecondsBytes, CUCFractionalBytes> time(timestamp1);
+		REQUIRE(time.asTAIseconds() == 163728000);
+	}
+}
+
+// SECTION("Check different templates, should break at compile"){
+//   TimeStamp<1, 2> time1;
+//   TimeStamp<4, 4> time2;
+//   REQUIRE(time1==time2);
+// }
+
+TEST_CASE("Time operators") {
+	TimeStamp<1, 2> time1;
+	TimeStamp<1, 2> time2;
+	TimeStamp<1, 2> time3(10);
+	TimeStamp<1, 2> time4(12);
+	TimeStamp<1, 2> time5(10);
+	TimeStamp<2, 2> time6;
+	REQUIRE(time1 == time2);
+	REQUIRE(time2 == time1);
+	REQUIRE(time3 == time5);
+	REQUIRE(time1 != time3);
+	REQUIRE(time3 != time4);
+	REQUIRE(time3 <= time4);
+	REQUIRE(time3 < time4);
+
+	// REQUIRE(time1 == time6); //should fail at compile, different templates
+}
+
+TEST_CASE("Time runtime class size") {
+	int input_time = 1000;
+	TimeStamp<CUCSecondsBytes, CUCFractionalBytes> time(input_time);
+	REQUIRE(sizeof(time) < 32);
+}