From 1206251231696359270a260508551e044f3af33a Mon Sep 17 00:00:00 2001
From: Stefan <stefan.wunsch@student.kit.edu>
Date: Tue, 1 Sep 2015 12:46:09 +0200
Subject: include random.h in swig; add qa_random testcase

---
 gnuradio-runtime/python/gnuradio/gr/qa_random.py | 99 ++++++++++++++++++++++++
 1 file changed, 99 insertions(+)
 create mode 100644 gnuradio-runtime/python/gnuradio/gr/qa_random.py

(limited to 'gnuradio-runtime/python')

diff --git a/gnuradio-runtime/python/gnuradio/gr/qa_random.py b/gnuradio-runtime/python/gnuradio/gr/qa_random.py
new file mode 100644
index 0000000000..39d75f3afa
--- /dev/null
+++ b/gnuradio-runtime/python/gnuradio/gr/qa_random.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+#
+# Copyright 2006,2007,2010 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio 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, or (at your option)
+# any later version.
+#
+# GNU Radio 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 GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import numpy as np
+from scipy.stats import norm, laplace
+
+class test_random(gr_unittest.TestCase):
+
+    # Disclaimer
+    def test_0(self):
+        print 'NOTE: Following tests are not statistically significant! Check out fulltest_random.py for full testing.'
+        self.assertEqual(1,1)
+
+    # Check for range [0,1) of uniform distributed random numbers and print minimal and maximal value
+    def test_1(self):
+        print '# TEST 1'
+        print 'Uniform distributed numbers: Range'
+        num_tests = 10000
+        values = np.zeros(num_tests)
+        rndm = gr.random()
+        for k in range(num_tests):
+            values[k] = rndm.ran1()
+        for value in values:
+            self.assertLess(value, 1)
+            self.assertGreaterEqual(value, 0)
+        print 'Uniform random numbers (num/min/max):', num_tests, min(values), max(values)
+
+    # Check uniformly distributed random numbers on uniformity (without assert, only printing)
+    def test_2(self):
+        print '# TEST 2'
+        print 'Uniform random numbers: Distribution'
+        num_tests = 10000
+        num_bins = 11
+        values = np.zeros(num_tests)
+        rndm = gr.random()
+        for k in range(num_tests):
+            values[k] = rndm.ran1()
+        bins = np.linspace(0,1,num_bins) # These are the bin edges!
+        hist = np.histogram(values,bins)
+        print 'Lower edge bin / upper edge bin / count / expected'
+        for k in range(len(hist[0])):
+                print hist[1][k], hist[1][k+1], hist[0][k], float(num_tests)/(num_bins-1)
+
+    # Check distribution of normally (gaussian, mean=0, variance=1) distributed random numbers (no assert)
+    def test_3(self):
+        print '# TEST 3'
+        print 'Normal random numbers: Distribution'
+        num_tests = 10000
+        num_bins = 11
+        hist_range = [-5,5]
+        values = np.zeros(num_tests)
+        rndm = gr.random()
+        for k in range(num_tests):
+            values[k] = rndm.gasdev()
+        bins = np.linspace(hist_range[0],hist_range[1],num_bins)
+        hist = np.histogram(values,bins)
+        print 'Lower edge bin / upper edge bin / count / expected'
+        for k in range(len(hist[0])):
+            print hist[1][k], hist[1][k+1], hist[0][k], float(norm.cdf(hist[1][k+1])-norm.cdf(hist[1][k]))*num_tests
+
+    # Check distribution of laplacian (mean=0, variance=1) distributed random numbers (no assert)
+    def test_4(self):
+        print '# TEST 4'
+        print 'Laplacian random numbers: Distribution'
+        num_tests = 100000
+        num_bins = 11
+        hist_range = [-5,5]
+        values = np.zeros(num_tests)
+        rndm = gr.random()
+        for k in range(num_tests):
+            values[k] = rndm.laplacian()
+        bins = np.linspace(hist_range[0],hist_range[1],num_bins)
+        hist = np.histogram(values,bins)
+        print 'Lower edge bin / upper edge bin / count / expected'
+        for k in range(len(hist[0])):
+            print hist[1][k], hist[1][k+1], hist[0][k], float(laplace.cdf(hist[1][k+1])-laplace.cdf(hist[1][k]))*num_tests
+
+if __name__ == '__main__':
+    gr_unittest.run(test_random, "test_random.xml")
-- 
cgit v1.2.3


From ebad2162b40b2b144449ff2927dbe89c683c4972 Mon Sep 17 00:00:00 2001
From: Stefan <stefan.wunsch@student.kit.edu>
Date: Tue, 1 Sep 2015 13:21:51 +0200
Subject: fix wrong laplacian random numbers and add testcase

---
 gnuradio-runtime/include/gnuradio/random.h       | 21 ++++++++--
 gnuradio-runtime/lib/math/random.cc              | 23 ++---------
 gnuradio-runtime/python/gnuradio/gr/qa_random.py | 51 +++++++++++++++---------
 3 files changed, 54 insertions(+), 41 deletions(-)

(limited to 'gnuradio-runtime/python')

