void LMS7002M_rxtsp_enable(LMS7002M_t *self, const LMS7002M_chan_t channel, const bool enable)
{
    LMS7002M_set_mac_ch(self, channel);

    self->regs->reg_0x0400_en = enable?1:0;
    self->regs->reg_0x0400_bstart = 0;
    self->regs->reg_0x0400_insel = REG_0X0400_INSEL_LML; //r19 regs - probably means baseband input
    LMS7002M_regs_spi_write(self, 0x0400);

    self->regs->reg_0x0403_hbd_ovr = REG_0X0403_HBD_OVR_BYPASS;

    self->regs->reg_0x040a_agc_mode = REG_0X040A_AGC_MODE_BYPASS;

    self->regs->reg_0x040c_cmix_byp = 1;
    self->regs->reg_0x040c_agc_byp = 1;
    self->regs->reg_0x040c_gfir3_byp = 1;
    self->regs->reg_0x040c_gfir2_byp = 1;
    self->regs->reg_0x040c_gfir1_byp = 1;
    self->regs->reg_0x040c_dc_byp = 1;
    self->regs->reg_0x040c_gc_byp = 1;
    self->regs->reg_0x040c_ph_byp = 1;

    LMS7002M_regs_spi_write(self, 0x0400);
    LMS7002M_regs_spi_write(self, 0x0403);
    LMS7002M_regs_spi_write(self, 0x040a);
    LMS7002M_regs_spi_write(self, 0x040c);
}
LMS7002M_API void LMS7002M_txtsp_set_freq(LMS7002M_t *self, const LMS7002M_chan_t channel, const double freqRel)
{
    LMS7002M_set_mac_ch(self, channel);
    self->regs.reg_0x0208_cmix_byp = 0;
    LMS7002M_regs_spi_write(self, 0x0208);
    LMS7002M_set_nco_freq(self, LMS_TX, channel, freqRel);
}
double LMS7002M_rxtsp_read_rssi(LMS7002M_t *self, const LMS7002M_chan_t channel)
{
    LMS7002M_set_mac_ch(self, channel);

    self->regs->reg_0x040c_agc_byp = 0;
    LMS7002M_regs_spi_write(self, 0x040c);

    self->regs->reg_0x040a_agc_mode = REG_0X040A_AGC_MODE_RSSI;
    LMS7002M_regs_spi_write(self, 0x040a);

    self->regs->reg_0x0400_capsel = REG_0X0400_CAPSEL_RSSI;
    self->regs->reg_0x0400_capture = 0;
    LMS7002M_regs_spi_write(self, 0x0400);

    //trigger capture to capd readback regs
    self->regs->reg_0x0400_capture = 1;
    LMS7002M_regs_spi_write(self, 0x0400);

    const int rssi_lo = LMS7002M_spi_read(self, 0x040E);
    const int rssi_hi = LMS7002M_spi_read(self, 0x040F);
    const int rssi_int = (rssi_hi << 2) | rssi_lo;

    //a full scale DC input level be 1.0 RSSI
    return rssi_int/((double)(1 << 16));
}
double LMS7002M_tbb_set_filter_bw(LMS7002M_t *self, const LMS7002M_chan_t channel, const double bw)
{
    LMS7002M_set_mac_ch(self, channel);

    int val = 0;
    double actual = bw;
    bool bypass = false;
    const bool hb = bw >= 18.5e6;

    if (bw <= 2.4e6) val = 6, actual = 2.4e6;
    else if (bw <= 2.74e6) val = 19, actual = 2.74e6;
    else if (bw <= 5.5e6) val = 75, actual = 5.5e6;
    else if (bw <= 8.2e6) val = 133, actual = 8.2e6;
    else if (bw <= 11.0e6) val = 193, actual = 11.0e6;
    else if (bw <= 18.5e6) val = 18, actual = 18.5e6;
    else if (bw <= 38.0e6) val = 97, actual = 38.0e6;
    else if (bw <= 54.0e6) val = 163, actual = 54.0e6;
    else bypass = true;

    //only one filter is actually used
    self->regs->reg_0x0109_rcal_lpfh_tbb = val;
    self->regs->reg_0x0109_rcal_lpflad_tbb = val;
    LMS7002M_regs_spi_write(self, 0x0109);

    //set path
    if (bypass) LMS7002M_tbb_set_path(self, channel, LMS7002M_TBB_BYP);
    else if (hb) LMS7002M_tbb_set_path(self, channel, LMS7002M_TBB_HBF);
    else LMS7002M_tbb_set_path(self, channel, LMS7002M_TBB_LBF);

    return actual;
}
void LMS7002M_tbb_set_path(LMS7002M_t *self, const LMS7002M_chan_t channel, const int path)
{
    LMS7002M_set_mac_ch(self, channel);

    self->regs->reg_0x0105_pd_lpfh_tbb = 1;
    self->regs->reg_0x0105_pd_lpflad_tbb = 1;
    self->regs->reg_0x0105_pd_lpfs5_tbb = 1;
    self->regs->reg_0x010a_bypladder_tbb = 1;

    switch (path)
    {
    case LMS7002M_TBB_BYP:
        break;

    case LMS7002M_TBB_LBF:
        self->regs->reg_0x010a_bypladder_tbb = 0;
        self->regs->reg_0x0105_pd_lpflad_tbb = 0;
        self->regs->reg_0x0105_pd_lpfs5_tbb = 0;
        break;

    case LMS7002M_TBB_HBF:
        self->regs->reg_0x0105_pd_lpfh_tbb = 0;
        break;
    }

    LMS7002M_regs_spi_write(self, 0x0105);
    LMS7002M_regs_spi_write(self, 0x010A);
}
Beispiel #6
0
LMS7002M_API void LMS7002M_trf_select_band(LMS7002M_t *self, const LMS7002M_chan_t channel, const int band)
{
    LMS7002M_set_mac_ch(self, channel);
    self->regs.reg_0x0103_sel_band1_trf = (band == 1)?1:0;
    self->regs.reg_0x0103_sel_band2_trf = (band == 2)?1:0;
    LMS7002M_regs_spi_write(self, 0x0103);
}
LMS7002M_API void LMS7002M_txtsp_tsg_const(LMS7002M_t *self, const LMS7002M_chan_t channel, const int valI, const int valQ)
{
    LMS7002M_set_mac_ch(self, channel);

    //muxes
    self->regs.reg_0x0200_tsgfc = REG_0X0200_TSGFC_FS;
    self->regs.reg_0x0200_tsgmode = REG_0X0200_TSGMODE_DC;
    self->regs.reg_0x0200_insel = REG_0X0200_INSEL_TEST;
    LMS7002M_regs_spi_write(self, 0x0200);

    //load I
    self->regs.reg_0x020c_dc_reg = valI;
    LMS7002M_regs_spi_write(self, 0x020c);
    self->regs.reg_0x0200_tsgdcldi = 0;
    LMS7002M_regs_spi_write(self, 0x0200);
    self->regs.reg_0x0200_tsgdcldi = 1;
    LMS7002M_regs_spi_write(self, 0x0200);
    self->regs.reg_0x0200_tsgdcldi = 0;
    LMS7002M_regs_spi_write(self, 0x0200);

    //load Q
    self->regs.reg_0x020c_dc_reg = valQ;
    LMS7002M_regs_spi_write(self, 0x020c);
    self->regs.reg_0x0200_tsgdcldq = 0;
    LMS7002M_regs_spi_write(self, 0x0200);
    self->regs.reg_0x0200_tsgdcldq = 1;
    LMS7002M_regs_spi_write(self, 0x0200);
    self->regs.reg_0x0200_tsgdcldq = 0;
    LMS7002M_regs_spi_write(self, 0x0200);
}
void LMS7002M_tbb_enable_loopback(LMS7002M_t *self, const LMS7002M_chan_t channel, const int mode, const bool swap)
{
    LMS7002M_set_mac_ch(self, channel);

    self->regs->reg_0x0105_loopb_tbb = mode;
    self->regs->reg_0x0105_loopb_tbb |= swap?0:(1 << 2);

    LMS7002M_regs_spi_write(self, 0x0105);
}
Beispiel #9
0
LMS7002M_API void LMS7002M_trf_enable(LMS7002M_t *self, const LMS7002M_chan_t channel, const bool enable)
{
    LMS7002M_set_mac_ch(self, channel);
    self->regs.reg_0x0124_en_dir_trf = 1;
    LMS7002M_regs_spi_write(self, 0x0124);

    self->regs.reg_0x0100_en_g_trf = enable?1:0;
    self->regs.reg_0x0100_pd_tlobuf_trf = 0;
    self->regs.reg_0x0100_pd_txpad_trf = 0;
    LMS7002M_regs_spi_write(self, 0x0100);
}
void LMS7002M_tbb_enable(LMS7002M_t *self, const LMS7002M_chan_t channel, const bool enable)
{
    LMS7002M_set_mac_ch(self, channel);
    self->regs->reg_0x0124_en_dir_tbb = 1;
    LMS7002M_regs_spi_write(self, 0x0124);

    self->regs->reg_0x0105_en_g_tbb = enable?1:0;
    LMS7002M_tbb_set_test_in(self, channel, LMS7002M_TBB_TSTIN_OFF);
    LMS7002M_tbb_enable_loopback(self, channel, LMS7002M_TBB_LB_DISCONNECTED, false);

    LMS7002M_regs_spi_write(self, 0x0105);
}
Beispiel #11
0
LMS7002M_API void LMS7002M_txtsp_tsg_tone(LMS7002M_t *self, const LMS7002M_chan_t channel)
{
    LMS7002M_set_mac_ch(self, channel);

    //muxes
    self->regs.reg_0x0200_tsgmode = REG_0X0200_TSGMODE_NCO;
    self->regs.reg_0x0200_insel = REG_0X0200_INSEL_TEST;
    LMS7002M_regs_spi_write(self, 0x0200);

    self->regs.reg_0x0200_tsgfcw = REG_0X0200_TSGFCW_DIV8;
    LMS7002M_regs_spi_write(self, 0x0200);
}
void LMS7002M_rxtsp_set_decim(LMS7002M_t *self, const LMS7002M_chan_t channel, const size_t decim)
{
    LMS7002M_set_mac_ch(self, channel);

    if (decim == 1) self->regs->reg_0x0403_hbd_ovr = REG_0X0403_HBD_OVR_BYPASS;
    if (decim == 2) self->regs->reg_0x0403_hbd_ovr = 0;
    if (decim == 4) self->regs->reg_0x0403_hbd_ovr = 1;
    if (decim == 8) self->regs->reg_0x0403_hbd_ovr = 2;
    if (decim == 16) self->regs->reg_0x0403_hbd_ovr = 3;
    if (decim == 32) self->regs->reg_0x0403_hbd_ovr = 4;

    LMS7002M_regs_spi_write(self, 0x0403);
}
Beispiel #13
0
LMS7002M_API void LMS7002M_txtsp_set_interp(LMS7002M_t *self, const LMS7002M_chan_t channel, const size_t interp)
{
    LMS7002M_set_mac_ch(self, channel);

    if (interp == 1) self->regs.reg_0x0203_hbi_ovr = REG_0X0203_HBI_OVR_BYPASS;
    if (interp == 2) self->regs.reg_0x0203_hbi_ovr = 0;
    if (interp == 4) self->regs.reg_0x0203_hbi_ovr = 1;
    if (interp == 8) self->regs.reg_0x0203_hbi_ovr = 2;
    if (interp == 16) self->regs.reg_0x0203_hbi_ovr = 3;
    if (interp == 32) self->regs.reg_0x0203_hbi_ovr = 4;

    LMS7002M_regs_spi_write(self, 0x0203);
}
void LMS7002M_rxtsp_set_dc_correction(
    LMS7002M_t *self,
    const LMS7002M_chan_t channel,
    const bool enabled,
    const int window)
{
    LMS7002M_set_mac_ch(self, channel);

    self->regs->reg_0x040c_dc_byp = (enabled)?0:1;
    LMS7002M_regs_spi_write(self, 0x040c);

    self->regs->reg_0x0404_dccorr_avg = window;
    LMS7002M_regs_spi_write(self, 0x0404);
}
double LMS7002M_tbb_set_iamp(LMS7002M_t *self, const LMS7002M_chan_t channel, const double gain)
{
    LMS7002M_set_mac_ch(self, channel);
    int value = (int)(gain + 0.5);
    if (value < 0) value = 0;
    if (value > 63) value = 63;

    //ensure enabled iamp
    self->regs->reg_0x0105_pd_lpfiamp_tbb = 0;
    LMS7002M_regs_spi_write(self, 0x0105);

    //TODO this may need calibration

    self->regs->reg_0x0108_cg_iamp_tbb = value;

    return gain;
}
void LMS7002M_rxtsp_set_iq_correction(
    LMS7002M_t *self,
    const LMS7002M_chan_t channel,
    const double phase,
    const double gain)
{
    LMS7002M_set_mac_ch(self, channel);

    const bool bypassPhase = (phase == 0.0);
    const bool bypassGain = (gain == 0.0);
    self->regs->reg_0x040c_ph_byp = bypassPhase?1:0;
    self->regs->reg_0x040c_gc_byp = bypassGain?1:0;
    LMS7002M_regs_spi_write(self, 0x040c);

    //TODO
    //self->regs->reg_0x0403_iqcorr = 
    //self->regs->reg_0x0402_gcorri = 
    //self->regs->reg_0x0401_gcorrq = 
}
void LMS7002M_ldo_enable(LMS7002M_t *self, const bool enable, const int group)
{
    //LDO is a global register space
    LMS7002M_set_mac_ch(self, LMS_CHAB);

    //TODO, we can implement more groups, for now only 1
    if (group != LMS7002M_LDO_ALL) return;

    const int val = enable?1:0;

    self->regs->reg_0x0092_en_ldo_dig= val;
    self->regs->reg_0x0092_en_ldo_diggn= val;
    self->regs->reg_0x0092_en_ldo_digsxr= val;
    self->regs->reg_0x0092_en_ldo_digsxt= val;
    self->regs->reg_0x0092_en_ldo_divgn= val;
    self->regs->reg_0x0092_en_ldo_divsxr= val;
    self->regs->reg_0x0092_en_ldo_divsxt= val;
    self->regs->reg_0x0092_en_ldo_lna12= val;
    self->regs->reg_0x0092_en_ldo_lna14= val;
    self->regs->reg_0x0092_en_ldo_mxrfe= val;
    self->regs->reg_0x0092_en_ldo_rbb= val;
    self->regs->reg_0x0092_en_ldo_rxbuf= val;
    self->regs->reg_0x0092_en_ldo_tbb= val;
    self->regs->reg_0x0092_en_ldo_tia12= val;
    self->regs->reg_0x0092_en_ldo_tia14= val;
    self->regs->reg_0x0092_en_g_ldo= val;

    self->regs->reg_0x0093_en_ldo_afe= val;
    self->regs->reg_0x0093_en_ldo_cpgn= val;
    self->regs->reg_0x0093_en_ldo_cpsxr= val;
    self->regs->reg_0x0093_en_ldo_tlob= val;
    self->regs->reg_0x0093_en_ldo_tpad= val;
    self->regs->reg_0x0093_en_ldo_txbuf= val;
    self->regs->reg_0x0093_en_ldo_vcogn= val;
    self->regs->reg_0x0093_en_ldo_vcosxr= val;
    self->regs->reg_0x0093_en_ldo_vcosxt= val;
    self->regs->reg_0x0093_en_ldo_cpsxt= val;
    LMS7002M_regs_spi_write(self, 0x0092);
    LMS7002M_regs_spi_write(self, 0x0093);
}
Beispiel #18
0
LMS7002M_API void LMS7002M_txtsp_enable(LMS7002M_t *self, const LMS7002M_chan_t channel, const bool enable)
{
    LMS7002M_set_mac_ch(self, channel);

    self->regs.reg_0x0200_en = enable?1:0;
    self->regs.reg_0x0200_bstart = 0;
    self->regs.reg_0x0200_insel = REG_0X0200_INSEL_LML;
    LMS7002M_regs_spi_write(self, 0x0200);

    self->regs.reg_0x0203_hbi_ovr = REG_0X0203_HBI_OVR_BYPASS;

    self->regs.reg_0x0208_cmix_byp = 1;
    self->regs.reg_0x0208_isinc_byp = 1;
    self->regs.reg_0x0208_gfir3_byp = 1;
    self->regs.reg_0x0208_gfir2_byp = 1;
    self->regs.reg_0x0208_gfir1_byp = 1;
    self->regs.reg_0x0208_dc_byp = 1;
    self->regs.reg_0x0208_gc_byp = 1;
    self->regs.reg_0x0208_ph_byp = 1;

    LMS7002M_regs_spi_write(self, 0x0200);
    LMS7002M_regs_spi_write(self, 0x0203);
    LMS7002M_regs_spi_write(self, 0x0208);
}
int LMS7002M_set_gfir_taps(
    LMS7002M_t *self,
    const LMS7002M_dir_t direction,
    const LMS7002M_chan_t channel,
    const int which,
    const short *taps,
    const size_t ntaps)
{
    LMS7002M_set_mac_ch(self, channel);

    //bypass the filter for null/empty filter taps
    const bool bypass = (taps == NULL) || (ntaps == 0);
    if (direction == LMS_RX)
    {
        if (which == 1) self->regs->reg_0x040c_gfir1_byp = bypass?1:0;
        if (which == 2) self->regs->reg_0x040c_gfir2_byp = bypass?1:0;
        if (which == 3) self->regs->reg_0x040c_gfir3_byp = bypass?1:0;
        LMS7002M_regs_spi_write(self, 0x040c);
    }
    if (direction == LMS_TX)
    {
        if (which == 1) self->regs->reg_0x0208_gfir1_byp = bypass?1:0;
        if (which == 2) self->regs->reg_0x0208_gfir2_byp = bypass?1:0;
        if (which == 3) self->regs->reg_0x0208_gfir3_byp = bypass?1:0;
        LMS7002M_regs_spi_write(self, 0x0208);
    }

    //bypass programed, skip writing taps
    if (bypass) return 0;

    //bounds check
    if (which < 1) return -1;
    if (which > 3) return -1;
    if (which == 1 && ntaps != 5*8) return -2;
    if (which == 2 && ntaps != 5*8) return -2;
    if (which == 3 && ntaps != 3*5*8) return -2;

    //taps configuration logic from LMS7002_MainControl::LoadGFIRCoefficients
    if (which == 1)
    {
        int addr = (direction == LMS_RX)?0x0480:0x0280;
        for (int k=0; k<5; ++k)
        {
            for (int i=0; i<8; ++i)
            {
                LMS7002M_spi_write(self, addr, taps[k*8+i]);
                ++addr;
            }
        }
    }
    else if (which == 2)
    {
        int addr = (direction == LMS_RX)?0x04c0:0x02c0;
        for (int k=0; k<5; ++k)
        {
            for (int i=0; i<8; ++i)
            {
                LMS7002M_spi_write(self, addr, taps[k*8+i]);
                ++addr;
            }
        }
    }
    else if (which == 3)
    {
        int addr = (direction == LMS_RX)?0x0500:0x0300;
        int coefIndex = 0;
        unsigned short coefValue = 0;
        for (int n=0; n<3; ++n)
        {
            for (int k=0; k<5; ++k)
            {
                for (int i=0; i<8; ++i)
                {
                    coefValue = taps[coefIndex];
                    ++coefIndex;

                    LMS7002M_spi_write(self, addr, coefValue);
                    ++addr;
                }
            }
            addr += 24; //skip reserved
        }
    }

    return 0; //OK
}
Beispiel #20
0
LMS7002M_API void LMS7002M_trf_enable_loopback(LMS7002M_t *self, const LMS7002M_chan_t channel, const bool enable)
{
    LMS7002M_set_mac_ch(self, channel);
    self->regs.reg_0x0101_en_loopb_txpad_trf = enable?0:1;
    LMS7002M_regs_spi_write(self, 0x0101);
}
int LMS7002M_set_data_clock(LMS7002M_t *self, const double fref, const double fout, double *factual)
{
    LMS7_logf(LMS7_INFO, "CGEN tune %f MHz begin", fout/1e6);

    //always use the channel A shadow, CGEN is in global register space
    LMS7002M_set_mac_ch(self, LMS_CHA);

    //The equations:
    // fref * N = fvco
    // fvco / fdiv = fout
    // fref * N = fout * fdiv
    int fdiv = 512+2;
    double Ndiv = 0;
    double fvco = 0;

    //calculation loop to find dividers that are possible
    while (true)
    {
        //try the next even divider
        fdiv -= 2;

        Ndiv = fout*fdiv/fref;
        fvco = fout*fdiv;
        LMS7_logf(LMS7_TRACE, "Trying: fdiv = %d, Ndiv = %f, fvco = %f MHz", fdiv, Ndiv, fvco/1e6);

        //check dividers and vco in range...
        if (fdiv < 2) return -1;
        if (fdiv > 512) continue;
        if (Ndiv < 4) return -1;
        if (Ndiv > 512) continue;

        //check vco boundaries
        if (fvco < LMS7002M_CGEN_VCO_LO) continue;
        if (fvco > LMS7002M_CGEN_VCO_HI) continue;

        break; //its good
    }
    LMS7_logf(LMS7_DEBUG, "Using: fdiv = %d, Ndiv = %f, fvco = %f MHz", fdiv, Ndiv, fvco/1e6);

    //stash the freq now that we know the loop above passed
    self->cgen_freq = fout;
    self->cgen_fref = fref;

    //reset
    self->regs->reg_0x0086_reset_n_cgen = 0;
    LMS7002M_regs_spi_write(self, 0x0086);
    self->regs->reg_0x0086_reset_n_cgen = 1;
    LMS7002M_regs_spi_write(self, 0x0086);

    //configure and enable synthesizer
    self->regs->reg_0x0086_en_intonly_sdm_cgen = 0; //support frac-N
    self->regs->reg_0x0086_en_sdm_clk_cgen = 1; //enable
    self->regs->reg_0x0086_pd_cp_cgen = 0; //enable
    self->regs->reg_0x0086_pd_fdiv_fb_cgen = 0; //enable
    self->regs->reg_0x0086_pd_fdiv_o_cgen = 0; //enable
    self->regs->reg_0x0086_pd_sdm_cgen = 0; //enable
    self->regs->reg_0x0086_pd_vco_cgen = 0; //enable
    self->regs->reg_0x0086_pd_vco_comp_cgen = 0; //enable
    self->regs->reg_0x0086_en_g_cgen = 1;
    self->regs->reg_0x0086_en_coarse_cklgen = 0;
    self->regs->reg_0x008b_coarse_start_cgen = 0;
    self->regs->reg_0x0086_spdup_vco_cgen = 1; //fast settling
    LMS7002M_regs_spi_write(self, 0x0086);

    //program the N divider
    const int Nint = (int)Ndiv;
    const int Nfrac = (int)((Ndiv-Nint)*(1 << 20));
    self->regs->reg_0x0087_frac_sdm_cgen = (Nfrac) & 0xffff; //lower 16 bits
    self->regs->reg_0x0088_frac_sdm_cgen = (Nfrac) >> 16; //upper 4 bits
    self->regs->reg_0x0088_int_sdm_cgen = Nint-1;
    LMS7_logf(LMS7_DEBUG, "fdiv = %d, Ndiv = %f, Nint = %d, Nfrac = %d, fvco = %f MHz", fdiv, Ndiv, Nint, Nfrac, fvco/1e6);
    LMS7002M_regs_spi_write(self, 0x0087);
    LMS7002M_regs_spi_write(self, 0x0088);

    //program the feedback divider
    self->regs->reg_0x0089_sel_sdmclk_cgen = REG_0X0089_SEL_SDMCLK_CGEN_CLK_DIV;
    self->regs->reg_0x0089_div_outch_cgen = (fdiv/2)-1;
    LMS7002M_regs_spi_write(self, 0x0089);

    //select the correct CSW for this VCO frequency
    int csw_lowest = -1;
    self->regs->reg_0x008b_csw_vco_cgen = 0;
    for (int i = 7; i >= 0; i--)
    {
        self->regs->reg_0x008b_csw_vco_cgen |= 1 << i;
        LMS7002M_regs_spi_write(self, 0x008B);
        LMS7_sleep_for(cgen_cmp_sleep_ticks());
        LMS7002M_regs_spi_read(self, 0x008C);

        LMS7_logf(LMS7_DEBUG, "i=%d, hi=%d, lo=%d", i, self->regs->reg_0x008c_vco_cmpho_cgen, self->regs->reg_0x008c_vco_cmplo_cgen);
        if (self->regs->reg_0x008c_vco_cmplo_cgen != 0)
        {
            self->regs->reg_0x008b_csw_vco_cgen &= ~(1 << i); //clear bit i
        }
        if (self->regs->reg_0x008c_vco_cmpho_cgen != 0 && self->regs->reg_0x008c_vco_cmplo_cgen == 0 && csw_lowest < 0)
        {
            csw_lowest = self->regs->reg_0x008b_csw_vco_cgen;
        }
        LMS7002M_regs_spi_write(self, 0x008B);
    }

    //find the midpoint for the high and low bounds
    if (csw_lowest >= 0)
    {
        int csw_highest = self->regs->reg_0x008b_csw_vco_cgen;
        if (csw_lowest == csw_highest)
        {
            while (csw_lowest >= 0)
            {
                self->regs->reg_0x008b_csw_vco_cgen = csw_lowest;
                LMS7002M_regs_spi_write(self, 0x008B);
                LMS7_sleep_for(cgen_cmp_sleep_ticks());
                LMS7002M_regs_spi_read(self, 0x008C);

                if (self->regs->reg_0x008c_vco_cmpho_cgen == 0 && self->regs->reg_0x008c_vco_cmplo_cgen == 0) break;
                else csw_lowest--;
            }
            if (csw_lowest < 0) csw_lowest = 0;
        }
        csw_lowest += 1;

        LMS7_logf(LMS7_INFO, "lowest CSW_VCO %i, highest CSW_VCO %i", csw_lowest, csw_highest);
        self->regs->reg_0x008b_csw_vco_cgen = (csw_highest+csw_lowest)/2;
        LMS7002M_regs_spi_write(self, 0x008B);
    }

    //check that the vco selection was successful
    LMS7_sleep_for(cgen_cmp_sleep_ticks());
    LMS7002M_regs_spi_read(self, 0x008C);
    if (self->regs->reg_0x008c_vco_cmpho_cgen != 0 && self->regs->reg_0x008c_vco_cmplo_cgen == 0)
    {
        LMS7_log(LMS7_INFO, "CGEN VCO OK");
    }
    else
    {
        LMS7_log(LMS7_ERROR, "CGEN VCO select FAIL");
        return -3;
    }

    self->regs->reg_0x0086_spdup_vco_cgen = 0; //done with fast settling
    LMS7002M_regs_spi_write(self, 0x0086);

    //calculate the actual rate
    if (factual != NULL) *factual = fref * (Nint + (Nfrac/((double)(1 << 20)))) / fdiv;

    return 0; //OK
}
void LMS7002M_tbb_set_test_in(LMS7002M_t *self, const LMS7002M_chan_t channel, const int path)
{
    LMS7002M_set_mac_ch(self, channel);
    self->regs->reg_0x010a_tstin_tbb = path;
    LMS7002M_regs_spi_write(self, 0x010a);
}