-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSSVEP_Stim_Validation.py
More file actions
133 lines (108 loc) · 5.94 KB
/
Copy pathSSVEP_Stim_Validation.py
File metadata and controls
133 lines (108 loc) · 5.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
# HARMONIC DETECTION INTEGRATION
# --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
import nidaqmx
import numpy as np
from scipy.signal import butter, filtfilt
from nidaqmx.constants import TerminalConfiguration
import time
import csv
# Configuration
SAMPLE_RATE = 2000 # Sampling rate in Hz
SAMPLES_PER_CHANNEL = 8000 # Number of samples to acquire
CHANNELS = ["Dev1/ai1", "Dev1/ai2", "Dev1/ai3", "Dev1/ai4", "Dev1/ai5", "Dev1/ai6"]
FREQ_RANGE = (9, 14) # Frequency range for band-pass filter
AMPLITUDE_THRESHOLD = 0.01 # Minimum amplitude threshold for valid signal
HARMONIC_COUNT = 3 # Number of harmonics to analyze
# Band-pass filter function
def bandpass_filter(signal, lowcut, highcut, fs, order=4):
nyquist = 0.5 * fs
low = lowcut / nyquist
high = highcut / nyquist
b, a = butter(order, [low, high], btype='band')
return filtfilt(b, a, signal)
# Frequency and harmonic analysis function
def analyze_frequency_and_harmonics(signal, sample_rate):
fft_length = len(signal) * 10 # Zero-padding to increase resolution
fft_result = np.fft.fft(signal, n=fft_length)
frequencies = np.fft.fftfreq(fft_length, d=1/sample_rate)
positive_frequencies = frequencies[:fft_length//2]
fft_magnitude = np.abs(fft_result[:fft_length//2])
# Find dominant frequency
dominant_index = np.argmax(fft_magnitude)
fundamental_freq = positive_frequencies[dominant_index]
fundamental_magnitude = fft_magnitude[dominant_index]
# Analyze harmonics
harmonics = {}
for n in range(2, HARMONIC_COUNT + 1):
harmonic_freq = fundamental_freq * n
if harmonic_freq > max(positive_frequencies):
break
harmonic_index = np.argmin(np.abs(positive_frequencies - harmonic_freq))
harmonics[f"{n}x"] = {
"frequency": positive_frequencies[harmonic_index],
"fft_magnitude": fft_magnitude[harmonic_index],
}
return fundamental_freq, fundamental_magnitude, harmonics
# Main measurement function
def measure_frequencies():
with nidaqmx.Task() as task:
# Add analog input channels with proper configuration
for channel in CHANNELS:
task.ai_channels.add_ai_voltage_chan(
channel,
terminal_config=TerminalConfiguration.RSE,
min_val=0.0, # Adjust based on expected range
max_val=4.5 # Adjust based on expected range
)
# Configure sample clock
task.timing.cfg_samp_clk_timing(SAMPLE_RATE, samps_per_chan=SAMPLES_PER_CHANNEL)
print("Starting SSVEP validation. Press Ctrl+C to stop.")
# Open a CSV file to log data
with open("ssvep_validation_log.csv", "w", newline="") as csvfile:
csvwriter = csv.writer(csvfile)
# Write header row
header = ["Channel", "Fundamental Frequency (Hz)", "Fundamental FFT Magnitude"]
for n in range(2, HARMONIC_COUNT + 1):
header.append(f"Harmonic {n}x Frequency (Hz)")
header.append(f"Harmonic {n}x FFT Magnitude")
header.extend(["Stability (Variance)", "Amplitude"])
csvwriter.writerow(header)
try:
frequency_history = {channel: [] for channel in CHANNELS}
while True:
data = np.array(task.read(number_of_samples_per_channel=SAMPLES_PER_CHANNEL))
# Process data and calculate frequencies
for i, signal in enumerate(data):
# Apply band-pass filter
filtered_signal = bandpass_filter(signal, FREQ_RANGE[0], FREQ_RANGE[1], SAMPLE_RATE)
amplitude = np.max(filtered_signal) - np.min(filtered_signal)
if amplitude < AMPLITUDE_THRESHOLD:
print(f"Channel {CHANNELS[i]}: Weak Signal Detected (Amplitude = {amplitude:.3f})")
continue
# Analyze frequency and harmonics
fundamental_freq, fundamental_magnitude, harmonics = analyze_frequency_and_harmonics(filtered_signal, SAMPLE_RATE)
# Record frequency for stability analysis
frequency_history[CHANNELS[i]].append(fundamental_freq)
if len(frequency_history[CHANNELS[i]]) > 10: # Keep the last 10 readings
frequency_history[CHANNELS[i]] = frequency_history[CHANNELS[i]][-10:]
# Calculate stability (variance over time)
stability = np.var(frequency_history[CHANNELS[i]])
# Output results
print(f"Channel {CHANNELS[i]:<10}:")
print(f" Fundamental Frequency: {fundamental_freq:.2f} Hz (FFT Magnitude: {fundamental_magnitude:.3f})")
row = [CHANNELS[i], fundamental_freq, fundamental_magnitude]
for harmonic, info in harmonics.items():
print(f" Harmonic {harmonic}: {info['frequency']:.2f} Hz (FFT Magnitude: {info['fft_magnitude']:.3f})")
row.extend([info['frequency'], info['fft_magnitude']])
print(f" Stability (Variance): {stability:.3f}")
print(f" Amplitude: {amplitude:.3f}")
row.extend([stability, amplitude])
# Write data to CSV
csvwriter.writerow(row)
print("-" * 50)
time.sleep(0.5)
except KeyboardInterrupt:
print("SSVEP validation stopped.")
if __name__ == "__main__":
measure_frequencies()