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., 59 Temple Place - Suite 330,
19 # Boston, MA 02111-1307, USA.
22 # See gnuradio-examples/python/gmsk2 for examples
25 PSK and differential PSK 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 # mPSK mod/demod with steams of bytes as data i/o
93 # /////////////////////////////////////////////////////////////////////////////
96 class mpsk_mod(gr.hier_block):
98 def __init__(self, fg, spb, arity, excess_bw, diff=False):
100 Hierarchical block for RRC-filtered PSK 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
111 @param arity: whick PSK: 2, 4, 8
112 @type arity: int in {2, 4, 8}
113 @param diff: differential PSK if true
116 if not isinstance(spb, int) or spb < 2:
117 raise TypeError, "sbp must be an integer >= 2"
120 if not arity in (2, 4):
121 raise ValueError, "n must be 2, 4, or 8"
125 bits_per_symbol = int(gru.log2(arity))
126 self.bits_per_symbol = bits_per_symbol
127 print "bits_per_symbol =", bits_per_symbol
129 # turn bytes into k-bit vectors
130 self.bytes2chunks = \
131 gr.packed_to_unpacked_bb(bits_per_symbol, gr.GR_MSB_FIRST)
133 if True or arity > 2:
134 self.gray_coder = gr.map_bb(binary_to_gray[arity])
136 self.gray_coder = None
139 self.diffenc = gr.diff_encoder_bb(arity)
143 self.chunks2symbols = gr.chunks_to_symbols_bc(constellation[arity])
145 # pulse shaping filter
146 self.rrc_taps = gr.firdes.root_raised_cosine(
147 spb, # gain (spb since we're interpolating by spb)
150 excess_bw, # excess bandwidth (roll-off factor)
153 self.rrc_filter = gr.interp_fir_filter_ccf(spb, self.rrc_taps)
157 fg.connect(self.bytes2chunks, self.gray_coder)
160 t = self.bytes2chunks
163 fg.connect(t, self.diffenc, self.chunks2symbols, self.rrc_filter)
165 fg.connect(t, self.chunks2symbols, self.rrc_filter)
168 fg.connect(self.gray_coder,
169 gr.file_sink(gr.sizeof_char, "graycoder.dat"))
171 fg.connect(self.diffenc,
172 gr.file_sink(gr.sizeof_char, "diffenc.dat"))
174 # Initialize base class
175 gr.hier_block.__init__(self, fg, self.bytes2chunks, self.rrc_filter)
177 def samples_per_baud(self):
180 def bits_per_baud(self):
181 return self.bits_per_symbol
184 class mpsk_demod__coherent_detection_of_differentially_encoded_psk(gr.hier_block):
185 def __init__(self, fg, spb, arity, excess_bw, diff=False, costas_alpha=0.005, gain_mu=0.05):
187 Hierarchical block for RRC-filtered PSK demodulation
189 The input is the complex modulated signal at baseband.
190 The output is a stream of bits packed 1 bit per byte (LSB)
192 @param fg: flow graph
194 @param spb: samples per baud >= 2
196 @param excess_bw: Root-raised cosine filter excess bandwidth
197 @type excess_bw: float
198 @param arity: whick PSK: 2, 4, 8
199 @type arity: int in {2, 4, 8}
200 @param diff: differential PSK if true
202 @param costas_alpha: loop filter gain
203 @type costas_alphas: float
208 raise TypeError, "sbp must be >= 2"
211 if not arity in (2, 4):
212 raise ValueError, "n must be 2 or 4"
214 if not diff and arity==4:
215 raise NotImplementedError, "non-differential QPSK not supported yet"
217 bits_per_symbol = int(gru.log2(arity))
218 print "bits_per_symbol =", bits_per_symbol
220 # Automatic gain control
221 self.agc = gr.agc_cc(1e-3, 1, 1)
223 # Costas loop (carrier tracking)
224 # FIXME: need to decide how to handle this more generally; do we pull it from higher layer?
227 costas_alpha *= 15 # 2nd order loop needs more gain
230 beta = .25 * costas_alpha * costas_alpha
231 self.costas_loop = gr.costas_loop_cc(costas_alpha, beta, 0.05, -0.05, costas_order)
235 self.rrc_taps = gr.firdes.root_raised_cosine(
239 excess_bw, # excess bandwidth (roll-off factor)
242 self.rrc_filter=gr.fir_filter_ccf(1, self.rrc_taps)
244 # symbol clock recovery
246 gain_omega = .25 * gain_mu * gain_mu
247 omega_rel_limit = 0.5
250 self.clock_recovery=gr.clock_recovery_mm_cc(omega, gain_omega,
251 mu, gain_mu, omega_rel_limit)
253 # find closest constellation point
256 rotated_const = map(lambda pt: pt * rot, constellation[arity])
257 print "rotated_const =", rotated_const
260 self.diffdec = gr.diff_phasor_cc()
261 #self.diffdec = gr.diff_decoder_bb(arity)
263 self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity))
264 self.gray_decoder = gr.map_bb(gray_to_binary[arity])
266 # unpack the k bit vector into a stream of bits
267 self.unpack = gr.unpack_k_bits_bb(bits_per_symbol)
270 fg.connect(self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery,
271 self.diffdec, self.slicer, self.gray_decoder, self.unpack)
273 fg.connect(self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery,
274 self.slicer, self.gray_decoder, self.unpack)
276 #fg.connect(self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery,
277 # self.slicer, self.diffdec, self.gray_decoder, self.unpack)
282 gr.file_sink(gr.sizeof_gr_complex, "agc.dat"))
283 fg.connect(self.costas_loop,
284 gr.file_sink(gr.sizeof_gr_complex, "costas_loop.dat"))
285 fg.connect(self.rrc_filter,
286 gr.file_sink(gr.sizeof_gr_complex, "rrc.dat"))
287 fg.connect(self.clock_recovery,
288 gr.file_sink(gr.sizeof_gr_complex, "clock_recovery.dat"))
289 fg.connect(self.slicer,
290 gr.file_sink(gr.sizeof_char, "slicer.dat"))
292 fg.connect(self.diffdec,
293 gr.file_sink(gr.sizeof_gr_complex, "diffdec.dat"))
294 #fg.connect(self.diffdec,
295 # gr.file_sink(gr.sizeof_char, "diffdec.dat"))
296 fg.connect(self.unpack,
297 gr.file_sink(gr.sizeof_char, "unpack.dat"))
299 # Initialize base class
300 gr.hier_block.__init__(self, fg, self.agc, self.unpack)
302 def samples_per_baud(self):
305 def bits_per_baud(self):
306 return self.bits_per_symbol
309 #########################################################################
311 class mpsk_demod__coherent_detection_of_nondifferentially_encoded_psk(gr.hier_block):
312 def __init__(self, fg, spb, arity, excess_bw, diff=False, costas_alpha=0.005, gain_mu=0.05):
314 Hierarchical block for RRC-filtered PSK demodulation
316 The input is the complex modulated signal at baseband.
317 The output is a stream of bits packed 1 bit per byte (LSB)
319 @param fg: flow graph
321 @param spb: samples per baud >= 2
323 @param excess_bw: Root-raised cosine filter excess bandwidth
324 @type excess_bw: float
325 @param arity: whick PSK: 2, 4, 8
326 @type arity: int in {2, 4, 8}
327 @param diff: differential PSK if true
329 @param costas_alpha: loop filter gain
330 @type costas_alphas: float
335 raise TypeError, "sbp must be >= 2"
338 if not arity in (2, 4):
339 raise ValueError, "n must be 2 or 4"
341 bits_per_symbol = int(gru.log2(arity))
342 print "bits_per_symbol =", bits_per_symbol
344 # Automatic gain control
345 self.agc = gr.agc_cc(1e-3, 1, 1)
347 # Costas loop (carrier tracking)
348 # FIXME: need to decide how to handle this more generally; do we pull it from higher layer?
351 costas_alpha *= 15 # 2nd order loop needs more gain
354 beta = .25 * costas_alpha * costas_alpha
355 self.costas_loop = gr.costas_loop_cc(costas_alpha, beta, 0.05, -0.05, costas_order)
359 self.rrc_taps = gr.firdes.root_raised_cosine(
363 excess_bw, # excess bandwidth (roll-off factor)
366 self.rrc_filter=gr.fir_filter_ccf(1, self.rrc_taps)
368 # symbol clock recovery
370 gain_omega = .25 * gain_mu * gain_mu
371 omega_rel_limit = 0.5
374 self.clock_recovery=gr.clock_recovery_mm_cc(omega, gain_omega,
375 mu, gain_mu, omega_rel_limit)
377 # find closest constellation point
380 rotated_const = map(lambda pt: pt * rot, constellation[arity])
381 print "rotated_const =", rotated_const
383 self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity))
384 self.gray_decoder = gr.map_bb(gray_to_binary[arity])
386 # unpack the k bit vector into a stream of bits
387 self.unpack = gr.unpack_k_bits_bb(bits_per_symbol)
389 fg.connect(self.agc, self.costas_loop, self.rrc_filter, self.clock_recovery,
390 self.slicer, self.gray_decoder, self.unpack)
395 gr.file_sink(gr.sizeof_gr_complex, "agc.dat"))
396 fg.connect(self.costas_loop,
397 gr.file_sink(gr.sizeof_gr_complex, "costas_loop.dat"))
398 fg.connect(self.rrc_filter,
399 gr.file_sink(gr.sizeof_gr_complex, "rrc.dat"))
400 fg.connect(self.clock_recovery,
401 gr.file_sink(gr.sizeof_gr_complex, "clock_recovery.dat"))
402 fg.connect(self.slicer,
403 gr.file_sink(gr.sizeof_char, "slicer.dat"))
404 fg.connect(self.unpack,
405 gr.file_sink(gr.sizeof_char, "unpack.dat"))
407 # Initialize base class
408 gr.hier_block.__init__(self, fg, self.agc, self.unpack)
410 def samples_per_baud(self):
413 def bits_per_baud(self):
414 return self.bits_per_symbol
417 mpsk_demod = mpsk_demod__coherent_detection_of_differentially_encoded_psk
418 #mpsk_demod = mpsk_demod__coherent_detection_of_nondifferentially_encoded_psk