blob: c9b2c91fbc7c035f6831304ae094b2f728064a88 [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
30*/
31
32
33#include <stdint.h>
34#include <string.h>
35#include <stdlib.h>
36#include "Threads.h"
37#include "USRPDevice.h"
38
39#include <Logger.h>
40
41
42using namespace std;
43
kurtis.heimerl79e71c92011-11-26 03:16:48 +000044enum dboardConfigType {
45 TXA_RXB,
46 TXB_RXA,
47 TXA_RXA,
48 TXB_RXB
49};
dburgessb3a0ca42011-10-12 07:44:40 +000050
kurtis.heimerl79e71c92011-11-26 03:16:48 +000051const dboardConfigType dboardConfig = TXA_RXB;
52const double USRPDevice::masterClockRate = 52.0e6;
dburgessb3a0ca42011-10-12 07:44:40 +000053
kurtis.heimerl965e7572011-11-26 03:16:54 +000054USRPDevice::USRPDevice (double _desiredSampleRate, bool skipRx)
55 : skipRx(skipRx)
dburgessb3a0ca42011-10-12 07:44:40 +000056{
57 LOG(INFO) << "creating USRP device...";
58 decimRate = (unsigned int) round(masterClockRate/_desiredSampleRate);
59 actualSampleRate = masterClockRate/decimRate;
60 rxGain = 0;
61
62#ifdef SWLOOPBACK
63 samplePeriod = 1.0e6/actualSampleRate;
64 loopbackBufferSize = 0;
65 gettimeofday(&lastReadTime,NULL);
66 firstRead = false;
67#endif
68}
69
kurtis.heimerl965e7572011-11-26 03:16:54 +000070bool USRPDevice::open()
dburgessb3a0ca42011-10-12 07:44:40 +000071{
dburgessb3a0ca42011-10-12 07:44:40 +000072 writeLock.unlock();
73
kurtis.heimerl965e7572011-11-26 03:16:54 +000074 LOG(INFO) << "opening USRP device..";
dburgessb3a0ca42011-10-12 07:44:40 +000075#ifndef SWLOOPBACK
76 string rbf = "std_inband.rbf";
77 //string rbf = "inband_1rxhb_1tx.rbf";
78 m_uRx.reset();
79 if (!skipRx) {
80 try {
81 m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make(0,decimRate,1,-1,
82 usrp_standard_rx::FPGA_MODE_NORMAL,
83 1024,16*8,rbf));
84#ifdef HAVE_LIBUSRP_3_2
85 m_uRx->set_fpga_master_clock_freq(masterClockRate);
86#endif
87 }
88
89 catch(...) {
90 LOG(ALERT) << "make failed on Rx";
91 m_uRx.reset();
92 return false;
93 }
94
95 if (m_uRx->fpga_master_clock_freq() != masterClockRate)
96 {
97 LOG(ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq()
98 << ", desired clock freq = " << masterClockRate;
99 m_uRx.reset();
100 return false;
101 }
102 }
103
104 try {
105 m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make(0,decimRate*2,1,-1,
106 1024,16*8,rbf));
107#ifdef HAVE_LIBUSRP_3_2
108 m_uTx->set_fpga_master_clock_freq(masterClockRate);
109#endif
110 }
111
112 catch(...) {
113 LOG(ALERT) << "make failed on Tx";
114 m_uTx.reset();
115 return false;
116 }
117
118 if (m_uTx->fpga_master_clock_freq() != masterClockRate)
119 {
120 LOG(ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq()
121 << ", desired clock freq = " << masterClockRate;
122 m_uTx.reset();
123 return false;
124 }
125
126 if (!skipRx) m_uRx->stop();
127 m_uTx->stop();
128
129#endif
130
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000131 switch (dboardConfig) {
132 case TXA_RXB:
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000133 txSubdevSpec = usrp_subdev_spec(0,0);
134 rxSubdevSpec = usrp_subdev_spec(1,0);
135 break;
136 case TXB_RXA:
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000137 txSubdevSpec = usrp_subdev_spec(1,0);
138 rxSubdevSpec = usrp_subdev_spec(0,0);
139 break;
140 case TXA_RXA:
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000141 txSubdevSpec = usrp_subdev_spec(0,0);
142 rxSubdevSpec = usrp_subdev_spec(0,0);
143 break;
144 case TXB_RXB:
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000145 txSubdevSpec = usrp_subdev_spec(1,0);
146 rxSubdevSpec = usrp_subdev_spec(1,0);
147 break;
148 default:
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000149 txSubdevSpec = usrp_subdev_spec(0,0);
150 rxSubdevSpec = usrp_subdev_spec(1,0);
151 }
152
kurtis.heimerlb9e237e2011-11-26 03:17:59 +0000153 m_dbTx = m_uTx->selected_subdev(txSubdevSpec);
154 m_dbRx = m_uRx->selected_subdev(rxSubdevSpec);
155
dburgessb3a0ca42011-10-12 07:44:40 +0000156 samplesRead = 0;
157 samplesWritten = 0;
158 started = false;
159
160 return true;
161}
162
163
164
165bool USRPDevice::start()
166{
167 LOG(INFO) << "starting USRP...";
168#ifndef SWLOOPBACK
169 if (!m_uRx && !skipRx) return false;
170 if (!m_uTx) return false;
171
172 if (!skipRx) m_uRx->stop();
173 m_uTx->stop();
174
175 writeLock.lock();
176 // power up and configure daughterboards
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000177 m_dbTx->set_enable(true);
178 m_uTx->set_mux(m_uTx->determine_tx_mux_value(txSubdevSpec));
179 m_uRx->set_mux(m_uRx->determine_rx_mux_value(rxSubdevSpec));
180
181 if (!m_dbRx->select_rx_antenna(1))
182 m_dbRx->select_rx_antenna(0);
183
dburgessb3a0ca42011-10-12 07:44:40 +0000184 writeLock.unlock();
185
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000186 // Set gains to midpoint
187 setTxGain((minTxGain() + maxTxGain()) / 2);
188 setRxGain((minRxGain() + maxRxGain()) / 2);
dburgessb3a0ca42011-10-12 07:44:40 +0000189
190 data = new short[currDataSize];
191 dataStart = 0;
192 dataEnd = 0;
193 timeStart = 0;
194 timeEnd = 0;
195 timestampOffset = 0;
196 latestWriteTimestamp = 0;
197 lastPktTimestamp = 0;
198 hi32Timestamp = 0;
199 isAligned = false;
200
201
202 if (!skipRx)
203 started = (m_uRx->start() && m_uTx->start());
204 else
205 started = m_uTx->start();
206 return started;
207#else
208 gettimeofday(&lastReadTime,NULL);
209 return true;
210#endif
211}
212
213bool USRPDevice::stop()
214{
215#ifndef SWLOOPBACK
216 if (!m_uRx) return false;
217 if (!m_uTx) return false;
218
dburgessb3a0ca42011-10-12 07:44:40 +0000219 delete[] currData;
220
221 started = !(m_uRx->stop() && m_uTx->stop());
222 return !started;
223#else
224 return true;
225#endif
226}
227
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000228double USRPDevice::maxTxGain()
229{
230 return m_dbTx->gain_max();
231}
232
233double USRPDevice::minTxGain()
234{
235 return m_dbTx->gain_min();
236}
237
238double USRPDevice::maxRxGain()
239{
240 return m_dbRx->gain_max();
241}
242
243double USRPDevice::minRxGain()
244{
245 return m_dbRx->gain_min();
246}
247
dburgessb3a0ca42011-10-12 07:44:40 +0000248double USRPDevice::setTxGain(double dB) {
249
250 writeLock.lock();
251 if (dB > maxTxGain()) dB = maxTxGain();
252 if (dB < minTxGain()) dB = minTxGain();
253
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000254 LOG(NOTICE) << "Setting TX gain to " << dB << " dB.";
dburgessb3a0ca42011-10-12 07:44:40 +0000255
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000256 if (!m_dbRx->set_gain(dB))
257 LOG(ERROR) << "Error setting TX gain";
dburgessb3a0ca42011-10-12 07:44:40 +0000258
259 writeLock.unlock();
260
261 return dB;
262}
263
264
265double USRPDevice::setRxGain(double dB) {
266
267 writeLock.lock();
268 if (dB > maxRxGain()) dB = maxRxGain();
269 if (dB < minRxGain()) dB = minRxGain();
270
kurtis.heimerlc3c7c3e2011-11-26 03:17:57 +0000271 LOG(NOTICE) << "Setting RX gain to " << dB << " dB.";
dburgessb3a0ca42011-10-12 07:44:40 +0000272
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000273 if (!m_dbRx->set_gain(dB))
274 LOG(ERROR) << "Error setting RX gain";
275
dburgessb3a0ca42011-10-12 07:44:40 +0000276 writeLock.unlock();
277
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000278 return dB;
dburgessb3a0ca42011-10-12 07:44:40 +0000279}
280
281
282// NOTE: Assumes sequential reads
283int USRPDevice::readSamples(short *buf, int len, bool *overrun,
284 TIMESTAMP timestamp,
285 bool *underrun,
286 unsigned *RSSI)
287{
288#ifndef SWLOOPBACK
289 if (!m_uRx) return 0;
290
291 timestamp += timestampOffset;
292
293 if (timestamp + len < timeStart) {
294 memset(buf,0,len*2*sizeof(short));
295 return len;
296 }
297
298 if (underrun) *underrun = false;
299
300 uint32_t readBuf[2000];
301
302 while (1) {
303 //guestimate USB read size
304 int readLen=0;
305 {
306 int numSamplesNeeded = timestamp + len - timeEnd;
307 if (numSamplesNeeded <=0) break;
308 readLen = 512 * ((int) ceil((float) numSamplesNeeded/126.0));
309 if (readLen > 8000) readLen= (8000/512)*512;
310 }
311
312 // read USRP packets, parse and save A/D data as needed
313 readLen = m_uRx->read((void *)readBuf,readLen,overrun);
314 for(int pktNum = 0; pktNum < (readLen/512); pktNum++) {
315 // tmpBuf points to start of a USB packet
316 uint32_t* tmpBuf = (uint32_t *) (readBuf+pktNum*512/4);
317 TIMESTAMP pktTimestamp = usrp_to_host_u32(tmpBuf[1]);
318 uint32_t word0 = usrp_to_host_u32(tmpBuf[0]);
319 uint32_t chan = (word0 >> 16) & 0x1f;
320 unsigned payloadSz = word0 & 0x1ff;
321 LOG(DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp;
322
323 bool incrementHi32 = ((lastPktTimestamp & 0x0ffffffffll) > pktTimestamp);
324 if (incrementHi32 && (timeStart!=0)) {
325 LOG(DEBUG) << "high 32 increment!!!";
326 hi32Timestamp++;
327 }
328 pktTimestamp = (((TIMESTAMP) hi32Timestamp) << 32) | pktTimestamp;
329 lastPktTimestamp = pktTimestamp;
330
331 if (chan == 0x01f) {
332 // control reply, check to see if its ping reply
333 uint32_t word2 = usrp_to_host_u32(tmpBuf[2]);
334 if ((word2 >> 16) == ((0x01 << 8) | 0x02)) {
335 timestamp -= timestampOffset;
336 timestampOffset = pktTimestamp - pingTimestamp + PINGOFFSET;
337 LOG(DEBUG) << "updating timestamp offset to: " << timestampOffset;
338 timestamp += timestampOffset;
339 isAligned = true;
340 }
341 continue;
342 }
343 if (chan != 0) {
344 LOG(DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz;
345 continue;
346 }
347 if ((word0 >> 28) & 0x04) {
348 if (underrun) *underrun = true;
349 LOG(DEBUG) << "UNDERRUN in TRX->USRP interface";
350 }
351 if (RSSI) *RSSI = (word0 >> 21) & 0x3f;
352
353 if (!isAligned) continue;
354
355 unsigned cursorStart = pktTimestamp - timeStart + dataStart;
356 while (cursorStart*2 > currDataSize) {
357 cursorStart -= currDataSize/2;
358 }
359 if (cursorStart*2 + payloadSz/2 > currDataSize) {
360 // need to circle around buffer
361 memcpy(data+cursorStart*2,tmpBuf+2,(currDataSize-cursorStart*2)*sizeof(short));
362 memcpy(data,tmpBuf+2+(currDataSize/2-cursorStart),payloadSz-(currDataSize-cursorStart*2)*sizeof(short));
363 }
364 else {
365 memcpy(data+cursorStart*2,tmpBuf+2,payloadSz);
366 }
367 if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd)
368 timeEnd = pktTimestamp+payloadSz/2/sizeof(short);
369
370 LOG(DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp;
371
372 }
373 }
374
375 // copy desired data to buf
376 unsigned bufStart = dataStart+(timestamp-timeStart);
377 if (bufStart + len < currDataSize/2) {
378 LOG(DEBUG) << "bufStart: " << bufStart;
379 memcpy(buf,data+bufStart*2,len*2*sizeof(short));
380 memset(data+bufStart*2,0,len*2*sizeof(short));
381 }
382 else {
383 LOG(DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart;
384 unsigned firstLength = (currDataSize/2-bufStart);
385 LOG(DEBUG) << "firstLength: " << firstLength;
386 memcpy(buf,data+bufStart*2,firstLength*2*sizeof(short));
387 memset(data+bufStart*2,0,firstLength*2*sizeof(short));
388 memcpy(buf+firstLength*2,data,(len-firstLength)*2*sizeof(short));
389 memset(data,0,(len-firstLength)*2*sizeof(short));
390 }
391 dataStart = (bufStart + len) % (currDataSize/2);
392 timeStart = timestamp + len;
393
dburgessb3a0ca42011-10-12 07:44:40 +0000394 return len;
395
396#else
397 if (loopbackBufferSize < 2) return 0;
398 int numSamples = 0;
399 struct timeval currTime;
400 gettimeofday(&currTime,NULL);
401 double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 +
402 (currTime.tv_usec - lastReadTime.tv_usec);
403 if (timeElapsed < samplePeriod) {return 0;}
404 int numSamplesToRead = (int) floor(timeElapsed/samplePeriod);
405 if (numSamplesToRead < len) return 0;
406
407 if (numSamplesToRead > len) numSamplesToRead = len;
408 if (numSamplesToRead > loopbackBufferSize/2) {
409 firstRead =false;
410 numSamplesToRead = loopbackBufferSize/2;
411 }
412 memcpy(buf,loopbackBuffer,sizeof(short)*2*numSamplesToRead);
413 loopbackBufferSize -= 2*numSamplesToRead;
414 memcpy(loopbackBuffer,loopbackBuffer+2*numSamplesToRead,
415 sizeof(short)*loopbackBufferSize);
416 numSamples = numSamplesToRead;
417 if (firstRead) {
418 int new_usec = lastReadTime.tv_usec + (int) round((double) numSamplesToRead * samplePeriod);
419 lastReadTime.tv_sec = lastReadTime.tv_sec + new_usec/1000000;
420 lastReadTime.tv_usec = new_usec % 1000000;
421 }
422 else {
423 gettimeofday(&lastReadTime,NULL);
424 firstRead = true;
425 }
426 samplesRead += numSamples;
427
428 return numSamples;
429#endif
430}
431
432int USRPDevice::writeSamples(short *buf, int len, bool *underrun,
433 unsigned long long timestamp,
434 bool isControl)
435{
436 writeLock.lock();
437
438#ifndef SWLOOPBACK
439 if (!m_uTx) return 0;
440
kurtis.heimerlc8739b82011-11-02 00:06:34 +0000441 static uint32_t outData[128*20];
dburgessb3a0ca42011-10-12 07:44:40 +0000442
443 for (int i = 0; i < len*2; i++) {
444 buf[i] = host_to_usrp_short(buf[i]);
445 }
446
447 int numWritten = 0;
448 unsigned isStart = 1;
449 unsigned RSSI = 0;
450 unsigned CHAN = (isControl) ? 0x01f : 0x00;
451 len = len*2*sizeof(short);
452 int numPkts = (int) ceil((float)len/(float)504);
453 unsigned isEnd = (numPkts < 2);
454 uint32_t *outPkt = outData;
455 int pktNum = 0;
456 while (numWritten < len) {
457 // pkt is pointer to start of a USB packet
458 uint32_t *pkt = outPkt + pktNum*128;
459 isEnd = (len - numWritten <= 504);
460 unsigned payloadLen = ((len - numWritten) < 504) ? (len-numWritten) : 504;
461 pkt[0] = (isStart << 12 | isEnd << 11 | (RSSI & 0x3f) << 5 | CHAN) << 16 | payloadLen;
462 pkt[1] = timestamp & 0x0ffffffffll;
463 memcpy(pkt+2,buf+(numWritten/sizeof(short)),payloadLen);
464 numWritten += payloadLen;
465 timestamp += payloadLen/2/sizeof(short);
466 isStart = 0;
467 pkt[0] = host_to_usrp_u32(pkt[0]);
468 pkt[1] = host_to_usrp_u32(pkt[1]);
469 pktNum++;
470 }
471 m_uTx->write((const void*) outPkt,sizeof(uint32_t)*128*numPkts,NULL);
472
473 samplesWritten += len/2/sizeof(short);
474 writeLock.unlock();
475
476 return len/2/sizeof(short);
477#else
478 int retVal = len;
479 memcpy(loopbackBuffer+loopbackBufferSize,buf,sizeof(short)*2*len);
480 samplesWritten += retVal;
481 loopbackBufferSize += retVal*2;
482
483 return retVal;
484#endif
485}
486
487bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
488{
489#ifndef SWLOOPBACK
490 short data[] = {0x00,0x02,0x00,0x00};
491 uint32_t *wordPtr = (uint32_t *) data;
kurtis.heimerlc8739b82011-11-02 00:06:34 +0000492 *wordPtr = host_to_usrp_u32(*wordPtr);
dburgessb3a0ca42011-10-12 07:44:40 +0000493 bool tmpUnderrun;
494 if (writeSamples((short *) data,1,&tmpUnderrun,timestamp & 0x0ffffffffll,true)) {
495 pingTimestamp = timestamp;
496 return true;
497 }
498 return false;
499#else
500 return true;
501#endif
502}
503
504#ifndef SWLOOPBACK
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000505bool USRPDevice::setTxFreq(double wFreq)
506{
507 usrp_tune_result result;
dburgessb3a0ca42011-10-12 07:44:40 +0000508
kurtis.heimerlb9e237e2011-11-26 03:17:59 +0000509 if (m_uTx->tune(txSubdevSpec.side, m_dbTx, wFreq, &result)) {
kurtis.heimerl79e71c92011-11-26 03:16:48 +0000510 LOG(INFO) << "set TX: " << wFreq << std::endl
511 << " baseband freq: " << result.baseband_freq << std::endl
512 << " DDC freq: " << result.dxc_freq << std::endl
513 << " residual freq: " << result.residual_freq;
514 return true;
515 }
516 else {
517 LOG(ERROR) << "set TX: " << wFreq << "failed" << std::endl
518 << " baseband freq: " << result.baseband_freq << std::endl
519 << " DDC freq: " << result.dxc_freq << std::endl
520 << " residual freq: " << result.residual_freq;
521 return false;
522 }
523}
524
525bool USRPDevice::setRxFreq(double wFreq)
526{
527 usrp_tune_result result;
528
529 if (m_uRx->tune(0, m_dbRx, wFreq, &result)) {
530 LOG(INFO) << "set RX: " << wFreq << std::endl
531 << " baseband freq: " << result.baseband_freq << std::endl
532 << " DDC freq: " << result.dxc_freq << std::endl
533 << " residual freq: " << result.residual_freq;
534 return true;
535 }
536 else {
537 LOG(ERROR) << "set RX: " << wFreq << "failed" << std::endl
538 << " baseband freq: " << result.baseband_freq << std::endl
539 << " DDC freq: " << result.dxc_freq << std::endl
540 << " residual freq: " << result.residual_freq;
541 return false;
542 }
543
544}
dburgessb3a0ca42011-10-12 07:44:40 +0000545
546#else
547bool USRPDevice::setTxFreq(double wFreq) { return true;};
548bool USRPDevice::setRxFreq(double wFreq) { return true;};
549#endif
kurtis.heimerl965e7572011-11-26 03:16:54 +0000550
551RadioDevice *RadioDevice::make(double desiredSampleRate, bool skipRx)
552{
553 return new USRPDevice(desiredSampleRate, skipRx);
554}