File: base.py

package info (click to toggle)
python-sigima 1.0.3-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 24,956 kB
  • sloc: python: 33,326; makefile: 3
file content (129 lines) | stat: -rw-r--r-- 3,797 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.

"""
Base I/O registry
"""

# pylint: disable=invalid-name  # Allows short reference names like x, y, ...

from __future__ import annotations

import abc
import os.path as osp
from typing import Sequence

import numpy as np

from sigima.config import _
from sigima.io.base import BaseIORegistry, FormatBase
from sigima.objects.signal import SignalObj, create_signal
from sigima.worker import CallbackWorkerProtocol


class SignalIORegistry(BaseIORegistry):
    """Metaclass for registering signal I/O handler classes"""

    REGISTRY_INFO: str = _("Signal I/O formats")

    _io_format_instances: list[SignalFormatBase] = []


class SignalFormatBaseMeta(SignalIORegistry, abc.ABCMeta):
    """Mixed metaclass to avoid conflicts"""


class SignalFormatBase(abc.ABC, FormatBase, metaclass=SignalFormatBaseMeta):
    """Class representing a signal file type"""

    HEADER_KEY = "header"

    @staticmethod
    def create_object(filename: str, index: int | None = None) -> SignalObj:
        """Create empty object

        Args:
            filename: File name
            index: Index of object in file

        Returns:
            Signal object
        """
        name = osp.basename(filename)
        if index is not None:
            name += f" {index:02d}"
        return create_signal(name, metadata={"source": filename})

    def create_signal(
        self, xydata: np.ndarray, filename: str, index: int | None = None
    ) -> SignalObj:
        """Create signal object from xydata and filename.

        Args:
            xydata: XY data
            filename: File name
            index: Index of object in file

        Returns:
            Signal object
        """
        obj = self.create_object(filename, index=index)
        obj.set_xydata(xydata[:, 0], xydata[:, index or 1])
        return obj

    def create_signals_from(self, xydata: np.ndarray, filename: str) -> list[SignalObj]:
        """Create signal objects from xydata and filename

        Args:
            xydata: XY data
            filename: File name

        Returns:
            List of signal objects
        """
        assert isinstance(xydata, np.ndarray), "Data type not supported"
        assert len(xydata.shape) in (1, 2), "Data not supported"
        if len(xydata.shape) == 1:
            # 1D data
            obj = self.create_object(filename)
            obj.set_xydata(np.arange(xydata.size), xydata)
            return [obj]
        # 2D data: x, y1, y2, ...
        # Eventually transpose data:
        if xydata.shape[1] > xydata.shape[0]:
            xydata = xydata.T
        # If only data contains one x and y columns, return single object without index
        # in title
        if xydata.shape[1] == 2:
            return [self.create_signal(xydata, filename)]

        objs = []
        # Create objects for each y column
        for i in range(1, xydata.shape[1]):
            objs.append(self.create_signal(xydata, filename, i))
        return objs

    def read(
        self, filename: str, worker: CallbackWorkerProtocol | None = None
    ) -> Sequence[SignalObj]:
        """Read list of signal objects from file

        Args:
            filename: File name
            worker: Callback worker object

        Returns:
            List of signal objects
        """
        xydata = self.read_xydata(filename)
        return self.create_signals_from(xydata, filename)

    def read_xydata(self, filename: str) -> np.ndarray:
        """Read data and metadata from file, write metadata to object, return xydata

        Args:
            filename: File name

        Returns:
            XY data
        """
        raise NotImplementedError(f"Reading from {self.info.name} is not supported")