// // Copyright 2010 Ettus Research LLC // // This program 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 of the License, or // (at your option) any later version. // // This program 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 this program. If not, see . // // No RX IO Pins Used // RX IO Functions //ADC/DAC functions: //DAC 1: RF AGC //DAC 2: IF AGC //min freq: 50e6 //max freq: 860e6 //gain range: [0:1dB:115dB] #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace uhd; using namespace uhd::usrp; using namespace boost::assign; /*********************************************************************** * The tvrx constants **********************************************************************/ static const bool tvrx_debug = true; static const freq_range_t tvrx_freq_range(50e6, 860e6); static const prop_names_t tvrx_antennas = ""; //only got one static const uhd::dict tvrx_gain_ranges = map_list_of ("RF", gain_range_t(0, 60, float(60.0/4096))) //both gains are analog and controlled by DAC ("IF", gain_range_t(0, 35, float(35.0/4096))) //the old driver used 1dB for both; i don't think that's right? ; static const uhd::dict tvrx_freq_ranges = map_list_of ("VHFLO", freq_range_t(50e6, 158e6)) //this isn't used outside this driver. just for us. ("VHFHI", freq_range_t(158e6, 454e6)) ("UHF" , freq_range_t(454e6, 860e6)) ; //we might need to spec the various bands for band selection. static const double tvrx_lo_freq = 36e6; //LO freq of TVRX module /*********************************************************************** * The tvrx dboard class **********************************************************************/ class tvrx : public rx_dboard_base{ public: tvrx(ctor_args_t args); ~tvrx(void); void rx_get(const wax::obj &key, wax::obj &val); void rx_set(const wax::obj &key, const wax::obj &val); private: uhd::dict _gains; dtt75403_regs_t _dtt75403_regs; boost::uint8_t _dtt75403_addr(void){ return (this->get_iface()->get_special_props().mangle_i2c_addrs)? 0x60 : 0x61; //ok really? we could rename that call }; void set_gain(float gain, const std::string &name); void send_reg(boost::uint8_t start_reg, boost::uint8_t stop_reg){ //why is this an inline anyway? bring this out start_reg = boost::uint8_t(std::clip(int(start_reg), 0x0, 0x5)); stop_reg = boost::uint8_t(std::clip(int(stop_reg), 0x0, 0x5)); for(boost::uint8_t start_addr=start_reg; start_addr <= stop_reg; start_addr += sizeof(boost::uint32_t) - 1){ int num_bytes = int(stop_reg - start_addr + 1) > int(sizeof(boost::uint32_t)) - 1 ? sizeof(boost::uint32_t) - 1 : stop_reg - start_addr + 1; //create buffer for register data (+1 for start address) byte_vector_t regs_vector(num_bytes + 1); //first byte is the address of first register regs_vector[0] = start_addr; //get the register data for(int i=0; iget_iface()->write_i2c( _max2118_addr(), regs_vector ); } } }; /*********************************************************************** * Register the tvrx dboard **********************************************************************/ static dboard_base::sptr make_tvrx(dboard_base::ctor_args_t args){ return dboard_base::sptr(new tvrx(args)); } UHD_STATIC_BLOCK(reg_tvrx_dboard){ //register the factory function for the rx dbid dboard_manager::register_dboard(0x0003, &make_tvrx, "tvrx"); } /*********************************************************************** * Structors **********************************************************************/ tvrx::tvrx(ctor_args_t args) : rx_dboard_base(args){ //enable only the clocks we need //TODO: YOU GOT SOME CLOCK SETUP TO DO HERE, DOGGGG this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); //set the gpio directions and atr controls (identically) this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0x0); // All unused in atr if (this->get_iface()->get_special_props().soft_clock_divider){ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x1); // GPIO0 is clock } else{ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x0); // All Inputs } //send initial register settings //HAAAAY GUYYYYYSSS //set default gains BOOST_FOREACH(const std::string &name, tvrx_gain_ranges.keys()){ set_gain(tvrx_gain_ranges[name].min, name); } } tvrx::~tvrx(void){ } /*********************************************************************** * Gain Handling **********************************************************************/ /*! * Convert a requested gain for the GC2 vga into the integer register value. * The gain passed into the function will be set to the actual value. * \param gain the requested gain in dB * \return 5 bit the register value */ //yo this should look the same as the below one for the IF, dogg static int gain_to_gc2_vga_reg(float &gain){ int reg = 0; gain = std::clip(float(boost::math::iround(gain)), tvrx_gain_ranges["GC2"].min, tvrx_gain_ranges["GC2"].max); // Half dB steps from 0-5dB, 1dB steps from 5-24dB if (gain < 5) { reg = boost::math::iround(31.0 - gain/0.5); gain = float(boost::math::iround(gain) * 0.5); } else { reg = boost::math::iround(22.0 - (gain - 4.0)); gain = float(boost::math::iround(gain)); } if (tvrx_debug) std::cerr << boost::format( "tvrx GC2 Gain: %f dB, reg: %d" ) % gain % reg << std::endl; return reg; } /*! * Convert a requested gain for the RF gain into the dac_volts value. * The gain passed into the function will be set to the actual value. * \param gain the requested gain in dB * \return dac voltage value */ //yo what about the 1.22 opamp gain of the USRP1 -- is this replicated on U2? static float gain_to_rfgain_dac(float &gain){ //clip the input gain = std::clip(gain, tvrx_gain_ranges["RF"].min, tvrx_gain_ranges["RF"].max); //voltage level constants static const float max_volts = float(0), min_volts = float(3.3); static const float slope = (max_volts-min_volts)/tvrx_gain_ranges["RF"].max; //calculate the voltage for the aux dac float dac_volts = gain*slope + min_volts; if (tvrx_debug) std::cerr << boost::format( "tvrx RFAGC Gain: %f dB, dac_volts: %f V" ) % gain % dac_volts << std::endl; //the actual gain setting gain = (dac_volts - min_volts)/slope; return dac_volts; } void tvrx::set_gain(float gain, const std::string &name){ assert_has(tvrx_gain_ranges.keys(), name, "tvrx gain name"); if (name == "RF"){ //should look the same as below } else if(name == "IF"){ //write the new voltage to the aux dac CHECK THIS this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_A, gain_to_gc1_rfvga_dac(gain)); } else UHD_THROW_INVALID_CODE_PATH(); _gains[name] = gain; } /*********************************************************************** * RX Get and Set **********************************************************************/ void tvrx::rx_get(const wax::obj &key_, wax::obj &val){ named_prop_t key = named_prop_t::extract(key_); //handle the get request conditioned on the key switch(key.as()){ case SUBDEV_PROP_NAME: val = get_rx_id().to_pp_string(); return; case SUBDEV_PROP_OTHERS: val = prop_names_t(); //empty return; case SUBDEV_PROP_GAIN: assert_has(_gains.keys(), key.name, "tvrx gain name"); val = _gains[key.name]; return; case SUBDEV_PROP_GAIN_RANGE: assert_has(tvrx_gain_ranges.keys(), key.name, "tvrx gain name"); val = tvrx_gain_ranges[key.name]; return; case SUBDEV_PROP_GAIN_NAMES: val = prop_names_t(tvrx_gain_ranges.keys()); return; case SUBDEV_PROP_FREQ: val = tvrx_lo_freq; return; case SUBDEV_PROP_FREQ_RANGE: val = tvrx_freq_range; return; case SUBDEV_PROP_ANTENNA: val = std::string(tvrx_antennas); return; case SUBDEV_PROP_ANTENNA_NAMES: val = tvrx_antennas; return; case SUBDEV_PROP_CONNECTION: val = SUBDEV_CONN_COMPLEX_IQ; return; case SUBDEV_PROP_USE_LO_OFFSET: val = false; return; case SUBDEV_PROP_LO_LOCKED: val = true; return; default: UHD_THROW_PROP_GET_ERROR(); } } //for smaaht kids to use to set advanced props void tvrx::rx_set(const wax::obj &key_, const wax::obj &val){ named_prop_t key = named_prop_t::extract(key_); //handle the get request conditioned on the key switch(key.as()){ case SUBDEV_PROP_GAIN: this->set_gain(val.as(), key.name); return; default: UHD_THROW_PROP_SET_ERROR(); } }