diff options
Diffstat (limited to 'gr-digital/python/utils')
-rw-r--r-- | gr-digital/python/utils/.gitignore | 3 | ||||
-rw-r--r-- | gr-digital/python/utils/Makefile.am | 35 | ||||
-rw-r--r-- | gr-digital/python/utils/__init__.py | 21 | ||||
-rw-r--r-- | gr-digital/python/utils/alignment.py | 139 | ||||
-rw-r--r-- | gr-digital/python/utils/gray_code.py | 66 | ||||
-rw-r--r-- | gr-digital/python/utils/mod_codes.py | 33 | ||||
-rw-r--r-- | gr-digital/python/utils/run_tests.in | 11 |
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. |