FFT_tools: add LOG and rework tests to use self.assert...().
authorW. Trevor King <wking@tremily.us>
Mon, 19 Nov 2012 06:38:46 +0000 (01:38 -0500)
committerW. Trevor King <wking@tremily.us>
Mon, 19 Nov 2012 06:38:46 +0000 (01:38 -0500)
Now you don't need to compare expected-vs-actual by hand.  The
troublesome

  self.run_sin(sin_freq=1, samp_freq=1024, samples=2048)

case is handled by explicitly making a very fuzzy comparison.

This gets rid of all the print() statements.

FFT_tools.py

index 51a2fea5d2bb50583ac38a9e9af316d8cf350284..abdc75f455542f7c14f84f7ce2de2dcc4aa4b806 100644 (file)
@@ -26,6 +26,7 @@ Main entry functions:
 * :func:`unitary_avg_power_spectrum`
 """
 
+import logging as _logging
 import unittest as _unittest
 
 import numpy as _numpy
@@ -33,6 +34,12 @@ import numpy as _numpy
 
 __version__ = '0.4'
 
+
+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
 
@@ -358,22 +365,21 @@ class TestRFFT (_unittest.TestCase):
             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):
-                if (Xs[k] - Xa[k]) / _numpy.abs(Xa[k]) >= 1e-6:
-                    raise ValueError(
-                        ('rfft mismatch on element {}: {} != {}, '
-                         'relative error {}').format(
-                            k, Xs[k], Xa[k],
-                            (Xs[k] - Xa[k]) / _numpy.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.
         #   m=0               k=0
         timeSum = sum([_numpy.abs(x)**2 for x in xs])
         freqSum = sum([_numpy.abs(X)**2 for X in Xa])
-        if _numpy.abs(freqSum / _numpy.float(n) - timeSum) / timeSum >= 1e-6:
-            raise ValueError(
-                "Mismatch on Parseval's, {} != 1/{} * {}".format(
-                    timeSum, n, freqSum))
+        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]
@@ -394,20 +400,18 @@ class TestUnitaryRFFT (_unittest.TestCase):
         """
         dt = 1.0 / freq
         df = freqs[1] - freqs[0]
-        if (df - 1 / (len(xs) * dt)) / df >= 1e-6:
-            raise ValueError(
-                'Mismatch in spacing, {} != 1/({}*{})'.format(df, len(xs), dt))
+        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):
             Xa.append(Xa[k])
-        if len(xs) != len(Xa):
-            raise ValueError(
-                'Length mismatch {} != {}'.format(len(xs), len(Xa)))
+        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
-        if _numpy.abs(lhs - rhs) / lhs >= 1e-4:
-            raise ValueError(
-                "Mismatch on Parseval's, {} != {}".format(lhs, rhs))
+        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"
@@ -464,8 +468,7 @@ class TestUnitaryRFFT (_unittest.TestCase):
         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
-        if _numpy.sinc(0.5) != 2.0 / _numpy.pi:
-            raise ValueError('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 / _numpy.abs(a) * _numpy.sinc(f / a)
@@ -589,13 +592,15 @@ class TestUnitaryPowerSpectrum (_unittest.TestCase):
         #       = 0.5 / df                (T = 1/df)
         expected[i] = 0.5 / df
 
-        print('The power should be a peak at {} Hz of {} ({}, {})'.format(
+        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 {} ({})'.format(Pexp, P))
+        self.assertAlmostEqual(
+            _numpy.abs((P - Pexp) / Pexp), 0, 1,
+            'The total power should be {} ({})'.format(Pexp, P))
 
         if TEST_PLOTS:
             figure = _pyplot.figure()
@@ -645,8 +650,10 @@ class TestUnitaryPowerSpectrum (_unittest.TestCase):
         expected = _numpy.ones(
             (len(freq_axis),), dtype=_numpy.float) * expected_amp
 
-        print('The power should be flat at y = {} ({})'.format(
-            expected_amp, power[0]))
+        self.assertAlmostEqual(
+            expected_amp, power[0], 4,
+            'The power should be flat at y = {} ({})'.format(
+                expected_amp, power[0]))
 
         if TEST_PLOTS:
             figure = _pyplot.figure()
@@ -711,8 +718,10 @@ class TestUnitaryPowerSpectrum (_unittest.TestCase):
             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, '
-               'with a peak at 0 Hz with amplitude {} ({})').format(
+        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:
@@ -740,7 +749,7 @@ class TestUnitaryPowerSpectrum (_unittest.TestCase):
 
 class TestUnitaryAvgPowerSpectrum (_unittest.TestCase):
     def run_sin(self, sin_freq=10, samp_freq=512, samples=1024, chunk_size=512,
-                overlap=True, window=window_hann):
+                overlap=True, window=window_hann, places=3):
         """TODO
         """
         x = _numpy.zeros((samples,), dtype=_numpy.float)
@@ -758,13 +767,15 @@ class TestUnitaryAvgPowerSpectrum (_unittest.TestCase):
         # see TestUnitaryPowerSpectrum.run_unitary_power_spectrum_sin()
         expected[i] = 0.5 / df
 
-        print('The power should peak at {} Hz of {} ({}, {})'.format(
-            sin_freq, expected[i], freq_axis[imax], power[imax]))
+        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 {} ({})'.format(Pexp, P))
+        self.assertAlmostEqual(
+            Pexp, P, places,
+            'The total power should be {} ({})'.format(Pexp, P))
 
         if TEST_PLOTS:
             figure = _pyplot.figure()
@@ -787,7 +798,7 @@ class TestUnitaryAvgPowerSpectrum (_unittest.TestCase):
         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_sin(sin_freq=1, samp_freq=1024, samples=2048)
+        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(