summaryrefslogtreecommitdiff
path: root/gr-atsc/src/python
diff options
context:
space:
mode:
authorjcorgan <jcorgan@221aa14e-8319-0410-a670-987f0aec2ac5>2006-08-03 04:51:51 +0000
committerjcorgan <jcorgan@221aa14e-8319-0410-a670-987f0aec2ac5>2006-08-03 04:51:51 +0000
commit5d69a524f81f234b3fbc41d49ba18d6f6886baba (patch)
treeb71312bf7f1e8d10fef0f3ac6f28784065e73e72 /gr-atsc/src/python
Houston, we have a trunk.
git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@3122 221aa14e-8319-0410-a670-987f0aec2ac5
Diffstat (limited to 'gr-atsc/src/python')
-rw-r--r--gr-atsc/src/python/Makefile.am33
-rw-r--r--gr-atsc/src/python/atsc_utils.py74
-rwxr-xr-xgr-atsc/src/python/qa_atsc.py217
-rw-r--r--gr-atsc/src/python/run_tests.in47
4 files changed, 371 insertions, 0 deletions
diff --git a/gr-atsc/src/python/Makefile.am b/gr-atsc/src/python/Makefile.am
new file mode 100644
index 0000000000..bdca325715
--- /dev/null
+++ b/gr-atsc/src/python/Makefile.am
@@ -0,0 +1,33 @@
+#
+# Copyright 2004 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 2, 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., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+include $(top_srcdir)/Makefile.common
+
+EXTRA_DIST = run_tests.in
+
+
+TESTS = \
+ run_tests
+
+
+noinst_PYTHON = \
+ atsc_utils.py \
+ qa_atsc.py
diff --git a/gr-atsc/src/python/atsc_utils.py b/gr-atsc/src/python/atsc_utils.py
new file mode 100644
index 0000000000..11976640cf
--- /dev/null
+++ b/gr-atsc/src/python/atsc_utils.py
@@ -0,0 +1,74 @@
+#
+# Copyright 2006 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 2, 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., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+import random
+import sys
+
+MPEG_SYNC_BYTE = 0x47
+
+def make_fake_transport_stream_packet(npkts):
+ """
+ Return a sequence of 8-bit ints that represents an MPEG Transport Stream packet.
+
+ @param npkts: how many 188-byte packets to return
+
+ FYI, each ATSC Data Frame contains two Data Fields, each of which contains
+ 312 data segments. Each transport stream packet maps to a data segment.
+ """
+ r = [0] * (npkts * 188)
+ i = 0
+ for j in range(npkts):
+ r[i+0] = MPEG_SYNC_BYTE
+ r[i+1] = random.randint(0, 127) # top bit (transport error bit) clear
+ i = i + 2
+ for n in range(186):
+ r[i + n] = random.randint(0, 255)
+ i = i + 186
+
+ return r
+
+
+def pad_stream(src, sizeof_total, sizeof_pad):
+ sizeof_valid = sizeof_total - sizeof_pad
+ assert sizeof_valid > 0
+ assert (len(src) % sizeof_valid) == 0
+ npkts = len(src) // sizeof_valid
+ dst = [0] * (npkts * sizeof_total)
+ for i in range(npkts):
+ src_s = i * sizeof_valid
+ dst_s = i * sizeof_total
+ dst[dst_s:dst_s + sizeof_valid] = src[src_s:src_s + sizeof_valid]
+ return dst
+
+
+def depad_stream(src, sizeof_total, sizeof_pad):
+ sizeof_valid = sizeof_total - sizeof_pad
+ assert sizeof_valid > 0
+ assert (len(src) % sizeof_total) == 0
+ npkts = len(src) // sizeof_total
+ dst = [0] * (npkts * sizeof_valid)
+ for i in range(npkts):
+ src_s = i * sizeof_total
+ dst_s = i * sizeof_valid
+ dst[dst_s:dst_s + sizeof_valid] = src[src_s:src_s + sizeof_valid]
+ return dst
+
+
diff --git a/gr-atsc/src/python/qa_atsc.py b/gr-atsc/src/python/qa_atsc.py
new file mode 100755
index 0000000000..d9c94e195c
--- /dev/null
+++ b/gr-atsc/src/python/qa_atsc.py
@@ -0,0 +1,217 @@
+#!/usr/bin/env python
+#
+# Copyright 2004,2006 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 2, 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., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import atsc # qa code needs to run without being installed
+#from gnuradio import atsc
+from atsc_utils import *
+import sys
+
+
+class memoize(object):
+ def __init__(self, thunk):
+ self.thunk = thunk
+ self.cached = False
+ self.value = None
+
+ def __call__(self):
+ if self.cached:
+ return self.value
+ self.value = self.thunk()
+ self.cached = True
+ return self.value
+
+
+"""
+Make a fake transport stream that's big enough for our purposes.
+We generate 8 full fields. This is relatively expensive. It
+takes about 2 seconds to execute.
+"""
+make_transport_stream = \
+ memoize(lambda : tuple(make_fake_transport_stream_packet(8 * atsc.ATSC_DSEGS_PER_FIELD)))
+
+
+def pad_transport_stream(src):
+ """
+ An MPEG transport stream packet is 188 bytes long. Internally we use a packet
+ that is 256 bytes long to help with buffer alignment. This function adds the
+ appropriate trailing padding to convert each packet from 188 to 256 bytes.
+ """
+ return pad_stream(src, atsc.sizeof_atsc_mpeg_packet, atsc.sizeof_atsc_mpeg_packet_pad)
+
+
+def depad_transport_stream(src):
+ """
+ An MPEG transport stream packet is 188 bytes long. Internally we use a packet
+ that is 256 bytes long to help with buffer alignment. This function removes the
+ trailing padding to convert each packet from 256 back to 188 bytes.
+ """
+ return depad_stream(src, atsc.sizeof_atsc_mpeg_packet, atsc.sizeof_atsc_mpeg_packet_pad)
+
+
+class vector_source_ts(gr.hier_block):
+ """
+ MPEG Transport stream source for testing.
+ """
+ def __init__(self, fg, ts):
+ """
+ Pad tranport stream packets to 256 bytes and reformat appropriately.
+
+ @param fg: flow graph
+ @param ts: MPEG transport stream.
+ @type ts: sequence of ints in [0,255]; len(ts) % 188 == 0
+ """
+ src = gr.vector_source_b(pad_transport_stream(ts))
+ s2v = gr.stream_to_vector(gr.sizeof_char, atsc.sizeof_atsc_mpeg_packet)
+ fg.connect(src, s2v)
+ gr.hier_block.__init__(self, fg, None, s2v)
+
+
+class vector_sink_ts(gr.hier_block):
+ """
+ MPEG Transport stream sink for testing.
+ """
+ def __init__(self, fg):
+ """
+ @param fg: flow graph
+ """
+ v2s = gr.vector_to_stream(gr.sizeof_char, atsc.sizeof_atsc_mpeg_packet)
+ self.sink = gr.vector_sink_b()
+ fg.connect(v2s, self.sink)
+ gr.hier_block.__init__(self, fg, v2s, None)
+
+ def data(self):
+ """
+ Extracts tranport stream from sink and returns it to python.
+
+ Depads tranport stream packets from 256 back to 188 bytes.
+ @rtype: tuple of ints in [0,255]; len(result) % 188 == 0
+ """
+ return tuple(depad_transport_stream(self.sink.data()))
+
+
+
+class qa_atsc(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.fg = gr.flow_graph()
+
+ def tearDown(self):
+ self.fg = None
+
+
+ # The tests are run in alphabetical order
+
+ def test_loopback_000(self):
+ """
+ Loopback randomizer to derandomizer
+ """
+ src_data = make_transport_stream()
+ expected_result = src_data
+
+ src = vector_source_ts(self.fg, src_data)
+ rand = atsc.randomizer()
+ derand = atsc.derandomizer()
+ dst = vector_sink_ts(self.fg)
+ self.fg.connect(src, rand, derand, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ self.assertEqual (expected_result, result_data)
+
+ def test_loopback_001(self):
+ """
+ Loopback randomizer/rs_encoder to rs_decoder/derandomizer
+ """
+ src_data = make_transport_stream()
+ expected_result = src_data
+
+ src = vector_source_ts(self.fg, src_data)
+ rand = atsc.randomizer()
+ rs_enc = atsc.rs_encoder()
+ rs_dec = atsc.rs_decoder()
+ derand = atsc.derandomizer()
+ dst = vector_sink_ts(self.fg)
+ self.fg.connect(src, rand, rs_enc, rs_dec, derand, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ self.assertEqual (expected_result, result_data)
+
+ def test_loopback_002(self):
+ """
+ Loopback randomizer/rs_encoder/interleaver to
+ deinterleaver/rs_decoder/derandomizer
+ """
+ src_data = make_transport_stream()
+ interleaver_delay = 52
+ expected_result = src_data[0:len(src_data)-(interleaver_delay*atsc.ATSC_MPEG_PKT_LENGTH)]
+
+ src = vector_source_ts(self.fg, src_data)
+ rand = atsc.randomizer()
+ rs_enc = atsc.rs_encoder()
+ inter = atsc.interleaver()
+ deinter = atsc.deinterleaver()
+ rs_dec = atsc.rs_decoder()
+ derand = atsc.derandomizer()
+ dst = vector_sink_ts(self.fg)
+ self.fg.connect(src, rand, rs_enc, inter, deinter, rs_dec, derand, dst)
+ self.fg.run ()
+ result_data = dst.data ()
+ result_data = result_data[(interleaver_delay*atsc.ATSC_MPEG_PKT_LENGTH):len(result_data)]
+ self.assertEqual (expected_result, result_data)
+
+
+ def test_loopback_003(self):
+ """
+ Loopback randomizer/rs_encoder/interleaver/trellis_encoder
+ via ds_to_softds to
+ viterbi_decoder/deinterleaver/rs_decoder/derandomizer
+ """
+ src_data = make_transport_stream()
+ interleaver_delay = 52
+ viterbi_delay = 12
+ expected_result = src_data[0:len(src_data)-((interleaver_delay+viterbi_delay)*atsc.ATSC_MPEG_PKT_LENGTH)]
+
+ src = vector_source_ts(self.fg, src_data)
+ rand = atsc.randomizer()
+ rs_enc = atsc.rs_encoder()
+ inter = atsc.interleaver()
+ trellis = atsc.trellis_encoder()
+ softds = atsc.ds_to_softds()
+ viterbi = atsc.viterbi_decoder()
+ deinter = atsc.deinterleaver()
+ rs_dec = atsc.rs_decoder()
+ derand = atsc.derandomizer()
+ dst = vector_sink_ts(self.fg)
+ self.fg.connect(src, rand, rs_enc, inter, trellis, softds, viterbi, deinter, rs_dec, derand, dst)
+ self.fg.run ()
+ result_data = dst.data ()[((interleaver_delay+viterbi_delay)*atsc.ATSC_MPEG_PKT_LENGTH):len(dst.data())]
+ self.assertEqual (expected_result, result_data)
+
+
+if __name__ == '__main__':
+ gr_unittest.main()
+
+
+
+
+
+
diff --git a/gr-atsc/src/python/run_tests.in b/gr-atsc/src/python/run_tests.in
new file mode 100644
index 0000000000..b83d85bfbe
--- /dev/null
+++ b/gr-atsc/src/python/run_tests.in
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+# All this strange PYTHONPATH manipulation is required to run our
+# tests using our just built shared library and swig-generated python
+# code prior to installation.
+
+# build tree == src tree unless you're doing a VPATH build.
+# If you don't know what a VPATH build is, you're not doing one. Relax...
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+
+# Where to look in the build tree for our shared library
+libbld=@abs_top_builddir@/gr-atsc/src/lib
+# Where to look in the src tree for swig generated python code
+libsrc=@abs_top_srcdir@/gr-atsc/src/lib
+# Where to look in the src tree for hand written python code
+py=@abs_top_srcdir@/gr-atsc/src/python
+
+# Where to look for GNU Radio python modules in current build tree
+# FIXME this is wrong on a distcheck. We really need to ask gnuradio-core
+# where it put its python files.
+grpythonbld=@abs_top_builddir@/gnuradio-core/src/python/:@abs_top_builddir@/gnuradio-core/src/lib/swig/:@abs_top_builddir@/gnuradio-core/src/lib/swig/.libs
+
+PYTHONPATH="$grpythonbld:$libbld:$libbld/.libs:$libsrc:$py:$PYTHONPATH"
+export PYTHONPATH
+
+#
+# This is the simple part...
+# Run everything that matches qa_*.py and return the final result.
+#
+
+ok=yes
+for file in @srcdir@/qa_*.py
+do
+ if ! $file
+ then
+ ok=no
+ fi
+done
+
+if [ $ok = yes ]
+then
+ exit 0
+else
+ exit 1
+fi