# Source code for obspy.imaging.spectrogram

    # -*- coding: utf-8 -*-
    # ------------------------------------------------------------------
    # Filename: spectrogram.py
    #  Purpose: Plotting spectrogram of Seismograms.
    #   Author: Christian Sippl, Moritz Beyreuther
    #    Email: sippl@geophysik.uni-muenchen.de
    #
    # Copyright (C) 2008-2012 Christian Sippl
    # --------------------------------------------------------------------
    """
    Plotting spectrogram of seismograms.

    :copyright:
        The ObsPy Development Team (devs@obspy.org)
    :license:
        GNU Lesser General Public License, Version 3
        (https://www.gnu.org/copyleft/lesser.html)
    """
    from __future__ import (absolute_import, division, print_function,
                            unicode_literals)
    from future.builtins import *  # NOQA @UnusedWildImport

    import math as M

    import numpy as np
    from matplotlib import mlab
    from matplotlib.colors import Normalize

    from obspy.imaging.cm import obspy_sequential

    def _nearest_pow_2(x):
        """
        Find power of two nearest to x

        >>> _nearest_pow_2(3)
        2.0
        >>> _nearest_pow_2(15)
        16.0

        :type x: float
        :param x: Number
        :rtype: Int
        :return: Nearest power of 2 to x
        """
        a = M.pow(2, M.ceil(np.log2(x)))
        b = M.pow(2, M.floor(np.log2(x)))
        if abs(a - x) < abs(b - x):
            return a
        else:
            return b

    def spectrogram(data, samp_rate, per_lap=0.9, wlen=None, log=False,
                    outfile=None, fmt=None, axes=None, dbscale=False,
                    mult=8.0, cmap=obspy_sequential, zorder=None, title=None,
                    show=True, sphinx=False, clip=[0.0, 1.0]):
        """
        Computes and plots spectrogram of the input data.

        :param data: Input data
        :type samp_rate: float
        :param samp_rate: Samplerate in Hz
        :type per_lap: float
        :param per_lap: Percentage of overlap of sliding window, ranging from 0
            to 1. High overlaps take a long time to compute.
        :type wlen: int or float
        :param wlen: Window length for fft in seconds. If this parameter is too
            small, the calculation will take forever. If None, it defaults to
            (samp_rate/100.0).
        :type log: bool
        :param log: Logarithmic frequency axis if True, linear frequency axis
            otherwise.
        :type outfile: str
        :param outfile: String for the filename of output file, if None
            interactive plotting is activated.
        :type fmt: str
        :param fmt: Format of image to save
        :type axes: :class:`matplotlib.axes.Axes`
        :param axes: Plot into given axes, this deactivates the fmt and
            outfile option.
        :type dbscale: bool
        :param dbscale: If True 10 * log10 of color values is taken, if False the
            sqrt is taken.
        :type mult: float
        :param mult: Pad zeros to length mult * wlen. This will make the
            spectrogram smoother.
        :type cmap: :class:`matplotlib.colors.Colormap`
        :param cmap: Specify a custom colormap instance. If not specified, then the
            default ObsPy sequential colormap is used.
        :type zorder: float
        :param zorder: Specify the zorder of the plot. Only of importance if other
            plots in the same axes are executed.
        :type title: str
        :param title: Set the plot title
        :type show: bool
        :param show: Do not call `plt.show()` at end of routine. That way, further
            modifications can be done to the figure before showing it.
        :type sphinx: bool
        :param sphinx: Internal flag used for API doc generation, default False
        :type clip: [float, float]
        :param clip: adjust colormap to clip at lower and/or upper end. The given
            percentages of the amplitude range (linear or logarithmic depending
            on option `dbscale`) are clipped.
        """
        import matplotlib.pyplot as plt
        # enforce float for samp_rate
        samp_rate = float(samp_rate)

        # set wlen from samp_rate if not specified otherwise
        if not wlen:
            wlen = samp_rate / 100.

        npts = len(data)
        # nfft needs to be an integer, otherwise a deprecation will be raised
        # XXX add condition for too many windows => calculation takes for ever
        nfft = int(_nearest_pow_2(wlen * samp_rate))
        if nfft > npts:
            nfft = int(_nearest_pow_2(npts / 8.0))

        if mult is not None:
            mult = int(_nearest_pow_2(mult))
            mult = mult * nfft
        nlap = int(nfft * float(per_lap))

        data = data - data.mean()
        end = npts / samp_rate

        # Here we call not plt.specgram as this already produces a plot
        # matplotlib.mlab.specgram should be faster as it computes only the
        # arrays
        # XXX mlab.specgram uses fft, would be better and faster use rfft
        specgram, freq, time = mlab.specgram(data, Fs=samp_rate, NFFT=nfft,
                                             pad_to=mult, noverlap=nlap)
        # db scale and remove zero/offset for amplitude
        if dbscale:
            specgram = 10 * np.log10(specgram[1:, :])
        else:
            specgram = np.sqrt(specgram[1:, :])
        freq = freq[1:]

        vmin, vmax = clip
        if vmin < 0 or vmax > 1 or vmin >= vmax:
            msg = "Invalid parameters for clip option."
            raise ValueError(msg)
        _range = float(specgram.max() - specgram.min())
        vmin = specgram.min() + vmin * _range
        vmax = specgram.min() + vmax * _range
        norm = Normalize(vmin, vmax, clip=True)

        if not axes:
            fig = plt.figure()
            ax = fig.add_subplot(111)
        else:
            ax = axes

        # calculate half bin width
        halfbin_time = (time[1] - time[0]) / 2.0
        halfbin_freq = (freq[1] - freq[0]) / 2.0

        # argument None is not allowed for kwargs on matplotlib python 3.3
        kwargs = {k: v for k, v in (('cmap', cmap), ('zorder', zorder))
                  if v is not None}

        if log:
            # pcolor expects one bin more at the right end
            freq = np.concatenate((freq, [freq[-1] + 2 * halfbin_freq]))
            time = np.concatenate((time, [time[-1] + 2 * halfbin_time]))
            # center bin
            time -= halfbin_time
            freq -= halfbin_freq
            # Log scaling for frequency values (y-axis)
            ax.set_yscale('log')
            # Plot times
            ax.pcolormesh(time, freq, specgram, norm=norm, **kwargs)
        else:
            # this method is much much faster!
            specgram = np.flipud(specgram)
            # center bin
            extent = (time[0] - halfbin_time, time[-1] + halfbin_time,
                      freq[0] - halfbin_freq, freq[-1] + halfbin_freq)
            ax.imshow(specgram, interpolation="nearest", extent=extent, **kwargs)

        # set correct way of axis, whitespace before and after with window
        # length
        ax.axis('tight')
        ax.set_xlim(0, end)
        ax.grid(False)

        if axes:
            return ax

        ax.set_xlabel('Time [s]')
        ax.set_ylabel('Frequency [Hz]')
        if title:
            ax.set_title(title)

        if not sphinx:
            # ignoring all NumPy warnings during plot
            with np.errstate(all='ignore'):
                plt.draw()
        if outfile:
            if fmt:
                fig.savefig(outfile, format=fmt)
            else:
                fig.savefig(outfile)
        elif show:
            plt.show()
        else:
            return fig

