2 # Copyright 2005,2006 Free Software Foundation, Inc.
4 # This file is part of GNU Radio
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)
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.
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.
22 # See gnuradio-examples/python/gmsk2 for examples
25 differential BPSK modulation and demodulation.
28 from gnuradio import gr, gru
29 from math import pi, sqrt
32 from pprint import pprint
36 def make_constellation(m):
37 return [cmath.exp(i * 2 * pi / m * 1j) for i in range(m)]
39 # Common definition of constellations for Tx and Rx
41 2 : make_constellation(2), # BPSK
42 4 : make_constellation(4), # QPSK
43 8 : make_constellation(8) # 8PSK
48 pprint(constellation[2])
50 pprint(constellation[4])
52 pprint(constellation[8])
56 # -----------------------
58 # -----------------------
59 # binary to gray coding
63 8 : (0, 1, 3, 2, 7, 6, 4, 5)
70 8 : (0, 1, 3, 2, 6, 7, 5, 4)
73 # -----------------------
75 # -----------------------
80 8 : (0, 1, 2, 3, 4, 5, 6, 7)
87 8 : (0, 1, 2, 3, 4, 5, 6, 7)
91 # /////////////////////////////////////////////////////////////////////////////
92 # BPSK mod/demod with steams of bytes as data i/o
93 # /////////////////////////////////////////////////////////////////////////////
96 class dbpsk_mod(gr.hier_block):
98 def __init__(self, fg, spb, excess_bw):
100 Hierarchical block for RRC-filtered QPSK modulation.
102 The input is a byte stream (unsigned char) and the
103 output is the complex modulated signal at baseband.
105 @param fg: flow graph
107 @param spb: samples per baud >= 2
109 @param excess_bw: Root-raised cosine filter excess bandwidth
110 @type excess_bw: float
112 if not isinstance(spb, int) or spb < 2:
113 raise TypeError, "sbp must be an integer >= 2"
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
123 # turn bytes into k-bit vectors
124 self.bytes2chunks = \
125 gr.packed_to_unpacked_bb(bits_per_symbol, gr.GR_MSB_FIRST)
128 self.gray_coder = gr.map_bb(binary_to_gray[arity])
130 self.gray_coder = None
132 self.diffenc = gr.diff_encoder_bb(arity)
134 self.chunks2symbols = gr.chunks_to_symbols_bc(constellation[arity])
136 # pulse shaping filter
137 self.rrc_taps = gr.firdes.root_raised_cosine(
138 spb, # gain (spb since we're interpolating by spb)
141 excess_bw, # excess bandwidth (roll-off factor)
144 self.rrc_filter = gr.interp_fir_filter_ccf(spb, self.rrc_taps)
148 fg.connect(self.bytes2chunks, self.gray_coder)
151 t = self.bytes2chunks
153 fg.connect(t, self.diffenc, self.chunks2symbols, self.rrc_filter)
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"))
161 # Initialize base class
162 gr.hier_block.__init__(self, fg, self.bytes2chunks, self.rrc_filter)
164 def samples_per_baud(self):
167 def bits_per_baud(self=None): # staticmethod that's also callable on an instance
169 bits_per_baud = staticmethod(bits_per_baud) # make it a static method. RTFM
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):
176 Hierarchical block for RRC-filtered BPSK demodulation
178 The input is the complex modulated signal at baseband.
179 The output is a stream of bits packed 1 bit per byte (LSB)
181 @param fg: flow graph
183 @param spb: samples per baud >= 2
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
193 raise TypeError, "sbp must be >= 2"
196 bits_per_symbol = self.bits_per_baud()
197 arity = pow(2,bits_per_symbol)
198 print "bits_per_symbol =", bits_per_symbol
200 # Automatic gain control
201 self.preamp = gr.multiply_const_cc(10e-5)
202 self.agc = gr.agc_cc(1e-3, 1, 1, 1000)
204 # Costas loop (carrier tracking)
205 # FIXME: need to decide how to handle this more generally; do we pull it from higher layer?
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)
213 self.rrc_taps = gr.firdes.root_raised_cosine(
217 excess_bw, # excess bandwidth (roll-off factor)
220 self.rrc_filter=gr.fir_filter_ccf(1, self.rrc_taps)
222 # symbol clock recovery
224 gain_omega = .25 * gain_mu * gain_mu
225 omega_rel_limit = 0.5
228 self.clock_recovery=gr.clock_recovery_mm_cc(omega, gain_omega,
229 mu, gain_mu, omega_rel_limit)
231 # find closest constellation point
234 rotated_const = map(lambda pt: pt * rot, constellation[arity])
235 print "rotated_const =", rotated_const
237 self.diffdec = gr.diff_phasor_cc()
238 #self.diffdec = gr.diff_decoder_bb(arity)
240 self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity))
241 self.gray_decoder = gr.map_bb(gray_to_binary[arity])
243 # unpack the k bit vector into a stream of bits
244 self.unpack = gr.unpack_k_bits_bb(bits_per_symbol)
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)
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"))
270 # Initialize base class
271 gr.hier_block.__init__(self, fg, self.preamp, self.unpack)
273 def samples_per_baud(self):
276 def bits_per_baud(self=None): # staticmethod that's also callable on an instance
278 bits_per_baud = staticmethod(bits_per_baud) # make it a static method. RTFM
281 dbpsk_demod = dbpsk_demod__coherent_detection_of_differentially_encoded_psk