FFT_tools: Catch RuntimeErrors when importing matplotlib
[FFT-tools.git] / FFT_tools.py
index 0daec76c34875d5638a0e4571e11cc4c2ed73e16..e6d058cc178416892a8425bcc05cc67b36d7aeb7 100644 (file)
@@ -1,63 +1,54 @@
-#!/usr/bin/python
+# Copyright (C) 2008-2012  W. Trevor King
+# This program 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.
 #
-# Wrap Numpy's fft module to produce 1D unitary transforms and power spectra.
+# This program 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.
 #
-# Copyright (C) 2008  W. Trevor King
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 
-#  * Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-#
-#  * Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in
-#    the documentation and/or other materials provided with the
-#    distribution.
-#
-#  * Neither the name of the copyright holders nor the names of the
-#    contributors may be used to endorse or promote products derived
-#    from this software without specific prior written permission.
-# 
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-"""
-Define some FFT wrappers to reduce clutter.
-Provides a unitary discrete FFT and a windowed version.
-Based on :func:`numpy.fft.rfft`.
+"""Wrap Numpy's :py:mod:`~numpy.fft` module to reduce clutter.
+
+Provides a unitary discrete FFT and a windowed version based on
+:py:func:`numpy.fft.rfft`.
 
 Main entry functions:
 
-* :func:`unitary_rfft`
-* :func:`power_spectrum`
-* :func:`unitary_power_spectrum`
-* :func:`avg_power_spectrum`
-* :func:`unitary_avg_power_spectrum`
+* :py:func:`unitary_rfft`
+* :py:func:`power_spectrum`
+* :py:func:`unitary_power_spectrum`
+* :py:func:`avg_power_spectrum`
+* :py:func:`unitary_avg_power_spectrum`
 """
 
-import unittest
+import logging as _logging
+import unittest as _unittest
+
+import numpy as _numpy
+try:
+    import matplotlib.pyplot as _pyplot
+except (ImportError, RuntimeError) as e:
+    _pyplot = None
+    _pyplot_import_error = e
+
 
-from numpy import log2, floor, round, ceil, abs, pi, exp, cos, sin, sqrt, \
-    sinc, arctan2, array, ones, arange, linspace, zeros, \
-    uint16, float, concatenate, fromfile, argmax, complex
-from numpy.fft import rfft
+__version__ = '0.5'
 
 
+LOG = _logging.getLogger('FFT-tools')
+LOG.addHandler(_logging.StreamHandler())
+LOG.setLevel(_logging.ERROR)
+
+
+# Display time- and freq-space plots of the test transforms if True
 TEST_PLOTS = False
 
+
 def floor_pow_of_two(num):
     """Round `num` down to the closest exact a power of two.
 
@@ -71,11 +62,12 @@ def floor_pow_of_two(num):
     >>> floor_pow_of_two(15)
     8
     """
-    lnum = log2(num)
+    lnum = _numpy.log2(num)
     if int(lnum) != lnum:
-        num = 2**floor(lnum)
+        num = 2**_numpy.floor(lnum)
     return int(num)
 
+
 def round_pow_of_two(num):
     """Round `num` to the closest exact a power of two on a log scale.
 
@@ -89,11 +81,12 @@ def round_pow_of_two(num):
     >>> round_pow_of_two(15)
     16
     """
-    lnum = log2(num)
+    lnum = _numpy.log2(num)
     if int(lnum) != lnum:
-        num = 2**round(lnum)
+        num = 2**_numpy.round(lnum)
     return int(num)
 
+
 def ceil_pow_of_two(num):
     """Round `num` up to the closest exact a power of two.
 
@@ -107,11 +100,12 @@ def ceil_pow_of_two(num):
     >>> ceil_pow_of_two(15)
     16
     """
-    lnum = log2(num)
+    lnum = _numpy.log2(num)
     if int(lnum) != lnum:
-        num = 2**ceil(lnum)
+        num = 2**_numpy.ceil(lnum)
     return int(num)
 
+
 def unitary_rfft(data, freq=1.0):
     """Compute the unitary Fourier transform of real data.
 
@@ -139,7 +133,7 @@ def unitary_rfft(data, freq=1.0):
     nsamps = floor_pow_of_two(len(data))
     # Which should satisfy the discrete form of Parseval's theorem
     #   n-1               n-1
-    #   SUM |x_m|^2 = 1/n SUM |X_k|^2. 
+    #   SUM |x_m|^2 = 1/n SUM |X_k|^2.
     #   m=0               k=0
     # However, we want our FFT to satisfy the continuous Parseval eqn
     #   int_{-infty}^{infty} |x(t)|^2 dt = int_{-infty}^{infty} |X(f)|^2 df
@@ -168,10 +162,11 @@ def unitary_rfft(data, freq=1.0):
     # so we can convert the Numpy transformed data to match our unitary
     # continuous transformed data with (also NR 12.1.8)
     #   X'_k = dtX = X / <sampling freq>
-    trans = rfft(data[0:nsamps]) / float(freq)
-    freq_axis = linspace(0, freq/2, nsamps/2+1)
+    trans = _numpy.fft.rfft(data[0:nsamps]) / _numpy.float(freq)
+    freq_axis = _numpy.linspace(0, freq / 2, nsamps / 2 + 1)
     return (freq_axis, trans)
 
+
 def power_spectrum(data, freq=1.0):
     """Compute the power spectrum of the time series `data`.
 
@@ -197,15 +192,16 @@ def power_spectrum(data, freq=1.0):
     unitary_power_spectrum,avg_power_spectrum
     """
     nsamps = floor_pow_of_two(len(data))
-    
-    freq_axis = linspace(0, freq/2, nsamps/2+1)
+
+    freq_axis = _numpy.linspace(0, freq / 2, nsamps / 2 + 1)
     # nsamps/2+1 b/c zero-freq and nyqist-freq are both fully real.
     # >>> help(numpy.fft.fftpack.rfft) for Numpy's explaination.
     # See Numerical Recipies for a details.
-    trans = rfft(data[0:nsamps])
-    power = trans * trans.conj() # We want the square of the amplitude.
+    trans = _numpy.fft.rfft(data[0:nsamps])
+    power = (trans * trans.conj()).real  # we want the square of the amplitude
     return (freq_axis, power)
 
