Updated license from GPL version 2 or later to GPL version 3 or later.
[debian/gnuradio] / gnuradio-core / src / python / gnuradio / blksimpl / cpm.py
1 #
2 # CPM modulation and demodulation.  
3 #
4 #
5 # Copyright 2005,2006,2007 Free Software Foundation, Inc.
6
7 # This file is part of GNU Radio
8
9 # GNU Radio is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 3, or (at your option)
12 # any later version.
13
14 # GNU Radio is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 # GNU General Public License for more details.
18
19 # You should have received a copy of the GNU General Public License
20 # along with GNU Radio; see the file COPYING.  If not, write to
21 # the Free Software Foundation, Inc., 51 Franklin Street,
22 # Boston, MA 02110-1301, USA.
23
24
25 # See gnuradio-examples/python/digital for examples
26
27 from gnuradio import gr
28 from gnuradio import modulation_utils
29 from math import pi
30 import numpy
31 from pprint import pprint
32 import inspect
33
34 # default values (used in __init__ and add_options)
35 _def_samples_per_symbol = 2
36 _def_bits_per_symbol = 1
37 _def_h_numerator = 1
38 _def_h_denominator = 2
39 _def_cpm_type = 0 # 0=CPFSK, 1=GMSK, 2=RC, 3=GENERAL
40 _def_bt = 0.35
41 _def_symbols_per_pulse = 1
42 _def_generic_taps = numpy.empty(1)
43 _def_verbose = False
44 _def_log = False
45
46
47 # /////////////////////////////////////////////////////////////////////////////
48 #                              CPM modulator
49 # /////////////////////////////////////////////////////////////////////////////
50
51 class cpm_mod(gr.hier_block):
52     def __init__(self, fg,
53                  samples_per_symbol=_def_samples_per_symbol,
54                  bits_per_symbol=_def_bits_per_symbol,
55                  h_numerator=_def_h_numerator,
56                  h_denominator=_def_h_denominator,
57                  cpm_type=_def_cpm_type,
58                  bt=_def_bt,
59                  symbols_per_pulse=_def_symbols_per_pulse,
60                  generic_taps=_def_generic_taps,
61                  verbose=_def_verbose,
62                  log=_def_log):
63         """
64         Hierarchical block for Continuous Phase
65         modulation.
66
67         The input is a byte stream (unsigned char) 
68         representing packed bits and the
69         output is the complex modulated signal at baseband.
70
71         See Proakis for definition of generic CPM signals:
72         s(t)=exp(j phi(t))
73         phi(t)= 2 pi h int_0^t f(t') dt'
74         f(t)=sum_k a_k g(t-kT)
75         (normalizing assumption: int_0^infty g(t) dt = 1/2)
76
77         @param fg: flow graph
78         @type fg: flow graph
79         @param samples_per_symbol: samples per baud >= 2
80         @type samples_per_symbol: integer
81         @param bits_per_symbol: bits per symbol
82         @type bits_per_symbol: integer
83         @param h_numerator: numerator of modulation index
84         @type h_numerator: integer
85         @param h_denominator: denominator of modulation index (numerator and denominator must be relative primes)
86         @type h_denominator: integer
87         @param cpm_type: supported types are: 0=CPFSK, 1=GMSK, 2=RC, 3=GENERAL
88         @type cpm_type: integer
89         @param bt: bandwidth symbol time product for GMSK
90         @type bt: float
91         @param symbols_per_pulse: shaping pulse duration in symbols
92         @type symbols_per_pulse: integer
93         @param generic_taps: define a generic CPM pulse shape (sum = samples_per_symbol/2)
94         @type generic_taps: array of floats
95
96         @param verbose: Print information about modulator?
97         @type verbose: bool
98         @param debug: Print modulation data to files?
99         @type debug: bool       
100         """
101
102         self._fg = fg
103         self._samples_per_symbol = samples_per_symbol
104         self._bits_per_symbol = bits_per_symbol
105         self._h_numerator = h_numerator
106         self._h_denominator = h_denominator
107         self._cpm_type = cpm_type
108         self._bt=bt
109         if cpm_type == 0 or cpm_type == 2 or cpm_type == 3: # CPFSK, RC, Generic
110             self._symbols_per_pulse = symbols_per_pulse
111         elif cpm_type == 1: # GMSK
112             self._symbols_per_pulse = 4
113         else:
114             raise TypeError, ("cpm_type must be an integer in {0,1,2,3}, is %r" % (cpm_type,))
115
116         self._generic_taps=numpy.array(generic_taps)
117
118         if not isinstance(samples_per_symbol, int) or samples_per_symbol < 2:
119             raise TypeError, ("samples_per_symbol must be an integer >= 2, is %r" % (samples_per_symbol,))
120
121         self.nsymbols = 2**bits_per_symbol
122         self.sym_alphabet=numpy.arange(-(self.nsymbols-1),self.nsymbols,2)
123
124
125         self.ntaps = self._symbols_per_pulse * samples_per_symbol
126         sensitivity = 2 * pi * h_numerator / h_denominator / samples_per_symbol
127
128         # Unpack Bytes into bits_per_symbol groups
129         self.B2s = gr.packed_to_unpacked_bb(bits_per_symbol,gr.GR_MSB_FIRST)
130  
131  
132         # Turn it into symmetric PAM data.
133         self.pam = gr.chunks_to_symbols_bf(self.sym_alphabet,1)
134
135         # Generate pulse (sum of taps = samples_per_symbol/2)
136         if cpm_type == 0: # CPFSK
137             self.taps= (1.0/self._symbols_per_pulse/2,) * self.ntaps
138         elif cpm_type == 1: # GMSK
139             gaussian_taps = gr.firdes.gaussian(
140                 1.0/2,                     # gain
141                 samples_per_symbol,    # symbol_rate
142                 bt,                    # bandwidth * symbol time
143                 self.ntaps                  # number of taps
144                 )
145             sqwave = (1,) * samples_per_symbol       # rectangular window
146             self.taps = numpy.convolve(numpy.array(gaussian_taps),numpy.array(sqwave))
147         elif cpm_type == 2: # Raised Cosine
148             # generalize it for arbitrary roll-off factor
149             self.taps = (1-numpy.cos(2*pi*numpy.arange(0,self.ntaps)/samples_per_symbol/self._symbols_per_pulse))/(2*self._symbols_per_pulse)
150         elif cpm_type == 3: # Generic CPM
151             self.taps = generic_taps
152         else:
153             raise TypeError, ("cpm_type must be an integer in {0,1,2,3}, is %r" % (cpm_type,))
154
155         self.filter = gr.interp_fir_filter_fff(samples_per_symbol, self.taps)
156
157         # FM modulation
158         self.fmmod = gr.frequency_modulator_fc(sensitivity)
159                 
160         if verbose:
161             self._print_verbage()
162          
163         if log:
164             self._setup_logging()
165
166         # Connect & Initialize base class
167         self._fg.connect(self.B2s, self.pam, self.filter, self.fmmod)
168         gr.hier_block.__init__(self, self._fg, self.B2s, self.fmmod)
169
170     #def samples_per_symbol(self):
171         #return self._samples_per_symbol
172
173     #def bits_per_symbol(self):  
174         #return self._bits_per_symbol
175
176     #def h_numerator(self):  
177         #return self._h_numerator
178
179     #def h_denominator(self):  
180         #return self._h_denominator
181
182     #def cpm_type(self):  
183         #return self._cpm_type
184
185     #def bt(self):  
186         #return self._bt
187
188     #def symbols_per_pulse(self):  
189         #return self._symbols_per_pulse
190
191
192     def _print_verbage(self):
193         print "Samples per symbol = %d" % self._samples_per_symbol
194         print "Bits per symbol = %d" % self._bits_per_symbol
195         print "h = " , self._h_numerator , " / " ,  self._h_denominator
196         print "Symbol alphabet = " , self.sym_alphabet
197         print "Symbols per pulse = %d" % self._symbols_per_pulse
198         print "taps = " , self.taps
199
200         print "CPM type = %d" % self._cpm_type
201         if self._cpm_type == 1:
202              print "Gaussian filter BT = %.2f" % self._bt
203
204
205     def _setup_logging(self):
206         print "Modulation logging turned on."
207         self._fg.connect(self.B2s,
208                          gr.file_sink(gr.sizeof_float, "symbols.dat"))
209         self._fg.connect(self.pam,
210                          gr.file_sink(gr.sizeof_float, "pam.dat"))
211         self._fg.connect(self.filter,
212                          gr.file_sink(gr.sizeof_float, "filter.dat"))
213         self._fg.connect(self.fmmod,
214                          gr.file_sink(gr.sizeof_gr_complex, "fmmod.dat"))
215
216
217     def add_options(parser):
218         """
219         Adds CPM modulation-specific options to the standard parser
220         """
221         parser.add_option("", "--bt", type="float", default=_def_bt,
222                           help="set bandwidth-time product [default=%default] (GMSK)")
223     add_options=staticmethod(add_options)
224
225
226     def extract_kwargs_from_options(options):
227         """
228         Given command line options, create dictionary suitable for passing to __init__
229         """
230         return modulation_utils.extract_kwargs_from_options(cpm_mod.__init__,
231                                                             ('self', 'fg'), options)
232     extract_kwargs_from_options=staticmethod(extract_kwargs_from_options)
233
234
235
236 # /////////////////////////////////////////////////////////////////////////////
237 #                            CPM demodulator
238 # /////////////////////////////////////////////////////////////////////////////
239 #
240 # Not yet implemented
241 #
242
243
244
245 #
246 # Add these to the mod/demod registry
247 #
248 modulation_utils.add_type_1_mod('cpm', cpm_mod)
249 #modulation_utils.add_type_1_demod('cpm', cpm_demod)