# coding=utf-8
# Copyright (C) Duncan Macleod (2015)
#
# This file is part of the GW DetChar python package.
#
# GW DetChar is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GW DetChar is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GW DetChar. If not, see <http://www.gnu.org/licenses/>.
"""Utilities for analysing optical scattering
"""
import numpy
from scipy.signal import savgol_filter
__author__ = 'Duncan Macleod <duncan.macleod@ligo.org>'
__credits__ = ('Siddharth Soni <siddharth.soni@ligo.org>, '
'Alex Urban <alexander.urban@ligo.org>')
OPTIC_MOTION_CHANNELS = {
'BS': ['SUS-BS_M1_DAMP_L_IN1_DQ',
'SUS-BS_M2_WIT_L_DQ'],
'ETMX': ['SUS-ETMX_M0_DAMP_L_IN1_DQ',
'SUS-ETMX_R0_DAMP_L_IN1_DQ',
'SUS-ETMX_L2_WIT_L_DQ'],
'ETMY': ['SUS-ETMY_M0_DAMP_L_IN1_DQ',
'SUS-ETMY_R0_DAMP_L_IN1_DQ',
'SUS-ETMY_L2_WIT_L_DQ'],
'IM1': ['SUS-IM1_M1_DAMP_L_IN1_DQ'],
'IM2': ['SUS-IM2_M1_DAMP_L_IN1_DQ'],
'IM3': ['SUS-IM3_M1_DAMP_L_IN1_DQ'],
'IM4': ['SUS-IM4_M1_DAMP_L_IN1_DQ'],
'ITMX': ['SUS-ITMX_M0_DAMP_L_IN1_DQ',
'SUS-ITMX_R0_DAMP_L_IN1_DQ',
'SUS-ITMX_L2_WIT_L_DQ'],
'ITMY': ['SUS-ITMY_M0_DAMP_L_IN1_DQ',
'SUS-ITMY_R0_DAMP_L_IN1_DQ',
'SUS-ITMY_L1_WIT_L_DQ'],
'MC1': ['SUS-MC1_M1_DAMP_L_IN1_DQ',
'SUS-MC1_M3_WIT_L_DQ'],
'MC2': ['SUS-MC2_M1_DAMP_L_IN1_DQ',
'SUS-MC2_M3_WIT_L_DQ'],
'MC3': ['SUS-MC3_M1_DAMP_L_IN1_DQ',
'SUS-MC3_M3_WIT_L_DQ'],
'OM1': ['SUS-OM1_M1_DAMP_L_IN1_DQ'],
'OM2': ['SUS-OM2_M1_DAMP_L_IN1_DQ'],
'OM3': ['SUS-OM3_M1_DAMP_L_IN1_DQ'],
'OMC': ['SUS-OMC_M1_DAMP_L_IN1_DQ'],
'PR2': ['SUS-PR2_M1_DAMP_L_IN1_DQ',
'SUS-PR2_M3_WIT_L_DQ'],
'PR3': ['SUS-PR3_M1_DAMP_L_IN1_DQ',
'SUS-PR3_M3_WIT_L_DQ'],
'PRM': ['SUS-PRM_M1_DAMP_L_IN1_DQ',
'SUS-PRM_M3_WIT_L_DQ'],
'RM1': ['SUS-RM1_M1_DAMP_L_IN1_DQ'],
'RM2': ['SUS-RM2_M1_DAMP_L_IN1_DQ'],
'ZM1': ['SUS-ZM1_M1_DAMP_L_IN1_DQ'],
'ZM2': ['SUS-ZM2_M1_DAMP_L_IN1_DQ'],
'OPO': ['SUS-OPO_M1_DAMP_L_IN1_DQ'],
'OFI': ['SUS-OFI_M1_DAMP_L_IN1_DQ',
'SUS-OFI_M1_DAMP_T_IN1_DQ'],
'SR2': ['SUS-SR2_M1_DAMP_L_IN1_DQ',
'SUS-SR2_M3_WIT_L_DQ'],
'SR3': ['SUS-SR3_M1_DAMP_L_IN1_DQ',
'SUS-SR3_M3_WIT_L_DQ'],
'SRM': ['SUS-SRM_M1_DAMP_L_IN1_DQ',
'SUS-SRM_M3_WIT_L_DQ'],
'TMSX': ['SUS-TMSX_M1_DAMP_L_IN1_DQ'],
'TMSY': ['SUS-TMSY_M1_DAMP_L_IN1_DQ'],
}
TRANSMON_CHANNELS = ['ASC-X_TR_B_NSUM_OUT_DQ',
'ASC-Y_TR_B_NSUM_OUT_DQ']
FREQUENCY_MULTIPLIERS = range(1, 5)
[docs]
def get_fringe_frequency(series, multiplier=2.0):
"""Predict scattering fringe frequency from the derivative of a timeseries
Parameters
----------
series : `~gwpy.timeseries.TimeSeries`
timeseries record of relative motion
multiplier : `float`
harmonic number of fringe frequency
Returns
-------
fringef : `~gwpy.timeseries.TimeSeries`
timeseries record of fringe frequency
See Also
--------
scipy.signal.savgol_filter
for an implementation of the Savitzky-Golay filter
"""
velocity = type(series)(savgol_filter(series.value, 5, 2, deriv=1))
velocity.__array_finalize__(series)
fringef = numpy.abs(multiplier * 2. / 1.064 * velocity *
velocity.sample_rate.value)
fringef.override_unit('Hz')
return fringef
[docs]
def get_blrms(series, flow=4.0, fhigh=10.0, stride=1, whiten=True,
fftlength=4, overlap=2, **kwargs):
"""Compute the whitened, band-limited RMS of a `TimeSeries`
Parameters
----------
series : `~gwpy.timeseries.TimeSeries`
the input `TimeSeries` data
flow : `float`, optional
lower limit (Hz) of the passband, default: 4.0
fhigh : `float`, optional
upper limit (Hz) of the passband, default: 10.0
stride: `float`, optional
RMS integration length (seconds), default: 1
whiten : `bool`, optional
boolean switch to enable (`True`) or disable (`False`)
whitening of the input, default: `True`
fftlength : `float`, optional
FFT integration length (seconds), default: 4
overlap : `float`, optional
FFT overlap length (seconds), default: 2
**kwargs : `dict`, optional
additional keyword arguments to `TimeSeries.whiten`
Returns
-------
wblrms : `~gwpy.timeseries.TimeSeries`
whitened, band-limited RMS trends of the input `TimeSeries`
See Also
--------
gwpy.timeseries.TimeSeries.whiten
for the underlying whitening scheme
gwpy.timeseries.TimeSeries.rms
for the underlying root-mean-square (RMS) estimation method
"""
if whiten:
series = series.whiten(fftlength=fftlength, overlap=overlap, **kwargs)
bpseries = series.bandpass(flow, fhigh)
return bpseries.rms(stride)
[docs]
def get_segments(series, threshold, name=None, pad=0):
"""Generate data-quality segments by thresholding a `TimeSeries`
Parameters
----------
series : `~gwpy.timeseries.TimeSeries`
the input `TimeSeries` data
threshold : `float`
the threshold value for active data-quality segments
name : `str`, optional
name of the data-quality flag, defaults to `series.name`
pad : `float`, optional
length (seconds) by which to pad active segments, default: 0
Returns
--------
threshflag : `~gwpy.segments.DataQualityFlag`
the populated data-quality flag
"""
if series.value.max() < threshold:
from gwpy.segments import DataQualityFlag
return DataQualityFlag(name, known=[series.span])
else:
thresh = series >= threshold * series.unit
threshflag = thresh.to_dqflag(name or series.name)
threshflag.protract(pad)
return threshflag.coalesce()