9b8ada4a2284ac23bf203a024979648a81839323
[debian/gnuradio] / usrp / host / lib / db_bitshark_rx.cc
1 //
2 // Copyright 2010 Free Software Foundation, Inc.
3 // 
4 // This file is part of GNU Radio
5 // 
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 asversion 3, or (at your option)
9 // any later version.
10 // 
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.
15 // 
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., 51 Franklin Street,
19 // Boston, MA 02110-1301, USA.
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <usrp/db_bitshark_rx.h>
26 #include <db_base_impl.h>
27 #include <cmath>
28 #include <cstdio>
29 #include <string.h>
30 #include <stdint.h>
31
32 /* Note: Thie general structure of this file is based on the db_dbsrx.cc 
33    codebase for the dbsrx daughterboard. */
34
35 /* The following defines specify the address map provided by the
36    Bitshark card. These registers are all accessed over I2C. */
37 #define RF_CENTER_FREQ_REG 0x00
38 #define RF_CHAN_FILTER_BW_REG 0x01
39 #define RF_GAIN_REG 0x02
40 #define BB_GAIN_REG 0x03
41 #define ADF4350_REG 0x10
42 #define SKY73202_REG 0x11
43 #define CLOCK_SCHEME_REG 0x20
44
45 /* The following table lists the registers provided by the BURX board that
46    are accessible over I2C:
47    --------------------------------------------------------
48    |RegAddr: 0x00-RF Center Freq register |
49        |4-bytes 0x00|
50        |4-byte unsigned RF center freq (in KHz)|
51    |RegAddr: 0x01-RF channel filter bandwidth register |
52        |4-bytes 0x00|
53        |4-byte unsigned RF channel filter bw (in KHz)|
54    |RegAddr: 0x02-RF gain register |
55        |7-bytes 0x00|
56        |1-byte signed RF gain (in dB)|
57    |RegAddr: 0x03-Baseband gain register |
58        |4-bytes 0x00|
59        |4-byte signed baseband filter gain (in dB)|
60    |RegAddr: 0x10-ADF4350 register |
61        |4-bytes 0x00|
62        |4-byte ADF4350 register value (actual ADF4350 reg addr embedded 
63         within 4-byte value)|
64    |RegAddr: 0x11-SKY73202 register |
65        |5-bytes 0x00|
66        |1-byte reg 0 of SKY73202 |
67        |1-byte reg 1 of SKY73202 |
68        |1-byte reg 2 of SKY73202 |
69    |RegAddr: 0x20-Clock Scheme |
70        |3-bytes 0x00|
71        |1-byte indicating clocking scheme:
72         -0x00 -> BURX local TCXO off, BURX accepts ref clock from
73                  USRP (freq of USRP's ref clock specified in bytes 2-5)
74         -0x01 -> BURX local TCXO on, BURX uses its local TCXO as its ref
75                  clock, TCXO signal output for use by USRP |
76        |4-byte USRP ref clock freq in hz (only needed if byte 1 set to 0x00) |
77        
78   ---------------------------------------------------------------------------
79    
80    As an example, lets say the client wants to set an RF center freq of
81    1000 MHz.  In KHz, this translates to 1000000 (resolution is only down to
82    steps of 1 KHz), which is 0x000F4240 in hex.  So the complete 9-byte I2C 
83    sequence that the client should send is as follows:
84    byte 0: 0x00-register 0x00 is the target of the write operation
85    bytes 1-4: 0x00 (padding)
86    byte 5: 0x40 (LSB of the 1000000 KHz value, in hex)
87    byte 6: 0x42
88    byte 7: 0x0F
89    byte 8: 0x00 (MSB of the 1000000 KHz value, in hex)
90
91    If using the usrper cmd-line application on a PC, this sequence would
92    be sent as follows (assuming that the BURX is in slot A):
93    
94    # usrper i2c_write 0x47 000000000040420F00
95    
96    How about another example...lets say the client wants to setup the clock
97    scheme to use scheme #1 where the 26 MHz TCXO on the BURX board is enabled,
98    and is provided to the USRP.  26 MHz (i.e. 26 million), in hex, is 0x18CBA80.
99    So the complete 9-byte I2C sequence that the client should send is as follows:
100    byte 0: 0x20-register 0x20 is the target of the write operation
101    bytes 1-3: 0x00 (padding)
102    byte 4: 0x01 (indicating that clock scheme #1 is wanted)
103    byte 5: 0x80 (LSB of the BURX ref clk freq)
104    byte 6: 0xBA
105    byte 7: 0x8C
106    byte 8: 0x01 (MSB of the BURX ref clk freq)
107    
108    To enable the BURX local ref clk, which will also make it available on the
109    on-board U.FL connector as a source for the USRP, a user can also use
110    the usrper cmd-line application on a PC.  The following sequence would
111    be sent (assuming that the BURX is in slot A):
112    
113    # usrper i2c_write 0x47 200000000180BA8C01
114
115 */
116
117 #define NUM_BYTES_IN_I2C_CMD 9   
118
119 /*****************************************************************************/
120
121 db_bitshark_rx::db_bitshark_rx(usrp_basic_sptr _usrp, int which)
122   : db_base(_usrp, which)
123 {
124     // Control Bitshark receiver USRP daughterboard.
125     // 
126     // @param usrp: instance of usrp.source_c
127     // @param which: which side: 0, 1 corresponding to RX_A or RX_B respectively
128
129     // turn off all outputs
130     usrp()->_write_oe(d_which, 0, 0xffff);
131
132     if (which == 0) 
133     {
134         d_i2c_addr = 0x47;
135     }
136     else 
137     {
138         d_i2c_addr = 0x45;
139     }
140
141     // initialize gain
142     set_gain((gain_min() + gain_max()) / 2.0);
143
144     // by default, assume we're using the USRPs clock as the ref clk,
145     // so setup the clock scheme and frequency.  If the user wants
146     // to use the Bitshark's TCXO, the clock scheme should be set
147     // to 1, the freq should be set to 26000000, and a top-level
148     // 'make' and 'make install' needs to be executed.  In addition, 
149     // a U.FL to SMA cable needs to connect J6 on the Bitshark to 
150     // the external clk input on the USRP
151     set_clock_scheme(0,64000000);
152
153     bypass_adc_buffers(true);
154 }
155
156 db_bitshark_rx::~db_bitshark_rx()
157 {
158     shutdown();
159 }
160
161 /************ Private Functions **********************/
162
163 void
164 db_bitshark_rx::_set_pga(int pga_gain)
165 {
166     assert(pga_gain>=0 && pga_gain<=20);
167     if(d_which == 0) 
168     {
169         usrp()->set_pga (0, pga_gain);
170         usrp()->set_pga (1, pga_gain);
171     }
172     else 
173     {
174         usrp()->set_pga (2, pga_gain);
175         usrp()->set_pga (3, pga_gain);
176     }
177 }
178
179 /************ Public Functions **********************/
180 void
181 db_bitshark_rx::shutdown()
182 {
183     if (!d_is_shutdown)
184     {
185         d_is_shutdown = true;
186     }
187 }
188
189 bool
190 db_bitshark_rx::set_bw (float bw)
191 {
192     std::vector<int> args(NUM_BYTES_IN_I2C_CMD,0);
193     uint16_t rf_bw_in_khz = (uint16_t)(bw/1000.0);
194     char val[4];
195     bool result = false;
196     uint8_t try_count = 0;
197     
198     memset(val,0x00,4);
199     if (rf_bw_in_khz < 660  || rf_bw_in_khz > 56000) 
200     {
201         fprintf(stderr, "db_bitshark_rx::set_bw: bw (=%d) must be between 660 KHz and 56 MHz inclusive\n", rf_bw_in_khz);
202         return false;
203     }
204     //fprintf(stdout,"Setting bw: requested bw in khz is %d\r\n",rf_bw_in_khz);
205     memcpy(val,&rf_bw_in_khz,4);
206     args[0] = RF_CHAN_FILTER_BW_REG;
207     args[5] = val[0];
208     args[6] = val[1];
209     args[7] = val[2];
210     args[8] = val[3];
211     while ((result != true) && (try_count < 3))
212     {
213         result=usrp()->write_i2c (d_i2c_addr, int_seq_to_str (args));
214         try_count++;
215     }
216
217     if (result == false)
218     {
219         fprintf(stderr, "db_bitshark_rx:set_bw: giving up after 3 tries without success\n");
220     }
221     
222     return result;
223 }
224
225 /* The gain referenced below is RF gain only.  There are two independent
226    gain settings at RF: a digital step attenuator (providing 0, -6, -12, and
227    -18 dB of attenuation), and a second LNA (LNA2) that provides ~25 dB of
228    gain (roughly...it actually depends on the RF freq).  So combining these
229    two stages can provide an overall gain range from 0 (which is mapped
230    to -18 dB on the step attenuator + LNA2 turned off) to 42 (which is
231    mapped to 0 dB on the step attenuator + LNA2 turned on).  
232    
233    There could be better ways to map these, but this is sufficient for
234    now. */
235 float
236 db_bitshark_rx::gain_min()
237 {
238     return 0;
239 }
240
241 float
242 db_bitshark_rx::gain_max()
243 {
244     return 42;
245 }
246
247 float
248 db_bitshark_rx::gain_db_per_step()
249 {
250     return 6;
251 }
252
253 bool 
254 db_bitshark_rx::set_gain(float gain)
255 {
256     // Set the gain.
257     // 
258     // @param gain:  RF gain in decibels, range of 0-42
259     // @returns True/False
260     
261     std::vector<int> args(NUM_BYTES_IN_I2C_CMD,0);
262     uint8_t final_gain = (uint8_t)gain;
263     bool result = false;
264     uint8_t try_count = 0;
265         
266     if (gain < gain_min() || gain > gain_max()) 
267     {
268         fprintf(stderr,"db_bitshark_rx::set_gain: gain (=%f) must be between %f and %f inclusive\n", gain,gain_min(),gain_max());
269         return false;
270     }
271     //fprintf(stdout,"db_bitshark_rx::set_gain: requested gain of %f\r\n",gain);
272     args[0] = RF_GAIN_REG;
273     args[5] = (int)gain;
274
275     while ((result != true) && (try_count < 3))
276     {
277         result=usrp()->write_i2c (d_i2c_addr, int_seq_to_str (args));
278         try_count++;
279     }
280
281     if (result == false)
282     {
283         fprintf(stderr, "db_bitshark_rx:set_gain: giving up after 3 tries without success\n");
284     }
285     
286     return result;
287 }
288
289
290 bool 
291 db_bitshark_rx::set_clock_scheme(uint8_t clock_scheme, uint32_t ref_clk_freq)
292 {
293     // Set the clock scheme for determining how the BURX
294     // dboard receives its clock.  Note: Ideally, the constructor for the
295     // BURX board could simply call this method to set how it wants the
296     // clock scheme configured.  However, depending on the application
297     // using the daughterboard, the constructor may run _after_ some
298     // other portion of the application needs the FPGA.  And if the
299     // the clock source for the FPGA was the BURX's 26 MHz TCXO, we're in
300     // a chicken-before-the-egg dilemna.  So the solution is to leave
301     // this function here for reference in case an app wants to use it,
302     // and also give the user the ability to set the clock scheme through
303     // the usrper cmd-line application (see example at the top of this
304     // file).
305     // 
306     // @param clock_scheme
307     // @param ref_clk_freq in Hz
308     // @returns True/False
309     
310     std::vector<int> args(NUM_BYTES_IN_I2C_CMD,0);
311     bool result = false;
312     uint8_t try_count = 0;
313     char val[4];
314         
315     if (clock_scheme > 1) 
316     {
317         fprintf(stderr,"db_bitshark_rx::set_clock_scheme: invalid scheme %d\n",clock_scheme);
318         return false;
319     }
320     //fprintf(stdout,"db_bitshark_rx::set_clock_scheme: requested clock schem of %d with freq %d Hz \n",clock_scheme,ref_clk_freq);
321     memcpy(val,&ref_clk_freq,4);
322     args[0] = CLOCK_SCHEME_REG;
323     args[4] = (int)clock_scheme;
324     args[5] = val[0];
325     args[6] = val[1];
326     args[7] = val[2];
327     args[8] = val[3];
328
329     while ((result != true) && (try_count < 3))
330     {
331         result=usrp()->write_i2c (d_i2c_addr, int_seq_to_str (args));
332         try_count++;
333     }
334
335     if (result == false)
336     {
337         fprintf(stderr, "db_bitshark_rx:set_clock_scheme: giving up after 3 tries without success\n");
338     }
339     return result;
340 }
341
342 double
343 db_bitshark_rx::freq_min()
344 {    
345     return 300e6;
346 }
347
348 double
349 db_bitshark_rx::freq_max()
350 {    
351     return 4e9;
352 }
353
354 struct freq_result_t
355 db_bitshark_rx::set_freq(double freq)
356 {
357     // Set the frequency.
358     // 
359     // @param freq:  target RF frequency in Hz
360     // @type freq:   double
361     // 
362     // @returns (ok, actual_baseband_freq) where:
363     //   ok is True or False and indicates success or failure,
364     //   actual_baseband_freq is RF frequency that corresponds to DC in the IF.
365     
366     std::vector<int> args(NUM_BYTES_IN_I2C_CMD,0);
367     std::vector<int> bytes(2);
368     char val[4];
369     freq_result_t act_freq = {false, 0};
370     uint32_t freq_in_khz = (uint32_t)(freq/1000.0);
371     bool result = false;
372     uint8_t try_count = 0;
373         
374     memset(val,0x00,4);
375     if(!(freq>=freq_min() && freq<=freq_max())) 
376     {
377         return act_freq;
378     }
379     
380     //fprintf(stdout,"db_bitshark_rx::set_freq: requested freq is %d KHz\n",freq_in_khz);
381     memcpy(val,&freq_in_khz,4);
382     args[0] = RF_CENTER_FREQ_REG;
383     args[5] = val[0];
384     args[6] = val[1];
385     args[7] = val[2];
386     args[8] = val[3];
387
388     while ((result != true) && (try_count < 3))
389     {
390         result=usrp()->write_i2c (d_i2c_addr, int_seq_to_str (args));
391         try_count++;
392     }
393
394     if (result == false)
395     {
396         fprintf(stderr, "db_bitshark_rx:set_freq: giving up after 3 tries without success\n");
397     }
398         
399     act_freq.ok = result;
400     act_freq.baseband_freq = (double)freq;
401     return act_freq;
402 }
403
404 bool 
405 db_bitshark_rx::is_quadrature()
406 {    
407     // Return True if this board requires both I & Q analog channels.  
408     return true;
409 }
410
411 bool
412 db_bitshark_rx::i_and_q_swapped()
413 {
414     // Returns True since our I and Q channels are swapped
415     return true;
416 }