modusa 0.2.23__py3-none-any.whl → 0.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. modusa/.DS_Store +0 -0
  2. modusa/__init__.py +8 -1
  3. modusa/devtools/{generate_doc_source.py → generate_docs_source.py} +5 -5
  4. modusa/devtools/generate_template.py +5 -5
  5. modusa/devtools/main.py +3 -3
  6. modusa/devtools/templates/generator.py +1 -1
  7. modusa/devtools/templates/io.py +1 -1
  8. modusa/devtools/templates/{signal.py → model.py} +18 -11
  9. modusa/devtools/templates/plugin.py +1 -1
  10. modusa/generators/__init__.py +11 -1
  11. modusa/generators/audio.py +188 -0
  12. modusa/generators/audio_waveforms.py +1 -1
  13. modusa/generators/base.py +1 -1
  14. modusa/generators/ftds.py +298 -0
  15. modusa/generators/s1d.py +270 -0
  16. modusa/generators/s2d.py +300 -0
  17. modusa/generators/s_ax.py +102 -0
  18. modusa/generators/t_ax.py +64 -0
  19. modusa/generators/tds.py +267 -0
  20. modusa/models/__init__.py +14 -0
  21. modusa/models/__pycache__/signal1D.cpython-312.pyc.4443461152 +0 -0
  22. modusa/models/audio.py +90 -0
  23. modusa/models/base.py +70 -0
  24. modusa/models/data.py +457 -0
  25. modusa/models/ftds.py +584 -0
  26. modusa/models/s1d.py +578 -0
  27. modusa/models/s2d.py +619 -0
  28. modusa/models/s_ax.py +448 -0
  29. modusa/models/t_ax.py +335 -0
  30. modusa/models/tds.py +465 -0
  31. modusa/plugins/__init__.py +3 -1
  32. modusa/tmp.py +98 -0
  33. modusa/tools/__init__.py +5 -0
  34. modusa/tools/audio_converter.py +56 -67
  35. modusa/tools/audio_loader.py +90 -0
  36. modusa/tools/audio_player.py +42 -67
  37. modusa/tools/math_ops.py +104 -1
  38. modusa/tools/plotter.py +305 -497
  39. modusa/tools/youtube_downloader.py +31 -98
  40. modusa/utils/excp.py +6 -0
  41. modusa/utils/np_func_cat.py +44 -0
  42. modusa/utils/plot.py +142 -0
  43. {modusa-0.2.23.dist-info → modusa-0.3.1.dist-info}/METADATA +24 -19
  44. modusa-0.3.1.dist-info/RECORD +60 -0
  45. modusa/devtools/docs/source/generators/audio_waveforms.rst +0 -8
  46. modusa/devtools/docs/source/generators/base.rst +0 -8
  47. modusa/devtools/docs/source/generators/index.rst +0 -8
  48. modusa/devtools/docs/source/io/audio_loader.rst +0 -8
  49. modusa/devtools/docs/source/io/base.rst +0 -8
  50. modusa/devtools/docs/source/io/index.rst +0 -8
  51. modusa/devtools/docs/source/plugins/base.rst +0 -8
  52. modusa/devtools/docs/source/plugins/index.rst +0 -7
  53. modusa/devtools/docs/source/signals/audio_signal.rst +0 -8
  54. modusa/devtools/docs/source/signals/base.rst +0 -8
  55. modusa/devtools/docs/source/signals/frequency_domain_signal.rst +0 -8
  56. modusa/devtools/docs/source/signals/index.rst +0 -11
  57. modusa/devtools/docs/source/signals/spectrogram.rst +0 -8
  58. modusa/devtools/docs/source/signals/time_domain_signal.rst +0 -8
  59. modusa/devtools/docs/source/tools/audio_converter.rst +0 -8
  60. modusa/devtools/docs/source/tools/audio_player.rst +0 -8
  61. modusa/devtools/docs/source/tools/base.rst +0 -8
  62. modusa/devtools/docs/source/tools/fourier_tranform.rst +0 -8
  63. modusa/devtools/docs/source/tools/index.rst +0 -13
  64. modusa/devtools/docs/source/tools/math_ops.rst +0 -8
  65. modusa/devtools/docs/source/tools/plotter.rst +0 -8
  66. modusa/devtools/docs/source/tools/youtube_downloader.rst +0 -8
  67. modusa/io/__init__.py +0 -5
  68. modusa/io/audio_loader.py +0 -184
  69. modusa/io/base.py +0 -43
  70. modusa/signals/__init__.py +0 -3
  71. modusa/signals/audio_signal.py +0 -540
  72. modusa/signals/base.py +0 -27
  73. modusa/signals/frequency_domain_signal.py +0 -376
  74. modusa/signals/spectrogram.py +0 -564
  75. modusa/signals/time_domain_signal.py +0 -412
  76. modusa/tools/fourier_tranform.py +0 -24
  77. modusa-0.2.23.dist-info/RECORD +0 -70
  78. {modusa-0.2.23.dist-info → modusa-0.3.1.dist-info}/WHEEL +0 -0
  79. {modusa-0.2.23.dist-info → modusa-0.3.1.dist-info}/entry_points.txt +0 -0
  80. {modusa-0.2.23.dist-info → modusa-0.3.1.dist-info}/licenses/LICENSE.md +0 -0
