blob: 1a9a7e11ae50607252f0f087ca34407aae31a538 [file] [log] [blame]
dburgessb3a0ca42011-10-12 07:44:40 +00001/*
2* Copyright 2008, 2009 Free Software Foundation, Inc.
3*
Pau Espin Pedrol21d03d32019-07-22 12:05:52 +02004* SPDX-License-Identifier: AGPL-3.0+
5*
dburgessb3a0ca42011-10-12 07:44:40 +00006* This software is distributed under the terms of the GNU Affero Public License.
7* See the COPYING file in the main directory for details.
8*
9* This use of this software may be subject to additional restrictions.
10* See the LEGAL file in the main directory for details.
11
12 This program is free software: you can redistribute it and/or modify
13 it under the terms of the GNU Affero General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU Affero General Public License for more details.
21
22 You should have received a copy of the GNU Affero General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
24
25*/
26
27
28/*
29 Compilation Flags
30
31 SWLOOPBACK compile for software loopback testing
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +010032*/
dburgessb3a0ca42011-10-12 07:44:40 +000033
34
35#include <stdint.h>
36#include <string.h>
37#include <stdlib.h>
Alexander Huemer6fafd332018-01-12 15:04:02 +010038#include "Logger.h"
dburgessb3a0ca42011-10-12 07:44:40 +000039#include "Threads.h"
40#include "USRPDevice.h"
41
kurtis.heimerl3758b162011-11-26 03:19:22 +000042#ifdef HAVE_CONFIG_H
43#include "config.h"
44#endif
dburgessb3a0ca42011-10-12 07:44:40 +000045
46using namespace std;
47
kurtis.heimerl79e71c92011-11-26 03:16:48 +000048enum dboardConfigType {
49 TXA_RXB,
50 TXB_RXA,
51 TXA_RXA,
52 TXB_RXB
53};
dburgessb3a0ca42011-10-12 07:44:40 +000054
kurtis.heimerl3758b162011-11-26 03:19:22 +000055#ifdef SINGLEDB
56const dboardConfigType dboardConfig = TXA_RXA;
57#else
kurtis.heimerl79e71c92011-11-26 03:16:48 +000058const dboardConfigType dboardConfig = TXA_RXB;
kurtis.heimerl3758b162011-11-26 03:19:22 +000059#endif
60
kurtis.heimerl79e71c92011-11-26 03:16:48 +000061const double USRPDevice::masterClockRate = 52.0e6;
dburgessb3a0ca42011-10-12 07:44:40 +000062
Harald Welte61707e82018-06-13 23:21:57 +020063USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
Pau Espin Pedrolb0e54262020-01-13 16:00:04 +010064 size_t chan_num, double lo_offset,
Harald Welte61707e82018-06-13 23:21:57 +020065 const std::vector<std::string>& tx_paths,
66 const std::vector<std::string>& rx_paths):
Pau Espin Pedrolb0e54262020-01-13 16:00:04 +010067 RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths)
dburgessb3a0ca42011-10-12 07:44:40 +000068{
Harald Welte5cc88582018-08-17 19:55:38 +020069 LOGC(DDEV, INFO) << "creating USRP device...";
Thomas Tsouc1f7c422013-10-11 13:49:55 -040070
Harald Weltece70ba52018-06-13 22:47:48 +020071 decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) tx_sps));
dburgessb3a0ca42011-10-12 07:44:40 +000072 actualSampleRate = masterClockRate/decimRate;
73 rxGain = 0;
Pau Espin Pedrol331c88a2019-09-13 16:53:31 +020074 txGain = 0;
dburgessb3a0ca42011-10-12 07:44:40 +000075
Thomas Tsouc1f7c422013-10-11 13:49:55 -040076 /*
77 * Undetermined delay b/w ping response timestamp and true
78 * receive timestamp. Values are empirically measured. With
79 * split sample rate Tx/Rx - 4/1 sps we need to need to
80 * compensate for advance rather than delay.
81 */
Harald Weltece70ba52018-06-13 22:47:48 +020082 if (tx_sps == 1)
Thomas Tsouc1f7c422013-10-11 13:49:55 -040083 pingOffset = 272;
Harald Weltece70ba52018-06-13 22:47:48 +020084 else if (tx_sps == 4)
Thomas Tsouc1f7c422013-10-11 13:49:55 -040085 pingOffset = 269 - 7500;
86 else
87 pingOffset = 0;
88
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +010089#ifdef SWLOOPBACK
dburgessb3a0ca42011-10-12 07:44:40 +000090 samplePeriod = 1.0e6/actualSampleRate;
91 loopbackBufferSize = 0;
92 gettimeofday(&lastReadTime,NULL);
93 firstRead = false;
94#endif
95}
96
Tom Tsou2f3e60b2016-07-17 19:29:08 -070097int USRPDevice::open(const std::string &, int, bool)
dburgessb3a0ca42011-10-12 07:44:40 +000098{
dburgessb3a0ca42011-10-12 07:44:40 +000099 writeLock.unlock();
100
Harald Welte5cc88582018-08-17 19:55:38 +0200101 LOGC(DDEV, INFO) << "opening USRP device..";
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100102#ifndef SWLOOPBACK
dburgessb3a0ca42011-10-12 07:44:40 +0000103 string rbf = "std_inband.rbf";
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100104 //string rbf = "inband_1rxhb_1tx.rbf";
dburgessb3a0ca42011-10-12 07:44:40 +0000105 m_uRx.reset();
dburgessb3a0ca42011-10-12 07:44:40 +0000106 try {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400107 m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make(
Harald Weltece70ba52018-06-13 22:47:48 +0200108 0, decimRate * tx_sps, 1, -1,
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400109 usrp_standard_rx::FPGA_MODE_NORMAL,
110 1024, 16 * 8, rbf));
dburgessb3a0ca42011-10-12 07:44:40 +0000111 m_uRx->set_fpga_master_clock_freq(masterClockRate);
dburgessb3a0ca42011-10-12 07:44:40 +0000112 }
Thomas Tsouc0641242013-10-11 14:55:31 -0400113
dburgessb3a0ca42011-10-12 07:44:40 +0000114 catch(...) {
Harald Welte5cc88582018-08-17 19:55:38 +0200115 LOGC(DDEV, ALERT) << "make failed on Rx";
dburgessb3a0ca42011-10-12 07:44:40 +0000116 m_uRx.reset();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400117 return -1;
dburgessb3a0ca42011-10-12 07:44:40 +0000118 }
119
120 if (m_uRx->fpga_master_clock_freq() != masterClockRate)
121 {
Harald Welte5cc88582018-08-17 19:55:38 +0200122 LOGC(DDEV, ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq()
dburgessb3a0ca42011-10-12 07:44:40 +0000123 << ", desired clock freq = " << masterClockRate;
124 m_uRx.reset();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400125 return -1;
dburgessb3a0ca42011-10-12 07:44:40 +0000126 }
dburgessb3a0ca42011-10-12 07:44:40 +0000127
128 try {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400129 m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make(
130 0, decimRate * 2, 1, -1,
131 1024, 16 * 8, rbf));
dburgessb3a0ca42011-10-12 07:44:40 +0000132 m_uTx->set_fpga_master_clock_freq(masterClockRate);
dburgessb3a0ca42011-10-12 07:44:40 +0000133 }
Thomas Tsouc0641242013-10-11 14:55:31 -0400134
dburgessb3a0ca42011-10-12 07:44:40 +0000135 catch(...) {
Harald Welte5cc88582018-08-17 19:55:38 +0200136 LOGC(DDEV, ALERT) << "make failed on Tx";
dburgessb3a0ca42011-10-12 07:44:40 +0000137 m_uTx.reset();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400138 return -1;
dburgessb3a0ca42011-10-12 07:44:40 +0000139 }
140
141 if (m_uTx->fpga_master_clock_freq() != masterClockRate)
142 {
Harald Welte5cc88582018-08-17 19:55:38 +0200143 LOGC(DDEV, ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq()
dburgessb3a0ca42011-10-12 07:44:40 +0000144 << ", desired clock freq = " << masterClockRate;
145 m_uTx.reset();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400146 return -1;
dburgessb3a0ca42011-10-12 07:44:40 +0000147 }
148
Harald Weltea9440012019-01-11 21:36:34 +0100149 m_uRx->stop();
dburgessb3a0ca42011-10-12 07:44:40 +0000150 m_uTx->stop();
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100151
dburgessb3a0ca42011-10-12 07:44:40 +0000152#endif
153
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000154 switch (dboardConfig) {
155 case TXA_RXB:
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000156 txSubdevSpec = usrp_subdev_spec(0,0);
157 rxSubdevSpec = usrp_subdev_spec(1,0);
158 break;
159 case TXB_RXA:
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000160 txSubdevSpec = usrp_subdev_spec(1,0);
161 rxSubdevSpec = usrp_subdev_spec(0,0);
162 break;
163 case TXA_RXA:
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000164 txSubdevSpec = usrp_subdev_spec(0,0);
165 rxSubdevSpec = usrp_subdev_spec(0,0);
166 break;
167 case TXB_RXB:
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000168 txSubdevSpec = usrp_subdev_spec(1,0);
169 rxSubdevSpec = usrp_subdev_spec(1,0);
170 break;
171 default:
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000172 txSubdevSpec = usrp_subdev_spec(0,0);
173 rxSubdevSpec = usrp_subdev_spec(1,0);
174 }
175
kurtis.heimerlb9e237e2011-11-26 03:17:59 +0000176 m_dbTx = m_uTx->selected_subdev(txSubdevSpec);
177 m_dbRx = m_uRx->selected_subdev(rxSubdevSpec);
178
dburgessb3a0ca42011-10-12 07:44:40 +0000179 started = false;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100180
Thomas Tsoucb69f082013-04-08 14:18:26 -0400181 return NORMAL;
dburgessb3a0ca42011-10-12 07:44:40 +0000182}
183
184
185
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100186bool USRPDevice::start()
dburgessb3a0ca42011-10-12 07:44:40 +0000187{
Harald Welte5cc88582018-08-17 19:55:38 +0200188 LOGC(DDEV, INFO) << "starting USRP...";
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100189#ifndef SWLOOPBACK
Harald Weltea9440012019-01-11 21:36:34 +0100190 if (!m_uRx) return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000191 if (!m_uTx) return false;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100192
Harald Weltea9440012019-01-11 21:36:34 +0100193 m_uRx->stop();
dburgessb3a0ca42011-10-12 07:44:40 +0000194 m_uTx->stop();
195
196 writeLock.lock();
197 // power up and configure daughterboards
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000198 m_dbTx->set_enable(true);
199 m_uTx->set_mux(m_uTx->determine_tx_mux_value(txSubdevSpec));
200 m_uRx->set_mux(m_uRx->determine_rx_mux_value(rxSubdevSpec));
201
202 if (!m_dbRx->select_rx_antenna(1))
203 m_dbRx->select_rx_antenna(0);
204
dburgessb3a0ca42011-10-12 07:44:40 +0000205 writeLock.unlock();
206
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000207 // Set gains to midpoint
208 setTxGain((minTxGain() + maxTxGain()) / 2);
209 setRxGain((minRxGain() + maxRxGain()) / 2);
dburgessb3a0ca42011-10-12 07:44:40 +0000210
211 data = new short[currDataSize];
212 dataStart = 0;
213 dataEnd = 0;
214 timeStart = 0;
215 timeEnd = 0;
216 timestampOffset = 0;
217 latestWriteTimestamp = 0;
218 lastPktTimestamp = 0;
219 hi32Timestamp = 0;
220 isAligned = false;
221
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100222
dburgessb3a0ca42011-10-12 07:44:40 +0000223 started = (m_uRx->start() && m_uTx->start());
dburgessb3a0ca42011-10-12 07:44:40 +0000224 return started;
225#else
226 gettimeofday(&lastReadTime,NULL);
227 return true;
228#endif
229}
230
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100231bool USRPDevice::stop()
dburgessb3a0ca42011-10-12 07:44:40 +0000232{
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100233#ifndef SWLOOPBACK
dburgessb3a0ca42011-10-12 07:44:40 +0000234 if (!m_uRx) return false;
235 if (!m_uTx) return false;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100236
dburgessb3a0ca42011-10-12 07:44:40 +0000237 delete[] currData;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100238
dburgessb3a0ca42011-10-12 07:44:40 +0000239 started = !(m_uRx->stop() && m_uTx->stop());
240 return !started;
241#else
242 return true;
243#endif
244}
245
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000246double USRPDevice::maxTxGain()
247{
248 return m_dbTx->gain_max();
249}
250
251double USRPDevice::minTxGain()
252{
253 return m_dbTx->gain_min();
254}
255
256double USRPDevice::maxRxGain()
257{
258 return m_dbRx->gain_max();
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100259}
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000260
261double USRPDevice::minRxGain()
262{
263 return m_dbRx->gain_min();
264}
265
Thomas Tsou204a9f12013-10-29 18:34:16 -0400266double USRPDevice::setTxGain(double dB, size_t chan)
267{
268 if (chan) {
Harald Welte5cc88582018-08-17 19:55:38 +0200269 LOGC(DDEV, ALERT) << "Invalid channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400270 return 0.0;
271 }
dburgessb3a0ca42011-10-12 07:44:40 +0000272
Thomas Tsou204a9f12013-10-29 18:34:16 -0400273 writeLock.lock();
274 if (dB > maxTxGain())
275 dB = maxTxGain();
276 if (dB < minTxGain())
277 dB = minTxGain();
dburgessb3a0ca42011-10-12 07:44:40 +0000278
Harald Welte5cc88582018-08-17 19:55:38 +0200279 LOGC(DDEV, NOTICE) << "Setting TX gain to " << dB << " dB.";
dburgessb3a0ca42011-10-12 07:44:40 +0000280
Thomas Tsou204a9f12013-10-29 18:34:16 -0400281 if (!m_dbTx->set_gain(dB))
Harald Welte5cc88582018-08-17 19:55:38 +0200282 LOGC(DDEV, ERR) << "Error setting TX gain";
Pau Espin Pedrol331c88a2019-09-13 16:53:31 +0200283 else
284 txGain = dB;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400285 writeLock.unlock();
286
Pau Espin Pedrol331c88a2019-09-13 16:53:31 +0200287 return txGain;
dburgessb3a0ca42011-10-12 07:44:40 +0000288}
289
290
Thomas Tsou204a9f12013-10-29 18:34:16 -0400291double USRPDevice::setRxGain(double dB, size_t chan)
292{
293 if (chan) {
Harald Welte5cc88582018-08-17 19:55:38 +0200294 LOGC(DDEV, ALERT) << "Invalid channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400295 return 0.0;
296 }
dburgessb3a0ca42011-10-12 07:44:40 +0000297
Thomas Tsou204a9f12013-10-29 18:34:16 -0400298 dB = 47.0;
dburgessb3a0ca42011-10-12 07:44:40 +0000299
Thomas Tsou204a9f12013-10-29 18:34:16 -0400300 writeLock.lock();
301 if (dB > maxRxGain())
302 dB = maxRxGain();
303 if (dB < minRxGain())
304 dB = minRxGain();
305
Harald Welte5cc88582018-08-17 19:55:38 +0200306 LOGC(DDEV, NOTICE) << "Setting RX gain to " << dB << " dB.";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400307
308 if (!m_dbRx->set_gain(dB))
Harald Welte5cc88582018-08-17 19:55:38 +0200309 LOGC(DDEV, ERR) << "Error setting RX gain";
Pau Espin Pedrolca0892d2019-09-13 16:36:25 +0200310 else
311 rxGain = dB;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400312 writeLock.unlock();
313
Pau Espin Pedrolca0892d2019-09-13 16:36:25 +0200314 return rxGain;
dburgessb3a0ca42011-10-12 07:44:40 +0000315}
316
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100317bool USRPDevice::setRxAntenna(const std::string &ant, size_t chan)
318{
319 if (chan >= rx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200320 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100321 return false;
322 }
Harald Welte5cc88582018-08-17 19:55:38 +0200323 LOGC(DDEV, ALERT) << "Not implemented";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100324 return true;
325}
326
327std::string USRPDevice::getRxAntenna(size_t chan)
328{
329 if (chan >= rx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200330 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100331 return "";
332 }
Harald Welte5cc88582018-08-17 19:55:38 +0200333 LOGC(DDEV, ALERT) << "Not implemented";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100334 return "";
335}
336
337bool USRPDevice::setTxAntenna(const std::string &ant, size_t chan)
338{
339 if (chan >= tx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200340 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100341 return false;
342 }
Harald Welte5cc88582018-08-17 19:55:38 +0200343 LOGC(DDEV, ALERT) << "Not implemented";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100344 return true;
345}
346
347std::string USRPDevice::getTxAntenna(size_t chan)
348{
349 if (chan >= tx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200350 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100351 return "";
352 }
Harald Welte5cc88582018-08-17 19:55:38 +0200353 LOGC(DDEV, ALERT) << "Not implemented";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100354 return "";
355}
356
Pau Espin Pedrol0fc20d12018-04-24 17:48:52 +0200357bool USRPDevice::requiresRadioAlign()
358{
359 return true;
360}
dburgessb3a0ca42011-10-12 07:44:40 +0000361
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +0200362GSM::Time USRPDevice::minLatency() {
363 return GSM::Time(1,1);
364}
365
dburgessb3a0ca42011-10-12 07:44:40 +0000366// NOTE: Assumes sequential reads
Thomas Tsou204a9f12013-10-29 18:34:16 -0400367int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
Pau Espin Pedrolf8c0c462020-03-12 19:08:46 +0100368 TIMESTAMP timestamp, bool *underrun)
dburgessb3a0ca42011-10-12 07:44:40 +0000369{
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100370#ifndef SWLOOPBACK
Thomas Tsou204a9f12013-10-29 18:34:16 -0400371 if (!m_uRx)
372 return 0;
373
374 short *buf = bufs[0];
375
dburgessb3a0ca42011-10-12 07:44:40 +0000376 timestamp += timestampOffset;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100377
dburgessb3a0ca42011-10-12 07:44:40 +0000378 if (timestamp + len < timeStart) {
379 memset(buf,0,len*2*sizeof(short));
380 return len;
381 }
382
Pau Espin Pedrol207911b2019-07-29 20:25:44 +0200383 *underrun = false;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100384
dburgessb3a0ca42011-10-12 07:44:40 +0000385 uint32_t readBuf[2000];
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100386
dburgessb3a0ca42011-10-12 07:44:40 +0000387 while (1) {
388 //guestimate USB read size
389 int readLen=0;
390 {
391 int numSamplesNeeded = timestamp + len - timeEnd;
392 if (numSamplesNeeded <=0) break;
393 readLen = 512 * ((int) ceil((float) numSamplesNeeded/126.0));
394 if (readLen > 8000) readLen= (8000/512)*512;
395 }
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100396
dburgessb3a0ca42011-10-12 07:44:40 +0000397 // read USRP packets, parse and save A/D data as needed
398 readLen = m_uRx->read((void *)readBuf,readLen,overrun);
Pau Espin Pedrol4d179ab2018-09-10 10:36:04 +0200399 for (int pktNum = 0; pktNum < (readLen/512); pktNum++) {
dburgessb3a0ca42011-10-12 07:44:40 +0000400 // tmpBuf points to start of a USB packet
401 uint32_t* tmpBuf = (uint32_t *) (readBuf+pktNum*512/4);
402 TIMESTAMP pktTimestamp = usrp_to_host_u32(tmpBuf[1]);
403 uint32_t word0 = usrp_to_host_u32(tmpBuf[0]);
404 uint32_t chan = (word0 >> 16) & 0x1f;
405 unsigned payloadSz = word0 & 0x1ff;
Harald Welte5cc88582018-08-17 19:55:38 +0200406 LOGC(DDEV, DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp;
dburgessb3a0ca42011-10-12 07:44:40 +0000407
408 bool incrementHi32 = ((lastPktTimestamp & 0x0ffffffffll) > pktTimestamp);
409 if (incrementHi32 && (timeStart!=0)) {
Harald Welte5cc88582018-08-17 19:55:38 +0200410 LOGC(DDEV, DEBUG) << "high 32 increment!!!";
dburgessb3a0ca42011-10-12 07:44:40 +0000411 hi32Timestamp++;
412 }
413 pktTimestamp = (((TIMESTAMP) hi32Timestamp) << 32) | pktTimestamp;
414 lastPktTimestamp = pktTimestamp;
415
416 if (chan == 0x01f) {
417 // control reply, check to see if its ping reply
418 uint32_t word2 = usrp_to_host_u32(tmpBuf[2]);
419 if ((word2 >> 16) == ((0x01 << 8) | 0x02)) {
420 timestamp -= timestampOffset;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400421 timestampOffset = pktTimestamp - pingTimestamp + pingOffset;
Harald Welte5cc88582018-08-17 19:55:38 +0200422 LOGC(DDEV, DEBUG) << "updating timestamp offset to: " << timestampOffset;
dburgessb3a0ca42011-10-12 07:44:40 +0000423 timestamp += timestampOffset;
424 isAligned = true;
425 }
426 continue;
427 }
428 if (chan != 0) {
Harald Welte5cc88582018-08-17 19:55:38 +0200429 LOGC(DDEV, DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz;
dburgessb3a0ca42011-10-12 07:44:40 +0000430 continue;
431 }
432 if ((word0 >> 28) & 0x04) {
Pau Espin Pedrol207911b2019-07-29 20:25:44 +0200433 *underrun = true;
Harald Welte5cc88582018-08-17 19:55:38 +0200434 LOGC(DDEV, DEBUG) << "UNDERRUN in TRX->USRP interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000435 }
Pau Espin Pedrolf8c0c462020-03-12 19:08:46 +0100436#if 0
437 /* FIXME: Do something with this ? */
438 unsigned RSSI = (word0 >> 21) & 0x3f;
439#endif
dburgessb3a0ca42011-10-12 07:44:40 +0000440 if (!isAligned) continue;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100441
dburgessb3a0ca42011-10-12 07:44:40 +0000442 unsigned cursorStart = pktTimestamp - timeStart + dataStart;
443 while (cursorStart*2 > currDataSize) {
444 cursorStart -= currDataSize/2;
445 }
446 if (cursorStart*2 + payloadSz/2 > currDataSize) {
447 // need to circle around buffer
448 memcpy(data+cursorStart*2,tmpBuf+2,(currDataSize-cursorStart*2)*sizeof(short));
449 memcpy(data,tmpBuf+2+(currDataSize/2-cursorStart),payloadSz-(currDataSize-cursorStart*2)*sizeof(short));
450 }
451 else {
452 memcpy(data+cursorStart*2,tmpBuf+2,payloadSz);
453 }
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100454 if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd)
dburgessb3a0ca42011-10-12 07:44:40 +0000455 timeEnd = pktTimestamp+payloadSz/2/sizeof(short);
456
Harald Welte5cc88582018-08-17 19:55:38 +0200457 LOGC(DDEV, DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp;
dburgessb3a0ca42011-10-12 07:44:40 +0000458
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100459 }
460 }
461
dburgessb3a0ca42011-10-12 07:44:40 +0000462 // copy desired data to buf
463 unsigned bufStart = dataStart+(timestamp-timeStart);
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100464 if (bufStart + len < currDataSize/2) {
Harald Welte5cc88582018-08-17 19:55:38 +0200465 LOGC(DDEV, DEBUG) << "bufStart: " << bufStart;
dburgessb3a0ca42011-10-12 07:44:40 +0000466 memcpy(buf,data+bufStart*2,len*2*sizeof(short));
467 memset(data+bufStart*2,0,len*2*sizeof(short));
468 }
469 else {
Harald Welte5cc88582018-08-17 19:55:38 +0200470 LOGC(DDEV, DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart;
dburgessb3a0ca42011-10-12 07:44:40 +0000471 unsigned firstLength = (currDataSize/2-bufStart);
Harald Welte5cc88582018-08-17 19:55:38 +0200472 LOGC(DDEV, DEBUG) << "firstLength: " << firstLength;
dburgessb3a0ca42011-10-12 07:44:40 +0000473 memcpy(buf,data+bufStart*2,firstLength*2*sizeof(short));
474 memset(data+bufStart*2,0,firstLength*2*sizeof(short));
475 memcpy(buf+firstLength*2,data,(len-firstLength)*2*sizeof(short));
476 memset(data,0,(len-firstLength)*2*sizeof(short));
477 }
478 dataStart = (bufStart + len) % (currDataSize/2);
479 timeStart = timestamp + len;
480
dburgessb3a0ca42011-10-12 07:44:40 +0000481 return len;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100482
dburgessb3a0ca42011-10-12 07:44:40 +0000483#else
484 if (loopbackBufferSize < 2) return 0;
485 int numSamples = 0;
486 struct timeval currTime;
487 gettimeofday(&currTime,NULL);
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100488 double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 +
dburgessb3a0ca42011-10-12 07:44:40 +0000489 (currTime.tv_usec - lastReadTime.tv_usec);
490 if (timeElapsed < samplePeriod) {return 0;}
491 int numSamplesToRead = (int) floor(timeElapsed/samplePeriod);
492 if (numSamplesToRead < len) return 0;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100493
dburgessb3a0ca42011-10-12 07:44:40 +0000494 if (numSamplesToRead > len) numSamplesToRead = len;
495 if (numSamplesToRead > loopbackBufferSize/2) {
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100496 firstRead =false;
dburgessb3a0ca42011-10-12 07:44:40 +0000497 numSamplesToRead = loopbackBufferSize/2;
498 }
499 memcpy(buf,loopbackBuffer,sizeof(short)*2*numSamplesToRead);
500 loopbackBufferSize -= 2*numSamplesToRead;
501 memcpy(loopbackBuffer,loopbackBuffer+2*numSamplesToRead,
502 sizeof(short)*loopbackBufferSize);
503 numSamples = numSamplesToRead;
504 if (firstRead) {
505 int new_usec = lastReadTime.tv_usec + (int) round((double) numSamplesToRead * samplePeriod);
506 lastReadTime.tv_sec = lastReadTime.tv_sec + new_usec/1000000;
507 lastReadTime.tv_usec = new_usec % 1000000;
508 }
509 else {
510 gettimeofday(&lastReadTime,NULL);
511 firstRead = true;
512 }
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100513
dburgessb3a0ca42011-10-12 07:44:40 +0000514 return numSamples;
515#endif
516}
517
Pau Espin Pedroldfc6e5f2020-03-12 19:35:33 +0100518int USRPDevice::writeSamplesControl(std::vector<short *> &bufs, int len,
519 bool *underrun, unsigned long long timestamp, bool isControl)
dburgessb3a0ca42011-10-12 07:44:40 +0000520{
521 writeLock.lock();
522
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100523#ifndef SWLOOPBACK
Thomas Tsou204a9f12013-10-29 18:34:16 -0400524 if (!m_uTx)
525 return 0;
526
527 short *buf = bufs[0];
528
kurtis.heimerlc8739b82011-11-02 00:06:34 +0000529 static uint32_t outData[128*20];
Thomas Tsou204a9f12013-10-29 18:34:16 -0400530
dburgessb3a0ca42011-10-12 07:44:40 +0000531 for (int i = 0; i < len*2; i++) {
532 buf[i] = host_to_usrp_short(buf[i]);
533 }
534
535 int numWritten = 0;
536 unsigned isStart = 1;
537 unsigned RSSI = 0;
538 unsigned CHAN = (isControl) ? 0x01f : 0x00;
539 len = len*2*sizeof(short);
540 int numPkts = (int) ceil((float)len/(float)504);
541 unsigned isEnd = (numPkts < 2);
542 uint32_t *outPkt = outData;
543 int pktNum = 0;
544 while (numWritten < len) {
545 // pkt is pointer to start of a USB packet
546 uint32_t *pkt = outPkt + pktNum*128;
547 isEnd = (len - numWritten <= 504);
548 unsigned payloadLen = ((len - numWritten) < 504) ? (len-numWritten) : 504;
549 pkt[0] = (isStart << 12 | isEnd << 11 | (RSSI & 0x3f) << 5 | CHAN) << 16 | payloadLen;
550 pkt[1] = timestamp & 0x0ffffffffll;
551 memcpy(pkt+2,buf+(numWritten/sizeof(short)),payloadLen);
552 numWritten += payloadLen;
553 timestamp += payloadLen/2/sizeof(short);
554 isStart = 0;
555 pkt[0] = host_to_usrp_u32(pkt[0]);
556 pkt[1] = host_to_usrp_u32(pkt[1]);
557 pktNum++;
558 }
559 m_uTx->write((const void*) outPkt,sizeof(uint32_t)*128*numPkts,NULL);
560
dburgessb3a0ca42011-10-12 07:44:40 +0000561 writeLock.unlock();
562
563 return len/2/sizeof(short);
564#else
565 int retVal = len;
566 memcpy(loopbackBuffer+loopbackBufferSize,buf,sizeof(short)*2*len);
dburgessb3a0ca42011-10-12 07:44:40 +0000567 loopbackBufferSize += retVal*2;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100568
dburgessb3a0ca42011-10-12 07:44:40 +0000569 return retVal;
570#endif
571}
572
Pau Espin Pedroldfc6e5f2020-03-12 19:35:33 +0100573int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
574 bool *underrun, unsigned long long timestamp)
575{
576 return writeSamplesControl(bufs, len, underrun, timestamp, false);
577}
578
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100579bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
dburgessb3a0ca42011-10-12 07:44:40 +0000580{
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100581#ifndef SWLOOPBACK
dburgessb3a0ca42011-10-12 07:44:40 +0000582 short data[] = {0x00,0x02,0x00,0x00};
583 uint32_t *wordPtr = (uint32_t *) data;
kurtis.heimerlc8739b82011-11-02 00:06:34 +0000584 *wordPtr = host_to_usrp_u32(*wordPtr);
dburgessb3a0ca42011-10-12 07:44:40 +0000585 bool tmpUnderrun;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400586
587 std::vector<short *> buf(1, data);
Pau Espin Pedroldfc6e5f2020-03-12 19:35:33 +0100588 if (writeSamplesControl(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000589 pingTimestamp = timestamp;
590 return true;
591 }
592 return false;
593#else
594 return true;
595#endif
596}
597
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100598#ifndef SWLOOPBACK
Thomas Tsou204a9f12013-10-29 18:34:16 -0400599bool USRPDevice::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000600{
601 usrp_tune_result result;
dburgessb3a0ca42011-10-12 07:44:40 +0000602
Thomas Tsou204a9f12013-10-29 18:34:16 -0400603 if (chan) {
Harald Welte5cc88582018-08-17 19:55:38 +0200604 LOGC(DDEV, ALERT) << "Invalid channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400605 return false;
606 }
607
kurtis.heimerlb9e237e2011-11-26 03:17:59 +0000608 if (m_uTx->tune(txSubdevSpec.side, m_dbTx, wFreq, &result)) {
Harald Welte5cc88582018-08-17 19:55:38 +0200609 LOGC(DDEV, INFO) << "set TX: " << wFreq << std::endl
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000610 << " baseband freq: " << result.baseband_freq << std::endl
611 << " DDC freq: " << result.dxc_freq << std::endl
612 << " residual freq: " << result.residual_freq;
613 return true;
614 }
615 else {
Harald Weltef97d75b2019-01-11 22:41:58 +0100616 LOGC(DDEV, ALERT) << "set TX: " << wFreq << " failed" << std::endl
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000617 << " baseband freq: " << result.baseband_freq << std::endl
618 << " DDC freq: " << result.dxc_freq << std::endl
619 << " residual freq: " << result.residual_freq;
620 return false;
621 }
622}
623
Thomas Tsou204a9f12013-10-29 18:34:16 -0400624bool USRPDevice::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000625{
626 usrp_tune_result result;
627
Thomas Tsou204a9f12013-10-29 18:34:16 -0400628 if (chan) {
Harald Welte5cc88582018-08-17 19:55:38 +0200629 LOGC(DDEV, ALERT) << "Invalid channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400630 return false;
631 }
632
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000633 if (m_uRx->tune(0, m_dbRx, wFreq, &result)) {
Harald Welte5cc88582018-08-17 19:55:38 +0200634 LOGC(DDEV, INFO) << "set RX: " << wFreq << std::endl
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000635 << " baseband freq: " << result.baseband_freq << std::endl
636 << " DDC freq: " << result.dxc_freq << std::endl
637 << " residual freq: " << result.residual_freq;
638 return true;
639 }
640 else {
Harald Weltef97d75b2019-01-11 22:41:58 +0100641 LOGC(DDEV, ALERT) << "set RX: " << wFreq << " failed" << std::endl
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000642 << " baseband freq: " << result.baseband_freq << std::endl
643 << " DDC freq: " << result.dxc_freq << std::endl
644 << " residual freq: " << result.residual_freq;
645 return false;
646 }
647
648}
dburgessb3a0ca42011-10-12 07:44:40 +0000649
650#else
651bool USRPDevice::setTxFreq(double wFreq) { return true;};
652bool USRPDevice::setRxFreq(double wFreq) { return true;};
653#endif
kurtis.heimerl965e7572011-11-26 03:16:54 +0000654
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800655RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
Harald Welte61707e82018-06-13 23:21:57 +0200656 InterfaceType iface, size_t chans, double lo_offset,
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100657 const std::vector<std::string>& tx_paths,
658 const std::vector<std::string>& rx_paths)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000659{
Harald Welteb2294392018-06-13 23:42:19 +0200660 if (tx_sps != rx_sps) {
Harald Welte5cc88582018-08-17 19:55:38 +0200661 LOGC(DDEV, ERROR) << "USRP1 requires tx_sps == rx_sps";
Harald Welteb2294392018-06-13 23:42:19 +0200662 return NULL;
663 }
664 if (chans != 1) {
Harald Welte5cc88582018-08-17 19:55:38 +0200665 LOGC(DDEV, ERROR) << "USRP1 supports only 1 channel";
Harald Welteb2294392018-06-13 23:42:19 +0200666 return NULL;
667 }
668 if (lo_offset != 0.0) {
Harald Welte5cc88582018-08-17 19:55:38 +0200669 LOGC(DDEV, ERROR) << "USRP1 doesn't support lo_offset";
Harald Welteb2294392018-06-13 23:42:19 +0200670 return NULL;
671 }
Harald Welte61707e82018-06-13 23:21:57 +0200672 return new USRPDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000673}