### Thank you!

We would like to thank our contributors, whose efforts make this software what it is. These people have helped by writing code and documentation, and by testing. They have created and maintained this product, its associated libraries and applications, our build tools and our web sites.

#### Contributors

- Ackerley, Nick
- Allgeyer, Sebastien
- Ammon, Charles J.
- Antunes, Emanuel
- Arnarsson, Ólafur St.
- Bank, Markus
- Barsch, Robert
- Behr, Yannik
- Bernardi, Fabrizio
- Bernauer, Felix
- Bes de Berc, Maxime
- Beyreuther, Moritz
- Boltz, Shawn
- Bonaimé, Sébastien
- Carothers, Lloyd
- Chamberlain, Calum
- Chambers, Derrick
- Chen, Zhao
- Clark, Adam
- Danecek, Peter
- Egdorf, Sven
- Engels, Fabian
- Ermert, Laura
- Eulenfeld, Tom

- Fabbri, Tommaso
- Falco, Nicholas
- Fee, Jeremy
- Grellier, Clément
- Grunberg, Marc
- Hammer, Conny
- Heimann, Sebastian
- Heiniger, Lukas
- Hope, Gaute
- Igel, Heiner
- Inza, Adolfo
- Isken, Marius
- Ketchum, David
- Koymans, Mathijs
- Kremers, Simon
- Kress, Victor
- Krieger, Lars
- Krischer, Lion
- Kula, Damian
- Käufl, Paul
- Köhler, Andreas
- Lecocq, Thomas
- Leeman, John
- Legovini, Paride

- Lesage, Philippe
- Li, Yulin
- Lomax, Anthony
- Lopes, Rui L.
- MacCarthy, Jonathan
- Maggi, Alessia
- Martin, Henri
- Medlin, Andrew
- Megies, Tobias
- Meschede, Matthias
- Michelini, Alberto
- Miller, Nathaniel C.
- Morgenstern, Bernhard
- Nof, Ran Novitsky
- Panning, Mark P.
- Parker, Tom
- Pestourie, Romain
- Rapagnani, Giovanni
- Reyes, Celso
- Ringler, Adam
- Rothenhäusler, Nicolas
- Russo, Emiliano
- Sales de Andrade, Elliott

- Satriano, Claudio
- Saul, Joachim
- Scheingraber, Chris
- Schneider, Simon
- Sippl, Christian
- Snoke, Arthur
- Stange, Stefan
- Stähler, Simon C.
- Sullivan, Benjamin
- Tian, Dongdong
- Trabant, Chad
- Trani, Luca
- Uieda, Leonardo
- Walker, Andrew
- Walther, Marcus
- Wassermann, Joachim
- Williams, Mark C.
- Winkelman, Andrew
- Zaccarelli, Riccardo
- Zad, Seyed Kasra Hosseini
- Zhu, Lijun
- van Driel, Martin
- van Wijk, Kasper

#### Funds

ObsPy was partially funded by the

- German Science Foundation (DFG) via grant DFG IG 16/9-1
- German Ministry for Education and Research (BMBF), GEOTECHNOLOGIEN grant 03G0646H.
- NERA project (Network of European Research Infrastructures for Earthquake Risk Assessment and Mitigation) under the European Community's Seventh Framework Programme (FP7/2007-2013) grant agreement n° 262330
- Leibniz Institute for Applied Geophysics (LIAG)
- VERCE EU-FP7 project (no. 283543)