Spectrum Analysis Tools - contains tools to estimate Power Spectral Densities using methods based on Fourier transform, Parametric methods or eigenvalues analysis
—
Comprehensive collection of window functions for signal processing applications. Windows are used to reduce spectral leakage in Fourier analysis, control sidelobe levels, and provide different trade-offs between main lobe width and sidelobe suppression.
High-level interface for creating and managing window functions with automatic parameter validation.
def create_window(N, name=None, **kargs):
"""
Create window function with specified parameters.
Parameters:
- N: int, window length
- name: str, window function name
- **kargs: dict, window-specific parameters
Returns:
array: Window function values
"""
class Window(N, name=None, **kargs):
"""
Window function generator and analyzer.
Parameters:
- N: int, window length
- name: str, window type ('hamming', 'hanning', 'kaiser', etc.)
- **kargs: dict, window parameters (beta, alpha, etc.)
Methods:
- plot(): visualize window and its frequency response
- info(): display window properties
"""
def enbw(data):
"""
Calculate equivalent noise bandwidth of window function.
Parameters:
- data: array-like, window function values
Returns:
float: Equivalent noise bandwidth (normalized)
"""
def window_visu(N=51, name="hamming", **kargs):
"""
Visualize window function and its frequency response.
Parameters:
- N: int, window length
- name: str, window function name
- **kargs: dict, window parameters
Returns:
None: displays plots directly
"""Standard window functions commonly used in signal processing applications.
def window_rectangle(N):
"""
Rectangular (boxcar) window.
Parameters:
- N: int, window length
Returns:
array: Rectangular window values
"""
def window_hamming(N):
"""
Hamming window with optimized coefficients.
Parameters:
- N: int, window length
Returns:
array: Hamming window values
"""
def window_hann(N):
"""
Hann (Hanning) window - raised cosine.
Parameters:
- N: int, window length
Returns:
array: Hann window values
"""
def window_bartlett(N):
"""
Bartlett (triangular) window.
Parameters:
- N: int, window length
Returns:
array: Bartlett window values
"""
def window_blackman(N, alpha=0.16):
"""
Blackman window with adjustable parameter.
Parameters:
- N: int, window length
- alpha: float, window parameter (0.16 for standard Blackman)
Returns:
array: Blackman window values
"""
def window_kaiser(N, beta=8.6):
"""
Kaiser window using modified Bessel function.
Parameters:
- N: int, window length
- beta: float, shape parameter (higher = more suppression)
Returns:
array: Kaiser window values
"""Advanced window functions for specific applications and requirements.
def window_gaussian(N, alpha=2.5):
"""
Gaussian window for smooth spectral characteristics.
Parameters:
- N: int, window length
- alpha: float, shape parameter (higher = narrower window)
Returns:
array: Gaussian window values
"""
def window_chebwin(N, attenuation=50):
"""
Chebyshev window with specified sidelobe attenuation.
Parameters:
- N: int, window length
- attenuation: float, sidelobe attenuation in dB
Returns:
array: Chebyshev window values
"""
def window_taylor(N, nbar=4, sll=-30):
"""
Taylor window for array processing applications.
Parameters:
- N: int, window length
- nbar: int, number of nearly equal sidelobes
- sll: float, sidelobe level in dB
Returns:
array: Taylor window values
"""
def window_flattop(N, mode="symmetric", precision=None):
"""
Flat-top window for amplitude measurements.
Parameters:
- N: int, window length
- mode: str, symmetry mode ('symmetric', 'periodic')
- precision: str, precision level (None, 'octave')
Returns:
array: Flat-top window values
"""
def window_tukey(N, r=0.5):
"""
Tukey (tapered cosine) window.
Parameters:
- N: int, window length
- r: float, taper ratio (0=rectangular, 1=Hann)
Returns:
array: Tukey window values
"""Additional window functions for specialized applications.
def window_cosine(N):
"""
Simple cosine window.
Parameters:
- N: int, window length
Returns:
array: Cosine window values
"""
def window_lanczos(N):
"""
Lanczos window (sinc function).
Parameters:
- N: int, window length
Returns:
array: Lanczos window values
"""
def window_bartlett_hann(N):
"""
Bartlett-Hann window (hybrid design).
Parameters:
- N: int, window length
Returns:
array: Bartlett-Hann window values
"""
def window_nuttall(N):
"""
Nuttall window (4-term Blackman-Harris).
Parameters:
- N: int, window length
Returns:
array: Nuttall window values
"""
def window_blackman_nuttall(N):
"""
Blackman-Nuttall window.
Parameters:
- N: int, window length
Returns:
array: Blackman-Nuttall window values
"""
def window_blackman_harris(N):
"""
Blackman-Harris window for low sidelobes.
Parameters:
- N: int, window length
Returns:
array: Blackman-Harris window values
"""
def window_bohman(N):
"""
Bohman window (convolution of triangular functions).
Parameters:
- N: int, window length
Returns:
array: Bohman window values
"""
def window_parzen(N):
"""
Parzen window (4th-order B-spline).
Parameters:
- N: int, window length
Returns:
array: Parzen window values
"""Window functions based on probability density functions.
def window_riesz(N):
"""
Riesz window.
Parameters:
- N: int, window length
Returns:
array: Riesz window values
"""
def window_riemann(N):
"""
Riemann window.
Parameters:
- N: int, window length
Returns:
array: Riemann window values
"""
def window_poisson(N, alpha=2):
"""
Poisson window (exponential).
Parameters:
- N: int, window length
- alpha: float, decay parameter
Returns:
array: Poisson window values
"""
def window_poisson_hanning(N, alpha=2):
"""
Poisson-Hanning hybrid window.
Parameters:
- N: int, window length
- alpha: float, Poisson parameter
Returns:
array: Poisson-Hanning window values
"""
def window_cauchy(N, alpha=3):
"""
Cauchy window (Lorentzian).
Parameters:
- N: int, window length
- alpha: float, shape parameter
Returns:
array: Cauchy window values
"""import spectrum
import numpy as np
import matplotlib.pyplot as plt
# Compare common window functions
N = 64
windows = {
'Rectangular': spectrum.window_rectangle(N),
'Hamming': spectrum.window_hamming(N),
'Hanning': spectrum.window_hann(N),
'Blackman': spectrum.window_blackman(N),
'Kaiser (β=8.6)': spectrum.window_kaiser(N, beta=8.6),
'Gaussian': spectrum.window_gaussian(N, alpha=2.5)
}
# Plot window shapes
plt.figure(figsize=(15, 10))
plt.subplot(2, 2, 1)
for name, window in windows.items():
plt.plot(window, label=name, linewidth=2)
plt.title('Window Functions (Time Domain)')
plt.xlabel('Sample')
plt.ylabel('Amplitude')
plt.legend()
plt.grid(True)
# Plot frequency responses
plt.subplot(2, 2, 2)
NFFT = 512
freqs = np.linspace(0, 0.5, NFFT//2)
for name, window in windows.items():
# Zero-pad and compute FFT
windowed_padded = np.concatenate([window, np.zeros(NFFT - N)])
W = np.fft.fft(windowed_padded)
W_mag = 20 * np.log10(np.abs(W[:NFFT//2]) / np.max(np.abs(W)))
plt.plot(freqs, W_mag, label=name, linewidth=2)
plt.title('Window Frequency Responses')
plt.xlabel('Normalized Frequency')
plt.ylabel('Magnitude (dB)')
plt.ylim(-120, 10)
plt.legend()
plt.grid(True)
# Compute and display window properties
plt.subplot(2, 2, 3)
properties = {}
for name, window in windows.items():
enbw_val = spectrum.enbw(window)
coherent_gain = np.mean(window)
processing_gain = np.sum(window**2) / len(window)
properties[name] = [enbw_val, coherent_gain, processing_gain]
prop_names = ['ENBW', 'Coherent Gain', 'Processing Gain']
x_pos = np.arange(len(windows))
for i, prop in enumerate(prop_names):
values = [properties[name][i] for name in windows.keys()]
plt.subplot(2, 2, 3)
if i == 0: # ENBW
plt.bar(x_pos, values, alpha=0.7, label=prop)
plt.xticks(x_pos, list(windows.keys()), rotation=45)
plt.ylabel('ENBW')
plt.title('Equivalent Noise Bandwidth')
plt.grid(True)
plt.subplot(2, 2, 4)
# Detailed properties table
props_text = "Window Properties:\n\n"
for name in windows.keys():
enbw_val, cg, pg = properties[name]
props_text += f"{name}:\n"
props_text += f" ENBW: {enbw_val:.3f}\n"
props_text += f" Coherent Gain: {cg:.3f}\n"
props_text += f" Processing Gain: {pg:.3f}\n\n"
plt.text(0.05, 0.95, props_text, transform=plt.gca().transAxes,
fontfamily='monospace', fontsize=8, verticalalignment='top')
plt.axis('off')
plt.tight_layout()
plt.show()import spectrum
import numpy as np
import matplotlib.pyplot as plt
# Study Kaiser window for different β values
N = 128
beta_values = [0, 2, 5, 8.6, 12, 20]
colors = plt.cm.viridis(np.linspace(0, 1, len(beta_values)))
plt.figure(figsize=(15, 8))
# Time domain
plt.subplot(1, 3, 1)
for i, beta in enumerate(beta_values):
kaiser_win = spectrum.window_kaiser(N, beta=beta)
plt.plot(kaiser_win, color=colors[i], linewidth=2,
label=f'β = {beta}')
plt.title('Kaiser Windows - Time Domain')
plt.xlabel('Sample')
plt.ylabel('Amplitude')
plt.legend()
plt.grid(True)
# Frequency domain
plt.subplot(1, 3, 2)
NFFT = 1024
freqs = np.linspace(0, 0.5, NFFT//2)
for i, beta in enumerate(beta_values):
kaiser_win = spectrum.window_kaiser(N, beta=beta)
# Zero-pad and compute FFT
padded = np.concatenate([kaiser_win, np.zeros(NFFT - N)])
W = np.fft.fft(padded)
W_mag = 20 * np.log10(np.abs(W[:NFFT//2]) / np.max(np.abs(W)))
plt.plot(freqs, W_mag, color=colors[i], linewidth=2,
label=f'β = {beta}')
plt.title('Kaiser Windows - Frequency Domain')
plt.xlabel('Normalized Frequency')
plt.ylabel('Magnitude (dB)')
plt.ylim(-140, 10)
plt.legend()
plt.grid(True)
# Properties vs β
plt.subplot(1, 3, 3)
beta_range = np.linspace(0, 20, 50)
enbw_values = []
main_lobe_width = []
peak_sidelobe = []
for beta in beta_range:
kaiser_win = spectrum.window_kaiser(N, beta=beta)
enbw_val = spectrum.enbw(kaiser_win)
enbw_values.append(enbw_val)
# Compute frequency response for main lobe and sidelobe analysis
padded = np.concatenate([kaiser_win, np.zeros(1024 - N)])
W = np.fft.fft(padded)
W_mag = np.abs(W[:512])
W_mag_db = 20 * np.log10(W_mag / np.max(W_mag))
# Find main lobe width (3dB points)
peak_idx = np.argmax(W_mag)
main_lobe_3db = np.where(W_mag_db >= -3)[0]
main_lobe_width.append(len(main_lobe_3db))
# Find peak sidelobe level
# Find first null after main lobe
first_null = peak_idx + np.argmin(W_mag[peak_idx:peak_idx+50])
if first_null + 10 < len(W_mag_db):
sidelobe_region = W_mag_db[first_null+5:]
peak_sidelobe.append(np.max(sidelobe_region))
else:
peak_sidelobe.append(-100)
plt.plot(beta_range, enbw_values, 'b-', linewidth=2, label='ENBW')
plt.xlabel('Kaiser β Parameter')
plt.ylabel('ENBW')
plt.title('Kaiser Window Properties vs β')
plt.grid(True)
# Add second y-axis for sidelobe level
ax2 = plt.gca().twinx()
ax2.plot(beta_range, peak_sidelobe, 'r--', linewidth=2, label='Peak Sidelobe (dB)')
ax2.set_ylabel('Peak Sidelobe Level (dB)')
ax2.set_ylim(-120, -20)
# Combined legend
lines1, labels1 = plt.gca().get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
plt.gca().legend(lines1 + lines2, labels1 + labels2, loc='center right')
plt.tight_layout()
plt.show()
# Print some specific values
print("Kaiser Window Design Guidelines:")
print("β = 0: Rectangular window")
print("β = 5: Good compromise (≈ Hamming)")
print("β = 8.6: Similar to Blackman")
print("β = 12: High sidelobe suppression")
print("β > 15: Very high suppression, wide main lobe")import spectrum
import numpy as np
import matplotlib.pyplot as plt
def demonstrate_windowing_effects(signal, windows_dict, title=""):
"""Demonstrate the effect of different windows on spectral analysis."""
plt.figure(figsize=(15, 12))
# Plot original signal
plt.subplot(3, 3, 1)
plt.plot(signal)
plt.title(f'{title} - Time Domain')
plt.xlabel('Sample')
plt.ylabel('Amplitude')
plt.grid(True)
# Compute and plot windowed signals and spectra
for i, (window_name, window_func) in enumerate(windows_dict.items()):
# Apply window
windowed_signal = signal * window_func
# Compute FFT
N_fft = 1024
fft_result = np.fft.fft(windowed_signal, N_fft)
freqs = np.linspace(0, 0.5, N_fft//2)
psd = np.abs(fft_result[:N_fft//2])**2
psd_db = 10 * np.log10(psd / np.max(psd))
# Plot windowed signal
plt.subplot(3, 3, 2 + i)
plt.plot(windowed_signal, 'b-', alpha=0.7)
plt.plot(window_func, 'r--', alpha=0.7, label='Window')
plt.title(f'{window_name} Windowed')
plt.xlabel('Sample')
plt.grid(True)
if i == 0:
plt.legend()
# Plot spectrum
plt.subplot(3, 3, 6 + i)
plt.plot(freqs, psd_db, linewidth=2)
plt.title(f'{window_name} Spectrum')
plt.xlabel('Normalized Frequency')
plt.ylabel('PSD (dB)')
plt.ylim(-80, 5)
plt.grid(True)
plt.tight_layout()
plt.show()
# Test Case 1: Single sinusoid (spectral leakage)
N = 128
n = np.arange(N)
f = 0.1234 # Non-integer number of cycles
signal1 = np.sin(2 * np.pi * f * n)
windows1 = {
'Rectangular': spectrum.window_rectangle(N),
'Hamming': spectrum.window_hamming(N),
'Kaiser β=8.6': spectrum.window_kaiser(N, beta=8.6),
'Blackman': spectrum.window_blackman(N)
}
demonstrate_windowing_effects(signal1, windows1, "Single Sinusoid (Leakage Test)")
# Test Case 2: Two close sinusoids (resolution test)
f1, f2 = 0.2, 0.22 # Close frequencies
signal2 = np.sin(2*np.pi*f1*n) + np.sin(2*np.pi*f2*n)
windows2 = {
'Rectangular': spectrum.window_rectangle(N),
'Hanning': spectrum.window_hann(N),
'Flat-top': spectrum.window_flattop(N),
'Gaussian': spectrum.window_gaussian(N, alpha=2.5)
}
demonstrate_windowing_effects(signal2, windows2, "Two Close Sinusoids (Resolution Test)")
# Test Case 3: Amplitude measurement test
signal3 = 2.5 * np.sin(2*np.pi*0.1*n) + 1.8 * np.sin(2*np.pi*0.3*n)
windows3 = {
'Rectangular': spectrum.window_rectangle(N),
'Flat-top': spectrum.window_flattop(N),
'Kaiser β=12': spectrum.window_kaiser(N, beta=12),
'Nuttall': spectrum.window_nuttall(N)
}
demonstrate_windowing_effects(signal3, windows3, "Amplitude Measurement Test")
# Window selection guidelines
print("\n" + "="*60)
print("WINDOW SELECTION GUIDELINES")
print("="*60)
print("Application | Recommended Windows")
print("-" * 60)
print("General purpose | Hamming, Hanning")
print("Spectral analysis | Kaiser (β=8.6), Blackman")
print("Amplitude measurements | Flat-top, Kaiser (β=12)")
print("Frequency resolution | Rectangular, Tukey")
print("Sidelobe suppression | Chebyshev, Kaiser (β>10)")
print("Transient analysis | Gaussian, Hamming")
print("Array processing | Taylor, Chebyshev")
print("Low noise floor | Blackman-Harris, Nuttall")import spectrum
import numpy as np
import matplotlib.pyplot as plt
def design_custom_window(N, target_sidelobe_db=-60):
"""Design custom Kaiser window for target sidelobe level."""
# Kaiser window design formula
if target_sidelobe_db > -21:
beta = 0
elif target_sidelobe_db >= -50:
beta = 0.5842 * (abs(target_sidelobe_db) - 21)**0.4 + 0.07886 * (abs(target_sidelobe_db) - 21)
else:
beta = 0.1102 * (abs(target_sidelobe_db) - 8.7)
return spectrum.window_kaiser(N, beta=beta), beta
def optimize_window_parameters():
"""Demonstrate window parameter optimization."""
N = 64
target_levels = [-40, -60, -80, -100]
plt.figure(figsize=(15, 10))
# Design windows for different sidelobe targets
plt.subplot(2, 2, 1)
for target_db in target_levels:
custom_window, beta_used = design_custom_window(N, target_db)
plt.plot(custom_window, label=f'Target: {target_db}dB (β={beta_used:.2f})', linewidth=2)
plt.title('Custom Kaiser Windows for Different Sidelobe Targets')
plt.xlabel('Sample')
plt.ylabel('Amplitude')
plt.legend()
plt.grid(True)
# Frequency responses
plt.subplot(2, 2, 2)
NFFT = 1024
freqs = np.linspace(0, 0.5, NFFT//2)
for target_db in target_levels:
custom_window, beta_used = design_custom_window(N, target_db)
padded = np.concatenate([custom_window, np.zeros(NFFT - N)])
W = np.fft.fft(padded)
W_mag_db = 20 * np.log10(np.abs(W[:NFFT//2]) / np.max(np.abs(W)))
plt.plot(freqs, W_mag_db, label=f'{target_db}dB target', linewidth=2)
plt.title('Frequency Responses of Custom Windows')
plt.xlabel('Normalized Frequency')
plt.ylabel('Magnitude (dB)')
plt.ylim(-120, 10)
plt.legend()
plt.grid(True)
# Parameter relationships
plt.subplot(2, 2, 3)
target_range = np.linspace(-20, -120, 100)
beta_values = []
enbw_values = []
for target in target_range:
window, beta = design_custom_window(N, target)
beta_values.append(beta)
enbw_values.append(spectrum.enbw(window))
plt.plot(target_range, beta_values, 'b-', linewidth=2, label='Kaiser β')
plt.xlabel('Target Sidelobe Level (dB)')
plt.ylabel('Kaiser β Parameter')
plt.title('Kaiser β vs Sidelobe Target')
plt.grid(True)
# ENBW vs sidelobe level
plt.subplot(2, 2, 4)
plt.plot(target_range, enbw_values, 'r-', linewidth=2)
plt.xlabel('Target Sidelobe Level (dB)')
plt.ylabel('Equivalent Noise Bandwidth')
plt.title('ENBW vs Sidelobe Level Trade-off')
plt.grid(True)
plt.tight_layout()
plt.show()
# Demonstrate window class usage
print("Using Window Class for Analysis:")
print("-" * 40)
# Create window object
N = 64
w = spectrum.Window(N, name='kaiser', beta=10.0)
# Get window properties
print(f"Window type: {w.name if hasattr(w, 'name') else 'Kaiser'}")
print(f"Window length: {N}")
print(f"ENBW: {spectrum.enbw(w.data if hasattr(w, 'data') else spectrum.window_kaiser(N, 10.0)):.4f}")
# Visualize multiple windows with the visualization function
print("\nGenerating window visualizations...")
spectrum.window_visu(N=64, name="hamming")
spectrum.window_visu(N=64, name="kaiser", beta=8.6)
# Run the optimization demonstration
optimize_window_parameters()Install with Tessl CLI
npx tessl i tessl/pypi-spectrum