update
This commit is contained in:
		
							parent
							
								
									49189c3f48
								
							
						
					
					
						commit
						6b66a2f3da
					
				
					 3 changed files with 506 additions and 1 deletions
				
			
		| 
						 | 
					@ -1 +1 @@
 | 
				
			||||||
__version__ = '0.1.0'
 | 
					__version__ = '0.1.1'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										504
									
								
								mysignal/phasefreq.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										504
									
								
								mysignal/phasefreq.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,504 @@
 | 
				
			||||||
 | 
					import warnings
 | 
				
			||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					import pandas as pd
 | 
				
			||||||
 | 
					import pywt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from scipy.fft import fft, fftfreq
 | 
				
			||||||
 | 
					from scipy.signal import find_peaks, hilbert, butter, filtfilt, correlate
 | 
				
			||||||
 | 
					from scipy.stats import linregress
 | 
				
			||||||
 | 
					from scipy.optimize import curve_fit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def filter_signal_by_modes(time, signal, num_modes=1, bandwidth_factor=0.1):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Filtre un signal pour extraire ses composantes fréquentielles dominantes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Parameters:
 | 
				
			||||||
 | 
					        time (array-like): Array contenant les données temporelles.
 | 
				
			||||||
 | 
					        signal (array-like): Array contenant les données du signal.
 | 
				
			||||||
 | 
					        num_modes (int): Nombre de modes fréquentiels à extraire.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Returns:
 | 
				
			||||||
 | 
					        tuple: (filtered_signals, frequencies, time_filtered)
 | 
				
			||||||
 | 
					               filtered_signals: Liste des signaux filtrés pour chaque mode
 | 
				
			||||||
 | 
					               frequencies: Fréquences correspondantes à chaque mode
 | 
				
			||||||
 | 
					               time_filtered: Temps après filtrage (même que l'entrée)
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    # Convertir en arrays numpy
 | 
				
			||||||
 | 
					    time = np.array(time)
 | 
				
			||||||
 | 
					    signal = np.array(signal)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculer la période d'échantillonnage et la fréquence
 | 
				
			||||||
 | 
					    dt = np.mean(np.diff(time))
 | 
				
			||||||
 | 
					    fs = 1 / dt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Supprimer la composante DC
 | 
				
			||||||
 | 
					    signal_clean = signal - np.mean(signal)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculer la FFT
 | 
				
			||||||
 | 
					    n = len(time)
 | 
				
			||||||
 | 
					    fft_signal = fft(signal_clean)
 | 
				
			||||||
 | 
					    freqs = fftfreq(n, dt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Prendre seulement les fréquences positives
 | 
				
			||||||
 | 
					    positive_mask = freqs > 0
 | 
				
			||||||
 | 
					    freqs_pos = freqs[positive_mask]
 | 
				
			||||||
 | 
					    fft_pos = fft_signal[positive_mask]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Trouver les pics dans le spectre
 | 
				
			||||||
 | 
					    magnitude = np.abs(fft_pos)
 | 
				
			||||||
 | 
					    peaks, _ = find_peaks(magnitude, height=0.1*np.max(magnitude))
 | 
				
			||||||
 | 
					    peak_freqs = freqs_pos[peaks]
 | 
				
			||||||
 | 
					    peak_mags = magnitude[peaks]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Trier les pics par magnitude et sélectionner les num_modes plus importants
 | 
				
			||||||
 | 
					    idx = np.argsort(peak_mags)[-num_modes:]
 | 
				
			||||||
 | 
					    dominant_freqs = peak_freqs[idx]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Trier les fréquences par ordre croissant
 | 
				
			||||||
 | 
					    dominant_freqs.sort()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Initialiser la liste des signaux filtrés
 | 
				
			||||||
 | 
					    filtered_signals = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Pour chaque fréquence dominante, appliquer un filtre passe-bande
 | 
				
			||||||
 | 
					    for freq in dominant_freqs:
 | 
				
			||||||
 | 
					        # Définir la bande passante (10% de la fréquence centrale de chaque côté)
 | 
				
			||||||
 | 
					        bandwidth = bandwidth_factor * freq
 | 
				
			||||||
 | 
					        lowcut = freq - bandwidth
 | 
				
			||||||
 | 
					        highcut = freq + bandwidth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Éviter les fréquences négatives ou supérieures à la fréquence de Nyquist
 | 
				
			||||||
 | 
					        nyq = 0.5 * fs
 | 
				
			||||||
 | 
					        if lowcut <= 0:
 | 
				
			||||||
 | 
					            lowcut = 0.01
 | 
				
			||||||
 | 
					        if highcut >= nyq:
 | 
				
			||||||
 | 
					            highcut = nyq - 0.01
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Créer le filtre Butterworth
 | 
				
			||||||
 | 
					        low = lowcut / nyq
 | 
				
			||||||
 | 
					        high = highcut / nyq
 | 
				
			||||||
 | 
					        b, a = butter(4, [low, high], btype='band')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Appliquer le filtre (filtfilt pour éviter le déphasage)
 | 
				
			||||||
 | 
					        filtered_signal = filtfilt(b, a, signal_clean)
 | 
				
			||||||
 | 
					        filtered_signals.append(filtered_signal)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Le temps reste le même après filtrage
 | 
				
			||||||
 | 
					    time_filtered = time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return filtered_signals, dominant_freqs, time_filtered
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def analyze_signal_Hilbert(time, e_signal, s_signal):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Analyzes the dominant frequencies, phase shift, time shift, and periods between two signals e_signal(t) and s_signal(t) from a DataFrame.
 | 
				
			||||||
 | 
					    Uses Hilbert transform method for phase shift calculation (single mode only).
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    # Extract signals and time
 | 
				
			||||||
 | 
					    #time = df[time_column].values
 | 
				
			||||||
 | 
					    #e_signal = df[e_signal_column].values
 | 
				
			||||||
 | 
					    #s_signal = df[s_signal_column].values
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate sampling period and rate
 | 
				
			||||||
 | 
					    dt = np.mean(np.diff(time))
 | 
				
			||||||
 | 
					    fs = 1 / dt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Remove mean to eliminate DC component
 | 
				
			||||||
 | 
					    e_signal_clean = e_signal - np.mean(e_signal)
 | 
				
			||||||
 | 
					    s_signal_clean = s_signal - np.mean(s_signal)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Compute FFT
 | 
				
			||||||
 | 
					    n = len(time)
 | 
				
			||||||
 | 
					    e_fft = fft(e_signal_clean)
 | 
				
			||||||
 | 
					    s_fft = fft(s_signal_clean)
 | 
				
			||||||
 | 
					    freqs = fftfreq(n, dt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Get positive frequencies only
 | 
				
			||||||
 | 
					    positive_mask = freqs > 0
 | 
				
			||||||
 | 
					    freqs_pos = freqs[positive_mask]
 | 
				
			||||||
 | 
					    e_fft_pos = e_fft[positive_mask]
 | 
				
			||||||
 | 
					    s_fft_pos = s_fft[positive_mask]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Find dominant frequencies using peak detection
 | 
				
			||||||
 | 
					    e_magnitude = np.abs(e_fft_pos)
 | 
				
			||||||
 | 
					    s_magnitude = np.abs(s_fft_pos)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Find peaks for e signal
 | 
				
			||||||
 | 
					    e_peaks, _ = find_peaks(e_magnitude, height=0.1*np.max(e_magnitude))
 | 
				
			||||||
 | 
					    e_peak_freqs = freqs_pos[e_peaks]
 | 
				
			||||||
 | 
					    e_peak_mags = e_magnitude[e_peaks]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Find peaks for s signal
 | 
				
			||||||
 | 
					    s_peaks, _ = find_peaks(s_magnitude, height=0.1*np.max(s_magnitude))
 | 
				
			||||||
 | 
					    s_peak_freqs = freqs_pos[s_peaks]
 | 
				
			||||||
 | 
					    s_peak_mags = s_magnitude[s_peaks]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Sort peaks by magnitude and select the top one
 | 
				
			||||||
 | 
					    e_idx = np.argsort(e_peak_mags)[-1]
 | 
				
			||||||
 | 
					    s_idx = np.argsort(s_peak_mags)[-1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    freq_e = e_peak_freqs[e_idx]
 | 
				
			||||||
 | 
					    freq_s = s_peak_freqs[s_idx]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Check if frequencies match within 1%
 | 
				
			||||||
 | 
					    if np.abs(freq_e - freq_s) / ((freq_e + freq_s)/2) > 0.01:
 | 
				
			||||||
 | 
					        raise ValueError("Frequency difference exceeds 1%. Phase shift cannot be determined.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate mean frequency for phase shift calculation
 | 
				
			||||||
 | 
					    mean_freq = (freq_e + freq_s) / 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Create a bandpass filter around the mean frequency
 | 
				
			||||||
 | 
					    nyq = 0.5 * fs
 | 
				
			||||||
 | 
					    low = (mean_freq - 0.1 * mean_freq) / nyq
 | 
				
			||||||
 | 
					    high = (mean_freq + 0.1 * mean_freq) / nyq
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if low <= 0:
 | 
				
			||||||
 | 
					        low = 0.01
 | 
				
			||||||
 | 
					    if high >= 1:
 | 
				
			||||||
 | 
					        high = 0.99
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    b, a = butter(4, [low, high], btype='band')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Filter both signals
 | 
				
			||||||
 | 
					    e_filtered = filtfilt(b, a, e_signal_clean)
 | 
				
			||||||
 | 
					    s_filtered = filtfilt(b, a, s_signal_clean)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Apply Hilbert transform to filtered signals
 | 
				
			||||||
 | 
					    e_analytic_filtered = hilbert(e_filtered)
 | 
				
			||||||
 | 
					    s_analytic_filtered = hilbert(s_filtered)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Extract phase
 | 
				
			||||||
 | 
					    e_phase_filtered = np.unwrap(np.angle(e_analytic_filtered))
 | 
				
			||||||
 | 
					    s_phase_filtered = np.unwrap(np.angle(s_analytic_filtered))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate phase difference
 | 
				
			||||||
 | 
					    phase_diff = s_phase_filtered - e_phase_filtered
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Average phase difference over the stable part of the signal
 | 
				
			||||||
 | 
					    n_stable = len(phase_diff) // 4
 | 
				
			||||||
 | 
					    stable_phase_diff = phase_diff[n_stable:-n_stable]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate mean phase difference
 | 
				
			||||||
 | 
					    mean_phase_diff = np.mean(stable_phase_diff)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Normalize to [-π, π]
 | 
				
			||||||
 | 
					    mean_phase_diff = np.arctan2(np.sin(mean_phase_diff), np.cos(mean_phase_diff))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Convert to degrees
 | 
				
			||||||
 | 
					    phase_diff_deg = np.degrees(mean_phase_diff)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate time shift
 | 
				
			||||||
 | 
					    time_shift = mean_phase_diff / (2 * np.pi * mean_freq)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate periods
 | 
				
			||||||
 | 
					    period_e = 1 / freq_e
 | 
				
			||||||
 | 
					    period_s = 1 / freq_s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print(f'phase = {mean_phase_diff}')
 | 
				
			||||||
 | 
					    return (period_e, period_s, freq_e, freq_s,
 | 
				
			||||||
 | 
					            mean_phase_diff, phase_diff_deg, time_shift)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def analyze_signal_sinfit(time, e_signal, s_signal):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Analyzes the dominant frequencies, phase shift, time shift, and periods between two signals e_signal(t) and s_signal(t) from a DataFrame.
 | 
				
			||||||
 | 
					    Uses curve fitting method for phase shift calculation (single mode only).
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    # Extract signals and time
 | 
				
			||||||
 | 
					    #time = df[time_column].values
 | 
				
			||||||
 | 
					    #e_signal = df[e_signal_column].values
 | 
				
			||||||
 | 
					    #s_signal = df[s_signal_column].values
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate sampling period and rate
 | 
				
			||||||
 | 
					    dt = np.mean(np.diff(time))
 | 
				
			||||||
 | 
					    fs = 1 / dt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Remove mean to eliminate DC component
 | 
				
			||||||
 | 
					    e_signal_clean = e_signal - np.mean(e_signal)
 | 
				
			||||||
 | 
					    s_signal_clean = s_signal - np.mean(s_signal)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Compute FFT
 | 
				
			||||||
 | 
					    n = len(time)
 | 
				
			||||||
 | 
					    e_fft = fft(e_signal_clean)
 | 
				
			||||||
 | 
					    s_fft = fft(s_signal_clean)
 | 
				
			||||||
 | 
					    freqs = fftfreq(n, dt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Get positive frequencies only
 | 
				
			||||||
 | 
					    positive_mask = freqs > 0
 | 
				
			||||||
 | 
					    freqs_pos = freqs[positive_mask]
 | 
				
			||||||
 | 
					    e_fft_pos = e_fft[positive_mask]
 | 
				
			||||||
 | 
					    s_fft_pos = s_fft[positive_mask]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Find dominant frequencies using peak detection
 | 
				
			||||||
 | 
					    e_magnitude = np.abs(e_fft_pos)
 | 
				
			||||||
 | 
					    s_magnitude = np.abs(s_fft_pos)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Find peaks for e signal
 | 
				
			||||||
 | 
					    e_peaks, _ = find_peaks(e_magnitude, height=0.1*np.max(e_magnitude))
 | 
				
			||||||
 | 
					    e_peak_freqs = freqs_pos[e_peaks]
 | 
				
			||||||
 | 
					    e_peak_mags = e_magnitude[e_peaks]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Find peaks for s signal
 | 
				
			||||||
 | 
					    s_peaks, _ = find_peaks(s_magnitude, height=0.1*np.max(s_magnitude))
 | 
				
			||||||
 | 
					    s_peak_freqs = freqs_pos[s_peaks]
 | 
				
			||||||
 | 
					    s_peak_mags = s_magnitude[s_peaks]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Sort peaks by magnitude and select the top one
 | 
				
			||||||
 | 
					    e_idx = np.argsort(e_peak_mags)[-1]
 | 
				
			||||||
 | 
					    s_idx = np.argsort(s_peak_mags)[-1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    freq_e = e_peak_freqs[e_idx]
 | 
				
			||||||
 | 
					    freq_s = s_peak_freqs[s_idx]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Check if frequencies match within 1%
 | 
				
			||||||
 | 
					    if np.abs(freq_e - freq_s) / ((freq_e + freq_s)/2) > 0.01:
 | 
				
			||||||
 | 
					        raise ValueError("Frequency difference exceeds 1%. Phase shift cannot be determined.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate mean frequency for phase shift calculation
 | 
				
			||||||
 | 
					    mean_freq = (freq_e + freq_s) / 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Define sine function for fitting
 | 
				
			||||||
 | 
					    def sine_func(t, A, phi, offset):
 | 
				
			||||||
 | 
					        return A * np.sin(2 * np.pi * mean_freq * t + phi) + offset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Fit sine wave to e signal
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        popt_e, _ = curve_fit(sine_func, time, e_signal,
 | 
				
			||||||
 | 
					                             p0=[np.std(e_signal), 0, np.mean(e_signal)],
 | 
				
			||||||
 | 
					                             bounds=([0, -np.pi, -np.inf], [np.inf, np.pi, np.inf]))
 | 
				
			||||||
 | 
					    except:
 | 
				
			||||||
 | 
					        popt_e = [np.std(e_signal), 0, np.mean(e_signal)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Fit sine wave to s signal
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        popt_s, _ = curve_fit(sine_func, time, s_signal,
 | 
				
			||||||
 | 
					                             p0=[np.std(s_signal), 0, np.mean(s_signal)],
 | 
				
			||||||
 | 
					                             bounds=([0, -np.pi, -np.inf], [np.inf, np.pi, np.inf]))
 | 
				
			||||||
 | 
					    except:
 | 
				
			||||||
 | 
					        popt_s = [np.std(s_signal), 0, np.mean(s_signal)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate phase difference
 | 
				
			||||||
 | 
					    phase_diff = popt_s[1] - popt_e[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Normalize to [-π, π]
 | 
				
			||||||
 | 
					    phase_diff = np.arctan2(np.sin(phase_diff), np.cos(phase_diff))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Convert to degrees
 | 
				
			||||||
 | 
					    phase_diff_deg = np.degrees(phase_diff)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate time shift
 | 
				
			||||||
 | 
					    time_shift = phase_diff / (2 * np.pi * mean_freq)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate periods
 | 
				
			||||||
 | 
					    period_e = 1 / freq_e
 | 
				
			||||||
 | 
					    period_s = 1 / freq_s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print(f'phase = {phase_diff}')
 | 
				
			||||||
 | 
					    return (period_e, period_s, freq_e, freq_s,
 | 
				
			||||||
 | 
					            phase_diff, phase_diff_deg, time_shift)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def analyze_signal_cross_correlation(time, e_signal, s_signal):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Analyzes signals using cross-correlation method for phase shift calculation (single mode only).
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    # Extract signals and time
 | 
				
			||||||
 | 
					    #time = df[time_column].values
 | 
				
			||||||
 | 
					    #e_signal = df[e_signal_column].values
 | 
				
			||||||
 | 
					    #s_signal = df[s_signal_column].values
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate sampling period and rate
 | 
				
			||||||
 | 
					    dt = np.mean(np.diff(time))
 | 
				
			||||||
 | 
					    fs = 1 / dt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Remove mean to eliminate DC component
 | 
				
			||||||
 | 
					    e_signal_clean = e_signal - np.mean(e_signal)
 | 
				
			||||||
 | 
					    s_signal_clean = s_signal - np.mean(s_signal)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Compute FFT
 | 
				
			||||||
 | 
					    n = len(time)
 | 
				
			||||||
 | 
					    e_fft = fft(e_signal_clean)
 | 
				
			||||||
 | 
					    s_fft = fft(s_signal_clean)
 | 
				
			||||||
 | 
					    freqs = fftfreq(n, dt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Get positive frequencies only
 | 
				
			||||||
 | 
					    positive_mask = freqs > 0
 | 
				
			||||||
 | 
					    freqs_pos = freqs[positive_mask]
 | 
				
			||||||
 | 
					    e_fft_pos = e_fft[positive_mask]
 | 
				
			||||||
 | 
					    s_fft_pos = s_fft[positive_mask]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Find dominant frequencies using peak detection
 | 
				
			||||||
 | 
					    e_magnitude = np.abs(e_fft_pos)
 | 
				
			||||||
 | 
					    s_magnitude = np.abs(s_fft_pos)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Find peaks for e signal
 | 
				
			||||||
 | 
					    e_peaks, _ = find_peaks(e_magnitude, height=0.1*np.max(e_magnitude))
 | 
				
			||||||
 | 
					    e_peak_freqs = freqs_pos[e_peaks]
 | 
				
			||||||
 | 
					    e_peak_mags = e_magnitude[e_peaks]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Find peaks for s signal
 | 
				
			||||||
 | 
					    s_peaks, _ = find_peaks(s_magnitude, height=0.1*np.max(s_magnitude))
 | 
				
			||||||
 | 
					    s_peak_freqs = freqs_pos[s_peaks]
 | 
				
			||||||
 | 
					    s_peak_mags = s_magnitude[s_peaks]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Sort peaks by magnitude and select the top one
 | 
				
			||||||
 | 
					    e_idx = np.argsort(e_peak_mags)[-1]
 | 
				
			||||||
 | 
					    s_idx = np.argsort(s_peak_mags)[-1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    freq_e = e_peak_freqs[e_idx]
 | 
				
			||||||
 | 
					    freq_s = s_peak_freqs[s_idx]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Check if frequencies match within 1%
 | 
				
			||||||
 | 
					    if np.abs(freq_e - freq_s) / ((freq_e + freq_s)/2) > 0.01:
 | 
				
			||||||
 | 
					        raise ValueError("Frequency difference exceeds 1%. Phase shift cannot be determined.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate mean frequency for phase shift calculation
 | 
				
			||||||
 | 
					    mean_freq = (freq_e + freq_s) / 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Create a bandpass filter around the mean frequency
 | 
				
			||||||
 | 
					    nyq = 0.5 * fs
 | 
				
			||||||
 | 
					    low = (mean_freq - 0.1 * mean_freq) / nyq
 | 
				
			||||||
 | 
					    high = (mean_freq + 0.1 * mean_freq) / nyq
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if low <= 0:
 | 
				
			||||||
 | 
					        low = 0.01
 | 
				
			||||||
 | 
					    if high >= 1:
 | 
				
			||||||
 | 
					        high = 0.99
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    b, a = butter(4, [low, high], btype='band')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Filter both signals
 | 
				
			||||||
 | 
					    e_filtered = filtfilt(b, a, e_signal_clean)
 | 
				
			||||||
 | 
					    s_filtered = filtfilt(b, a, s_signal_clean)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Compute cross-correlation
 | 
				
			||||||
 | 
					    correlation = correlate(s_filtered, e_filtered, mode='full')
 | 
				
			||||||
 | 
					    lags = np.arange(-len(e_filtered) + 1, len(e_filtered))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Find the lag with maximum correlation
 | 
				
			||||||
 | 
					    max_lag = lags[np.argmax(correlation)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate time shift
 | 
				
			||||||
 | 
					    time_shift = max_lag * dt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate phase shift
 | 
				
			||||||
 | 
					    phase_diff = 2 * np.pi * mean_freq * time_shift
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Normalize to [-π, π]
 | 
				
			||||||
 | 
					    phase_diff = np.arctan2(np.sin(phase_diff), np.cos(phase_diff))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Convert to degrees
 | 
				
			||||||
 | 
					    phase_diff_deg = np.degrees(phase_diff)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate periods
 | 
				
			||||||
 | 
					    period_e = 1 / freq_e
 | 
				
			||||||
 | 
					    period_s = 1 / freq_s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print(f'phase = {phase_diff}')
 | 
				
			||||||
 | 
					    return (period_e, period_s, freq_e, freq_s,
 | 
				
			||||||
 | 
					            phase_diff, phase_diff_deg, time_shift)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def analyze_signal_wavelet(time, e_signal, s_signal):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Analyzes signals using wavelet transform for phase shift calculation (single mode only).
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    # Extract signals and time
 | 
				
			||||||
 | 
					    #time = df[time_column].values
 | 
				
			||||||
 | 
					    #e_signal = df[e_signal_column].values
 | 
				
			||||||
 | 
					    #s_signal = df[s_signal_column].values
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate sampling period and rate
 | 
				
			||||||
 | 
					    dt = np.mean(np.diff(time))
 | 
				
			||||||
 | 
					    fs = 1 / dt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Remove mean to eliminate DC component
 | 
				
			||||||
 | 
					    e_signal_clean = e_signal - np.mean(e_signal)
 | 
				
			||||||
 | 
					    s_signal_clean = s_signal - np.mean(s_signal)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Compute FFT
 | 
				
			||||||
 | 
					    n = len(time)
 | 
				
			||||||
 | 
					    e_fft = fft(e_signal_clean)
 | 
				
			||||||
 | 
					    s_fft = fft(s_signal_clean)
 | 
				
			||||||
 | 
					    freqs = fftfreq(n, dt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Get positive frequencies only
 | 
				
			||||||
 | 
					    positive_mask = freqs > 0
 | 
				
			||||||
 | 
					    freqs_pos = freqs[positive_mask]
 | 
				
			||||||
 | 
					    e_fft_pos = e_fft[positive_mask]
 | 
				
			||||||
 | 
					    s_fft_pos = s_fft[positive_mask]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Find dominant frequencies using peak detection
 | 
				
			||||||
 | 
					    e_magnitude = np.abs(e_fft_pos)
 | 
				
			||||||
 | 
					    s_magnitude = np.abs(s_fft_pos)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Find peaks for e signal
 | 
				
			||||||
 | 
					    e_peaks, _ = find_peaks(e_magnitude, height=0.1*np.max(e_magnitude))
 | 
				
			||||||
 | 
					    e_peak_freqs = freqs_pos[e_peaks]
 | 
				
			||||||
 | 
					    e_peak_mags = e_magnitude[e_peaks]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Find peaks for s signal
 | 
				
			||||||
 | 
					    s_peaks, _ = find_peaks(s_magnitude, height=0.1*np.max(s_magnitude))
 | 
				
			||||||
 | 
					    s_peak_freqs = freqs_pos[s_peaks]
 | 
				
			||||||
 | 
					    s_peak_mags = s_magnitude[s_peaks]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Sort peaks by magnitude and select the top one
 | 
				
			||||||
 | 
					    e_idx = np.argsort(e_peak_mags)[-1]
 | 
				
			||||||
 | 
					    s_idx = np.argsort(s_peak_mags)[-1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    freq_e = e_peak_freqs[e_idx]
 | 
				
			||||||
 | 
					    freq_s = s_peak_freqs[s_idx]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Check if frequencies match within 1%
 | 
				
			||||||
 | 
					    if np.abs(freq_e - freq_s) / ((freq_e + freq_s)/2) > 0.01:
 | 
				
			||||||
 | 
					        raise ValueError("Frequency difference exceeds 1%. Phase shift cannot be determined.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate mean frequency for phase shift calculation
 | 
				
			||||||
 | 
					    mean_freq = (freq_e + freq_s) / 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Perform continuous wavelet transform
 | 
				
			||||||
 | 
					    scales = np.arange(1, 128)
 | 
				
			||||||
 | 
					    coefficients_e, frequencies_e = pywt.cwt(e_signal_clean, scales, 'morl', sampling_period=dt)
 | 
				
			||||||
 | 
					    coefficients_s, frequencies_s = pywt.cwt(s_signal_clean, scales, 'morl', sampling_period=dt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Find the scale index closest to the mean frequency
 | 
				
			||||||
 | 
					    scale_idx = np.argmin(np.abs(frequencies_e - mean_freq))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Extract the wavelet coefficients at this scale
 | 
				
			||||||
 | 
					    coeffs_e = coefficients_e[scale_idx, :]
 | 
				
			||||||
 | 
					    coeffs_s = coefficients_s[scale_idx, :]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate the instantaneous phase using the Hilbert transform
 | 
				
			||||||
 | 
					    analytic_e = hilbert(coeffs_e)
 | 
				
			||||||
 | 
					    analytic_s = hilbert(coeffs_s)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    phase_e = np.unwrap(np.angle(analytic_e))
 | 
				
			||||||
 | 
					    phase_s = np.unwrap(np.angle(analytic_s))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate phase difference
 | 
				
			||||||
 | 
					    phase_diff = phase_s - phase_e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Average phase difference over the stable part of the signal
 | 
				
			||||||
 | 
					    n_stable = len(phase_diff) // 4
 | 
				
			||||||
 | 
					    stable_phase_diff = phase_diff[n_stable:-n_stable]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate mean phase difference
 | 
				
			||||||
 | 
					    mean_phase_diff = np.mean(stable_phase_diff)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Normalize to [-π, π]
 | 
				
			||||||
 | 
					    mean_phase_diff = np.arctan2(np.sin(mean_phase_diff), np.cos(mean_phase_diff))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Convert to degrees
 | 
				
			||||||
 | 
					    phase_diff_deg = np.degrees(mean_phase_diff)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate time shift
 | 
				
			||||||
 | 
					    time_shift = mean_phase_diff / (2 * np.pi * mean_freq)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Calculate periods
 | 
				
			||||||
 | 
					    period_e = 1 / freq_e
 | 
				
			||||||
 | 
					    period_s = 1 / freq_s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print(f'phase = {mean_phase_diff}')
 | 
				
			||||||
 | 
					    return (period_e, period_s, freq_e, freq_s,
 | 
				
			||||||
 | 
					            mean_phase_diff, phase_diff_deg, time_shift)
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,4 @@
 | 
				
			||||||
numpy
 | 
					numpy
 | 
				
			||||||
scipy
 | 
					scipy
 | 
				
			||||||
pandas
 | 
					pandas
 | 
				
			||||||
 | 
					PyWavelets
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue