blob: 151c0021f1561d3633e12fd5c52fa1ed91a7f212 [file] [log] [blame]
Ericb7253c62022-11-28 19:21:08 +01001#pragma once
2/*
3 * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
4 * All Rights Reserved
5 *
6 * Author: Eric Wild <ewild@sysmocom.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23#include <uhd/version.hpp>
24#include <uhd/usrp/multi_usrp.hpp>
25#include <uhd/types/metadata.hpp>
26#include <complex>
27#include <cstring>
28#include <iostream>
29#include <thread>
30
31#include <Timeval.h>
32#include <vector>
33
34using blade_sample_type = std::complex<int16_t>;
35const int SAMPLE_SCALE_FACTOR = 1;
36
37struct uhd_buf_wrap {
38 double rxticks;
39 size_t num_samps;
40 uhd::rx_metadata_t *md;
41 blade_sample_type *buf;
42 auto actual_samples_per_buffer()
43 {
44 return num_samps;
45 }
46 long get_first_ts()
47 {
48 return md->time_spec.to_ticks(rxticks);
49 }
50 int readall(blade_sample_type *outaddr)
51 {
52 memcpy(outaddr, buf, num_samps * sizeof(blade_sample_type));
53 return num_samps;
54 }
55 int read_n(blade_sample_type *outaddr, int start, int num)
56 {
57 assert(start >= 0);
58 auto to_read = std::min((int)num_samps - start, num);
59 assert(to_read >= 0);
60 memcpy(outaddr, buf + start, to_read * sizeof(blade_sample_type));
61 return to_read;
62 }
63};
64
65using dev_buf_t = uhd_buf_wrap;
66using bh_fn_t = std::function<int(dev_buf_t *)>;
67
Eric2e6c3622023-05-02 14:34:49 +020068template <typename T>
69struct uhd_hw {
Ericb7253c62022-11-28 19:21:08 +010070 uhd::usrp::multi_usrp::sptr dev;
71 uhd::rx_streamer::sptr rx_stream;
72 uhd::tx_streamer::sptr tx_stream;
73 blade_sample_type *one_pkt_buf;
74 std::vector<blade_sample_type *> pkt_ptrs;
75 size_t rx_spp;
76 double rxticks;
77 const unsigned int rxFullScale, txFullScale;
78 const int rxtxdelay;
79 float rxgain, txgain;
Eric40978042023-05-23 10:59:58 +020080 static std::atomic<bool> stop_lower_threads_flag;
Ericc0f0a612023-05-02 15:34:19 +020081 double rxfreq_cache, txfreq_cache;
Ericb7253c62022-11-28 19:21:08 +010082
83 virtual ~uhd_hw()
84 {
85 delete[] one_pkt_buf;
86 }
Ericc0f0a612023-05-02 15:34:19 +020087 uhd_hw() : rxFullScale(32767), txFullScale(32767 * 0.3), rxtxdelay(-67), rxfreq_cache(0), txfreq_cache(0)
Ericb7253c62022-11-28 19:21:08 +010088 {
89 }
90
91 void close_device()
92 {
93 }
94
95 bool tuneTx(double freq, size_t chan = 0)
96 {
Ericc0f0a612023-05-02 15:34:19 +020097 if (txfreq_cache == freq)
98 return true;
Ericb7253c62022-11-28 19:21:08 +010099 msleep(25);
100 dev->set_tx_freq(freq, chan);
Ericc0f0a612023-05-02 15:34:19 +0200101 txfreq_cache = freq;
Ericb7253c62022-11-28 19:21:08 +0100102 msleep(25);
103 return true;
104 };
105 bool tuneRx(double freq, size_t chan = 0)
106 {
Ericc0f0a612023-05-02 15:34:19 +0200107 if (rxfreq_cache == freq)
108 return true;
Ericb7253c62022-11-28 19:21:08 +0100109 msleep(25);
110 dev->set_rx_freq(freq, chan);
Ericc0f0a612023-05-02 15:34:19 +0200111 rxfreq_cache = freq;
Ericb7253c62022-11-28 19:21:08 +0100112 msleep(25);
113 return true;
114 };
115 bool tuneRxOffset(double offset, size_t chan = 0)
116 {
117 return true;
118 };
119
120 double setRxGain(double dB, size_t chan = 0)
121 {
122 rxgain = dB;
123 msleep(25);
124 dev->set_rx_gain(dB, chan);
125 msleep(25);
126 return dB;
127 };
128 double setTxGain(double dB, size_t chan = 0)
129 {
130 txgain = dB;
131 msleep(25);
132 dev->set_tx_gain(dB, chan);
133 msleep(25);
134 return dB;
135 };
136 int setPowerAttenuation(int atten, size_t chan = 0)
137 {
138 return atten;
139 };
140
141 int init_device(bh_fn_t rxh, bh_fn_t txh)
142 {
143 auto const lock_delay_ms = 500;
Eric Wild10b4e312023-01-11 18:53:48 +0100144 auto clock_lock_attempts = 15; // x lock_delay_ms
Ericb7253c62022-11-28 19:21:08 +0100145 auto const mcr = 26e6;
146 auto const rate = (1625e3 / 6) * 4;
147 auto const ref = "external";
148 auto const gain = 35;
149 auto const freq = 931.4e6; // 936.8e6
150 auto bw = 0.5e6;
151 auto const channel = 0;
152 // aligned to blade: 1020 samples per transfer
153 std::string args = { "recv_frame_size=4092,send_frame_size=4092" };
154
155 dev = uhd::usrp::multi_usrp::make(args);
156 std::cout << "Using Device: " << dev->get_pp_string() << std::endl;
157 dev->set_clock_source(ref);
158 dev->set_master_clock_rate(mcr);
159 dev->set_rx_rate(rate, channel);
160 dev->set_tx_rate(rate, channel);
161 uhd::tune_request_t tune_request(freq, 0);
162 dev->set_rx_freq(tune_request, channel);
163 dev->set_rx_gain(gain, channel);
164 dev->set_tx_gain(60, channel);
165 dev->set_rx_bandwidth(bw, channel);
166 dev->set_tx_bandwidth(bw, channel);
167
168 while (!(dev->get_rx_sensor("lo_locked", channel).to_bool() &&
Eric Wild10b4e312023-01-11 18:53:48 +0100169 dev->get_mboard_sensor("ref_locked").to_bool()) &&
170 clock_lock_attempts > 0) {
171 std::cerr << "clock source lock attempts remaining: " << clock_lock_attempts << ".."
172 << std::endl;
Ericb7253c62022-11-28 19:21:08 +0100173 std::this_thread::sleep_for(std::chrono::milliseconds(lock_delay_ms));
Eric Wild10b4e312023-01-11 18:53:48 +0100174 clock_lock_attempts--;
175 }
176
177 if (clock_lock_attempts <= 0) {
178 std::cerr << "Error locking clock, gpsdo missing? quitting.." << std::endl;
179 return -1;
180 }
Ericb7253c62022-11-28 19:21:08 +0100181
182 uhd::stream_args_t stream_args("sc16", "sc16");
183 rx_stream = dev->get_rx_stream(stream_args);
184 uhd::stream_args_t stream_args2("sc16", "sc16");
185 tx_stream = dev->get_tx_stream(stream_args2);
186
187 rx_spp = rx_stream->get_max_num_samps();
188 rxticks = dev->get_rx_rate();
189 assert(rxticks == dev->get_tx_rate());
190 one_pkt_buf = new blade_sample_type[rx_spp];
191 pkt_ptrs = { 1, &one_pkt_buf[0] };
192 return 0;
193 }
194
Ericb3157b92023-05-23 11:32:34 +0200195 void actually_enable_streams()
196 {
197 // nop: stream cmd in handler
198 }
199
Ericb7253c62022-11-28 19:21:08 +0100200 void *rx_cb(bh_fn_t burst_handler)
201 {
202 void *ret = nullptr;
203 static int to_skip = 0;
204
205 uhd::rx_metadata_t md;
206 auto num_rx_samps = rx_stream->recv(pkt_ptrs.front(), rx_spp, md, 1.0, true);
207
208 if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) {
209 std::cerr << boost::format("Timeout while streaming") << std::endl;
210 exit(0);
211 }
212 if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW) {
213 std::cerr << boost::format("Got an overflow indication\n") << std::endl;
214 exit(0);
215 }
216 if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE) {
217 std::cerr << str(boost::format("Receiver error: %s") % md.strerror());
218 exit(0);
219 }
220
221 dev_buf_t rcd = { rxticks, num_rx_samps, &md, &one_pkt_buf[0] };
222
223 if (to_skip < 120) // prevents weird overflows on startup
224 to_skip++;
225 else {
226 burst_handler(&rcd);
227 }
228
229 return ret;
230 }
231
232 auto get_rx_burst_handler_fn(bh_fn_t burst_handler)
233 {
Eric989fe752023-10-06 16:06:13 +0200234 // C cb -> ghetto closure capture, which is fine, the args never change.
235 static auto rx_burst_cap_this = this;
236 static auto rx_burst_cap_bh = burst_handler;
237 auto fn = [](void *args) -> void * {
Ericb7253c62022-11-28 19:21:08 +0100238 pthread_setname_np(pthread_self(), "rxrun");
239
240 uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
241 stream_cmd.stream_now = true;
242 stream_cmd.time_spec = uhd::time_spec_t();
Eric989fe752023-10-06 16:06:13 +0200243 rx_burst_cap_this->rx_stream->issue_stream_cmd(stream_cmd);
Ericb7253c62022-11-28 19:21:08 +0100244
Eric989fe752023-10-06 16:06:13 +0200245 while (!rx_burst_cap_this->stop_lower_threads_flag) {
246 rx_burst_cap_this->rx_cb(rx_burst_cap_bh);
Ericb7253c62022-11-28 19:21:08 +0100247 }
Eric989fe752023-10-06 16:06:13 +0200248 return 0;
Ericb7253c62022-11-28 19:21:08 +0100249 };
250 return fn;
251 }
252 auto get_tx_burst_handler_fn(bh_fn_t burst_handler)
253 {
Eric989fe752023-10-06 16:06:13 +0200254 auto fn = [](void *args) -> void * {
Ericb7253c62022-11-28 19:21:08 +0100255 // dummy
Eric989fe752023-10-06 16:06:13 +0200256 return 0;
Ericb7253c62022-11-28 19:21:08 +0100257 };
258 return fn;
259 }
260 void submit_burst_ts(blade_sample_type *buffer, int len, uint64_t ts)
261 {
262 uhd::tx_metadata_t m = {};
263 m.end_of_burst = true;
264 m.start_of_burst = true;
265 m.has_time_spec = true;
266 m.time_spec = m.time_spec.from_ticks(ts + rxtxdelay, rxticks); // uhd specific b210 delay!
267 std::vector<void *> ptrs(1, buffer);
268
269 tx_stream->send(ptrs, len, m, 1.0);
270#ifdef DBGXX
271 uhd::async_metadata_t async_md;
272 bool tx_ack = false;
273 while (!tx_ack && tx_stream->recv_async_msg(async_md)) {
274 tx_ack = (async_md.event_code == uhd::async_metadata_t::EVENT_CODE_BURST_ACK);
275 }
276 std::cout << (tx_ack ? "yay" : "nay") << " " << async_md.time_spec.to_ticks(rxticks) << std::endl;
277#endif
278 }
279};