+
 def unitary_power_spectrum(data, freq=1.0):
     """Compute the unitary power spectrum of the time series `data`.
 
@@ -214,17 +210,20 @@ def unitary_power_spectrum(data, freq=1.0):
     power_spectrum,unitary_avg_power_spectrum
     """
     freq_axis,power = power_spectrum(data, freq)
-    # One sided power spectral density, so 2|H(f)|**2 (see NR 2nd edition 12.0.14, p498)
+    # One sided power spectral density, so 2|H(f)|**2
+    # (see NR 2nd edition 12.0.14, p498)
     #
     # numpy normalizes with 1/N on the inverse transform ifft,
     # so we should normalize the freq-space representation with 1/sqrt(N).
-    # But we're using the rfft, where N points are like N/2 complex points, so 1/sqrt(N/2)
+    # But we're using the rfft, where N points are like N/2 complex points,
+    # so 1/sqrt(N/2)
     # So the power gets normalized by that twice and we have 2/N
     #
     # On top of this, the FFT assumes a sampling freq of 1 per second,
     # and we want to preserve area under our curves.
     # If our total time T = len(data)/freq is smaller than 1,
-    # our df_real = freq/len(data) is bigger that the FFT expects (dt_fft = 1/len(data)), 
+    # our df_real = freq/len(data) is bigger that the FFT expects
+    # (dt_fft = 1/len(data)),
     # and we need to scale the powers down to conserve area.
     # df_fft * F_fft(f) = df_real *F_real(f)
     # F_real = F_fft(f) * (1/len)/(freq/len) = F_fft(f)*freq
@@ -238,10 +237,11 @@ def unitary_power_spectrum(data, freq=1.0):
     # total_time = len(data)/float(freq)
     # power *= 2.0 / float(freq)**2   /   total_time
     # power *= 2.0 / freq**2   *   freq / len(data)
-    power *= 2.0 / (freq * float(len(data)))
+    power *= 2.0 / (freq * _numpy.float(len(data)))
 
     return (freq_axis, power)
 
+
 def window_hann(length):
     r"""Returns a Hann window array with length entries
 
@@ -251,13 +251,15 @@ def window_hann(length):
 
     .. math:: w_i = \frac{1}{2} (1-\cos(2\pi i/L))
     """
-    win = zeros((length,), dtype=float)
+    win = _numpy.zeros((length,), dtype=_numpy.float)
     for i in range(length):
-        win[i] = 0.5*(1.0-cos(2.0*pi*float(i)/(length)))
+        win[i] = 0.5 * (
+            1.0 - _numpy.cos(2.0 * _numpy.pi * _numpy.float(i) / (length)))
     # avg value of cos over a period is 0
     # so average height of Hann window is 0.5
     return win
 
+
 def avg_power_spectrum(data, freq=1.0, chunk_size=2048,
                        overlap=True, window=window_hann):
     """Compute the avgerage power spectrum of `data`.
@@ -297,30 +299,32 @@ def avg_power_spectrum(data, freq=1.0, chunk_size=2048,
     If the number of samples in `data` is not a multiple of
     `chunk_size`, we ignore the extra points.
     """
-    assert chunk_size == floor_pow_of_two(chunk_size), \
-        "chunk_size %d should be a power of 2" % chunk_size
+    if chunk_size != floor_pow_of_two(chunk_size):
+        raise ValueError(
+            'chunk_size {} should be a power of 2'.format(chunk_size))
 
-    nchunks = len(data)/chunk_size # integer division = implicit floor
+    nchunks = len(data) // chunk_size  # integer division = implicit floor
     if overlap:
-        chunk_step = chunk_size/2
+        chunk_step = chunk_size / 2
     else:
         chunk_step = chunk_size
-    
-    win = window(chunk_size) # generate a window of the appropriate size
-    freq_axis = linspace(0, freq/2, chunk_size/2+1)
+
+    win = window(chunk_size)  # generate a window of the appropriate size
+    freq_axis = _numpy.linspace(0, freq / 2, chunk_size / 2 + 1)
     # nsamps/2+1 b/c zero-freq and nyqist-freq are both fully real.
     # >>> help(numpy.fft.fftpack.rfft) for Numpy's explaination.
     # See Numerical Recipies for a details.
-    power = zeros((chunk_size/2+1,), dtype=float)
+    power = _numpy.zeros((chunk_size / 2 + 1, ), dtype=_numpy.float)
     for i in range(nchunks):
-        starti = i*chunk_step
-        stopi = starti+chunk_size
-        fft_chunk = rfft(data[starti:stopi]*win)
-        p_chunk = fft_chunk * fft_chunk.conj() 
-        power += p_chunk.astype(float)
-    power /= float(nchunks)
+        starti = i * chunk_step
+        stopi = starti + chunk_size
+        fft_chunk = _numpy.fft.rfft(data[starti:stopi] * win)
+        p_chunk = (fft_chunk * fft_chunk.conj()).real
+        power += p_chunk.astype(_numpy.float)
+    power /= _numpy.float(nchunks)
     return (freq_axis, power)
 
+
 def unitary_avg_power_spectrum(data, freq=1.0, chunk_size=2048,
                                overlap=True, window=window_hann):
     """Compute the unitary average power spectrum of `data`.
@@ -329,23 +333,25 @@ def unitary_avg_power_spectrum(data, freq=1.0, chunk_size=2048,
     --------
     avg_power_spectrum,unitary_power_spectrum
     """
-    freq_axis,power = avg_power_spectrum(data, freq, chunk_size,
-                                         overlap, window)
-    #        2.0 / (freq * chunk_size)          |rfft()|**2 --> unitary_power_spectrum
-    power *= 2.0 / (freq*float(chunk_size)) * 8/3 # see unitary_power_spectrum()            
-    #                                       * 8/3  to remove power from windowing
+    freq_axis,power = avg_power_spectrum(
+        data, freq, chunk_size, overlap, window)
+    #   2.0 / (freq * chunk_size)       |rfft()|**2 --> unitary_power_spectrum
+    # see unitary_power_spectrum()
+    power *= 2.0 / (freq * _numpy.float(chunk_size)) * 8.0 / 3
+    #             * 8/3  to remove power from windowing
     #  <[x(t)*w(t)]**2> = <x(t)**2 * w(t)**2> ~= <x(t)**2> * <w(t)**2>
     # where the ~= is because the frequency of x(t) >> the frequency of w(t).
     # So our calulated power has and extra <w(t)**2> in it.
