0
# Detector Utilities
1
2
Detector-specific utilities for channel management, timezone handling, and interferometer-specific configurations. These tools provide essential metadata and configuration information for working with data from different gravitational-wave detectors worldwide.
3
4
## Capabilities
5
6
### Channel - Detector Channel Representation
7
8
Represents a data channel from a gravitational-wave detector with metadata and query capabilities.
9
10
```python { .api }
11
from gwpy.detector import Channel
12
13
class Channel:
14
def __init__(self, name, sample_rate=None, unit=None, type=None, **kwargs):
15
"""
16
Create a detector channel object.
17
18
Parameters:
19
- name: str, channel name (e.g., 'H1:LSC-DARM_ERR_DBL_DQ')
20
- sample_rate: float, sampling rate in Hz
21
- unit: str or Unit, physical unit of the data
22
- type: str, channel data type
23
"""
24
25
@classmethod
26
def query(cls, name, **kwargs):
27
"""
28
Query channel information from detector databases.
29
30
Parameters:
31
- name: str, channel name to query
32
- **kwargs: additional query parameters
33
34
Returns:
35
Channel object with metadata
36
"""
37
38
@property
39
def name(self):
40
"""Channel name string."""
41
42
@property
43
def ifo(self):
44
"""Interferometer code (e.g., 'H1', 'L1', 'V1')."""
45
46
@property
47
def system(self):
48
"""Detector subsystem (e.g., 'LSC', 'ASC', 'CAL')."""
49
50
@property
51
def subsystem(self):
52
"""Detector subsystem component."""
53
54
@property
55
def signal(self):
56
"""Signal name within subsystem."""
57
58
@property
59
def sample_rate(self):
60
"""Channel sampling rate in Hz."""
61
62
@property
63
def unit(self):
64
"""Physical unit of channel data."""
65
66
@property
67
def type(self):
68
"""Channel data type."""
69
70
def __str__(self):
71
"""String representation of channel."""
72
73
def __repr__(self):
74
"""Detailed string representation."""
75
```
76
77
### ChannelList - Collection of Channels
78
79
List container for managing multiple detector channels with batch operations.
80
81
```python { .api }
82
from gwpy.detector import ChannelList
83
84
class ChannelList(list):
85
def __init__(self, channels=None):
86
"""
87
Create a list of detector channels.
88
89
Parameters:
90
- channels: iterable, initial Channel objects
91
"""
92
93
@classmethod
94
def query(cls, names, **kwargs):
95
"""
96
Query multiple channels from detector databases.
97
98
Parameters:
99
- names: list, channel names to query
100
101
Returns:
102
ChannelList with metadata for all channels
103
"""
104
105
def find(self, name):
106
"""
107
Find channel by name.
108
109
Parameters:
110
- name: str, channel name to find
111
112
Returns:
113
Channel object or None if not found
114
"""
115
116
def sieve(self, **kwargs):
117
"""
118
Filter channels by criteria.
119
120
Parameters:
121
- sample_rate: float, filter by sampling rate
122
- unit: str, filter by unit
123
- ifo: str, filter by interferometer
124
125
Returns:
126
Filtered ChannelList
127
"""
128
```
129
130
### Timezone Functions
131
132
Functions for handling interferometer-specific timezone information.
133
134
```python { .api }
135
from gwpy.detector import get_timezone, get_timezone_offset, TIMEZONE
136
137
def get_timezone(ifo):
138
"""
139
Get timezone object for interferometer.
140
141
Parameters:
142
- ifo: str, interferometer code ('H1', 'L1', 'V1', 'G1', 'C1')
143
144
Returns:
145
datetime.timezone object for the interferometer location
146
147
Raises:
148
ValueError: if interferometer code is not recognized
149
"""
150
151
def get_timezone_offset(ifo, dt=None):
152
"""
153
Get timezone offset for interferometer.
154
155
Parameters:
156
- ifo: str, interferometer code
157
- dt: datetime, specific date/time for offset calculation
158
159
Returns:
160
float, timezone offset in seconds from UTC
161
"""
162
163
# Timezone mapping constant
164
TIMEZONE = {
165
'C1': 'US/Pacific', # Caltech 40m prototype
166
'G1': 'Europe/Berlin', # GEO600
167
'H1': 'US/Pacific', # LIGO Hanford
168
'L1': 'US/Central', # LIGO Livingston
169
'V1': 'Europe/Rome' # Virgo
170
}
171
```
172
173
### Usage Examples
174
175
#### Channel Information and Metadata
176
177
```python
178
from gwpy.detector import Channel, ChannelList
179
180
# Create channel object
181
strain_channel = Channel('H1:DCS-CALIB_STRAIN_C02')
182
183
# Access channel properties
184
print(f"Interferometer: {strain_channel.ifo}")
185
print(f"System: {strain_channel.system}")
186
print(f"Signal: {strain_channel.signal}")
187
188
# Query channel metadata from databases
189
try:
190
channel_info = Channel.query('H1:LSC-DARM_ERR_DBL_DQ')
191
print(f"Sample rate: {channel_info.sample_rate} Hz")
192
print(f"Unit: {channel_info.unit}")
193
print(f"Type: {channel_info.type}")
194
except Exception as e:
195
print(f"Could not query channel metadata: {e}")
196
197
# Work with channel names
198
channels = ['H1:LSC-DARM_ERR_DBL_DQ',
199
'H1:CAL-DELTAL_EXTERNAL_DQ',
200
'H1:ASC-AS_A_RF45_I_ERR_DQ']
201
202
channel_list = ChannelList([Channel(name) for name in channels])
203
print(f"Number of channels: {len(channel_list)}")
204
205
# Filter channels by interferometer
206
h1_channels = channel_list.sieve(ifo='H1')
207
print(f"H1 channels: {len(h1_channels)}")
208
```
209
210
#### Multi-Detector Analysis Setup
211
212
```python
213
# Define channels for multi-detector analysis
214
detector_channels = {
215
'H1': ['H1:DCS-CALIB_STRAIN_C02',
216
'H1:LSC-DARM_ERR_DBL_DQ'],
217
'L1': ['L1:DCS-CALIB_STRAIN_C02',
218
'L1:LSC-DARM_ERR_DBL_DQ'],
219
'V1': ['V1:Hrec_hoft_16384Hz',
220
'V1:LSC_DARM_ERR_DQ']
221
}
222
223
# Create channel objects for each detector
224
all_channels = ChannelList()
225
for ifo, names in detector_channels.items():
226
for name in names:
227
all_channels.append(Channel(name))
228
229
# Find specific channels
230
strain_channels = []
231
for channel in all_channels:
232
if 'STRAIN' in channel.name or 'hoft' in channel.name:
233
strain_channels.append(channel)
234
235
print(f"Found {len(strain_channels)} strain channels")
236
for ch in strain_channels:
237
print(f" {ch.name}")
238
```
239
240
#### Timezone Handling for Different Detectors
241
242
```python
243
from gwpy.detector import get_timezone, get_timezone_offset, TIMEZONE
244
from datetime import datetime
245
import pytz
246
247
# Show timezone information for all detectors
248
print("Detector Timezones:")
249
for ifo, tz_name in TIMEZONE.items():
250
timezone = get_timezone(ifo)
251
print(f" {ifo}: {tz_name}")
252
253
# Get timezone offsets
254
reference_time = datetime(2015, 9, 14, 9, 50, 45) # GW150914 UTC time
255
256
print(f"\nTimezone offsets for {reference_time} UTC:")
257
for ifo in ['H1', 'L1', 'V1', 'G1']:
258
offset_sec = get_timezone_offset(ifo, reference_time)
259
offset_hours = offset_sec / 3600
260
print(f" {ifo}: {offset_hours:+.1f} hours")
261
262
# Convert GPS time to local times
263
gps_time = 1126259462.4 # GW150914 GPS time
264
utc_time = datetime.utcfromtimestamp(gps_time - 315964800) # GPS to UTC
265
266
print(f"\nGW150914 local times:")
267
print(f" GPS: {gps_time}")
268
print(f" UTC: {utc_time}")
269
270
for ifo in ['H1', 'L1', 'V1']:
271
tz = get_timezone(ifo)
272
local_time = utc_time.replace(tzinfo=pytz.UTC).astimezone(tz)
273
print(f" {ifo} local: {local_time.strftime('%Y-%m-%d %H:%M:%S %Z')}")
274
```
275
276
#### Channel Discovery and Selection
277
278
```python
279
# Common gravitational-wave analysis channels
280
analysis_channels = {
281
'strain': ['H1:DCS-CALIB_STRAIN_C02', 'L1:DCS-CALIB_STRAIN_C02'],
282
'calibration': ['H1:CAL-DELTAL_EXTERNAL_DQ', 'L1:CAL-DELTAL_EXTERNAL_DQ'],
283
'darm_error': ['H1:LSC-DARM_ERR_DBL_DQ', 'L1:LSC-DARM_ERR_DBL_DQ'],
284
'alignment': ['H1:ASC-AS_A_RF45_I_ERR_DQ', 'L1:ASC-AS_A_RF45_I_ERR_DQ'],
285
'environment': ['H1:PEM-BSC5_MIC_SEIS_Z_DQ', 'L1:PEM-EX_MIC_SEIS_Z_DQ']
286
}
287
288
# Create organized channel list
289
organized_channels = {}
290
for category, names in analysis_channels.items():
291
organized_channels[category] = ChannelList([Channel(name) for name in names])
292
293
print("Channel Categories:")
294
for category, channels in organized_channels.items():
295
print(f" {category}: {len(channels)} channels")
296
for ch in channels:
297
print(f" {ch.ifo}: {ch.system}-{ch.signal}")
298
```
299
300
#### Channel-Based Data Reading
301
302
```python
303
from gwpy.timeseries import TimeSeries, TimeSeriesDict
304
305
# Read data using channel objects
306
start_time = 1126259446
307
end_time = 1126259478
308
309
# Single channel read
310
strain_ch = Channel('H1:DCS-CALIB_STRAIN_C02')
311
try:
312
strain_data = TimeSeries.get(strain_ch.name, start=start_time, end=end_time)
313
print(f"Read {strain_data.duration} seconds of {strain_ch.name}")
314
except Exception as e:
315
print(f"Could not read {strain_ch.name}: {e}")
316
317
# Multi-channel read using ChannelList
318
aux_channels = ChannelList([
319
Channel('H1:LSC-DARM_ERR_DBL_DQ'),
320
Channel('H1:CAL-DELTAL_EXTERNAL_DQ'),
321
Channel('H1:ASC-AS_A_RF45_I_ERR_DQ')
322
])
323
324
# Read all auxiliary channels
325
aux_data = {}
326
for channel in aux_channels:
327
try:
328
data = TimeSeries.get(channel.name, start=start_time, end=end_time)
329
aux_data[channel.name] = data
330
print(f"Successfully read {channel.name}")
331
except Exception as e:
332
print(f"Failed to read {channel.name}: {e}")
333
334
print(f"Successfully read {len(aux_data)} auxiliary channels")
335
```
336
337
#### Detector Configuration Management
338
339
```python
340
# Define standard detector configurations
341
DETECTOR_CONFIG = {
342
'LIGO': {
343
'sites': ['H1', 'L1'],
344
'strain_channels': {
345
'H1': 'H1:DCS-CALIB_STRAIN_C02',
346
'L1': 'L1:DCS-CALIB_STRAIN_C02'
347
},
348
'sample_rate': 16384,
349
'timezone_mapping': {
350
'H1': 'US/Pacific',
351
'L1': 'US/Central'
352
}
353
},
354
'Virgo': {
355
'sites': ['V1'],
356
'strain_channels': {
357
'V1': 'V1:Hrec_hoft_16384Hz'
358
},
359
'sample_rate': 16384,
360
'timezone_mapping': {
361
'V1': 'Europe/Rome'
362
}
363
}
364
}
365
366
def get_detector_config(detector_name):
367
"""Get configuration for a specific detector."""
368
return DETECTOR_CONFIG.get(detector_name, {})
369
370
def get_all_strain_channels():
371
"""Get all strain channels across detectors."""
372
channels = []
373
for config in DETECTOR_CONFIG.values():
374
for ifo, channel_name in config['strain_channels'].items():
375
channels.append(Channel(channel_name))
376
return ChannelList(channels)
377
378
# Use configuration
379
ligo_config = get_detector_config('LIGO')
380
print(f"LIGO sites: {ligo_config['sites']}")
381
382
all_strain = get_all_strain_channels()
383
print(f"All strain channels: {[ch.name for ch in all_strain]}")
384
```
385
386
#### Channel Name Parsing and Validation
387
388
```python
389
def parse_channel_name(channel_name):
390
"""Parse channel name into components."""
391
try:
392
parts = channel_name.split(':')
393
if len(parts) != 2:
394
raise ValueError("Invalid channel name format")
395
396
ifo = parts[0]
397
signal_parts = parts[1].split('-')
398
399
return {
400
'ifo': ifo,
401
'system': signal_parts[0] if signal_parts else None,
402
'subsystem': signal_parts[1] if len(signal_parts) > 1 else None,
403
'signal': '-'.join(signal_parts[2:]) if len(signal_parts) > 2 else None,
404
'full_name': channel_name
405
}
406
except Exception as e:
407
print(f"Error parsing channel name '{channel_name}': {e}")
408
return None
409
410
def validate_channel_names(channel_names):
411
"""Validate a list of channel names."""
412
valid_channels = []
413
invalid_channels = []
414
415
for name in channel_names:
416
parsed = parse_channel_name(name)
417
if parsed and parsed['ifo'] in TIMEZONE:
418
valid_channels.append(name)
419
else:
420
invalid_channels.append(name)
421
422
return valid_channels, invalid_channels
423
424
# Example usage
425
test_channels = [
426
'H1:DCS-CALIB_STRAIN_C02',
427
'L1:LSC-DARM_ERR_DBL_DQ',
428
'InvalidChannel',
429
'V1:Hrec_hoft_16384Hz',
430
'X1:FAKE-CHANNEL' # X1 is not a real detector
431
]
432
433
valid, invalid = validate_channel_names(test_channels)
434
print(f"Valid channels: {valid}")
435
print(f"Invalid channels: {invalid}")
436
437
# Parse valid channels
438
for name in valid:
439
info = parse_channel_name(name)
440
print(f"{name}:")
441
print(f" IFO: {info['ifo']}")
442
print(f" System: {info['system']}")
443
print(f" Signal: {info['signal']}")
444
```