@@ -1,540 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
-
4
- from modusa import excp
5
- from modusa.decorators import immutable_property, validate_args_type
6
- from modusa.signals.base import ModusaSignal
7
- from modusa.tools.math_ops import MathOps
8
- from typing import Self, Any
9
- import numpy as np
10
- import matplotlib.pyplot as plt
11
- from pathlib import Path
12
-
13
- class AudioSignal(ModusaSignal):
14
- """
15
- Represents a 1D audio signal within modusa framework.
16
-
17
- Note
18
- ----
19
- - It is highly recommended to use :class:`~modusa.io.AudioLoader` to instantiate an object of this class.
20
- - This class assumes audio is mono (1D numpy array).
21
-
22
- Parameters
23
- ----------
24
- y : np.ndarray
25
- 1D numpy array representing the audio signal.
26
- sr : int | None
27
- Sampling rate in Hz. Required if `t` is not provided.
28
- t0 : float, optional
29
- Starting time in seconds. Defaults to 0.0.
30
- title : str | None, optional
31
- Optional title for the signal. Defaults to `"Audio Signal"`.
32
- """
33
-
34
- #--------Meta Information----------
35
- _name = "Audio Signal"
36
- _description = ""
37
- _author_name = "Ankit Anand"
38
- _author_email = "ankit0.anand0@gmail.com"
39
- _created_at = "2025-07-04"
40
- #----------------------------------
41
-
42
- @validate_args_type()
43
- def __init__(self, y: np.ndarray, sr: int, t0: float = 0.0, title: str | None = None):
44
- """
45
- Loads the audio signal.
46
- """
47
- if y.ndim != 1: # Mono signal only
48
- raise excp.InputValueError(f"`y` must have 1 dimension, not {y.ndim}.")
49
-
50
- self._y = y
51
- self._sr = sr
52
- self._t0 = t0
53
- self.title = title or self._name
54
-
55
- #----------------------
56
- # Properties
57
- #----------------------
58
- @immutable_property("Create a new object instead.")
59
- def y(self) -> np.ndarray:
60
- """Returns audio data."""
61
- return self._y
62
-
63
- @immutable_property("Create a new object instead.")
64
- def sr(self) -> np.ndarray:
65
- """Returns sampling rate of the audio."""
66
- return self._sr
67
-
68
- @immutable_property("Create a new object instead.")
69
- def t0(self) -> np.ndarray:
70
- """Returns start timestamp of the audio."""
71
- return self._t0
72
-
73
- #----------------------
74
- # Derived Properties
75
- #----------------------
76
- @immutable_property("Create a new object instead.")
77
- def t(self) -> np.ndarray:
78
- """Timestamp array of the audio."""
79
- return self.t0 + np.arange(len(self.y)) / self.sr
80
-
81
- @immutable_property("Mutation not allowed.")
82
- def Ts(self) -> float:
83
- """Sampling Period of the audio."""
84
- return 1. / self.sr
85
-
86
- @immutable_property("Mutation not allowed.")
87
- def duration(self) -> float:
88
- """Duration of the audio."""
89
- return len(self.y) / self.sr
90
-
91
- @immutable_property("Mutation not allowed.")
92
- def shape(self) -> tuple:
93
- """Shape of the audio signal."""
94
- return self.y.shape
95
-
96
- @immutable_property("Mutation not allowed.")
97
- def ndim(self) -> int:
98
- """Dimension of the audio."""
99
- return self.y.ndim
100
-
101
- @immutable_property("Mutation not allowed.")
102
- def __len__(self) -> int:
103
- """Dimension of the audio."""
104
- return len(self.y)
105
-
106
- #----------------------
107
- # Methods
108
- #----------------------
109
-
110
- def print_info(self) -> None:
111
- """Prints info about the audio."""
112
- print("-" * 50)
113
- print(f"{'Title':<20}: {self.title}")
114
- print(f"{'Type':<20}: {self._name}")
115
- print(f"{'Duration':<20}: {self.duration:.2f} sec")
116
- print(f"{'Sampling Rate':<20}: {self.sr} Hz")
117
- print(f"{'Sampling Period':<20}: {(self.Ts*1000) :.4f} ms")
118
- print("-" * 50)
119
-
120
- def __getitem__(self, key):
121
- sliced_y = self.y[key]
122
-
123
- # If key is a single integer, return just the sample value
124
- if isinstance(key, int):
125
- return sliced_y
126
-
127
- # Otherwise, slicing: use self.t[key][0] as new t0
128
- new_t0 = self.t[key][0]
129
-
130
- return self.__class__(
131
- y=sliced_y,
132
- sr=self.sr,
133
- t0=new_t0,
134
- title=f"{self.title}[{key}]"
135
- )
136
-
137
- @validate_args_type()
138
- def crop(self, t_min: int | float | None = None, t_max: int | float | None = None) -> "AudioSignal":
139
- """
140
- Crop the audio signal to a time range [t_min, t_max].
141
-
142
- .. code-block:: python
143
-
144
- from modusa.generators import AudioSignalGenerator
145
- audio_example = AudioSignalGenerator.generate_example()
146
- cropped_audio = audio_example.crop(1.5, 2)
147
-
148
- Parameters
149
- ----------
150
- t_min : float or None
151
- Inclusive lower time bound. If None, no lower bound.
152
- t_max : float or None
153
- Exclusive upper time bound. If None, no upper bound.
154
-
155
- Returns
156
- -------
157
- AudioSignal
158
- Cropped audio signal.
159
- """
160
- y = self.y
161
- t = self.t
162
-
163
- mask = np.ones_like(t, dtype=bool)
164
- if t_min is not None:
165
- mask &= (t >= t_min)
166
- if t_max is not None:
167
- mask &= (t < t_max)
168
-
169
- cropped_y = y[mask]
170
- new_t0 = t[mask][0] if np.any(mask) else self.t0 # fallback to original t0 if mask is empty
171
-
172
- return self.__class__(y=cropped_y, sr=self.sr, t0=new_t0, title=self.title)
173
-
174
-
175
- @validate_args_type()
176
- def plot(
177
- self,
178
- ax: plt.Axes | None = None,
179
- fmt: str = "k-",
180
- title: str | None = None,
181
- label: str | None = None,
182
- ylabel: str | None = "Amplitude",
183
- xlabel: str | None = "Time (sec)",
184
- ylim: tuple[float, float] | None = None,
185
- xlim: tuple[float, float] | None = None,
186
- highlight: list[tuple[float, float]] | None = None,
187
- vlines: list[float] | None = None,
188
- hlines: list[float] | None = None,
189
- show_grid: bool = False,
190
- stem: bool = False,
191
- legend_loc: str | None = None,
192
- ) -> plt.Figure | None:
193
- """
194
- Plot the audio waveform using matplotlib.
195
-
196
- .. code-block:: python
197
-
198
- from modusa.generators import AudioSignalGenerator
199
- audio_example = AudioSignalGenerator.generate_example()
200
- audio_example.plot(color="orange", title="Example Audio")
201
-
202
- Parameters
203
- ----------
204
- ax : matplotlib.axes.Axes | None
205
- Pre-existing axes to plot into. If None, a new figure and axes are created.
206
- fmt : str | None
207
- Format of the plot as per matplotlib standards (Eg. "k-" or "blue--o)
208
- title : str | None
209
- Plot title. Defaults to the signal’s title.
210
- label: str | None
211
- Label for the plot, shown as legend.
212
- ylabel : str | None
213
- Label for the y-axis. Defaults to `"Amplitude"`.
214
- xlabel : str | None
215
- Label for the x-axis. Defaults to `"Time (sec)"`.
216
- ylim : tuple[float, float] | None
217
- Limits for the y-axis.
218
- xlim : tuple[float, float] | None
219
- highlight : list[tuple[float, float]] | None
220
- List of time intervals to highlight on the plot, each as (start, end).
221
- vlines: list[float]
222
- List of x values to draw vertical lines. (Eg. [10, 13.5])
223
- hlines: list[float]
224
- List of y values to draw horizontal lines. (Eg. [10, 13.5])
225
- show_grid: bool
226
- If true, shows grid.
227
- stem : bool
228
- If True, use a stem plot instead of a continuous line. Autorejects if signal is too large.
229
- legend_loc : str | None
230
- If provided, adds a legend at the specified location (e.g., "upper right" or "best").
231
- Limits for the x-axis.
232
-
233
- Returns
234
- -------
235
- matplotlib.figure.Figure | None
236
- The figure object containing the plot or None in case an axis is provided.
237
- """
238
-
239
- from modusa.tools.plotter import Plotter
240
-
241
- if title is None:
242
- title = self.title
243
-
244
- fig: plt.Figure | None = Plotter.plot_signal(
245
- y=self.y,
246
- x=self.t,
247
- ax=ax,
248
- fmt=fmt,
249
- title=title,
250
- label=label,
251
- ylabel=ylabel,
252
- xlabel=xlabel,
253
- ylim=ylim,
254
- xlim=xlim,
255
- highlight=highlight,
256
- vlines=vlines,
257
- hlines=hlines,
258
- show_grid=show_grid,
259
- stem=stem,
260
- legend_loc=legend_loc,
261
- )
262
-
263
- return fig
264
-
265
- def play(self, regions: list[tuple[float, float], ...] | None = None, title: str | None = None):
266
- """
267
- Play the audio signal inside a Jupyter Notebook.
268
-
269
- .. code-block:: python
270
-
271
- from modusa.generators import AudioSignalGenerator
272
- audio = AudioSignalGenerator.generate_example()
273
- audio.play(regions=[(0.0, 1.0), (2.0, 3.0)])
274
-
275
- Parameters
276
- ----------
277
- regions : list of tuple of float, optional
278
- List of (start_time, end_time) pairs in seconds specifying the regions to play.
279
- If None, the entire signal is played.
280
- title : str or None, optional
281
- Optional title for the player interface. Defaults to the signal’s internal title.
282
-
283
- Returns
284
- -------
285
- IPython.display.Audio
286
- An interactive audio player widget for Jupyter environments.
287
-
288
- See Also
289
- --------
290
- :class:`~modusa.tools.audio_player.AudioPlayer`
291
- """
292
-
293
- from modusa.tools.audio_player import AudioPlayer
294
- title = title or self.title
295
- audio_player = AudioPlayer.play(y=self.y, sr=self.sr, regions=regions, title=title)
296
-
297
- return audio_player
298
-
299
- def to_spectrogram(
300
- self,
301
- n_fft: int = 2048,
302
- hop_length: int = 512,
303
- win_length: int | None = None,
304
- window: str = "hann"
305
- ) -> "Spectrogram":
306
- """
307
- Compute the Short-Time Fourier Transform (STFT) and return a Spectrogram object.
308
-
309
- Parameters
310
- ----------
311
- n_fft : int
312
- FFT size.
313
- win_length : int or None
314
- Window length. Defaults to `n_fft` if None.
315
- hop_length : int
316
- Hop length between frames.
317
- window : str
318
- Type of window function to use (e.g., 'hann', 'hamming').
319
-
320
- Returns
321
- -------
322
- Spectrogram
323
- Spectrogram object containing S (complex STFT), t (time bins), and f (frequency bins).
324
- """
325
- import warnings
326
- warnings.filterwarnings("ignore", category=UserWarning, module="librosa.core.intervals")
327
-
328
- from modusa.signals.spectrogram import Spectrogram
329
- import librosa
330
-
331
- S = librosa.stft(self.y, n_fft=n_fft, win_length=win_length, hop_length=hop_length, window=window)
332
- f = librosa.fft_frequencies(sr=self.sr, n_fft=n_fft)
333
- t = librosa.frames_to_time(np.arange(S.shape[1]), sr=self.sr, hop_length=hop_length)
334
- frame_rate = self.sr / hop_length
335
- spec = Spectrogram(S=S, f=f, frame_rate=frame_rate, t0=self.t0)
336
- if self.title != self._name: # Means title of the audio was reset so we pass that info to spec
337
- spec.title = self.title
338
-
339
- return spec
340
-
341
-
342
- #----------------------------
343
- # Math ops
344
- #----------------------------
345
-
346
- def __array__(self, dtype=None):
347
- return np.asarray(self.y, dtype=dtype)
348
-
349
- def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
350
- if method == "__call__":
351
- input_arrays = [x.y if isinstance(x, self.__class__) else x for x in inputs]
352
- result = ufunc(*input_arrays, **kwargs)
353
- return self.__class__(y=result, sr=self.sr, title=f"{self.title}")
354
- return NotImplemented
355
-
356
- def __add__(self, other):
357
- other_data = other.y if isinstance(other, self.__class__) else other
358
- result = MathOps.add(self.y, other_data)
359
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
360
-
361
- def __radd__(self, other):
362
- other_data = other.y if isinstance(other, self.__class__) else other
363
- result = MathOps.add(other_data, self.y)
364
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
365
-
366
- def __sub__(self, other):
367
- other_data = other.y if isinstance(other, self.__class__) else other
368
- result = MathOps.subtract(self.y, other_data)
369
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
370
-
371
- def __rsub__(self, other):
372
- other_data = other.y if isinstance(other, self.__class__) else other
373
- result = MathOps.subtract(other_data, self.y)
374
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
375
-
376
- def __mul__(self, other):
377
- other_data = other.y if isinstance(other, self.__class__) else other
378
- result = MathOps.multiply(self.y, other_data)
379
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
380
-
381
- def __rmul__(self, other):
382
- other_data = other.y if isinstance(other, self.__class__) else other
383
- result = MathOps.multiply(other_data, self.y)
384
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
385
-
386
- def __truediv__(self, other):
387
- other_data = other.y if isinstance(other, self.__class__) else other
388
- result = MathOps.divide(self.y, other_data)
389
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
390
-
391
- def __rtruediv__(self, other):
392
- other_data = other.y if isinstance(other, self.__class__) else other
393
- result = MathOps.divide(other_data, self.y)
394
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
395
-
396
- def __floordiv__(self, other):
397
- other_data = other.y if isinstance(other, self.__class__) else other
398
- result = MathOps.floor_divide(self.y, other_data)
399
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
400
-
401
- def __rfloordiv__(self, other):
402
- other_data = other.y if isinstance(other, self.__class__) else other
403
- result = MathOps.floor_divide(other_data, self.y)
404
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
405
-
406
- def __pow__(self, other):
407
- other_data = other.y if isinstance(other, self.__class__) else other
408
- result = MathOps.power(self.y, other_data)
409
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
410
-
411
- def __rpow__(self, other):
412
- other_data = other.y if isinstance(other, self.__class__) else other
413
- result = MathOps.power(other_data, self.y)
414
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
415
-
416
- def __abs__(self):
417
- other_data = other.y if isinstance(other, self.__class__) else other
418
- result = MathOps.abs(self.y)
419
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
420
-
421
- def __or__(self, other):
422
- if not isinstance(other, self.__class__):
423
- raise excp.InputTypeError(f"Can only concatenate with another {self.__class__.__name__}")
424
-
425
- if self.sr != other.sr:
426
- raise excp.InputValueError(f"Cannot concatenate: Sampling rates differ ({self.sr} vs {other.sr})")
427
-
428
- # Concatenate raw audio data
429
- y_cat = np.concatenate([self.y, other.y])
430
-
431
- # Preserve t0 of the first signal
432
- new_title = f"{self.title} | {other.title}"
433
- return self.__class__(y=y_cat, sr=self.sr, t0=self.t0, title=new_title)
434
-
435
-
436
- #--------------------------
437
- # Other signal ops
438
- #--------------------------
439
- def abs(self) -> Self:
440
- """Compute the element-wise abs of the signal data."""
441
- result = MathOps.abs(self.y)
442
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
443
-
444
- def sin(self) -> Self:
445
- """Compute the element-wise sine of the signal data."""
446
- result = MathOps.sin(self.y)
447
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
448
-
449
- def cos(self) -> Self:
450
- """Compute the element-wise cosine of the signal data."""
451
- result = MathOps.cos(self.y)
452
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
453
-
454
- def exp(self) -> Self:
455
- """Compute the element-wise exponential of the signal data."""
456
- result = MathOps.exp(self.y)
457
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
458
-
459
- def tanh(self) -> Self:
460
- """Compute the element-wise hyperbolic tangent of the signal data."""
461
- result = MathOps.tanh(self.y)
462
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
463
-
464
- def log(self) -> Self:
465
- """Compute the element-wise natural logarithm of the signal data."""
466
- result = MathOps.log(self.y)
467
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
468
-
469
- def log1p(self) -> Self:
470
- """Compute the element-wise natural logarithm of (1 + signal data)."""
471
- result = MathOps.log1p(self.y)
472
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
473
-
474
- def log10(self) -> Self:
475
- """Compute the element-wise base-10 logarithm of the signal data."""
476
- result = MathOps.log10(self.y)
477
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
478
-
479
- def log2(self) -> Self:
480
- """Compute the element-wise base-2 logarithm of the signal data."""
481
- result = MathOps.log2(self.y)
482
- return self.__class__(y=result, sr=self.sr, t0=self.t0, title=self.title)
483
-
484
-
485
- #--------------------------
486
- # Aggregation signal ops
487
- #--------------------------
488
- def mean(self) -> "np.generic":
489
- """Compute the mean of the signal data."""
490
- return MathOps.mean(self.y)
491
-
492
- def std(self) -> "np.generic":
493
- """Compute the standard deviation of the signal data."""
494
- return MathOps.std(self.y)
495
-
496
- def min(self) -> "np.generic":
497
- """Compute the minimum value in the signal data."""
498
- return MathOps.min(self.y)
499
-
500
- def max(self) -> "np.generic":
501
- """Compute the maximum value in the signal data."""
502
- return MathOps.max(self.y)
503
-
504
- def sum(self) -> "np.generic":
505
- """Compute the sum of the signal data."""
506
- return MathOps.sum(self.y)
507
-
508
- #-----------------------------------
509
- # Repr
510
- #-----------------------------------
511
-
512
- def __str__(self):
513
- cls = self.__class__.__name__
514
- data = self.y
515
-
516
- arr_str = np.array2string(
517
- data,
518
- separator=", ",
519
- threshold=50, # limit number of elements shown
520
- edgeitems=3, # show first/last 3 rows and columns
521
- max_line_width=120, # avoid wrapping
522
- formatter={'float_kind': lambda x: f"{x:.4g}"}
523
- )
524
-
525
- return f"Signal({arr_str}, shape={data.shape}, type={cls})"
526
-
527
- def __repr__(self):
528
- cls = self.__class__.__name__
529
- data = self.y
530
-
531
- arr_str = np.array2string(
532
- data,
533
- separator=", ",
534
- threshold=50, # limit number of elements shown
535
- edgeitems=3, # show first/last 3 rows and columns
536
- max_line_width=120, # avoid wrapping
537
- formatter={'float_kind': lambda x: f"{x:.4g}"}
538
- )
539
-
540
- return f"Signal({arr_str}, shape={data.shape}, type={cls})"
modusa/signals/base.py DELETED
@@ -1,27 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
-
4
- from abc import ABC, abstractmethod
5
-
6
- class ModusaSignal(ABC):
7
- """
8
- Base class for any signal in the modusa framework.
9
-
10
- Note
11
- ----
12
- - Intended to be subclassed.
13
- """
14
-
15
- #--------Meta Information----------
16
- _name = "Modusa Signal"
17
- _description = "Base class for any signal types in the Modusa framework."
18
- _author_name = "Ankit Anand"
19
- _author_email = "ankit0.anand0@gmail.com"
20
- _created_at = "2025-06-23"
21
- #----------------------------------
22
-
23
- def __init__(self):
24
- self._plugin_chain = []
25
-
26
-
27
-