faketelemetry 0.1.0__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Adam Billekvist
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.4
2
+ Name: faketelemetry
3
+ Version: 0.1.0
4
+ Summary: Real-time Fake Telemetry Stream Generator
5
+ Home-page: https://github.com/adkvi/faketelemetry
6
+ Author: Adam Billekvist
7
+ Author-email: adam.billeqvist@example.com
8
+ Project-URL: Documentation, https://github.com/adkvi/faketelemetry#readme
9
+ Project-URL: Source, https://github.com/adkvi/faketelemetry
10
+ Project-URL: Tracker, https://github.com/adkvi/faketelemetry/issues
11
+ Keywords: telemetry,signal,generator,iot,testing,simulation,waveform,python
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
18
+ Classifier: Topic :: Software Development :: Testing
19
+ Requires-Python: >=3.7
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Dynamic: author
23
+ Dynamic: author-email
24
+ Dynamic: classifier
25
+ Dynamic: description
26
+ Dynamic: description-content-type
27
+ Dynamic: home-page
28
+ Dynamic: keywords
29
+ Dynamic: license-file
30
+ Dynamic: project-url
31
+ Dynamic: requires-python
32
+ Dynamic: summary
33
+
34
+ [![PyPI version](https://img.shields.io/pypi/v/faketelemetry.svg)](https://pypi.org/project/faketelemetry/)
35
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
36
+ [![CI](https://github.com/adkvi/faketelemetry/actions/workflows/python-package.yml/badge.svg)](https://github.com/adkvi/faketelemetry/actions)
37
+
38
+ # FakeTelemetry
39
+
40
+ **A minimal Python package to generate real-time, timestamped fake telemetry streams for testing, simulation, and development.**
41
+
42
+ ## Features
43
+ - Sine, cosine, square, sawtooth, triangle, pulse, and random noise waveforms
44
+ - Custom user-defined waveform support
45
+ - Adjustable amplitude, frequency, offset, and sample rate
46
+ - Optional Gaussian noise injection
47
+ - Real-time streaming of (datetime, value) tuples
48
+ - Multi-channel (parallel) telemetry generation
49
+ - Well-tested and extensible
50
+
51
+ ## Example Usage
52
+
53
+ ```python
54
+ from faketelemetry import TelemetryGenerator, WaveformType, NoiseInjector, MultiChannelTelemetryGenerator
55
+
56
+ # Single channel with noise (sine wave)
57
+ noise = NoiseInjector(noise_level=0.2)
58
+ gen = TelemetryGenerator(
59
+ waveform=WaveformType.SINE,
60
+ frequency=1.0,
61
+ amplitude=1.0,
62
+ offset=0.0,
63
+ noise_injector=noise
64
+ )
65
+ for timestamp, value in gen.stream(sampling_rate=2.0, duration=3):
66
+ print(timestamp, value)
67
+
68
+ # Multi-channel example (sine and cosine)
69
+ ch1 = TelemetryGenerator(WaveformType.SINE, frequency=1.0)
70
+ ch2 = TelemetryGenerator(WaveformType.COSINE, frequency=0.5)
71
+ multi = MultiChannelTelemetryGenerator([ch1, ch2])
72
+ for sample in multi.stream(sampling_rate=2.0, duration=3):
73
+ print({k: (v[0], v[1]) for k, v in sample.items()})
74
+
75
+ # Triangle and pulse waveforms
76
+ tri = TelemetryGenerator(WaveformType.TRIANGLE, frequency=1.0)
77
+ pulse = TelemetryGenerator(WaveformType.PULSE, frequency=1.0)
78
+ print('Triangle:', next(tri.stream(1)))
79
+ print('Pulse:', next(pulse.stream(1)))
80
+
81
+ # Custom waveform (e.g., constant 42)
82
+ def custom_func(t):
83
+ return 42.0
84
+ gen_custom = TelemetryGenerator(WaveformType.CUSTOM, custom_func=custom_func)
85
+ print('Custom:', next(gen_custom.stream(1)))
86
+ ```
87
+
88
+ ## Installation
89
+
90
+ Clone this repo and install locally:
91
+
92
+ ```sh
93
+ pip install .
94
+ ```
95
+
96
+ Or install in editable/development mode:
97
+
98
+ ```sh
99
+ pip install -e .
100
+ ```
101
+
102
+ ## Testing
103
+
104
+ Run all tests:
105
+
106
+ ```sh
107
+ python -m unittest discover -s tests
108
+ ```
109
+
110
+ ## License
111
+ MIT
@@ -0,0 +1,78 @@
1
+ [![PyPI version](https://img.shields.io/pypi/v/faketelemetry.svg)](https://pypi.org/project/faketelemetry/)
2
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
3
+ [![CI](https://github.com/adkvi/faketelemetry/actions/workflows/python-package.yml/badge.svg)](https://github.com/adkvi/faketelemetry/actions)
4
+
5
+ # FakeTelemetry
6
+
7
+ **A minimal Python package to generate real-time, timestamped fake telemetry streams for testing, simulation, and development.**
8
+
9
+ ## Features
10
+ - Sine, cosine, square, sawtooth, triangle, pulse, and random noise waveforms
11
+ - Custom user-defined waveform support
12
+ - Adjustable amplitude, frequency, offset, and sample rate
13
+ - Optional Gaussian noise injection
14
+ - Real-time streaming of (datetime, value) tuples
15
+ - Multi-channel (parallel) telemetry generation
16
+ - Well-tested and extensible
17
+
18
+ ## Example Usage
19
+
20
+ ```python
21
+ from faketelemetry import TelemetryGenerator, WaveformType, NoiseInjector, MultiChannelTelemetryGenerator
22
+
23
+ # Single channel with noise (sine wave)
24
+ noise = NoiseInjector(noise_level=0.2)
25
+ gen = TelemetryGenerator(
26
+ waveform=WaveformType.SINE,
27
+ frequency=1.0,
28
+ amplitude=1.0,
29
+ offset=0.0,
30
+ noise_injector=noise
31
+ )
32
+ for timestamp, value in gen.stream(sampling_rate=2.0, duration=3):
33
+ print(timestamp, value)
34
+
35
+ # Multi-channel example (sine and cosine)
36
+ ch1 = TelemetryGenerator(WaveformType.SINE, frequency=1.0)
37
+ ch2 = TelemetryGenerator(WaveformType.COSINE, frequency=0.5)
38
+ multi = MultiChannelTelemetryGenerator([ch1, ch2])
39
+ for sample in multi.stream(sampling_rate=2.0, duration=3):
40
+ print({k: (v[0], v[1]) for k, v in sample.items()})
41
+
42
+ # Triangle and pulse waveforms
43
+ tri = TelemetryGenerator(WaveformType.TRIANGLE, frequency=1.0)
44
+ pulse = TelemetryGenerator(WaveformType.PULSE, frequency=1.0)
45
+ print('Triangle:', next(tri.stream(1)))
46
+ print('Pulse:', next(pulse.stream(1)))
47
+
48
+ # Custom waveform (e.g., constant 42)
49
+ def custom_func(t):
50
+ return 42.0
51
+ gen_custom = TelemetryGenerator(WaveformType.CUSTOM, custom_func=custom_func)
52
+ print('Custom:', next(gen_custom.stream(1)))
53
+ ```
54
+
55
+ ## Installation
56
+
57
+ Clone this repo and install locally:
58
+
59
+ ```sh
60
+ pip install .
61
+ ```
62
+
63
+ Or install in editable/development mode:
64
+
65
+ ```sh
66
+ pip install -e .
67
+ ```
68
+
69
+ ## Testing
70
+
71
+ Run all tests:
72
+
73
+ ```sh
74
+ python -m unittest discover -s tests
75
+ ```
76
+
77
+ ## License
78
+ MIT
@@ -0,0 +1,6 @@
1
+ from .telemetry_generator import TelemetryGenerator
2
+ from .multi_channel import MultiChannelTelemetryGenerator
3
+ from .noise_injector import NoiseInjector
4
+ from .enums import WaveformType
5
+
6
+ __version__ = "0.1.0"
@@ -0,0 +1,16 @@
1
+ from enum import Enum
2
+
3
+
4
+ class WaveformType(Enum):
5
+ """
6
+ Enumeration of supported waveform types for telemetry generation.
7
+ """
8
+
9
+ SINE = "sine"
10
+ COSINE = "cosine"
11
+ SQUARE = "square"
12
+ SAWTOOTH = "sawtooth"
13
+ TRIANGLE = "triangle"
14
+ PULSE = "pulse"
15
+ RANDOM_NOISE = "random_noise"
16
+ CUSTOM = "custom"
@@ -0,0 +1,33 @@
1
+ from datetime import datetime
2
+ import time
3
+ from typing import Optional
4
+ from .telemetry_generator import TelemetryGenerator
5
+
6
+
7
+ class MultiChannelTelemetryGenerator:
8
+ """
9
+ Generate multiple telemetry streams (channels) in parallel.
10
+ """
11
+
12
+ def __init__(self, generators: list[TelemetryGenerator]):
13
+ """
14
+ :param generators: List of TelemetryGenerator instances (one per channel)
15
+ """
16
+ self.generators = generators
17
+
18
+ def stream(self, sampling_rate: float, duration: Optional[float] = None):
19
+ """
20
+ Yield a dict of {channel_index: (datetime, value)} for each sample.
21
+ """
22
+ interval = 1.0 / sampling_rate
23
+ start_time = time.time()
24
+ elapsed = 0.0
25
+ while duration is None or elapsed < duration:
26
+ now = time.time() - start_time
27
+ result = {}
28
+ for idx, gen in enumerate(self.generators):
29
+ value = gen.generate_point(now)
30
+ result[idx] = (datetime.now(), value)
31
+ yield result
32
+ time.sleep(interval)
33
+ elapsed = time.time() - start_time
@@ -0,0 +1,21 @@
1
+ import random
2
+
3
+
4
+ class NoiseInjector:
5
+ """
6
+ Injects Gaussian noise into a signal value.
7
+ """
8
+
9
+ def __init__(self, noise_level: float = 0.0):
10
+ """
11
+ :param noise_level: Standard deviation of the Gaussian noise.
12
+ """
13
+ self.noise_level = noise_level
14
+
15
+ def add_noise(self, value: float) -> float:
16
+ """
17
+ Add Gaussian noise to the input value.
18
+ """
19
+ if self.noise_level > 0:
20
+ return value + random.gauss(0, self.noise_level)
21
+ return value
@@ -0,0 +1,119 @@
1
+ import math
2
+ import time
3
+ import random
4
+ from datetime import datetime
5
+ from typing import Iterator, Tuple, Optional, Callable
6
+ from .enums import WaveformType
7
+ from .noise_injector import NoiseInjector
8
+
9
+
10
+ class TelemetryGenerator:
11
+ """
12
+ Generates real-time telemetry data based on mathematical waveforms.
13
+ """
14
+
15
+ def __init__(
16
+ self,
17
+ waveform: WaveformType,
18
+ frequency: float = 1.0,
19
+ amplitude: float = 1.0,
20
+ offset: float = 0.0,
21
+ noise_injector: Optional[NoiseInjector] = None,
22
+ custom_func: Optional[Callable[[float], float]] = None,
23
+ ):
24
+ """
25
+ Initialize the telemetry generator.
26
+ :param waveform: Type of signal to generate
27
+ :param frequency: Frequency in Hz (must be >= 0)
28
+ :param amplitude: Peak amplitude (must be >= 0)
29
+ :param offset: Base offset (vertical shift)
30
+ :param noise_injector: Optional NoiseInjector
31
+ :param custom_func: Optional function for custom waveform, signature: (t: float) -> float
32
+ """
33
+ if frequency < 0:
34
+ raise ValueError("Frequency must be non-negative.")
35
+ if amplitude < 0:
36
+ raise ValueError("Amplitude must be non-negative.")
37
+ if waveform == WaveformType.CUSTOM and not callable(custom_func):
38
+ raise ValueError(
39
+ "A callable custom_func must be provided for CUSTOM waveform."
40
+ )
41
+ self.waveform = waveform
42
+ self.frequency = frequency
43
+ self.amplitude = amplitude
44
+ self.offset = offset
45
+ self.noise_injector = noise_injector
46
+ self.custom_func = custom_func
47
+
48
+ def generate_point(self, t: float) -> float:
49
+ """
50
+ Generate a signal value at time t (seconds).
51
+ """
52
+ if t < 0:
53
+ raise ValueError("Time t must be non-negative.")
54
+ if self.waveform == WaveformType.SINE:
55
+ value = (
56
+ self.amplitude * math.sin(2 * math.pi * self.frequency * t)
57
+ + self.offset
58
+ )
59
+ elif self.waveform == WaveformType.COSINE:
60
+ value = (
61
+ self.amplitude * math.cos(2 * math.pi * self.frequency * t)
62
+ + self.offset
63
+ )
64
+ elif self.waveform == WaveformType.SQUARE:
65
+ value = (
66
+ self.amplitude
67
+ * (1 if math.sin(2 * math.pi * self.frequency * t) >= 0 else -1)
68
+ + self.offset
69
+ )
70
+ elif self.waveform == WaveformType.SAWTOOTH:
71
+ value = (
72
+ self.amplitude
73
+ * (2 * (t * self.frequency - math.floor(0.5 + t * self.frequency)))
74
+ + self.offset
75
+ )
76
+ elif self.waveform == WaveformType.TRIANGLE:
77
+ value = (
78
+ self.amplitude
79
+ * (
80
+ 2
81
+ * abs(
82
+ 2 * (t * self.frequency - math.floor(t * self.frequency + 0.5))
83
+ )
84
+ - 1
85
+ )
86
+ + self.offset
87
+ )
88
+ elif self.waveform == WaveformType.PULSE:
89
+ value = (
90
+ self.amplitude * (1 if (t * self.frequency) % 1 < 0.1 else 0)
91
+ + self.offset
92
+ )
93
+ elif self.waveform == WaveformType.RANDOM_NOISE:
94
+ value = self.amplitude * random.gauss(0, 1) + self.offset
95
+ elif self.waveform == WaveformType.CUSTOM:
96
+ if self.custom_func is None:
97
+ raise ValueError("Custom waveform requires a custom_func argument.")
98
+ value = self.custom_func(t)
99
+ else:
100
+ raise ValueError(f"Unsupported waveform: {self.waveform}")
101
+ if self.noise_injector:
102
+ value = self.noise_injector.add_noise(value)
103
+ return value
104
+
105
+ def stream(
106
+ self, sampling_rate: float, duration: Optional[float] = None
107
+ ) -> Iterator[Tuple[datetime, float]]:
108
+ """
109
+ Stream (datetime, value) tuples in real-time at the given sampling rate.
110
+ """
111
+ interval = 1.0 / sampling_rate
112
+ start_time = time.time()
113
+ elapsed = 0.0
114
+ while duration is None or elapsed < duration:
115
+ now = time.time() - start_time
116
+ value = self.generate_point(now)
117
+ yield (datetime.now(), value)
118
+ time.sleep(interval)
119
+ elapsed = time.time() - start_time
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.4
2
+ Name: faketelemetry
3
+ Version: 0.1.0
4
+ Summary: Real-time Fake Telemetry Stream Generator
5
+ Home-page: https://github.com/adkvi/faketelemetry
6
+ Author: Adam Billekvist
7
+ Author-email: adam.billeqvist@example.com
8
+ Project-URL: Documentation, https://github.com/adkvi/faketelemetry#readme
9
+ Project-URL: Source, https://github.com/adkvi/faketelemetry
10
+ Project-URL: Tracker, https://github.com/adkvi/faketelemetry/issues
11
+ Keywords: telemetry,signal,generator,iot,testing,simulation,waveform,python
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
18
+ Classifier: Topic :: Software Development :: Testing
19
+ Requires-Python: >=3.7
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Dynamic: author
23
+ Dynamic: author-email
24
+ Dynamic: classifier
25
+ Dynamic: description
26
+ Dynamic: description-content-type
27
+ Dynamic: home-page
28
+ Dynamic: keywords
29
+ Dynamic: license-file
30
+ Dynamic: project-url
31
+ Dynamic: requires-python
32
+ Dynamic: summary
33
+
34
+ [![PyPI version](https://img.shields.io/pypi/v/faketelemetry.svg)](https://pypi.org/project/faketelemetry/)
35
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
36
+ [![CI](https://github.com/adkvi/faketelemetry/actions/workflows/python-package.yml/badge.svg)](https://github.com/adkvi/faketelemetry/actions)
37
+
38
+ # FakeTelemetry
39
+
40
+ **A minimal Python package to generate real-time, timestamped fake telemetry streams for testing, simulation, and development.**
41
+
42
+ ## Features
43
+ - Sine, cosine, square, sawtooth, triangle, pulse, and random noise waveforms
44
+ - Custom user-defined waveform support
45
+ - Adjustable amplitude, frequency, offset, and sample rate
46
+ - Optional Gaussian noise injection
47
+ - Real-time streaming of (datetime, value) tuples
48
+ - Multi-channel (parallel) telemetry generation
49
+ - Well-tested and extensible
50
+
51
+ ## Example Usage
52
+
53
+ ```python
54
+ from faketelemetry import TelemetryGenerator, WaveformType, NoiseInjector, MultiChannelTelemetryGenerator
55
+
56
+ # Single channel with noise (sine wave)
57
+ noise = NoiseInjector(noise_level=0.2)
58
+ gen = TelemetryGenerator(
59
+ waveform=WaveformType.SINE,
60
+ frequency=1.0,
61
+ amplitude=1.0,
62
+ offset=0.0,
63
+ noise_injector=noise
64
+ )
65
+ for timestamp, value in gen.stream(sampling_rate=2.0, duration=3):
66
+ print(timestamp, value)
67
+
68
+ # Multi-channel example (sine and cosine)
69
+ ch1 = TelemetryGenerator(WaveformType.SINE, frequency=1.0)
70
+ ch2 = TelemetryGenerator(WaveformType.COSINE, frequency=0.5)
71
+ multi = MultiChannelTelemetryGenerator([ch1, ch2])
72
+ for sample in multi.stream(sampling_rate=2.0, duration=3):
73
+ print({k: (v[0], v[1]) for k, v in sample.items()})
74
+
75
+ # Triangle and pulse waveforms
76
+ tri = TelemetryGenerator(WaveformType.TRIANGLE, frequency=1.0)
77
+ pulse = TelemetryGenerator(WaveformType.PULSE, frequency=1.0)
78
+ print('Triangle:', next(tri.stream(1)))
79
+ print('Pulse:', next(pulse.stream(1)))
80
+
81
+ # Custom waveform (e.g., constant 42)
82
+ def custom_func(t):
83
+ return 42.0
84
+ gen_custom = TelemetryGenerator(WaveformType.CUSTOM, custom_func=custom_func)
85
+ print('Custom:', next(gen_custom.stream(1)))
86
+ ```
87
+
88
+ ## Installation
89
+
90
+ Clone this repo and install locally:
91
+
92
+ ```sh
93
+ pip install .
94
+ ```
95
+
96
+ Or install in editable/development mode:
97
+
98
+ ```sh
99
+ pip install -e .
100
+ ```
101
+
102
+ ## Testing
103
+
104
+ Run all tests:
105
+
106
+ ```sh
107
+ python -m unittest discover -s tests
108
+ ```
109
+
110
+ ## License
111
+ MIT
@@ -0,0 +1,15 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ setup.py
5
+ faketelemetry/__init__.py
6
+ faketelemetry/enums.py
7
+ faketelemetry/multi_channel.py
8
+ faketelemetry/noise_injector.py
9
+ faketelemetry/telemetry_generator.py
10
+ faketelemetry.egg-info/PKG-INFO
11
+ faketelemetry.egg-info/SOURCES.txt
12
+ faketelemetry.egg-info/dependency_links.txt
13
+ faketelemetry.egg-info/top_level.txt
14
+ tests/test_generator.py
15
+ tests/test_noise.py
@@ -0,0 +1 @@
1
+ faketelemetry
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64", "wheel"]
3
+ build-backend = "setuptools.build_meta"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,36 @@
1
+ from setuptools import setup, find_packages
2
+ import pathlib
3
+
4
+ here = pathlib.Path(__file__).parent.resolve()
5
+ long_description = (here / "README.md").read_text(encoding="utf-8")
6
+
7
+ setup(
8
+ name="faketelemetry",
9
+ version="0.1.0",
10
+ description="Real-time Fake Telemetry Stream Generator",
11
+ long_description=long_description,
12
+ long_description_content_type="text/markdown",
13
+ author="Adam Billekvist",
14
+ author_email="adam.billeqvist@example.com",
15
+ url="https://github.com/adkvi/faketelemetry",
16
+ project_urls={
17
+ "Documentation": "https://github.com/adkvi/faketelemetry#readme",
18
+ "Source": "https://github.com/adkvi/faketelemetry",
19
+ "Tracker": "https://github.com/adkvi/faketelemetry/issues",
20
+ },
21
+ keywords=[
22
+ "telemetry", "signal", "generator", "iot", "testing", "simulation", "waveform", "python"
23
+ ],
24
+ packages=find_packages(),
25
+ install_requires=[],
26
+ python_requires=">=3.7",
27
+ classifiers=[
28
+ "Programming Language :: Python :: 3",
29
+ "License :: OSI Approved :: MIT License",
30
+ "Operating System :: OS Independent",
31
+ "Intended Audience :: Developers",
32
+ "Topic :: Software Development :: Libraries :: Python Modules",
33
+ "Topic :: Scientific/Engineering :: Information Analysis",
34
+ "Topic :: Software Development :: Testing",
35
+ ],
36
+ )
@@ -0,0 +1,101 @@
1
+ import unittest
2
+ from datetime import datetime
3
+ from faketelemetry import (
4
+ TelemetryGenerator,
5
+ WaveformType,
6
+ MultiChannelTelemetryGenerator,
7
+ )
8
+
9
+
10
+ class TestTelemetryGenerator(unittest.TestCase):
11
+ def test_sine_wave(self):
12
+ gen = TelemetryGenerator(
13
+ WaveformType.SINE, frequency=1.0, amplitude=2.0, offset=1.0
14
+ )
15
+ value = gen.generate_point(0)
16
+ self.assertAlmostEqual(value, 1.0)
17
+ value = gen.generate_point(0.25)
18
+ self.assertAlmostEqual(value, 3.0, places=1)
19
+
20
+ def test_cosine_wave(self):
21
+ gen = TelemetryGenerator(
22
+ WaveformType.COSINE, frequency=1.0, amplitude=1.0, offset=0.0
23
+ )
24
+ value = gen.generate_point(0)
25
+ self.assertAlmostEqual(value, 1.0)
26
+
27
+ def test_square_wave(self):
28
+ gen = TelemetryGenerator(
29
+ WaveformType.SQUARE, frequency=1.0, amplitude=1.0, offset=0.0
30
+ )
31
+ self.assertIn(gen.generate_point(0), [1.0, -1.0])
32
+
33
+ def test_sawtooth_wave(self):
34
+ gen = TelemetryGenerator(
35
+ WaveformType.SAWTOOTH, frequency=1.0, amplitude=1.0, offset=0.0
36
+ )
37
+ value = gen.generate_point(0)
38
+ self.assertAlmostEqual(value, 0.0)
39
+
40
+ def test_multichannel_stream(self):
41
+ gen1 = TelemetryGenerator(WaveformType.SINE, frequency=1.0)
42
+ gen2 = TelemetryGenerator(WaveformType.COSINE, frequency=2.0)
43
+ multi = MultiChannelTelemetryGenerator([gen1, gen2])
44
+ stream = multi.stream(sampling_rate=2.0, duration=1)
45
+ results = list(stream)
46
+ self.assertTrue(len(results) > 0)
47
+ for sample in results:
48
+ self.assertEqual(len(sample), 2)
49
+ for v in sample.values():
50
+ self.assertIsInstance(v[0], datetime)
51
+ self.assertIsInstance(v[1], float)
52
+
53
+ def test_triangle_wave(self):
54
+ gen = TelemetryGenerator(
55
+ WaveformType.TRIANGLE, frequency=1.0, amplitude=1.0, offset=0.0
56
+ )
57
+ self.assertAlmostEqual(gen.generate_point(0), -1.0, places=1)
58
+ self.assertAlmostEqual(gen.generate_point(0.25), 0.0, places=1)
59
+ self.assertAlmostEqual(gen.generate_point(0.5), 1.0, places=1)
60
+ self.assertAlmostEqual(gen.generate_point(0.75), 0.0, places=1)
61
+
62
+ def test_pulse_wave(self):
63
+ gen = TelemetryGenerator(
64
+ WaveformType.PULSE, frequency=1.0, amplitude=1.0, offset=0.0
65
+ )
66
+ self.assertEqual(gen.generate_point(0), 1.0)
67
+ self.assertEqual(gen.generate_point(0.11), 0.0)
68
+ self.assertEqual(gen.generate_point(1.0), 1.0)
69
+
70
+ def test_custom_wave(self):
71
+ def custom_func(t):
72
+ return 42.0
73
+
74
+ gen = TelemetryGenerator(WaveformType.CUSTOM, custom_func=custom_func)
75
+ self.assertEqual(gen.generate_point(0), 42.0)
76
+ self.assertEqual(gen.generate_point(1), 42.0)
77
+
78
+ def test_invalid_frequency(self):
79
+ with self.assertRaises(ValueError) as cm:
80
+ TelemetryGenerator(WaveformType.SINE, frequency=-1)
81
+ self.assertIn("Frequency must be non-negative", str(cm.exception))
82
+
83
+ def test_invalid_amplitude(self):
84
+ with self.assertRaises(ValueError) as cm:
85
+ TelemetryGenerator(WaveformType.SINE, amplitude=-1)
86
+ self.assertIn("Amplitude must be non-negative", str(cm.exception))
87
+
88
+ def test_invalid_custom_func(self):
89
+ with self.assertRaises(ValueError) as cm:
90
+ TelemetryGenerator(WaveformType.CUSTOM, custom_func=None)
91
+ self.assertIn("callable custom_func", str(cm.exception))
92
+
93
+ def test_negative_time(self):
94
+ gen = TelemetryGenerator(WaveformType.SINE)
95
+ with self.assertRaises(ValueError) as cm:
96
+ gen.generate_point(-1)
97
+ self.assertIn("Time t must be non-negative", str(cm.exception))
98
+
99
+
100
+ if __name__ == "__main__":
101
+ unittest.main()
@@ -0,0 +1,17 @@
1
+ import unittest
2
+ from faketelemetry.noise_injector import NoiseInjector
3
+
4
+
5
+ class TestNoiseInjector(unittest.TestCase):
6
+ def test_no_noise(self):
7
+ ni = NoiseInjector(noise_level=0.0)
8
+ self.assertEqual(ni.add_noise(5.0), 5.0)
9
+
10
+ def test_with_noise(self):
11
+ ni = NoiseInjector(noise_level=1.0)
12
+ values = [ni.add_noise(5.0) for _ in range(100)]
13
+ self.assertTrue(any(abs(v - 5.0) > 0.1 for v in values))
14
+
15
+
16
+ if __name__ == "__main__":
17
+ unittest.main()