Skip to content

Commit fe8fc88

Browse files
author
elwaagaa
committed
Example showing FFT and current amplitude
1 parent fa045ef commit fe8fc88

File tree

1 file changed

+117
-0
lines changed

1 file changed

+117
-0
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
"""
2+
Script to check FFT of SPS quadrupolar circuit currents
3+
"""
4+
import fma_ions
5+
import numpy as np
6+
import matplotlib.pyplot as plt
7+
from scipy.fft import fft, fftfreq
8+
9+
# Simulation parameters
10+
fs = 1000 # Sampling frequency (Hz)
11+
duration = 2 # Duration in seconds
12+
t = np.linspace(0, duration, int(fs * duration), endpoint=False)
13+
N = len(t)
14+
15+
# Signal parameters
16+
I_dc = 70.0 # DC current (A)
17+
I_50hz = 0.001 # 50 Hz component amplitude (A) - at the threshold
18+
I_100hz = 0.0005 # 100 Hz harmonic (A)
19+
I_150hz = 0.0003 # 150 Hz harmonic (A)
20+
21+
# Add some broadband noise to simulate other fluctuations
22+
noise_level = 0.003 # A (RMS)
23+
24+
# Create synthetic DCCT signal
25+
current = (I_dc +
26+
I_50hz * np.sin(2 * np.pi * 50 * t) +
27+
I_100hz * np.sin(2 * np.pi * 100 * t + 0.5) +
28+
I_150hz * np.sin(2 * np.pi * 150 * t + 1.2) +
29+
noise_level * np.random.randn(N))
30+
31+
print(f"Signal statistics:")
32+
print(f"Mean current: {np.mean(current):.4f} A")
33+
print(f"Current fluctuations (std): {np.std(current):.4f} A")
34+
print(f"Peak-to-peak variation: {np.ptp(current):.4f} A")
35+
36+
# Remove DC component (subtract mean)
37+
current_ac = current - np.mean(current)
38+
39+
# Perform FFT
40+
fft_result = fft(current_ac)
41+
frequencies = fftfreq(N, 1/fs)
42+
43+
# Calculate normalized FFT amplitude (amplitude/N)
44+
fft_amplitude_normalized = np.abs(fft_result) / N
45+
46+
# Only consider positive frequencies
47+
pos_freq_mask = frequencies >= 0
48+
frequencies_pos = frequencies[pos_freq_mask]
49+
fft_amplitude_pos = fft_amplitude_normalized[pos_freq_mask]
50+
51+
# Find the 50 Hz component
52+
freq_50hz_idx = np.argmin(np.abs(frequencies_pos - 50))
53+
amplitude_50hz_normalized = fft_amplitude_pos[freq_50hz_idx]
54+
amplitude_50hz_actual = amplitude_50hz_normalized * 2 # Convert back to actual amplitude
55+
56+
print(f"\nFFT Analysis:")
57+
print(f"50 Hz normalized FFT amplitude: {amplitude_50hz_normalized:.6f}")
58+
print(f"50 Hz actual current amplitude: {amplitude_50hz_actual:.6f} A")
59+
print(f"Threshold (5e-4): {'PASS' if amplitude_50hz_normalized < 5e-4 else 'FAIL'}")
60+
61+
# Create plots
62+
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(12, 10))
63+
64+
# Time domain plot
65+
ax1.plot(t[:500], current[:500], 'b-', linewidth=0.8)
66+
ax1.set_xlabel('Time (s)')
67+
ax1.set_ylabel('Current (A)')
68+
ax1.set_title('DCCT Current Measurement (first 0.5 seconds)')
69+
ax1.grid(True, alpha=0.3)
70+
ax1.set_ylim([I_dc - 0.02, I_dc + 0.02])
71+
72+
# AC component time domain
73+
ax2.plot(t[:500], current_ac[:500], 'r-', linewidth=0.8)
74+
ax2.set_xlabel('Time (s)')
75+
ax2.set_ylabel('AC Current (A)')
76+
ax2.set_title('AC Component (DC removed)')
77+
ax2.grid(True, alpha=0.3)
78+
79+
# Frequency domain plot
80+
ax3.semilogy(frequencies_pos[:N//10], fft_amplitude_pos[:N//10], 'g-', linewidth=1)
81+
ax3.axhline(y=5e-4, color='r', linestyle='--', label='Threshold (5e-4)')
82+
ax3.axvline(x=50, color='orange', linestyle=':', label='50 Hz')
83+
ax3.set_xlabel('Frequency (Hz)')
84+
ax3.set_ylabel('Normalized FFT Amplitude')
85+
ax3.set_title('FFT Analysis (Normalized to Number of Samples)')
86+
ax3.grid(True, alpha=0.3)
87+
ax3.legend()
88+
ax3.set_xlim([0, 200])
89+
ax3.set_ylim([1e-6, 1e-2])
90+
91+
plt.tight_layout()
92+
plt.show()
93+
94+
# Additional analysis: Find all significant peaks
95+
significant_peaks = []
96+
for i, freq in enumerate(frequencies_pos):
97+
if 1 <= freq <= 500 and fft_amplitude_pos[i] > 1e-5: # Above noise floor
98+
significant_peaks.append((freq, fft_amplitude_pos[i], fft_amplitude_pos[i] * 2))
99+
100+
print(f"\nSignificant frequency components:")
101+
print(f"{'Frequency (Hz)':<15} {'Normalized Amp':<15} {'Actual Amp (A)':<15}")
102+
print("-" * 45)
103+
for freq, norm_amp, actual_amp in sorted(significant_peaks, key=lambda x: x[1], reverse=True)[:10]:
104+
print(f"{freq:<15.1f} {norm_amp:<15.6f} {actual_amp:<15.6f}")
105+
106+
# Compensation quality assessment
107+
print(f"\nCompensation Quality Assessment:")
108+
print(f"50 Hz component: {amplitude_50hz_actual*1000:.3f} mA")
109+
if amplitude_50hz_normalized < 5e-4:
110+
print("✓ Good compensation achieved (below threshold)")
111+
else:
112+
print("✗ Poor compensation (above threshold)")
113+
114+
print(f"\nFor reference:")
115+
print(f"- Total RMS fluctuation: {np.std(current_ac)*1000:.2f} mA")
116+
print(f"- 50 Hz component represents {(amplitude_50hz_actual/np.std(current_ac)*100):.1f}% of total fluctuation")
117+
plt.show()

0 commit comments

Comments
 (0)