mcp-server-mcsa 0.1.0__py3-none-any.whl → 0.1.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.
@@ -1,232 +1,232 @@
1
- """Synthetic test‑signal generator for MCSA.
2
-
3
- Generates simulated stator‑current waveforms with optional fault
4
- signatures for testing, validation, and demonstration purposes.
5
- """
6
-
7
- from __future__ import annotations
8
-
9
- import numpy as np
10
- from numpy.typing import NDArray
11
-
12
-
13
- def generate_healthy_signal(
14
- duration_s: float,
15
- fs_sample: float,
16
- supply_freq_hz: float = 50.0,
17
- amplitude: float = 1.0,
18
- noise_std: float = 0.01,
19
- harmonics: list[tuple[int, float]] | None = None,
20
- ) -> tuple[NDArray[np.floating], NDArray[np.floating]]:
21
- """Generate a clean (healthy) motor‑current signal.
22
-
23
- Args:
24
- duration_s: Signal duration in seconds.
25
- fs_sample: Sampling frequency in Hz.
26
- supply_freq_hz: Supply fundamental frequency in Hz.
27
- amplitude: Peak amplitude of the fundamental.
28
- noise_std: Standard deviation of additive Gaussian noise.
29
- harmonics: Optional list of (harmonic_number, relative_amplitude)
30
- to add supply harmonics (e.g. [(3, 0.05), (5, 0.03)]).
31
-
32
- Returns:
33
- (time, signal) — 1‑D arrays.
34
- """
35
- n_samples = int(duration_s * fs_sample)
36
- t = np.arange(n_samples) / fs_sample
37
-
38
- x = amplitude * np.sin(2.0 * np.pi * supply_freq_hz * t)
39
-
40
- if harmonics:
41
- for h_num, h_amp in harmonics:
42
- x += h_amp * amplitude * np.sin(2.0 * np.pi * h_num * supply_freq_hz * t)
43
-
44
- if noise_std > 0:
45
- x += np.random.default_rng(42).normal(0, noise_std, n_samples)
46
-
47
- return t, x
48
-
49
-
50
- def inject_brb_fault(
51
- t: NDArray[np.floating],
52
- x: NDArray[np.floating],
53
- supply_freq_hz: float,
54
- slip: float,
55
- sideband_amplitude: float = 0.02,
56
- n_harmonics: int = 1,
57
- ) -> NDArray[np.floating]:
58
- """Inject broken‑rotor‑bar sidebands into a current signal.
59
-
60
- Adds components at (1 ± 2ks)·f_s for k = 1 … n_harmonics.
61
-
62
- Args:
63
- t: Time axis.
64
- x: Original signal.
65
- supply_freq_hz: Supply frequency (Hz).
66
- slip: Motor slip (per‑unit).
67
- sideband_amplitude: Amplitude of each sideband component.
68
- n_harmonics: Number of sideband pairs.
69
-
70
- Returns:
71
- Signal with BRB fault injected.
72
- """
73
- y = x.copy()
74
- fs = supply_freq_hz
75
-
76
- for k in range(1, n_harmonics + 1):
77
- f_lower = (1 - 2 * k * slip) * fs
78
- f_upper = (1 + 2 * k * slip) * fs
79
- y += sideband_amplitude * np.sin(2.0 * np.pi * f_lower * t)
80
- y += sideband_amplitude * np.sin(2.0 * np.pi * f_upper * t)
81
-
82
- return y
83
-
84
-
85
- def inject_eccentricity_fault(
86
- t: NDArray[np.floating],
87
- x: NDArray[np.floating],
88
- supply_freq_hz: float,
89
- rotor_freq_hz: float,
90
- sideband_amplitude: float = 0.015,
91
- n_harmonics: int = 2,
92
- ) -> NDArray[np.floating]:
93
- """Inject eccentricity sidebands at f_s ± k·f_r.
94
-
95
- Args:
96
- t: Time axis.
97
- x: Original signal.
98
- supply_freq_hz: Supply frequency (Hz).
99
- rotor_freq_hz: Rotor mechanical frequency (Hz).
100
- sideband_amplitude: Amplitude per sideband.
101
- n_harmonics: Number of sideband pairs.
102
-
103
- Returns:
104
- Signal with eccentricity fault injected.
105
- """
106
- y = x.copy()
107
-
108
- for k in range(1, n_harmonics + 1):
109
- f_lo = supply_freq_hz - k * rotor_freq_hz
110
- f_hi = supply_freq_hz + k * rotor_freq_hz
111
- y += sideband_amplitude * np.sin(2.0 * np.pi * f_lo * t)
112
- y += sideband_amplitude * np.sin(2.0 * np.pi * f_hi * t)
113
-
114
- return y
115
-
116
-
117
- def inject_bearing_fault(
118
- t: NDArray[np.floating],
119
- x: NDArray[np.floating],
120
- supply_freq_hz: float,
121
- defect_freq_hz: float,
122
- modulation_depth: float = 0.01,
123
- n_harmonics: int = 2,
124
- ) -> NDArray[np.floating]:
125
- """Inject bearing defect modulation into a current signal.
126
-
127
- Bearing faults produce torque oscillations that amplitude‑modulate
128
- the stator current, creating sidebands at f_s ± k·f_defect.
129
-
130
- Args:
131
- t: Time axis.
132
- x: Original signal.
133
- supply_freq_hz: Supply frequency (Hz).
134
- defect_freq_hz: Bearing characteristic defect frequency (Hz).
135
- modulation_depth: Modulation amplitude.
136
- n_harmonics: Number of sideband pairs.
137
-
138
- Returns:
139
- Signal with bearing fault injected.
140
- """
141
- y = x.copy()
142
-
143
- for k in range(1, n_harmonics + 1):
144
- f_lo = supply_freq_hz - k * defect_freq_hz
145
- f_hi = supply_freq_hz + k * defect_freq_hz
146
- y += modulation_depth * np.sin(2.0 * np.pi * f_lo * t)
147
- y += modulation_depth * np.sin(2.0 * np.pi * f_hi * t)
148
-
149
- return y
150
-
151
-
152
- def generate_test_signal(
153
- duration_s: float = 10.0,
154
- fs_sample: float = 5000.0,
155
- supply_freq_hz: float = 50.0,
156
- poles: int = 4,
157
- rotor_speed_rpm: float = 1470.0,
158
- amplitude: float = 1.0,
159
- noise_std: float = 0.01,
160
- faults: list[str] | None = None,
161
- fault_severity: float = 0.02,
162
- bearing_defect_freq_hz: float | None = None,
163
- ) -> dict:
164
- """Generate a complete synthetic motor‑current test signal.
165
-
166
- Convenience function that creates a healthy baseline and optionally
167
- injects one or more fault types.
168
-
169
- Args:
170
- duration_s: Signal duration in seconds.
171
- fs_sample: Sampling frequency in Hz.
172
- supply_freq_hz: Supply frequency in Hz.
173
- poles: Number of poles.
174
- rotor_speed_rpm: Rotor speed in RPM.
175
- amplitude: Fundamental amplitude.
176
- noise_std: Noise level.
177
- faults: List of fault types to inject. Options:
178
- ``"brb"`` — broken rotor bars,
179
- ``"eccentricity"`` — air‑gap eccentricity,
180
- ``"bearing"`` — bearing defect.
181
- fault_severity: Amplitude of fault components (0–1 relative).
182
- bearing_defect_freq_hz: Bearing defect frequency (Hz). Required
183
- if ``"bearing"`` is in faults.
184
-
185
- Returns:
186
- Dictionary with ``time_s``, ``signal``, ``sampling_freq_hz``,
187
- ``motor_params``, ``faults_injected``.
188
- """
189
- sync_speed = 120.0 * supply_freq_hz / poles
190
- slip = (sync_speed - rotor_speed_rpm) / sync_speed
191
- rotor_freq = rotor_speed_rpm / 60.0
192
-
193
- t, x = generate_healthy_signal(
194
- duration_s, fs_sample, supply_freq_hz, amplitude, noise_std,
195
- harmonics=[(3, 0.03), (5, 0.015)],
196
- )
197
-
198
- injected = []
199
-
200
- if faults:
201
- for fault in faults:
202
- fault = fault.lower().strip()
203
- if fault == "brb":
204
- x = inject_brb_fault(t, x, supply_freq_hz, slip, fault_severity)
205
- injected.append("broken_rotor_bars")
206
- elif fault == "eccentricity":
207
- x = inject_eccentricity_fault(
208
- t, x, supply_freq_hz, rotor_freq, fault_severity
209
- )
210
- injected.append("eccentricity")
211
- elif fault == "bearing":
212
- bdf = bearing_defect_freq_hz or (3.5 * rotor_freq)
213
- x = inject_bearing_fault(t, x, supply_freq_hz, bdf, fault_severity)
214
- injected.append("bearing")
215
-
216
- return {
217
- "time_s": t.tolist(),
218
- "signal": x.tolist(),
219
- "sampling_freq_hz": fs_sample,
220
- "duration_s": duration_s,
221
- "n_samples": len(t),
222
- "motor_params": {
223
- "supply_freq_hz": supply_freq_hz,
224
- "poles": poles,
225
- "sync_speed_rpm": sync_speed,
226
- "rotor_speed_rpm": rotor_speed_rpm,
227
- "slip": slip,
228
- "rotor_freq_hz": rotor_freq,
229
- },
230
- "faults_injected": injected,
231
- "fault_severity": fault_severity,
232
- }
1
+ """Synthetic test‑signal generator for MCSA.
2
+
3
+ Generates simulated stator‑current waveforms with optional fault
4
+ signatures for testing, validation, and demonstration purposes.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import numpy as np
10
+ from numpy.typing import NDArray
11
+
12
+
13
+ def generate_healthy_signal(
14
+ duration_s: float,
15
+ fs_sample: float,
16
+ supply_freq_hz: float = 50.0,
17
+ amplitude: float = 1.0,
18
+ noise_std: float = 0.01,
19
+ harmonics: list[tuple[int, float]] | None = None,
20
+ ) -> tuple[NDArray[np.floating], NDArray[np.floating]]:
21
+ """Generate a clean (healthy) motor‑current signal.
22
+
23
+ Args:
24
+ duration_s: Signal duration in seconds.
25
+ fs_sample: Sampling frequency in Hz.
26
+ supply_freq_hz: Supply fundamental frequency in Hz.
27
+ amplitude: Peak amplitude of the fundamental.
28
+ noise_std: Standard deviation of additive Gaussian noise.
29
+ harmonics: Optional list of (harmonic_number, relative_amplitude)
30
+ to add supply harmonics (e.g. [(3, 0.05), (5, 0.03)]).
31
+
32
+ Returns:
33
+ (time, signal) — 1‑D arrays.
34
+ """
35
+ n_samples = int(duration_s * fs_sample)
36
+ t = np.arange(n_samples) / fs_sample
37
+
38
+ x = amplitude * np.sin(2.0 * np.pi * supply_freq_hz * t)
39
+
40
+ if harmonics:
41
+ for h_num, h_amp in harmonics:
42
+ x += h_amp * amplitude * np.sin(2.0 * np.pi * h_num * supply_freq_hz * t)
43
+
44
+ if noise_std > 0:
45
+ x += np.random.default_rng(42).normal(0, noise_std, n_samples)
46
+
47
+ return t, x
48
+
49
+
50
+ def inject_brb_fault(
51
+ t: NDArray[np.floating],
52
+ x: NDArray[np.floating],
53
+ supply_freq_hz: float,
54
+ slip: float,
55
+ sideband_amplitude: float = 0.02,
56
+ n_harmonics: int = 1,
57
+ ) -> NDArray[np.floating]:
58
+ """Inject broken‑rotor‑bar sidebands into a current signal.
59
+
60
+ Adds components at (1 ± 2ks)·f_s for k = 1 … n_harmonics.
61
+
62
+ Args:
63
+ t: Time axis.
64
+ x: Original signal.
65
+ supply_freq_hz: Supply frequency (Hz).
66
+ slip: Motor slip (per‑unit).
67
+ sideband_amplitude: Amplitude of each sideband component.
68
+ n_harmonics: Number of sideband pairs.
69
+
70
+ Returns:
71
+ Signal with BRB fault injected.
72
+ """
73
+ y = x.copy()
74
+ fs = supply_freq_hz
75
+
76
+ for k in range(1, n_harmonics + 1):
77
+ f_lower = (1 - 2 * k * slip) * fs
78
+ f_upper = (1 + 2 * k * slip) * fs
79
+ y += sideband_amplitude * np.sin(2.0 * np.pi * f_lower * t)
80
+ y += sideband_amplitude * np.sin(2.0 * np.pi * f_upper * t)
81
+
82
+ return y
83
+
84
+
85
+ def inject_eccentricity_fault(
86
+ t: NDArray[np.floating],
87
+ x: NDArray[np.floating],
88
+ supply_freq_hz: float,
89
+ rotor_freq_hz: float,
90
+ sideband_amplitude: float = 0.015,
91
+ n_harmonics: int = 2,
92
+ ) -> NDArray[np.floating]:
93
+ """Inject eccentricity sidebands at f_s ± k·f_r.
94
+
95
+ Args:
96
+ t: Time axis.
97
+ x: Original signal.
98
+ supply_freq_hz: Supply frequency (Hz).
99
+ rotor_freq_hz: Rotor mechanical frequency (Hz).
100
+ sideband_amplitude: Amplitude per sideband.
101
+ n_harmonics: Number of sideband pairs.
102
+
103
+ Returns:
104
+ Signal with eccentricity fault injected.
105
+ """
106
+ y = x.copy()
107
+
108
+ for k in range(1, n_harmonics + 1):
109
+ f_lo = supply_freq_hz - k * rotor_freq_hz
110
+ f_hi = supply_freq_hz + k * rotor_freq_hz
111
+ y += sideband_amplitude * np.sin(2.0 * np.pi * f_lo * t)
112
+ y += sideband_amplitude * np.sin(2.0 * np.pi * f_hi * t)
113
+
114
+ return y
115
+
116
+
117
+ def inject_bearing_fault(
118
+ t: NDArray[np.floating],
119
+ x: NDArray[np.floating],
120
+ supply_freq_hz: float,
121
+ defect_freq_hz: float,
122
+ modulation_depth: float = 0.01,
123
+ n_harmonics: int = 2,
124
+ ) -> NDArray[np.floating]:
125
+ """Inject bearing defect modulation into a current signal.
126
+
127
+ Bearing faults produce torque oscillations that amplitude‑modulate
128
+ the stator current, creating sidebands at f_s ± k·f_defect.
129
+
130
+ Args:
131
+ t: Time axis.
132
+ x: Original signal.
133
+ supply_freq_hz: Supply frequency (Hz).
134
+ defect_freq_hz: Bearing characteristic defect frequency (Hz).
135
+ modulation_depth: Modulation amplitude.
136
+ n_harmonics: Number of sideband pairs.
137
+
138
+ Returns:
139
+ Signal with bearing fault injected.
140
+ """
141
+ y = x.copy()
142
+
143
+ for k in range(1, n_harmonics + 1):
144
+ f_lo = supply_freq_hz - k * defect_freq_hz
145
+ f_hi = supply_freq_hz + k * defect_freq_hz
146
+ y += modulation_depth * np.sin(2.0 * np.pi * f_lo * t)
147
+ y += modulation_depth * np.sin(2.0 * np.pi * f_hi * t)
148
+
149
+ return y
150
+
151
+
152
+ def generate_test_signal(
153
+ duration_s: float = 10.0,
154
+ fs_sample: float = 5000.0,
155
+ supply_freq_hz: float = 50.0,
156
+ poles: int = 4,
157
+ rotor_speed_rpm: float = 1470.0,
158
+ amplitude: float = 1.0,
159
+ noise_std: float = 0.01,
160
+ faults: list[str] | None = None,
161
+ fault_severity: float = 0.02,
162
+ bearing_defect_freq_hz: float | None = None,
163
+ ) -> dict:
164
+ """Generate a complete synthetic motor‑current test signal.
165
+
166
+ Convenience function that creates a healthy baseline and optionally
167
+ injects one or more fault types.
168
+
169
+ Args:
170
+ duration_s: Signal duration in seconds.
171
+ fs_sample: Sampling frequency in Hz.
172
+ supply_freq_hz: Supply frequency in Hz.
173
+ poles: Number of poles.
174
+ rotor_speed_rpm: Rotor speed in RPM.
175
+ amplitude: Fundamental amplitude.
176
+ noise_std: Noise level.
177
+ faults: List of fault types to inject. Options:
178
+ ``"brb"`` — broken rotor bars,
179
+ ``"eccentricity"`` — air‑gap eccentricity,
180
+ ``"bearing"`` — bearing defect.
181
+ fault_severity: Amplitude of fault components (0–1 relative).
182
+ bearing_defect_freq_hz: Bearing defect frequency (Hz). Required
183
+ if ``"bearing"`` is in faults.
184
+
185
+ Returns:
186
+ Dictionary with ``time_s``, ``signal``, ``sampling_freq_hz``,
187
+ ``motor_params``, ``faults_injected``.
188
+ """
189
+ sync_speed = 120.0 * supply_freq_hz / poles
190
+ slip = (sync_speed - rotor_speed_rpm) / sync_speed
191
+ rotor_freq = rotor_speed_rpm / 60.0
192
+
193
+ t, x = generate_healthy_signal(
194
+ duration_s, fs_sample, supply_freq_hz, amplitude, noise_std,
195
+ harmonics=[(3, 0.03), (5, 0.015)],
196
+ )
197
+
198
+ injected = []
199
+
200
+ if faults:
201
+ for fault in faults:
202
+ fault = fault.lower().strip()
203
+ if fault == "brb":
204
+ x = inject_brb_fault(t, x, supply_freq_hz, slip, fault_severity)
205
+ injected.append("broken_rotor_bars")
206
+ elif fault == "eccentricity":
207
+ x = inject_eccentricity_fault(
208
+ t, x, supply_freq_hz, rotor_freq, fault_severity
209
+ )
210
+ injected.append("eccentricity")
211
+ elif fault == "bearing":
212
+ bdf = bearing_defect_freq_hz or (3.5 * rotor_freq)
213
+ x = inject_bearing_fault(t, x, supply_freq_hz, bdf, fault_severity)
214
+ injected.append("bearing")
215
+
216
+ return {
217
+ "time_s": t.tolist(),
218
+ "signal": x.tolist(),
219
+ "sampling_freq_hz": fs_sample,
220
+ "duration_s": duration_s,
221
+ "n_samples": len(t),
222
+ "motor_params": {
223
+ "supply_freq_hz": supply_freq_hz,
224
+ "poles": poles,
225
+ "sync_speed_rpm": sync_speed,
226
+ "rotor_speed_rpm": rotor_speed_rpm,
227
+ "slip": slip,
228
+ "rotor_freq_hz": rotor_freq,
229
+ },
230
+ "faults_injected": injected,
231
+ "fault_severity": fault_severity,
232
+ }