diff --git a/gnuradio-runtime/include/gnuradio/random.h b/gnuradio-runtime/include/gnuradio/random.h
index e01fcb7be7..454c88ee6c 100644
--- a/gnuradio-runtime/include/gnuradio/random.h
+++ b/gnuradio-runtime/include/gnuradio/random.h
@@ -60,18 +60,33 @@ namespace gr {
     void reseed(long seed);
 
     /*!
-     * \brief uniform random deviate in the range [0.0, 1.0)
+     * \brief Uniform random numbers in the range [0.0, 1.0)
      */
     float ran1();
 
     /*!
-     * \brief normally distributed deviate with zero mean and variance 1
+     * \brief Normally distributed random numbers (Gaussian distribution with zero mean and variance 1)
      */
     float gasdev();
 
+    /*!
+     * \brief Laplacian distributed random numbers with zero mean and variance 1
+     */
     float laplacian();
-    float impulse(float factor);
+
+    /*!
+     * \brief Rayleigh distributed random numbers (zero mean and variance 1 for the underlying Gaussian distributions)
+     */
     float rayleigh();
+
+    /*!
+     * \brief FIXME: add description
+     */
+    float impulse(float factor);
+
+    /*!
+     * \brief Normally distributed random numbers with zero mean and variance 1 on real and imaginary part. This results in a Rayleigh distribution for the amplitude and an uniform distribution for the phase.
+     */
     gr_complex rayleigh_complex();
   };
 
diff --git a/gnuradio-runtime/lib/math/random.cc b/gnuradio-runtime/lib/math/random.cc
index 7170f27edf..ecf70e0f9a 100644
--- a/gnuradio-runtime/lib/math/random.cc
+++ b/gnuradio-runtime/lib/math/random.cc
@@ -130,18 +130,12 @@ namespace gr {
     return d_gset;
   }
 
-  /*
-   * Copied from The KC7WW / OH2BNS Channel Simulator
-   * FIXME Need to check how good this is at some point
-   */
   float
   random::laplacian()
   {
-    float z = ran1();
-    if(z < 0.5)
-      return log(2.0 * z) / M_SQRT2;
-    else
-      return -log(2.0 * (1.0 - z)) / M_SQRT2;
+    float z = ran1()-0.5;
+    if(z>0) return -log(1-2*z);
+    else return log(1+2*z);
   }
 
   /*
@@ -161,10 +155,6 @@ namespace gr {
 
   /*
    * Complex rayleigh is really gaussian I and gaussian Q
-   * It can also be generated by real rayleigh magnitude and
-   * uniform random angle
-   * Adapted from The KC7WW / OH2BNS Channel Simulator
-   * FIXME Need to check how good this is at some point
    */
   gr_complex
   random::rayleigh_complex()
@@ -172,13 +162,6 @@ namespace gr {
     return gr_complex(gasdev(),gasdev());
   }
 
