diff --git a/tests/test_model/test_reftek/test_core.py b/tests/test_model/test_reftek/test_core.py
new file mode 100644
index 0000000000000000000000000000000000000000..ff05396c02a3cbf743f8a96b57dd2b5cd705f24e
--- /dev/null
+++ b/tests/test_model/test_reftek/test_core.py
@@ -0,0 +1,101 @@
+import os
+import unittest
+from pathlib import Path
+
+import numpy
+import obspy.core
+from numpy.testing import assert_array_equal
+
+from sohstationviewer.model.reftek.reftek_data.core import (
+    DiscontinuousTrace,
+    Reftek130,
+)
+from sohstationviewer.model.reftek.reftek_data.header import NotRT130FileError
+
+
+class TestDiscontinuousTrace(unittest.TestCase):
+    def setUp(self) -> None:
+        data = numpy.arange(1024)
+        stub_stats = obspy.core.Stats()
+        times = numpy.arange(1024)
+        self.trace = DiscontinuousTrace(data, stub_stats, times=times)
+
+    def test_times_argument_is_stored(self):
+        self.assertTrue(hasattr(self.trace, '_times'))
+
+    def test_times_utcdatetime(self):
+        with self.assertRaises(NotImplementedError):
+            self.trace.times('utcdatetime')
+
+    def test_times_matplotlib(self):
+        with self.assertRaises(NotImplementedError):
+            self.trace.times('matplotlib')
+
+    def test_times_relative(self):
+        with self.subTest('test_relative_to_start_time'):
+            # The default start time of a trace is 0 anyhow, but we write that
+            # down explicitly for clarity.
+            self.trace.stats.starttime = obspy.core.UTCDateTime(0)
+            expected = numpy.arange(1024)
+            assert_array_equal(self.trace.times('relative'), expected)
+
+        with self.subTest('test_relative_to_given_reftime'):
+            reftime = obspy.core.UTCDateTime(0)
+            expected = numpy.arange(1024)
+            assert_array_equal(self.trace.times('relative', reftime),
+                               expected)
+
+            reftime = obspy.core.UTCDateTime(1024)
+            expected = numpy.arange(-1024, 0)
+            assert_array_equal(self.trace.times('relative', reftime),
+                               expected)
+
+            reftime = obspy.core.UTCDateTime(-1024)
+            expected = numpy.arange(1024, 2048)
+            assert_array_equal(self.trace.times('relative', reftime),
+                               expected)
+
+    def test_times_timestamp(self):
+        expected = numpy.arange(1024)
+        assert_array_equal(self.trace.times('timestamp'), expected)
+
+
+class TestReftek130FromFile(unittest.TestCase):
+    def setUp(self) -> None:
+        self.TEST_DATA_DIR = Path(os.getcwd()).joinpath('tests/test_data')
+        self.rt130_dir = self.TEST_DATA_DIR.joinpath(
+            'RT130-sample/2017149.92EB/2017150/92EB'
+        )
+
+    def test_rt130_file(self):
+        file = self.rt130_dir.joinpath('0/000000000_00000000')
+        rt130 = Reftek130.from_file(file)
+        self.assertIsInstance(rt130, Reftek130)
+
+    def test_rt130_soh_file(self):
+        file = self.rt130_dir.joinpath('0/000000000_00000000')
+        rt130 = Reftek130.from_file(file)
+        # The most common SOH packet type looks to be SH, so we use that as
+        # the default.
+        self.assertIn(b'SH', rt130._data['packet_type'])
+
+    def test_rt130_raw_data_file(self):
+        file = self.rt130_dir.joinpath('1/000000015_0036EE80')
+        rt130 = Reftek130.from_file(file)
+        assert_array_equal(
+            numpy.unique(numpy.sort(rt130._data['packet_type'])),
+            numpy.sort([b'EH', b'DT', b'ET'])
+        )
+
+    def test_non_rt130_file(self):
+        with self.subTest('test_file_exist'):
+            test_file = self.TEST_DATA_DIR.joinpath(
+                'Q330-sample/day_vols_AX08/AX08.XA..HHE.2021.186'
+            )
+            with self.assertRaises(NotRT130FileError):
+                Reftek130.from_file(test_file)
+
+        with self.subTest('test_file_does_not_exist'):
+            test_file = ''
+            with self.assertRaises(FileNotFoundError):
+                Reftek130.from_file(test_file)
diff --git a/tests/test_model/test_reftek/test_header.py b/tests/test_model/test_reftek/test_header.py
new file mode 100644
index 0000000000000000000000000000000000000000..b73e3d18356e39d2efd68aeeefa5782c71f7d829
--- /dev/null
+++ b/tests/test_model/test_reftek/test_header.py
@@ -0,0 +1,74 @@
+import unittest
+
+from sohstationviewer.model.reftek.reftek_data.header import (
+    parse_rt130_time,
+    get_rt130_packet_header, NotRT130FileError,
+)
+
+
+class TestParseRT130Time(unittest.TestCase):
+    def test_time_bytes_parsed_correctly(self):
+        time_bytes = b'\x36\x01\x15\x13\x51\x35'
+        year = 15
+        result = parse_rt130_time(year, time_bytes)
+        self.assertEqual(result.julday, 360)
+        self.assertEqual(result.day, 26)
+        self.assertEqual(result.month, 12)
+        self.assertEqual(result.hour, 11)
+        self.assertEqual(result.minute, 51)
+        self.assertEqual(result.second, 35)
+        self.assertEqual(result.microsecond, 135000)
+        self.assertEqual(result.ns, 1451130695135000000)
+
+    def test_year_1900s(self):
+        time_bytes = b'\x36\x01\x15\x13\x51\x35'
+        year = 71
+        result = parse_rt130_time(year, time_bytes)
+        self.assertEqual(result.year, 1971)
+
+    def test_year_2000s(self):
+        time_bytes = b'\x36\x01\x15\x13\x51\x35'
+        year = 12
+        result = parse_rt130_time(year, time_bytes)
+        self.assertEqual(result.year, 2012)
+
+    def test_year_threshold(self):
+        with self.subTest('test_year_is_49'):
+            time_bytes = b'\x36\x01\x15\x13\x51\x35'
+            year = 49
+            result = parse_rt130_time(year, time_bytes)
+            self.assertEqual(result.year, 2049)
+        with self.subTest('test_year_is_50'):
+            time_bytes = b'\x36\x01\x15\x13\x51\x35'
+            year = 50
+            result = parse_rt130_time(year, time_bytes)
+            self.assertEqual(result.year, 1950)
+
+
+
+class TestGetRT130PacketHeader(unittest.TestCase):
+    def test_header_extracted_correctly(self):
+        header = b'DT\x12\x15\x98\xe1\x36\x01\x15\x13\x51\x35\x05\x12\x01\x11'
+        packet = header + b' ' * 1008
+        result = get_rt130_packet_header(packet)
+        self.assertEqual(result.packet_type, 'DT')
+        self.assertEqual(result.experiment_number, 12)
+        self.assertEqual(result.unit_id, '98E1')
+        self.assertEqual(result.time.ns, 1451130695135000000)
+        self.assertEqual(result.byte_count, 512)
+        self.assertEqual(result.packet_sequence, 111)
+
+    def test_packet_type_cannot_be_parsed(self):
+        packet_type = b'\x01\x02'
+        header = packet_type + b'\x11' * 14
+        packet = header + b' ' * 1008
+        with self.assertRaises(NotRT130FileError):
+            get_rt130_packet_header(packet)
+
+    def test_packet_type_is_not_valid(self):
+        packet_type = b'AB'
+        header = packet_type + b'\x11' * 14
+        packet = header + b' ' * 1008
+        with self.assertRaises(NotRT130FileError):
+            get_rt130_packet_header(packet)
+
diff --git a/tests/test_model/test_reftek/test_packet_readers.py b/tests/test_model/test_reftek/test_packet_readers.py
new file mode 100644
index 0000000000000000000000000000000000000000..180e03b445f6f71e92ff4f3dca9a22ce5f4f5f5c
--- /dev/null
+++ b/tests/test_model/test_reftek/test_packet_readers.py
@@ -0,0 +1,184 @@
+import unittest
+from unittest.mock import patch
+
+from sohstationviewer.model.mseed_data.record_reader_helper import Unpacker
+from sohstationviewer.model.reftek.reftek_data.packet import \
+    eh_et_payload_end_in_packet
+from sohstationviewer.model.reftek.reftek_data.packet_readers import \
+    (
+    decode_uncompressed, decode_compressed, read_dt_packet, read_eh_et_packet,
+    read_soh_packet,
+)
+from sohstationviewer.model.reftek.reftek_data.packets import SOHExtendedHeader
+
+unpacker = Unpacker('>')
+
+
+class TestDecodeFunctions(unittest.TestCase):
+    def setUp(self) -> None:
+        self.header = b' ' * 24
+
+    def test_decode_uncompressed_format_16(self):
+        data_format = '16'
+        with self.subTest('test_positive_number'):
+            first_data_point_byte = b'\x06\x19'
+            data_filler = b' ' * 998
+            packet = self.header + first_data_point_byte + data_filler
+            actual = decode_uncompressed(packet, data_format, unpacker)
+            expected = 1561
+            self.assertEqual(actual, expected)
+        with self.subTest('test_negative_number'):
+            first_data_point_byte = b'\xf9\xe4'
+            data_filler = b' ' * 998
+            packet = self.header + first_data_point_byte + data_filler
+            actual = decode_uncompressed(packet, data_format, unpacker)
+            expected = -1564
+            self.assertEqual(actual, expected)
+
+    def test_decode_uncompressed_format_32(self):
+        data_format = '32'
+        with self.subTest('test_positive_number'):
+            first_data_point_byte = b'\x03\xc5\x4e\x9a'
+            data_filler = b' ' * 996
+            packet = self.header + first_data_point_byte + data_filler
+            actual = decode_uncompressed(packet, data_format, unpacker)
+            expected = 63262362
+            self.assertEqual(actual, expected)
+        with self.subTest('test_negative_number'):
+            first_data_point_byte = b'\xf6\xac\xba\x00'
+            data_filler = b' ' * 996
+            packet = self.header + first_data_point_byte + data_filler
+            actual = decode_uncompressed(packet, data_format, unpacker)
+            expected = -156452352
+            self.assertEqual(actual, expected)
+
+    def test_decode_uncompressed_format_33(self):
+        data_format = '33'
+        with self.subTest('test_positive_number'):
+            first_data_point_byte = b'\x03\xc5\x4e\x9a'
+            data_filler = b' ' * 996
+            packet = self.header + first_data_point_byte + data_filler
+            actual = decode_uncompressed(packet, data_format, unpacker)
+            expected = 63262362
+            self.assertEqual(actual, expected)
+        with self.subTest('test_negative_number'):
+            first_data_point_byte = b'\xf6\xac\xba\x00'
+            data_filler = b' ' * 996
+            packet = self.header + first_data_point_byte + data_filler
+            actual = decode_uncompressed(packet, data_format, unpacker)
+            expected = -156452352
+            self.assertEqual(actual, expected)
+
+    def test_decode_compressed(self):
+        data_format = 'C0'
+        filler = b' ' * 40
+        first_frame_code = b'\x00\x11\x11\x11'
+        start_data_point_byte = b'0000'
+        header = self.header + filler
+        bytes_before_data = header + first_frame_code + start_data_point_byte
+        with self.subTest('test_positive_number'):
+            end_point_byte = b'\x03\xc5\x4e\x9a'
+            data_filler = b' ' * 952
+            packet = bytes_before_data + end_point_byte + data_filler
+            actual = decode_compressed(packet, data_format, unpacker)
+            expected = 63262362
+            self.assertEqual(actual, expected)
+        with self.subTest('test_negative_number'):
+            end_point_byte = b'\xf6\xac\xba\x00'
+            data_filler = b' ' * 952
+            packet = bytes_before_data + end_point_byte + data_filler
+            actual = decode_compressed(packet, data_format, unpacker)
+            expected = -156452352
+            self.assertEqual(actual, expected)
+
+
+class TestReadDTPacket(unittest.TestCase):
+    def setUp(self) -> None:
+        self.header = b' ' * 16
+        # We only test if the correct method is used to extract the data point,
+        # so the data can be anything we want.
+        self.data = b' ' * 1000
+
+        uncompressed_patcher = patch(
+            'sohstationviewer.model.reftek.reftek_data.packet_readers.'
+            'decode_uncompressed'
+        )
+        compressed_patcher = patch(
+            'sohstationviewer.model.reftek.reftek_data.packet_readers.'
+            'decode_compressed'
+        )
+        self.mock_uncompressed = uncompressed_patcher.start()
+        self.mock_compressed = compressed_patcher.start()
+        self.addCleanup(uncompressed_patcher.stop)
+        self.addCleanup(compressed_patcher.stop)
+
+    def test_extended_header_is_extracted_correctly(self):
+        extended_header_bytes = b'\x01\x11\x01\x02\x05\x00\x00\xc0'
+        packet = self.header + extended_header_bytes + self.data
+        extended_header, _ = read_dt_packet(packet, unpacker)
+        self.assertEqual(extended_header.event_number, 111)
+        self.assertEqual(extended_header.data_stream_number, 1)
+        self.assertEqual(extended_header.channel_number, 2)
+        self.assertEqual(extended_header.number_of_samples, 500)
+        self.assertEqual(extended_header.flags, 0)
+        self.assertEqual(extended_header.data_format, 'C0')
+
+    def test_data_point_extracted_with_correct_method(self):
+        with self.subTest('test_uncompressed_packet'):
+            extended_header_bytes = b'\x01\x11\x01\x02\x05\x00\x00\x16'
+            packet = self.header + extended_header_bytes + self.data
+            read_dt_packet(packet, unpacker)
+            self.assertTrue(self.mock_uncompressed.called)
+            self.assertFalse(self.mock_compressed.called)
+
+        self.mock_uncompressed.reset_mock()
+        self.mock_compressed.reset_mock()
+
+        with self.subTest('test_compressed_packet'):
+            extended_header_bytes = b'\x01\x11\x01\x02\x05\x00\x00\xc0'
+            packet = self.header + extended_header_bytes + self.data
+            read_dt_packet(packet, unpacker)
+            self.assertTrue(self.mock_compressed.called)
+            self.assertFalse(self.mock_uncompressed.called)
+
+
+class TestReadEHETPacket(unittest.TestCase):
+    def setUp(self) -> None:
+        header = b' ' * 16
+        extended_header_bytes = b'\x01\x11\x01\x00\x00\x00\x00\xc0'
+        # We only care about the length of the payload (the content is dealt
+        # with somewhere else), and so it can contain dummy data.
+        payload = b' ' * 1000
+        self.packet = header + extended_header_bytes + payload
+
+    def test_extended_header_is_extracted_correctly(self):
+        extended_header, _ = read_eh_et_packet(self.packet, unpacker)
+        self.assertEqual(extended_header.event_number, 111)
+        self.assertEqual(extended_header.data_stream_number, 1)
+        self.assertEqual(extended_header.channel_number, 0)
+        self.assertEqual(extended_header.number_of_samples, 0)
+        self.assertEqual(extended_header.flags, 0)
+        self.assertEqual(extended_header.data_format, 'C0')
+
+    def test_payload_extracted_correctly(self):
+        _, payload = read_eh_et_packet(self.packet, unpacker)
+        self.assertEqual(len(payload), eh_et_payload_end_in_packet - 24)
+
+
+class TestReadSOHPacket(unittest.TestCase):
+    """
+    Test suite for packet_readers.read_soh_packet. We only test that the
+    function has the correct interface, seeing as the intended purpose of this
+    method is to be compatible with packet_readers.read_dt_packet and
+    packet_readers.read_eh_et_packet interface-wise.
+    """
+    def test_correct_interface(self):
+        packet = b' ' * 1024
+        extended_header, payload = read_soh_packet(packet, unpacker)
+        self.assertIsInstance(extended_header, SOHExtendedHeader)
+        self.assertIsInstance(payload, bytes)
+
+    def test_payload_has_correct_length(self):
+        packet = b' ' * 1024
+        extended_header, payload = read_soh_packet(packet, unpacker)
+        self.assertEqual(len(payload), 1000)
diff --git a/tests/test_model/test_reftek/test_reftek_helper.py b/tests/test_model/test_reftek/test_reftek_helper.py
new file mode 100644
index 0000000000000000000000000000000000000000..ecf16f80e5d43b44e737371b70d58296f2fab525
--- /dev/null
+++ b/tests/test_model/test_reftek/test_reftek_helper.py
@@ -0,0 +1,107 @@
+import os
+import unittest
+from pathlib import Path
+from unittest.mock import patch
+
+from obspy.io.reftek.packet import PACKET_FINAL_DTYPE
+
+from sohstationviewer.model.mseed_data.record_reader_helper import Unpacker
+from sohstationviewer.model.reftek.reftek_data.header import NotRT130FileError
+from sohstationviewer.model.reftek.reftek_data.packet_readers import \
+    (
+    read_eh_et_packet, read_dt_packet, read_soh_packet,
+)
+from sohstationviewer.model.reftek.reftek_data.packets import (
+    SOHPacket,
+    EHETPacket, DTPacket,
+)
+from sohstationviewer.model.reftek.reftek_data.reftek_helper import \
+    (
+    read_rt130_file, convert_packet_to_obspy_format,
+)
+
+unpacker = Unpacker('>')
+
+
+class TestReadRT130File(unittest.TestCase):
+    def setUp(self) -> None:
+        self.TEST_DATA_DIR = Path(os.getcwd()).joinpath('tests/test_data')
+        self.rt130_dir = self.TEST_DATA_DIR.joinpath(
+            'RT130-sample/2017149.92EB/2017150/92EB'
+        )
+
+        eh_et_patcher = patch(
+            'sohstationviewer.model.reftek.reftek_data.reftek_helper.'
+            'read_eh_et_packet',
+            wraps=read_eh_et_packet
+        )
+        self.mock_read_eh_et = eh_et_patcher.start()
+        self.addCleanup(eh_et_patcher.stop)
+
+        dt_patcher = patch(
+            'sohstationviewer.model.reftek.reftek_data.reftek_helper.'
+            'read_dt_packet',
+            wraps=read_dt_packet
+        )
+        self.mock_read_dt = dt_patcher.start()
+        self.addCleanup(dt_patcher.stop)
+
+        soh_patcher = patch(
+            'sohstationviewer.model.reftek.reftek_data.reftek_helper.'
+            'read_soh_packet',
+            wraps=read_soh_packet
+        )
+        self.mock_read_soh = soh_patcher.start()
+        self.addCleanup(soh_patcher.stop)
+
+    def test_rt130_soh_file(self):
+        file = self.rt130_dir.joinpath('0/000000000_00000000')
+        packets = read_rt130_file(file, unpacker)
+        self.assertTrue(
+            all(isinstance(packet, SOHPacket) for packet in packets)
+        )
+        self.assertTrue(self.mock_read_soh.called)
+        self.assertFalse(self.mock_read_dt.called)
+        self.assertFalse(self.mock_read_eh_et.called)
+
+    def test_rt130_raw_data_file(self):
+        file = self.rt130_dir.joinpath('1/000000015_0036EE80')
+        packets = read_rt130_file(file, unpacker)
+        self.assertTrue(all(
+            isinstance(packet, EHETPacket) or isinstance(packet, DTPacket)
+            for packet in packets)
+        )
+        self.assertFalse(self.mock_read_soh.called)
+        self.assertTrue(self.mock_read_dt.called)
+        self.assertTrue(self.mock_read_eh_et.called)
+
+    def test_non_rt130_file(self):
+        with self.subTest('test_file_exist'):
+            file = self.TEST_DATA_DIR.joinpath(
+                'Q330-sample/day_vols_AX08/AX08.XA..HHE.2021.186'
+            )
+            with self.assertRaises(NotRT130FileError):
+                read_rt130_file(file, unpacker)
+
+        with self.subTest('test_file_does_not_exist'):
+            file = ''
+            with self.assertRaises(FileNotFoundError):
+                read_rt130_file(file, unpacker)
+
+
+class TestConvertPacketToObspyFormat(unittest.TestCase):
+    def setUp(self) -> None:
+        TEST_DATA_DIR = Path(os.getcwd()).joinpath('tests/test_data')
+        rt130_dir = TEST_DATA_DIR.joinpath(
+            'RT130-sample/2017149.92EB/2017150/92EB'
+        )
+        file = rt130_dir.joinpath('1/000000015_0036EE80')
+        self.packet = read_rt130_file(file, unpacker)[0]
+
+    def test_all_needed_fields_are_available(self):
+        converted_packet = convert_packet_to_obspy_format(
+            self.packet, unpacker
+        )
+
+        self.assertEqual(len(converted_packet), len(PACKET_FINAL_DTYPE))
+