gwpy/cli/coherencegram.py

Summary

Maintainability
A
25 mins
Test Coverage
# -*- coding: utf-8 -*-
# Copyright (C) Joseph Areeda (2015-2020)
#
# This file is part of GWpy.
#
# GWpy 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.
#
# GWpy 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 GWpy.  If not, see <http://www.gnu.org/licenses/>.

"""Coherence spectrogram
"""

from .spectrogram import Spectrogram

__author__ = 'Joseph Areeda <joseph.areeda@ligo.org>'


class Coherencegram(Spectrogram):
    """Plot the coherence-spectrogram comparing two time series
    """
    DEFAULT_CMAP = "plasma"
    MIN_DATASETS = 2
    MAX_DATASETS = 2
    action = 'coherencegram'

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.ref_chan = self.args.ref or self.chan_list[0]
        # deal with channel type (e.g. m-trend)
        if ',' in self.ref_chan:
            self.ref_chan = self.ref_chan.split(',')[0]

    @classmethod
    def arg_channels(cls, parser):
        group = super().arg_channels(parser)
        group.add_argument('--ref', help='Reference channel against which '
                                         'others will be compared')
        return group

    def _finalize_arguments(self, args):
        if args.color_scale is None:
            args.color_scale = 'linear'
        if args.color_scale == 'linear':
            if args.imin is None:
                args.imin = 0.
            if args.imax is None:
                args.imax = 1.
        return super()._finalize_arguments(args)

    def get_ylabel(self):
        """Text for y-axis label
        """
        return 'Frequency (Hz)'

    def get_suptitle(self):
        """Start of default super title, first channel is appended to it
        """
        a, b = self.chan_list
        return f"Coherence spectrogram: {a} vs {b}"

    def get_color_label(self):
        if self.args.norm:
            return f'Normalized to {self.args.norm}'
        return 'Coherence'

    def get_stride(self):
        fftlength = float(self.args.secpfft)
        overlap = self.args.overlap  # fractional overlap
        return max(self.duration / (self.width * 0.8),
                   fftlength * (1 + (1-overlap)*32),
                   fftlength * 2)

    def get_spectrogram(self):
        args = self.args
        fftlength = float(args.secpfft)
        overlap = args.overlap  # fractional overlap
        stride = self.get_stride()
        self.log(2, "Calculating coherence spectrogram, "
                    f"secpfft: {fftlength}, overlap: {overlap}")

        if overlap is not None:  # overlap in seconds
            overlap *= fftlength

        if self.timeseries[0].name == self.ref_chan:
            ref = 0
            other = 1
        else:
            ref = 1
            other = 0
        return self.timeseries[ref].coherence_spectrogram(
            self.timeseries[other], stride, fftlength=fftlength,
            overlap=overlap, window=args.window)