1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
#
# Copyright 2008 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# GNU Radio is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
from gnuradio import gr, eng_notation
import math
n2s = eng_notation.num_to_str
class receive_path(gr.hier_block2):
def __init__(self,
if_rate, # Incoming sample rate
symbol_rate, # Original symbol rate
excess_bw, # RRC excess bandwidth, typically 0.35-0.5
costas_alpha, # Costas loop 1st order gain, typically 0.01-0.2
costas_beta, # Costas loop 2nd order gain, typically alpha^2/4.0
costas_max, # Costas loop max frequency offset in radians/sample
mm_gain_mu, # M&M loop 1st order gain, typically 0.001-0.2
mm_gain_omega, # M&M loop 2nd order gain, typically alpha^2/4.0
mm_omega_limit, # M&M loop max timing error
):
gr.hier_block2.__init__(self, "receive_path",
gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
gr.io_signature(0, 0, 0)) # Output signature
self._if_rate = if_rate
self._sps = int(self._if_rate/symbol_rate)
print "IF sample rate:", n2s(self._if_rate)
print "Symbol rate:", n2s(symbol_rate)
print "Samples/symbol:", self._sps
print "RRC bandwidth:", excess_bw
# Create AGC to scale input to unity
self._agc = gr.agc_cc(1e-5, 1.0, 1.0, 1.0)
# Create RRC with specified excess bandwidth
taps = gr.firdes.root_raised_cosine(1.0, # Gain
self._sps, # Sampling rate
1.0, # Symbol rate
excess_bw, # Roll-off factor
11*self._sps) # Number of taps
self._rrc = gr.fir_filter_ccf(1, taps)
# Create a Costas loop frequency/phase recovery block
print "Costas alpha:", costas_alpha
print "Costas beta:", costas_beta
print "Costas max:", costas_max
self._costas = gr.costas_loop_cc(costas_alpha, # PLL first order gain
costas_beta, # PLL second order gain
costas_max, # Max frequency offset rad/sample
-costas_max, # Min frequency offset rad/sample
2) # BPSK
# Create a M&M bit synchronization retiming block
mm_mu = 0.5
mm_omega = self._sps
print "MM gain mu:", mm_gain_mu
print "MM gain omega:", mm_gain_omega
print "MM omega limit:", mm_omega_limit
self._mm = gr.clock_recovery_mm_cc(mm_omega, # Initial samples/symbol
mm_gain_omega, # Second order gain
mm_mu, # Initial symbol phase
mm_gain_mu, # First order gain
mm_omega_limit) # Maximum timing offset
# Add an SNR probe on the demodulated constellation
self._snr_probe = gr.probe_mpsk_snr_c(10.0/symbol_rate)
self.connect(self._mm, self._snr_probe)
# Slice the resulting constellation into bits.
# Get inphase channel and make decision about 0
self._c2r = gr.complex_to_real()
self._slicer = gr.binary_slicer_fb()
# Descramble BERT sequence. A channel error will create 3 incorrect bits
self._descrambler = gr.descrambler_bb(0x8A, 0x7F, 7) # CCSDS 7-bit descrambler
# Measure BER by the density of 0s in the stream
self._ber = gr.probe_density_b(1.0/symbol_rate)
self.connect(self, self._agc, self._rrc, self._costas, self._mm,
self._c2r, self._slicer, self._descrambler, self._ber)
def frequency_offset(self):
return self._costas.freq()*self._if_rate/(2*math.pi)
def timing_offset(self):
return self._mm.omega()/self._sps-1.0
def snr(self):
return self._snr_probe.snr()
def ber(self):
return (1.0-self._ber.density())/3.0
|