diff --git a/CAL/CalibrationCommon/lib/CMakeLists.txt b/CAL/CalibrationCommon/lib/CMakeLists.txt
index 649177b5079428855a15f071a9b3cdf29dc14047..33896b794a21bf367bc8673dbb6ae432bd29d9fa 100644
--- a/CAL/CalibrationCommon/lib/CMakeLists.txt
+++ b/CAL/CalibrationCommon/lib/CMakeLists.txt
@@ -26,6 +26,7 @@ python_install(
     datacontainers/holography_specification.py
     datacontainers/holography_observation.py
     datacontainers/holography_measurementset.py
+    datacontainers/calibration_table.py
     datacontainers/__init__.py
     coordinates.py
     utils.py
diff --git a/CAL/CalibrationCommon/lib/datacontainers/calibration_table.py b/CAL/CalibrationCommon/lib/datacontainers/calibration_table.py
new file mode 100644
index 0000000000000000000000000000000000000000..e4c406e1054de78c4a9aa7571160f843fd969f63
--- /dev/null
+++ b/CAL/CalibrationCommon/lib/datacontainers/calibration_table.py
@@ -0,0 +1,134 @@
+import logging
+from datetime import datetime
+from re import fullmatch
+from struct import iter_unpack
+from typing import BinaryIO
+from typing import List
+
+from dataclasses import dataclass, asdict
+from numpy import empty as empty_ndarray, ndarray, fromiter as array_from_iter, float64
+
+logger = logging.getLogger(__name__)
+
+__MAX_HEADER_LINES = 100
+__HEADER_LINE_PATTERN = '(^[A-z]*\.[A-z]*\.[A-z]*\s=\s.*$)|(^[A-z]*\.[A-z]*\s=\s.*$)'
+
+_ATTRIBUTE_NAME_TO_SERIALIZED_NAME = {
+    'observation_station': 'CalTableHeader.Observation.Station',
+    'observation_mode': 'CalTableHeader.Observation.Mode',
+    'observation_antennaset': 'CalTableHeader.Observation.AntennaSet',
+    'observation_band': 'CalTableHeader.Observation.Band',
+    'observation_source': 'CalTableHeader.Observation.Source',
+    'observation_date': 'CalTableHeader.Observation.Date',
+    'calibration_version': 'CalTableHeader.Calibration.Version',
+    'calibration_name': 'CalTableHeader.Calibration.Name',
+    'calibration_date': 'CalTableHeader.Calibration.Date',
+    'calibration_ppsdelay': 'CalTableHeader.Calibration.PPSDelay',
+    'comment': 'CalTableHeader.Comment'
+}
+
+
+class UnvalidFileException(Exception):
+    def __init__(self, message):
+        self.message = message
+
+
+def _extract_header(fstream: BinaryIO):
+    header = {}
+    for i in range(__MAX_HEADER_LINES):
+        line = fstream.readline().decode('utf8').rstrip('\n')
+
+        if line == 'HeaderStop':
+            break
+        elif line == 'HeaderStart':
+            continue
+        elif fullmatch(__HEADER_LINE_PATTERN, line):
+
+            key, value = line.split('=')
+
+            key = key.lower().replace('caltableheader.', '').strip().replace('.', '_')
+            value = value.strip()
+            header[key] = value
+        else:
+            logger.error('unrecognized line %s', line)
+            raise UnvalidFileException('unrecognized line %s' % line)
+    if len(header) == 0:
+        raise UnvalidFileException('empty header')
+    return header
+
+
+__FREQUENCIES = 512
+__FLOATS_PER_FREQUENCY = 2
+__N_ANTENNAS_DUTCH = 96
+__N_ANTENNAS_INTERNATIONAL = 192
+
+
+def parse_data(data_buffer):
+    data = array_from_iter(map(lambda x: x[0], iter_unpack('d', data_buffer)), dtype=float)
+    n_antennas = data.shape[0] // __FREQUENCIES // __FLOATS_PER_FREQUENCY
+    if n_antennas not in [__N_ANTENNAS_DUTCH, __N_ANTENNAS_INTERNATIONAL]:
+        raise UnvalidFileException('invalid data range expected %s or %s antennas got %s' %
+                                   (__N_ANTENNAS_DUTCH,
+                                    __N_ANTENNAS_INTERNATIONAL,
+                                    n_antennas))
+
+    data = data.reshape((__FREQUENCIES, n_antennas, __FLOATS_PER_FREQUENCY))
+    complex_data = empty_ndarray([__FREQUENCIES, n_antennas], dtype=complex)
+    complex_data.real = data[:, :, 0]
+    complex_data.imag = data[:, :, 1]
+
+    return complex_data
+
+
+@dataclass(init=True, repr=True, frozen=True)
+class CalibrationTable:
+    observation_station: str
+    observation_mode: str
+    observation_antennaset: str
+    observation_band: str
+    observation_source: str
+    observation_date: datetime
+    calibration_version: int
+    calibration_name: str
+    calibration_date: datetime
+    calibration_ppsdelay: List[int]
+    data: ndarray
+    comment: str = ''
+
+    @staticmethod
+    def load_from_file(file_path):
+        with open(file_path, 'rb') as file_stream:
+            header = _extract_header(file_stream)
+            data_raw = file_stream.read()
+
+        data = parse_data(data_raw)
+
+        calibration_table = CalibrationTable(**header,
+                                             data=data)
+        return calibration_table
+
+    def __serialize_header(self, f_stream: BinaryIO):
+        f_stream.write(b'HeaderStart')
+
+        for key, value in asdict(self).items():
+            if key is 'data':
+                # skipping field data
+                continue
+            serialized_name = _ATTRIBUTE_NAME_TO_SERIALIZED_NAME[key]
+            serialized_line = '{} = {}\n'.format(serialized_name, value).encode('utf8')
+            f_stream.write(serialized_line)
+
+        f_stream.write(b'HeaderStop')
+
+    def __serialize_data(self, f_stream: BinaryIO):
+        dimensions = list(self.data.shape) + [2]
+        data_flatten = empty_ndarray(dimensions, dtype=float64)
+        data_flatten[:, :, 0] = self.data.real
+        data_flatten[:, :, 1] = self.data.imag
+        data_flattened = data_flatten.flatten().tolist()
+        f_stream.write()
+
+
+    def store_to_file(self, file_path):
+        with open(file_path, 'wb') as file_stream:
+            self.__serialize_header(file_stream)
diff --git a/CAL/CalibrationCommon/test/CMakeLists.txt b/CAL/CalibrationCommon/test/CMakeLists.txt
index 17249cdfb5adc3b4af1eb3457bb1be00e0afed75..2a5b743bbaaabb90bf35aa52b1d951770e2e1907 100644
--- a/CAL/CalibrationCommon/test/CMakeLists.txt
+++ b/CAL/CalibrationCommon/test/CMakeLists.txt
@@ -25,5 +25,7 @@ lofar_add_test(t_holography_datatable_class)
 lofar_add_test(t_holography_dataset_class)
 lofar_add_test(t_holography_dataset_class2)
 lofar_add_test(t_holography_observation)
+lofar_add_test(t_calibration_table)
+
 
 
diff --git a/CAL/CalibrationCommon/test/t_calibration_table.in_CalTable-401-HBA-110_190.dat b/CAL/CalibrationCommon/test/t_calibration_table.in_CalTable-401-HBA-110_190.dat
new file mode 100644
index 0000000000000000000000000000000000000000..6dd4108be0159aba328b674b8cf9a5d874258b31
Binary files /dev/null and b/CAL/CalibrationCommon/test/t_calibration_table.in_CalTable-401-HBA-110_190.dat differ
diff --git a/CAL/CalibrationCommon/test/t_calibration_table.py b/CAL/CalibrationCommon/test/t_calibration_table.py
new file mode 100644
index 0000000000000000000000000000000000000000..f833e3319135e563734f42f9ccaa7b8eefca1c9f
--- /dev/null
+++ b/CAL/CalibrationCommon/test/t_calibration_table.py
@@ -0,0 +1,36 @@
+import unittest
+from lofar.calibration.common.datacontainers.calibration_table import CalibrationTable, UnvalidFileException
+
+import logging
+from tempfile import NamedTemporaryFile
+
+CALIBRATION_TABLE_FILENAME = 't_calibration_table.in_CalTable-401-HBA-110_190.dat'
+
+
+class TestCalibrationTable(unittest.TestCase):
+    def test_loading(self):
+        calibration_table = CalibrationTable.load_from_file(CALIBRATION_TABLE_FILENAME)
+        self.assertEqual(calibration_table.observation_station, 'CS401')
+
+    def test_unvalid_file(self):
+        with NamedTemporaryFile('w+b') as temp_file:
+            temp_file.write(b'stuff')
+            temp_file.flush()
+
+            with self.assertRaises(UnvalidFileException):
+                _ = CalibrationTable.load_from_file(temp_file.name)
+
+    def test_storing_file(self):
+        with NamedTemporaryFile('w+b') as temp_file:
+            test_calibration_table = CalibrationTable.load_from_file(CALIBRATION_TABLE_FILENAME)
+
+            test_calibration_table.store_to_file(temp_file.name)
+
+            stored_calibration_table = CalibrationTable.load_from_file(temp_file.name)
+            self.assertEqual(test_calibration_table.observation_station,
+                             stored_calibration_table.observation_station)
+
+
+if __name__ == '__main__':
+    logging.basicConfig(level=logging.DEBUG)
+    unittest.main()
diff --git a/CAL/CalibrationCommon/test/t_calibration_table.run b/CAL/CalibrationCommon/test/t_calibration_table.run
new file mode 100755
index 0000000000000000000000000000000000000000..c60df34d3bf1a38b2ccf4806c398abe0ca7187ad
--- /dev/null
+++ b/CAL/CalibrationCommon/test/t_calibration_table.run
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+# Copyright (C) 2018    ASTRON (Netherlands Institute for Radio Astronomy)
+# P.O. Box 2, 7990 AA Dwingeloo, The Netherlands
+#
+# This file is part of the LOFAR software suite.
+# The LOFAR software suite 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.
+#
+# The LOFAR software suite 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 the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+
+# Run the unit test
+source python-coverage.sh
+python_coverage_test "*calibration_table*" t_calibration_table.py
diff --git a/CAL/CalibrationCommon/test/t_calibration_table.sh b/CAL/CalibrationCommon/test/t_calibration_table.sh
new file mode 100755
index 0000000000000000000000000000000000000000..c4b6e470dab72bbbfb232195b7487564239af6c3
--- /dev/null
+++ b/CAL/CalibrationCommon/test/t_calibration_table.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+# Copyright (C) 2018    ASTRON (Netherlands Institute for Radio Astronomy)
+# P.O. Box 2, 7990 AA Dwingeloo, The Netherlands
+#
+# This file is part of the LOFAR software suite.
+# The LOFAR software suite 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.
+#
+# The LOFAR software suite 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 the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+
+./runctest.sh t_calibration_table
diff --git a/CAL/CalibrationProcessing/test/convert/CMakeLists.txt b/CAL/CalibrationProcessing/test/convert/CMakeLists.txt
index 1006e5ec9f93df055aa14a6a914df05542210968..2093e1068a2aebd49dca269fd99303d965efeefb 100644
--- a/CAL/CalibrationProcessing/test/convert/CMakeLists.txt
+++ b/CAL/CalibrationProcessing/test/convert/CMakeLists.txt
@@ -2,4 +2,4 @@
 
 include(LofarCTest)
 
-lofar_add_test(t_processing)
\ No newline at end of file
+lofar_add_test(t_convert_to_calibration_table)
\ No newline at end of file
diff --git a/CAL/CalibrationProcessing/test/convert/t_convert_to_calibration_table.py b/CAL/CalibrationProcessing/test/convert/t_convert_to_calibration_table.py
index 8b137891791fe96927ad78e64b0aad7bded08bdc..a5aa6a0b3f2d0f838c283b10e9687deac10090c0 100644
--- a/CAL/CalibrationProcessing/test/convert/t_convert_to_calibration_table.py
+++ b/CAL/CalibrationProcessing/test/convert/t_convert_to_calibration_table.py
@@ -1 +1,6 @@
+import unittest
 
+
+class ConvertAcceptanceTest(unittest.TestCase):
+    def test_convertion(self):
+        pass