-    # For the Hann window, <w(t)**2> = <0.5(1 + 2cos + cos**2)> = 1/4 + 0 + 1/8 = 3/8
-    # For low frequency components, where the frequency of x(t) is ~= the frequency of w(t),
-    # The normalization is not perfect. ??
+    # For the Hann window,
+    #   <w(t)**2> = <0.5(1 + 2cos + cos**2)> = 1/4 + 0 + 1/8 = 3/8
+    # For low frequency components,
+    # where the frequency of x(t) is ~= the frequency of w(t),
+    # the normalization is not perfect. ??
     # The normalization approaches perfection as chunk_size -> infinity.
     return (freq_axis, power)
 
 
-
-class TestRFFT (unittest.TestCase):
+class TestRFFT (_unittest.TestCase):
     r"""Ensure Numpy's FFT algorithm acts as expected.
 
     Notes
@@ -354,35 +360,43 @@ class TestRFFT (unittest.TestCase):
 
     .. math:: X_k = \sum_{m=0}^{n-1} x_m \exp^{-2\pi imk/n}
 
-    .. [#dft] See the *Background information* section of :mod:`numpy.fft`.
+    .. [#dft] See the *Background information* section of
+       :py:mod:`numpy.fft`.
     """
     def run_rfft(self, xs, Xs):
-        i = complex(0,1)
+        i = _numpy.complex(0, 1)
         n = len(xs)
         Xa = []
         for k in range(n):
-            Xa.append(sum([x*exp(-2*pi*i*m*k/n) for x,m in zip(xs,range(n))]))
+            Xa.append(sum([x * _numpy.exp(-2 * _numpy.pi * i * m * k / n)
+                           for x,m in zip(xs, range(n))]))
             if k < len(Xs):
-                assert (Xs[k]-Xa[k])/abs(Xa[k]) < 1e-6, \
-                    "rfft mismatch on element %d: %g != %g, relative error %g" \
-                    % (k, Xs[k], Xa[k], (Xs[k]-Xa[k])/abs(Xa[k]))
+                self.assertAlmostEqual(
+                    (Xs[k] - Xa[k]) / _numpy.abs(Xa[k]), 0, 6,
+                    ('rfft mismatch on element {}: {} != {}, '
+                     'relative error {}').format(
+                        k, Xs[k], Xa[k], (Xs[k] - Xa[k]) / _numpy.abs(Xa[k])))
         # Which should satisfy the discrete form of Parseval's theorem
         #   n-1               n-1
-        #   SUM |x_m|^2 = 1/n SUM |X_k|^2. 
+        #   SUM |x_m|^2 = 1/n SUM |X_k|^2.
         #   m=0               k=0
-        timeSum = sum([abs(x)**2 for x in xs])
-        freqSum = sum([abs(X)**2 for X in Xa])
-        assert abs(freqSum/float(n) - timeSum)/timeSum < 1e-6, \
-            "Mismatch on Parseval's, %g != 1/%d * %g" % (timeSum, n, freqSum)
+        timeSum = sum([_numpy.abs(x)**2 for x in xs])
+        freqSum = sum([_numpy.abs(X)**2 for X in Xa])
+        self.assertAlmostEqual(
+            _numpy.abs(freqSum / _numpy.float(n) - timeSum) / timeSum, 0, 6,
+            "Mismatch on Parseval's, {} != 1/{} * {}".format(
+                timeSum, n, freqSum))
 
     def test_rfft(self):
-        xs = [1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1]
-        self.run_rfft(xs, rfft(xs))
+        "Test NumPy's builtin :py:func:`numpy.fft.rfft`"
+        xs = [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1]
+        self.run_rfft(xs, _numpy.fft.rfft(xs))
+
 
-class TestUnitaryRFFT (unittest.TestCase):
-    """Verify `unitary_rfft`.
+class TestUnitaryRFFT (_unittest.TestCase):
+    """Verify :py:func:`unitary_rfft`.
     """
-    def run_unitary_rfft_parsevals(self, xs, freq, freqs, Xs):
+    def run_parsevals(self, xs, freq, freqs, Xs):
         """Check the discretized integral form of Parseval's theorem
 
         Notes
@@ -391,26 +405,28 @@ class TestUnitaryRFFT (unittest.TestCase):
 
         .. math:: \sum_{m=0}^{n-1} |x_m|^2 dt = \sum_{k=0}^{n-1} |X_k|^2 df
         """
-        dt = 1.0/freq
-        df = freqs[1]-freqs[0]
-        assert (df - 1/(len(xs)*dt))/df < 1e-6, \
-            "Mismatch in spacing, %g != 1/(%d*%g)" % (df, len(xs), dt)
+        dt = 1.0 / freq
+        df = freqs[1] - freqs[0]
+        self.assertAlmostEqual(
+            (df - 1 / (len(xs) * dt)) / df, 0, 6,
+            'Mismatch in spacing, {} != 1/({}*{})'.format(df, len(xs), dt))
         Xa = list(Xs)
-        for k in range(len(Xs)-1,1,-1):
+        for k in range(len(Xs) - 1, 1, -1):
             Xa.append(Xa[k])
-        assert len(xs) == len(Xa), "Length mismatch %d != %d" % (len(xs), len(Xa))
-        lhs = sum([abs(x)**2 for x in xs]) * dt
-        rhs = sum([abs(X)**2 for X in Xa]) * df
-        assert abs(lhs - rhs)/lhs < 1e-4, "Mismatch on Parseval's, %g != %g" \
-            % (lhs, rhs)
-    
-    def test_unitary_rfft_parsevals(self):
+        self.assertEqual(len(xs), len(Xa))
+        lhs = sum([_numpy.abs(x)**2 for x in xs]) * dt
+        rhs = sum([_numpy.abs(X)**2 for X in Xa]) * df
+        self.assertAlmostEqual(
+            _numpy.abs(lhs - rhs) / lhs, 0, 3,
+            "Mismatch on Parseval's, {} != {}".format(lhs, rhs))
+
+    def test_parsevals(self):
         "Test unitary rfft on Parseval's theorem"
-        xs = [1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1]
-        dt = pi
-        freqs,Xs = unitary_rfft(xs, 1.0/dt)
-        self.run_unitary_rfft_parsevals(xs, 1.0/dt, freqs, Xs)
-    
+        xs = [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1]
+        dt = _numpy.pi
+        freqs,Xs = unitary_rfft(xs, 1.0 / dt)
+        self.run_parsevals(xs, 1.0 / dt, freqs, Xs)
+
     def rect(self, t):
         r"""Rectangle function.
 
