2
# CPM modulation and demodulation.
5
# Copyright 2005-2007,2011 Free Software Foundation, Inc.
7
# This file is part of GNU Radio
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)
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.
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.
25
# See gnuradio-examples/python/digital for examples
27
from gnuradio import gr, blks2
32
import modulation_utils
34
# default values (used in __init__ and add_options)
35
_def_samples_per_symbol = 2
36
_def_bits_per_symbol = 1
38
_def_h_denominator = 2
39
_def_cpm_type = 0 # 0=CPFSK, 1=GMSK, 2=RC, 3=GENERAL
41
_def_symbols_per_pulse = 1
42
_def_generic_taps = numpy.empty(1)
47
# /////////////////////////////////////////////////////////////////////////////
49
# /////////////////////////////////////////////////////////////////////////////
51
class cpm_mod(gr.hier_block2):
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,
59
symbols_per_pulse=_def_symbols_per_pulse,
60
generic_taps=_def_generic_taps,
64
Hierarchical block for Continuous Phase
67
The input is a byte stream (unsigned char)
68
representing packed bits and the
69
output is the complex modulated signal at baseband.
71
See Proakis for definition of generic CPM signals:
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)
77
@param samples_per_symbol: samples per baud >= 2
78
@type samples_per_symbol: integer
79
@param bits_per_symbol: bits per symbol
80
@type bits_per_symbol: integer
81
@param h_numerator: numerator of modulation index
82
@type h_numerator: integer
83
@param h_denominator: denominator of modulation index (numerator and denominator must be relative primes)
84
@type h_denominator: integer
85
@param cpm_type: supported types are: 0=CPFSK, 1=GMSK, 2=RC, 3=GENERAL
86
@type cpm_type: integer
87
@param bt: bandwidth symbol time product for GMSK
89
@param symbols_per_pulse: shaping pulse duration in symbols
90
@type symbols_per_pulse: integer
91
@param generic_taps: define a generic CPM pulse shape (sum = samples_per_symbol/2)
92
@type generic_taps: array of floats
94
@param verbose: Print information about modulator?
96
@param debug: Print modulation data to files?
100
gr.hier_block2.__init__(self, "cpm_mod",
101
gr.io_signature(1, 1, gr.sizeof_char), # Input signature
102
gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature
104
self._samples_per_symbol = samples_per_symbol
105
self._bits_per_symbol = bits_per_symbol
106
self._h_numerator = h_numerator
107
self._h_denominator = h_denominator
108
self._cpm_type = cpm_type
110
if cpm_type == 0 or cpm_type == 2 or cpm_type == 3: # CPFSK, RC, Generic
111
self._symbols_per_pulse = symbols_per_pulse
112
elif cpm_type == 1: # GMSK
113
self._symbols_per_pulse = 4
115
raise TypeError, ("cpm_type must be an integer in {0,1,2,3}, is %r" % (cpm_type,))
117
self._generic_taps=numpy.array(generic_taps)
119
if samples_per_symbol < 2:
120
raise TypeError, ("samples_per_symbol must be >= 2, is %r" % (samples_per_symbol,))
122
self.nsymbols = 2**bits_per_symbol
123
self.sym_alphabet = numpy.arange(-(self.nsymbols-1),self.nsymbols,2).tolist()
126
self.ntaps = int(self._symbols_per_pulse * samples_per_symbol)
127
sensitivity = 2 * pi * h_numerator / h_denominator / samples_per_symbol
129
# Unpack Bytes into bits_per_symbol groups
130
self.B2s = gr.packed_to_unpacked_bb(bits_per_symbol,gr.GR_MSB_FIRST)
133
# Turn it into symmetric PAM data.
134
self.pam = gr.chunks_to_symbols_bf(self.sym_alphabet,1)
136
# Generate pulse (sum of taps = samples_per_symbol/2)
137
if cpm_type == 0: # CPFSK
138
self.taps= (1.0/self._symbols_per_pulse/2,) * self.ntaps
139
elif cpm_type == 1: # GMSK
140
gaussian_taps = gr.firdes.gaussian(
142
samples_per_symbol, # symbol_rate
143
bt, # bandwidth * symbol time
144
self.ntaps # number of taps
146
sqwave = (1,) * samples_per_symbol # rectangular window
147
self.taps = numpy.convolve(numpy.array(gaussian_taps),numpy.array(sqwave))
148
elif cpm_type == 2: # Raised Cosine
149
# generalize it for arbitrary roll-off factor
150
self.taps = (1-numpy.cos(2*pi*numpy.arange(0,self.ntaps)/samples_per_symbol/self._symbols_per_pulse))/(2*self._symbols_per_pulse)
151
elif cpm_type == 3: # Generic CPM
152
self.taps = generic_taps
154
raise TypeError, ("cpm_type must be an integer in {0,1,2,3}, is %r" % (cpm_type,))
156
self.filter = blks2.pfb_arb_resampler_fff(samples_per_symbol, self.taps)
159
self.fmmod = gr.frequency_modulator_fc(sensitivity)
162
self._print_verbage()
165
self._setup_logging()
168
self.connect(self, self.B2s, self.pam, self.filter, self.fmmod, self)
170
def samples_per_symbol(self):
171
return self._samples_per_symbol
173
def bits_per_symbol(self):
174
return self._bits_per_symbol
176
def h_numerator(self):
177
return self._h_numerator
179
def h_denominator(self):
180
return self._h_denominator
183
return self._cpm_type
188
def symbols_per_pulse(self):
189
return self._symbols_per_pulse
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
200
print "CPM type = %d" % self._cpm_type
201
if self._cpm_type == 1:
202
print "Gaussian filter BT = %.2f" % self._bt
205
def _setup_logging(self):
206
print "Modulation logging turned on."
207
self.connect(self.B2s,
208
gr.file_sink(gr.sizeof_float, "symbols.dat"))
209
self.connect(self.pam,
210
gr.file_sink(gr.sizeof_float, "pam.dat"))
211
self.connect(self.filter,
212
gr.file_sink(gr.sizeof_float, "filter.dat"))
213
self.connect(self.fmmod,
214
gr.file_sink(gr.sizeof_gr_complex, "fmmod.dat"))
217
def add_options(parser):
219
Adds CPM modulation-specific options to the standard parser
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)
226
def extract_kwargs_from_options(options):
228
Given command line options, create dictionary suitable for passing to __init__
230
return modulation_utils.extract_kwargs_from_options(cpm_mod.__init__,
232
extract_kwargs_from_options=staticmethod(extract_kwargs_from_options)
236
# /////////////////////////////////////////////////////////////////////////////
238
# /////////////////////////////////////////////////////////////////////////////
240
# Not yet implemented
244
# Add these to the mod/demod registry
246
modulation_utils.add_type_1_mod('cpm', cpm_mod)
247
#modulation_utils.add_type_1_demod('cpm', cpm_demod)