Repository: pyfar/pyfar
Issue: Calculations for Signal containing NaN elements return only NaN.

If a Signal object including NaN values is used for a calculation, every value will be returned with NaN. Example:
`calc = pf.Signal([1, 2, np.nan], 44100) / 2`
I would expect that `calc.time == [0.5, 1, nan]` but the actual result is `calc.time == [nan, nan, nan]`.


Changed files:
tests/test_audio_signal.py, pyfar/classes/audio.py

diff --git a/pyfar/classes/audio.py b/pyfar/classes/audio.py
index ca5bb090..e6a5d3bc 100644
--- a/pyfar/classes/audio.py
+++ b/pyfar/classes/audio.py
@@ -601,8 +601,9 @@ class Signal(FrequencyData, TimeData):
             raise ValueError(("Invalid FFT normalization. Has to be "
                               f"{', '.join(self._VALID_FFT_NORMS)}, but "
                               f"found '{fft_norm}'"))
-        # time / normalized frequency data (depending on domain)
+        # check input
         data = np.atleast_2d(data)
+        self._check_input(data)
 
         # initialize domain specific parameters
         if domain == 'time':
@@ -628,6 +629,14 @@ class Signal(FrequencyData, TimeData):
         else:
             raise ValueError("Invalid domain. Has to be 'time' or 'freq'.")
 
+    @staticmethod
+    def _check_input(data: np.ndarray):
+        """Check if input data is numeric."""
+        if data.dtype.kind not in ["u", "i", "f", "c"] or \\
+                np.any(np.isnan(data)) or np.any(np.isinf(data)):
+            raise ValueError(("The input data contains at least one non-"
+                              "numerical value (NaN, inf, string, etc.)"))
+
     @TimeData.time.getter
     def time(self):
         """Return the data in the time domain."""
@@ -636,6 +645,16 @@ class Signal(FrequencyData, TimeData):
 
         return super().time
 
+    @time.setter
+    def time(self, data):
+
+        # check data type
+        data = np.atleast_2d(np.asarray(data))
+        self._check_input(data)
+
+        # call setter from parent class
+        TimeData.time.fset(self, data)
+
     @FrequencyData.freq.getter
     def freq(self):
         """Return the normalized frequency domain data.
@@ -674,10 +693,12 @@ class Signal(FrequencyData, TimeData):
         """Set the frequency domain data without normalization."""
         self._freq(value, raw=True)
 
-    def _freq(self, value, raw):
+    def _freq(self, data, raw):
         """Set the frequency domain data."""
         # check data type
-        data = np.atleast_2d(np.asarray(value))
+        data = np.atleast_2d(np.asarray(data))
+        self._check_input(data)
+
         if data.dtype.kind not in ["i", "f", "c"]:
             raise ValueError((f"frequency data is {data.dtype} must be int, "
                               "float, or complex"))
diff --git a/tests/test_audio_signal.py b/tests/test_audio_signal.py
index 305243e6..9a47a1b8 100644
--- a/tests/test_audio_signal.py
+++ b/tests/test_audio_signal.py
@@ -38,6 +38,16 @@ def test_signal_init_assertions():
     with pytest.raises(ValueError, match="Invalid domain"):
         Signal(1, 44100, domain="space")
 
+    # invalid data: object array
+    with pytest.raises(ValueError, match="The input data contains at least"):
+        Signal([1, 2, "3"], 44100)
+    # invalid data: NaN
+    with pytest.raises(ValueError, match="The input data contains at least"):
+        Signal([1, 2, np.nan], 44100)
+    # invalid data: infinity
+    with pytest.raises(ValueError, match="The input data contains at least"):
+        Signal([1, 2, np.inf], 44100)
+
 
 def test_signal_init_time_dtype():
     """
@@ -75,10 +85,6 @@ def test_data_frequency_init_dtype():
     signal = Signal([1+1j, 2+2j, 3+3j], 44100, 4, "freq")
     assert signal.freq.dtype.kind == "c"
 
-    # object array
-    with pytest.raises(ValueError, match="frequency data is"):
-        Signal(["1", "2", "3"], 44100, 4, "freq")
-
 
 def test_signal_comment():
     signal = Signal([1, 2, 3], 44100, comment='Bla')
@@ -525,6 +531,16 @@ def test_setter_freq_raw_dtype():
     signal.freq_raw = [1+1j, 2+2j, 3+3j]
     assert signal.freq_raw.dtype.kind == "c"
 
-    # object array
-    with pytest.raises(ValueError, match="frequency data is"):
-        signal.freq_raw = ["1", "2", "3"]
+
+def test_setter_assertions():
+    """
+    Test assertions for the freq setter. Other cases are already checked in
+    ``test_signal_init_assertions``."""
+
+    signal = Signal([1, 2, 3], 44100)
+
+    with pytest.raises(ValueError, match="The input data contains at least"):
+        signal.freq = [1, 2, "3"]
+
+    with pytest.raises(ValueError, match="The input data contains at least"):
+        signal.freq_raw = [1, 2, "3"]