@@ -422,16 +438,15 @@ class TestUnitaryRFFT (unittest.TestCase):
             \rect(t) = \begin{cases}
                1& \text{if $|t| < 0.5$}, \\
                0& \text{if $|t| \ge 0.5$}.
-                       \end{cases}
+                             \end{cases}
         """
-        if abs(t) < 0.5:
+        if _numpy.abs(t) < 0.5:
             return 1
         else:
             return 0
-    
-    def run_unitary_rfft_rect(self, a=1.0, time_shift=5.0, samp_freq=25.6,
-                              samples=256):
-        r"""Test `unitary_rttf` on known function `rect(at)`.
+
+    def run_rect(self, a=1.0, time_shift=5.0, samp_freq=25.6, samples=256):
+        r"""Test :py:func:`unitary_rfft` on known function :py:meth:`rect`.
 
         Notes
         -----
@@ -439,50 +454,52 @@ class TestUnitaryRFFT (unittest.TestCase):
 
         .. math:: \rfft(\rect(at)) = 1/|a|\cdot\sinc(f/a)
         """
-        samp_freq = float(samp_freq)
-        a = float(a)
-    
-        x = zeros((samples,), dtype=float)
-        dt = 1.0/samp_freq
+        samp_freq = _numpy.float(samp_freq)
+        a = _numpy.float(a)
+
+        x = _numpy.zeros((samples,), dtype=_numpy.float)
+        dt = 1.0 / samp_freq
         for i in range(samples):
-            t = i*dt
-            x[i] = self.rect(a*(t-time_shift))
-        freq_axis, X = unitary_rfft(x, samp_freq)
-        #_test_unitary_rfft_parsevals(x, samp_freq, freq_axis, X)
-    
+            t = i * dt
+            x[i] = self.rect(a * (t - time_shift))
+        freq_axis,X = unitary_rfft(x, samp_freq)
+
         # remove the phase due to our time shift
-        j = complex(0.0,1.0) # sqrt(-1)
+        j = _numpy.complex(0.0, 1.0)  # sqrt(-1)
         for i in range(len(freq_axis)):
             f = freq_axis[i]
-            inverse_phase_shift = exp(j*2.0*pi*time_shift*f)
+            inverse_phase_shift = _numpy.exp(
+                j * 2.0 * _numpy.pi * time_shift * f)
             X[i] *= inverse_phase_shift
-    
-        expected = zeros((len(freq_axis),), dtype=float)
+
+        expected = _numpy.zeros((len(freq_axis),), dtype=_numpy.float)
         # normalized sinc(x) = sin(pi x)/(pi x)
         # so sinc(0.5) = sin(pi/2)/(pi/2) = 2/pi
-        assert sinc(0.5) == 2.0/pi, "abnormal sinc()"
+        self.assertEqual(_numpy.sinc(0.5), 2.0 / _numpy.pi)
         for i in range(len(freq_axis)):
             f = freq_axis[i]
-            expected[i] = 1.0/abs(a) * sinc(f/a)
-    
+            expected[i] = 1.0 / _numpy.abs(a) * _numpy.sinc(f / a)
+
         if TEST_PLOTS:
-            pylab.figure()
-            pylab.subplot(211)
-            pylab.plot(arange(0, dt*samples, dt), x)
-            pylab.title('time series')
-            pylab.subplot(212)
-            pylab.plot(freq_axis, X.real, 'r.')
-            pylab.plot(freq_axis, X.imag, 'g.')
-            pylab.plot(freq_axis, expected, 'b-')
-            pylab.title('freq series')
-    
-    def test_unitary_rfft_rect(self):
+            if _pyplot is None:
+                raise _pyplot_import_error
+            figure = _pyplot.figure()
+            time_axes = figure.add_subplot(2, 1, 1)
+            time_axes.plot(_numpy.arange(0, dt * samples, dt), x)
+            time_axes.set_title('time series')
+            freq_axes = figure.add_subplot(2, 1, 2)
+            freq_axes.plot(freq_axis, X.real, 'r.')
+            freq_axes.plot(freq_axis, X.imag, 'g.')
+            freq_axes.plot(freq_axis, expected, 'b-')
+            freq_axes.set_title('freq series')
+
+    def test_rect(self):
         "Test unitary FFTs on variously shaped rectangular functions."
-        self.run_unitary_rfft_rect(a=0.5)
-        self.run_unitary_rfft_rect(a=2.0)
-        self.run_unitary_rfft_rect(a=0.7, samp_freq=50, samples=512)
-        self.run_unitary_rfft_rect(a=3.0, samp_freq=60, samples=1024)
-    
+        self.run_rect(a=0.5)
+        self.run_rect(a=2.0)
+        self.run_rect(a=0.7, samp_freq=50, samples=512)
+        self.run_rect(a=3.0, samp_freq=60, samples=1024)
+
     def gaussian(self, a, t):
         r"""Gaussian function.
 
@@ -491,11 +508,10 @@ class TestUnitaryRFFT (unittest.TestCase):
 
         .. math:: \gaussian(a,t) = \exp^{-at^2}
         """
-        return exp(-a * t**2)
-    
-    def run_unitary_rfft_gaussian(self, a=1.0, time_shift=5.0, samp_freq=25.6,
-                                  samples=256):
-        r"""Test `unitary_rttf` on known function `gaussian(a,t)`.
+        return _numpy.exp(-a * t**2)
+
+    def run_gaussian(self, a=1.0, time_shift=5.0, samp_freq=25.6, samples=256):
+        r"""Test :py:func:`unitary_rttf` on known function :py:meth:`gaussian`.
 
         Notes
         -----
@@ -503,63 +519,69 @@ class TestUnitaryRFFT (unittest.TestCase):
 
         .. math::
 
