# # Copyright 2005 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 2, 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. # __all__ = ['tv_rx'] import math from usrpm import usrp_dbid import db_base import db_instantiator def int_seq_to_str(seq): """convert a sequence of integers into a string""" return ''.join (map (chr, seq)) def str_to_int_seq(str): """convert a string to a list of integers""" return map (ord, str) def control_byte_1(fast_tuning_p, reference_divisor): c = 0x88 if fast_tuning_p: c |= 0x40 if reference_divisor == 512: c |= 0x3 << 1 elif reference_divisor == 640: c |= 0x0 << 1 elif reference_divisor == 1024: c |= 0x1 << 1 else: assert 0 return c def control_byte_2(target_freq, shutdown_tx_PGA): if target_freq < 158e6: # VHF low c = 0xa0 elif target_freq < 464e6: # VHF high c = 0x90 else: # UHF c = 0x30 if shutdown_tx_PGA: c |= 0x08 return c class db_tv_rx(db_base.db_base): def __init__(self, usrp, which, first_IF, second_IF): """ Control Microtune 4937 based USRP daughterboard. @param usrp: instance of usrp.source_c @param which: which side: 0 or 1 corresponding to RX_A or RX_B respectively @type which: int """ # sets _u and _which db_base.db_base.__init__(self, usrp, which) self._i2c_addr = (0x60, 0x61)[which] self._first_IF = first_IF self._second_IF = second_IF self._reference_divisor = 640 self._fast_tuning = False self._inverted = False # FIXME get rid of this g = self.gain_range() # initialize gain self.set_gain(float(g[0]+g[1]) / 2) self.bypass_adc_buffers(False) # Gain setting def _set_rfagc(self,gain): assert gain <= 60 and gain >= 0 # FIXME this has a 0.5V step between gain = 60 and gain = 59. # Why are there two cases instead of a single linear case? if gain == 60: voltage = 4 else: voltage = gain/60.0 * 2.25 + 1.25 dacword = int(4096*voltage/1.22/3.3) # 1.22 = opamp gain assert dacword>=0 and dacword<4096 self._u.write_aux_dac(self._which, 1, dacword) def _set_ifagc(self,gain): assert gain <= 35 and gain >= 0 voltage = gain/35.0 * 2.1 + 1.4 dacword = int(4096*voltage/1.22/3.3) # 1.22 = opamp gain assert dacword>=0 and dacword<4096 self._u.write_aux_dac(self._which, 0, dacword) def _set_pga(self,pga_gain): assert pga_gain >=0 and pga_gain <=20 if(self._which == 0): self._u.set_pga (0, pga_gain) else: self._u.set_pga (2, pga_gain) def gain_range(self): return (0, 115, 1) def set_gain(self,gain): assert gain>=0 and gain<=115 if gain>60: rfgain = 60 gain = gain - 60 else: rfgain = gain gain = 0 if gain > 35: ifgain = 35 gain = gain - 35 else: ifgain = gain gain = 0 pgagain = gain self._set_rfagc(rfgain) self._set_ifagc(ifgain) self._set_pga(pgagain) def freq_range(self): return (50e6, 860e6, 10e3) def set_freq(self, target_freq): """ @returns (ok, actual_baseband_freq) where: ok is True or False and indicates success or failure, actual_baseband_freq is the RF frequency that corresponds to DC in the IF. """ r = self.freq_range() if target_freq < r[0] or target_freq > r[1]: return (False, 0) target_lo_freq = target_freq + self._first_IF; # High side mixing f_ref = 4e6 / self._reference_divisor # frequency steps divisor = int((target_lo_freq + (f_ref * 4)) / (f_ref * 8)) actual_lo_freq = (f_ref * 8 * divisor) actual_freq = actual_lo_freq - self._first_IF; if (divisor & ~0x7fff) != 0: # must be 15-bits or less return (False, 0) # build i2c command string buf = [0] * 4 buf[0] = (divisor >> 8) & 0xff # DB1 buf[1] = divisor & 0xff # DB2 buf[2] = control_byte_1(self._fast_tuning, self._reference_divisor) buf[3] = control_byte_2(actual_freq, True) ok = self._u.write_i2c(self._i2c_addr, int_seq_to_str (buf)) return (ok, actual_freq - self._second_IF) def is_quadrature(self): """ Return True if this board requires both I & Q analog channels. This bit of info is useful when setting up the USRP Rx mux register. """ return False def spectrum_inverted(self): """ The 43.75 MHz version is inverted """ return self._inverted # hook this daughterboard class into the auto-instantiation framework # With MT4937DI5-3x7702 with second downconversion db_instantiator.add(usrp_dbid.TV_RX, lambda usrp, which : (db_tv_rx(usrp, which, 43.75e6, 5.75e6),)) # With MT4937DI5-3x8680, and 3x8769 without second downconversion db_instantiator.add(usrp_dbid.TV_RX_REV_2, lambda usrp, which : (db_tv_rx(usrp, which, 44e6, 20e6),)) # With MT4937DI5-3x7901 without second downconversion, basically the same as tvrx2 db_instantiator.add(usrp_dbid.TV_RX_REV_3, lambda usrp, which : (db_tv_rx(usrp, which, 44e6, 20e6),))