Imported Upstream version 3.0
[debian/gnuradio] / gnuradio-core / src / python / gnuradio / gruimpl / lmx2306.py
1 #!/usr/bin/env python
2 #
3 # Copyright 2004 Free Software Foundation, Inc.
4
5 # This file is part of GNU Radio
6
7 # GNU Radio is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2, or (at your option)
10 # any later version.
11
12 # GNU Radio is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16
17 # You should have received a copy of the GNU General Public License
18 # along with GNU Radio; see the file COPYING.  If not, write to
19 # the Free Software Foundation, Inc., 51 Franklin Street,
20 # Boston, MA 02110-1301, USA.
21
22
23 '''Control National LMX2306 based frequency synthesizer'''
24
25 from gnuradio import gr
26 from gnuradio import eng_notation
27 from gnuradio.eng_option import eng_option
28 from optparse import OptionParser
29
30 # bottom two bits of 21 bit word select which register to program
31
32 R_REG =  0x0
33 AB_REG = 0x1
34 F_REG  = 0x2
35
36 F_counter_reset = (1 << 2)
37 F_phase_detector_polarity = (1 << 7)
38
39 F_LD_tri_state           = (0 << 4)
40 F_LD_R_divider_output    = (4 << 4)
41 F_LD_N_divider_output    = (2 << 4)
42 F_LD_serial_data_output  = (6 << 4)
43 F_LD_digital_lock_detect = (1 << 4)
44 F_LD_open_drain          = (5 << 4)
45 F_LD_high                = (3 << 4)
46 F_LD_low                 = (7 << 4)
47
48 # F_default = F_LD_digital_lock_detect | F_phase_detector_polarity
49 F_default = F_LD_open_drain | F_phase_detector_polarity
50
51 #
52 # 4 control pins:
53 #   CE   always high
54 #   LE   load enable.  When LE goes high, data stored in the shift register
55 #        is loaded into one of the three registers
56 #   CLK  data is clocked in on the rising edge
57 #   DATA single data bit.  Entered MSB first
58
59 DB_CLK =  (1 << 0)
60 DB_DATA = (1 << 1)
61 DB_LE =   (1 << 2)
62 DB_CE =   (1 << 3)
63
64 class lmx2306 (object):
65     '''Control the National LMX2306 PLL'''
66     __slots__ = ['pp', 'shadow', 'fosc', 'r', 'step_size', 'verbose']
67     def __init__ (self, fosc, step_size, which_pp = 0):
68         '''FOSC is the frequency of the reference oscillator,
69         STEP_SIZE is the step between valid frequencies,
70         WHICH_PP specifies which parallel port to use
71         '''
72         self.pp = gr.make_ppio (which_pp)
73         self.shadow = DB_CE
74         self.pp.lock ()
75         self.pp.write_data (self.shadow)
76         self.pp.unlock ()
77         self.verbose = False
78         self._set_fosc (fosc)
79         self._set_step (step_size)
80
81         
82     def program (self, r, a, b):
83         if self.verbose:
84             print "lmx2306: r = %d  a = %d  b = %d" % (r, a, b)
85         self.pp.lock ()
86         self._write_word (F_REG | F_default | F_counter_reset)
87         self._write_word (R_REG | ((r & 0x3fff) << 2))
88         self._write_word (AB_REG | ((a & 0x1f) << 2) | ((b & 0x1fff) << 7))
89         self._write_word (F_REG | F_default)
90         self.pp.unlock ()
91
92     def set_freq (self, freq):
93         '''Set the PLL frequency to FREQ
94
95         Return the actual freq value set.  It will be rounded down to a
96         multiple of step_size
97         '''
98         divisor = int (freq / self.step_size)
99         actual = divisor * self.step_size
100         (a, b) = self._compute_ab (divisor)
101         self.program (self.r, a, b)
102         return actual
103
104     # ----------------------------------------------------------------
105     
106     def _set_fosc (self, ref_oscillator_freq):
107         self.fosc = ref_oscillator_freq
108         
109     def _set_step (self, step_size):
110         r = int (self.fosc / step_size)
111         if r * step_size != self.fosc:
112             raise ValueError, "step_size is not a factor of self.fosc"
113         if r < 3 or r > 16383:
114             raise ValueError, "r is out of range"
115         self.r = r
116         self.step_size = step_size
117         
118     def _compute_ab (self, divisor):
119         b = divisor / 8
120         a = divisor - (b * 8)
121         if b < 3 or b > 8191 or a > b:
122             raise ValueError, "Invalid divisor"
123         return (a, b)
124
125     def _write_word (self, w):
126         for i in range(21):
127             if w & (1 << 20):
128                 self._set_DATA_1 ()
129             else:
130                 self._set_DATA_0 ()
131             w = (w << 1) & 0x0ffffff
132             self._set_CLK_1 ()
133             self._set_CLK_0 ()
134         self._set_LE_1 ()
135         self._set_LE_0 ()
136
137     def _set_LE_0 (self):
138         self.shadow = self.shadow & ~DB_LE
139         self.pp.write_data (self.shadow)
140
141     def _set_LE_1 (self):
142         self.shadow = self.shadow | DB_LE
143         self.pp.write_data (self.shadow)
144
145     def _set_CLK_0 (self):
146         self.shadow = self.shadow & ~DB_CLK
147         self.pp.write_data (self.shadow)
148
149     def _set_CLK_1 (self):
150         self.shadow = self.shadow | DB_CLK
151         self.pp.write_data (self.shadow)
152
153     def _set_DATA_0 (self):
154         self.shadow = self.shadow & ~DB_DATA
155         self.pp.write_data (self.shadow)
156
157     def _set_DATA_1 (self):
158         self.shadow = self.shadow | DB_DATA
159         self.pp.write_data (self.shadow)
160
161 if __name__ == '__main__':
162     parser = OptionParser (option_class=eng_option)
163     parser.add_option ("-o", "--fosc", type="eng_float", default=32e6,
164                        help="set reference oscillator freq to FREQ", metavar="FREQ")
165     parser.add_option ("-s", "--step-size", type="eng_float", default=10e3,
166                        help="set the frequency step size to STEP_SIZE")
167     parser.add_option ("-f", "--freq", type="eng_float", default=430e6,
168                        help="set VCO frequency to FREQ")
169     parser.add_option ("-v", "--verbose", action="store_true", default=False)
170     (options, args) = parser.parse_args ()
171
172     if options.verbose:
173         print "fosc = %s  step = %s  fvco = %s" % (
174             eng_notation.num_to_str (options.fosc),
175             eng_notation.num_to_str (options.step_size),
176             eng_notation.num_to_str (options.freq))
177     
178     lmx = lmx2306 (options.fosc, options.step_size)
179     lmx.verbose = options.verbose
180
181     actual = lmx.set_freq (options.freq)
182
183     if options.verbose:
184         print "fvco_actual = %s  delta = %s" % (
185             eng_notation.num_to_str (actual),
186             eng_notation.num_to_str (options.freq - actual))