-  /*   Other option
-       mag = rayleigh();
-       ang = 2.0 * M_PI * RNG();
-       *Rx = rxx * cos(z);
-       *Iy = rxx * sin(z);
-       */
-
   float
   random::rayleigh()
   {
diff --git a/gnuradio-runtime/python/gnuradio/gr/qa_random.py b/gnuradio-runtime/python/gnuradio/gr/qa_random.py
index 39d75f3afa..ee4018327b 100644
--- a/gnuradio-runtime/python/gnuradio/gr/qa_random.py
+++ b/gnuradio-runtime/python/gnuradio/gr/qa_random.py
@@ -22,78 +22,93 @@
 
 from gnuradio import gr, gr_unittest
 import numpy as np
-from scipy.stats import norm, laplace
+from scipy.stats import norm, laplace, rayleigh
 
 class test_random(gr_unittest.TestCase):
 
+    num_tests = 10000
+
     # Disclaimer
     def test_0(self):
-        print 'NOTE: Following tests are not statistically significant! Check out fulltest_random.py for full testing.'
+        print 'NOTE: Following tests are not statistically significant!'
+        print 'Realisations per test:',self.num_tests
         self.assertEqual(1,1)
 
     # Check for range [0,1) of uniform distributed random numbers and print minimal and maximal value
     def test_1(self):
         print '# TEST 1'
         print 'Uniform distributed numbers: Range'
-        num_tests = 10000
-        values = np.zeros(num_tests)
+        values = np.zeros(self.num_tests)
         rndm = gr.random()
-        for k in range(num_tests):
+        for k in range(self.num_tests):
             values[k] = rndm.ran1()
         for value in values:
             self.assertLess(value, 1)
             self.assertGreaterEqual(value, 0)
-        print 'Uniform random numbers (num/min/max):', num_tests, min(values), max(values)
+        print 'Uniform random numbers (num/min/max):', self.num_tests, min(values), max(values)
 
     # Check uniformly distributed random numbers on uniformity (without assert, only printing)
     def test_2(self):
         print '# TEST 2'
         print 'Uniform random numbers: Distribution'
-        num_tests = 10000
         num_bins = 11
-        values = np.zeros(num_tests)
+        values = np.zeros(self.num_tests)
         rndm = gr.random()
-        for k in range(num_tests):
+        for k in range(self.num_tests):
             values[k] = rndm.ran1()
         bins = np.linspace(0,1,num_bins) # These are the bin edges!
         hist = np.histogram(values,bins)
         print 'Lower edge bin / upper edge bin / count / expected'
         for k in range(len(hist[0])):
-                print hist[1][k], hist[1][k+1], hist[0][k], float(num_tests)/(num_bins-1)
+                print hist[1][k], hist[1][k+1], hist[0][k], float(self.num_tests)/(num_bins-1)
 
     # Check distribution of normally (gaussian, mean=0, variance=1) distributed random numbers (no assert)
     def test_3(self):
         print '# TEST 3'
         print 'Normal random numbers: Distribution'
-        num_tests = 10000
         num_bins = 11
         hist_range = [-5,5]
-        values = np.zeros(num_tests)
+        values = np.zeros(self.num_tests)
         rndm = gr.random()
-        for k in range(num_tests):
+        for k in range(self.num_tests):
             values[k] = rndm.gasdev()
         bins = np.linspace(hist_range[0],hist_range[1],num_bins)
         hist = np.histogram(values,bins)
         print 'Lower edge bin / upper edge bin / count / expected'
         for k in range(len(hist[0])):
-            print hist[1][k], hist[1][k+1], hist[0][k], float(norm.cdf(hist[1][k+1])-norm.cdf(hist[1][k]))*num_tests
+            print hist[1][k], hist[1][k+1], hist[0][k], float(norm.cdf(hist[1][k+1])-norm.cdf(hist[1][k]))*self.num_tests
 
     # Check distribution of laplacian (mean=0, variance=1) distributed random numbers (no assert)
     def test_4(self):
         print '# TEST 4'
         print 'Laplacian random numbers: Distribution'
-        num_tests = 100000
         num_bins = 11
         hist_range = [-5,5]
-        values = np.zeros(num_tests)
+        values = np.zeros(self.num_tests)
         rndm = gr.random()
-        for k in range(num_tests):
+        for k in range(self.num_tests):
             values[k] = rndm.laplacian()
         bins = np.linspace(hist_range[0],hist_range[1],num_bins)
         hist = np.histogram(values,bins)
         print 'Lower edge bin / upper edge bin / count / expected'
         for k in range(len(hist[0])):
-            print hist[1][k], hist[1][k+1], hist[0][k], float(laplace.cdf(hist[1][k+1])-laplace.cdf(hist[1][k]))*num_tests
+            print hist[1][k], hist[1][k+1], hist[0][k], float(laplace.cdf(hist[1][k+1])-laplace.cdf(hist[1][k]))*self.num_tests
+
+    # Check distribution of laplacian (mean=0, variance=1) distributed random numbers (no assert)
+    def test_5(self):
+        print '# TEST 5'
+        print 'Rayleigh random numbers: Distribution'
+        num_bins = 11
+        hist_range = [0,10]
+        values = np.zeros(self.num_tests)
+        rndm = gr.random()
+        for k in range(self.num_tests):
+            values[k] = rndm.rayleigh()
+        bins = np.linspace(hist_range[0],hist_range[1],num_bins)
+        hist = np.histogram(values,bins)
+        print 'Lower edge bin / upper edge bin / count / expected'
+        for k in range(len(hist[0])):
+            print hist[1][k], hist[1][k+1], hist[0][k], float(rayleigh.cdf(hist[1][k+1])-rayleigh.cdf(hist[1][k]))*self.num_tests
 
 if __name__ == '__main__':
     gr_unittest.run(test_random, "test_random.xml")
-- 
cgit v1.2.3


From e172d55e2bfe63e5b99e7d432f9386c37bff8c84 Mon Sep 17 00:00:00 2001
From: Stefan <stefan.wunsch@student.kit.edu>
Date: Tue, 1 Sep 2015 15:09:17 +0200
Subject: add boost.random as random number generator

---
 gnuradio-runtime/include/gnuradio/random.h       |  21 +++--
 gnuradio-runtime/lib/math/random.cc              | 106 +++++++++--------------
 gnuradio-runtime/python/gnuradio/gr/qa_random.py |  19 ++++
 3 files changed, 73 insertions(+), 73 deletions(-)

(limited to 'gnuradio-runtime/python')

diff --git a/gnuradio-runtime/include/gnuradio/random.h b/gnuradio-runtime/include/gnuradio/random.h
index 454c88ee6c..f15b12a492 100644
--- a/gnuradio-runtime/include/gnuradio/random.h
+++ b/gnuradio-runtime/include/gnuradio/random.h
@@ -37,6 +37,8 @@ static const int RANDOM_MAX = 2147483647; // 2^31-1
 #endif /* RANDOM_MAX */
 
 #include <stdlib.h>
+#include <boost/random.hpp>
+#include <ctime>
 
 namespace gr {
 
@@ -47,17 +49,22 @@ namespace gr {
   class GR_RUNTIME_API random
   {
   protected:
-    static const int NTAB = 32;
     long d_seed;
-    long d_iy;
-    long d_iv[NTAB];
-    int	d_iset;
-    float d_gset;
+    bool d_gauss_stored;
+    float d_gauss_value;
+
+    boost::mt19937 *d_rng; // mersenne twister as random number generator
+    boost::uniform_real<float> *d_uniform; // choose uniform distribution, default is [0,1)
+    boost::variate_generator<boost::mt19937&, boost::uniform_real<float> > *d_generator;
 
   public:
-    random(long seed=3021);
+    random(unsigned int seed=0);
+    ~random();
 
-    void reseed(long seed);
+    /*!
+     * \brief Change the seed for the initialized number generator
+     */
+    void reseed(unsigned int seed);
 
     /*!
      * \brief Uniform random numbers in the range [0.0, 1.0)
diff --git a/gnuradio-runtime/lib/math/random.cc b/gnuradio-runtime/lib/math/random.cc
index ecf70e0f9a..76de3bdde5 100644
--- a/gnuradio-runtime/lib/math/random.cc
+++ b/gnuradio-runtime/lib/math/random.cc
@@ -44,90 +44,67 @@
 
 namespace gr {
 
-#define IA 16807
-#define IM 2147483647
-#define AM (1.0/IM)
-#define IQ 127773
-#define IR 2836
-#define NDIV (1+(IM-1)/NTAB)
-#define EPS 1.2e-7
-#define RNMX (1.0-EPS)
+  random::random(unsigned int seed)
+  {
+    d_gauss_stored = false; // set gasdev (gauss distributed numbers) on calculation state
+
+    // Setup random number generator
+    d_rng = new boost::mt19937;
+    reseed(seed); // set seed for random number generator
+    d_uniform = new boost::uniform_real<float>;
+    d_generator = new boost::variate_generator<boost::mt19937&, boost::uniform_real<float> > (*d_rng,*d_uniform); // create number generator in [0,1) from boost.random
+  }
 
-  random::random(long seed)
+  random::~random()
   {
-    reseed(seed);
+      delete d_rng;
+      delete d_uniform;
+      delete d_generator;
   }
 
+  /*
+   * Seed is initialized with time if the given seed is 0. Otherwise the seed is taken directly. Sets the seed for the random number generator.
+   */
   void
-  random::reseed(long seed)
+  random::reseed(unsigned int seed)
   {
-    d_seed = seed;
-    d_iy = 0;
-    for(int i = 0; i < NTAB; i++)
-      d_iv[i] = 0;
-    d_iset = 0;
-    d_gset = 0;
+    if(seed==0) d_seed = static_cast<unsigned int>(std::time(0)); // FIXME: add seed method correctly
+    else d_seed = seed;
+    d_rng->seed(d_seed);
   }
 
   /*
-   * This looks like it returns a uniform random deviate between 0.0 and 1.0
-   * It looks similar to code from "Numerical Recipes in C".
+   * Returns uniformly distributed numbers in [0,1) taken from boost.random using a Mersenne twister
    */
   float
   random::ran1()
   {
-    int j;
-    long k;
-    float temp;
-
-    if(d_seed <= 0 || !d_iy)  {
-      if(-d_seed < 1)
-        d_seed=1;
-      else
-        d_seed = -d_seed;
-      for(j=NTAB+7;j>=0;j--) {
-        k=d_seed/IQ;
-        d_seed=IA*(d_seed-k*IQ)-IR*k;
-        if(d_seed < 0)
-          d_seed += IM;
-        if(j < NTAB)
-          d_iv[j] = d_seed;
-      }
-      d_iy=d_iv[0];
-    }
-    k=(d_seed)/IQ;
-    d_seed=IA*(d_seed-k*IQ)-IR*k;
-    if(d_seed < 0)
-      d_seed += IM;
-    j=d_iy/NDIV;
-    d_iy=d_iv[j];
-    d_iv[j] = d_seed;
-    temp=AM * d_iy;
-    if(temp > RNMX)
-      temp = RNMX;
-    return temp;
+    return (*d_generator)();
   }
 
   /*
    * Returns a normally distributed deviate with zero mean and variance 1.
-   * Also looks like it's from "Numerical Recipes in C".
+   * Used is the Marsaglia polar method.
+   * Every second call a number is stored because the transformation works only in pairs. Otherwise half calculation is thrown away.
    */
   float
   random::gasdev()
   {
-    float fac,rsq,v1,v2;
-    d_iset = 1 - d_iset;
-    if(d_iset) {
-      do {
-        v1=2.0*ran1()-1.0;
-        v2=2.0*ran1()-1.0;
-        rsq=v1*v1+v2*v2;
-      } while(rsq >= 1.0 || rsq == 0.0);
-      fac= sqrt(-2.0*log(rsq)/rsq);
-      d_gset=v1*fac;
-      return v2*fac;
-    }
-    return d_gset;
+      if(d_gauss_stored){ // just return the stored value if available
+          d_gauss_stored = false;
+          return d_gauss_value;
+      }
+      else{ // generate a pair of gaussian distributed numbers
+          float x,y,s;
+          do{
+              x = 2.0*ran1()-1.0;
+              y = 2.0*ran1()-1.0;
+              s = x*x+y*y;
+          }while(not(s<1.0));
+          d_gauss_stored = true;
+          d_gauss_value = x*sqrt(-2.0*log(s)/s);
+          return y*sqrt(-2.0*log(s)/s);
+      }
   }
 
   float
@@ -153,9 +130,6 @@ namespace gr {
       return z;
   }
 
-  /*
-   * Complex rayleigh is really gaussian I and gaussian Q
-   */
   gr_complex
   random::rayleigh_complex()
   {
diff --git a/gnuradio-runtime/python/gnuradio/gr/qa_random.py b/gnuradio-runtime/python/gnuradio/gr/qa_random.py
index ee4018327b..ef85337ea6 100644
--- a/gnuradio-runtime/python/gnuradio/gr/qa_random.py
+++ b/gnuradio-runtime/python/gnuradio/gr/qa_random.py
@@ -110,5 +110,24 @@ class test_random(gr_unittest.TestCase):
         for k in range(len(hist[0])):
             print hist[1][k], hist[1][k+1], hist[0][k], float(rayleigh.cdf(hist[1][k+1])-rayleigh.cdf(hist[1][k]))*self.num_tests
 
+    # Check seeds (init with time and seed as fix number, no assert)
+    def test_6(self):
+        print '# TEST 6'
+        rndm0 = gr.random(0); # init with time
+        rndm1 = gr.random(42); # init with fix seed
+        num = 5
+
+        print 'Some random numbers in [0,1), should change every run:'
+        for k in range(num):
+            print rndm0.ran1(),
+        print ' '
+
+        print 'Some random numbers in [0,1), should be the same every run:'
+        for k in range(num):
+            print rndm1.ran1(),
+        print '== '
+        print '0.374540120363 0.796543002129 0.950714290142 0.183434784412 0.731993913651'
+
+
 if __name__ == '__main__':
     gr_unittest.run(test_random, "test_random.xml")
-- 
cgit v1.2.3


From 30965aab6c6b28fabb1403c635d016fc532caa0d Mon Sep 17 00:00:00 2001
From: Stefan <stefan.wunsch@student.kit.edu>
Date: Tue, 1 Sep 2015 17:01:29 +0200
Subject: add test-case for reseed feature

---
 gnuradio-runtime/include/gnuradio/random.h       |  4 ++-
 gnuradio-runtime/python/gnuradio/gr/qa_random.py | 38 +++++++++++++++++++-----
 2 files changed, 33 insertions(+), 9 deletions(-)

(limited to 'gnuradio-runtime/python')

diff --git a/gnuradio-runtime/include/gnuradio/random.h b/gnuradio-runtime/include/gnuradio/random.h
index f15b12a492..3ecdf6a2b7 100644
--- a/gnuradio-runtime/include/gnuradio/random.h
+++ b/gnuradio-runtime/include/gnuradio/random.h
@@ -36,6 +36,8 @@
 static const int RANDOM_MAX = 2147483647; // 2^31-1
 #endif /* RANDOM_MAX */
 
+// FIXME: RANDOM_MAX still necessary? Some test-cases (use grep, e.g. in gr-filter or gr-atsc) use this constant, but combine it with the random() function from the std namespace. That should not the way to use this.
+
 #include <stdlib.h>
 #include <boost/random.hpp>
 #include <ctime>
@@ -62,7 +64,7 @@ namespace gr {
     ~random();
 
     /*!
-     * \brief Change the seed for the initialized number generator
+     * \brief Change the seed for the initialized number generator. seed = 0 initializes the random number generator with the system time. Note that a fast initialization of various instances can result in the same seed.
      */
     void reseed(unsigned int seed);
 
diff --git a/gnuradio-runtime/python/gnuradio/gr/qa_random.py b/gnuradio-runtime/python/gnuradio/gr/qa_random.py
index ef85337ea6..c0d9a7f34c 100644
--- a/gnuradio-runtime/python/gnuradio/gr/qa_random.py
+++ b/gnuradio-runtime/python/gnuradio/gr/qa_random.py
@@ -23,6 +23,7 @@
 from gnuradio import gr, gr_unittest
 import numpy as np
 from scipy.stats import norm, laplace, rayleigh
+#from time import sleep
 
 class test_random(gr_unittest.TestCase):
 
@@ -110,24 +111,45 @@ class test_random(gr_unittest.TestCase):
         for k in range(len(hist[0])):
             print hist[1][k], hist[1][k+1], hist[0][k], float(rayleigh.cdf(hist[1][k+1])-rayleigh.cdf(hist[1][k]))*self.num_tests
 
-    # Check seeds (init with time and seed as fix number, no assert)
+    # Check seeds (init with time and seed as fix number)
     def test_6(self):
         print '# TEST 6'
-        rndm0 = gr.random(0); # init with time
-        rndm1 = gr.random(42); # init with fix seed
         num = 5
 
         print 'Some random numbers in [0,1), should change every run:'
+        rndm0 = gr.random(0); # init with time
+        # NOTE: the sleep increases the executiont time massively, remove assert for convenience
+        #sleep(1)
+        #rndm1 = gr.random(0); # init with fix seed
         for k in range(num):
-            print rndm0.ran1(),
+            x = rndm0.ran1();
+            print x,
+        #    y = rndm1.ran1();
+        #    print x, '!=', y
+        #    self.assertNotEqual(x,y)
         print ' '
 
-        print 'Some random numbers in [0,1), should be the same every run:'
+        print 'Some random numbers in [0,1) (seed two instances), should be the same every run:'
+        rndm0 = gr.random(42); # init with time
+        rndm1 = gr.random(42); # init with fix seed
         for k in range(num):
-            print rndm1.ran1(),
-        print '== '
-        print '0.374540120363 0.796543002129 0.950714290142 0.183434784412 0.731993913651'
+            x = rndm0.ran1();
+            y = rndm1.ran1();
+            print x, '=', y
+            self.assertEqual(x,y)
 
+        print 'Some random numbers in [0,1) (reseed one instance), should be the same every run:'
+        x = np.zeros(num)
+        y = np.zeros(num)
+        rndm0 = gr.random(42); # init with time
+        for k in range(num):
+            x[k] = rndm0.ran1();
+        rndm1.reseed(43); # init with fix seed
+        for k in range(num):
+            y[k] = rndm0.ran1();
+        for k in range(num):
+            print x[k], '!=', y[k]
+            self.assertNotEqual(x[k],y[k])
 
 if __name__ == '__main__':
     gr_unittest.run(test_random, "test_random.xml")
-- 
cgit v1.2.3


From 44fb1cb0482fa778c8e652164551711818db5476 Mon Sep 17 00:00:00 2001
From: Stefan <stefan.wunsch@student.kit.edu>
Date: Fri, 4 Sep 2015 11:22:13 +0200
Subject: redo qa_random without print statements and scipy; add stand-alone
 evaluation script in gnuradio-runtime/apps

---
 gnuradio-runtime/apps/evaluation_random_numbers.py | 139 +++++++++++++++++++++
 gnuradio-runtime/python/gnuradio/gr/qa_random.py   | 109 ++--------------
 2 files changed, 148 insertions(+), 100 deletions(-)
 create mode 100644 gnuradio-runtime/apps/evaluation_random_numbers.py

(limited to 'gnuradio-runtime/python')

diff --git a/gnuradio-runtime/apps/evaluation_random_numbers.py b/gnuradio-runtime/apps/evaluation_random_numbers.py
new file mode 100644
index 0000000000..069493c73e
--- /dev/null
+++ b/gnuradio-runtime/apps/evaluation_random_numbers.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python
+#
+# Copyright 2015 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio 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, or (at your option)
+# any later version.
+#
+# GNU Radio 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 GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr
+import numpy as np
+from scipy.stats import norm, laplace, rayleigh
+from matplotlib import pyplot as plt
+
+# NOTE: scipy and matplotlib are optional packages and not included in the default gnuradio dependencies
+
+#*** SETUP ***#
+
+# Number of realisations per histogram
+num_tests = 1000000
+
+# Set number of bins in histograms
+uniform_num_bins = 31
+gauss_num_bins = 31
+rayleigh_num_bins = 31
+laplace_num_bins = 31
+
+rndm = gr.random() # instance of gnuradio random class (gr::random)
+
+print 'All histograms contain',num_tests,'realisations.'
+
+#*** GENERATE DATA ***#
+
+uniform_values = np.zeros(num_tests)
+gauss_values = np.zeros(num_tests)
+rayleigh_values = np.zeros(num_tests)
+laplace_values = np.zeros(num_tests)
+
+for k in range(num_tests):
+    uniform_values[k] = rndm.ran1()
+    gauss_values[k] = rndm.gasdev()
+    rayleigh_values[k] = rndm.rayleigh()
+    laplace_values[k] = rndm.laplacian()
+
+#*** HISTOGRAM DATA AND CALCULATE EXPECTED COUNTS ***#
+
+uniform_bins = np.linspace(0,1,uniform_num_bins)
+gauss_bins = np.linspace(-8,8,gauss_num_bins)
+laplace_bins = np.linspace(-8,8,laplace_num_bins)
+rayleigh_bins = np.linspace(0,10,rayleigh_num_bins)
+
+uniform_hist = np.histogram(uniform_values,uniform_bins)
+gauss_hist = np.histogram(gauss_values,gauss_bins)
+rayleigh_hist = np.histogram(rayleigh_values,rayleigh_bins)
+laplace_hist = np.histogram(laplace_values,laplace_bins)
+
+uniform_expected = np.zeros(uniform_num_bins-1)
+gauss_expected = np.zeros(gauss_num_bins-1)
+rayleigh_expected = np.zeros(rayleigh_num_bins-1)
+laplace_expected = np.zeros(laplace_num_bins-1)
+
+for k in range(len(uniform_hist[0])):
+    uniform_expected[k] = num_tests/float(uniform_num_bins-1)
+
+for k in range(len(gauss_hist[0])):
+    gauss_expected[k] = float(norm.cdf(gauss_hist[1][k+1])-norm.cdf(gauss_hist[1][k]))*num_tests
+
+for k in range(len(rayleigh_hist[0])):
+    rayleigh_expected[k] = float(rayleigh.cdf(rayleigh_hist[1][k+1])-rayleigh.cdf(rayleigh_hist[1][k]))*num_tests
+
+for k in range(len(laplace_hist[0])):
+    laplace_expected[k] = float(laplace.cdf(laplace_hist[1][k+1])-laplace.cdf(laplace_hist[1][k]))*num_tests
+
+#*** PLOT HISTOGRAMS AND EXPECTATIONS TAKEN FROM SCIPY ***#
+
+uniform_bins_center = uniform_bins[0:-1]+(uniform_bins[1]-uniform_bins[0])/2.0
+gauss_bins_center = gauss_bins[0:-1]+(gauss_bins[1]-gauss_bins[0])/2.0
+rayleigh_bins_center = rayleigh_bins[0:-1]+(rayleigh_bins[1]-rayleigh_bins[0])/2.0
+laplace_bins_center = laplace_bins[0:-1]+(laplace_bins[1]-laplace_bins[0])/2.0
+
+plt.figure(1)
+
+plt.subplot(2,1,1)
+plt.plot(uniform_bins_center,uniform_hist[0],'s--',uniform_bins_center,uniform_expected,'o:')
+plt.xlabel('Bins'), plt.ylabel('Count'), plt.title('Uniform: Distribution')
+plt.legend(['histogram gr::random','calculation scipy'],loc=1)
+
+plt.subplot(2,1,2)
+plt.plot(uniform_bins_center,uniform_hist[0]/uniform_expected,'rs--')
+plt.xlabel('Bins'), plt.ylabel('Relative deviation'), plt.title('Uniform: Relative deviation to scipy')
+
+plt.figure(2)
+
+plt.subplot(2,1,1)
+plt.plot(gauss_bins_center,gauss_hist[0],'s--',gauss_bins_center,gauss_expected,'o:')
+plt.xlabel('Bins'), plt.ylabel('Count'), plt.title('Gauss: Distribution')
+plt.legend(['histogram gr::random','calculation scipy'],loc=1)
+
+plt.subplot(2,1,2)
+plt.plot(gauss_bins_center,gauss_hist[0]/gauss_expected,'rs--')
+plt.xlabel('Bins'), plt.ylabel('Relative deviation'), plt.title('Gauss: Relative deviation to scipy')
+
+plt.figure(3)
+
+plt.subplot(2,1,1)
+plt.plot(rayleigh_bins_center,rayleigh_hist[0],'s--',rayleigh_bins_center,rayleigh_expected,'o:')
+plt.xlabel('Bins'), plt.ylabel('Count'), plt.title('Rayleigh: Distribution')
+plt.legend(['histogram gr::random','calculation scipy'],loc=1)
+
+
+plt.subplot(2,1,2)
+plt.plot(rayleigh_bins_center,rayleigh_hist[0]/rayleigh_expected,'rs--')
+plt.xlabel('Bins'), plt.ylabel('Relative deviation'), plt.title('Rayleigh: Relative deviation to scipy')
+
+plt.figure(4)
+
+plt.subplot(2,1,1)
+plt.plot(laplace_bins_center,laplace_hist[0],'s--',laplace_bins_center,laplace_expected,'o:')
+plt.xlabel('Bins'), plt.ylabel('Count'), plt.title('Laplace: Distribution')
+plt.legend(['histogram gr::random','calculation scipy'],loc=1)
+
+plt.subplot(2,1,2)
+plt.plot(laplace_bins_center,laplace_hist[0]/laplace_expected,'rs--')
+plt.xlabel('Bins'), plt.ylabel('Relative deviation'), plt.title('Laplace: Relative deviation to scipy')
+
+plt.show()
diff --git a/gnuradio-runtime/python/gnuradio/gr/qa_random.py b/gnuradio-runtime/python/gnuradio/gr/qa_random.py
index c0d9a7f34c..83fee56181 100644
--- a/gnuradio-runtime/python/gnuradio/gr/qa_random.py
+++ b/gnuradio-runtime/python/gnuradio/gr/qa_random.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2006,2007,2010 Free Software Foundation, Inc.
+# Copyright 2006,2007,2010,2015 Free Software Foundation, Inc.
 #
 # This file is part of GNU Radio
 #
@@ -22,133 +22,42 @@
 
 from gnuradio import gr, gr_unittest
 import numpy as np
-from scipy.stats import norm, laplace, rayleigh
-#from time import sleep
 
 class test_random(gr_unittest.TestCase):
 
-    num_tests = 10000
+    # NOTE: For tests on the output distribution of the random numbers, see gnuradio-runtime/apps/evaluation_random_numbers.py.
 
-    # Disclaimer
-    def test_0(self):
-        print 'NOTE: Following tests are not statistically significant!'
-        print 'Realisations per test:',self.num_tests
-        self.assertEqual(1,1)
-
-    # Check for range [0,1) of uniform distributed random numbers and print minimal and maximal value
+    # Check for range [0,1) of uniform distributed random numbers
     def test_1(self):
-        print '# TEST 1'
-        print 'Uniform distributed numbers: Range'
-        values = np.zeros(self.num_tests)
+        num_tests = 10000
+        values = np.zeros(num_tests)
         rndm = gr.random()
-        for k in range(self.num_tests):
+        for k in range(num_tests):
             values[k] = rndm.ran1()
         for value in values:
             self.assertLess(value, 1)
             self.assertGreaterEqual(value, 0)
-        print 'Uniform random numbers (num/min/max):', self.num_tests, min(values), max(values)
 
-    # Check uniformly distributed random numbers on uniformity (without assert, only printing)
+    # Check reseed method (init with time and seed as fix number)
     def test_2(self):
-        print '# TEST 2'
-        print 'Uniform random numbers: Distribution'
-        num_bins = 11
-        values = np.zeros(self.num_tests)
-        rndm = gr.random()
-        for k in range(self.num_tests):
-            values[k] = rndm.ran1()
-        bins = np.linspace(0,1,num_bins) # These are the bin edges!
-        hist = np.histogram(values,bins)
-        print 'Lower edge bin / upper edge bin / count / expected'
-        for k in range(len(hist[0])):
-                print hist[1][k], hist[1][k+1], hist[0][k], float(self.num_tests)/(num_bins-1)
-
-    # Check distribution of normally (gaussian, mean=0, variance=1) distributed random numbers (no assert)
-    def test_3(self):
-        print '# TEST 3'
-        print 'Normal random numbers: Distribution'
-        num_bins = 11
-        hist_range = [-5,5]
-        values = np.zeros(self.num_tests)
-        rndm = gr.random()
-        for k in range(self.num_tests):
-            values[k] = rndm.gasdev()
-        bins = np.linspace(hist_range[0],hist_range[1],num_bins)
-        hist = np.histogram(values,bins)
-        print 'Lower edge bin / upper edge bin / count / expected'
-        for k in range(len(hist[0])):
-            print hist[1][k], hist[1][k+1], hist[0][k], float(norm.cdf(hist[1][k+1])-norm.cdf(hist[1][k]))*self.num_tests
-
-    # Check distribution of laplacian (mean=0, variance=1) distributed random numbers (no assert)
-    def test_4(self):
-        print '# TEST 4'
-        print 'Laplacian random numbers: Distribution'
-        num_bins = 11
-        hist_range = [-5,5]
-        values = np.zeros(self.num_tests)
-        rndm = gr.random()
-        for k in range(self.num_tests):
-            values[k] = rndm.laplacian()
-        bins = np.linspace(hist_range[0],hist_range[1],num_bins)
-        hist = np.histogram(values,bins)
-        print 'Lower edge bin / upper edge bin / count / expected'
-        for k in range(len(hist[0])):
-            print hist[1][k], hist[1][k+1], hist[0][k], float(laplace.cdf(hist[1][k+1])-laplace.cdf(hist[1][k]))*self.num_tests
-
-    # Check distribution of laplacian (mean=0, variance=1) distributed random numbers (no assert)
-    def test_5(self):
-        print '# TEST 5'
-        print 'Rayleigh random numbers: Distribution'
-        num_bins = 11
-        hist_range = [0,10]
-        values = np.zeros(self.num_tests)
-        rndm = gr.random()
-        for k in range(self.num_tests):
-            values[k] = rndm.rayleigh()
-        bins = np.linspace(hist_range[0],hist_range[1],num_bins)
-        hist = np.histogram(values,bins)
-        print 'Lower edge bin / upper edge bin / count / expected'
-        for k in range(len(hist[0])):
-            print hist[1][k], hist[1][k+1], hist[0][k], float(rayleigh.cdf(hist[1][k+1])-rayleigh.cdf(hist[1][k]))*self.num_tests
-
-    # Check seeds (init with time and seed as fix number)
-    def test_6(self):
-        print '# TEST 6'
         num = 5
 
-        print 'Some random numbers in [0,1), should change every run:'
-        rndm0 = gr.random(0); # init with time
-        # NOTE: the sleep increases the executiont time massively, remove assert for convenience
-        #sleep(1)
-        #rndm1 = gr.random(0); # init with fix seed
-        for k in range(num):
-            x = rndm0.ran1();
-            print x,
-        #    y = rndm1.ran1();
-        #    print x, '!=', y
-        #    self.assertNotEqual(x,y)
-        print ' '
-
-        print 'Some random numbers in [0,1) (seed two instances), should be the same every run:'
         rndm0 = gr.random(42); # init with time
         rndm1 = gr.random(42); # init with fix seed
         for k in range(num):
             x = rndm0.ran1();
             y = rndm1.ran1();
-            print x, '=', y
             self.assertEqual(x,y)
 
-        print 'Some random numbers in [0,1) (reseed one instance), should be the same every run:'
         x = np.zeros(num)
         y = np.zeros(num)
-        rndm0 = gr.random(42); # init with time
+        rndm0 = gr.random(42); # init with fix seed 1
         for k in range(num):
             x[k] = rndm0.ran1();
-        rndm1.reseed(43); # init with fix seed
+        rndm1.reseed(43); # init with fix seed 2
         for k in range(num):
             y[k] = rndm0.ran1();
         for k in range(num):
-            print x[k], '!=', y[k]
             self.assertNotEqual(x[k],y[k])
 
 if __name__ == '__main__':
-- 
cgit v1.2.3