blob: b1d6c56ed49b88115cddc9c12608daf891314a1e [file] [log] [blame]
dburgessb3a0ca42011-10-12 07:44:40 +00001/*
2* Copyright 2008, 2009 Free Software Foundation, Inc.
3*
4* This software is distributed under the terms of the GNU Affero Public License.
5* See the COPYING file in the main directory for details.
6*
7* This use of this software may be subject to additional restrictions.
8* See the LEGAL file in the main directory for details.
9
10 This program is free software: you can redistribute it and/or modify
11 it under the terms of the GNU Affero General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Affero General Public License for more details.
19
20 You should have received a copy of the GNU Affero General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
22
23*/
24
25
26/*
27 Compilation Flags
28
29 SWLOOPBACK compile for software loopback testing
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +010030*/
dburgessb3a0ca42011-10-12 07:44:40 +000031
32
33#include <stdint.h>
34#include <string.h>
35#include <stdlib.h>
Alexander Huemer6fafd332018-01-12 15:04:02 +010036#include "Logger.h"
dburgessb3a0ca42011-10-12 07:44:40 +000037#include "Threads.h"
38#include "USRPDevice.h"
39
kurtis.heimerl3758b162011-11-26 03:19:22 +000040#ifdef HAVE_CONFIG_H
41#include "config.h"
42#endif
dburgessb3a0ca42011-10-12 07:44:40 +000043
44using namespace std;
45
kurtis.heimerl79e71c92011-11-26 03:16:48 +000046enum dboardConfigType {
47 TXA_RXB,
48 TXB_RXA,
49 TXA_RXA,
50 TXB_RXB
51};
dburgessb3a0ca42011-10-12 07:44:40 +000052
kurtis.heimerl3758b162011-11-26 03:19:22 +000053#ifdef SINGLEDB
54const dboardConfigType dboardConfig = TXA_RXA;
55#else
kurtis.heimerl79e71c92011-11-26 03:16:48 +000056const dboardConfigType dboardConfig = TXA_RXB;
kurtis.heimerl3758b162011-11-26 03:19:22 +000057#endif
58
kurtis.heimerl79e71c92011-11-26 03:16:48 +000059const double USRPDevice::masterClockRate = 52.0e6;
dburgessb3a0ca42011-10-12 07:44:40 +000060
Harald Welte61707e82018-06-13 23:21:57 +020061USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
62 size_t chans, double lo_offset,
63 const std::vector<std::string>& tx_paths,
64 const std::vector<std::string>& rx_paths):
65 RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths)
dburgessb3a0ca42011-10-12 07:44:40 +000066{
Harald Welte5cc88582018-08-17 19:55:38 +020067 LOGC(DDEV, INFO) << "creating USRP device...";
Thomas Tsouc1f7c422013-10-11 13:49:55 -040068
Harald Weltece70ba52018-06-13 22:47:48 +020069 decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) tx_sps));
dburgessb3a0ca42011-10-12 07:44:40 +000070 actualSampleRate = masterClockRate/decimRate;
71 rxGain = 0;
72
Thomas Tsouc1f7c422013-10-11 13:49:55 -040073 /*
74 * Undetermined delay b/w ping response timestamp and true
75 * receive timestamp. Values are empirically measured. With
76 * split sample rate Tx/Rx - 4/1 sps we need to need to
77 * compensate for advance rather than delay.
78 */
Harald Weltece70ba52018-06-13 22:47:48 +020079 if (tx_sps == 1)
Thomas Tsouc1f7c422013-10-11 13:49:55 -040080 pingOffset = 272;
Harald Weltece70ba52018-06-13 22:47:48 +020081 else if (tx_sps == 4)
Thomas Tsouc1f7c422013-10-11 13:49:55 -040082 pingOffset = 269 - 7500;
83 else
84 pingOffset = 0;
85
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +010086#ifdef SWLOOPBACK
dburgessb3a0ca42011-10-12 07:44:40 +000087 samplePeriod = 1.0e6/actualSampleRate;
88 loopbackBufferSize = 0;
89 gettimeofday(&lastReadTime,NULL);
90 firstRead = false;
91#endif
92}
93
Tom Tsou2f3e60b2016-07-17 19:29:08 -070094int USRPDevice::open(const std::string &, int, bool)
dburgessb3a0ca42011-10-12 07:44:40 +000095{
dburgessb3a0ca42011-10-12 07:44:40 +000096 writeLock.unlock();
97
Harald Welte5cc88582018-08-17 19:55:38 +020098 LOGC(DDEV, INFO) << "opening USRP device..";
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +010099#ifndef SWLOOPBACK
dburgessb3a0ca42011-10-12 07:44:40 +0000100 string rbf = "std_inband.rbf";
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100101 //string rbf = "inband_1rxhb_1tx.rbf";
dburgessb3a0ca42011-10-12 07:44:40 +0000102 m_uRx.reset();
dburgessb3a0ca42011-10-12 07:44:40 +0000103 try {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400104 m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make(
Harald Weltece70ba52018-06-13 22:47:48 +0200105 0, decimRate * tx_sps, 1, -1,
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400106 usrp_standard_rx::FPGA_MODE_NORMAL,
107 1024, 16 * 8, rbf));
dburgessb3a0ca42011-10-12 07:44:40 +0000108 m_uRx->set_fpga_master_clock_freq(masterClockRate);
dburgessb3a0ca42011-10-12 07:44:40 +0000109 }
Thomas Tsouc0641242013-10-11 14:55:31 -0400110
dburgessb3a0ca42011-10-12 07:44:40 +0000111 catch(...) {
Harald Welte5cc88582018-08-17 19:55:38 +0200112 LOGC(DDEV, ALERT) << "make failed on Rx";
dburgessb3a0ca42011-10-12 07:44:40 +0000113 m_uRx.reset();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400114 return -1;
dburgessb3a0ca42011-10-12 07:44:40 +0000115 }
116
117 if (m_uRx->fpga_master_clock_freq() != masterClockRate)
118 {
Harald Welte5cc88582018-08-17 19:55:38 +0200119 LOGC(DDEV, ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq()
dburgessb3a0ca42011-10-12 07:44:40 +0000120 << ", desired clock freq = " << masterClockRate;
121 m_uRx.reset();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400122 return -1;
dburgessb3a0ca42011-10-12 07:44:40 +0000123 }
dburgessb3a0ca42011-10-12 07:44:40 +0000124
125 try {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400126 m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make(
127 0, decimRate * 2, 1, -1,
128 1024, 16 * 8, rbf));
dburgessb3a0ca42011-10-12 07:44:40 +0000129 m_uTx->set_fpga_master_clock_freq(masterClockRate);
dburgessb3a0ca42011-10-12 07:44:40 +0000130 }
Thomas Tsouc0641242013-10-11 14:55:31 -0400131
dburgessb3a0ca42011-10-12 07:44:40 +0000132 catch(...) {
Harald Welte5cc88582018-08-17 19:55:38 +0200133 LOGC(DDEV, ALERT) << "make failed on Tx";
dburgessb3a0ca42011-10-12 07:44:40 +0000134 m_uTx.reset();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400135 return -1;
dburgessb3a0ca42011-10-12 07:44:40 +0000136 }
137
138 if (m_uTx->fpga_master_clock_freq() != masterClockRate)
139 {
Harald Welte5cc88582018-08-17 19:55:38 +0200140 LOGC(DDEV, ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq()
dburgessb3a0ca42011-10-12 07:44:40 +0000141 << ", desired clock freq = " << masterClockRate;
142 m_uTx.reset();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400143 return -1;
dburgessb3a0ca42011-10-12 07:44:40 +0000144 }
145
Harald Weltea9440012019-01-11 21:36:34 +0100146 m_uRx->stop();
dburgessb3a0ca42011-10-12 07:44:40 +0000147 m_uTx->stop();
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100148
dburgessb3a0ca42011-10-12 07:44:40 +0000149#endif
150
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000151 switch (dboardConfig) {
152 case TXA_RXB:
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000153 txSubdevSpec = usrp_subdev_spec(0,0);
154 rxSubdevSpec = usrp_subdev_spec(1,0);
155 break;
156 case TXB_RXA:
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000157 txSubdevSpec = usrp_subdev_spec(1,0);
158 rxSubdevSpec = usrp_subdev_spec(0,0);
159 break;
160 case TXA_RXA:
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000161 txSubdevSpec = usrp_subdev_spec(0,0);
162 rxSubdevSpec = usrp_subdev_spec(0,0);
163 break;
164 case TXB_RXB:
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000165 txSubdevSpec = usrp_subdev_spec(1,0);
166 rxSubdevSpec = usrp_subdev_spec(1,0);
167 break;
168 default:
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000169 txSubdevSpec = usrp_subdev_spec(0,0);
170 rxSubdevSpec = usrp_subdev_spec(1,0);
171 }
172
kurtis.heimerlb9e237e2011-11-26 03:17:59 +0000173 m_dbTx = m_uTx->selected_subdev(txSubdevSpec);
174 m_dbRx = m_uRx->selected_subdev(rxSubdevSpec);
175
dburgessb3a0ca42011-10-12 07:44:40 +0000176 started = false;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100177
Thomas Tsoucb69f082013-04-08 14:18:26 -0400178 return NORMAL;
dburgessb3a0ca42011-10-12 07:44:40 +0000179}
180
181
182
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100183bool USRPDevice::start()
dburgessb3a0ca42011-10-12 07:44:40 +0000184{
Harald Welte5cc88582018-08-17 19:55:38 +0200185 LOGC(DDEV, INFO) << "starting USRP...";
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100186#ifndef SWLOOPBACK
Harald Weltea9440012019-01-11 21:36:34 +0100187 if (!m_uRx) return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000188 if (!m_uTx) return false;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100189
Harald Weltea9440012019-01-11 21:36:34 +0100190 m_uRx->stop();
dburgessb3a0ca42011-10-12 07:44:40 +0000191 m_uTx->stop();
192
193 writeLock.lock();
194 // power up and configure daughterboards
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000195 m_dbTx->set_enable(true);
196 m_uTx->set_mux(m_uTx->determine_tx_mux_value(txSubdevSpec));
197 m_uRx->set_mux(m_uRx->determine_rx_mux_value(rxSubdevSpec));
198
199 if (!m_dbRx->select_rx_antenna(1))
200 m_dbRx->select_rx_antenna(0);
201
dburgessb3a0ca42011-10-12 07:44:40 +0000202 writeLock.unlock();
203
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000204 // Set gains to midpoint
205 setTxGain((minTxGain() + maxTxGain()) / 2);
206 setRxGain((minRxGain() + maxRxGain()) / 2);
dburgessb3a0ca42011-10-12 07:44:40 +0000207
208 data = new short[currDataSize];
209 dataStart = 0;
210 dataEnd = 0;
211 timeStart = 0;
212 timeEnd = 0;
213 timestampOffset = 0;
214 latestWriteTimestamp = 0;
215 lastPktTimestamp = 0;
216 hi32Timestamp = 0;
217 isAligned = false;
218
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100219
dburgessb3a0ca42011-10-12 07:44:40 +0000220 started = (m_uRx->start() && m_uTx->start());
dburgessb3a0ca42011-10-12 07:44:40 +0000221 return started;
222#else
223 gettimeofday(&lastReadTime,NULL);
224 return true;
225#endif
226}
227
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100228bool USRPDevice::stop()
dburgessb3a0ca42011-10-12 07:44:40 +0000229{
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100230#ifndef SWLOOPBACK
dburgessb3a0ca42011-10-12 07:44:40 +0000231 if (!m_uRx) return false;
232 if (!m_uTx) return false;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100233
dburgessb3a0ca42011-10-12 07:44:40 +0000234 delete[] currData;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100235
dburgessb3a0ca42011-10-12 07:44:40 +0000236 started = !(m_uRx->stop() && m_uTx->stop());
237 return !started;
238#else
239 return true;
240#endif
241}
242
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000243double USRPDevice::maxTxGain()
244{
245 return m_dbTx->gain_max();
246}
247
248double USRPDevice::minTxGain()
249{
250 return m_dbTx->gain_min();
251}
252
253double USRPDevice::maxRxGain()
254{
255 return m_dbRx->gain_max();
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100256}
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000257
258double USRPDevice::minRxGain()
259{
260 return m_dbRx->gain_min();
261}
262
Thomas Tsou204a9f12013-10-29 18:34:16 -0400263double USRPDevice::setTxGain(double dB, size_t chan)
264{
265 if (chan) {
Harald Welte5cc88582018-08-17 19:55:38 +0200266 LOGC(DDEV, ALERT) << "Invalid channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400267 return 0.0;
268 }
dburgessb3a0ca42011-10-12 07:44:40 +0000269
Thomas Tsou204a9f12013-10-29 18:34:16 -0400270 writeLock.lock();
271 if (dB > maxTxGain())
272 dB = maxTxGain();
273 if (dB < minTxGain())
274 dB = minTxGain();
dburgessb3a0ca42011-10-12 07:44:40 +0000275
Harald Welte5cc88582018-08-17 19:55:38 +0200276 LOGC(DDEV, NOTICE) << "Setting TX gain to " << dB << " dB.";
dburgessb3a0ca42011-10-12 07:44:40 +0000277
Thomas Tsou204a9f12013-10-29 18:34:16 -0400278 if (!m_dbTx->set_gain(dB))
Harald Welte5cc88582018-08-17 19:55:38 +0200279 LOGC(DDEV, ERR) << "Error setting TX gain";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400280
281 writeLock.unlock();
282
283 return dB;
dburgessb3a0ca42011-10-12 07:44:40 +0000284}
285
286
Thomas Tsou204a9f12013-10-29 18:34:16 -0400287double USRPDevice::setRxGain(double dB, size_t chan)
288{
289 if (chan) {
Harald Welte5cc88582018-08-17 19:55:38 +0200290 LOGC(DDEV, ALERT) << "Invalid channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400291 return 0.0;
292 }
dburgessb3a0ca42011-10-12 07:44:40 +0000293
Thomas Tsou204a9f12013-10-29 18:34:16 -0400294 dB = 47.0;
dburgessb3a0ca42011-10-12 07:44:40 +0000295
Thomas Tsou204a9f12013-10-29 18:34:16 -0400296 writeLock.lock();
297 if (dB > maxRxGain())
298 dB = maxRxGain();
299 if (dB < minRxGain())
300 dB = minRxGain();
301
Harald Welte5cc88582018-08-17 19:55:38 +0200302 LOGC(DDEV, NOTICE) << "Setting RX gain to " << dB << " dB.";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400303
304 if (!m_dbRx->set_gain(dB))
Harald Welte5cc88582018-08-17 19:55:38 +0200305 LOGC(DDEV, ERR) << "Error setting RX gain";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400306
307 writeLock.unlock();
308
309 return dB;
dburgessb3a0ca42011-10-12 07:44:40 +0000310}
311
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100312bool USRPDevice::setRxAntenna(const std::string &ant, size_t chan)
313{
314 if (chan >= rx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200315 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100316 return false;
317 }
Harald Welte5cc88582018-08-17 19:55:38 +0200318 LOGC(DDEV, ALERT) << "Not implemented";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100319 return true;
320}
321
322std::string USRPDevice::getRxAntenna(size_t chan)
323{
324 if (chan >= rx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200325 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100326 return "";
327 }
Harald Welte5cc88582018-08-17 19:55:38 +0200328 LOGC(DDEV, ALERT) << "Not implemented";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100329 return "";
330}
331
332bool USRPDevice::setTxAntenna(const std::string &ant, size_t chan)
333{
334 if (chan >= tx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200335 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100336 return false;
337 }
Harald Welte5cc88582018-08-17 19:55:38 +0200338 LOGC(DDEV, ALERT) << "Not implemented";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100339 return true;
340}
341
342std::string USRPDevice::getTxAntenna(size_t chan)
343{
344 if (chan >= tx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200345 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100346 return "";
347 }
Harald Welte5cc88582018-08-17 19:55:38 +0200348 LOGC(DDEV, ALERT) << "Not implemented";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100349 return "";
350}
351
Pau Espin Pedrol0fc20d12018-04-24 17:48:52 +0200352bool USRPDevice::requiresRadioAlign()
353{
354 return true;
355}
dburgessb3a0ca42011-10-12 07:44:40 +0000356
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +0200357GSM::Time USRPDevice::minLatency() {
358 return GSM::Time(1,1);
359}
360
dburgessb3a0ca42011-10-12 07:44:40 +0000361// NOTE: Assumes sequential reads
Thomas Tsou204a9f12013-10-29 18:34:16 -0400362int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
363 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
dburgessb3a0ca42011-10-12 07:44:40 +0000364{
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100365#ifndef SWLOOPBACK
Thomas Tsou204a9f12013-10-29 18:34:16 -0400366 if (!m_uRx)
367 return 0;
368
369 short *buf = bufs[0];
370
dburgessb3a0ca42011-10-12 07:44:40 +0000371 timestamp += timestampOffset;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100372
dburgessb3a0ca42011-10-12 07:44:40 +0000373 if (timestamp + len < timeStart) {
374 memset(buf,0,len*2*sizeof(short));
375 return len;
376 }
377
378 if (underrun) *underrun = false;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100379
dburgessb3a0ca42011-10-12 07:44:40 +0000380 uint32_t readBuf[2000];
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100381
dburgessb3a0ca42011-10-12 07:44:40 +0000382 while (1) {
383 //guestimate USB read size
384 int readLen=0;
385 {
386 int numSamplesNeeded = timestamp + len - timeEnd;
387 if (numSamplesNeeded <=0) break;
388 readLen = 512 * ((int) ceil((float) numSamplesNeeded/126.0));
389 if (readLen > 8000) readLen= (8000/512)*512;
390 }
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100391
dburgessb3a0ca42011-10-12 07:44:40 +0000392 // read USRP packets, parse and save A/D data as needed
393 readLen = m_uRx->read((void *)readBuf,readLen,overrun);
Pau Espin Pedrol4d179ab2018-09-10 10:36:04 +0200394 for (int pktNum = 0; pktNum < (readLen/512); pktNum++) {
dburgessb3a0ca42011-10-12 07:44:40 +0000395 // tmpBuf points to start of a USB packet
396 uint32_t* tmpBuf = (uint32_t *) (readBuf+pktNum*512/4);
397 TIMESTAMP pktTimestamp = usrp_to_host_u32(tmpBuf[1]);
398 uint32_t word0 = usrp_to_host_u32(tmpBuf[0]);
399 uint32_t chan = (word0 >> 16) & 0x1f;
400 unsigned payloadSz = word0 & 0x1ff;
Harald Welte5cc88582018-08-17 19:55:38 +0200401 LOGC(DDEV, DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp;
dburgessb3a0ca42011-10-12 07:44:40 +0000402
403 bool incrementHi32 = ((lastPktTimestamp & 0x0ffffffffll) > pktTimestamp);
404 if (incrementHi32 && (timeStart!=0)) {
Harald Welte5cc88582018-08-17 19:55:38 +0200405 LOGC(DDEV, DEBUG) << "high 32 increment!!!";
dburgessb3a0ca42011-10-12 07:44:40 +0000406 hi32Timestamp++;
407 }
408 pktTimestamp = (((TIMESTAMP) hi32Timestamp) << 32) | pktTimestamp;
409 lastPktTimestamp = pktTimestamp;
410
411 if (chan == 0x01f) {
412 // control reply, check to see if its ping reply
413 uint32_t word2 = usrp_to_host_u32(tmpBuf[2]);
414 if ((word2 >> 16) == ((0x01 << 8) | 0x02)) {
415 timestamp -= timestampOffset;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400416 timestampOffset = pktTimestamp - pingTimestamp + pingOffset;
Harald Welte5cc88582018-08-17 19:55:38 +0200417 LOGC(DDEV, DEBUG) << "updating timestamp offset to: " << timestampOffset;
dburgessb3a0ca42011-10-12 07:44:40 +0000418 timestamp += timestampOffset;
419 isAligned = true;
420 }
421 continue;
422 }
423 if (chan != 0) {
Harald Welte5cc88582018-08-17 19:55:38 +0200424 LOGC(DDEV, DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz;
dburgessb3a0ca42011-10-12 07:44:40 +0000425 continue;
426 }
427 if ((word0 >> 28) & 0x04) {
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100428 if (underrun) *underrun = true;
Harald Welte5cc88582018-08-17 19:55:38 +0200429 LOGC(DDEV, DEBUG) << "UNDERRUN in TRX->USRP interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000430 }
431 if (RSSI) *RSSI = (word0 >> 21) & 0x3f;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100432
dburgessb3a0ca42011-10-12 07:44:40 +0000433 if (!isAligned) continue;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100434
dburgessb3a0ca42011-10-12 07:44:40 +0000435 unsigned cursorStart = pktTimestamp - timeStart + dataStart;
436 while (cursorStart*2 > currDataSize) {
437 cursorStart -= currDataSize/2;
438 }
439 if (cursorStart*2 + payloadSz/2 > currDataSize) {
440 // need to circle around buffer
441 memcpy(data+cursorStart*2,tmpBuf+2,(currDataSize-cursorStart*2)*sizeof(short));
442 memcpy(data,tmpBuf+2+(currDataSize/2-cursorStart),payloadSz-(currDataSize-cursorStart*2)*sizeof(short));
443 }
444 else {
445 memcpy(data+cursorStart*2,tmpBuf+2,payloadSz);
446 }
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100447 if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd)
dburgessb3a0ca42011-10-12 07:44:40 +0000448 timeEnd = pktTimestamp+payloadSz/2/sizeof(short);
449
Harald Welte5cc88582018-08-17 19:55:38 +0200450 LOGC(DDEV, DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp;
dburgessb3a0ca42011-10-12 07:44:40 +0000451
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100452 }
453 }
454
dburgessb3a0ca42011-10-12 07:44:40 +0000455 // copy desired data to buf
456 unsigned bufStart = dataStart+(timestamp-timeStart);
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100457 if (bufStart + len < currDataSize/2) {
Harald Welte5cc88582018-08-17 19:55:38 +0200458 LOGC(DDEV, DEBUG) << "bufStart: " << bufStart;
dburgessb3a0ca42011-10-12 07:44:40 +0000459 memcpy(buf,data+bufStart*2,len*2*sizeof(short));
460 memset(data+bufStart*2,0,len*2*sizeof(short));
461 }
462 else {
Harald Welte5cc88582018-08-17 19:55:38 +0200463 LOGC(DDEV, DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart;
dburgessb3a0ca42011-10-12 07:44:40 +0000464 unsigned firstLength = (currDataSize/2-bufStart);
Harald Welte5cc88582018-08-17 19:55:38 +0200465 LOGC(DDEV, DEBUG) << "firstLength: " << firstLength;
dburgessb3a0ca42011-10-12 07:44:40 +0000466 memcpy(buf,data+bufStart*2,firstLength*2*sizeof(short));
467 memset(data+bufStart*2,0,firstLength*2*sizeof(short));
468 memcpy(buf+firstLength*2,data,(len-firstLength)*2*sizeof(short));
469 memset(data,0,(len-firstLength)*2*sizeof(short));
470 }
471 dataStart = (bufStart + len) % (currDataSize/2);
472 timeStart = timestamp + len;
473
dburgessb3a0ca42011-10-12 07:44:40 +0000474 return len;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100475
dburgessb3a0ca42011-10-12 07:44:40 +0000476#else
477 if (loopbackBufferSize < 2) return 0;
478 int numSamples = 0;
479 struct timeval currTime;
480 gettimeofday(&currTime,NULL);
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100481 double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 +
dburgessb3a0ca42011-10-12 07:44:40 +0000482 (currTime.tv_usec - lastReadTime.tv_usec);
483 if (timeElapsed < samplePeriod) {return 0;}
484 int numSamplesToRead = (int) floor(timeElapsed/samplePeriod);
485 if (numSamplesToRead < len) return 0;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100486
dburgessb3a0ca42011-10-12 07:44:40 +0000487 if (numSamplesToRead > len) numSamplesToRead = len;
488 if (numSamplesToRead > loopbackBufferSize/2) {
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100489 firstRead =false;
dburgessb3a0ca42011-10-12 07:44:40 +0000490 numSamplesToRead = loopbackBufferSize/2;
491 }
492 memcpy(buf,loopbackBuffer,sizeof(short)*2*numSamplesToRead);
493 loopbackBufferSize -= 2*numSamplesToRead;
494 memcpy(loopbackBuffer,loopbackBuffer+2*numSamplesToRead,
495 sizeof(short)*loopbackBufferSize);
496 numSamples = numSamplesToRead;
497 if (firstRead) {
498 int new_usec = lastReadTime.tv_usec + (int) round((double) numSamplesToRead * samplePeriod);
499 lastReadTime.tv_sec = lastReadTime.tv_sec + new_usec/1000000;
500 lastReadTime.tv_usec = new_usec % 1000000;
501 }
502 else {
503 gettimeofday(&lastReadTime,NULL);
504 firstRead = true;
505 }
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100506
dburgessb3a0ca42011-10-12 07:44:40 +0000507 return numSamples;
508#endif
509}
510
Thomas Tsou204a9f12013-10-29 18:34:16 -0400511int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
512 bool *underrun, unsigned long long timestamp,
513 bool isControl)
dburgessb3a0ca42011-10-12 07:44:40 +0000514{
515 writeLock.lock();
516
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100517#ifndef SWLOOPBACK
Thomas Tsou204a9f12013-10-29 18:34:16 -0400518 if (!m_uTx)
519 return 0;
520
521 short *buf = bufs[0];
522
kurtis.heimerlc8739b82011-11-02 00:06:34 +0000523 static uint32_t outData[128*20];
Thomas Tsou204a9f12013-10-29 18:34:16 -0400524
dburgessb3a0ca42011-10-12 07:44:40 +0000525 for (int i = 0; i < len*2; i++) {
526 buf[i] = host_to_usrp_short(buf[i]);
527 }
528
529 int numWritten = 0;
530 unsigned isStart = 1;
531 unsigned RSSI = 0;
532 unsigned CHAN = (isControl) ? 0x01f : 0x00;
533 len = len*2*sizeof(short);
534 int numPkts = (int) ceil((float)len/(float)504);
535 unsigned isEnd = (numPkts < 2);
536 uint32_t *outPkt = outData;
537 int pktNum = 0;
538 while (numWritten < len) {
539 // pkt is pointer to start of a USB packet
540 uint32_t *pkt = outPkt + pktNum*128;
541 isEnd = (len - numWritten <= 504);
542 unsigned payloadLen = ((len - numWritten) < 504) ? (len-numWritten) : 504;
543 pkt[0] = (isStart << 12 | isEnd << 11 | (RSSI & 0x3f) << 5 | CHAN) << 16 | payloadLen;
544 pkt[1] = timestamp & 0x0ffffffffll;
545 memcpy(pkt+2,buf+(numWritten/sizeof(short)),payloadLen);
546 numWritten += payloadLen;
547 timestamp += payloadLen/2/sizeof(short);
548 isStart = 0;
549 pkt[0] = host_to_usrp_u32(pkt[0]);
550 pkt[1] = host_to_usrp_u32(pkt[1]);
551 pktNum++;
552 }
553 m_uTx->write((const void*) outPkt,sizeof(uint32_t)*128*numPkts,NULL);
554
dburgessb3a0ca42011-10-12 07:44:40 +0000555 writeLock.unlock();
556
557 return len/2/sizeof(short);
558#else
559 int retVal = len;
560 memcpy(loopbackBuffer+loopbackBufferSize,buf,sizeof(short)*2*len);
dburgessb3a0ca42011-10-12 07:44:40 +0000561 loopbackBufferSize += retVal*2;
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100562
dburgessb3a0ca42011-10-12 07:44:40 +0000563 return retVal;
564#endif
565}
566
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100567bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
dburgessb3a0ca42011-10-12 07:44:40 +0000568{
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100569#ifndef SWLOOPBACK
dburgessb3a0ca42011-10-12 07:44:40 +0000570 short data[] = {0x00,0x02,0x00,0x00};
571 uint32_t *wordPtr = (uint32_t *) data;
kurtis.heimerlc8739b82011-11-02 00:06:34 +0000572 *wordPtr = host_to_usrp_u32(*wordPtr);
dburgessb3a0ca42011-10-12 07:44:40 +0000573 bool tmpUnderrun;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400574
575 std::vector<short *> buf(1, data);
576 if (writeSamples(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000577 pingTimestamp = timestamp;
578 return true;
579 }
580 return false;
581#else
582 return true;
583#endif
584}
585
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100586#ifndef SWLOOPBACK
Thomas Tsou204a9f12013-10-29 18:34:16 -0400587bool USRPDevice::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000588{
589 usrp_tune_result result;
dburgessb3a0ca42011-10-12 07:44:40 +0000590
Thomas Tsou204a9f12013-10-29 18:34:16 -0400591 if (chan) {
Harald Welte5cc88582018-08-17 19:55:38 +0200592 LOGC(DDEV, ALERT) << "Invalid channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400593 return false;
594 }
595
kurtis.heimerlb9e237e2011-11-26 03:17:59 +0000596 if (m_uTx->tune(txSubdevSpec.side, m_dbTx, wFreq, &result)) {
Harald Welte5cc88582018-08-17 19:55:38 +0200597 LOGC(DDEV, INFO) << "set TX: " << wFreq << std::endl
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000598 << " baseband freq: " << result.baseband_freq << std::endl
599 << " DDC freq: " << result.dxc_freq << std::endl
600 << " residual freq: " << result.residual_freq;
601 return true;
602 }
603 else {
Harald Weltef97d75b2019-01-11 22:41:58 +0100604 LOGC(DDEV, ALERT) << "set TX: " << wFreq << " failed" << std::endl
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000605 << " baseband freq: " << result.baseband_freq << std::endl
606 << " DDC freq: " << result.dxc_freq << std::endl
607 << " residual freq: " << result.residual_freq;
608 return false;
609 }
610}
611
Thomas Tsou204a9f12013-10-29 18:34:16 -0400612bool USRPDevice::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000613{
614 usrp_tune_result result;
615
Thomas Tsou204a9f12013-10-29 18:34:16 -0400616 if (chan) {
Harald Welte5cc88582018-08-17 19:55:38 +0200617 LOGC(DDEV, ALERT) << "Invalid channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400618 return false;
619 }
620
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000621 if (m_uRx->tune(0, m_dbRx, wFreq, &result)) {
Harald Welte5cc88582018-08-17 19:55:38 +0200622 LOGC(DDEV, INFO) << "set RX: " << wFreq << std::endl
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000623 << " baseband freq: " << result.baseband_freq << std::endl
624 << " DDC freq: " << result.dxc_freq << std::endl
625 << " residual freq: " << result.residual_freq;
626 return true;
627 }
628 else {
Harald Weltef97d75b2019-01-11 22:41:58 +0100629 LOGC(DDEV, ALERT) << "set RX: " << wFreq << " failed" << std::endl
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000630 << " baseband freq: " << result.baseband_freq << std::endl
631 << " DDC freq: " << result.dxc_freq << std::endl
632 << " residual freq: " << result.residual_freq;
633 return false;
634 }
635
636}
dburgessb3a0ca42011-10-12 07:44:40 +0000637
638#else
639bool USRPDevice::setTxFreq(double wFreq) { return true;};
640bool USRPDevice::setRxFreq(double wFreq) { return true;};
641#endif
kurtis.heimerl965e7572011-11-26 03:16:54 +0000642
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800643RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
Harald Welte61707e82018-06-13 23:21:57 +0200644 InterfaceType iface, size_t chans, double lo_offset,
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100645 const std::vector<std::string>& tx_paths,
646 const std::vector<std::string>& rx_paths)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000647{
Harald Welteb2294392018-06-13 23:42:19 +0200648 if (tx_sps != rx_sps) {
Harald Welte5cc88582018-08-17 19:55:38 +0200649 LOGC(DDEV, ERROR) << "USRP1 requires tx_sps == rx_sps";
Harald Welteb2294392018-06-13 23:42:19 +0200650 return NULL;
651 }
652 if (chans != 1) {
Harald Welte5cc88582018-08-17 19:55:38 +0200653 LOGC(DDEV, ERROR) << "USRP1 supports only 1 channel";
Harald Welteb2294392018-06-13 23:42:19 +0200654 return NULL;
655 }
656 if (lo_offset != 0.0) {
Harald Welte5cc88582018-08-17 19:55:38 +0200657 LOGC(DDEV, ERROR) << "USRP1 doesn't support lo_offset";
Harald Welteb2294392018-06-13 23:42:19 +0200658 return NULL;
659 }
Harald Welte61707e82018-06-13 23:21:57 +0200660 return new USRPDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000661}