Houston, we have a trunk.
[debian/gnuradio] / gnuradio-examples / python / gmsk2 / dbpsk.py
1 #
2 # Copyright 2005,2006 Free Software Foundation, Inc.
3
4 # This file is part of GNU Radio
5
6 # GNU Radio is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2, or (at your option)
9 # any later version.
10
11 # GNU Radio is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15
16 # You should have received a copy of the GNU General Public License
17 # along with GNU Radio; see the file COPYING.  If not, write to
18 # the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 # Boston, MA 02111-1307, USA.
20
21
22 # See gnuradio-examples/python/gmsk2 for examples
23
24 """
25 differential BPSK modulation and demodulation.
26 """
27
28 from gnuradio import gr, gru
29 from math import pi, sqrt
30 import cmath
31 import Numeric
32 from pprint import pprint
33
34 _use_gray_code = True
35
36 def make_constellation(m):
37     return [cmath.exp(i * 2 * pi / m * 1j) for i in range(m)]
38         
39 # Common definition of constellations for Tx and Rx
40 constellation = {
41     2 : make_constellation(2),           # BPSK
42     4 : make_constellation(4),           # QPSK
43     8 : make_constellation(8)            # 8PSK
44     }
45
46 if 0:
47     print "const(2) ="
48     pprint(constellation[2])
49     print "const(4) ="
50     pprint(constellation[4])
51     print "const(8) ="
52     pprint(constellation[8])
53
54
55 if _use_gray_code:
56     # -----------------------
57     # Do Gray code
58     # -----------------------
59     # binary to gray coding
60     binary_to_gray = {
61         2 : (0, 1),
62         4 : (0, 1, 3, 2),
63         8 : (0, 1, 3, 2, 7, 6, 4, 5)
64         }
65     
66     # gray to binary
67     gray_to_binary = {
68         2 : (0, 1),
69         4 : (0, 1, 3, 2),
70         8 : (0, 1, 3, 2, 6, 7, 5, 4)
71         }
72 else:
73     # -----------------------
74     # Don't Gray code
75     # -----------------------
76     # identity mapping
77     binary_to_gray = {
78         2 : (0, 1),
79         4 : (0, 1, 2, 3),
80         8 : (0, 1, 2, 3, 4, 5, 6, 7)
81         }
82     
83     # identity mapping
84     gray_to_binary = {
85         2 : (0, 1),
86         4 : (0, 1, 2, 3),
87         8 : (0, 1, 2, 3, 4, 5, 6, 7)
88         }
89
90
91 # /////////////////////////////////////////////////////////////////////////////
92 #            BPSK mod/demod with steams of bytes as data i/o
93 # /////////////////////////////////////////////////////////////////////////////
94
95
96 class dbpsk_mod(gr.hier_block):
97
98     def __init__(self, fg, spb, excess_bw):
99         """
100         Hierarchical block for RRC-filtered QPSK modulation.
101
102         The input is a byte stream (unsigned char) and the
103         output is the complex modulated signal at baseband.
104
105         @param fg: flow graph
106         @type fg: flow graph
107         @param spb: samples per baud >= 2
108         @type spb: integer
109         @param excess_bw: Root-raised cosine filter excess bandwidth
110         @type excess_bw: float
111         """
112         if not isinstance(spb, int) or spb < 2:
113             raise TypeError, "sbp must be an integer >= 2"
114         self.spb = spb
115
116         ntaps = 11 * spb
117
118         bits_per_symbol = self.bits_per_baud()
119         arity = pow(2,bits_per_symbol)
120         self.bits_per_symbol = bits_per_symbol
121         print "bits_per_symbol =", bits_per_symbol
122         
123         # turn bytes into k-bit vectors
124         self.bytes2chunks = \
125           gr.packed_to_unpacked_bb(bits_per_symbol, gr.GR_MSB_FIRST)
126
127         if True:
128             self.gray_coder = gr.map_bb(binary_to_gray[arity])
129         else:
130             self.gray_coder = None
131
132         self.diffenc = gr.diff_encoder_bb(arity)
133         
134         self.chunks2symbols = gr.chunks_to_symbols_bc(constellation[arity])
135
136         # pulse shaping filter
137         self.rrc_taps = gr.firdes.root_raised_cosine(
138                 spb,            # gain  (spb since we're interpolating by spb)
139                 spb,            # sampling rate
140                 1.0,            # symbol rate
141                 excess_bw,      # excess bandwidth (roll-off factor)
142                 ntaps)
143
144         self.rrc_filter = gr.interp_fir_filter_ccf(spb, self.rrc_taps)
145
146         # Connect
147         if self.gray_coder:
148             fg.connect(self.bytes2chunks, self.gray_coder)
149             t = self.gray_coder
150         else:
151             t = self.bytes2chunks
152
153         fg.connect(t, self.diffenc, self.chunks2symbols, self.rrc_filter)
154         
155         if 1:
156             fg.connect(self.gray_coder,
157                        gr.file_sink(gr.sizeof_char, "graycoder.dat"))
158             fg.connect(self.diffenc,
159                        gr.file_sink(gr.sizeof_char, "diffenc.dat"))
160             
161         # Initialize base class
162         gr.hier_block.__init__(self, fg, self.bytes2chunks, self.rrc_filter)
163
164     def samples_per_baud(self):
165         return self.spb
166
167     def bits_per_baud(self=None):   # staticmethod that's also callable on an instance
168         return 1
169     bits_per_baud = staticmethod(bits_per_baud)      # make it a static method.  RTFM
170
171
172
173 class dbpsk_demod__coherent_detection_of_differentially_encoded_psk(gr.hier_block):
174     def __init__(self, fg, spb, excess_bw, costas_alpha=0.005, gain_mu=0.05):
175         """
176         Hierarchical block for RRC-filtered BPSK demodulation
177
178         The input is the complex modulated signal at baseband.
179         The output is a stream of bits packed 1 bit per byte (LSB)
180
181         @param fg: flow graph
182         @type fg: flow graph
183         @param spb: samples per baud >= 2
184         @type spb: float
185         @param excess_bw: Root-raised cosine filter excess bandwidth
186         @type excess_bw: float
187         @param costas_alpha: loop filter gain
188         @type costas_alphas: float
189         @param gain_mu:
190         @type gain_mu: float
191         """
192         if spb < 2:
193             raise TypeError, "sbp must be >= 2"
194         self.spb = spb
195
196         bits_per_symbol = self.bits_per_baud()
197         arity = pow(2,bits_per_symbol)
198         print "bits_per_symbol =", bits_per_symbol
199
200         # Automatic gain control
201         self.preamp = gr.multiply_const_cc(10e-5)
202         self.agc = gr.agc_cc(1e-3, 1, 1, 1000)
203         
204         # Costas loop (carrier tracking)
205         # FIXME: need to decide how to handle this more generally; do we pull it from higher layer?
206         costas_order = 2
207         costas_alpha *= 15   # 2nd order loop needs more gain
208         beta = .25 * costas_alpha * costas_alpha
209         self.costas_loop = gr.costas_loop_cc(costas_alpha, beta, 0.05, -0.05, costas_order)
210
211         # RRC data filter
212         ntaps = 11 * spb
213         self.rrc_taps = gr.firdes.root_raised_cosine(
214             1.0,                # gain 
215             spb,                # sampling rate
216             1.0,                # symbol rate
217             excess_bw,          # excess bandwidth (roll-off factor)
218             ntaps)
219
220         self.rrc_filter=gr.fir_filter_ccf(1, self.rrc_taps)
221
222         # symbol clock recovery
223         omega = spb
224         gain_omega = .25 * gain_mu * gain_mu
225         omega_rel_limit = 0.5
226         mu = 0.05
227         gain_mu = 0.1
228         self.clock_recovery=gr.clock_recovery_mm_cc(omega, gain_omega,
229                                                     mu, gain_mu, omega_rel_limit)
230
231         # find closest constellation point
232         #rot = .707 + .707j
233         rot = 1
234         rotated_const = map(lambda pt: pt * rot, constellation[arity])
235         print "rotated_const =", rotated_const
236
237         self.diffdec = gr.diff_phasor_cc()
238         #self.diffdec = gr.diff_decoder_bb(arity)
239
240         self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity))
241         self.gray_decoder = gr.map_bb(gray_to_binary[arity])
242         
243         # unpack the k bit vector into a stream of bits
244         self.unpack = gr.unpack_k_bits_bb(bits_per_symbol)
245
246         fg.connect(self.preamp, self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery,
247                    self.diffdec, self.slicer, self.gray_decoder, self.unpack)
248         #fg.connect(self.preamp, self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery,
249         #           self.slicer, self.diffdec, self.gray_decoder, self.unpack)
250         
251         # Debug sinks
252         if 1:
253             fg.connect(self.agc,
254                        gr.file_sink(gr.sizeof_gr_complex, "agc.dat"))
255             fg.connect(self.costas_loop,
256                        gr.file_sink(gr.sizeof_gr_complex, "costas_loop.dat"))
257             fg.connect(self.rrc_filter,
258                        gr.file_sink(gr.sizeof_gr_complex, "rrc.dat"))
259             fg.connect(self.clock_recovery,
260                        gr.file_sink(gr.sizeof_gr_complex, "clock_recovery.dat"))
261             fg.connect(self.slicer,
262                        gr.file_sink(gr.sizeof_char, "slicer.dat"))
263             fg.connect(self.diffdec,
264                        gr.file_sink(gr.sizeof_gr_complex, "diffdec.dat"))
265             #fg.connect(self.diffdec,
266             #          gr.file_sink(gr.sizeof_char, "diffdec.dat"))
267             fg.connect(self.unpack,
268                        gr.file_sink(gr.sizeof_char, "unpack.dat"))
269
270         # Initialize base class
271         gr.hier_block.__init__(self, fg, self.preamp, self.unpack)
272
273     def samples_per_baud(self):
274         return self.spb
275
276     def bits_per_baud(self=None):   # staticmethod that's also callable on an instance
277         return 1
278     bits_per_baud = staticmethod(bits_per_baud)      # make it a static method.  RTFM
279
280
281 dbpsk_demod = dbpsk_demod__coherent_detection_of_differentially_encoded_psk
282