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