-            \rfft(\gaussian(a,t)) = \sqrt{\pi/a} \cdot \gaussian(1/a,\pi f)
+            \rfft(\gaussian(a,t))
+              = \sqrt{\pi/a} \cdot \gaussian(1/a,\pi f)
         """
-        samp_freq = float(samp_freq)
-        a = float(a)
-    
-        x = zeros((samples,), dtype=float)
-        dt = 1.0/samp_freq
+        samp_freq = _numpy.float(samp_freq)
+        a = _numpy.float(a)
+
+        x = _numpy.zeros((samples,), dtype=_numpy.float)
+        dt = 1.0 / samp_freq
         for i in range(samples):
-            t = i*dt
-            x[i] = self.gaussian(a, (t-time_shift))
-        freq_axis, X = unitary_rfft(x, samp_freq)
-        #_test_unitary_rfft_parsevals(x, samp_freq, freq_axis, X)
-    
+            t = i * dt
+            x[i] = self.gaussian(a, (t - time_shift))
+        freq_axis,X = unitary_rfft(x, samp_freq)
+
         # remove the phase due to our time shift
-        j = complex(0.0,1.0) # sqrt(-1)
+        j = _numpy.complex(0.0, 1.0)  # sqrt(-1)
         for i in range(len(freq_axis)):
             f = freq_axis[i]
-            inverse_phase_shift = exp(j*2.0*pi*time_shift*f)
+            inverse_phase_shift = _numpy.exp(
+                j * 2.0 * _numpy.pi * time_shift * f)
             X[i] *= inverse_phase_shift
-    
-        expected = zeros((len(freq_axis),), dtype=float)
+
+        expected = _numpy.zeros((len(freq_axis),), dtype=_numpy.float)
         for i in range(len(freq_axis)):
             f = freq_axis[i]
-            expected[i] = sqrt(pi/a) * self.gaussian(1.0/a, pi*f) # see Wikipedia, or do the integral yourself.
-    
+            # see Wikipedia, or do the integral yourself.
+            expected[i] = _numpy.sqrt(_numpy.pi / a) * self.gaussian(
+                1.0 / a, _numpy.pi * f)
+
         if TEST_PLOTS:
-            pylab.figure()
-            pylab.subplot(211)
-            pylab.plot(arange(0, dt*samples, dt), x)
-            pylab.title('time series')
-            pylab.subplot(212)
-            pylab.plot(freq_axis, X.real, 'r.')
-            pylab.plot(freq_axis, X.imag, 'g.')
-            pylab.plot(freq_axis, expected, 'b-')
-            pylab.title('freq series')
-    
-    def test_unitary_rfft_gaussian(self):
+            if _pyplot is None:
+                raise _pyplot_import_error
+            figure = _pyplot.figure()
+            time_axes = figure.add_subplot(2, 1, 1)
+            time_axes.plot(_numpy.arange(0, dt * samples, dt), x)
+            time_axes.set_title('time series')
+            freq_axes = figure.add_subplot(2, 1, 2)
+            freq_axes.plot(freq_axis, X.real, 'r.')
+            freq_axes.plot(freq_axis, X.imag, 'g.')
+            freq_axes.plot(freq_axis, expected, 'b-')
+            freq_axes.set_title('freq series')
+
+    def test_gaussian(self):
         "Test unitary FFTs on variously shaped gaussian functions."
-        self.run_unitary_rfft_gaussian(a=0.5)
-        self.run_unitary_rfft_gaussian(a=2.0)
-        self.run_unitary_rfft_gaussian(a=0.7, samp_freq=50, samples=512)
-        self.run_unitary_rfft_gaussian(a=3.0, samp_freq=60, samples=1024)
-
-class TestUnitaryPowerSpectrum (unittest.TestCase):
-    def run_unitary_power_spectrum_sin(self, sin_freq=10, samp_freq=512,
-                                       samples=1024):
-        x = zeros((samples,), dtype=float)
-        samp_freq = float(samp_freq)
+        self.run_gaussian(a=0.5)
+        self.run_gaussian(a=2.0)
+        self.run_gaussian(a=0.7, samp_freq=50, samples=512)
+        self.run_gaussian(a=3.0, samp_freq=60, samples=1024)
+
+
+class TestUnitaryPowerSpectrum (_unittest.TestCase):
+    def run_sin(self, sin_freq=10, samp_freq=512, samples=1024):
+        x = _numpy.zeros((samples,), dtype=_numpy.float)
+        samp_freq = _numpy.float(samp_freq)
         for i in range(samples):
-            x[i] = sin(2.0 * pi * (i/samp_freq) * sin_freq)
-        freq_axis, power = unitary_power_spectrum(x, samp_freq)
-        imax = argmax(power)
-    
-        expected = zeros((len(freq_axis),), dtype=float)
-        df = samp_freq/float(samples) # df = 1/T, where T = total_time
-        i = int(sin_freq/df)
-        # average power per unit time is 
+            x[i] = _numpy.sin(2.0 * _numpy.pi * (i / samp_freq) * sin_freq)
+        freq_axis,power = unitary_power_spectrum(x, samp_freq)
+        imax = _numpy.argmax(power)
+
+        expected = _numpy.zeros((len(freq_axis),), dtype=_numpy.float)
+        # df = 1/T, where T = total_time
+        df = samp_freq / _numpy.float(samples)
+        i = int(sin_freq / df)
+        # average power per unit time is
         #  P = <x(t)**2>
         # average value of sin(t)**2 = 0.5    (b/c sin**2+cos**2 == 1)
         # so average value of (int sin(t)**2 dt) per unit time is 0.5
@@ -576,52 +598,58 @@ class TestUnitaryPowerSpectrum (unittest.TestCase):
         #  P(f) = 2 |X(f)|**2 / T
         #       = 2 * |0.5 delta(f-f0)|**2 / T
         #       = 0.5 * |delta(f-f0)|**2 / T
-        # but we're discrete and want the integral of the 'delta' to be 1, 
+        # but we're discrete and want the integral of the 'delta' to be 1,
         # so 'delta'*df = 1  --> 'delta' = 1/df, and
         #  P(f) = 0.5 / (df**2 * T)
         #       = 0.5 / df                (T = 1/df)
         expected[i] = 0.5 / df
-    
-        print "The power should be a peak at %g Hz of %g (%g, %g)" % \
-            (sin_freq, expected[i], freq_axis[imax], power[imax])
-        Pexp = 0
-        P    = 0
+
+        LOG.debug('The power should be a peak at {} Hz of {} ({}, {})'.format(
+                sin_freq, expected[i], freq_axis[imax], power[imax]))
+        Pexp = P = 0
         for i in range(len(freq_axis)):
-            Pexp += expected[i] *df
-            P    += power[i] * df
-        print " The total power should be %g (%g)" % (Pexp, P)
-    
+            Pexp += expected[i] * df
+            P += power[i] * df
+        self.assertAlmostEqual(
+            _numpy.abs((P - Pexp) / Pexp), 0, 1,
+            'The total power should be {} ({})'.format(Pexp, P))
+
         if TEST_PLOTS:
-            pylab.figure()
-            pylab.subplot(211)
-            pylab.plot(arange(0, samples/samp_freq, 1.0/samp_freq), x, 'b-')
-            pylab.title('time series')
-            pylab.subplot(212)
-            pylab.plot(freq_axis, power, 'r.')
-            pylab.plot(freq_axis, expected, 'b-')
-            pylab.title('%g samples of sin at %g Hz' % (samples, sin_freq))
-    
-    def test_unitary_power_spectrum_sin(self):
+            if _pyplot is None:
+                raise _pyplot_import_error
+            figure = _pyplot.figure()
+            time_axes = figure.add_subplot(2, 1, 1)
+            time_axes.plot(
+                _numpy.arange(0, samples / samp_freq, 1.0 / samp_freq), x, 'b-')
+            time_axes.set_title('time series')
+            freq_axes = figure.add_subplot(2, 1, 2)
+            freq_axes.plot(freq_axis, power, 'r.')
+            freq_axes.plot(freq_axis, expected, 'b-')
+            freq_axes.set_title(
+                '{} samples of sin at {} Hz'.format(samples, sin_freq))
+
+    def test_sin(self):
         "Test unitary power spectrums on variously shaped sin functions"
-        self.run_unitary_power_spectrum_sin(sin_freq=5, samp_freq=512, samples=1024)
-        self.run_unitary_power_spectrum_sin(sin_freq=5, samp_freq=512, samples=2048)
-        self.run_unitary_power_spectrum_sin(sin_freq=5, samp_freq=512, samples=4098)
-        self.run_unitary_power_spectrum_sin(sin_freq=7, samp_freq=512, samples=1024)
-        self.run_unitary_power_spectrum_sin(sin_freq=5, samp_freq=1024, samples=2048)
-        # finally, with some irrational numbers, to check that I'm not getting lucky
-        self.run_unitary_power_spectrum_sin(sin_freq=pi, samp_freq=100*exp(1), samples=1024)
+        self.run_sin(sin_freq=5, samp_freq=512, samples=1024)
+        self.run_sin(sin_freq=5, samp_freq=512, samples=2048)
+        self.run_sin(sin_freq=5, samp_freq=512, samples=4098)
+        self.run_sin(sin_freq=7, samp_freq=512, samples=1024)
+        self.run_sin(sin_freq=5, samp_freq=1024, samples=2048)
+        # finally, with some irrational numbers, to check that I'm not
+        # getting lucky
+        self.run_sin(
+            sin_freq=_numpy.pi, samp_freq=100 * _numpy.exp(1), samples=1024)
         # test with non-integer number of periods
-        self.run_unitary_power_spectrum_sin(sin_freq=5, samp_freq=512, samples=256)
-    
-    def run_unitary_power_spectrum_delta(self, amp=1, samp_freq=1,
-                                         samples=256):
+        self.run_sin(sin_freq=5, samp_freq=512, samples=256)
+
+    def run_delta(self, amp=1, samp_freq=1, samples=256):
         """TODO
         """
-        x = zeros((samples,), dtype=float)
-        samp_freq = float(samp_freq)
+        x = _numpy.zeros((samples,), dtype=_numpy.float)
+        samp_freq = _numpy.float(samp_freq)
         x[0] = amp
-        freq_axis, power = unitary_power_spectrum(x, samp_freq)
-    
+        freq_axis,power = unitary_power_spectrum(x, samp_freq)
+
         # power = <x(t)**2> = (amp)**2 * dt/T
         # we spread that power over the entire freq_axis [0,fN], so
         #  P(f)  = (amp)**2 dt / (T fN)
@@ -633,132 +661,165 @@ class TestUnitaryPowerSpectrum (unittest.TestCase):
         #  P(f) = amp**2 / (samp_freq * samples/samp_freq * 0.5 samp_freq)
         #       = 2 amp**2 / (samp_freq*samples)
         expected_amp = 2.0 * amp**2 / (samp_freq * samples)
-        expected = ones((len(freq_axis),), dtype=float) * expected_amp
-    
-        print "The power should be flat at y = %g (%g)" % (expected_amp, power[0])
-        
+        expected = _numpy.ones(
+            (len(freq_axis),), dtype=_numpy.float) * expected_amp
+
+        self.assertAlmostEqual(
+            expected_amp, power[0], 4,
+            'The power should be flat at y = {} ({})'.format(
+                expected_amp, power[0]))
+
         if TEST_PLOTS:
-            pylab.figure()
-            pylab.subplot(211)
-            pylab.plot(arange(0, samples/samp_freq, 1.0/samp_freq), x, 'b-')
-            pylab.title('time series')
-            pylab.subplot(212)
-            pylab.plot(freq_axis, power, 'r.')
-            pylab.plot(freq_axis, expected, 'b-')
-            pylab.title('%g samples of delta amp %g' % (samples, amp))
-    
-    def _test_unitary_power_spectrum_delta(self):
+            if _pyplot is None:
+                raise _pyplot_import_error
+            figure = _pyplot.figure()
+            time_axes = figure.add_subplot(2, 1, 1)
+            time_axes.plot(
+                _numpy.arange(0, samples / samp_freq, 1.0 / samp_freq), x, 'b-')
+            time_axes.set_title('time series')
+            freq_axes = figure.add_subplot(2, 1, 2)
+            freq_axes.plot(freq_axis, power, 'r.')
+            freq_axes.plot(freq_axis, expected, 'b-')
+            freq_axes.set_title('{} samples of delta amp {}'.format(samples, amp))
+
+    def test_delta(self):
         "Test unitary power spectrums on various delta functions"
-        _test_unitary_power_spectrum_delta(amp=1, samp_freq=1.0, samples=1024)
-        _test_unitary_power_spectrum_delta(amp=1, samp_freq=1.0, samples=2048)
-        _test_unitary_power_spectrum_delta(amp=1, samp_freq=0.5, samples=2048)# expected = 2*computed
-        _test_unitary_power_spectrum_delta(amp=1, samp_freq=2.0, samples=2048)# expected = 0.5*computed
-        _test_unitary_power_spectrum_delta(amp=3, samp_freq=1.0, samples=1024)
-        _test_unitary_power_spectrum_delta(amp=pi, samp_freq=exp(1), samples=1024)
-    
+        self.run_delta(amp=1, samp_freq=1.0, samples=1024)
+        self.run_delta(amp=1, samp_freq=1.0, samples=2048)
+        # expected = 2*computed
+        self.run_delta(amp=1, samp_freq=0.5, samples=2048)
+        # expected = 0.5*computed
+        self.run_delta(amp=1, samp_freq=2.0, samples=2048)
+        self.run_delta(amp=3, samp_freq=1.0, samples=1024)
+        self.run_delta(amp=_numpy.pi, samp_freq=_numpy.exp(1), samples=1024)
+
     def gaussian(self, area, mean, std, t):
         "Integral over all time = area (i.e. normalized for area=1)"
-        return area/(std*sqrt(2.0*pi)) * exp(-0.5*((t-mean)/std)**2)
-        
-    def run_unitary_power_spectrum_gaussian(self, area=2.5, mean=5, std=1,
-                                            samp_freq=10.24 ,samples=512):
+        return area / (std * _numpy.sqrt(2.0 * _numpy.pi)) * _numpy.exp(
+            -0.5 * ((t-mean)/std)**2)
+
+    def run_gaussian(self, area=2.5, mean=5, std=1, samp_freq=10.24,
+                     samples=512):
         """TODO.
         """
-        x = zeros((samples,), dtype=float)
-        mean = float(mean)
+        x = _numpy.zeros((samples,), dtype=_numpy.float)
+        mean = _numpy.float(mean)
         for i in range(samples):
-            t = i/float(samp_freq)
+            t = i / _numpy.float(samp_freq)
             x[i] = self.gaussian(area, mean, std, t)
-        freq_axis, power = unitary_power_spectrum(x, samp_freq)
-    
-        # generate the predicted curve
-        # by comparing our self.gaussian() form to _gaussian(),
+        freq_axis,power = unitary_power_spectrum(x, samp_freq)
+
+        # generate the predicted curve by comparing our
+        # TestUnitaryPowerSpectrum.gaussian() form to
+        # TestUnitaryRFFT.gaussian(),
         # we see that the Fourier transform of x(t) has parameters:
-        #  std'  = 1/(2 pi std)    (references declaring std' = 1/std are converting to angular frequency, not frequency like we are)
-        #  area' = area/[std sqrt(2*pi)]   (plugging into FT of _gaussian() above)
-        #  mean' = 0               (changing the mean in the time-domain just changes the phase in the freq-domain)
+        #  std'  = 1/(2 pi std)    (references declaring std' = 1/std are
+        #                           converting to angular frequency,
+        #                           not frequency like we are)
+        #  area' = area/[std sqrt(2*pi)]   (plugging into FT of
+        #                                   TestUnitaryRFFT.gaussian() above)
+        #  mean' = 0               (changing the mean in the time-domain just
+        #                           changes the phase in the freq-domain)
         # So our power spectral density per unit time is given by
         #  P(f) = 2 |X(f)|**2 / T
         # Where
         #  T  = samples/samp_freq  (total time of data aquisition)
         mean = 0.0
-        area = area /(std*sqrt(2.0*pi))
-        std = 1.0/(2.0*pi*std)
-        expected = zeros((len(freq_axis),), dtype=float)
-        df = float(samp_freq)/samples # 1/total_time ( = freq_axis[1]-freq_axis[0] = freq_axis[1])
+        area = area / (std * _numpy.sqrt(2.0 * _numpy.pi))
+        std = 1.0 / (2.0 * _numpy.pi * std)
+        expected = _numpy.zeros((len(freq_axis),), dtype=_numpy.float)
+        # 1/total_time ( = freq_axis[1]-freq_axis[0] = freq_axis[1])
+        df = _numpy.float(samp_freq) / samples
         for i in range(len(freq_axis)):
-            f = i*df
+            f = i * df
             gaus = self.gaussian(area, mean, std, f)
-            expected[i] = 2.0 * gaus**2 * samp_freq/samples
-        print "The power should be a half-gaussian, ",
-        print "with a peak at 0 Hz with amplitude %g (%g)" % (expected[0], power[0])
-    
+            expected[i] = 2.0 * gaus**2 * samp_freq / samples
+        self.assertAlmostEqual(
+            expected[0], power[0], 3,
+            ('The power should be a half-gaussian, '
+             'with a peak at 0 Hz with amplitude {} ({})').format(
+                expected[0], power[0]))
+
         if TEST_PLOTS:
-            pylab.figure()
-            pylab.subplot(211)
-            pylab.plot(arange(0, samples/samp_freq, 1.0/samp_freq), x, 'b-')
-            pylab.title('time series')
-            pylab.subplot(212)
-            pylab.plot(freq_axis, power, 'r.')
-            pylab.plot(freq_axis, expected, 'b-')
-            pylab.title('freq series')
-    
-    def test_unitary_power_spectrum_gaussian(self):
+            if _pyplot is None:
+                raise _pyplot_import_error
+            figure = _pyplot.figure()
+            time_axes = figure.add_subplot(2, 1, 1)
+            time_axes.plot(
+                _numpy.arange(0, samples / samp_freq, 1.0 / samp_freq),
+                x, 'b-')
+            time_axes.set_title('time series')
+            freq_axes = figure.add_subplot(2, 1, 2)
+            freq_axes.plot(freq_axis, power, 'r.')
+            freq_axes.plot(freq_axis, expected, 'b-')
+            freq_axes.set_title('freq series')
+
+    def test_gaussian(self):
         "Test unitary power spectrums on various gaussian functions"
-        for area in [1,pi]:
-            for std in [1,sqrt(2)]:
-                for samp_freq in [10.0, exp(1)]:
-                    for samples in [1024,2048]:
-                        self.run_unitary_power_spectrum_gaussian(
+        for area in [1, _numpy.pi]:
+            for std in [1, _numpy.sqrt(2)]:
+                for samp_freq in [10.0, _numpy.exp(1)]:
+                    for samples in [1024, 2048]:
+                        self.run_gaussian(
                             area=area, std=std, samp_freq=samp_freq,
                             samples=samples)
 
-class TestUnitaryAvgPowerSpectrum (unittest.TestCase):
-    def run_unitary_avg_power_spectrum_sin(self, sin_freq=10, samp_freq=512,
-                                           samples=1024, chunk_size=512,
-                                           overlap=True, window=window_hann):
+
+class TestUnitaryAvgPowerSpectrum (_unittest.TestCase):
+    def run_sin(self, sin_freq=10, samp_freq=512, samples=1024, chunk_size=512,
+                overlap=True, window=window_hann, places=3):
         """TODO
         """
-        x = zeros((samples,), dtype=float)
-        samp_freq = float(samp_freq)
+        x = _numpy.zeros((samples,), dtype=_numpy.float)
+        samp_freq = _numpy.float(samp_freq)
         for i in range(samples):
-            x[i] = sin(2.0 * pi * (i/samp_freq) * sin_freq)
-        freq_axis, power = unitary_avg_power_spectrum(x, samp_freq, chunk_size,
-                                                      overlap, window)
-        imax = argmax(power)
-    
-        expected = zeros((len(freq_axis),), dtype=float)
-        df = samp_freq/float(chunk_size) # df = 1/T, where T = total_time
-        i = int(sin_freq/df)
-        expected[i] = 0.5 / df # see _test_unitary_power_spectrum_sin()
-    
-        print "The power should be a peak at %g Hz of %g (%g, %g)" % \
-            (sin_freq, expected[i], freq_axis[imax], power[imax])
-        Pexp = 0
-        P    = 0
+            x[i] = _numpy.sin(2.0 * _numpy.pi * (i / samp_freq) * sin_freq)
+        freq_axis,power = unitary_avg_power_spectrum(
+            x, samp_freq, chunk_size, overlap, window)
+        imax = _numpy.argmax(power)
+
+        expected = _numpy.zeros((len(freq_axis),), dtype=_numpy.float)
+        # df = 1/T, where T = total_time
+        df = samp_freq / _numpy.float(chunk_size)
+        i = int(sin_freq / df)
+        # see TestUnitaryPowerSpectrum.run_unitary_power_spectrum_sin()
+        expected[i] = 0.5 / df
+
+        LOG.debug('The power should peak at {} Hz of {} ({}, {})'.format(
+                sin_freq, expected[i], freq_axis[imax], power[imax]))
+        Pexp = P = 0
         for i in range(len(freq_axis)):
             Pexp += expected[i] * df
-            P    += power[i] * df
-        print " The total power should be %g (%g)" % (Pexp, P)
-    
+            P += power[i] * df
+        self.assertAlmostEqual(
+            Pexp, P, places,
+            'The total power should be {} ({})'.format(Pexp, P))
+
         if TEST_PLOTS:
-            pylab.figure()
-            pylab.subplot(211)
-            pylab.plot(arange(0, samples/samp_freq, 1.0/samp_freq), x, 'b-')
-            pylab.title('time series')
-            pylab.subplot(212)
-            pylab.plot(freq_axis, power, 'r.')
-            pylab.plot(freq_axis, expected, 'b-')
-            pylab.title('%g samples of sin at %g Hz' % (samples, sin_freq))
-    
-    def test_unitary_avg_power_spectrum_sin(self):
+            if _pyplot is None:
+                raise _pyplot_import_error
+            figure = _pyplot.figure()
+            time_axes = figure.add_subplot(2, 1, 1)
+            time_axes.plot(
+                _numpy.arange(0, samples / samp_freq, 1.0 / samp_freq),
+                x, 'b-')
+            time_axes.set_title('time series')
+            freq_axes = figure.add_subplot(2, 1, 2)
+            freq_axes.plot(freq_axis, power, 'r.')
+            freq_axes.plot(freq_axis, expected, 'b-')
+            freq_axes.set_title(
+                '{} samples of sin at {} Hz'.format(samples, sin_freq))
+
+    def test_sin(self):
         "Test unitary avg power spectrums on variously shaped sin functions."
-        self.run_unitary_avg_power_spectrum_sin(sin_freq=5, samp_freq=512, samples=1024)
-        self.run_unitary_avg_power_spectrum_sin(sin_freq=5, samp_freq=512, samples=2048)
-        self.run_unitary_avg_power_spectrum_sin(sin_freq=5, samp_freq=512, samples=4098)
-        self.run_unitary_avg_power_spectrum_sin(sin_freq=17, samp_freq=512, samples=1024)
-        self.run_unitary_avg_power_spectrum_sin(sin_freq=5, samp_freq=1024, samples=2048)
+        self.run_sin(sin_freq=5, samp_freq=512, samples=1024)
+        self.run_sin(sin_freq=5, samp_freq=512, samples=2048)
+        self.run_sin(sin_freq=5, samp_freq=512, samples=4098)
+        self.run_sin(sin_freq=17, samp_freq=512, samples=1024)
+        self.run_sin(sin_freq=5, samp_freq=1024, samples=2048)
         # test long wavelenth sin, so be closer to window frequency
-        self.run_unitary_avg_power_spectrum_sin(sin_freq=1, samp_freq=1024, samples=2048)
-        # finally, with some irrational numbers, to check that I'm not getting lucky
-        self.run_unitary_avg_power_spectrum_sin(sin_freq=pi, samp_freq=100*exp(1), samples=1024)
+        self.run_sin(sin_freq=1, samp_freq=1024, samples=2048, places=0)
+        # finally, with some irrational numbers, to check that I'm not
+        # getting lucky
+        self.run_sin(
+            sin_freq=_numpy.pi, samp_freq=100 * _numpy.exp(1), samples=1024)