0
# Frequency Domain Analysis
1
2
Spectral analysis tools for power spectral densities, amplitude spectral densities, and frequency-domain filtering. GWpy's FrequencySeries class provides comprehensive support for frequency-domain gravitational-wave data analysis and detector characterization.
3
4
## Capabilities
5
6
### FrequencySeries - Frequency Domain Data Class
7
8
Primary class for handling frequency-domain data including power spectral densities (PSDs), amplitude spectral densities (ASDs), and transfer functions.
9
10
```python { .api }
11
class FrequencySeries(Series):
12
def __init__(self, data, frequencies=None, f0=None, df=None,
13
unit=None, name=None, **kwargs):
14
"""
15
Create a new FrequencySeries.
16
17
Parameters:
18
- data: array-like, frequency series data values
19
- frequencies: array-like, frequency values for each sample
20
- f0: float, starting frequency (Hz)
21
- df: float, frequency spacing (Hz)
22
- unit: str or Unit, physical unit of the data
23
- name: str, name for this series
24
"""
25
26
@classmethod
27
def read(cls, source, format=None, **kwargs):
28
"""
29
Read FrequencySeries from file.
30
31
Parameters:
32
- source: str, path to data file
33
- format: str, file format ('txt', 'hdf5', 'ligolw')
34
35
Returns:
36
FrequencySeries object
37
"""
38
39
def plot(self, **kwargs):
40
"""
41
Create a plot of the frequency series.
42
43
Returns:
44
Plot object
45
"""
46
47
def zpk(self, zeros, poles, gain, **kwargs):
48
"""
49
Filter using zero-pole-gain representation.
50
51
Parameters:
52
- zeros: array-like, filter zeros
53
- poles: array-like, filter poles
54
- gain: float, filter gain
55
56
Returns:
57
Filtered FrequencySeries
58
"""
59
60
def crop(self, start=None, end=None, copy=True):
61
"""
62
Crop to specific frequency range.
63
64
Parameters:
65
- start: float, start frequency
66
- end: float, end frequency
67
- copy: bool, return copy or view
68
69
Returns:
70
Cropped FrequencySeries
71
"""
72
73
def interpolate(self, df, **kwargs):
74
"""
75
Interpolate to new frequency spacing.
76
77
Parameters:
78
- df: float, new frequency resolution
79
80
Returns:
81
Interpolated FrequencySeries
82
"""
83
84
def inject(self, other, **kwargs):
85
"""
86
Inject another frequency series.
87
88
Parameters:
89
- other: FrequencySeries, series to inject
90
91
Returns:
92
Combined FrequencySeries
93
"""
94
95
def write(self, target, format=None, **kwargs):
96
"""
97
Write FrequencySeries to file.
98
99
Parameters:
100
- target: str, output file path
101
- format: str, output format
102
"""
103
```
104
105
### SpectralVariance - 2D Spectral Variance Analysis
106
107
Two-dimensional histogram for analyzing spectral variance and identifying non-stationary noise sources.
108
109
```python { .api }
110
class SpectralVariance(Array2D):
111
def __init__(self, data, bins=None, **kwargs):
112
"""
113
2D histogram for spectral variance analysis.
114
115
Parameters:
116
- data: 2D array, spectral variance data
117
- bins: array-like, histogram bin edges
118
"""
119
120
@classmethod
121
def from_spectrogram(cls, spectrogram, **kwargs):
122
"""
123
Calculate spectral variance from spectrogram.
124
125
Parameters:
126
- spectrogram: Spectrogram, input data
127
128
Returns:
129
SpectralVariance object
130
"""
131
132
def plot(self, **kwargs):
133
"""
134
Plot the spectral variance.
135
136
Returns:
137
Plot object
138
"""
139
```
140
141
### Usage Examples
142
143
#### Power Spectral Density Analysis
144
145
```python
146
from gwpy.timeseries import TimeSeries
147
from gwpy.frequencyseries import FrequencySeries
148
149
# Calculate PSD from time series
150
strain = TimeSeries.fetch_open_data('H1', 1126259446, 1126259478)
151
psd = strain.psd(fftlength=4, overlap=2)
152
153
# Plot PSD
154
plot = psd.plot()
155
plot.set_xlabel('Frequency [Hz]')
156
plot.set_ylabel('Power Spectral Density [strain^2/Hz]')
157
plot.set_yscale('log')
158
plot.show()
159
160
# Read PSD from file
161
ref_psd = FrequencySeries.read('reference_psd.txt')
162
163
# Compare PSDs
164
ratio = psd / ref_psd
165
ratio.plot()
166
```
167
168
#### Amplitude Spectral Density
169
170
```python
171
# Calculate ASD (square root of PSD)
172
asd = strain.asd(fftlength=4)
173
174
# Plot with proper scaling
175
plot = asd.plot()
176
plot.set_xlabel('Frequency [Hz]')
177
plot.set_ylabel('Amplitude Spectral Density [strain/√Hz]')
178
plot.set_yscale('log')
179
plot.set_xlim(10, 2000)
180
plot.show()
181
```
182
183
#### Frequency Domain Filtering
184
185
```python
186
from scipy.signal import butter
187
188
# Design filter in zpk format
189
z, p, k = butter(8, [30, 400], btype='bandpass', fs=4096, output='zpk')
190
191
# Apply filter to frequency series data
192
filtered_psd = psd.zpk(z, p, k)
193
194
# Crop to analysis band
195
analysis_psd = psd.crop(20, 2000)
196
```
197
198
#### Working with Transfer Functions
199
200
```python
201
# Read calibration transfer function
202
cal_response = FrequencySeries.read('calibration_response.txt',
203
format='txt')
204
205
# Apply calibration correction
206
calibrated_psd = raw_psd * cal_response
207
208
# Plot magnitude and phase
209
fig, axes = plt.subplots(2, 1, sharex=True)
210
211
# Magnitude
212
axes[0].loglog(cal_response.frequencies, abs(cal_response))
213
axes[0].set_ylabel('Magnitude')
214
215
# Phase
216
axes[1].semilogx(cal_response.frequencies,
217
np.angle(cal_response, deg=True))
218
axes[1].set_ylabel('Phase [degrees]')
219
axes[1].set_xlabel('Frequency [Hz]')
220
```
221
222
#### Spectral Variance Analysis
223
224
```python
225
from gwpy.frequencyseries import SpectralVariance
226
227
# Calculate spectrogram first
228
spec = strain.spectrogram(stride=60, fftlength=4)
229
230
# Create spectral variance histogram
231
variance = SpectralVariance.from_spectrogram(spec,
232
bins=50,
233
low=1e-24,
234
high=1e-20)
235
236
# Plot spectral variance
237
plot = variance.plot()
238
plot.set_xlabel('Frequency [Hz]')
239
plot.set_ylabel('Amplitude Spectral Density [strain/√Hz]')
240
plot.show()
241
```
242
243
#### Noise Budget Analysis
244
245
```python
246
# Read multiple PSDs for noise budget
247
psds = {}
248
channels = ['H1:CAL-DELTAL_EXTERNAL_DQ',
249
'H1:ASC-AS_A_RF45_I_ERR_DQ',
250
'H1:LSC-DARM_IN1_DQ']
251
252
for channel in channels:
253
ts = TimeSeries.get(channel, start, end)
254
psds[channel] = ts.psd(fftlength=8)
255
256
# Plot noise budget
257
plot = FrequencySeries.plot(figsize=(12, 6))
258
for name, psd in psds.items():
259
plot.add_frequencyseries(psd, label=name)
260
261
plot.set_xlabel('Frequency [Hz]')
262
plot.set_ylabel('Power Spectral Density')
263
plot.set_yscale('log')
264
plot.legend()
265
plot.show()
266
```
267
268
#### Cross-Spectral Density
269
270
```python
271
# Calculate cross-spectral density between channels
272
witness = TimeSeries.get('H1:ASC-AS_A_RF45_I_ERR_DQ', start, end)
273
strain = TimeSeries.get('H1:DCS-CALIB_STRAIN_C02', start, end)
274
275
# Cross-spectral density
276
csd = strain.csd(witness, fftlength=4, overlap=2)
277
278
# Coherence calculation
279
coherence = strain.coherence(witness, fftlength=4, overlap=2)
280
281
# Plot coherence
282
plot = coherence.plot()
283
plot.set_xlabel('Frequency [Hz]')
284
plot.set_ylabel('Coherence')
285
plot.set_ylim(0, 1)
286
plot.show()
287
```