aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/dboard/zbx/zbx_lo_ctrl.cpp
blob: 4fe2a243d4a297f69ba600de98ea97e8f1998d01 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//
// Copyright 2020 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: GPL-3.0-or-later
//

#include <uhd/exception.hpp>
#include <uhd/utils/log.hpp>
#include <uhdlib/usrp/dboard/zbx/zbx_lo_ctrl.hpp>
#include <thread>

namespace uhd { namespace usrp { namespace zbx {

zbx_lo_ctrl::zbx_lo_ctrl(zbx_lo_t lo,
    lmx2572_iface::write_fn_t&& poke16,
    lmx2572_iface::read_fn_t&& peek16,
    lmx2572_iface::sleep_fn_t&& sleep,
    const double default_frequency,
    const double db_prc_rate,
    const bool testing_mode_enabled)
    : _log_id(ZBX_LO_LOG_ID.at(lo))
    , _freq(default_frequency)
    , _db_prc_rate(db_prc_rate)
    , _testing_mode_enabled(testing_mode_enabled)
{
    _lmx = lmx2572_iface::make(std::move(poke16), std::move(peek16), std::move(sleep));
    UHD_ASSERT_THROW(_lmx);
    UHD_LOG_TRACE(_log_id, "LO initialized...");
    _lmx->reset();

    set_lo_port_enabled(true);
    // In ZBX, we always run the LOs in sync mode. It is theoretically possible
    // to not do so, but we gain nothing by doing that.
    _lmx->set_sync_mode(true);
    set_lo_freq(LMX2572_DEFAULT_FREQ);
    wait_for_lo_lock();
}

double zbx_lo_ctrl::set_lo_freq(const double freq)
{
    UHD_ASSERT_THROW(_lmx);
    UHD_LOG_TRACE(_log_id, "Setting LO frequency " << freq / 1e6 << " MHz");

    _freq = _lmx->set_frequency(freq,
        _db_prc_rate,
        false /*TODO: get_spur_dodging()*/);
    _lmx->commit();
    return _freq;
}

double zbx_lo_ctrl::get_lo_freq()
{
    return _freq;
}

void zbx_lo_ctrl::wait_for_lo_lock()
{
    UHD_LOG_TRACE(_log_id, "Waiting for LO lock,  " << ZBX_LO_LOCK_TIMEOUT_MS << " ms");
    const auto timeout = std::chrono::steady_clock::now()
                         + std::chrono::milliseconds(ZBX_LO_LOCK_TIMEOUT_MS);
    while (std::chrono::steady_clock::now() < timeout && !get_lock_status()) {
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }
    if (!get_lock_status()) {
        // If we can't lock our LO, this could be a lot of possible issues
        throw uhd::runtime_error(_log_id + " has failed to lock!");
    }
}

bool zbx_lo_ctrl::get_lock_status()
{
    return _lmx->get_lock_status();
}

void zbx_lo_ctrl::set_lo_port_enabled(bool enable)
{
    UHD_LOG_TRACE(_log_id,
        "Enabling LO " << (_testing_mode_enabled ? "test" : "output") << " port");

    // We want to set the output port regardless of test mode being enabled
    _lmx->set_output_enable(_get_output_port(false), enable);

    if (_testing_mode_enabled && enable) {
        // If testing mode is enabled, also set the test port
        _lmx->set_output_enable(_get_output_port(true), true);
    } else {
        // If testing mode is disabled, test port should be disabled
        _lmx->set_output_enable(_get_output_port(true), false);
        _lmx->set_mux_input(
            _get_output_port(true), lmx2572_iface::mux_in_t::HIGH_IMPEDANCE);
    }

    _lmx->set_enabled(enable);
    _lmx->commit();
}

bool zbx_lo_ctrl::get_lo_port_enabled()
{
    return _lmx->get_enabled();
}

void zbx_lo_ctrl::set_lo_test_mode_enabled(bool enable)
{
    _testing_mode_enabled = enable;
    set_lo_port_enabled(get_lo_port_enabled());
}

bool zbx_lo_ctrl::get_lo_test_mode_enabled()
{
    return _testing_mode_enabled;
}

zbx_lo_t zbx_lo_ctrl::lo_string_to_enum(
    const uhd::direction_t trx, const size_t channel, const std::string name)
{
    if (trx == TX_DIRECTION) {
        if (channel == 0) {
            if (name == ZBX_LO1) {
                return zbx_lo_t::TX0_LO1;
            } else if (name == ZBX_LO2) {
                return zbx_lo_t::TX0_LO2;
            }
        } else if (channel == 1) {
            if (name == ZBX_LO1) {
                return zbx_lo_t::TX1_LO1;
            } else if (name == ZBX_LO2) {
                return zbx_lo_t::TX1_LO2;
            }
        }
    } else {
        if (channel == 0) {
            if (name == ZBX_LO1) {
                return zbx_lo_t::RX0_LO1;
            } else if (name == ZBX_LO2) {
                return zbx_lo_t::RX0_LO2;
            }
        } else if (channel == 1) {
            if (name == ZBX_LO1) {
                return zbx_lo_t::RX1_LO1;
            } else if (name == ZBX_LO2) {
                return zbx_lo_t::RX1_LO2;
            }
        }
    }
    UHD_THROW_INVALID_CODE_PATH();
}

lmx2572_iface::output_t zbx_lo_ctrl::_get_output_port(bool testing_mode)
{
    // Note: The LO output ports here are dependent to the LO and zbx hardware
    // configuration, in no particular order (zbx radio configuration output vs.
    // test port output)
    if (!testing_mode) {
        // Rev B has all LO outputs on Port A
        return lmx2572_iface::output_t::RF_OUTPUT_A;
    } else {
        return lmx2572_iface::output_t::RF_OUTPUT_B;
    }
}

}}} // namespace uhd::usrp::zbx