summaryrefslogtreecommitdiff
path: root/gr-digital/python/utils
diff options
context:
space:
mode:
Diffstat (limited to 'gr-digital/python/utils')
-rw-r--r--gr-digital/python/utils/.gitignore3
-rw-r--r--gr-digital/python/utils/Makefile.am35
-rw-r--r--gr-digital/python/utils/__init__.py21
-rw-r--r--gr-digital/python/utils/alignment.py139
-rw-r--r--gr-digital/python/utils/gray_code.py66
-rw-r--r--gr-digital/python/utils/mod_codes.py33
-rw-r--r--gr-digital/python/utils/run_tests.in11
7 files changed, 308 insertions, 0 deletions
diff --git a/gr-digital/python/utils/.gitignore b/gr-digital/python/utils/.gitignore
new file mode 100644
index 000000000..60c81fdce
--- /dev/null
+++ b/gr-digital/python/utils/.gitignore
@@ -0,0 +1,3 @@
+run_tests
+Makefile
+Makefile.in
diff --git a/gr-digital/python/utils/Makefile.am b/gr-digital/python/utils/Makefile.am
new file mode 100644
index 000000000..c35951b44
--- /dev/null
+++ b/gr-digital/python/utils/Makefile.am
@@ -0,0 +1,35 @@
+#
+# Copyright 2007,2009 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.
+#
+
+include $(top_srcdir)/Makefile.common
+
+if PYTHON
+utilspythondir = $(grpythondir)/utils
+
+TESTS = \
+ run_tests
+
+nobase_utilspython_PYTHON = \
+ __init__.py \
+ gray_code.py \
+ mod_codes.py \
+ alignment.py
+endif \ No newline at end of file
diff --git a/gr-digital/python/utils/__init__.py b/gr-digital/python/utils/__init__.py
new file mode 100644
index 000000000..b3e997f9f
--- /dev/null
+++ b/gr-digital/python/utils/__init__.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+#
+# Copyright 2011 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.
+#
diff --git a/gr-digital/python/utils/alignment.py b/gr-digital/python/utils/alignment.py
new file mode 100644
index 000000000..d32365866
--- /dev/null
+++ b/gr-digital/python/utils/alignment.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python
+#
+# Copyright 2011 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.
+#
+"""
+This module contains functions for aligning sequences.
+
+>>> import random
+>>> random.seed(1234)
+>>> ran_seq = [random.randint(0,1) for i in range(0, 100)]
+>>> offset_seq = [0] * 20 + ran_seq
+>>> correct, overlap, offset = align_sequences(ran_seq, offset_seq)
+>>> print(correct, overlap, offset)
+(1.0, 100, -20)
+>>> offset_err_seq = []
+>>> for bit in offset_seq:
+... if random.randint(0,4) == 4:
+... offset_err_seq.append(random.randint(0,1))
+... else:
+... offset_err_seq.append(bit)
+>>> correct, overlap, offset = align_sequences(ran_seq, offset_err_seq)
+>>> print(overlap, offset)
+(100, -20)
+
+"""
+
+import random
+
+# DEFAULT PARAMETERS
+# If the fraction of matching bits between two sequences is greater than
+# this the sequences are assumed to be aligned.
+def_correct_cutoff = 0.9
+# The maximum offset to test during sequence alignment.
+def_max_offset = 500
+# The maximum number of samples to take from two sequences to check alignment.
+def_num_samples = 1000
+
+def compare_sequences(d1, d2, offset, sample_indices=None):
+ """
+ Takes two binary sequences and an offset and returns the number of
+ matching entries and the number of compared entries.
+ d1 & d2 -- sequences
+ offset -- offset of d2 relative to d1
+ sample_indices -- a list of indices to use for the comparison
+ """
+ max_index = min(len(d1), len(d2)+offset)
+ if sample_indices is None:
+ sample_indices = range(0, max_index)
+ correct = 0
+ total = 0
+ for i in sample_indices:
+ if i >= max_index:
+ break
+ if d1[i] == d2[i-offset]:
+ correct += 1
+ total += 1
+ return (correct, total)
+
+def random_sample(size, num_samples=def_num_samples, seed=None):
+ """
+ Returns a set of random integers between 0 and (size-1).
+ The set contains no more than num_samples integers.
+ """
+ random.seed(seed)
+ if num_samples > size:
+ indices = set(range(0, size))
+ else:
+ if num_samples > size/2:
+ num_samples = num_samples/2
+ indices = set([])
+ while len(indices) < num_samples:
+ index = random.randint(0, size-1)
+ indices.add(index)
+ indices = list(indices)
+ indices.sort()
+ return indices
+
+def align_sequences(d1, d2,
+ num_samples=def_num_samples,
+ max_offset=def_max_offset,
+ correct_cutoff=def_correct_cutoff,
+ seed=None,
+ indices=None):
+ """
+ Takes two sequences and finds the offset and which the two sequences best
+ match. It returns the fraction correct, the number of entries compared,
+ the offset.
+ d1 & d2 -- sequences to compare
+ num_samples -- the maximum number of entries to compare
+ max_offset -- the maximum offset between the sequences that is checked
+ correct_cutoff -- If the fraction of bits correct is greater than this then
+ the offset is assumed to optimum.
+ seed -- a random number seed
+ indices -- an explicit list of the indices used to compare the two sequences
+ """
+ max_overlap = max(len(d1), len(d2))
+ if indices is None:
+ indices = random_sample(max_overlap, num_samples, seed)
+ max_frac_correct = 0
+ best_offset = None
+ best_compared = None
+ best_correct = None
+ pos_range = range(0, min(len(d1), max_offset))
+ neg_range = range(-1, -min(len(d2), max_offset), -1)
+ # Interleave the positive and negative offsets.
+ int_range = [item for items in zip(pos_range, neg_range) for item in items]
+ for offset in int_range:
+ correct, compared = compare_sequences(d1, d2, offset, indices)
+ frac_correct = 1.0*correct/compared
+ if frac_correct > max_frac_correct:
+ max_frac_correct = frac_correct
+ best_offset = offset
+ best_compared = compared
+ best_correct = correct
+ if frac_correct > correct_cutoff:
+ break
+ return max_frac_correct, best_compared, best_offset, indices
+
+if __name__ == "__main__":
+ import doctest
+ doctest.testmod()
+
diff --git a/gr-digital/python/utils/gray_code.py b/gr-digital/python/utils/gray_code.py
new file mode 100644
index 000000000..926a1ded1
--- /dev/null
+++ b/gr-digital/python/utils/gray_code.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+#
+# Copyright 2011 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.
+#
+
+class GrayCodeGenerator(object):
+ """
+ Generates and caches gray codes.
+ """
+
+ def __init__(self):
+ self.gcs = [0, 1]
+ # The last power of two passed through.
+ self.lp2 = 2
+ # The next power of two that will be passed through.
+ self.np2 = 4
+ # Curent index
+ self.i = 2
+
+ def get_gray_code(self, length):
+ """
+ Returns a list of gray code of given length.
+ """
+ if len(self.gcs) < length:
+ self.generate_new_gray_code(length)
+ return self.gcs[:length]
+
+ def generate_new_gray_code(self, length):
+ """
+ Generates new gray code and places into cache.
+ """
+ while len(self.gcs) < length:
+ if self.i == self.lp2:
+ # if i is a power of two then gray number is of form 1100000...
+ result = self.i + self.i/2
+ else:
+ # if not we take advantage of the symmetry of all but the last bit
+ # around a power of two.
+ result = self.gcs[2*self.lp2-1-self.i] + self.lp2
+ self.gcs.append(result)
+ self.i += 1
+ if self.i == self.np2:
+ self.lp2 = self.i
+ self.np2 = self.i*2
+
+_gray_code_generator = GrayCodeGenerator()
+
+gray_code = _gray_code_generator.get_gray_code
+
diff --git a/gr-digital/python/utils/mod_codes.py b/gr-digital/python/utils/mod_codes.py
new file mode 100644
index 000000000..caacda5cc
--- /dev/null
+++ b/gr-digital/python/utils/mod_codes.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+#
+# Copyright 2011 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.
+#
+
+GRAY_CODE = 'gray'
+SET_PARTITION_CODE = 'set-partition'
+NO_CODE = 'none'
+
+codes = (GRAY_CODE, SET_PARTITION_CODE, NO_CODE)
+
+def invert_code(code):
+ c = enumerate(code)
+ ic = [(b, a) for (a, b) in c]
+ ic.sort()
+ return [a for (b, a) in ic]
diff --git a/gr-digital/python/utils/run_tests.in b/gr-digital/python/utils/run_tests.in
new file mode 100644
index 000000000..adcbdfd21
--- /dev/null
+++ b/gr-digital/python/utils/run_tests.in
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# 1st parameter is absolute path to component source directory
+# 2nd parameter is absolute path to component build directory
+# 3rd parameter is path to Python QA directory
+
+# Note: calling master run_tests.sh in gnuradio core is not strictly
+# correct, as it will result in a partially bogus PYTHONPATH, but it
+# does make the correct paths in the second half so all is well.
+
+# Nothing in here at the moment.