Updated FSF address in all files. Fixes ticket:51
[debian/gnuradio] / gnuradio-examples / python / gmsk2 / dqpsk.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., 51 Franklin Street,
19 # Boston, MA 02110-1301, USA.
20
21
22 # See gnuradio-examples/python/gmsk2 for examples
23
24 """
25 differential QPSK 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 #            QPSK mod/demod with steams of bytes as data i/o
93 # /////////////////////////////////////////////////////////////////////////////
94
95
96 class dqpsk_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         print "bits_per_symbol =", bits_per_symbol
121         
122         # turn bytes into k-bit vectors
123         self.bytes2chunks = \
124           gr.packed_to_unpacked_bb(bits_per_symbol, gr.GR_MSB_FIRST)
125
126         if True:
127             self.gray_coder = gr.map_bb(binary_to_gray[arity])
128         else:
129             self.gray_coder = None
130
131         self.diffenc = gr.diff_encoder_bb(arity)
132         
133         self.chunks2symbols = gr.chunks_to_symbols_bc(constellation[arity])
134
135         # pulse shaping filter
136         self.rrc_taps = gr.firdes.root_raised_cosine(
137                 spb,            # gain  (spb since we're interpolating by spb)
138                 spb,            # sampling rate
139                 1.0,            # symbol rate
140                 excess_bw,      # excess bandwidth (roll-off factor)
141                 ntaps)
142
143         self.rrc_filter = gr.interp_fir_filter_ccf(spb, self.rrc_taps)
144
145         # Connect
146         if self.gray_coder:
147             fg.connect(self.bytes2chunks, self.gray_coder)
148             t = self.gray_coder
149         else:
150             t = self.bytes2chunks
151
152         fg.connect(t, self.diffenc, self.chunks2symbols, self.rrc_filter)
153         
154         if 1:
155             fg.connect(self.gray_coder,
156                        gr.file_sink(gr.sizeof_char, "graycoder.dat"))
157             fg.connect(self.diffenc,
158                        gr.file_sink(gr.sizeof_char, "diffenc.dat"))
159             
160         # Initialize base class
161         gr.hier_block.__init__(self, fg, self.bytes2chunks, self.rrc_filter)
162
163     def samples_per_baud(self):
164         return self.spb
165
166     def bits_per_baud(self=None):   # staticmethod that's also callable on an instance
167         return 2
168     bits_per_baud = staticmethod(bits_per_baud)      # make it a static method.  RTFM
169
170
171
172 class dqpsk_demod__coherent_detection_of_differentially_encoded_psk(gr.hier_block):
173     def __init__(self, fg, spb, excess_bw, costas_alpha=0.005, gain_mu=0.05):
174         """
175         Hierarchical block for RRC-filtered QPSK demodulation
176
177         The input is the complex modulated signal at baseband.
178         The output is a stream of bits packed 1 bit per byte (LSB)
179
180         @param fg: flow graph
181         @type fg: flow graph
182         @param spb: samples per baud >= 2
183         @type spb: float
184         @param excess_bw: Root-raised cosine filter excess bandwidth
185         @type excess_bw: float
186         @param costas_alpha: loop filter gain
187         @type costas_alphas: float
188         @param gain_mu:
189         @type gain_mu: float
190         """
191         if spb < 2:
192             raise TypeError, "sbp must be >= 2"
193         self.spb = spb
194
195         bits_per_symbol = self.bits_per_baud()
196         arity = pow(2,bits_per_symbol)
197         print "bits_per_symbol =", bits_per_symbol
198
199         # Automatic gain control
200         self.preamp = gr.multiply_const_cc(10e-5)
201         self.agc = gr.agc_cc(1e-3, 1, 1)
202         
203         # Costas loop (carrier tracking)
204         # FIXME: need to decide how to handle this more generally; do we pull it from higher layer?
205         costas_order = 4
206         beta = .25 * costas_alpha * costas_alpha
207         self.costas_loop = gr.costas_loop_cc(costas_alpha, beta, 0.05, -0.05, costas_order)
208
209         # RRC data filter
210         ntaps = 11 * spb
211         self.rrc_taps = gr.firdes.root_raised_cosine(
212             1.0,                # gain 
213             spb,                # sampling rate
214             1.0,                # symbol rate
215             excess_bw,          # excess bandwidth (roll-off factor)
216             ntaps)
217
218         self.rrc_filter=gr.fir_filter_ccf(1, self.rrc_taps)
219
220         # symbol clock recovery
221         omega = spb
222         gain_omega = .25 * gain_mu * gain_mu
223         omega_rel_limit = 0.5
224         mu = 0.05
225         gain_mu = 0.1
226         self.clock_recovery=gr.clock_recovery_mm_cc(omega, gain_omega,
227                                                     mu, gain_mu, omega_rel_limit)
228
229         # find closest constellation point
230         #rot = .707 + .707j
231         rot = 1
232         rotated_const = map(lambda pt: pt * rot, constellation[arity])
233         print "rotated_const =", rotated_const
234
235         self.diffdec = gr.diff_phasor_cc()
236         #self.diffdec = gr.diff_decoder_bb(arity)
237
238         self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity))
239         self.gray_decoder = gr.map_bb(gray_to_binary[arity])
240         
241         # unpack the k bit vector into a stream of bits
242         self.unpack = gr.unpack_k_bits_bb(bits_per_symbol)
243
244         fg.connect(self.preamp, self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery,
245                    self.diffdec, self.slicer, self.gray_decoder, self.unpack)
246         #fg.connect(self.preamp, self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery,
247         #           self.slicer, self.diffdec, self.gray_decoder, self.unpack)
248         
249         # Debug sinks
250         if 1:
251             fg.connect(self.agc,
252                        gr.file_sink(gr.sizeof_gr_complex, "agc.dat"))
253             fg.connect(self.costas_loop,
254                        gr.file_sink(gr.sizeof_gr_complex, "costas_loop.dat"))
255             fg.connect(self.rrc_filter,
256                        gr.file_sink(gr.sizeof_gr_complex, "rrc.dat"))
257             fg.connect(self.clock_recovery,
258                        gr.file_sink(gr.sizeof_gr_complex, "clock_recovery.dat"))
259             fg.connect(self.slicer,
260                        gr.file_sink(gr.sizeof_char, "slicer.dat"))
261             fg.connect(self.diffdec,
262                        gr.file_sink(gr.sizeof_gr_complex, "diffdec.dat"))
263             #fg.connect(self.diffdec,
264             #          gr.file_sink(gr.sizeof_char, "diffdec.dat"))
265             fg.connect(self.unpack,
266                        gr.file_sink(gr.sizeof_char, "unpack.dat"))
267
268         # Initialize base class
269         gr.hier_block.__init__(self, fg, self.preamp, self.unpack)
270
271     def samples_per_baud(self):
272         return self.spb
273
274     def bits_per_baud(self=None):   # staticmethod that's also callable on an instance
275         return 2
276     bits_per_baud = staticmethod(bits_per_baud)      # make it a static method.  RTFM
277
278
279 dqpsk_demod = dqpsk_demod__coherent_detection_of_differentially_encoded_psk
280