mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
				synced 2025-11-04 06:03:17 +00:00 
			
		
		
		
	Compare commits
	
		
			60 Commits
		
	
	
		
			1.6.1
			...
			achemeris/
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					0b8aa00bc7 | ||
| 
						 | 
					1ed2e27d19 | ||
| 
						 | 
					91ac934749 | ||
| 
						 | 
					7398a756ea | ||
| 
						 | 
					44389b80d5 | ||
| 
						 | 
					3e24f791dd | ||
| 
						 | 
					ef7c258cbf | ||
| 
						 | 
					fb6e75789b | ||
| 
						 | 
					23ee9002b4 | ||
| 
						 | 
					98e58b9111 | ||
| 
						 | 
					0c260aecaf | ||
| 
						 | 
					acc9ee9fc0 | ||
| 
						 | 
					9520ecd0c5 | ||
| 
						 | 
					ab599f8b6d | ||
| 
						 | 
					d6ed8c0b0e | ||
| 
						 | 
					c8ce878daf | ||
| 
						 | 
					e01e1b3fa8 | ||
| 
						 | 
					dbd27a60b6 | ||
| 
						 | 
					c5da6607b4 | ||
| 
						 | 
					42ade041d7 | ||
| 
						 | 
					5e18001bb0 | ||
| 
						 | 
					621e52ab4a | ||
| 
						 | 
					f86aa2c923 | ||
| 
						 | 
					48f8fb34aa | ||
| 
						 | 
					40c3d0a6d9 | ||
| 
						 | 
					41c6657938 | ||
| 
						 | 
					cd576c9636 | ||
| 
						 | 
					3eeda4841d | ||
| 
						 | 
					5d64491f9b | ||
| 
						 | 
					b5c450dfdf | ||
| 
						 | 
					afb04f8b63 | ||
| 
						 | 
					5f13377b83 | ||
| 
						 | 
					a6ca73ca67 | ||
| 
						 | 
					5a37840dfa | ||
| 
						 | 
					ca5d35cce8 | ||
| 
						 | 
					507e6d4e12 | ||
| 
						 | 
					1f330a9801 | ||
| 
						 | 
					7c6f58af7a | ||
| 
						 | 
					996f426c16 | ||
| 
						 | 
					711e6afddf | ||
| 
						 | 
					222688d3dc | ||
| 
						 | 
					20bc24d367 | ||
| 
						 | 
					59796e1e3e | ||
| 
						 | 
					19a506dffa | ||
| 
						 | 
					fbd6e1c985 | ||
| 
						 | 
					03669856b7 | ||
| 
						 | 
					5d0e392b21 | ||
| 
						 | 
					801ce60d4a | ||
| 
						 | 
					ed64e799bc | ||
| 
						 | 
					d1bcab2731 | ||
| 
						 | 
					00493f1a41 | ||
| 
						 | 
					d565556b4b | ||
| 
						 | 
					2b48784c61 | ||
| 
						 | 
					635e34239c | ||
| 
						 | 
					9e5e208b6e | ||
| 
						 | 
					8f47387777 | ||
| 
						 | 
					c707f42396 | ||
| 
						 | 
					00ed1441a1 | ||
| 
						 | 
					8d804a4cd8 | ||
| 
						 | 
					82ede3e810 | 
@@ -35,7 +35,7 @@
 | 
			
		||||
#ifdef DEBUG_CONFIG
 | 
			
		||||
#define	debugLogEarly gLogEarly
 | 
			
		||||
#else
 | 
			
		||||
#define	debugLogEarly
 | 
			
		||||
#define	debugLogEarly(x,y,z)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,9 @@
 | 
			
		||||
 | 
			
		||||
include $(top_srcdir)/Makefile.common
 | 
			
		||||
 | 
			
		||||
DESTDIR := 
 | 
			
		||||
 | 
			
		||||
ACLOCAL_AMFLAGS = -I config
 | 
			
		||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES)
 | 
			
		||||
AM_CXXFLAGS = -Wall -pthread -ldl
 | 
			
		||||
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread -ldl
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										304
									
								
								Transceiver52M/DriveLoop.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										304
									
								
								Transceiver52M/DriveLoop.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,304 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright 2008, 2009, 2010, 2012 Free Software Foundation, Inc.
 | 
			
		||||
*
 | 
			
		||||
* This software is distributed under the terms of the GNU Public License.
 | 
			
		||||
* See the COPYING file in the main directory for details.
 | 
			
		||||
*
 | 
			
		||||
* This use of this software may be subject to additional restrictions.
 | 
			
		||||
* See the LEGAL file in the main directory for details.
 | 
			
		||||
 | 
			
		||||
    This program is free software: you can redistribute it and/or modify
 | 
			
		||||
    it under the terms of the GNU General Public License as published by
 | 
			
		||||
    the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
    (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
    This program is distributed in the hope that it will be useful,
 | 
			
		||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
    GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License
 | 
			
		||||
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include "DriveLoop.h"
 | 
			
		||||
#include <Logger.h>
 | 
			
		||||
 | 
			
		||||
using namespace GSM;
 | 
			
		||||
 | 
			
		||||
DriveLoop::DriveLoop(int wBasePort, const char *TRXAddress,
 | 
			
		||||
                     RadioInterface *wRadioInterface,
 | 
			
		||||
		     int wChanM, int wC0, int wSPS,
 | 
			
		||||
                     GSM::Time wTransmitLatency)
 | 
			
		||||
	:mClockSocket(wBasePort, TRXAddress, wBasePort + 100), mC0(wC0)
 | 
			
		||||
{
 | 
			
		||||
  mChanM = wChanM;
 | 
			
		||||
  mRadioDriveLoopThread = NULL;
 | 
			
		||||
  mSPS = wSPS;
 | 
			
		||||
  mRadioInterface = wRadioInterface;
 | 
			
		||||
 | 
			
		||||
  mStartTime = (random() % gHyperframe, 0);
 | 
			
		||||
 | 
			
		||||
  mTransmitDeadlineClock = mStartTime;
 | 
			
		||||
  mLatencyUpdateTime = mStartTime;
 | 
			
		||||
  mTransmitLatency = wTransmitLatency;
 | 
			
		||||
  mLastClockUpdateTime = mStartTime;
 | 
			
		||||
 | 
			
		||||
  mRadioInterface->getClock()->set(mStartTime);
 | 
			
		||||
  txFullScale = mRadioInterface->fullScaleInputValue();
 | 
			
		||||
 | 
			
		||||
  mOn = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DriveLoop::~DriveLoop()
 | 
			
		||||
{
 | 
			
		||||
  if (mOn) {
 | 
			
		||||
    mOn = false;
 | 
			
		||||
 | 
			
		||||
    if (mRadioDriveLoopThread)
 | 
			
		||||
      delete mRadioDriveLoopThread;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  sigProcLibDestroy();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool DriveLoop::init()
 | 
			
		||||
{
 | 
			
		||||
  if (!sigProcLibSetup(mSPS)) {
 | 
			
		||||
    LOG(ALERT) << "Failed to initialize signal processing library";
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // initialize filler tables with dummy bursts on C0, empty bursts otherwise
 | 
			
		||||
  for (int i = 0; i < 8; i++) {
 | 
			
		||||
    signalVector* modBurst = modulateBurst(gDummyBurst,
 | 
			
		||||
                                           8 + (i % 4 == 0), mSPS);
 | 
			
		||||
    if (!modBurst) {
 | 
			
		||||
      sigProcLibDestroy();
 | 
			
		||||
      LOG(ALERT) << "Failed to initialize filler table";
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    scaleVector(*modBurst, txFullScale);
 | 
			
		||||
    for (int j = 0; j < 102; j++) {
 | 
			
		||||
      for (int n = 0; n < mChanM; n++) {
 | 
			
		||||
#ifndef TRX_LOAD_TESTING
 | 
			
		||||
        if (n == mC0)
 | 
			
		||||
          fillerTable[n][j][i] = new signalVector(*modBurst);
 | 
			
		||||
        else
 | 
			
		||||
          fillerTable[n][j][i] = new signalVector(modBurst->size());
 | 
			
		||||
#else
 | 
			
		||||
          fillerTable[n][j][i] = new signalVector(*modBurst);
 | 
			
		||||
#endif
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    delete modBurst;
 | 
			
		||||
 | 
			
		||||
    for (int n = 0; n < mChanM; n++) {
 | 
			
		||||
      fillerModulus[n][i] = 26;
 | 
			
		||||
      mChanType[n][i] = NONE;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DriveLoop::start()
 | 
			
		||||
{
 | 
			
		||||
  if (mOn)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  mOn = true;
 | 
			
		||||
  mRadioDriveLoopThread = new Thread(32768);
 | 
			
		||||
  mRadioDriveLoopThread->start((void * (*)(void*))RadioDriveLoopAdapter, (void*) this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DriveLoop::pushRadioVector(GSM::Time &nowTime)
 | 
			
		||||
{
 | 
			
		||||
  int i;
 | 
			
		||||
  radioVector *staleBurst;
 | 
			
		||||
  radioVector *next;
 | 
			
		||||
 | 
			
		||||
  for (i = 0; i < mChanM; i++) {
 | 
			
		||||
    // dump stale bursts, if any
 | 
			
		||||
    while (staleBurst = mTransmitPriorityQueue[i].getStaleBurst(nowTime)) {
 | 
			
		||||
      // Even if the burst is stale, put it in the fillter table.
 | 
			
		||||
      // (It might be an idle pattern.)
 | 
			
		||||
      LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int TN = nowTime.TN();
 | 
			
		||||
    int modFN = nowTime.FN() % fillerModulus[i][nowTime.TN()];
 | 
			
		||||
 | 
			
		||||
    mTxBursts[i] = fillerTable[i][modFN][TN];
 | 
			
		||||
    mIsFiller[i] = true;
 | 
			
		||||
#ifndef TRX_LOAD_TESTING
 | 
			
		||||
    mIsZero[i] = (mChanType[i][TN] == NONE);
 | 
			
		||||
#else
 | 
			
		||||
	mIsZero[i] = false;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    // if queue contains data at the desired timestamp, stick it into FIFO
 | 
			
		||||
    if (next = (radioVector*) mTransmitPriorityQueue[i].getCurrentBurst(nowTime)) {
 | 
			
		||||
      LOG(DEBUG) << "transmitFIFO: wrote burst " << next << " at time: " << nowTime;
 | 
			
		||||
      mTxBursts[i] = next;
 | 
			
		||||
      mIsFiller[i] = false;
 | 
			
		||||
      mIsZero[i] = false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  mRadioInterface->driveTransmitRadio(mTxBursts, mIsZero);
 | 
			
		||||
 | 
			
		||||
  for (i = 0; i < mChanM; i++) {
 | 
			
		||||
    if (!mIsFiller[i])
 | 
			
		||||
      delete mTxBursts[i];
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DriveLoop::setModulus(int channel, int timeslot)
 | 
			
		||||
{
 | 
			
		||||
  switch (mChanType[channel][timeslot]) {
 | 
			
		||||
  case NONE:
 | 
			
		||||
  case I:
 | 
			
		||||
  case II:
 | 
			
		||||
  case III:
 | 
			
		||||
  case FILL:
 | 
			
		||||
    fillerModulus[channel][timeslot] = 26;
 | 
			
		||||
    break;
 | 
			
		||||
  case IV:
 | 
			
		||||
  case VI:
 | 
			
		||||
  case V:
 | 
			
		||||
    fillerModulus[channel][timeslot] = 51;
 | 
			
		||||
    break;
 | 
			
		||||
    //case V: 
 | 
			
		||||
  case VII:
 | 
			
		||||
    fillerModulus[channel][timeslot] = 102;
 | 
			
		||||
    break;
 | 
			
		||||
  default:
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DriveLoop::CorrType DriveLoop::expectedCorrType(int channel, GSM::Time currTime)
 | 
			
		||||
{
 | 
			
		||||
  unsigned burstTN = currTime.TN();
 | 
			
		||||
  unsigned burstFN = currTime.FN();
 | 
			
		||||
 | 
			
		||||
  switch (mChanType[channel][burstTN]) {
 | 
			
		||||
  case NONE:
 | 
			
		||||
    return OFF;
 | 
			
		||||
    break;
 | 
			
		||||
  case FILL:
 | 
			
		||||
    return IDLE;
 | 
			
		||||
    break;
 | 
			
		||||
  case I:
 | 
			
		||||
    return TSC;
 | 
			
		||||
    /*if (burstFN % 26 == 25) 
 | 
			
		||||
      return IDLE;
 | 
			
		||||
    else
 | 
			
		||||
      return TSC;*/
 | 
			
		||||
    break;
 | 
			
		||||
  case II:
 | 
			
		||||
    if (burstFN % 2 == 1)
 | 
			
		||||
      return IDLE;
 | 
			
		||||
    else
 | 
			
		||||
      return TSC;
 | 
			
		||||
    break;
 | 
			
		||||
  case III:
 | 
			
		||||
    return TSC;
 | 
			
		||||
    break;
 | 
			
		||||
  case IV:
 | 
			
		||||
  case VI:
 | 
			
		||||
    return RACH;
 | 
			
		||||
    break;
 | 
			
		||||
  case V: {
 | 
			
		||||
    int mod51 = burstFN % 51;
 | 
			
		||||
    if ((mod51 <= 36) && (mod51 >= 14))
 | 
			
		||||
      return RACH;
 | 
			
		||||
    else if ((mod51 == 4) || (mod51 == 5))
 | 
			
		||||
      return RACH;
 | 
			
		||||
    else if ((mod51 == 45) || (mod51 == 46))
 | 
			
		||||
      return RACH;
 | 
			
		||||
    else
 | 
			
		||||
      return TSC;
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case VII:
 | 
			
		||||
    if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
 | 
			
		||||
      return IDLE;
 | 
			
		||||
    else
 | 
			
		||||
      return TSC;
 | 
			
		||||
    break;
 | 
			
		||||
  case LOOPBACK:
 | 
			
		||||
    if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
 | 
			
		||||
      return IDLE;
 | 
			
		||||
    else
 | 
			
		||||
      return TSC;
 | 
			
		||||
    break;
 | 
			
		||||
  default:
 | 
			
		||||
    return OFF;
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
void DriveLoop::driveReceiveFIFO() 
 | 
			
		||||
{
 | 
			
		||||
  SoftVector *rxBurst = NULL;
 | 
			
		||||
  int RSSI;
 | 
			
		||||
  int TOA;  // in 1/256 of a symbol
 | 
			
		||||
  GSM::Time burstTime;
 | 
			
		||||
 | 
			
		||||
  mRadioInterface->driveReceiveRadio();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *  Features a carefully controlled latency mechanism, to
 | 
			
		||||
 *  assure that transmit packets arrive at the radio/USRP
 | 
			
		||||
 *  before they need to be transmitted.
 | 
			
		||||
 *  
 | 
			
		||||
 *  Deadline clock indicates the burst that needs to be
 | 
			
		||||
 *  pushed into the FIFO right NOW.  If transmit queue does
 | 
			
		||||
 *  not have a burst, stick in filler data.
 | 
			
		||||
 */
 | 
			
		||||
void DriveLoop::driveTransmitFIFO() 
 | 
			
		||||
{
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  RadioClock *radioClock = (mRadioInterface->getClock());
 | 
			
		||||
  while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
 | 
			
		||||
    pushRadioVector(mTransmitDeadlineClock);
 | 
			
		||||
    mTransmitDeadlineClock.incTN();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // FIXME -- This should not be a hard spin.
 | 
			
		||||
  // But any delay here causes us to throw omni_thread_fatal.
 | 
			
		||||
  //else radioClock->wait();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DriveLoop::writeClockInterface()
 | 
			
		||||
{
 | 
			
		||||
  char command[50];
 | 
			
		||||
  // FIXME -- This should be adaptive.
 | 
			
		||||
  sprintf(command,"IND CLOCK %llu",
 | 
			
		||||
          (unsigned long long) (mTransmitDeadlineClock.FN() + 2));
 | 
			
		||||
 | 
			
		||||
  LOG(INFO) << "ClockInterface: sending " << command;
 | 
			
		||||
 | 
			
		||||
  mClockSocket.write(command,strlen(command)+1);
 | 
			
		||||
 | 
			
		||||
  mLastClockUpdateTime = mTransmitDeadlineClock;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *RadioDriveLoopAdapter(DriveLoop *drive)
 | 
			
		||||
{
 | 
			
		||||
  drive->setPriority();
 | 
			
		||||
 | 
			
		||||
  while (drive->on()) {
 | 
			
		||||
    drive->driveReceiveFIFO();
 | 
			
		||||
    drive->driveTransmitFIFO();
 | 
			
		||||
    pthread_testcancel();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										195
									
								
								Transceiver52M/DriveLoop.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								Transceiver52M/DriveLoop.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,195 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright 2008, 2012 Free Software Foundation, Inc.
 | 
			
		||||
*
 | 
			
		||||
* This software is distributed under the terms of the GNU Public License.
 | 
			
		||||
* See the COPYING file in the main directory for details.
 | 
			
		||||
*
 | 
			
		||||
* This use of this software may be subject to additional restrictions.
 | 
			
		||||
* See the LEGAL file in the main directory for details.
 | 
			
		||||
 | 
			
		||||
    This program is free software: you can redistribute it and/or modify
 | 
			
		||||
    it under the terms of the GNU General Public License as published by
 | 
			
		||||
    the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
    (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
    This program is distributed in the hope that it will be useful,
 | 
			
		||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
    GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License
 | 
			
		||||
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
	Compilation switches
 | 
			
		||||
	TRANSMIT_LOGGING	write every burst on the given slot to a log
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#ifndef _DRIVELOOP_H_
 | 
			
		||||
#define _DRIVELOOP_H_
 | 
			
		||||
 | 
			
		||||
#include "radioInterface.h"
 | 
			
		||||
#include "Interthread.h"
 | 
			
		||||
#include "GSMCommon.h"
 | 
			
		||||
#include "Sockets.h"
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
 | 
			
		||||
/** Define this to be the slot number to be logged. */
 | 
			
		||||
//#define TRANSMIT_LOGGING 1
 | 
			
		||||
 | 
			
		||||
/** The Transceiver class, responsible for physical layer of basestation */
 | 
			
		||||
class DriveLoop {
 | 
			
		||||
  
 | 
			
		||||
private:
 | 
			
		||||
 | 
			
		||||
  GSM::Time mTransmitLatency;     ///< latency between basestation clock and transmit deadline clock
 | 
			
		||||
  GSM::Time mLatencyUpdateTime;   ///< last time latency was updated
 | 
			
		||||
  GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
 | 
			
		||||
 | 
			
		||||
  UDPSocket mClockSocket;	  ///< socket for writing clock updates to GSM core
 | 
			
		||||
 | 
			
		||||
  VectorQueue  mTransmitPriorityQueue[CHAN_MAX];   ///< priority queue of transmit bursts received from GSM core
 | 
			
		||||
 | 
			
		||||
  Thread *mRadioDriveLoopThread;  ///< thread to push/pull bursts into transmit/receive FIFO
 | 
			
		||||
 | 
			
		||||
  GSM::Time mTransmitDeadlineClock;       ///< deadline for pushing bursts into transmit FIFO 
 | 
			
		||||
  GSM::Time mStartTime;                   ///< random start time of the radio clock
 | 
			
		||||
 | 
			
		||||
  RadioInterface *mRadioInterface;	  ///< associated radioInterface object
 | 
			
		||||
  double txFullScale;                     ///< full scale input to radio
 | 
			
		||||
  double rxFullScale;                     ///< full scale output to radio
 | 
			
		||||
 | 
			
		||||
  /** Number of channels supported by the channelizer */
 | 
			
		||||
  int mChanM;
 | 
			
		||||
 | 
			
		||||
  /** unmodulate a modulated burst */
 | 
			
		||||
#ifdef TRANSMIT_LOGGING
 | 
			
		||||
  void unModulateVector(signalVector wVector); 
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  /** Push modulated burst into transmit FIFO corresponding to a particular timestamp */
 | 
			
		||||
  void pushRadioVector(GSM::Time &nowTime);
 | 
			
		||||
 | 
			
		||||
  /** Pull and demodulate a burst from the receive FIFO */ 
 | 
			
		||||
  SoftVector *pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset);
 | 
			
		||||
   
 | 
			
		||||
  int mSPS;               ///< number of samples per GSM symbol
 | 
			
		||||
 | 
			
		||||
  bool mOn;			       ///< flag to indicate that transceiver is powered on
 | 
			
		||||
  int fillerModulus[CHAN_MAX][8];                ///< modulus values of all timeslots, in frames
 | 
			
		||||
  signalVector *fillerTable[CHAN_MAX][102][8];   ///< table of modulated filler waveforms for all timeslots
 | 
			
		||||
 | 
			
		||||
  /** Channelizer path for primary ARFCN */
 | 
			
		||||
  int mC0;
 | 
			
		||||
 | 
			
		||||
  signalVector *mTxBursts[CHAN_MAX];
 | 
			
		||||
  bool         mIsFiller[CHAN_MAX];
 | 
			
		||||
  bool         mIsZero[CHAN_MAX];
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
  /** Transceiver constructor 
 | 
			
		||||
      @param wBasePort base port number of UDP sockets
 | 
			
		||||
      @param TRXAddress IP address of the TRX manager, as a string
 | 
			
		||||
      @param wSPS number of samples per GSM symbol
 | 
			
		||||
      @param wTransmitLatency initial setting of transmit latency
 | 
			
		||||
      @param radioInterface associated radioInterface object
 | 
			
		||||
  */
 | 
			
		||||
  DriveLoop(int wBasePort, const char *TRXAddress,
 | 
			
		||||
	    RadioInterface *wRadioInterface,
 | 
			
		||||
	    int wChanM = 1, int wC0 = 0,
 | 
			
		||||
            int wSPS = SAMPSPERSYM,
 | 
			
		||||
	    GSM::Time wTransmitLatency = GSM::Time(3, 0));
 | 
			
		||||
   
 | 
			
		||||
  /** Destructor */
 | 
			
		||||
  ~DriveLoop();
 | 
			
		||||
 | 
			
		||||
  /** start the Transceiver */
 | 
			
		||||
  bool init();
 | 
			
		||||
  void start();
 | 
			
		||||
 | 
			
		||||
  VectorQueue *priorityQueue(int m) { return &mTransmitPriorityQueue[m]; }
 | 
			
		||||
 | 
			
		||||
  /** Codes for burst types of received bursts*/
 | 
			
		||||
  typedef enum {
 | 
			
		||||
    OFF,               ///< timeslot is off
 | 
			
		||||
    TSC,	       ///< timeslot should contain a normal burst
 | 
			
		||||
    RACH,	       ///< timeslot should contain an access burst
 | 
			
		||||
    IDLE	       ///< timeslot is an idle (or dummy) burst
 | 
			
		||||
  } CorrType;
 | 
			
		||||
 | 
			
		||||
  /** Codes for channel combinations */
 | 
			
		||||
  typedef enum {
 | 
			
		||||
    FILL,               ///< Channel is transmitted, but unused
 | 
			
		||||
    I,                  ///< TCH/FS
 | 
			
		||||
    II,                 ///< TCH/HS, idle every other slot
 | 
			
		||||
    III,                ///< TCH/HS
 | 
			
		||||
    IV,                 ///< FCCH+SCH+CCCH+BCCH, uplink RACH
 | 
			
		||||
    V,                  ///< FCCH+SCH+CCCH+BCCH+SDCCH/4+SACCH/4, uplink RACH+SDCCH/4
 | 
			
		||||
    VI,                 ///< CCCH+BCCH, uplink RACH
 | 
			
		||||
    VII,                ///< SDCCH/8 + SACCH/8
 | 
			
		||||
    NONE,               ///< Channel is inactive, default
 | 
			
		||||
    LOOPBACK            ///< similar go VII, used in loopback testing
 | 
			
		||||
  } ChannelCombination;
 | 
			
		||||
 | 
			
		||||
  /** Set modulus for specific timeslot */
 | 
			
		||||
  void setModulus(int channel, int timeslot);
 | 
			
		||||
 | 
			
		||||
  /** return the expected burst type for the specified timestamp */
 | 
			
		||||
  CorrType expectedCorrType(int channel, GSM::Time currTime);
 | 
			
		||||
 | 
			
		||||
  void setTimeslot(int m, int timeslot, ChannelCombination comb)
 | 
			
		||||
  {
 | 
			
		||||
    mChanType[m][timeslot] = comb;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  GSM::Time getStartTime() { return mStartTime; }
 | 
			
		||||
  GSM::Time getLastClockUpdate() { return mLastClockUpdateTime; }
 | 
			
		||||
  GSM::Time getDeadlineClock() { return mTransmitDeadlineClock; }
 | 
			
		||||
 | 
			
		||||
  /** send messages over the clock socket */
 | 
			
		||||
  void writeClockInterface(void);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
 | 
			
		||||
  ChannelCombination mChanType[CHAN_MAX][8];     ///< channel types for all timeslots
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
 | 
			
		||||
  /** drive reception and demodulation of GSM bursts */ 
 | 
			
		||||
  void driveReceiveFIFO();
 | 
			
		||||
 | 
			
		||||
  /** drive transmission of GSM bursts */
 | 
			
		||||
  void driveTransmitFIFO();
 | 
			
		||||
 | 
			
		||||
  /** drive handling of control messages from GSM core */
 | 
			
		||||
  void driveControl();
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    drive modulation and sorting of GSM bursts from GSM core
 | 
			
		||||
    @return true if a burst was transferred successfully
 | 
			
		||||
  */
 | 
			
		||||
  bool driveTransmitPriorityQueue();
 | 
			
		||||
 | 
			
		||||
  friend void *RadioDriveLoopAdapter(DriveLoop *);
 | 
			
		||||
 | 
			
		||||
  void reset();
 | 
			
		||||
 | 
			
		||||
  /** return drive loop status */
 | 
			
		||||
  bool on() { return mOn; }
 | 
			
		||||
 | 
			
		||||
  /** set priority on current thread */
 | 
			
		||||
  void setPriority() { mRadioInterface->setPriority(); }
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** FIFO thread loop */
 | 
			
		||||
void *RadioDriveLoopAdapter(DriveLoop *);
 | 
			
		||||
 | 
			
		||||
#endif /* _DRIVELOOP_H_ */
 | 
			
		||||
@@ -21,19 +21,19 @@
 | 
			
		||||
 | 
			
		||||
include $(top_srcdir)/Makefile.common
 | 
			
		||||
 | 
			
		||||
LOAD_TEST_FLAGS = -DTRX_LOAD_TESTING
 | 
			
		||||
AM_CFLAGS = $(STD_DEFINES_AND_INCLUDES) -std=gnu99 -march=native
 | 
			
		||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
 | 
			
		||||
AM_CXXFLAGS = -ldl -lpthread $(LOAD_TEST_FLAGS)
 | 
			
		||||
 | 
			
		||||
#UHD wins if both are defined
 | 
			
		||||
if UHD
 | 
			
		||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(UHD_CFLAGS)
 | 
			
		||||
AM_CPPFLAGS += $(UHD_CFLAGS)
 | 
			
		||||
else
 | 
			
		||||
if USRP1
 | 
			
		||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USRP_CFLAGS)
 | 
			
		||||
else
 | 
			
		||||
#we should never be here, as this doesn't build if one of the above
 | 
			
		||||
#doesn't exist
 | 
			
		||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
 | 
			
		||||
AM_CPPFLAGS += $(USRP_CFLAGS)
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
AM_CXXFLAGS = -ldl -lpthread
 | 
			
		||||
 | 
			
		||||
rev2dir = $(datadir)/usrp/rev2
 | 
			
		||||
rev4dir = $(datadir)/usrp/rev4
 | 
			
		||||
@@ -52,23 +52,17 @@ COMMON_SOURCES = \
 | 
			
		||||
	radioVector.cpp \
 | 
			
		||||
	radioClock.cpp \
 | 
			
		||||
	sigProcLib.cpp \
 | 
			
		||||
	DriveLoop.cpp \
 | 
			
		||||
	Transceiver.cpp \
 | 
			
		||||
	DummyLoad.cpp
 | 
			
		||||
	DummyLoad.cpp \
 | 
			
		||||
	convolve.c \
 | 
			
		||||
	convert.c
 | 
			
		||||
 | 
			
		||||
if RESAMPLE
 | 
			
		||||
libtransceiver_la_SOURCES = \
 | 
			
		||||
	$(COMMON_SOURCES) \
 | 
			
		||||
	radioIOResamp.cpp
 | 
			
		||||
else
 | 
			
		||||
libtransceiver_la_SOURCES = \
 | 
			
		||||
	$(COMMON_SOURCES) \
 | 
			
		||||
	radioIO.cpp
 | 
			
		||||
endif
 | 
			
		||||
	$(COMMON_SOURCES)
 | 
			
		||||
 | 
			
		||||
noinst_PROGRAMS = \
 | 
			
		||||
	USRPping \
 | 
			
		||||
	transceiver \
 | 
			
		||||
	sigProcLibTest 
 | 
			
		||||
	transceiver
 | 
			
		||||
 | 
			
		||||
noinst_HEADERS = \
 | 
			
		||||
	Complex.h \
 | 
			
		||||
@@ -81,37 +75,24 @@ noinst_HEADERS = \
 | 
			
		||||
	USRPDevice.h \
 | 
			
		||||
	DummyLoad.h \
 | 
			
		||||
	rcvLPF_651.h \
 | 
			
		||||
	sendLPF_961.h
 | 
			
		||||
	sendLPF_961.h \
 | 
			
		||||
	convolve.h \
 | 
			
		||||
	convert.h
 | 
			
		||||
 | 
			
		||||
USRPping_SOURCES = USRPping.cpp
 | 
			
		||||
USRPping_LDADD = \
 | 
			
		||||
	libtransceiver.la \
 | 
			
		||||
	$(COMMON_LA) $(SQLITE_LA)
 | 
			
		||||
 | 
			
		||||
transceiver_SOURCES = runTransceiver.cpp
 | 
			
		||||
transceiver_SOURCES = multiTRX.cpp
 | 
			
		||||
transceiver_LDADD = \
 | 
			
		||||
	libtransceiver.la \
 | 
			
		||||
	$(GSM_LA) \
 | 
			
		||||
	$(COMMON_LA) $(SQLITE_LA)
 | 
			
		||||
 | 
			
		||||
sigProcLibTest_SOURCES = sigProcLibTest.cpp
 | 
			
		||||
sigProcLibTest_LDADD = \
 | 
			
		||||
	libtransceiver.la \
 | 
			
		||||
	$(GSM_LA) \
 | 
			
		||||
	$(COMMON_LA) $(SQLITE_LA)
 | 
			
		||||
 | 
			
		||||
#uhd wins
 | 
			
		||||
if UHD
 | 
			
		||||
libtransceiver_la_SOURCES += UHDDevice.cpp
 | 
			
		||||
transceiver_LDADD += $(UHD_LIBS)
 | 
			
		||||
USRPping_LDADD += $(UHD_LIBS)
 | 
			
		||||
sigProcLibTest_LDADD += $(UHD_LIBS)
 | 
			
		||||
else
 | 
			
		||||
if USRP1
 | 
			
		||||
libtransceiver_la_SOURCES += USRPDevice.cpp
 | 
			
		||||
transceiver_LDADD += $(USRP_LIBS)
 | 
			
		||||
USRPping_LDADD += $(USRP_LIBS)
 | 
			
		||||
sigProcLibTest_LDADD += $(USRP_LIBS)
 | 
			
		||||
else
 | 
			
		||||
#we should never be here, as one of the above mustbe defined for us to build
 | 
			
		||||
endif
 | 
			
		||||
@@ -119,10 +100,3 @@ endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MOSTLYCLEANFILES +=
 | 
			
		||||
 | 
			
		||||
#radioInterface.cpp
 | 
			
		||||
#ComplexTest.cpp
 | 
			
		||||
#sigProcLibTest.cpp
 | 
			
		||||
#sweepGenerator.cpp
 | 
			
		||||
#testRadio.cpp
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
			
		||||
* Copyright 2008, 2009, 2010, 2012 Free Software Foundation, Inc.
 | 
			
		||||
*
 | 
			
		||||
* This software is distributed under the terms of the GNU Public License.
 | 
			
		||||
* See the COPYING file in the main directory for details.
 | 
			
		||||
@@ -48,72 +48,56 @@ using namespace GSM;
 | 
			
		||||
 | 
			
		||||
#define INIT_ENERGY_THRSHD		5.0f
 | 
			
		||||
 | 
			
		||||
Transceiver::Transceiver(int wBasePort,
 | 
			
		||||
			 const char *TRXAddress,
 | 
			
		||||
			 int wSamplesPerSymbol,
 | 
			
		||||
			 GSM::Time wTransmitLatency,
 | 
			
		||||
			 RadioInterface *wRadioInterface)
 | 
			
		||||
Transceiver::Transceiver(int wBasePort, const char *TRXAddress,
 | 
			
		||||
			 DriveLoop *wDriveLoop, RadioInterface *wRadioInterface,
 | 
			
		||||
			 int wSPS, int wChannel, bool wPrimary)
 | 
			
		||||
	:mDataSocket(wBasePort+2,TRXAddress,wBasePort+102),
 | 
			
		||||
	 mControlSocket(wBasePort+1,TRXAddress,wBasePort+101),
 | 
			
		||||
	 mClockSocket(wBasePort,TRXAddress,wBasePort+100)
 | 
			
		||||
	 mDriveLoop(wDriveLoop), mRadioInterface(wRadioInterface),
 | 
			
		||||
	 mSPS(wSPS), mTransmitPriorityQueue(NULL),
 | 
			
		||||
	 mChannel(wChannel), mPrimary(wPrimary)
 | 
			
		||||
{
 | 
			
		||||
  //GSM::Time startTime(0,0);
 | 
			
		||||
  //GSM::Time startTime(gHyperframe/2 - 4*216*60,0);
 | 
			
		||||
  GSM::Time startTime(random() % gHyperframe,0);
 | 
			
		||||
 | 
			
		||||
  mFIFOServiceLoopThread = new Thread(32768);  ///< thread to push bursts into transmit FIFO
 | 
			
		||||
  mControlServiceLoopThread = new Thread(32768);       ///< thread to process control messages from GSM core
 | 
			
		||||
  mTransmitPriorityQueueServiceLoopThread = new Thread(32768);///< thread to process transmit bursts from GSM core
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  mSamplesPerSymbol = wSamplesPerSymbol;
 | 
			
		||||
  mRadioInterface = wRadioInterface;
 | 
			
		||||
  mTransmitLatency = wTransmitLatency;
 | 
			
		||||
  mTransmitDeadlineClock = startTime;
 | 
			
		||||
  mLastClockUpdateTime = startTime;
 | 
			
		||||
  mLatencyUpdateTime = startTime;
 | 
			
		||||
  mRadioInterface->getClock()->set(startTime);
 | 
			
		||||
  mFIFOServiceLoopThread = NULL;
 | 
			
		||||
  mControlServiceLoopThread = NULL;
 | 
			
		||||
  mTransmitPriorityQueueServiceLoopThread = NULL;
 | 
			
		||||
#ifndef TRX_LOAD_TESTING
 | 
			
		||||
  mMaxExpectedDelay = 0;
 | 
			
		||||
#else
 | 
			
		||||
  mMaxExpectedDelay = 10;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  // generate pulse and setup up signal processing library
 | 
			
		||||
  gsmPulse = generateGSMPulse(2,mSamplesPerSymbol);
 | 
			
		||||
  LOG(DEBUG) << "gsmPulse: " << *gsmPulse;
 | 
			
		||||
  sigProcLibSetup(mSamplesPerSymbol);
 | 
			
		||||
  mTransmitPriorityQueue = mDriveLoop->priorityQueue(mChannel);
 | 
			
		||||
  mReceiveFIFO = mRadioInterface->receiveFIFO(mChannel);
 | 
			
		||||
 | 
			
		||||
  txFullScale = mRadioInterface->fullScaleInputValue();
 | 
			
		||||
  rxFullScale = mRadioInterface->fullScaleOutputValue();
 | 
			
		||||
 | 
			
		||||
  // initialize filler tables with dummy bursts, initialize other per-timeslot variables
 | 
			
		||||
  // initialize per-timeslot variables
 | 
			
		||||
  for (int i = 0; i < 8; i++) {
 | 
			
		||||
    signalVector* modBurst = modulateBurst(gDummyBurst,*gsmPulse,
 | 
			
		||||
					   8 + (i % 4 == 0),
 | 
			
		||||
					   mSamplesPerSymbol);
 | 
			
		||||
    scaleVector(*modBurst,txFullScale);
 | 
			
		||||
    fillerModulus[i]=26;
 | 
			
		||||
    for (int j = 0; j < 102; j++) {
 | 
			
		||||
      fillerTable[j][i] = new signalVector(*modBurst);
 | 
			
		||||
    }
 | 
			
		||||
    delete modBurst;
 | 
			
		||||
    mChanType[i] = NONE;
 | 
			
		||||
    channelResponse[i] = NULL;
 | 
			
		||||
    DFEForward[i] = NULL;
 | 
			
		||||
    DFEFeedback[i] = NULL;
 | 
			
		||||
    channelEstimateTime[i] = startTime;
 | 
			
		||||
    channelEstimateTime[i] = mDriveLoop->getStartTime();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  mOn = false;
 | 
			
		||||
  mRunning = false;
 | 
			
		||||
  mTxFreq = 0.0;
 | 
			
		||||
  mRxFreq = 0.0;
 | 
			
		||||
  mFreqOffset = 0.0;
 | 
			
		||||
 | 
			
		||||
  mPower = -10;
 | 
			
		||||
  mEnergyThreshold = INIT_ENERGY_THRSHD;
 | 
			
		||||
  prevFalseDetectionTime = startTime;
 | 
			
		||||
  prevFalseDetectionTime = mDriveLoop->getStartTime();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Transceiver::~Transceiver()
 | 
			
		||||
{
 | 
			
		||||
  delete gsmPulse;
 | 
			
		||||
  sigProcLibDestroy();
 | 
			
		||||
  mTransmitPriorityQueue.clear();
 | 
			
		||||
  mTransmitPriorityQueue->clear();
 | 
			
		||||
 | 
			
		||||
  delete mFIFOServiceLoopThread;
 | 
			
		||||
  delete mControlServiceLoopThread;
 | 
			
		||||
  delete mTransmitPriorityQueueServiceLoopThread;
 | 
			
		||||
}
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
@@ -122,187 +106,26 @@ void Transceiver::addRadioVector(BitVector &burst,
 | 
			
		||||
				 GSM::Time &wTime)
 | 
			
		||||
{
 | 
			
		||||
  // modulate and stick into queue 
 | 
			
		||||
  signalVector* modBurst = modulateBurst(burst,*gsmPulse,
 | 
			
		||||
					 8 + (wTime.TN() % 4 == 0),
 | 
			
		||||
					 mSamplesPerSymbol);
 | 
			
		||||
  signalVector* modBurst = modulateBurst(burst,
 | 
			
		||||
                                         8 + (wTime.TN() % 4 == 0),
 | 
			
		||||
                                         mSPS);
 | 
			
		||||
  scaleVector(*modBurst,txFullScale * pow(10,-RSSI/10));
 | 
			
		||||
  radioVector *newVec = new radioVector(*modBurst,wTime);
 | 
			
		||||
  mTransmitPriorityQueue.write(newVec);
 | 
			
		||||
  mTransmitPriorityQueue->write(newVec);
 | 
			
		||||
 | 
			
		||||
  delete modBurst;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef TRANSMIT_LOGGING
 | 
			
		||||
void Transceiver::unModulateVector(signalVector wVector) 
 | 
			
		||||
{
 | 
			
		||||
  SoftVector *burst = demodulateBurst(wVector,
 | 
			
		||||
				   *gsmPulse,
 | 
			
		||||
				   mSamplesPerSymbol,
 | 
			
		||||
				   1.0,0.0);
 | 
			
		||||
  LOG(DEBUG) << "LOGGED BURST: " << *burst;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
  unsigned char burstStr[gSlotLen+1];
 | 
			
		||||
  SoftVector::iterator burstItr = burst->begin();
 | 
			
		||||
  for (int i = 0; i < gSlotLen; i++) {
 | 
			
		||||
    // FIXME: Demod bits are inverted!
 | 
			
		||||
    burstStr[i] = (unsigned char) ((*burstItr++)*255.0);
 | 
			
		||||
  }
 | 
			
		||||
  burstStr[gSlotLen]='\0';
 | 
			
		||||
  LOG(DEBUG) << "LOGGED BURST: " << burstStr;
 | 
			
		||||
*/
 | 
			
		||||
  delete burst;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void Transceiver::pushRadioVector(GSM::Time &nowTime)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  // dump stale bursts, if any
 | 
			
		||||
  while (radioVector* staleBurst = mTransmitPriorityQueue.getStaleBurst(nowTime)) {
 | 
			
		||||
    // Even if the burst is stale, put it in the fillter table.
 | 
			
		||||
    // (It might be an idle pattern.)
 | 
			
		||||
    LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface";
 | 
			
		||||
    const GSM::Time& nextTime = staleBurst->getTime();
 | 
			
		||||
    int TN = nextTime.TN();
 | 
			
		||||
    int modFN = nextTime.FN() % fillerModulus[TN];
 | 
			
		||||
    delete fillerTable[modFN][TN];
 | 
			
		||||
    fillerTable[modFN][TN] = staleBurst;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  int TN = nowTime.TN();
 | 
			
		||||
  int modFN = nowTime.FN() % fillerModulus[nowTime.TN()];
 | 
			
		||||
 | 
			
		||||
  // if queue contains data at the desired timestamp, stick it into FIFO
 | 
			
		||||
  if (radioVector *next = (radioVector*) mTransmitPriorityQueue.getCurrentBurst(nowTime)) {
 | 
			
		||||
    LOG(DEBUG) << "transmitFIFO: wrote burst " << next << " at time: " << nowTime;
 | 
			
		||||
    delete fillerTable[modFN][TN];
 | 
			
		||||
    fillerTable[modFN][TN] = new signalVector(*(next));
 | 
			
		||||
    mRadioInterface->driveTransmitRadio(*(next),(mChanType[TN]==NONE)); //fillerTable[modFN][TN]));
 | 
			
		||||
    delete next;
 | 
			
		||||
#ifdef TRANSMIT_LOGGING
 | 
			
		||||
    if (nowTime.TN()==TRANSMIT_LOGGING) { 
 | 
			
		||||
      unModulateVector(*(fillerTable[modFN][TN]));
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // otherwise, pull filler data, and push to radio FIFO
 | 
			
		||||
  mRadioInterface->driveTransmitRadio(*(fillerTable[modFN][TN]),(mChanType[TN]==NONE));
 | 
			
		||||
#ifdef TRANSMIT_LOGGING
 | 
			
		||||
  if (nowTime.TN()==TRANSMIT_LOGGING) 
 | 
			
		||||
    unModulateVector(*fillerTable[modFN][TN]);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Transceiver::setModulus(int timeslot)
 | 
			
		||||
{
 | 
			
		||||
  switch (mChanType[timeslot]) {
 | 
			
		||||
  case NONE:
 | 
			
		||||
  case I:
 | 
			
		||||
  case II:
 | 
			
		||||
  case III:
 | 
			
		||||
  case FILL:
 | 
			
		||||
    fillerModulus[timeslot] = 26;
 | 
			
		||||
    break;
 | 
			
		||||
  case IV:
 | 
			
		||||
  case VI:
 | 
			
		||||
  case V:
 | 
			
		||||
    fillerModulus[timeslot] = 51;
 | 
			
		||||
    break;
 | 
			
		||||
    //case V: 
 | 
			
		||||
  case VII:
 | 
			
		||||
    fillerModulus[timeslot] = 102;
 | 
			
		||||
    break;
 | 
			
		||||
  case XIII:
 | 
			
		||||
    fillerModulus[timeslot] = 52;
 | 
			
		||||
    break;
 | 
			
		||||
  default:
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime)
 | 
			
		||||
{
 | 
			
		||||
  
 | 
			
		||||
  unsigned burstTN = currTime.TN();
 | 
			
		||||
  unsigned burstFN = currTime.FN();
 | 
			
		||||
 | 
			
		||||
  switch (mChanType[burstTN]) {
 | 
			
		||||
  case NONE:
 | 
			
		||||
    return OFF;
 | 
			
		||||
    break;
 | 
			
		||||
  case FILL:
 | 
			
		||||
    return IDLE;
 | 
			
		||||
    break;
 | 
			
		||||
  case I:
 | 
			
		||||
    return TSC;
 | 
			
		||||
    /*if (burstFN % 26 == 25) 
 | 
			
		||||
      return IDLE;
 | 
			
		||||
    else
 | 
			
		||||
      return TSC;*/
 | 
			
		||||
    break;
 | 
			
		||||
  case II:
 | 
			
		||||
    return TSC;
 | 
			
		||||
    break;
 | 
			
		||||
  case III:
 | 
			
		||||
    return TSC;
 | 
			
		||||
    break;
 | 
			
		||||
  case IV:
 | 
			
		||||
  case VI:
 | 
			
		||||
    return RACH;
 | 
			
		||||
    break;
 | 
			
		||||
  case V: {
 | 
			
		||||
    int mod51 = burstFN % 51;
 | 
			
		||||
    if ((mod51 <= 36) && (mod51 >= 14))
 | 
			
		||||
      return RACH;
 | 
			
		||||
    else if ((mod51 == 4) || (mod51 == 5))
 | 
			
		||||
      return RACH;
 | 
			
		||||
    else if ((mod51 == 45) || (mod51 == 46))
 | 
			
		||||
      return RACH;
 | 
			
		||||
    else
 | 
			
		||||
      return TSC;
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case VII:
 | 
			
		||||
    if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
 | 
			
		||||
      return IDLE;
 | 
			
		||||
    else
 | 
			
		||||
      return TSC;
 | 
			
		||||
    break;
 | 
			
		||||
  case XIII: {
 | 
			
		||||
    int mod52 = burstFN % 52;
 | 
			
		||||
    if ((mod52 == 12) || (mod52 == 38))
 | 
			
		||||
      return RACH;
 | 
			
		||||
    else if ((mod52 == 25) || (mod52 == 51))
 | 
			
		||||
      return IDLE;
 | 
			
		||||
    else
 | 
			
		||||
      return TSC;
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case LOOPBACK:
 | 
			
		||||
    if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
 | 
			
		||||
      return IDLE;
 | 
			
		||||
    else
 | 
			
		||||
      return TSC;
 | 
			
		||||
    break;
 | 
			
		||||
  default:
 | 
			
		||||
    return OFF;
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
    
 | 
			
		||||
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
 | 
			
		||||
				      int &RSSI,
 | 
			
		||||
				      int &timingOffset)
 | 
			
		||||
{
 | 
			
		||||
  bool needDFE = (mMaxExpectedDelay > 1);
 | 
			
		||||
  bool needDFE = false;
 | 
			
		||||
 | 
			
		||||
  radioVector *rxBurst = (radioVector *) mReceiveFIFO->get();
 | 
			
		||||
  if ((mSPS == 1) && (mMaxExpectedDelay > 1))
 | 
			
		||||
    needDFE = true;
 | 
			
		||||
 | 
			
		||||
  radioVector *rxBurst = (radioVector *) mReceiveFIFO->read();
 | 
			
		||||
 | 
			
		||||
  if (!rxBurst) return NULL;
 | 
			
		||||
 | 
			
		||||
@@ -310,19 +133,22 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
 | 
			
		||||
 | 
			
		||||
  int timeslot = rxBurst->getTime().TN();
 | 
			
		||||
 | 
			
		||||
  CorrType corrType = expectedCorrType(rxBurst->getTime());
 | 
			
		||||
  DriveLoop::CorrType corrType = mDriveLoop->expectedCorrType(mChannel, rxBurst->getTime());
 | 
			
		||||
 | 
			
		||||
  if ((corrType==OFF) || (corrType==IDLE)) {
 | 
			
		||||
#ifndef TRX_LOAD_TESTING
 | 
			
		||||
  if ((corrType == DriveLoop::OFF) || (corrType == DriveLoop::IDLE)) {
 | 
			
		||||
    delete rxBurst;
 | 
			
		||||
    return NULL;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  // check to see if received burst has sufficient 
 | 
			
		||||
  signalVector *vectorBurst = rxBurst;
 | 
			
		||||
  complex amplitude = 0.0;
 | 
			
		||||
  float TOA = 0.0;
 | 
			
		||||
  float avgPwr = 0.0;
 | 
			
		||||
  if (!energyDetect(*vectorBurst,20*mSamplesPerSymbol,mEnergyThreshold,&avgPwr)) {
 | 
			
		||||
#ifdef ENERGY_DETECT
 | 
			
		||||
  if (!energyDetect(*vectorBurst,20*mSPS,mEnergyThreshold,&avgPwr)) {
 | 
			
		||||
     LOG(DEBUG) << "Estimated Energy: " << sqrt(avgPwr) << ", at time " << rxBurst->getTime();
 | 
			
		||||
     double framesElapsed = rxBurst->getTime()-prevFalseDetectionTime;
 | 
			
		||||
     if (framesElapsed > 50) {  // if we haven't had any false detections for a while, lower threshold
 | 
			
		||||
@@ -332,15 +158,18 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
 | 
			
		||||
 | 
			
		||||
        prevFalseDetectionTime = rxBurst->getTime();
 | 
			
		||||
     }
 | 
			
		||||
#ifndef TRX_LOAD_TESTING
 | 
			
		||||
     delete rxBurst;
 | 
			
		||||
     return NULL;
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
  LOG(DEBUG) << "Estimated Energy: " << sqrt(avgPwr) << ", at time " << rxBurst->getTime();
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
  // run the proper correlator
 | 
			
		||||
  bool success = false;
 | 
			
		||||
  if (corrType==TSC) {
 | 
			
		||||
  if (corrType == DriveLoop::TSC) {
 | 
			
		||||
    LOG(DEBUG) << "looking for TSC at time: " << rxBurst->getTime();
 | 
			
		||||
 | 
			
		||||
    signalVector *channelResp;
 | 
			
		||||
    double framesElapsed = rxBurst->getTime()-channelEstimateTime[timeslot];
 | 
			
		||||
    bool estimateChannel = false;
 | 
			
		||||
@@ -358,13 +187,16 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
 | 
			
		||||
    success = analyzeTrafficBurst(*vectorBurst,
 | 
			
		||||
				  mTSC,
 | 
			
		||||
				  3.0,
 | 
			
		||||
				  mSamplesPerSymbol,
 | 
			
		||||
				  mSPS,
 | 
			
		||||
				  &litude,
 | 
			
		||||
				  &TOA,
 | 
			
		||||
				  mMaxExpectedDelay, 
 | 
			
		||||
				  estimateChannel,
 | 
			
		||||
				  &channelResp,
 | 
			
		||||
				  &chanOffset);
 | 
			
		||||
#ifdef TRX_LOAD_TESTING
 | 
			
		||||
    success = true;
 | 
			
		||||
#endif
 | 
			
		||||
    if (success) {
 | 
			
		||||
      LOG(DEBUG) << "FOUND TSC!!!!!! " << amplitude << " " << TOA;
 | 
			
		||||
      mEnergyThreshold -= 1.0F/10.0F;
 | 
			
		||||
@@ -393,9 +225,12 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
 | 
			
		||||
    // RACH burst
 | 
			
		||||
    success = detectRACHBurst(*vectorBurst,
 | 
			
		||||
			      5.0,  // detection threshold
 | 
			
		||||
			      mSamplesPerSymbol,
 | 
			
		||||
			      mSPS,
 | 
			
		||||
			      &litude,
 | 
			
		||||
			      &TOA);
 | 
			
		||||
#ifdef TRX_LOAD_TESTING
 | 
			
		||||
    success = true;
 | 
			
		||||
#endif
 | 
			
		||||
    if (success) {
 | 
			
		||||
      LOG(DEBUG) << "FOUND RACH!!!!!! " << amplitude << " " << TOA;
 | 
			
		||||
      mEnergyThreshold -= (1.0F/10.0F);
 | 
			
		||||
@@ -413,24 +248,23 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
 | 
			
		||||
  // demodulate burst
 | 
			
		||||
  SoftVector *burst = NULL;
 | 
			
		||||
  if ((rxBurst) && (success)) {
 | 
			
		||||
    if ((corrType==RACH) || (!needDFE)) {
 | 
			
		||||
    if ((corrType == DriveLoop::RACH) || (!needDFE)) {
 | 
			
		||||
      burst = demodulateBurst(*vectorBurst,
 | 
			
		||||
			      *gsmPulse,
 | 
			
		||||
			      mSamplesPerSymbol,
 | 
			
		||||
			      mSPS,
 | 
			
		||||
			      amplitude,TOA);
 | 
			
		||||
    }
 | 
			
		||||
    else { // TSC
 | 
			
		||||
      scaleVector(*vectorBurst,complex(1.0,0.0)/amplitude);
 | 
			
		||||
      burst = equalizeBurst(*vectorBurst,
 | 
			
		||||
			    TOA-chanRespOffset[timeslot],
 | 
			
		||||
			    mSamplesPerSymbol,
 | 
			
		||||
			    mSPS,
 | 
			
		||||
			    *DFEForward[timeslot],
 | 
			
		||||
			    *DFEFeedback[timeslot]);
 | 
			
		||||
    }
 | 
			
		||||
    wTime = rxBurst->getTime();
 | 
			
		||||
    RSSI = (int) floor(20.0*log10(rxFullScale/amplitude.abs()));
 | 
			
		||||
    LOG(DEBUG) << "RSSI: " << RSSI;
 | 
			
		||||
    timingOffset = (int) round(TOA*256.0/mSamplesPerSymbol);
 | 
			
		||||
    timingOffset = (int) round(TOA*256.0/mSPS);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //if (burst) LOG(DEBUG) << "burst: " << *burst << '\n';
 | 
			
		||||
@@ -440,16 +274,69 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
 | 
			
		||||
  return burst;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Transceiver::pullFIFO()
 | 
			
		||||
{
 | 
			
		||||
  SoftVector *rxBurst = NULL;
 | 
			
		||||
  int RSSI;
 | 
			
		||||
  int TOA;  // in 1/256 of a symbol
 | 
			
		||||
  GSM::Time burstTime;
 | 
			
		||||
 | 
			
		||||
  rxBurst = pullRadioVector(burstTime,RSSI,TOA);
 | 
			
		||||
 | 
			
		||||
  if (rxBurst) {
 | 
			
		||||
    LOG(DEBUG) << "burst parameters: "
 | 
			
		||||
               << " time: " << burstTime
 | 
			
		||||
               << " RSSI: " << RSSI
 | 
			
		||||
               << " TOA: "  << TOA
 | 
			
		||||
               << " bits: " << *rxBurst;
 | 
			
		||||
 | 
			
		||||
    char burstString[gSlotLen+10];
 | 
			
		||||
    burstString[0] = burstTime.TN();
 | 
			
		||||
    for (int i = 0; i < 4; i++) {
 | 
			
		||||
            burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    burstString[5] = RSSI;
 | 
			
		||||
    burstString[6] = (TOA >> 8) & 0x0ff;
 | 
			
		||||
    burstString[7] = TOA & 0x0ff;
 | 
			
		||||
    SoftVector::iterator burstItr = rxBurst->begin();
 | 
			
		||||
 | 
			
		||||
    for (unsigned int i = 0; i < gSlotLen; i++) {
 | 
			
		||||
            burstString[8+i] =(char) round((*burstItr++)*255.0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    burstString[gSlotLen+9] = '\0';
 | 
			
		||||
    delete rxBurst;
 | 
			
		||||
 | 
			
		||||
    mDataSocket.write(burstString,gSlotLen+10);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Transceiver::start()
 | 
			
		||||
{
 | 
			
		||||
  mRunning = true;
 | 
			
		||||
  mControlServiceLoopThread = new Thread(32768);
 | 
			
		||||
  mControlServiceLoopThread->start((void * (*)(void*))ControlServiceLoopAdapter,(void*) this);
 | 
			
		||||
 | 
			
		||||
  if (!mPrimary) {
 | 
			
		||||
    mOn = true;
 | 
			
		||||
    mFIFOServiceLoopThread = new Thread(32768);
 | 
			
		||||
    mFIFOServiceLoopThread->start((void * (*)(void*))FIFOServiceLoopAdapter,(void*) this);
 | 
			
		||||
 
 | 
			
		||||
    mTransmitPriorityQueueServiceLoopThread = new Thread(32768);
 | 
			
		||||
    mTransmitPriorityQueueServiceLoopThread->start((void * (*)(void*))TransmitPriorityQueueServiceLoopAdapter,(void*) this);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Transceiver::shutdown()
 | 
			
		||||
{
 | 
			
		||||
  mOn = false;
 | 
			
		||||
  mRunning = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Transceiver::reset()
 | 
			
		||||
{
 | 
			
		||||
  mTransmitPriorityQueue.clear();
 | 
			
		||||
  //mTransmitFIFO->clear();
 | 
			
		||||
  //mReceiveFIFO->clear();
 | 
			
		||||
  mTransmitPriorityQueue->clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  
 | 
			
		||||
@@ -462,10 +349,19 @@ void Transceiver::driveControl()
 | 
			
		||||
  char buffer[MAX_PACKET_LENGTH];
 | 
			
		||||
  int msgLen = -1;
 | 
			
		||||
  buffer[0] = '\0';
 | 
			
		||||
 
 | 
			
		||||
  msgLen = mControlSocket.read(buffer);
 | 
			
		||||
 | 
			
		||||
  if (msgLen < 1) {
 | 
			
		||||
  try { 
 | 
			
		||||
    msgLen = mControlSocket.read(buffer);
 | 
			
		||||
    if (msgLen < 1) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  } catch (...) {
 | 
			
		||||
    /* Ignore the read exception on shutdown */
 | 
			
		||||
    if (!mRunning) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LOG(ALERT) << "Caught UHD socket exception";
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -474,8 +370,8 @@ void Transceiver::driveControl()
 | 
			
		||||
  char response[MAX_PACKET_LENGTH];
 | 
			
		||||
 | 
			
		||||
  sscanf(buffer,"%3s %s",cmdcheck,command);
 | 
			
		||||
 
 | 
			
		||||
  writeClockInterface();
 | 
			
		||||
 | 
			
		||||
  mDriveLoop->writeClockInterface();
 | 
			
		||||
 | 
			
		||||
  if (strcmp(cmdcheck,"CMD")!=0) {
 | 
			
		||||
    LOG(WARNING) << "bogus message on control interface";
 | 
			
		||||
@@ -489,22 +385,24 @@ void Transceiver::driveControl()
 | 
			
		||||
  }
 | 
			
		||||
  else if (strcmp(command,"POWERON")==0) {
 | 
			
		||||
    // turn on transmitter/demod
 | 
			
		||||
    if (!mTxFreq || !mRxFreq) 
 | 
			
		||||
    if (!mTxFreq || !mRxFreq || (mTSC<0))
 | 
			
		||||
      sprintf(response,"RSP POWERON 1");
 | 
			
		||||
    else {
 | 
			
		||||
      sprintf(response,"RSP POWERON 0");
 | 
			
		||||
      if (!mOn) {
 | 
			
		||||
      if (mPrimary && !mOn) {
 | 
			
		||||
        // Prepare for thread start
 | 
			
		||||
        mPower = -20;
 | 
			
		||||
        mRadioInterface->start();
 | 
			
		||||
        generateRACHSequence(*gsmPulse,mSamplesPerSymbol);
 | 
			
		||||
        mDriveLoop->start();
 | 
			
		||||
        mDriveLoop->writeClockInterface();
 | 
			
		||||
 | 
			
		||||
        // Start radio interface threads.
 | 
			
		||||
        mFIFOServiceLoopThread->start((void * (*)(void*))FIFOServiceLoopAdapter,(void*) this);
 | 
			
		||||
        mTransmitPriorityQueueServiceLoopThread->start((void * (*)(void*))TransmitPriorityQueueServiceLoopAdapter,(void*) this);
 | 
			
		||||
        writeClockInterface();
 | 
			
		||||
 | 
			
		||||
        mOn = true;
 | 
			
		||||
        mFIFOServiceLoopThread = new Thread(32768);
 | 
			
		||||
        mFIFOServiceLoopThread->start((void * (*)(void*))FIFOServiceLoopAdapter,(void*) this);
 | 
			
		||||
 | 
			
		||||
        mTransmitPriorityQueueServiceLoopThread = new Thread(32768);
 | 
			
		||||
        mTransmitPriorityQueueServiceLoopThread->start((void * (*)(void*))TransmitPriorityQueueServiceLoopAdapter,(void*) this);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@@ -512,15 +410,17 @@ void Transceiver::driveControl()
 | 
			
		||||
    //set expected maximum time-of-arrival
 | 
			
		||||
    int maxDelay;
 | 
			
		||||
    sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
 | 
			
		||||
#ifndef TRX_LOAD_TESTING
 | 
			
		||||
    mMaxExpectedDelay = maxDelay; // 1 GSM symbol is approx. 1 km
 | 
			
		||||
#endif
 | 
			
		||||
    sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
 | 
			
		||||
  }
 | 
			
		||||
  else if (strcmp(command,"SETRXGAIN")==0) {
 | 
			
		||||
    //set expected maximum time-of-arrival
 | 
			
		||||
    int newGain;
 | 
			
		||||
    sscanf(buffer,"%3s %s %d",cmdcheck,command,&newGain);
 | 
			
		||||
    newGain = mRadioInterface->setRxGain(newGain);
 | 
			
		||||
    mEnergyThreshold = INIT_ENERGY_THRSHD;
 | 
			
		||||
    newGain = mRadioInterface->setRxGain(newGain, mChannel);
 | 
			
		||||
    sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
 | 
			
		||||
  }
 | 
			
		||||
  else if (strcmp(command,"NOISELEV")==0) {
 | 
			
		||||
@@ -540,7 +440,7 @@ void Transceiver::driveControl()
 | 
			
		||||
      sprintf(response,"RSP SETPOWER 1 %d",dbPwr);
 | 
			
		||||
    else {
 | 
			
		||||
      mPower = dbPwr;
 | 
			
		||||
      mRadioInterface->setPowerAttenuation(dbPwr);
 | 
			
		||||
      mRadioInterface->setPowerAttenuation(dbPwr, mChannel);
 | 
			
		||||
      sprintf(response,"RSP SETPOWER 0 %d",dbPwr);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@@ -555,46 +455,45 @@ void Transceiver::driveControl()
 | 
			
		||||
      sprintf(response,"RSP ADJPOWER 0 %d",mPower);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
#define FREQOFFSET 0//11.2e3
 | 
			
		||||
  else if (strcmp(command,"RXTUNE")==0) {
 | 
			
		||||
    // tune receiver
 | 
			
		||||
    int freqKhz;
 | 
			
		||||
    sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
 | 
			
		||||
    mRxFreq = freqKhz*1.0e3+FREQOFFSET;
 | 
			
		||||
    if (!mRadioInterface->tuneRx(mRxFreq)) {
 | 
			
		||||
       LOG(ALERT) << "RX failed to tune";
 | 
			
		||||
       sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
 | 
			
		||||
    mRxFreq = freqKhz * 1.0e3 + mFreqOffset;
 | 
			
		||||
    if (!mRadioInterface->tuneRx(mRxFreq, mChannel)) {
 | 
			
		||||
      LOG(ALERT) << "RX failed to tune";
 | 
			
		||||
      sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
 | 
			
		||||
    } else {
 | 
			
		||||
      sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
       sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
 | 
			
		||||
  }
 | 
			
		||||
  else if (strcmp(command,"TXTUNE")==0) {
 | 
			
		||||
    // tune txmtr
 | 
			
		||||
    int freqKhz;
 | 
			
		||||
    sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
 | 
			
		||||
    //freqKhz = 890e3;
 | 
			
		||||
    mTxFreq = freqKhz*1.0e3+FREQOFFSET;
 | 
			
		||||
    if (!mRadioInterface->tuneTx(mTxFreq)) {
 | 
			
		||||
       LOG(ALERT) << "TX failed to tune";
 | 
			
		||||
       sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
 | 
			
		||||
    mTxFreq = freqKhz * 1.0e3 + mFreqOffset;
 | 
			
		||||
    if (!mRadioInterface->tuneTx(mTxFreq, mChannel)) {
 | 
			
		||||
      LOG(ALERT) << "TX failed to tune";
 | 
			
		||||
      sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
 | 
			
		||||
    } else {
 | 
			
		||||
      sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
       sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
 | 
			
		||||
  }
 | 
			
		||||
  else if (strcmp(command,"SETTSC")==0) {
 | 
			
		||||
    // set TSC
 | 
			
		||||
    int TSC;
 | 
			
		||||
    sscanf(buffer,"%3s %s %d",cmdcheck,command,&TSC);
 | 
			
		||||
    if (mOn)
 | 
			
		||||
    if (mOn || (TSC<0) || (TSC>7))
 | 
			
		||||
      sprintf(response,"RSP SETTSC 1 %d",TSC);
 | 
			
		||||
    else {
 | 
			
		||||
      mTSC = TSC;
 | 
			
		||||
      generateMidamble(*gsmPulse,mSamplesPerSymbol,TSC);
 | 
			
		||||
      generateMidamble(mSPS, TSC);
 | 
			
		||||
      sprintf(response,"RSP SETTSC 0 %d",TSC);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  else if (strcmp(command,"SETSLOT")==0) {
 | 
			
		||||
    // set TSC 
 | 
			
		||||
    // set slot type
 | 
			
		||||
    int  corrCode;
 | 
			
		||||
    int  timeslot;
 | 
			
		||||
    sscanf(buffer,"%3s %s %d %d",cmdcheck,command,×lot,&corrCode);
 | 
			
		||||
@@ -603,13 +502,14 @@ void Transceiver::driveControl()
 | 
			
		||||
      sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
 | 
			
		||||
      return;
 | 
			
		||||
    }     
 | 
			
		||||
    mChanType[timeslot] = (ChannelCombination) corrCode;
 | 
			
		||||
    setModulus(timeslot);
 | 
			
		||||
    mDriveLoop->setTimeslot(mChannel, timeslot, (DriveLoop::ChannelCombination) corrCode);
 | 
			
		||||
    mDriveLoop->setModulus(mChannel, timeslot);
 | 
			
		||||
    sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    LOG(WARNING) << "bogus command " << command << " on control interface.";
 | 
			
		||||
    sprintf(response,"RSP ERR 1");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  mControlSocket.write(response,strlen(response)+1);
 | 
			
		||||
@@ -618,14 +518,24 @@ void Transceiver::driveControl()
 | 
			
		||||
 | 
			
		||||
bool Transceiver::driveTransmitPriorityQueue() 
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  char buffer[gSlotLen+50];
 | 
			
		||||
 | 
			
		||||
  // check data socket
 | 
			
		||||
  size_t msgLen = mDataSocket.read(buffer);
 | 
			
		||||
  if (!mOn)
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
  if (msgLen!=gSlotLen+1+4+1) {
 | 
			
		||||
    LOG(ERR) << "badly formatted packet on GSM->TRX interface";
 | 
			
		||||
  try { 
 | 
			
		||||
    size_t msgLen = mDataSocket.read(buffer);
 | 
			
		||||
    if (msgLen!=gSlotLen+1+4+1) {
 | 
			
		||||
      LOG(ERR) << "badly formatted packet on GSM->TRX interface";
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  } catch (...) {
 | 
			
		||||
    if (!mOn) {
 | 
			
		||||
      /* Shutdown condition. End the thread. */
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LOG(ALERT) << "Caught UHD socket exception";
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -634,28 +544,12 @@ bool Transceiver::driveTransmitPriorityQueue()
 | 
			
		||||
  for (int i = 0; i < 4; i++)
 | 
			
		||||
    frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
 | 
			
		||||
  
 | 
			
		||||
  /*
 | 
			
		||||
  if (GSM::Time(frameNum,timeSlot) >  mTransmitDeadlineClock + GSM::Time(51,0)) {
 | 
			
		||||
    // stale burst
 | 
			
		||||
    //LOG(DEBUG) << "FAST! "<< GSM::Time(frameNum,timeSlot);
 | 
			
		||||
    //writeClockInterface();
 | 
			
		||||
    }*/
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
  DAB -- Just let these go through the demod.
 | 
			
		||||
  if (GSM::Time(frameNum,timeSlot) < mTransmitDeadlineClock) {
 | 
			
		||||
    // stale burst from GSM core
 | 
			
		||||
    LOG(NOTICE) << "STALE packet on GSM->TRX interface at time "<< GSM::Time(frameNum,timeSlot);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
*/
 | 
			
		||||
  
 | 
			
		||||
  // periodically update GSM core clock
 | 
			
		||||
  LOG(DEBUG) << "mTransmitDeadlineClock " << mTransmitDeadlineClock
 | 
			
		||||
		<< " mLastClockUpdateTime " << mLastClockUpdateTime;
 | 
			
		||||
  if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
 | 
			
		||||
    writeClockInterface();
 | 
			
		||||
 | 
			
		||||
  LOG(DEBUG) << "mTransmitDeadlineClock " << mDriveLoop->getDeadlineClock()
 | 
			
		||||
             << " mLastClockUpdateTime " << mDriveLoop->getLastClockUpdate();
 | 
			
		||||
  if (mDriveLoop->getDeadlineClock() > mDriveLoop->getLastClockUpdate() + GSM::Time(216,0)) {
 | 
			
		||||
    mDriveLoop->writeClockInterface();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
 | 
			
		||||
  
 | 
			
		||||
@@ -676,127 +570,11 @@ bool Transceiver::driveTransmitPriorityQueue()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
void Transceiver::driveReceiveFIFO() 
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  SoftVector *rxBurst = NULL;
 | 
			
		||||
  int RSSI;
 | 
			
		||||
  int TOA;  // in 1/256 of a symbol
 | 
			
		||||
  GSM::Time burstTime;
 | 
			
		||||
 | 
			
		||||
  mRadioInterface->driveReceiveRadio();
 | 
			
		||||
 | 
			
		||||
  rxBurst = pullRadioVector(burstTime,RSSI,TOA);
 | 
			
		||||
 | 
			
		||||
  if (rxBurst) { 
 | 
			
		||||
 | 
			
		||||
    LOG(DEBUG) << "burst parameters: "
 | 
			
		||||
	  << " time: " << burstTime
 | 
			
		||||
	  << " RSSI: " << RSSI
 | 
			
		||||
	  << " TOA: "  << TOA
 | 
			
		||||
	  << " bits: " << *rxBurst;
 | 
			
		||||
    
 | 
			
		||||
    char burstString[gSlotLen+10];
 | 
			
		||||
    burstString[0] = burstTime.TN();
 | 
			
		||||
    for (int i = 0; i < 4; i++)
 | 
			
		||||
      burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
 | 
			
		||||
    burstString[5] = RSSI;
 | 
			
		||||
    burstString[6] = (TOA >> 8) & 0x0ff;
 | 
			
		||||
    burstString[7] = TOA & 0x0ff;
 | 
			
		||||
    SoftVector::iterator burstItr = rxBurst->begin();
 | 
			
		||||
 | 
			
		||||
    for (unsigned int i = 0; i < gSlotLen; i++) {
 | 
			
		||||
      burstString[8+i] =(char) round((*burstItr++)*255.0);
 | 
			
		||||
    }
 | 
			
		||||
    burstString[gSlotLen+9] = '\0';
 | 
			
		||||
    delete rxBurst;
 | 
			
		||||
 | 
			
		||||
    mDataSocket.write(burstString,gSlotLen+10);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Transceiver::driveTransmitFIFO() 
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
      Features a carefully controlled latency mechanism, to 
 | 
			
		||||
      assure that transmit packets arrive at the radio/USRP
 | 
			
		||||
      before they need to be transmitted.
 | 
			
		||||
 | 
			
		||||
      Deadline clock indicates the burst that needs to be
 | 
			
		||||
      pushed into the FIFO right NOW.  If transmit queue does
 | 
			
		||||
      not have a burst, stick in filler data.
 | 
			
		||||
  */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  RadioClock *radioClock = (mRadioInterface->getClock());
 | 
			
		||||
  
 | 
			
		||||
  if (mOn) {
 | 
			
		||||
    //radioClock->wait(); // wait until clock updates
 | 
			
		||||
    LOG(DEBUG) << "radio clock " << radioClock->get();
 | 
			
		||||
    while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
 | 
			
		||||
      // if underrun, then we're not providing bursts to radio/USRP fast
 | 
			
		||||
      //   enough.  Need to increase latency by one GSM frame.
 | 
			
		||||
      if (mRadioInterface->getBus() == RadioDevice::USB) {
 | 
			
		||||
        if (mRadioInterface->isUnderrun()) {
 | 
			
		||||
          // only update latency at the defined frame interval
 | 
			
		||||
          if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
 | 
			
		||||
            mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
 | 
			
		||||
            LOG(INFO) << "new latency: " << mTransmitLatency;
 | 
			
		||||
            mLatencyUpdateTime = radioClock->get();
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
          // if underrun hasn't occurred in the last sec (216 frames) drop
 | 
			
		||||
          //    transmit latency by a timeslot
 | 
			
		||||
          if (mTransmitLatency > GSM::Time(USB_LATENCY_MIN)) {
 | 
			
		||||
              if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
 | 
			
		||||
              mTransmitLatency.decTN();
 | 
			
		||||
              LOG(INFO) << "reduced latency: " << mTransmitLatency;
 | 
			
		||||
              mLatencyUpdateTime = radioClock->get();
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      // time to push burst to transmit FIFO
 | 
			
		||||
      pushRadioVector(mTransmitDeadlineClock);
 | 
			
		||||
      mTransmitDeadlineClock.incTN();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
  }
 | 
			
		||||
  // FIXME -- This should not be a hard spin.
 | 
			
		||||
  // But any delay here causes us to throw omni_thread_fatal.
 | 
			
		||||
  //else radioClock->wait();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void Transceiver::writeClockInterface()
 | 
			
		||||
{
 | 
			
		||||
  char command[50];
 | 
			
		||||
  // FIXME -- This should be adaptive.
 | 
			
		||||
  sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
 | 
			
		||||
 | 
			
		||||
  LOG(INFO) << "ClockInterface: sending " << command;
 | 
			
		||||
 | 
			
		||||
  mClockSocket.write(command,strlen(command)+1);
 | 
			
		||||
 | 
			
		||||
  mLastClockUpdateTime = mTransmitDeadlineClock;
 | 
			
		||||
 | 
			
		||||
}   
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void *FIFOServiceLoopAdapter(Transceiver *transceiver)
 | 
			
		||||
{
 | 
			
		||||
  transceiver->setPriority();
 | 
			
		||||
 | 
			
		||||
  while (1) {
 | 
			
		||||
    transceiver->driveReceiveFIFO();
 | 
			
		||||
    transceiver->driveTransmitFIFO();
 | 
			
		||||
  while (transceiver->on()) {
 | 
			
		||||
    transceiver->pullFIFO();
 | 
			
		||||
    pthread_testcancel();
 | 
			
		||||
  }
 | 
			
		||||
  return NULL;
 | 
			
		||||
@@ -804,7 +582,7 @@ void *FIFOServiceLoopAdapter(Transceiver *transceiver)
 | 
			
		||||
 | 
			
		||||
void *ControlServiceLoopAdapter(Transceiver *transceiver)
 | 
			
		||||
{
 | 
			
		||||
  while (1) {
 | 
			
		||||
  while (transceiver->running()) {
 | 
			
		||||
    transceiver->driveControl();
 | 
			
		||||
    pthread_testcancel();
 | 
			
		||||
  }
 | 
			
		||||
@@ -813,15 +591,16 @@ void *ControlServiceLoopAdapter(Transceiver *transceiver)
 | 
			
		||||
 | 
			
		||||
void *TransmitPriorityQueueServiceLoopAdapter(Transceiver *transceiver)
 | 
			
		||||
{
 | 
			
		||||
  while (1) {
 | 
			
		||||
  while (transceiver->on()) {
 | 
			
		||||
    bool stale = false;
 | 
			
		||||
 | 
			
		||||
    // Flush the UDP packets until a successful transfer.
 | 
			
		||||
    while (!transceiver->driveTransmitPriorityQueue()) {
 | 
			
		||||
      stale = true; 
 | 
			
		||||
    }
 | 
			
		||||
    if (stale) {
 | 
			
		||||
      // If a packet was stale, remind the GSM stack of the clock.
 | 
			
		||||
      transceiver->writeClockInterface();
 | 
			
		||||
      transceiver->getDriveLoop()->writeClockInterface();
 | 
			
		||||
    }
 | 
			
		||||
    pthread_testcancel();
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright 2008 Free Software Foundation, Inc.
 | 
			
		||||
* Copyright 2008, 2012 Free Software Foundation, Inc.
 | 
			
		||||
*
 | 
			
		||||
* This software is distributed under the terms of the GNU Public License.
 | 
			
		||||
* See the COPYING file in the main directory for details.
 | 
			
		||||
@@ -29,6 +29,7 @@
 | 
			
		||||
	TRANSMIT_LOGGING	write every burst on the given slot to a log
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include "DriveLoop.h"
 | 
			
		||||
#include "radioInterface.h"
 | 
			
		||||
#include "Interthread.h"
 | 
			
		||||
#include "GSMCommon.h"
 | 
			
		||||
@@ -44,59 +45,24 @@
 | 
			
		||||
class Transceiver {
 | 
			
		||||
  
 | 
			
		||||
private:
 | 
			
		||||
 | 
			
		||||
  GSM::Time mTransmitLatency;     ///< latency between basestation clock and transmit deadline clock
 | 
			
		||||
  GSM::Time mLatencyUpdateTime;   ///< last time latency was updated
 | 
			
		||||
  DriveLoop *mDriveLoop;
 | 
			
		||||
 | 
			
		||||
  UDPSocket mDataSocket;	  ///< socket for writing to/reading from GSM core
 | 
			
		||||
  UDPSocket mControlSocket;	  ///< socket for writing/reading control commands from GSM core
 | 
			
		||||
  UDPSocket mClockSocket;	  ///< socket for writing clock updates to GSM core
 | 
			
		||||
 | 
			
		||||
  VectorQueue  mTransmitPriorityQueue;   ///< priority queue of transmit bursts received from GSM core
 | 
			
		||||
  VectorFIFO*  mTransmitFIFO;     ///< radioInterface FIFO of transmit bursts 
 | 
			
		||||
  VectorQueue  *mTransmitPriorityQueue;   ///< priority queue of transmit bursts received from GSM core
 | 
			
		||||
  VectorFIFO*  mReceiveFIFO;      ///< radioInterface FIFO of receive bursts 
 | 
			
		||||
 | 
			
		||||
  Thread *mFIFOServiceLoopThread;  ///< thread to push/pull bursts into transmit/receive FIFO
 | 
			
		||||
  Thread *mControlServiceLoopThread;       ///< thread to process control messages from GSM core
 | 
			
		||||
  Thread *mTransmitPriorityQueueServiceLoopThread;///< thread to process transmit bursts from GSM core
 | 
			
		||||
 | 
			
		||||
  GSM::Time mTransmitDeadlineClock;       ///< deadline for pushing bursts into transmit FIFO 
 | 
			
		||||
  GSM::Time mLastClockUpdateTime;         ///< last time clock update was sent up to core
 | 
			
		||||
  int mChannel;                           ///< channelizer attach number between 0 and 'M-1'
 | 
			
		||||
 | 
			
		||||
  RadioInterface *mRadioInterface;	  ///< associated radioInterface object
 | 
			
		||||
  double txFullScale;                     ///< full scale input to radio
 | 
			
		||||
  double rxFullScale;                     ///< full scale output to radio
 | 
			
		||||
 | 
			
		||||
  /** Codes for burst types of received bursts*/
 | 
			
		||||
  typedef enum {
 | 
			
		||||
    OFF,               ///< timeslot is off
 | 
			
		||||
    TSC,	       ///< timeslot should contain a normal burst
 | 
			
		||||
    RACH,	       ///< timeslot should contain an access burst
 | 
			
		||||
    IDLE	       ///< timeslot is an idle (or dummy) burst
 | 
			
		||||
  } CorrType;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /** Codes for channel combinations */
 | 
			
		||||
  typedef enum {
 | 
			
		||||
    FILL,               ///< Channel is transmitted, but unused
 | 
			
		||||
    I,                  ///< TCH/FS
 | 
			
		||||
    II,                 ///< TCH/HS, idle every other slot
 | 
			
		||||
    III,                ///< TCH/HS
 | 
			
		||||
    IV,                 ///< FCCH+SCH+CCCH+BCCH, uplink RACH
 | 
			
		||||
    V,                  ///< FCCH+SCH+CCCH+BCCH+SDCCH/4+SACCH/4, uplink RACH+SDCCH/4
 | 
			
		||||
    VI,                 ///< CCCH+BCCH, uplink RACH
 | 
			
		||||
    VII,                ///< SDCCH/8 + SACCH/8
 | 
			
		||||
    VIII,               ///< TCH/F + FACCH/F + SACCH/M
 | 
			
		||||
    IX,                 ///< TCH/F + SACCH/M
 | 
			
		||||
    X,                  ///< TCH/FD + SACCH/MD
 | 
			
		||||
    XI,                 ///< PBCCH+PCCCH+PDTCH+PACCH+PTCCH
 | 
			
		||||
    XII,                ///< PCCCH+PDTCH+PACCH+PTCCH
 | 
			
		||||
    XIII,               ///< PDTCH+PACCH+PTCCH
 | 
			
		||||
    NONE,               ///< Channel is inactive, default
 | 
			
		||||
    LOOPBACK            ///< similar go VII, used in loopback testing
 | 
			
		||||
  } ChannelCombination;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /** unmodulate a modulated burst */
 | 
			
		||||
#ifdef TRANSMIT_LOGGING
 | 
			
		||||
  void unModulateVector(signalVector wVector); 
 | 
			
		||||
@@ -115,29 +81,22 @@ private:
 | 
			
		||||
			   int &RSSI,
 | 
			
		||||
			   int &timingOffset);
 | 
			
		||||
   
 | 
			
		||||
  /** Set modulus for specific timeslot */
 | 
			
		||||
  void setModulus(int timeslot);
 | 
			
		||||
 | 
			
		||||
  /** return the expected burst type for the specified timestamp */
 | 
			
		||||
  CorrType expectedCorrType(GSM::Time currTime);
 | 
			
		||||
 | 
			
		||||
  /** send messages over the clock socket */
 | 
			
		||||
  void writeClockInterface(void);
 | 
			
		||||
 | 
			
		||||
  signalVector *gsmPulse;              ///< the GSM shaping pulse for modulation
 | 
			
		||||
  void pullFIFO(void);                 ///< blocking call on receive FIFO 
 | 
			
		||||
 | 
			
		||||
  int mSamplesPerSymbol;               ///< number of samples per GSM symbol
 | 
			
		||||
  int mSPS;               ///< number of samples per GSM symbol
 | 
			
		||||
 | 
			
		||||
  bool mOn;			       ///< flag to indicate that transceiver is powered on
 | 
			
		||||
  ChannelCombination mChanType[8];     ///< channel types for all timeslots
 | 
			
		||||
  bool mRunning;                       ///< flag to indicate control loop is running
 | 
			
		||||
  bool mPrimary;                       ///< flag to indicate C0 channel 
 | 
			
		||||
  double mTxFreq;                      ///< the transmit frequency
 | 
			
		||||
  double mRxFreq;                      ///< the receive frequency
 | 
			
		||||
  double mFreqOffset;                  ///< RF frequency offset
 | 
			
		||||
  int mPower;                          ///< the transmit power in dB
 | 
			
		||||
  unsigned mTSC;                       ///< the midamble sequence code
 | 
			
		||||
  double mEnergyThreshold;             ///< threshold to determine if received data is potentially a GSM burst
 | 
			
		||||
  GSM::Time prevFalseDetectionTime;    ///< last timestamp of a false energy detection
 | 
			
		||||
  int fillerModulus[8];                ///< modulus values of all timeslots, in frames
 | 
			
		||||
  signalVector *fillerTable[102][8];   ///< table of modulated filler waveforms for all timeslots
 | 
			
		||||
  unsigned mMaxExpectedDelay;            ///< maximum expected time-of-arrival offset in GSM symbols
 | 
			
		||||
 | 
			
		||||
  GSM::Time    channelEstimateTime[8]; ///< last timestamp of each timeslot's channel estimate
 | 
			
		||||
@@ -148,32 +107,30 @@ private:
 | 
			
		||||
  float        chanRespOffset[8];      ///< most recent timing offset, e.g. TOA, of all timeslots
 | 
			
		||||
  complex      chanRespAmplitude[8];   ///< most recent channel amplitude of all timeslots
 | 
			
		||||
 | 
			
		||||
  static int mTSC;                     ///< the midamble sequence code
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
  /** Transceiver constructor 
 | 
			
		||||
      @param wBasePort base port number of UDP sockets
 | 
			
		||||
      @param TRXAddress IP address of the TRX manager, as a string
 | 
			
		||||
      @param wSamplesPerSymbol number of samples per GSM symbol
 | 
			
		||||
      @param wSPS number of samples per GSM symbol
 | 
			
		||||
      @param wTransmitLatency initial setting of transmit latency
 | 
			
		||||
      @param radioInterface associated radioInterface object
 | 
			
		||||
  */
 | 
			
		||||
  Transceiver(int wBasePort,
 | 
			
		||||
	      const char *TRXAddress,
 | 
			
		||||
	      int wSamplesPerSymbol,
 | 
			
		||||
	      GSM::Time wTransmitLatency,
 | 
			
		||||
	      RadioInterface *wRadioInterface);
 | 
			
		||||
   
 | 
			
		||||
  Transceiver(int wBasePort, const char *TRXAddress,
 | 
			
		||||
	      DriveLoop *wDriveLoop, RadioInterface *wRadioInterface,
 | 
			
		||||
	      int wSPS = SAMPSPERSYM,
 | 
			
		||||
	      int wChannel = 0, bool wPrimary = true);
 | 
			
		||||
 | 
			
		||||
  /** Destructor */
 | 
			
		||||
  ~Transceiver();
 | 
			
		||||
 | 
			
		||||
  /** start the Transceiver */
 | 
			
		||||
  void start();
 | 
			
		||||
 | 
			
		||||
  /** attach the radioInterface receive FIFO */
 | 
			
		||||
  void receiveFIFO(VectorFIFO *wFIFO) { mReceiveFIFO = wFIFO;}
 | 
			
		||||
 | 
			
		||||
  /** attach the radioInterface transmit FIFO */
 | 
			
		||||
  void transmitFIFO(VectorFIFO *wFIFO) { mTransmitFIFO = wFIFO;}
 | 
			
		||||
  /** shutdown (teardown threads) the Transceiver */
 | 
			
		||||
  void shutdown();
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
 | 
			
		||||
@@ -200,9 +157,17 @@ protected:
 | 
			
		||||
 | 
			
		||||
  void reset();
 | 
			
		||||
 | 
			
		||||
  /** return transceiver on/off status */ 
 | 
			
		||||
  bool on() { return mOn; }
 | 
			
		||||
 | 
			
		||||
  /** return control loop operational status */ 
 | 
			
		||||
  bool running() { return mRunning; }
 | 
			
		||||
 | 
			
		||||
  /** return the drive loop pointer */
 | 
			
		||||
  DriveLoop *getDriveLoop() { return mDriveLoop; }
 | 
			
		||||
  
 | 
			
		||||
  /** set priority on current thread */
 | 
			
		||||
  void setPriority() { mRadioInterface->setPriority(); }
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** FIFO thread loop */
 | 
			
		||||
@@ -213,4 +178,3 @@ void *ControlServiceLoopAdapter(Transceiver *);
 | 
			
		||||
 | 
			
		||||
/** transmit queueing thread loop */
 | 
			
		||||
void *TransmitPriorityQueueServiceLoopAdapter(Transceiver *);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,31 +32,98 @@
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define U1_DEFAULT_CLK_RT 64e6
 | 
			
		||||
#define U2_DEFAULT_CLK_RT 100e6 
 | 
			
		||||
#define NUM_TX_CHANS      2
 | 
			
		||||
#define NUM_RX_CHANS      NUM_TX_CHANS
 | 
			
		||||
#define TX_CHAN_OFFSET    2e6
 | 
			
		||||
#define B100_CLK_RT       52e6 
 | 
			
		||||
#define USRP2_BASE_RT     400e3
 | 
			
		||||
#define TX_AMPL           0.3;
 | 
			
		||||
#define SAMPLE_BUF_SZ     (1 << 20)
 | 
			
		||||
 | 
			
		||||
enum uhd_dev_type {
 | 
			
		||||
	USRP1,
 | 
			
		||||
	USRP2,
 | 
			
		||||
	B100,
 | 
			
		||||
	UMTRX,
 | 
			
		||||
	NUM_USRP_TYPES,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct uhd_dev_offset {
 | 
			
		||||
	enum uhd_dev_type type;
 | 
			
		||||
	int sps;
 | 
			
		||||
	double offset;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static TIMESTAMP init_rd_ts = 0;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
    master_clk_rt     - Master clock frequency - ignored if host resampling is
 | 
			
		||||
                        enabled
 | 
			
		||||
 * Tx / Rx sample offset values. In a perfect world, there is no group delay
 | 
			
		||||
 * though analog components, and behaviour through digital filters exactly
 | 
			
		||||
 * matches calculated values. In reality, there are unaccounted factors,
 | 
			
		||||
 * which are captured in these empirically measured (using a loopback test)
 | 
			
		||||
 * timing correction values.
 | 
			
		||||
 *
 | 
			
		||||
 * Notes:
 | 
			
		||||
 *   USRP1 with timestamps is not supported by UHD.
 | 
			
		||||
 */
 | 
			
		||||
static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 3] = {
 | 
			
		||||
	{ USRP1, 1, 0.0 },
 | 
			
		||||
	{ USRP1, 2, 0.0 },
 | 
			
		||||
	{ USRP1, 4, 0.0 },
 | 
			
		||||
	{ USRP2, 1, 5.4394e-5 },
 | 
			
		||||
	{ USRP2, 2, 0.0 },
 | 
			
		||||
	{ USRP2, 4, 0.0 },
 | 
			
		||||
	{ B100,  1, 9.4778e-5 },
 | 
			
		||||
	{ B100,  2, 5.1100e-5 },
 | 
			
		||||
	{ B100,  4, 2.9418e-5 },
 | 
			
		||||
	{ UMTRX, 1, 9.1738e-5 },
 | 
			
		||||
	{ UMTRX, 2, 0.0 },
 | 
			
		||||
	{ UMTRX, 4, 4.1813e-5 },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
    rx_smpl_offset    - Timing correction in seconds between receive and
 | 
			
		||||
                        transmit timestamps. This value corrects for delays on
 | 
			
		||||
                        on the RF side of the timestamping point of the device.
 | 
			
		||||
                        This value is generally empirically measured.
 | 
			
		||||
static double get_dev_offset(enum uhd_dev_type type, int sps)
 | 
			
		||||
{
 | 
			
		||||
	if (type == USRP1) {
 | 
			
		||||
		LOG(ERR) << "Invalid device type";
 | 
			
		||||
		return 0.0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    smpl_buf_sz       - The receive sample buffer size in bytes.
 | 
			
		||||
	switch (sps) {
 | 
			
		||||
	case 1:
 | 
			
		||||
		return uhd_offsets[3 * type + 0].offset;
 | 
			
		||||
	case 2:
 | 
			
		||||
		return uhd_offsets[3 * type + 1].offset;
 | 
			
		||||
	case 4:
 | 
			
		||||
		return uhd_offsets[3 * type + 2].offset;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    tx_ampl           - Transmit amplitude must be between 0 and 1.0
 | 
			
		||||
*/
 | 
			
		||||
const double master_clk_rt = 52e6;
 | 
			
		||||
const size_t smpl_buf_sz = (1 << 20);
 | 
			
		||||
const float tx_ampl = .3;
 | 
			
		||||
	LOG(ERR) << "Unsupported samples-per-symbols: " << sps;
 | 
			
		||||
	return 0.0;	
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef RESAMPLE
 | 
			
		||||
const double rx_smpl_offset = .00005;
 | 
			
		||||
#else
 | 
			
		||||
const double rx_smpl_offset = .0000869;
 | 
			
		||||
#endif
 | 
			
		||||
/*
 | 
			
		||||
 * Select sample rate based on device type and requested samples-per-symbol.
 | 
			
		||||
 * The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
 | 
			
		||||
 * usable channel spacing of 400 kHz.
 | 
			
		||||
 */ 
 | 
			
		||||
static double select_rate(uhd_dev_type type, int sps)
 | 
			
		||||
{
 | 
			
		||||
	if ((sps != 4) && (sps != 2) && (sps != 1))
 | 
			
		||||
		return -9999.99;
 | 
			
		||||
 | 
			
		||||
	switch (type) {
 | 
			
		||||
	case USRP2:
 | 
			
		||||
		return USRP2_BASE_RT * sps;
 | 
			
		||||
		break;
 | 
			
		||||
	case B100:
 | 
			
		||||
	case UMTRX:
 | 
			
		||||
		return GSMRATE * sps;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOG(ALERT) << "Unknown device type " << type;
 | 
			
		||||
	return -9999.99;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** Timestamp conversion
 | 
			
		||||
    @param timestamp a UHD or OpenBTS timestamp
 | 
			
		||||
@@ -148,44 +215,48 @@ private:
 | 
			
		||||
*/
 | 
			
		||||
class uhd_device : public RadioDevice {
 | 
			
		||||
public:
 | 
			
		||||
	uhd_device(double rate, bool skip_rx);
 | 
			
		||||
	uhd_device(int sps, bool skip_rx);
 | 
			
		||||
	~uhd_device();
 | 
			
		||||
 | 
			
		||||
	bool open(const std::string &args);
 | 
			
		||||
	int open(const std::string &args);
 | 
			
		||||
	bool start();
 | 
			
		||||
	bool stop();
 | 
			
		||||
	void restart(uhd::time_spec_t ts);
 | 
			
		||||
	void setPriority();
 | 
			
		||||
	enum busType getBus() { return bus; }
 | 
			
		||||
	enum TxWindowType getWindowType() { return tx_window; }
 | 
			
		||||
 | 
			
		||||
	int readSamples(short *buf, int len, bool *overrun, 
 | 
			
		||||
			TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
 | 
			
		||||
	int readSamples(short **buf, int chans, int len, TIMESTAMP timestamp,
 | 
			
		||||
			bool *overrun, bool *underrun, unsigned *RSSI);
 | 
			
		||||
 | 
			
		||||
	int writeSamples(short *buf, int len, bool *underrun, 
 | 
			
		||||
			 TIMESTAMP timestamp, bool isControl);
 | 
			
		||||
	int writeSamples(short **buf, int chans, int len, TIMESTAMP timestamp,
 | 
			
		||||
			 bool *underrun, bool isControl);
 | 
			
		||||
 | 
			
		||||
	bool updateAlignment(TIMESTAMP timestamp);
 | 
			
		||||
 | 
			
		||||
	bool setTxFreq(double wFreq);
 | 
			
		||||
	bool setRxFreq(double wFreq);
 | 
			
		||||
	bool setTxFreq(double wFreq, int chan);
 | 
			
		||||
	bool setRxFreq(double wFreq, int chan);
 | 
			
		||||
 | 
			
		||||
	inline TIMESTAMP initialWriteTimestamp() { return 0; }
 | 
			
		||||
	inline TIMESTAMP initialReadTimestamp() { return 0; }
 | 
			
		||||
	inline TIMESTAMP initialWriteTimestamp() { return init_rd_ts; }
 | 
			
		||||
	inline TIMESTAMP initialReadTimestamp() { return init_rd_ts; }
 | 
			
		||||
 | 
			
		||||
	inline double fullScaleInputValue() { return 32000 * tx_ampl; }
 | 
			
		||||
	inline double fullScaleInputValue() { return 32000 * TX_AMPL; }
 | 
			
		||||
	inline double fullScaleOutputValue() { return 32000; }
 | 
			
		||||
 | 
			
		||||
	double setRxGain(double db);
 | 
			
		||||
	double getRxGain(void) { return rx_gain; }
 | 
			
		||||
	double setRxGain(double db, int chan);
 | 
			
		||||
	double getRxGain(int chan) { return !chan ? rx_gain[0] : tx_gain[1]; }
 | 
			
		||||
	double maxRxGain(void) { return rx_gain_max; }
 | 
			
		||||
	double minRxGain(void) { return rx_gain_min; }
 | 
			
		||||
 | 
			
		||||
	double setTxGain(double db);
 | 
			
		||||
	double setTxGain(double db, int chan);
 | 
			
		||||
	double maxTxGain(void) { return tx_gain_max; }
 | 
			
		||||
	double minTxGain(void) { return tx_gain_min; }
 | 
			
		||||
	void setTxAntenna(std::string &name);
 | 
			
		||||
	void setRxAntenna(std::string &name);
 | 
			
		||||
	std::string getRxAntenna();
 | 
			
		||||
	std::string getTxAntenna();
 | 
			
		||||
 | 
			
		||||
	double getTxFreq() { return tx_freq; }
 | 
			
		||||
	double getRxFreq() { return rx_freq; }
 | 
			
		||||
	double getTxFreq(int chan) { return !chan ? tx_freq[0] : tx_freq[1]; }
 | 
			
		||||
	double getRxFreq(int chan) { return !chan ? rx_freq[0] : rx_freq[1]; }
 | 
			
		||||
 | 
			
		||||
	inline double getSampleRate() { return actual_smpl_rt; }
 | 
			
		||||
	inline double numberRead() { return rx_pkt_cnt; }
 | 
			
		||||
@@ -195,6 +266,7 @@ public:
 | 
			
		||||
	    @return true if message received or false on timeout or error
 | 
			
		||||
	*/
 | 
			
		||||
	bool recv_async_msg();
 | 
			
		||||
	bool running() { return started; }
 | 
			
		||||
 | 
			
		||||
	enum err_code {
 | 
			
		||||
		ERROR_TIMING = -1,
 | 
			
		||||
@@ -204,14 +276,18 @@ public:
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	uhd::usrp::multi_usrp::sptr usrp_dev;
 | 
			
		||||
	enum busType bus;
 | 
			
		||||
	uhd::tx_streamer::sptr tx_stream;
 | 
			
		||||
	uhd::rx_streamer::sptr rx_stream;
 | 
			
		||||
	enum TxWindowType tx_window;
 | 
			
		||||
	enum uhd_dev_type dev_type;
 | 
			
		||||
 | 
			
		||||
	int sps;
 | 
			
		||||
	double desired_smpl_rt, actual_smpl_rt;
 | 
			
		||||
 | 
			
		||||
	double tx_gain, tx_gain_min, tx_gain_max;
 | 
			
		||||
	double rx_gain, rx_gain_min, rx_gain_max;
 | 
			
		||||
	double tx_gain[NUM_TX_CHANS], tx_gain_min, tx_gain_max;
 | 
			
		||||
	double rx_gain[NUM_RX_CHANS], rx_gain_min, rx_gain_max;
 | 
			
		||||
 | 
			
		||||
	double tx_freq, rx_freq;
 | 
			
		||||
	double tx_freq[NUM_TX_CHANS], rx_freq[NUM_RX_CHANS];
 | 
			
		||||
	size_t tx_spp, rx_spp;
 | 
			
		||||
 | 
			
		||||
	bool started;
 | 
			
		||||
@@ -223,11 +299,12 @@ private:
 | 
			
		||||
	uhd::time_spec_t prev_ts;
 | 
			
		||||
 | 
			
		||||
	TIMESTAMP ts_offset;
 | 
			
		||||
	smpl_buf *rx_smpl_buf;
 | 
			
		||||
	smpl_buf *rx_smpl_buf[NUM_RX_CHANS];
 | 
			
		||||
 | 
			
		||||
	void init_gains();
 | 
			
		||||
	void set_ref_clk(bool ext_clk);
 | 
			
		||||
	double set_rates(double rate);
 | 
			
		||||
	int set_master_clk(double rate);
 | 
			
		||||
	int set_rates(double rate);
 | 
			
		||||
	bool parse_dev_type();
 | 
			
		||||
	bool flush_recv(size_t num_pkts);
 | 
			
		||||
	int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
 | 
			
		||||
@@ -235,12 +312,12 @@ private:
 | 
			
		||||
	std::string str_code(uhd::rx_metadata_t metadata);
 | 
			
		||||
	std::string str_code(uhd::async_metadata_t metadata);
 | 
			
		||||
 | 
			
		||||
	Thread async_event_thrd;
 | 
			
		||||
	Thread *async_event_thrd;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void *async_event_loop(uhd_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	while (1) {
 | 
			
		||||
	while (dev->running()) {
 | 
			
		||||
		dev->recv_async_msg();
 | 
			
		||||
		pthread_testcancel();
 | 
			
		||||
	}
 | 
			
		||||
@@ -268,23 +345,36 @@ void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uhd_device::uhd_device(double rate, bool skip_rx)
 | 
			
		||||
	: desired_smpl_rt(rate), actual_smpl_rt(0),
 | 
			
		||||
	  tx_gain(0.0), tx_gain_min(0.0), tx_gain_max(0.0),
 | 
			
		||||
	  rx_gain(0.0), rx_gain_min(0.0), rx_gain_max(0.0),
 | 
			
		||||
	  tx_freq(0.0), rx_freq(0.0), tx_spp(0), rx_spp(0),
 | 
			
		||||
	  started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
 | 
			
		||||
	  prev_ts(0,0), ts_offset(0), rx_smpl_buf(NULL)
 | 
			
		||||
uhd_device::uhd_device(int sps, bool skip_rx)
 | 
			
		||||
	: tx_gain_min(0.0), tx_gain_max(0.0),
 | 
			
		||||
	  rx_gain_min(0.0), rx_gain_max(0.0),
 | 
			
		||||
	  tx_spp(0), rx_spp(0), started(false), aligned(false),
 | 
			
		||||
	  rx_pkt_cnt(0), drop_cnt(0), prev_ts(0,0), ts_offset(0),
 | 
			
		||||
	  async_event_thrd(NULL)
 | 
			
		||||
{
 | 
			
		||||
	this->sps = sps;
 | 
			
		||||
	this->skip_rx = skip_rx;
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < NUM_TX_CHANS; i++) {
 | 
			
		||||
		tx_freq[i] = 0.0f;
 | 
			
		||||
		tx_gain[i] = 0.0f;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < NUM_RX_CHANS; i++) {
 | 
			
		||||
		rx_smpl_buf[i] = NULL;
 | 
			
		||||
		rx_freq[i] = 0.0f;
 | 
			
		||||
		rx_gain[i] = 0.0f;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uhd_device::~uhd_device()
 | 
			
		||||
{
 | 
			
		||||
	stop();
 | 
			
		||||
 | 
			
		||||
	if (rx_smpl_buf)
 | 
			
		||||
		delete rx_smpl_buf;
 | 
			
		||||
	for (int i = 0; i < NUM_RX_CHANS; i++) {
 | 
			
		||||
		if (rx_smpl_buf[i])
 | 
			
		||||
			delete rx_smpl_buf[i];
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void uhd_device::init_gains()
 | 
			
		||||
@@ -299,95 +389,135 @@ void uhd_device::init_gains()
 | 
			
		||||
	rx_gain_min = range.start();
 | 
			
		||||
	rx_gain_max = range.stop();
 | 
			
		||||
 | 
			
		||||
	usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2);
 | 
			
		||||
	usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2);
 | 
			
		||||
	for (int i = 0; i < NUM_TX_CHANS; i++) {
 | 
			
		||||
		usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2, i);
 | 
			
		||||
		tx_gain[i] = usrp_dev->get_tx_gain(i);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tx_gain = usrp_dev->get_tx_gain();
 | 
			
		||||
	rx_gain = usrp_dev->get_rx_gain();
 | 
			
		||||
	for (int i = 0; i < NUM_RX_CHANS; i++) {
 | 
			
		||||
		usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2, i);
 | 
			
		||||
		rx_gain[i] = usrp_dev->get_rx_gain(i);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void uhd_device::set_ref_clk(bool ext_clk)
 | 
			
		||||
{
 | 
			
		||||
	uhd::clock_config_t clk_cfg;
 | 
			
		||||
 | 
			
		||||
	clk_cfg.pps_source = uhd::clock_config_t::PPS_SMA;
 | 
			
		||||
 | 
			
		||||
	if (ext_clk)
 | 
			
		||||
		clk_cfg.ref_source = uhd::clock_config_t::REF_SMA;
 | 
			
		||||
	else
 | 
			
		||||
		clk_cfg.ref_source = uhd::clock_config_t::REF_INT;
 | 
			
		||||
 | 
			
		||||
	usrp_dev->set_clock_config(clk_cfg);
 | 
			
		||||
		usrp_dev->set_clock_source("external");
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double uhd_device::set_rates(double rate)
 | 
			
		||||
int uhd_device::set_master_clk(double clk_rate)
 | 
			
		||||
{
 | 
			
		||||
	double actual_rt, actual_clk_rt;
 | 
			
		||||
	double actual_clk_rt;
 | 
			
		||||
 | 
			
		||||
#ifndef RESAMPLE
 | 
			
		||||
	// Make sure we can set the master clock rate on this device
 | 
			
		||||
	actual_clk_rt = usrp_dev->get_master_clock_rate();
 | 
			
		||||
	if (actual_clk_rt > U1_DEFAULT_CLK_RT) {
 | 
			
		||||
		LOG(ALERT) << "Cannot set clock rate on this device";
 | 
			
		||||
		LOG(ALERT) << "Please compile with host resampling support";
 | 
			
		||||
		return -1.0;
 | 
			
		||||
	try {
 | 
			
		||||
		usrp_dev->set_master_clock_rate(clk_rate);
 | 
			
		||||
		actual_clk_rt = usrp_dev->get_master_clock_rate();
 | 
			
		||||
	} catch (const std::exception &ex) {
 | 
			
		||||
		LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
 | 
			
		||||
		LOG(ALERT) << ex.what();
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Set master clock rate
 | 
			
		||||
	usrp_dev->set_master_clock_rate(master_clk_rt);
 | 
			
		||||
	actual_clk_rt = usrp_dev->get_master_clock_rate();
 | 
			
		||||
 | 
			
		||||
	if (actual_clk_rt != master_clk_rt) {
 | 
			
		||||
	if (actual_clk_rt != clk_rate) {
 | 
			
		||||
		LOG(ALERT) << "Failed to set master clock rate";
 | 
			
		||||
		LOG(ALERT) << "Requested clock rate " << clk_rate;
 | 
			
		||||
		LOG(ALERT) << "Actual clock rate " << actual_clk_rt;
 | 
			
		||||
		return -1.0;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int uhd_device::set_rates(double rate)
 | 
			
		||||
{
 | 
			
		||||
	double offset_limit = 10.0;
 | 
			
		||||
	double tx_offset, rx_offset;
 | 
			
		||||
 | 
			
		||||
	// B100 is the only device where we set FPGA clocking
 | 
			
		||||
	if (dev_type == B100) {
 | 
			
		||||
		if (set_master_clk(B100_CLK_RT) < 0)
 | 
			
		||||
			return -1;
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	// Set sample rates
 | 
			
		||||
	usrp_dev->set_tx_rate(rate);
 | 
			
		||||
	usrp_dev->set_rx_rate(rate);
 | 
			
		||||
	actual_rt = usrp_dev->get_tx_rate();
 | 
			
		||||
	try {
 | 
			
		||||
		usrp_dev->set_tx_rate(rate);
 | 
			
		||||
		usrp_dev->set_rx_rate(rate);
 | 
			
		||||
	} catch (const std::exception &ex) {
 | 
			
		||||
		LOG(ALERT) << "UHD rate setting failed: " << rate;
 | 
			
		||||
		LOG(ALERT) << ex.what();
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	actual_smpl_rt = usrp_dev->get_tx_rate();
 | 
			
		||||
 | 
			
		||||
	if (actual_rt != rate) {
 | 
			
		||||
	tx_offset = actual_smpl_rt - rate;
 | 
			
		||||
	rx_offset = usrp_dev->get_rx_rate() - rate;
 | 
			
		||||
	if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
 | 
			
		||||
		LOG(ALERT) << "Actual sample rate differs from desired rate";
 | 
			
		||||
		return -1.0;
 | 
			
		||||
	}
 | 
			
		||||
	if (usrp_dev->get_rx_rate() != actual_rt) {
 | 
			
		||||
		LOG(ALERT) << "Transmit and receive sample rates do not match";
 | 
			
		||||
		return -1.0;
 | 
			
		||||
		LOG(ALERT) << "Tx/Rx (" << actual_smpl_rt << "/"
 | 
			
		||||
			   << usrp_dev->get_rx_rate() << ")";
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return actual_rt;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double uhd_device::setTxGain(double db)
 | 
			
		||||
double uhd_device::setTxGain(double db, int chan)
 | 
			
		||||
{
 | 
			
		||||
	usrp_dev->set_tx_gain(db);
 | 
			
		||||
	tx_gain = usrp_dev->get_tx_gain();
 | 
			
		||||
	if (chan >= NUM_TX_CHANS) {
 | 
			
		||||
		LOG(ALERT) << "Attempting to set gain on non-existent channel";
 | 
			
		||||
		return 0.0f;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOG(INFO) << "Set TX gain to " << tx_gain << "dB";
 | 
			
		||||
	usrp_dev->set_tx_gain(db, chan);
 | 
			
		||||
	tx_gain[chan] = usrp_dev->get_tx_gain(chan);
 | 
			
		||||
	LOG(INFO) << "Set TX gain to " << tx_gain[chan] << "dB";
 | 
			
		||||
 | 
			
		||||
	return tx_gain;
 | 
			
		||||
	return tx_gain[chan];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double uhd_device::setRxGain(double db)
 | 
			
		||||
double uhd_device::setRxGain(double db, int chan)
 | 
			
		||||
{
 | 
			
		||||
	usrp_dev->set_rx_gain(db);
 | 
			
		||||
	rx_gain = usrp_dev->get_rx_gain();
 | 
			
		||||
	if (chan >= NUM_RX_CHANS) {
 | 
			
		||||
		LOG(ALERT) << "Attempting to read gain non-existent channel";
 | 
			
		||||
		return 0.0f;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOG(INFO) << "Set RX gain to " << rx_gain << "dB";
 | 
			
		||||
	usrp_dev->set_rx_gain(db, chan);
 | 
			
		||||
	rx_gain[chan] = usrp_dev->get_rx_gain(chan);
 | 
			
		||||
	LOG(INFO) << "Set RX gain to " << rx_gain[chan] << "dB";
 | 
			
		||||
 | 
			
		||||
	return rx_gain;
 | 
			
		||||
	return rx_gain[chan];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void uhd_device::setTxAntenna(std::string &name)
 | 
			
		||||
{
 | 
			
		||||
	usrp_dev->set_tx_antenna(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void uhd_device::setRxAntenna(std::string &name)
 | 
			
		||||
{
 | 
			
		||||
	usrp_dev->set_rx_antenna(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string uhd_device::getTxAntenna()
 | 
			
		||||
{
 | 
			
		||||
	return usrp_dev->get_tx_antenna();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string uhd_device::getRxAntenna()
 | 
			
		||||
{
 | 
			
		||||
	return usrp_dev->get_rx_antenna();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
    Parse the UHD device tree and mboard name to find out what device we're
 | 
			
		||||
    dealing with. We need the bus type so that the transceiver knows how to
 | 
			
		||||
    dealing with. We need the window type so that the transceiver knows how to
 | 
			
		||||
    deal with the transport latency. Reject the USRP1 because UHD doesn't
 | 
			
		||||
    support timestamped samples with it.
 | 
			
		||||
 */
 | 
			
		||||
@@ -395,34 +525,46 @@ bool uhd_device::parse_dev_type()
 | 
			
		||||
{
 | 
			
		||||
	std::string mboard_str, dev_str;
 | 
			
		||||
	uhd::property_tree::sptr prop_tree;
 | 
			
		||||
	size_t usrp1_str, usrp2_str, b100_str1, b100_str2;
 | 
			
		||||
	size_t usrp1_str, usrp2_str, b100_str, umtrx_str;
 | 
			
		||||
 | 
			
		||||
	prop_tree = usrp_dev->get_device()->get_tree();
 | 
			
		||||
	dev_str = prop_tree->access<std::string>("/name").get();
 | 
			
		||||
	mboard_str = usrp_dev->get_mboard_name();
 | 
			
		||||
 | 
			
		||||
	usrp1_str = dev_str.find("USRP1");
 | 
			
		||||
	b100_str1 = dev_str.find("B-Series");
 | 
			
		||||
	b100_str2 = mboard_str.find("B100");
 | 
			
		||||
	usrp2_str = dev_str.find("USRP2");
 | 
			
		||||
	umtrx_str = dev_str.find("UmTRX");
 | 
			
		||||
	b100_str = mboard_str.find("B100");
 | 
			
		||||
 | 
			
		||||
	if (usrp1_str != std::string::npos) {
 | 
			
		||||
		LOG(ALERT) << "USRP1 is not supported using the UHD driver";
 | 
			
		||||
		LOG(ALERT) << "Please compile with GNU Radio libusrp support";
 | 
			
		||||
		dev_type = USRP1;
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ((b100_str1 != std::string::npos) || (b100_str2 != std::string::npos)) {
 | 
			
		||||
		bus = USB;
 | 
			
		||||
		LOG(INFO) << "Using USB bus for " << dev_str;
 | 
			
		||||
	if (b100_str != std::string::npos) {
 | 
			
		||||
		tx_window = TX_WINDOW_USRP1;
 | 
			
		||||
		LOG(INFO) << "Using USRP1 type transmit window for "
 | 
			
		||||
			  << dev_str << " " << mboard_str;
 | 
			
		||||
		dev_type = B100;
 | 
			
		||||
		return true;
 | 
			
		||||
	} else if (usrp2_str != std::string::npos) {
 | 
			
		||||
		dev_type = USRP2;
 | 
			
		||||
	} else if (umtrx_str != std::string::npos) {
 | 
			
		||||
		dev_type = UMTRX;
 | 
			
		||||
	} else {
 | 
			
		||||
		bus = NET;
 | 
			
		||||
		LOG(INFO) << "Using network bus for " << dev_str;
 | 
			
		||||
		LOG(ALERT) << "Unknown UHD device type";
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tx_window = TX_WINDOW_FIXED;
 | 
			
		||||
	LOG(INFO) << "Using fixed transmit window for "
 | 
			
		||||
		  << dev_str << " " << mboard_str;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool uhd_device::open(const std::string &args)
 | 
			
		||||
int uhd_device::open(const std::string &args)
 | 
			
		||||
{
 | 
			
		||||
	// Register msg handler
 | 
			
		||||
	uhd::msg::register_handler(&uhd_msg_handler);
 | 
			
		||||
@@ -432,7 +574,7 @@ bool uhd_device::open(const std::string &args)
 | 
			
		||||
	uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
 | 
			
		||||
	if (dev_addrs.size() == 0) {
 | 
			
		||||
		LOG(ALERT) << "No UHD devices found with address '" << args << "'";
 | 
			
		||||
		return false;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Use the first found device
 | 
			
		||||
@@ -441,32 +583,51 @@ bool uhd_device::open(const std::string &args)
 | 
			
		||||
		usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]);
 | 
			
		||||
	} catch(...) {
 | 
			
		||||
		LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string();
 | 
			
		||||
		return false;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check for a valid device type and set bus type
 | 
			
		||||
	if (!parse_dev_type())
 | 
			
		||||
		return false;
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
#ifdef EXTREF
 | 
			
		||||
	set_ref_clk(true);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	// Number of samples per over-the-wire packet
 | 
			
		||||
	tx_spp = usrp_dev->get_device()->get_max_send_samps_per_packet();
 | 
			
		||||
	rx_spp = usrp_dev->get_device()->get_max_recv_samps_per_packet();
 | 
			
		||||
	if (NUM_TX_CHANS == 2) {
 | 
			
		||||
		uhd::usrp::subdev_spec_t subdev_spec("A:0 B:0");
 | 
			
		||||
		usrp_dev->set_tx_subdev_spec(subdev_spec);
 | 
			
		||||
		usrp_dev->set_rx_subdev_spec(subdev_spec);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Set rates
 | 
			
		||||
	actual_smpl_rt = set_rates(desired_smpl_rt);
 | 
			
		||||
	if (actual_smpl_rt < 0)
 | 
			
		||||
		return false;
 | 
			
		||||
	desired_smpl_rt = select_rate(dev_type, sps);
 | 
			
		||||
	if (set_rates(desired_smpl_rt) < 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	// Create TX and RX streamers
 | 
			
		||||
	uhd::stream_args_t stream_args("sc16");
 | 
			
		||||
	for (int i = 0; i < NUM_TX_CHANS; i++)
 | 
			
		||||
		stream_args.channels.push_back(i);
 | 
			
		||||
	tx_stream = usrp_dev->get_tx_stream(stream_args);
 | 
			
		||||
	rx_stream = usrp_dev->get_rx_stream(stream_args);
 | 
			
		||||
 | 
			
		||||
	// Number of samples per over-the-wire packet
 | 
			
		||||
	tx_spp = tx_stream->get_max_num_samps();
 | 
			
		||||
	rx_spp = rx_stream->get_max_num_samps();
 | 
			
		||||
 | 
			
		||||
	// Create receive buffer
 | 
			
		||||
	size_t buf_len = smpl_buf_sz / sizeof(uint32_t);
 | 
			
		||||
	rx_smpl_buf = new smpl_buf(buf_len, actual_smpl_rt);
 | 
			
		||||
	size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
 | 
			
		||||
	for (int i = 0; i < NUM_RX_CHANS; i++)
 | 
			
		||||
		rx_smpl_buf[i] = new smpl_buf(buf_len, actual_smpl_rt);
 | 
			
		||||
 | 
			
		||||
	// Set receive chain sample offset 
 | 
			
		||||
	ts_offset = (TIMESTAMP)(rx_smpl_offset * actual_smpl_rt);
 | 
			
		||||
	double offset = get_dev_offset(dev_type, sps);
 | 
			
		||||
	if (offset == 0.0) {
 | 
			
		||||
		LOG(ERR) << "Unsupported configuration, no correction applied";
 | 
			
		||||
		ts_offset = 0;
 | 
			
		||||
	} else  {
 | 
			
		||||
		ts_offset = (TIMESTAMP) (offset * actual_smpl_rt);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Initialize and shadow gain values 
 | 
			
		||||
	init_gains();
 | 
			
		||||
@@ -474,28 +635,30 @@ bool uhd_device::open(const std::string &args)
 | 
			
		||||
	// Print configuration
 | 
			
		||||
	LOG(INFO) << "\n" << usrp_dev->get_pp_string();
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
	if (dev_type == USRP2)
 | 
			
		||||
		return RESAMP;
 | 
			
		||||
 | 
			
		||||
	return NORMAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool uhd_device::flush_recv(size_t num_pkts)
 | 
			
		||||
{
 | 
			
		||||
	uhd::rx_metadata_t md;
 | 
			
		||||
	size_t num_smpls;
 | 
			
		||||
	uint32_t buff[rx_spp];
 | 
			
		||||
	float timeout;
 | 
			
		||||
 | 
			
		||||
	// Use .01 sec instead of the default .1 sec
 | 
			
		||||
	timeout = .01;
 | 
			
		||||
 | 
			
		||||
	for (size_t i = 0; i < num_pkts; i++) {
 | 
			
		||||
		num_smpls = usrp_dev->get_device()->recv(
 | 
			
		||||
					buff,
 | 
			
		||||
					rx_spp,
 | 
			
		||||
					md,
 | 
			
		||||
					uhd::io_type_t::COMPLEX_INT16,
 | 
			
		||||
					uhd::device::RECV_MODE_ONE_PACKET,
 | 
			
		||||
					timeout);
 | 
			
		||||
	std::vector<std::vector<std::complex<short> > > bufs(
 | 
			
		||||
		NUM_RX_CHANS, std::vector<std::complex<short> >(rx_spp));
 | 
			
		||||
	std::vector<std::complex<short> *> buf_ptrs;
 | 
			
		||||
	for (int i = 0; i < bufs.size(); i++)
 | 
			
		||||
		buf_ptrs.push_back(&bufs[i].front());
 | 
			
		||||
 | 
			
		||||
	for (size_t i = 0; i < num_pkts; i++) {
 | 
			
		||||
		num_smpls = rx_stream->recv(buf_ptrs, rx_spp, md,
 | 
			
		||||
					    timeout, true);
 | 
			
		||||
		if (!num_smpls) {
 | 
			
		||||
			switch (md.error_code) {
 | 
			
		||||
			case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
 | 
			
		||||
@@ -511,17 +674,15 @@ bool uhd_device::flush_recv(size_t num_pkts)
 | 
			
		||||
 | 
			
		||||
void uhd_device::restart(uhd::time_spec_t ts)
 | 
			
		||||
{
 | 
			
		||||
	uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
 | 
			
		||||
	usrp_dev->issue_stream_cmd(cmd);
 | 
			
		||||
 | 
			
		||||
	flush_recv(50);
 | 
			
		||||
 | 
			
		||||
	usrp_dev->set_time_now(ts);
 | 
			
		||||
	aligned = false;
 | 
			
		||||
 | 
			
		||||
	cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
 | 
			
		||||
	cmd.stream_now = true;
 | 
			
		||||
	uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
 | 
			
		||||
	cmd.time_spec = uhd::time_spec_t(0.1);
 | 
			
		||||
	cmd.stream_now = false;
 | 
			
		||||
	usrp_dev->issue_stream_cmd(cmd);
 | 
			
		||||
 | 
			
		||||
	uhd::rx_metadata_t md;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool uhd_device::start()
 | 
			
		||||
@@ -533,10 +694,12 @@ bool uhd_device::start()
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	started = true;
 | 
			
		||||
	setPriority();
 | 
			
		||||
 | 
			
		||||
	// Start asynchronous event (underrun check) loop
 | 
			
		||||
	async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
 | 
			
		||||
	async_event_thrd = new Thread(32768);
 | 
			
		||||
	async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
 | 
			
		||||
 | 
			
		||||
	// Start streaming
 | 
			
		||||
	restart(uhd::time_spec_t(0.0));
 | 
			
		||||
@@ -545,18 +708,23 @@ bool uhd_device::start()
 | 
			
		||||
	double time_now = usrp_dev->get_time_now().get_real_secs();
 | 
			
		||||
	LOG(INFO) << "The current time is " << time_now << " seconds";
 | 
			
		||||
 | 
			
		||||
	started = true;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool uhd_device::stop()
 | 
			
		||||
{
 | 
			
		||||
	if (!started)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	started = false;
 | 
			
		||||
 | 
			
		||||
	uhd::stream_cmd_t stream_cmd = 
 | 
			
		||||
		uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
 | 
			
		||||
 | 
			
		||||
	usrp_dev->issue_stream_cmd(stream_cmd);
 | 
			
		||||
 | 
			
		||||
	started = false;
 | 
			
		||||
	delete async_event_thrd;
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -607,40 +775,48 @@ int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int uhd_device::readSamples(short *buf, int len, bool *overrun,
 | 
			
		||||
			TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
 | 
			
		||||
int uhd_device::readSamples(short **buf, int chans, int len,
 | 
			
		||||
			    TIMESTAMP timestamp, bool *overrun,
 | 
			
		||||
			    bool *underrun, unsigned *RSSI)
 | 
			
		||||
{
 | 
			
		||||
	ssize_t rc;
 | 
			
		||||
	uhd::time_spec_t ts;
 | 
			
		||||
	uhd::rx_metadata_t metadata;
 | 
			
		||||
	uint32_t pkt_buf[rx_spp];
 | 
			
		||||
 | 
			
		||||
	if (skip_rx)
 | 
			
		||||
	if (skip_rx) {
 | 
			
		||||
		LOG(INFO) << "Skipping Rx";
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (chans != NUM_RX_CHANS) {
 | 
			
		||||
		LOG(ERR) << "Number of requested channels does not match build";
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Shift read time with respect to transmit clock
 | 
			
		||||
	timestamp += ts_offset;
 | 
			
		||||
 | 
			
		||||
	ts = convert_time(timestamp, actual_smpl_rt);
 | 
			
		||||
	LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
 | 
			
		||||
	LOG(DEBUG) << "Requested UHD timestamp = " << ts.get_real_secs();
 | 
			
		||||
 | 
			
		||||
	// Check that timestamp is valid
 | 
			
		||||
	rc = rx_smpl_buf->avail_smpls(timestamp);
 | 
			
		||||
	rc = rx_smpl_buf[0]->avail_smpls(timestamp);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOG(ERR) << rx_smpl_buf->str_code(rc);
 | 
			
		||||
		LOG(ERR) << rx_smpl_buf->str_status();
 | 
			
		||||
		LOG(ERR) << rx_smpl_buf[0]->str_code(rc);
 | 
			
		||||
		LOG(ERR) << rx_smpl_buf[0]->str_status();
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Receive samples from the usrp until we have enough
 | 
			
		||||
	while (rx_smpl_buf->avail_smpls(timestamp) < len) {
 | 
			
		||||
		size_t num_smpls = usrp_dev->get_device()->recv(
 | 
			
		||||
					(void*)pkt_buf,
 | 
			
		||||
					rx_spp,
 | 
			
		||||
					metadata,
 | 
			
		||||
					uhd::io_type_t::COMPLEX_INT16,
 | 
			
		||||
					uhd::device::RECV_MODE_ONE_PACKET);
 | 
			
		||||
	std::vector<std::vector<std::complex<short> > > bufs(
 | 
			
		||||
		NUM_RX_CHANS, std::vector<std::complex<short> >(rx_spp));
 | 
			
		||||
	std::vector<std::complex<short> *> buf_ptrs;
 | 
			
		||||
	for (int i = 0; i < bufs.size(); i++)
 | 
			
		||||
		buf_ptrs.push_back(&bufs[i].front());
 | 
			
		||||
 | 
			
		||||
	// Receive samples from the usrp until we have enough
 | 
			
		||||
	while (rx_smpl_buf[0]->avail_smpls(timestamp) < len) {
 | 
			
		||||
		size_t num_smpls = rx_stream->recv(buf_ptrs, rx_spp,
 | 
			
		||||
						   metadata, 0.1, true);
 | 
			
		||||
		rx_pkt_cnt++;
 | 
			
		||||
 | 
			
		||||
		// Check for errors 
 | 
			
		||||
@@ -653,39 +829,45 @@ int uhd_device::readSamples(short *buf, int len, bool *overrun,
 | 
			
		||||
		case ERROR_TIMING:
 | 
			
		||||
			restart(prev_ts);
 | 
			
		||||
		case ERROR_UNHANDLED:
 | 
			
		||||
			LOG(ALERT) << "UHD: Unhandled error";
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		ts = metadata.time_spec;
 | 
			
		||||
		LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
 | 
			
		||||
		LOG(DEBUG) << "Received " << num_smpls << " samples "
 | 
			
		||||
			   << "with timestamp = " << ts.get_real_secs();
 | 
			
		||||
 | 
			
		||||
		rc = rx_smpl_buf->write(pkt_buf,
 | 
			
		||||
					num_smpls,
 | 
			
		||||
					metadata.time_spec);
 | 
			
		||||
 | 
			
		||||
		// Continue on local overrun, exit on other errors
 | 
			
		||||
		if ((rc < 0)) {
 | 
			
		||||
			LOG(ERR) << rx_smpl_buf->str_code(rc);
 | 
			
		||||
			LOG(ERR) << rx_smpl_buf->str_status();
 | 
			
		||||
			if (rc != smpl_buf::ERROR_OVERFLOW)
 | 
			
		||||
				return 0;
 | 
			
		||||
		for (int i = 0; i < NUM_RX_CHANS; i++) {
 | 
			
		||||
			rc = rx_smpl_buf[i]->write((short *) &bufs[i].front(),
 | 
			
		||||
						   num_smpls,
 | 
			
		||||
						   metadata.time_spec);
 | 
			
		||||
                        
 | 
			
		||||
			// Continue on local overrun, exit on other errors
 | 
			
		||||
			if ((rc < 0)) {
 | 
			
		||||
				LOG(ERR) << rx_smpl_buf[i]->str_code(rc);
 | 
			
		||||
				LOG(ERR) << rx_smpl_buf[i]->str_status();
 | 
			
		||||
				if (rc != smpl_buf::ERROR_OVERFLOW)
 | 
			
		||||
					return 0;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We have enough samples
 | 
			
		||||
	rc = rx_smpl_buf->read(buf, len, timestamp);
 | 
			
		||||
	if ((rc < 0) || (rc != len)) {
 | 
			
		||||
		LOG(ERR) << rx_smpl_buf->str_code(rc);
 | 
			
		||||
		LOG(ERR) << rx_smpl_buf->str_status();
 | 
			
		||||
		return 0;
 | 
			
		||||
	for (int i = 0; i < NUM_RX_CHANS; i++) {
 | 
			
		||||
		rc = rx_smpl_buf[i]->read(buf[i], len, timestamp);
 | 
			
		||||
		if ((rc < 0) || (rc != len)) {
 | 
			
		||||
			LOG(ERR) << rx_smpl_buf[i]->str_code(rc);
 | 
			
		||||
			LOG(ERR) << rx_smpl_buf[i]->str_status();
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int uhd_device::writeSamples(short *buf, int len, bool *underrun,
 | 
			
		||||
			unsigned long long timestamp,bool isControl)
 | 
			
		||||
int uhd_device::writeSamples(short **buf, int chans, int len,
 | 
			
		||||
			     TIMESTAMP timestamp, bool *underrun,
 | 
			
		||||
			     bool isControl)
 | 
			
		||||
{
 | 
			
		||||
	uhd::tx_metadata_t metadata;
 | 
			
		||||
	metadata.has_time_spec = true;
 | 
			
		||||
@@ -718,12 +900,11 @@ int uhd_device::writeSamples(short *buf, int len, bool *underrun,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	size_t num_smpls = usrp_dev->get_device()->send(buf,
 | 
			
		||||
					len,
 | 
			
		||||
					metadata,
 | 
			
		||||
					uhd::io_type_t::COMPLEX_INT16,
 | 
			
		||||
					uhd::device::SEND_MODE_FULL_BUFF);
 | 
			
		||||
	std::vector<short *> bufs(NUM_TX_CHANS);
 | 
			
		||||
	for (int i = 0; i < NUM_TX_CHANS; i++)
 | 
			
		||||
		bufs[i] = buf[i];
 | 
			
		||||
 | 
			
		||||
	size_t num_smpls = tx_stream->send(bufs, len, metadata);
 | 
			
		||||
	if (num_smpls != (unsigned) len) {
 | 
			
		||||
		LOG(ALERT) << "UHD: Device send timed out";
 | 
			
		||||
		LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
 | 
			
		||||
@@ -736,24 +917,33 @@ int uhd_device::writeSamples(short *buf, int len, bool *underrun,
 | 
			
		||||
 | 
			
		||||
bool uhd_device::updateAlignment(TIMESTAMP timestamp)
 | 
			
		||||
{
 | 
			
		||||
	aligned = false;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool uhd_device::setTxFreq(double wFreq)
 | 
			
		||||
bool uhd_device::setTxFreq(double wFreq, int chan)
 | 
			
		||||
{
 | 
			
		||||
	uhd::tune_result_t tr = usrp_dev->set_tx_freq(wFreq);
 | 
			
		||||
	if (chan >= NUM_TX_CHANS) {
 | 
			
		||||
		LOG(ALERT) << "Attempting to tune non-existent channel";
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uhd::tune_result_t tr = usrp_dev->set_tx_freq(wFreq, chan);
 | 
			
		||||
	LOG(INFO) << "\n" << tr.to_pp_string();
 | 
			
		||||
	tx_freq = usrp_dev->get_tx_freq();
 | 
			
		||||
	tx_freq[chan] = usrp_dev->get_tx_freq(chan);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool uhd_device::setRxFreq(double wFreq)
 | 
			
		||||
bool uhd_device::setRxFreq(double wFreq, int chan)
 | 
			
		||||
{
 | 
			
		||||
	uhd::tune_result_t tr = usrp_dev->set_rx_freq(wFreq);
 | 
			
		||||
	if (chan >= NUM_RX_CHANS) {
 | 
			
		||||
		LOG(ALERT) << "Attempting to tune non-existent channel";
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uhd::tune_result_t tr = usrp_dev->set_rx_freq(wFreq, chan);
 | 
			
		||||
	LOG(INFO) << "\n" << tr.to_pp_string();
 | 
			
		||||
	rx_freq = usrp_dev->get_rx_freq();
 | 
			
		||||
	rx_freq[chan] = usrp_dev->get_rx_freq(chan);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
@@ -888,7 +1078,7 @@ ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
 | 
			
		||||
		num_smpls = len;
 | 
			
		||||
 | 
			
		||||
	// Starting index
 | 
			
		||||
	size_t read_start = data_start + (timestamp - time_start);
 | 
			
		||||
	size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
 | 
			
		||||
 | 
			
		||||
	// Read it
 | 
			
		||||
	if (read_start + num_smpls < buf_len) {
 | 
			
		||||
@@ -986,7 +1176,7 @@ std::string smpl_buf::str_code(ssize_t code)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RadioDevice *RadioDevice::make(double smpl_rt, bool skip_rx)
 | 
			
		||||
RadioDevice *RadioDevice::make(int sps, bool skip_rx)
 | 
			
		||||
{
 | 
			
		||||
	return new uhd_device(smpl_rt, skip_rx);
 | 
			
		||||
	return new uhd_device(sps, skip_rx);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -59,11 +59,11 @@ const dboardConfigType dboardConfig = TXA_RXB;
 | 
			
		||||
 | 
			
		||||
const double USRPDevice::masterClockRate = 52.0e6;
 | 
			
		||||
 | 
			
		||||
USRPDevice::USRPDevice (double _desiredSampleRate, bool skipRx)
 | 
			
		||||
USRPDevice::USRPDevice(int sps, bool skipRx)
 | 
			
		||||
  : skipRx(skipRx)
 | 
			
		||||
{
 | 
			
		||||
  LOG(INFO) << "creating USRP device...";
 | 
			
		||||
  decimRate = (unsigned int) round(masterClockRate/_desiredSampleRate);
 | 
			
		||||
  decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) sps));
 | 
			
		||||
  actualSampleRate = masterClockRate/decimRate;
 | 
			
		||||
  rxGain = 0;
 | 
			
		||||
 | 
			
		||||
@@ -75,7 +75,7 @@ USRPDevice::USRPDevice (double _desiredSampleRate, bool skipRx)
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool USRPDevice::open(const std::string &)
 | 
			
		||||
int USRPDevice::open(const std::string &)
 | 
			
		||||
{
 | 
			
		||||
  writeLock.unlock();
 | 
			
		||||
 | 
			
		||||
@@ -97,7 +97,7 @@ bool USRPDevice::open(const std::string &)
 | 
			
		||||
  catch(...) {
 | 
			
		||||
    LOG(ALERT) << "make failed on Rx";
 | 
			
		||||
    m_uRx.reset();
 | 
			
		||||
    return false;
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (m_uRx->fpga_master_clock_freq() != masterClockRate)
 | 
			
		||||
@@ -105,7 +105,7 @@ bool USRPDevice::open(const std::string &)
 | 
			
		||||
    LOG(ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq()
 | 
			
		||||
               << ", desired clock freq = " << masterClockRate;
 | 
			
		||||
    m_uRx.reset();
 | 
			
		||||
    return false;
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -120,7 +120,7 @@ bool USRPDevice::open(const std::string &)
 | 
			
		||||
  catch(...) {
 | 
			
		||||
    LOG(ALERT) << "make failed on Tx";
 | 
			
		||||
    m_uTx.reset();
 | 
			
		||||
    return false;
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (m_uTx->fpga_master_clock_freq() != masterClockRate)
 | 
			
		||||
@@ -128,7 +128,7 @@ bool USRPDevice::open(const std::string &)
 | 
			
		||||
    LOG(ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq()
 | 
			
		||||
               << ", desired clock freq = " << masterClockRate;
 | 
			
		||||
    m_uTx.reset();
 | 
			
		||||
    return false;
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!skipRx) m_uRx->stop();
 | 
			
		||||
@@ -165,7 +165,7 @@ bool USRPDevice::open(const std::string &)
 | 
			
		||||
  samplesWritten = 0;
 | 
			
		||||
  started = false;
 | 
			
		||||
  
 | 
			
		||||
  return true;
 | 
			
		||||
  return NORMAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -556,7 +556,7 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
 | 
			
		||||
bool USRPDevice::setRxFreq(double wFreq) { return true;};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
RadioDevice *RadioDevice::make(double desiredSampleRate, bool skipRx)
 | 
			
		||||
RadioDevice *RadioDevice::make(int sps, bool skipRx)
 | 
			
		||||
{
 | 
			
		||||
	return new USRPDevice(desiredSampleRate, skipRx);
 | 
			
		||||
	return new USRPDevice(sps, skipRx);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -112,10 +112,10 @@ private:
 | 
			
		||||
 public:
 | 
			
		||||
 | 
			
		||||
  /** Object constructor */
 | 
			
		||||
  USRPDevice (double _desiredSampleRate, bool skipRx);
 | 
			
		||||
  USRPDevice(int sps, bool skipRx);
 | 
			
		||||
 | 
			
		||||
  /** Instantiate the USRP */
 | 
			
		||||
  bool open(const std::string &);
 | 
			
		||||
  int open(const std::string &);
 | 
			
		||||
 | 
			
		||||
  /** Start the USRP */
 | 
			
		||||
  bool start();
 | 
			
		||||
@@ -126,8 +126,7 @@ private:
 | 
			
		||||
  /** Set priority not supported */
 | 
			
		||||
  void setPriority() { return; }
 | 
			
		||||
 | 
			
		||||
  /** Only USB bus supported */
 | 
			
		||||
  busType getBus() { return USB; }
 | 
			
		||||
  enum TxWindowType getWindowType() { return TX_WINDOW_USRP1; }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
	Read samples from the USRP.
 | 
			
		||||
 
 | 
			
		||||
@@ -41,7 +41,7 @@ int main(int argc, char *argv[]) {
 | 
			
		||||
  else gLogInit("DEBUG");
 | 
			
		||||
  //if (argc>2) gSetLogFile(argv[2]);
 | 
			
		||||
 | 
			
		||||
  RadioDevice *usrp = RadioDevice::make(52.0e6/192.0);
 | 
			
		||||
  RadioDevice *usrp = RadioDevice::make(52.0e6/192.0, 1);
 | 
			
		||||
 | 
			
		||||
  usrp->open("");
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										171
									
								
								Transceiver52M/convert.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								Transceiver52M/convert.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,171 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SSE type conversions
 | 
			
		||||
 * Copyright (C) 2013  Thomas Tsou <tom@tsou.cc>
 | 
			
		||||
 *
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Lesser General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
 * License along with this library; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <malloc.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_SSE3
 | 
			
		||||
#include <xmmintrin.h>
 | 
			
		||||
#include <emmintrin.h>
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_SSE4_1
 | 
			
		||||
#include <smmintrin.h>
 | 
			
		||||
 | 
			
		||||
/* 16*N 16-bit signed integer converted to single precision floats */
 | 
			
		||||
static void _sse_convert_si16_ps_16n(float *restrict out,
 | 
			
		||||
				     short *restrict in,
 | 
			
		||||
				     int len)
 | 
			
		||||
{
 | 
			
		||||
	__m128i m0, m1, m2, m3, m4, m5;
 | 
			
		||||
	__m128 m6, m7, m8, m9;
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < len / 16; i++) {
 | 
			
		||||
		/* Load (unaligned) packed floats */
 | 
			
		||||
		m0 = _mm_load_si128((__m128i *) &in[16 * i + 0]);
 | 
			
		||||
		m1 = _mm_load_si128((__m128i *) &in[16 * i + 8]);
 | 
			
		||||
 | 
			
		||||
		/* Unpack */
 | 
			
		||||
		m2 = _mm_cvtepi16_epi32(m0);
 | 
			
		||||
		m4 = _mm_cvtepi16_epi32(m1);
 | 
			
		||||
		m0 = _mm_shuffle_epi32(m0, _MM_SHUFFLE(1, 0, 3, 2));
 | 
			
		||||
		m1 = _mm_shuffle_epi32(m1, _MM_SHUFFLE(1, 0, 3, 2));
 | 
			
		||||
		m3 = _mm_cvtepi16_epi32(m0);
 | 
			
		||||
		m5 = _mm_cvtepi16_epi32(m1);
 | 
			
		||||
 | 
			
		||||
		/* Convert */
 | 
			
		||||
		m6 = _mm_cvtepi32_ps(m2);
 | 
			
		||||
		m7 = _mm_cvtepi32_ps(m3);
 | 
			
		||||
		m8 = _mm_cvtepi32_ps(m4);
 | 
			
		||||
		m9 = _mm_cvtepi32_ps(m5);
 | 
			
		||||
 | 
			
		||||
		/* Store */
 | 
			
		||||
		_mm_store_ps(&out[16 * i + 0], m6);
 | 
			
		||||
		_mm_store_ps(&out[16 * i + 4], m7);
 | 
			
		||||
		_mm_store_ps(&out[16 * i + 8], m8);
 | 
			
		||||
		_mm_store_ps(&out[16 * i + 12], m9);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
#endif /* HAVE_SSE4_1 */
 | 
			
		||||
 | 
			
		||||
/* 8*N single precision floats scaled and converted to 16-bit signed integer */
 | 
			
		||||
static void _sse_convert_scale_ps_si16_8n(short *restrict out,
 | 
			
		||||
					  float *restrict in,
 | 
			
		||||
					  float scale, int len)
 | 
			
		||||
{
 | 
			
		||||
	__m128 m0, m1, m2;
 | 
			
		||||
	__m128i m4, m5;
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < len / 8; i++) {
 | 
			
		||||
		/* Load (unaligned) packed floats */
 | 
			
		||||
		m0 = _mm_loadu_ps(&in[8 * i + 0]);
 | 
			
		||||
		m1 = _mm_loadu_ps(&in[8 * i + 4]);
 | 
			
		||||
		m2 = _mm_load1_ps(&scale);
 | 
			
		||||
 | 
			
		||||
		/* Scale */
 | 
			
		||||
		m0 = _mm_mul_ps(m0, m2);
 | 
			
		||||
		m1 = _mm_mul_ps(m1, m2);
 | 
			
		||||
 | 
			
		||||
		/* Convert */
 | 
			
		||||
		m4 = _mm_cvtps_epi32(m0);
 | 
			
		||||
		m5 = _mm_cvtps_epi32(m1);
 | 
			
		||||
 | 
			
		||||
		/* Pack and store */
 | 
			
		||||
		m5 = _mm_packs_epi32(m4, m5);
 | 
			
		||||
		_mm_store_si128((__m128i *) &out[8 * i], m5);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 16*N single precision floats scaled and converted to 16-bit signed integer */
 | 
			
		||||
static void _sse_convert_scale_ps_si16_16n(short *restrict out,
 | 
			
		||||
					   float *restrict in,
 | 
			
		||||
					   float scale, int len)
 | 
			
		||||
{
 | 
			
		||||
	__m128 m0, m1, m2, m3, m4;
 | 
			
		||||
	__m128i m5, m6, m7, m8;
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < len / 16; i++) {
 | 
			
		||||
		/* Load (unaligned) packed floats */
 | 
			
		||||
		m0 = _mm_loadu_ps(&in[16 * i + 0]);
 | 
			
		||||
		m1 = _mm_loadu_ps(&in[16 * i + 4]);
 | 
			
		||||
		m2 = _mm_loadu_ps(&in[16 * i + 8]);
 | 
			
		||||
		m3 = _mm_loadu_ps(&in[16 * i + 12]);
 | 
			
		||||
		m4 = _mm_load1_ps(&scale);
 | 
			
		||||
 | 
			
		||||
		/* Scale */
 | 
			
		||||
		m0 = _mm_mul_ps(m0, m4);
 | 
			
		||||
		m1 = _mm_mul_ps(m1, m4);
 | 
			
		||||
		m2 = _mm_mul_ps(m2, m4);
 | 
			
		||||
		m3 = _mm_mul_ps(m3, m4);
 | 
			
		||||
 | 
			
		||||
		/* Convert */
 | 
			
		||||
		m5 = _mm_cvtps_epi32(m0);
 | 
			
		||||
		m6 = _mm_cvtps_epi32(m1);
 | 
			
		||||
		m7 = _mm_cvtps_epi32(m2);
 | 
			
		||||
		m8 = _mm_cvtps_epi32(m3);
 | 
			
		||||
 | 
			
		||||
		/* Pack and store */
 | 
			
		||||
		m5 = _mm_packs_epi32(m5, m6);
 | 
			
		||||
		m7 = _mm_packs_epi32(m7, m8);
 | 
			
		||||
		_mm_store_si128((__m128i *) &out[16 * i + 0], m5);
 | 
			
		||||
		_mm_store_si128((__m128i *) &out[16 * i + 8], m7);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
#endif /* HAVE_SSE3 */
 | 
			
		||||
 | 
			
		||||
static void convert_si16_ps(float *out, short *in, int len)
 | 
			
		||||
{
 | 
			
		||||
	for (int i = 0; i < len; i++)
 | 
			
		||||
		out[i] = in[i];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void convert_scale_ps_si16(short *out, float *in, float scale, int len)
 | 
			
		||||
{
 | 
			
		||||
	for (int i = 0; i < len; i++)
 | 
			
		||||
		out[i] = in[i] * scale;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void convert_float_short(short *out, float *in, float scale, int len)
 | 
			
		||||
{
 | 
			
		||||
#ifdef HAVE_SSE3
 | 
			
		||||
	if (!(len % 16))
 | 
			
		||||
		_sse_convert_scale_ps_si16_16n(out, in, scale, len);
 | 
			
		||||
	else if (!(len % 8))
 | 
			
		||||
		_sse_convert_scale_ps_si16_8n(out, in, scale, len);
 | 
			
		||||
	else
 | 
			
		||||
		convert_scale_ps_si16(out, in, scale, len);
 | 
			
		||||
#else
 | 
			
		||||
	convert_scale_ps_si16(out, in, scale, len);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void convert_short_float(float *out, short *in, int len)
 | 
			
		||||
{
 | 
			
		||||
#ifdef HAVE_SSE4_1
 | 
			
		||||
	if (!(len % 16))
 | 
			
		||||
		_sse_convert_si16_ps_16n(out, in, len);
 | 
			
		||||
	else
 | 
			
		||||
		convert_si16_ps(out, in, len);
 | 
			
		||||
#else
 | 
			
		||||
	convert_si16_ps(out, in, len);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								Transceiver52M/convert.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								Transceiver52M/convert.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
#ifndef _CONVERT_H_
 | 
			
		||||
#define _CONVERT_H_
 | 
			
		||||
 | 
			
		||||
void convert_float_short(short *out, float *in, float scale, int len);
 | 
			
		||||
void convert_short_float(float *out, short *in, int len);
 | 
			
		||||
 | 
			
		||||
#endif /* _CONVERT_H_ */
 | 
			
		||||
							
								
								
									
										676
									
								
								Transceiver52M/convolve.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										676
									
								
								Transceiver52M/convolve.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,676 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SSE Convolution
 | 
			
		||||
 * Copyright (C) 2012, 2013  Thomas Tsou <tom@tsou.cc>
 | 
			
		||||
 *
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Lesser General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
 * License along with this library; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <malloc.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_SSE3
 | 
			
		||||
#include <xmmintrin.h>
 | 
			
		||||
#include <pmmintrin.h>
 | 
			
		||||
 | 
			
		||||
/* 4-tap SSE complex-real convolution */
 | 
			
		||||
static void sse_conv_real4(float *restrict x,
 | 
			
		||||
			   float *restrict h,
 | 
			
		||||
			   float *restrict y,
 | 
			
		||||
			   int len)
 | 
			
		||||
{
 | 
			
		||||
	__m128 m0, m1, m2, m3, m4, m5, m6, m7;
 | 
			
		||||
 | 
			
		||||
	/* Load (aligned) filter taps */
 | 
			
		||||
	m0 = _mm_load_ps(&h[0]);
 | 
			
		||||
	m1 = _mm_load_ps(&h[4]);
 | 
			
		||||
	m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < len; i++) {
 | 
			
		||||
		/* Load (unaligned) input data */
 | 
			
		||||
		m0 = _mm_loadu_ps(&x[2 * i + 0]);
 | 
			
		||||
		m1 = _mm_loadu_ps(&x[2 * i + 4]);
 | 
			
		||||
		m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
		m3 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
 | 
			
		||||
		/* Quad multiply */
 | 
			
		||||
		m4 = _mm_mul_ps(m2, m7);
 | 
			
		||||
		m5 = _mm_mul_ps(m3, m7);
 | 
			
		||||
 | 
			
		||||
		/* Sum and store */
 | 
			
		||||
		m6 = _mm_hadd_ps(m4, m5);
 | 
			
		||||
		m0 = _mm_hadd_ps(m6, m6);
 | 
			
		||||
 | 
			
		||||
		_mm_store_ss(&y[2 * i + 0], m0);
 | 
			
		||||
		m0 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0, 3, 2, 1));
 | 
			
		||||
		_mm_store_ss(&y[2 * i + 1], m0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 8-tap SSE complex-real convolution */
 | 
			
		||||
static void sse_conv_real8(float *restrict x,
 | 
			
		||||
			   float *restrict h,
 | 
			
		||||
			   float *restrict y,
 | 
			
		||||
			   int len)
 | 
			
		||||
{
 | 
			
		||||
	__m128 m0, m1, m2, m3, m4, m5, m6, m7, m8, m9;
 | 
			
		||||
 | 
			
		||||
	/* Load (aligned) filter taps */
 | 
			
		||||
	m0 = _mm_load_ps(&h[0]);
 | 
			
		||||
	m1 = _mm_load_ps(&h[4]);
 | 
			
		||||
	m2 = _mm_load_ps(&h[8]);
 | 
			
		||||
	m3 = _mm_load_ps(&h[12]);
 | 
			
		||||
 | 
			
		||||
	m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
	m5 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < len; i++) {
 | 
			
		||||
		/* Load (unaligned) input data */
 | 
			
		||||
		m0 = _mm_loadu_ps(&x[2 * i + 0]);
 | 
			
		||||
		m1 = _mm_loadu_ps(&x[2 * i + 4]);
 | 
			
		||||
		m2 = _mm_loadu_ps(&x[2 * i + 8]);
 | 
			
		||||
		m3 = _mm_loadu_ps(&x[2 * i + 12]);
 | 
			
		||||
 | 
			
		||||
		m6 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
		m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
		m8 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
		m9 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
 | 
			
		||||
		/* Quad multiply */
 | 
			
		||||
		m6 = _mm_mul_ps(m6, m4);
 | 
			
		||||
		m7 = _mm_mul_ps(m7, m4);
 | 
			
		||||
		m8 = _mm_mul_ps(m8, m5);
 | 
			
		||||
		m9 = _mm_mul_ps(m9, m5);
 | 
			
		||||
 | 
			
		||||
		/* Sum and store */
 | 
			
		||||
		m6 = _mm_add_ps(m6, m8);
 | 
			
		||||
		m7 = _mm_add_ps(m7, m9);
 | 
			
		||||
		m6 = _mm_hadd_ps(m6, m7);
 | 
			
		||||
		m6 = _mm_hadd_ps(m6, m6);
 | 
			
		||||
 | 
			
		||||
		_mm_store_ss(&y[2 * i + 0], m6);
 | 
			
		||||
		m6 = _mm_shuffle_ps(m6, m6, _MM_SHUFFLE(0, 3, 2, 1));
 | 
			
		||||
		_mm_store_ss(&y[2 * i + 1], m6);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 12-tap SSE complex-real convolution */
 | 
			
		||||
static void sse_conv_real12(float *restrict x,
 | 
			
		||||
			    float *restrict h,
 | 
			
		||||
			    float *restrict y,
 | 
			
		||||
			    int len)
 | 
			
		||||
{
 | 
			
		||||
	__m128 m0, m1, m2, m3, m4, m5, m6, m7;
 | 
			
		||||
	__m128 m8, m9, m10, m11, m12, m13, m14;
 | 
			
		||||
 | 
			
		||||
	/* Load (aligned) filter taps */
 | 
			
		||||
	m0 = _mm_load_ps(&h[0]);
 | 
			
		||||
	m1 = _mm_load_ps(&h[4]);
 | 
			
		||||
	m2 = _mm_load_ps(&h[8]);
 | 
			
		||||
	m3 = _mm_load_ps(&h[12]);
 | 
			
		||||
	m4 = _mm_load_ps(&h[16]);
 | 
			
		||||
	m5 = _mm_load_ps(&h[20]);
 | 
			
		||||
 | 
			
		||||
	m12 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
	m13 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
	m14 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < len; i++) {
 | 
			
		||||
		/* Load (unaligned) input data */
 | 
			
		||||
		m0 = _mm_loadu_ps(&x[2 * i + 0]);
 | 
			
		||||
		m1 = _mm_loadu_ps(&x[2 * i + 4]);
 | 
			
		||||
		m2 = _mm_loadu_ps(&x[2 * i + 8]);
 | 
			
		||||
		m3 = _mm_loadu_ps(&x[2 * i + 12]);
 | 
			
		||||
 | 
			
		||||
		m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
		m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
		m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
		m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
 | 
			
		||||
		m0 = _mm_loadu_ps(&x[2 * i + 16]);
 | 
			
		||||
		m1 = _mm_loadu_ps(&x[2 * i + 20]);
 | 
			
		||||
 | 
			
		||||
		m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
		m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
 | 
			
		||||
		/* Quad multiply */
 | 
			
		||||
		m0 = _mm_mul_ps(m4, m12);
 | 
			
		||||
		m1 = _mm_mul_ps(m5, m12);
 | 
			
		||||
		m2 = _mm_mul_ps(m6, m13);
 | 
			
		||||
		m3 = _mm_mul_ps(m7, m13);
 | 
			
		||||
		m4 = _mm_mul_ps(m8, m14);
 | 
			
		||||
		m5 = _mm_mul_ps(m9, m14);
 | 
			
		||||
 | 
			
		||||
		/* Sum and store */
 | 
			
		||||
		m8  = _mm_add_ps(m0, m2);
 | 
			
		||||
		m9  = _mm_add_ps(m1, m3);
 | 
			
		||||
		m10 = _mm_add_ps(m8, m4);
 | 
			
		||||
		m11 = _mm_add_ps(m9, m5);
 | 
			
		||||
 | 
			
		||||
		m2 = _mm_hadd_ps(m10, m11);
 | 
			
		||||
		m3 = _mm_hadd_ps(m2, m2);
 | 
			
		||||
 | 
			
		||||
		_mm_store_ss(&y[2 * i + 0], m3);
 | 
			
		||||
		m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1));
 | 
			
		||||
		_mm_store_ss(&y[2 * i + 1], m3);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 16-tap SSE complex-real convolution */
 | 
			
		||||
static void sse_conv_real16(float *restrict x,
 | 
			
		||||
			    float *restrict h,
 | 
			
		||||
			    float *restrict y,
 | 
			
		||||
			    int len)
 | 
			
		||||
{
 | 
			
		||||
	__m128 m0, m1, m2, m3, m4, m5, m6, m7;
 | 
			
		||||
	__m128 m8, m9, m10, m11, m12, m13, m14, m15;
 | 
			
		||||
 | 
			
		||||
	/* Load (aligned) filter taps */
 | 
			
		||||
	m0 = _mm_load_ps(&h[0]);
 | 
			
		||||
	m1 = _mm_load_ps(&h[4]);
 | 
			
		||||
	m2 = _mm_load_ps(&h[8]);
 | 
			
		||||
	m3 = _mm_load_ps(&h[12]);
 | 
			
		||||
 | 
			
		||||
	m4 = _mm_load_ps(&h[16]);
 | 
			
		||||
	m5 = _mm_load_ps(&h[20]);
 | 
			
		||||
	m6 = _mm_load_ps(&h[24]);
 | 
			
		||||
	m7 = _mm_load_ps(&h[28]);
 | 
			
		||||
 | 
			
		||||
	m12 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
	m13 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
	m14 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
	m15 = _mm_shuffle_ps(m6, m7, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < len; i++) {
 | 
			
		||||
		/* Load (unaligned) input data */
 | 
			
		||||
		m0 = _mm_loadu_ps(&x[2 * i + 0]);
 | 
			
		||||
		m1 = _mm_loadu_ps(&x[2 * i + 4]);
 | 
			
		||||
		m2 = _mm_loadu_ps(&x[2 * i + 8]);
 | 
			
		||||
		m3 = _mm_loadu_ps(&x[2 * i + 12]);
 | 
			
		||||
 | 
			
		||||
		m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
		m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
		m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
		m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
 | 
			
		||||
		m0 = _mm_loadu_ps(&x[2 * i + 16]);
 | 
			
		||||
		m1 = _mm_loadu_ps(&x[2 * i + 20]);
 | 
			
		||||
		m2 = _mm_loadu_ps(&x[2 * i + 24]);
 | 
			
		||||
		m3 = _mm_loadu_ps(&x[2 * i + 28]);
 | 
			
		||||
 | 
			
		||||
		m8  = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
		m9  = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
		m10 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
		m11 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
 | 
			
		||||
		/* Quad multiply */
 | 
			
		||||
		m0 = _mm_mul_ps(m4, m12);
 | 
			
		||||
		m1 = _mm_mul_ps(m5, m12);
 | 
			
		||||
		m2 = _mm_mul_ps(m6, m13);
 | 
			
		||||
		m3 = _mm_mul_ps(m7, m13);
 | 
			
		||||
 | 
			
		||||
		m4 = _mm_mul_ps(m8, m14);
 | 
			
		||||
		m5 = _mm_mul_ps(m9, m14);
 | 
			
		||||
		m6 = _mm_mul_ps(m10, m15);
 | 
			
		||||
		m7 = _mm_mul_ps(m11, m15);
 | 
			
		||||
 | 
			
		||||
		/* Sum and store */
 | 
			
		||||
		m8  = _mm_add_ps(m0, m2);
 | 
			
		||||
		m9  = _mm_add_ps(m1, m3);
 | 
			
		||||
		m10 = _mm_add_ps(m4, m6);
 | 
			
		||||
		m11 = _mm_add_ps(m5, m7);
 | 
			
		||||
 | 
			
		||||
		m0 = _mm_add_ps(m8, m10);
 | 
			
		||||
		m1 = _mm_add_ps(m9, m11);
 | 
			
		||||
		m2 = _mm_hadd_ps(m0, m1);
 | 
			
		||||
		m3 = _mm_hadd_ps(m2, m2);
 | 
			
		||||
 | 
			
		||||
		_mm_store_ss(&y[2 * i + 0], m3);
 | 
			
		||||
		m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1));
 | 
			
		||||
		_mm_store_ss(&y[2 * i + 1], m3);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 20-tap SSE complex-real convolution */
 | 
			
		||||
static void sse_conv_real20(float *restrict x,
 | 
			
		||||
			    float *restrict h,
 | 
			
		||||
			    float *restrict y,
 | 
			
		||||
			    int len)
 | 
			
		||||
{
 | 
			
		||||
	__m128 m0, m1, m2, m3, m4, m5, m6, m7;
 | 
			
		||||
	__m128 m8, m9, m11, m12, m13, m14, m15;
 | 
			
		||||
 | 
			
		||||
	/* Load (aligned) filter taps */
 | 
			
		||||
	m0 = _mm_load_ps(&h[0]);
 | 
			
		||||
	m1 = _mm_load_ps(&h[4]);
 | 
			
		||||
	m2 = _mm_load_ps(&h[8]);
 | 
			
		||||
	m3 = _mm_load_ps(&h[12]);
 | 
			
		||||
	m4 = _mm_load_ps(&h[16]);
 | 
			
		||||
	m5 = _mm_load_ps(&h[20]);
 | 
			
		||||
	m6 = _mm_load_ps(&h[24]);
 | 
			
		||||
	m7 = _mm_load_ps(&h[28]);
 | 
			
		||||
	m8 = _mm_load_ps(&h[32]);
 | 
			
		||||
	m9 = _mm_load_ps(&h[36]);
 | 
			
		||||
 | 
			
		||||
	m11 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
	m12 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
	m13 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
	m14 = _mm_shuffle_ps(m6, m7, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
	m15 = _mm_shuffle_ps(m8, m9, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < len; i++) {
 | 
			
		||||
		/* Multiply-accumulate first 12 taps */
 | 
			
		||||
		m0 = _mm_loadu_ps(&x[2 * i + 0]);
 | 
			
		||||
		m1 = _mm_loadu_ps(&x[2 * i + 4]);
 | 
			
		||||
		m2 = _mm_loadu_ps(&x[2 * i + 8]);
 | 
			
		||||
		m3 = _mm_loadu_ps(&x[2 * i + 12]);
 | 
			
		||||
		m4 = _mm_loadu_ps(&x[2 * i + 16]);
 | 
			
		||||
		m5 = _mm_loadu_ps(&x[2 * i + 20]);
 | 
			
		||||
 | 
			
		||||
		m6  = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
		m7  = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
		m8  = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
		m9  = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
		m0  = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
		m1  = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
 | 
			
		||||
		m2 = _mm_mul_ps(m6, m11);
 | 
			
		||||
		m3 = _mm_mul_ps(m7, m11);
 | 
			
		||||
		m4 = _mm_mul_ps(m8, m12);
 | 
			
		||||
		m5 = _mm_mul_ps(m9, m12);
 | 
			
		||||
		m6 = _mm_mul_ps(m0, m13);
 | 
			
		||||
		m7 = _mm_mul_ps(m1, m13);
 | 
			
		||||
 | 
			
		||||
		m0  = _mm_add_ps(m2, m4);
 | 
			
		||||
		m1  = _mm_add_ps(m3, m5);
 | 
			
		||||
		m8  = _mm_add_ps(m0, m6);
 | 
			
		||||
		m9  = _mm_add_ps(m1, m7);
 | 
			
		||||
 | 
			
		||||
		/* Multiply-accumulate last 8 taps */
 | 
			
		||||
		m0 = _mm_loadu_ps(&x[2 * i + 24]);
 | 
			
		||||
		m1 = _mm_loadu_ps(&x[2 * i + 28]);
 | 
			
		||||
		m2 = _mm_loadu_ps(&x[2 * i + 32]);
 | 
			
		||||
		m3 = _mm_loadu_ps(&x[2 * i + 36]);
 | 
			
		||||
 | 
			
		||||
		m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
		m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
		m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
		m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
 | 
			
		||||
		m0 = _mm_mul_ps(m4, m14);
 | 
			
		||||
		m1 = _mm_mul_ps(m5, m14);
 | 
			
		||||
		m2 = _mm_mul_ps(m6, m15);
 | 
			
		||||
		m3 = _mm_mul_ps(m7, m15);
 | 
			
		||||
 | 
			
		||||
		m4  = _mm_add_ps(m0, m2);
 | 
			
		||||
		m5  = _mm_add_ps(m1, m3);
 | 
			
		||||
 | 
			
		||||
		/* Final sum and store */
 | 
			
		||||
		m0 = _mm_add_ps(m8, m4);
 | 
			
		||||
		m1 = _mm_add_ps(m9, m5);
 | 
			
		||||
		m2 = _mm_hadd_ps(m0, m1);
 | 
			
		||||
		m3 = _mm_hadd_ps(m2, m2);
 | 
			
		||||
 | 
			
		||||
		_mm_store_ss(&y[2 * i + 0], m3);
 | 
			
		||||
		m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1));
 | 
			
		||||
		_mm_store_ss(&y[2 * i + 1], m3);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 4*N-tap SSE complex-complex convolution */
 | 
			
		||||
static void sse_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len)
 | 
			
		||||
{
 | 
			
		||||
	__m128 m0, m1, m2, m3, m4, m5, m6, m7;
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < len; i++) {
 | 
			
		||||
		/* Zero */
 | 
			
		||||
		m6 = _mm_setzero_ps();
 | 
			
		||||
		m7 = _mm_setzero_ps();
 | 
			
		||||
 | 
			
		||||
		for (int n = 0; n < h_len / 4; n++) {
 | 
			
		||||
			/* Load (aligned) filter taps */
 | 
			
		||||
			m0 = _mm_load_ps(&h[8 * n + 0]);
 | 
			
		||||
			m1 = _mm_load_ps(&h[8 * n + 4]);
 | 
			
		||||
			m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
			m3 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
 | 
			
		||||
			/* Load (unaligned) input data */
 | 
			
		||||
			m0 = _mm_loadu_ps(&x[2 * i + 8 * n + 0]);
 | 
			
		||||
			m1 = _mm_loadu_ps(&x[2 * i + 8 * n + 4]);
 | 
			
		||||
			m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
			m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
 | 
			
		||||
			/* Quad multiply */
 | 
			
		||||
			m0 = _mm_mul_ps(m2, m4);
 | 
			
		||||
			m1 = _mm_mul_ps(m3, m5);
 | 
			
		||||
 | 
			
		||||
			m2 = _mm_mul_ps(m2, m5);
 | 
			
		||||
			m3 = _mm_mul_ps(m3, m4);
 | 
			
		||||
 | 
			
		||||
			/* Sum */
 | 
			
		||||
			m0 = _mm_sub_ps(m0, m1);
 | 
			
		||||
			m2 = _mm_add_ps(m2, m3);
 | 
			
		||||
 | 
			
		||||
			/* Accumulate */
 | 
			
		||||
			m6 = _mm_add_ps(m6, m0);
 | 
			
		||||
			m7 = _mm_add_ps(m7, m2);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		m0 = _mm_hadd_ps(m6, m7);
 | 
			
		||||
		m0 = _mm_hadd_ps(m0, m0);
 | 
			
		||||
 | 
			
		||||
		_mm_store_ss(&y[2 * i + 0], m0);
 | 
			
		||||
		m0 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0, 3, 2, 1));
 | 
			
		||||
		_mm_store_ss(&y[2 * i + 1], m0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 8*N-tap SSE complex-complex convolution */
 | 
			
		||||
static void sse_conv_cmplx_8n(float *x, float *h, float *y, int h_len, int len)
 | 
			
		||||
{
 | 
			
		||||
	__m128 m0, m1, m2, m3, m4, m5, m6, m7;
 | 
			
		||||
	__m128 m8, m9, m10, m11, m12, m13, m14, m15;
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < len; i++) {
 | 
			
		||||
		/* Zero */
 | 
			
		||||
		m12 = _mm_setzero_ps();
 | 
			
		||||
		m13 = _mm_setzero_ps();
 | 
			
		||||
		m14 = _mm_setzero_ps();
 | 
			
		||||
		m15 = _mm_setzero_ps();
 | 
			
		||||
 | 
			
		||||
		for (int n = 0; n < h_len / 8; n++) {
 | 
			
		||||
			/* Load (aligned) filter taps */
 | 
			
		||||
			m0 = _mm_load_ps(&h[16 * n + 0]);
 | 
			
		||||
			m1 = _mm_load_ps(&h[16 * n + 4]);
 | 
			
		||||
			m2 = _mm_load_ps(&h[16 * n + 8]);
 | 
			
		||||
			m3 = _mm_load_ps(&h[16 * n + 12]);
 | 
			
		||||
 | 
			
		||||
			m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
			m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
			m6 = _mm_shuffle_ps(m2, m2, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
			m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
 | 
			
		||||
			/* Load (unaligned) input data */
 | 
			
		||||
			m0 = _mm_loadu_ps(&x[2 * i + 16 * n + 0]);
 | 
			
		||||
			m1 = _mm_loadu_ps(&x[2 * i + 16 * n + 4]);
 | 
			
		||||
			m2 = _mm_loadu_ps(&x[2 * i + 16 * n + 8]);
 | 
			
		||||
			m3 = _mm_loadu_ps(&x[2 * i + 16 * n + 12]);
 | 
			
		||||
 | 
			
		||||
			m8  = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
			m9  = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
			m10 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
 | 
			
		||||
			m11 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
 | 
			
		||||
 | 
			
		||||
			/* Quad multiply */
 | 
			
		||||
			m0 = _mm_mul_ps(m4, m8);
 | 
			
		||||
			m1 = _mm_mul_ps(m5, m9);
 | 
			
		||||
			m2 = _mm_mul_ps(m6, m10);
 | 
			
		||||
			m3 = _mm_mul_ps(m7, m11);
 | 
			
		||||
 | 
			
		||||
			m4 = _mm_mul_ps(m4, m9);
 | 
			
		||||
			m5 = _mm_mul_ps(m5, m8);
 | 
			
		||||
			m6 = _mm_mul_ps(m6, m11);
 | 
			
		||||
			m7 = _mm_mul_ps(m7, m10);
 | 
			
		||||
 | 
			
		||||
			/* Sum */
 | 
			
		||||
			m0 = _mm_sub_ps(m0, m1);
 | 
			
		||||
			m2 = _mm_sub_ps(m2, m3);
 | 
			
		||||
			m4 = _mm_add_ps(m4, m5);
 | 
			
		||||
			m6 = _mm_add_ps(m6, m7);
 | 
			
		||||
 | 
			
		||||
			/* Accumulate */
 | 
			
		||||
			m12 = _mm_add_ps(m12, m0);
 | 
			
		||||
			m13 = _mm_add_ps(m13, m2);
 | 
			
		||||
			m14 = _mm_add_ps(m14, m4);
 | 
			
		||||
			m15 = _mm_add_ps(m15, m6);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		m0 = _mm_add_ps(m12, m13);
 | 
			
		||||
		m1 = _mm_add_ps(m14, m15);
 | 
			
		||||
		m2 = _mm_hadd_ps(m0, m1);
 | 
			
		||||
		m2 = _mm_hadd_ps(m2, m2);
 | 
			
		||||
 | 
			
		||||
		_mm_store_ss(&y[2 * i + 0], m2);
 | 
			
		||||
		m2 = _mm_shuffle_ps(m2, m2, _MM_SHUFFLE(0, 3, 2, 1));
 | 
			
		||||
		_mm_store_ss(&y[2 * i + 1], m2);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Base multiply and accumulate complex-real */
 | 
			
		||||
static void mac_real(float *x, float *h, float *y)
 | 
			
		||||
{
 | 
			
		||||
	y[0] += x[0] * h[0];
 | 
			
		||||
	y[1] += x[1] * h[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Base multiply and accumulate complex-complex */
 | 
			
		||||
static void mac_cmplx(float *x, float *h, float *y)
 | 
			
		||||
{
 | 
			
		||||
	y[0] += x[0] * h[0] - x[1] * h[1];
 | 
			
		||||
	y[1] += x[0] * h[1] + x[1] * h[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Base vector complex-complex multiply and accumulate */
 | 
			
		||||
static void mac_real_vec_n(float *x, float *h, float *y,
 | 
			
		||||
			   int len, int step, int offset)
 | 
			
		||||
{
 | 
			
		||||
	for (int i = offset; i < len; i += step)
 | 
			
		||||
		mac_real(&x[2 * i], &h[2 * i], y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Base vector complex-complex multiply and accumulate */
 | 
			
		||||
static void mac_cmplx_vec_n(float *x, float *h, float *y,
 | 
			
		||||
			    int len, int step, int offset)
 | 
			
		||||
{
 | 
			
		||||
	for (int i = offset; i < len; i += step)
 | 
			
		||||
		mac_cmplx(&x[2 * i], &h[2 * i], y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Base complex-real convolution */
 | 
			
		||||
static int _base_convolve_real(float *x, int x_len,
 | 
			
		||||
			       float *h, int h_len,
 | 
			
		||||
			       float *y, int y_len,
 | 
			
		||||
			       int start, int len,
 | 
			
		||||
			       int step, int offset)
 | 
			
		||||
{
 | 
			
		||||
	for (int i = 0; i < len; i++) {
 | 
			
		||||
		mac_real_vec_n(&x[2 * (i - (h_len - 1) + start)],
 | 
			
		||||
			       h,
 | 
			
		||||
			       &y[2 * i], h_len,
 | 
			
		||||
			       step, offset);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Base complex-complex convolution */
 | 
			
		||||
static int _base_convolve_complex(float *x, int x_len,
 | 
			
		||||
				  float *h, int h_len,
 | 
			
		||||
				  float *y, int y_len,
 | 
			
		||||
				  int start, int len,
 | 
			
		||||
				  int step, int offset)
 | 
			
		||||
{
 | 
			
		||||
	for (int i = 0; i < len; i++) {
 | 
			
		||||
		mac_cmplx_vec_n(&x[2 * (i - (h_len - 1) + start)],
 | 
			
		||||
				h,
 | 
			
		||||
				&y[2 * i],
 | 
			
		||||
				h_len, step, offset);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Buffer validity checks */
 | 
			
		||||
static int bounds_check(int x_len, int h_len, int y_len,
 | 
			
		||||
			int start, int len, int step)
 | 
			
		||||
{
 | 
			
		||||
	if ((x_len < 1) || (h_len < 1) ||
 | 
			
		||||
	    (y_len < 1) || (len < 1) || (step < 1)) {
 | 
			
		||||
		fprintf(stderr, "Convolve: Invalid input\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ((start + len > x_len) || (len > y_len) || (x_len < h_len)) {
 | 
			
		||||
		fprintf(stderr, "Convolve: Boundary exception\n");
 | 
			
		||||
		fprintf(stderr, "start: %i, len: %i, x: %i, h: %i, y: %i\n",
 | 
			
		||||
				start, len, x_len, h_len, y_len);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* API: Aligned complex-real */
 | 
			
		||||
int convolve_real(float *x, int x_len,
 | 
			
		||||
		  float *h, int h_len,
 | 
			
		||||
		  float *y, int y_len,
 | 
			
		||||
		  int start, int len,
 | 
			
		||||
		  int step, int offset)
 | 
			
		||||
{
 | 
			
		||||
	void (*conv_func)(float *, float *, float *, int);
 | 
			
		||||
 | 
			
		||||
	if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	memset(y, 0, len * 2 * sizeof(float));
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_SSE3
 | 
			
		||||
	if (step <= 4) {
 | 
			
		||||
		switch (h_len) {
 | 
			
		||||
		case 4:
 | 
			
		||||
			conv_func = sse_conv_real4;
 | 
			
		||||
			break;
 | 
			
		||||
		case 8:
 | 
			
		||||
			conv_func = sse_conv_real8;
 | 
			
		||||
			break;
 | 
			
		||||
		case 12:
 | 
			
		||||
			conv_func = sse_conv_real12;
 | 
			
		||||
			break;
 | 
			
		||||
		case 16:
 | 
			
		||||
			conv_func = sse_conv_real16;
 | 
			
		||||
			break;
 | 
			
		||||
		case 20:
 | 
			
		||||
			conv_func = sse_conv_real20;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			conv_func = NULL;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		conv_func = NULL;
 | 
			
		||||
	}
 | 
			
		||||
#else
 | 
			
		||||
	conv_func = NULL;
 | 
			
		||||
#endif
 | 
			
		||||
	if (conv_func) {
 | 
			
		||||
		conv_func(&x[2 * (-(h_len - 1) + start)],
 | 
			
		||||
			  h, y, len);
 | 
			
		||||
	} else {
 | 
			
		||||
		_base_convolve_real(x, x_len,
 | 
			
		||||
				    h, h_len,
 | 
			
		||||
				    y, y_len,
 | 
			
		||||
				    start, len, step, offset);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* API: Aligned complex-complex */
 | 
			
		||||
int convolve_complex(float *x, int x_len,
 | 
			
		||||
		     float *h, int h_len,
 | 
			
		||||
		     float *y, int y_len,
 | 
			
		||||
		     int start, int len,
 | 
			
		||||
		     int step, int offset)
 | 
			
		||||
{
 | 
			
		||||
	void (*conv_func)(float *, float *, float *, int, int);
 | 
			
		||||
 | 
			
		||||
	if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	memset(y, 0, len * 2 * sizeof(float));
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_SSE3
 | 
			
		||||
	if (step <= 4) {
 | 
			
		||||
		if (!(h_len % 8))
 | 
			
		||||
			conv_func = sse_conv_cmplx_8n;
 | 
			
		||||
		else if (!(h_len % 4))
 | 
			
		||||
			conv_func = sse_conv_cmplx_4n;
 | 
			
		||||
	} else
 | 
			
		||||
		conv_func = NULL;
 | 
			
		||||
#else
 | 
			
		||||
	conv_func = NULL;
 | 
			
		||||
#endif
 | 
			
		||||
	if (conv_func) {
 | 
			
		||||
		conv_func(&x[2 * (-(h_len - 1) + start)],
 | 
			
		||||
			  h, y, h_len, len);
 | 
			
		||||
	} else {
 | 
			
		||||
		_base_convolve_complex(x, x_len,
 | 
			
		||||
				       h, h_len,
 | 
			
		||||
				       y, y_len,
 | 
			
		||||
				       start, len, step, offset);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* API: Non-aligned (no SSE) complex-real */
 | 
			
		||||
int base_convolve_real(float *x, int x_len,
 | 
			
		||||
		       float *h, int h_len,
 | 
			
		||||
		       float *y, int y_len,
 | 
			
		||||
		       int start, int len,
 | 
			
		||||
		       int step, int offset)
 | 
			
		||||
{
 | 
			
		||||
	if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	memset(y, 0, len * 2 * sizeof(float));
 | 
			
		||||
 | 
			
		||||
	return _base_convolve_real(x, x_len,
 | 
			
		||||
				   h, h_len,
 | 
			
		||||
				   y, y_len,
 | 
			
		||||
				   start, len, step, offset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* API: Non-aligned (no SSE) complex-complex */
 | 
			
		||||
int base_convolve_complex(float *x, int x_len,
 | 
			
		||||
			  float *h, int h_len,
 | 
			
		||||
			  float *y, int y_len,
 | 
			
		||||
			  int start, int len,
 | 
			
		||||
			  int step, int offset)
 | 
			
		||||
{
 | 
			
		||||
	if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	memset(y, 0, len * 2 * sizeof(float));
 | 
			
		||||
 | 
			
		||||
	return _base_convolve_complex(x, x_len,
 | 
			
		||||
				      h, h_len,
 | 
			
		||||
				      y, y_len,
 | 
			
		||||
				      start, len, step, offset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Aligned filter tap allocation */
 | 
			
		||||
void *convolve_h_alloc(int len)
 | 
			
		||||
{
 | 
			
		||||
#ifdef HAVE_SSE3
 | 
			
		||||
	return memalign(16, len * 2 * sizeof(float));
 | 
			
		||||
#else
 | 
			
		||||
	return malloc(len * 2 * sizeof(float));
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								Transceiver52M/convolve.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								Transceiver52M/convolve.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
#ifndef _CONVOLVE_H_
 | 
			
		||||
#define _CONVOLVE_H_
 | 
			
		||||
 | 
			
		||||
void *convolve_h_alloc(int num);
 | 
			
		||||
 | 
			
		||||
int convolve_real(float *x, int x_len,
 | 
			
		||||
		  float *h, int h_len,
 | 
			
		||||
		  float *y, int y_len,
 | 
			
		||||
		  int start, int len,
 | 
			
		||||
		  int step, int offset);
 | 
			
		||||
 | 
			
		||||
int convolve_complex(float *x, int x_len,
 | 
			
		||||
		     float *h, int h_len,
 | 
			
		||||
		     float *y, int y_len,
 | 
			
		||||
		     int start, int len,
 | 
			
		||||
		     int step, int offset);
 | 
			
		||||
 | 
			
		||||
int base_convolve_real(float *x, int x_len,
 | 
			
		||||
		       float *h, int h_len,
 | 
			
		||||
		       float *y, int y_len,
 | 
			
		||||
		       int start, int len,
 | 
			
		||||
		       int step, int offset);
 | 
			
		||||
 | 
			
		||||
int base_convolve_complex(float *x, int x_len,
 | 
			
		||||
			  float *h, int h_len,
 | 
			
		||||
			  float *y, int y_len,
 | 
			
		||||
			  int start, int len,
 | 
			
		||||
			  int step, int offset);
 | 
			
		||||
 | 
			
		||||
#endif /* _CONVOLVE_H_ */
 | 
			
		||||
							
								
								
									
										83
									
								
								Transceiver52M/laurent.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								Transceiver52M/laurent.m
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
			
		||||
%
 | 
			
		||||
% Laurent decomposition of GMSK signals
 | 
			
		||||
% Generates C0, C1, and C2 pulse shapes
 | 
			
		||||
%
 | 
			
		||||
% Pierre Laurent, "Exact and Approximate Construction of Digital Phase
 | 
			
		||||
%   Modulations by Superposition of Amplitude Modulated Pulses", IEEE
 | 
			
		||||
%   Transactions of Communications, Vol. 34, No. 2, Feb 1986.
 | 
			
		||||
%
 | 
			
		||||
% Author: Thomas Tsou <tom@tsou.cc>
 | 
			
		||||
%
 | 
			
		||||
 | 
			
		||||
% Modulation parameters
 | 
			
		||||
oversamp = 16;
 | 
			
		||||
L = 3;
 | 
			
		||||
f = 270.83333e3;
 | 
			
		||||
T = 1/f;
 | 
			
		||||
h = 0.5;
 | 
			
		||||
BT = 0.30;
 | 
			
		||||
B = BT / T;
 | 
			
		||||
 | 
			
		||||
% Generate sampling points for L symbol periods
 | 
			
		||||
t = -(L*T/2):T/oversamp:(L*T/2);
 | 
			
		||||
t = t(1:end-1) + (T/oversamp/2);
 | 
			
		||||
 | 
			
		||||
% Generate Gaussian pulse
 | 
			
		||||
g = qfunc(2*pi*B*(t - T/2)/(log(2)^.5)) - qfunc(2*pi*B*(t + T/2)/(log(2)^.5));
 | 
			
		||||
g = g / sum(g) * pi/2;
 | 
			
		||||
g = [0 g];
 | 
			
		||||
 | 
			
		||||
% Integrate phase 
 | 
			
		||||
q = 0;
 | 
			
		||||
for i = 1:size(g,2);
 | 
			
		||||
    q(i) = sum(g(1:i));
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
% Compute two sided "generalized phase pulse" function
 | 
			
		||||
s = 0;
 | 
			
		||||
for i = 1:size(g,2);
 | 
			
		||||
    s(i) = sin(q(i)) / sin(pi*h);
 | 
			
		||||
end
 | 
			
		||||
for i = (size(g,2) + 1):(2 * size(g,2) - 1);
 | 
			
		||||
    s(i) = sin(pi*h - q(i - (size(g,2) - 1))) / sin(pi*h);
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
% Compute C0 pulse: valid for all L values
 | 
			
		||||
c0 = s(1:end-(oversamp*(L-1)));
 | 
			
		||||
for i = 1:L-1;
 | 
			
		||||
    c0 = c0 .* s((1 + i*oversamp):end-(oversamp*(L - 1 - i)));
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
% Compute C1 pulse: valid for L = 3 only!
 | 
			
		||||
%   C1 = S0 * S4 * S2
 | 
			
		||||
c1 = s(1:end-(oversamp*(4)));
 | 
			
		||||
c1 = c1 .* s((1 + 4*oversamp):end-(oversamp*(4 - 1 - 3)));
 | 
			
		||||
c1 = c1 .* s((1 + 2*oversamp):end-(oversamp*(4 - 1 - 1)));
 | 
			
		||||
 | 
			
		||||
% Compute C2 pulse: valid for L = 3 only!
 | 
			
		||||
%   C2 = S0 * S1 * S5
 | 
			
		||||
c2 = s(1:end-(oversamp*(5)));
 | 
			
		||||
c2 = c2 .* s((1 + 1*oversamp):end-(oversamp*(5 - 1 - 0)));
 | 
			
		||||
c2 = c2 .* s((1 + 5*oversamp):end-(oversamp*(5 - 1 - 4)));
 | 
			
		||||
 | 
			
		||||
% Plot C0, C1, C2 Laurent pulse series
 | 
			
		||||
figure(1);
 | 
			
		||||
hold off;
 | 
			
		||||
plot((0:size(c0,2)-1)/oversamp - 2,c0, 'b');
 | 
			
		||||
hold on;
 | 
			
		||||
plot((0:size(c1,2)-1)/oversamp - 2,c1, 'r');
 | 
			
		||||
plot((0:size(c2,2)-1)/oversamp - 2,c2, 'g');
 | 
			
		||||
 | 
			
		||||
% Generate OpenBTS pulse
 | 
			
		||||
numSamples = size(c0,2); 
 | 
			
		||||
centerPoint = (numSamples - 1)/2;
 | 
			
		||||
i = ((0:numSamples) - centerPoint) / oversamp;
 | 
			
		||||
xP = .96*exp(-1.1380*i.^2 - 0.527*i.^4);
 | 
			
		||||
xP = xP / max(xP) * max(c0);
 | 
			
		||||
 | 
			
		||||
% Plot C0 pulse compared to OpenBTS pulse
 | 
			
		||||
figure(2);
 | 
			
		||||
hold off;
 | 
			
		||||
plot((0:size(c0,2)-1)/oversamp, c0, 'b');
 | 
			
		||||
hold on;
 | 
			
		||||
plot((0:size(xP,2)-1)/oversamp, xP, 'r');
 | 
			
		||||
							
								
								
									
										201
									
								
								Transceiver52M/multiTRX.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								Transceiver52M/multiTRX.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,201 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2012  Thomas Tsou <ttsou@vt.edu>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 * See the COPYING file in the main directory for details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
 | 
			
		||||
#include <GSMCommon.h>
 | 
			
		||||
#include <Logger.h>
 | 
			
		||||
#include <Configuration.h>
 | 
			
		||||
 | 
			
		||||
#include "Transceiver.h"
 | 
			
		||||
#include "radioDevice.h"
 | 
			
		||||
 | 
			
		||||
#define CONFIGDB	"/etc/OpenBTS/OpenBTS.db"
 | 
			
		||||
 | 
			
		||||
ConfigurationTable gConfig(CONFIGDB);
 | 
			
		||||
 | 
			
		||||
volatile bool gbShutdown = false;
 | 
			
		||||
 | 
			
		||||
int Transceiver::mTSC = -1;
 | 
			
		||||
 | 
			
		||||
static void sigHandler(int signum)
 | 
			
		||||
{
 | 
			
		||||
	LOG(NOTICE) << "Received shutdown signal";
 | 
			
		||||
	gbShutdown = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int setupSignals()
 | 
			
		||||
{
 | 
			
		||||
	struct sigaction action;
 | 
			
		||||
 | 
			
		||||
	action.sa_handler = sigHandler;
 | 
			
		||||
	sigemptyset(&action.sa_mask);
 | 
			
		||||
	action.sa_flags = 0;
 | 
			
		||||
 | 
			
		||||
	if (sigaction(SIGINT, &action, NULL) < 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
	if (sigaction(SIGTERM, &action, NULL) < 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Attempt to open and test the database file before
 | 
			
		||||
 * accessing the configuration table. We do this because
 | 
			
		||||
 * the global table constructor cannot provide notification
 | 
			
		||||
 * in the event of failure.
 | 
			
		||||
 */
 | 
			
		||||
static int testConfig(const char *filename)
 | 
			
		||||
{
 | 
			
		||||
	int rc, val = 9999;
 | 
			
		||||
	sqlite3 *db;
 | 
			
		||||
	std::string test = "sadf732zdvj2";
 | 
			
		||||
 | 
			
		||||
	const char *keys[3] = {
 | 
			
		||||
		"Log.Level",
 | 
			
		||||
		"TRX.Port",
 | 
			
		||||
		"TRX.IP",
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	/* Try to open the database	*/
 | 
			
		||||
	rc = sqlite3_open(filename, &db);
 | 
			
		||||
	if (rc || !db) {
 | 
			
		||||
		std::cerr << "Config: Database could not be opened"
 | 
			
		||||
			  << std::endl;
 | 
			
		||||
		return -1;
 | 
			
		||||
	} else {
 | 
			
		||||
		sqlite3_close(db);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Attempt to set a value in the global config */
 | 
			
		||||
	if (!gConfig.set(test, val)) {
 | 
			
		||||
		std::cerr << "Config: Failed to set test key - "
 | 
			
		||||
			  << "permission to access the database?"
 | 
			
		||||
			  << std::endl;
 | 
			
		||||
		return -1;
 | 
			
		||||
	} else {
 | 
			
		||||
		gConfig.remove(test);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Attempt to query */
 | 
			
		||||
	for (int i = 0; i < 3; i++) {
 | 
			
		||||
		try {
 | 
			
		||||
			gConfig.getStr(keys[i]); 
 | 
			
		||||
		} catch (...) {
 | 
			
		||||
			std::cerr << "Config: Failed query on "
 | 
			
		||||
				  << keys[i] << std::endl;
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0; 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	int trxPort, numARFCN = 1;
 | 
			
		||||
	std::string logLevel, trxAddr, deviceArgs = "";
 | 
			
		||||
 | 
			
		||||
	switch (argc) {
 | 
			
		||||
	case 3:
 | 
			
		||||
		deviceArgs = std::string(argv[2]);
 | 
			
		||||
	case 2:
 | 
			
		||||
		numARFCN = atoi(argv[1]);
 | 
			
		||||
		if (numARFCN > CHAN_MAX) {
 | 
			
		||||
			LOG(ALERT) << numARFCN  << " channels not supported "
 | 
			
		||||
						<< " with with current build";
 | 
			
		||||
			exit(-1);
 | 
			
		||||
		}
 | 
			
		||||
	case 1:
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		std::cout << argv[0] << " <chans> <device args>" << std::endl;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (setupSignals() < 0) {
 | 
			
		||||
		LOG(ERR) << "Failed to setup signal handlers, exiting...";
 | 
			
		||||
		exit(-1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Configure logger */
 | 
			
		||||
	if (testConfig(CONFIGDB) < 0) {
 | 
			
		||||
		std::cerr << "Config: Database failure" << std::endl;
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logLevel = gConfig.getStr("Log.Level");
 | 
			
		||||
	trxPort = gConfig.getNum("TRX.Port");
 | 
			
		||||
	trxAddr = gConfig.getStr("TRX.IP");
 | 
			
		||||
	gLogInit("transceiver", logLevel.c_str(), LOG_LOCAL7);
 | 
			
		||||
 | 
			
		||||
	srandom(time(NULL));
 | 
			
		||||
 | 
			
		||||
	RadioDevice *device = RadioDevice::make(SAMPSPERSYM);
 | 
			
		||||
	int radioType = device->open(deviceArgs);
 | 
			
		||||
	if (radioType < 0) {
 | 
			
		||||
		LOG(ALERT) << "Failed to open device, exiting...";
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	RadioInterface *radio;
 | 
			
		||||
	switch (radioType) {
 | 
			
		||||
	case RadioDevice::NORMAL:
 | 
			
		||||
		radio = new RadioInterface(device, numARFCN);
 | 
			
		||||
		break;
 | 
			
		||||
	case RadioDevice::RESAMP:
 | 
			
		||||
	default:
 | 
			
		||||
		LOG(ALERT) << "Unsupported configuration";
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DriveLoop *drive;
 | 
			
		||||
	drive = new DriveLoop(trxPort, trxAddr.c_str(), radio, numARFCN, 0);
 | 
			
		||||
	if (!drive->init()) {
 | 
			
		||||
		LOG(ALERT) << "Failed to initialize drive loop";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Transceiver *trx[CHAN_MAX];
 | 
			
		||||
	bool primary = true;
 | 
			
		||||
	for (int i = 0; i < numARFCN; i++) {
 | 
			
		||||
		trx[i] = new Transceiver(trxPort + 2 * i, trxAddr.c_str(),
 | 
			
		||||
					 drive, radio, SAMPSPERSYM,
 | 
			
		||||
					 i, primary);
 | 
			
		||||
		trx[i]->start();
 | 
			
		||||
		primary = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while (!gbShutdown)
 | 
			
		||||
		sleep(1);
 | 
			
		||||
 | 
			
		||||
	LOG(NOTICE) << "Shutting down transceivers...";
 | 
			
		||||
	for (int i = 0; i < numARFCN; i++)
 | 
			
		||||
		trx[i]->shutdown();
 | 
			
		||||
 | 
			
		||||
	/* Allow time for threads to end before we start freeing objects */
 | 
			
		||||
	sleep(2);
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < numARFCN; i++)
 | 
			
		||||
		delete trx[i];
 | 
			
		||||
	delete drive;
 | 
			
		||||
	delete radio;
 | 
			
		||||
	delete device;
 | 
			
		||||
}
 | 
			
		||||
@@ -21,6 +21,8 @@
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define GSMRATE       1625e3/6
 | 
			
		||||
 | 
			
		||||
/** a 64-bit virtual timestamp for radio data */
 | 
			
		||||
typedef unsigned long long TIMESTAMP;
 | 
			
		||||
 | 
			
		||||
@@ -29,12 +31,17 @@ class RadioDevice {
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
  /* Available transport bus types */
 | 
			
		||||
  enum busType { USB, NET };
 | 
			
		||||
  enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED };
 | 
			
		||||
 | 
			
		||||
  static RadioDevice *make(double desiredSampleRate, bool skipRx = false);
 | 
			
		||||
  /* Radio interface types */
 | 
			
		||||
  enum RadioInterfaceType { NORMAL, RESAMP };
 | 
			
		||||
 | 
			
		||||
  static RadioDevice *make(int sps, bool skipRx = false);
 | 
			
		||||
 | 
			
		||||
  virtual ~RadioDevice() {};
 | 
			
		||||
 | 
			
		||||
  /** Initialize the USRP */
 | 
			
		||||
  virtual bool open(const std::string &args)=0;
 | 
			
		||||
  virtual int open(const std::string &args)=0;
 | 
			
		||||
 | 
			
		||||
  /** Start the USRP */
 | 
			
		||||
  virtual bool start()=0;
 | 
			
		||||
@@ -42,8 +49,8 @@ class RadioDevice {
 | 
			
		||||
  /** Stop the USRP */
 | 
			
		||||
  virtual bool stop()=0;
 | 
			
		||||
 | 
			
		||||
  /** Get the bus type */
 | 
			
		||||
  virtual enum busType getBus()=0;
 | 
			
		||||
  /** Get the Tx window type */
 | 
			
		||||
  virtual enum TxWindowType getWindowType()=0;
 | 
			
		||||
 | 
			
		||||
  /** Enable thread priority */
 | 
			
		||||
  virtual void setPriority()=0;
 | 
			
		||||
@@ -58,10 +65,9 @@ class RadioDevice {
 | 
			
		||||
	@param RSSI The received signal strength of the read result
 | 
			
		||||
	@return The number of samples actually read
 | 
			
		||||
  */
 | 
			
		||||
  virtual int readSamples(short *buf, int len, bool *overrun, 
 | 
			
		||||
		   TIMESTAMP timestamp = 0xffffffff,
 | 
			
		||||
		   bool *underrun = 0,
 | 
			
		||||
		   unsigned *RSSI = 0)=0;
 | 
			
		||||
  virtual int readSamples(short **buf, int chans, int len, TIMESTAMP timestamp,
 | 
			
		||||
                          bool *overrun = NULL, bool *underrun = NULL,
 | 
			
		||||
                          unsigned *RSSI = NULL)=0;
 | 
			
		||||
  /**
 | 
			
		||||
        Write samples to the radio.
 | 
			
		||||
        @param buf Contains the data to be written.
 | 
			
		||||
@@ -71,18 +77,17 @@ class RadioDevice {
 | 
			
		||||
        @param isControl Set if data is a control packet, e.g. a ping command
 | 
			
		||||
        @return The number of samples actually written
 | 
			
		||||
  */
 | 
			
		||||
  virtual int writeSamples(short *buf, int len, bool *underrun, 
 | 
			
		||||
		    TIMESTAMP timestamp,
 | 
			
		||||
		    bool isControl=false)=0;
 | 
			
		||||
  virtual int writeSamples(short **buf, int chans, int len, TIMESTAMP timestamp,
 | 
			
		||||
                           bool *underrun = NULL, bool isControl = false)=0;
 | 
			
		||||
 
 | 
			
		||||
  /** Update the alignment between the read and write timestamps */
 | 
			
		||||
  virtual bool updateAlignment(TIMESTAMP timestamp)=0;
 | 
			
		||||
  
 | 
			
		||||
  /** Set the transmitter frequency */
 | 
			
		||||
  virtual bool setTxFreq(double wFreq)=0;
 | 
			
		||||
  virtual bool setTxFreq(double wFreq, int chan = 0)=0;
 | 
			
		||||
 | 
			
		||||
  /** Set the receiver frequency */
 | 
			
		||||
  virtual bool setRxFreq(double wFreq)=0;
 | 
			
		||||
  virtual bool setRxFreq(double wFreq, int chan = 0)=0;
 | 
			
		||||
 | 
			
		||||
  /** Returns the starting write Timestamp*/
 | 
			
		||||
  virtual TIMESTAMP initialWriteTimestamp(void)=0;
 | 
			
		||||
@@ -97,10 +102,10 @@ class RadioDevice {
 | 
			
		||||
  virtual double fullScaleOutputValue()=0;
 | 
			
		||||
 | 
			
		||||
  /** sets the receive chan gain, returns the gain setting **/
 | 
			
		||||
  virtual double setRxGain(double dB)=0;
 | 
			
		||||
  virtual double setRxGain(double dB, int chan = 0)=0;
 | 
			
		||||
 | 
			
		||||
  /** gets the current receive gain **/
 | 
			
		||||
  virtual double getRxGain(void)=0;
 | 
			
		||||
  virtual double getRxGain(int chan = 0)=0;
 | 
			
		||||
 | 
			
		||||
  /** return maximum Rx Gain **/
 | 
			
		||||
  virtual double maxRxGain(void) = 0;
 | 
			
		||||
@@ -109,7 +114,7 @@ class RadioDevice {
 | 
			
		||||
  virtual double minRxGain(void) = 0;
 | 
			
		||||
 | 
			
		||||
  /** sets the transmit chan gain, returns the gain setting **/
 | 
			
		||||
  virtual double setTxGain(double dB)=0;
 | 
			
		||||
  virtual double setTxGain(double dB, int chan = 0)=0;
 | 
			
		||||
 | 
			
		||||
  /** return maximum Tx Gain **/
 | 
			
		||||
  virtual double maxTxGain(void) = 0;
 | 
			
		||||
@@ -117,13 +122,18 @@ class RadioDevice {
 | 
			
		||||
  /** return minimum Tx Gain **/
 | 
			
		||||
  virtual double minTxGain(void) = 0;
 | 
			
		||||
 | 
			
		||||
  /** set and return antennas selection **/
 | 
			
		||||
  virtual void setTxAntenna(std::string &name) = 0;
 | 
			
		||||
  virtual void setRxAntenna(std::string &name) = 0;
 | 
			
		||||
  virtual std::string getRxAntenna() = 0;
 | 
			
		||||
  virtual std::string getTxAntenna() = 0;
 | 
			
		||||
 | 
			
		||||
  /** Return internal status values */
 | 
			
		||||
  virtual double getTxFreq()=0;
 | 
			
		||||
  virtual double getRxFreq()=0;
 | 
			
		||||
  virtual double getTxFreq(int chan = 0)=0;
 | 
			
		||||
  virtual double getRxFreq(int chan = 0)=0;
 | 
			
		||||
  virtual double getSampleRate()=0;
 | 
			
		||||
  virtual double numberRead()=0;
 | 
			
		||||
  virtual double numberWritten()=0;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,91 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Radio device I/O interface
 | 
			
		||||
 * Written by Thomas Tsou <ttsou@vt.edu>
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2011 Free Software Foundation, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 * See the COPYING file in the main directory for details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <radioInterface.h>
 | 
			
		||||
#include <Logger.h>
 | 
			
		||||
 | 
			
		||||
/* Device side buffers */
 | 
			
		||||
static short rx_buf[OUTCHUNK * 2 * 2];
 | 
			
		||||
static short tx_buf[INCHUNK * 2 * 2];
 | 
			
		||||
 | 
			
		||||
/* Complex float to short conversion */
 | 
			
		||||
static int float_to_short(short *shrt_out, float *flt_in, int num)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < num; i++) {
 | 
			
		||||
		shrt_out[2 * i + 0] = flt_in[2 * i + 0];
 | 
			
		||||
		shrt_out[2 * i + 1] = flt_in[2 * i + 1];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Comlpex short to float conversion */
 | 
			
		||||
static int short_to_float(float *flt_out, short *shrt_in, int num)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < num; i++) {
 | 
			
		||||
		flt_out[2 * i + 0] = shrt_in[2 * i + 0];
 | 
			
		||||
		flt_out[2 * i + 1] = shrt_in[2 * i + 1];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Receive a timestamped chunk from the device */ 
 | 
			
		||||
void RadioInterface::pullBuffer()
 | 
			
		||||
{
 | 
			
		||||
	bool local_underrun;
 | 
			
		||||
 | 
			
		||||
	/* Read samples. Fail if we don't get what we want. */
 | 
			
		||||
	int num_rd = mRadio->readSamples(rx_buf, OUTCHUNK, &overrun,
 | 
			
		||||
					    readTimestamp, &local_underrun);
 | 
			
		||||
 | 
			
		||||
	LOG(DEBUG) << "Rx read " << num_rd << " samples from device";
 | 
			
		||||
	assert(num_rd == OUTCHUNK);
 | 
			
		||||
 | 
			
		||||
	underrun |= local_underrun;
 | 
			
		||||
	readTimestamp += (TIMESTAMP) num_rd;
 | 
			
		||||
 | 
			
		||||
	short_to_float(rcvBuffer + 2 * rcvCursor, rx_buf, num_rd);
 | 
			
		||||
	rcvCursor += num_rd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Send timestamped chunk to the device with arbitrary size */ 
 | 
			
		||||
void RadioInterface::pushBuffer()
 | 
			
		||||
{
 | 
			
		||||
	if (sendCursor < INCHUNK)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	float_to_short(tx_buf, sendBuffer, sendCursor);
 | 
			
		||||
 | 
			
		||||
	/* Write samples. Fail if we don't get what we want. */
 | 
			
		||||
	int num_smpls = mRadio->writeSamples(tx_buf,
 | 
			
		||||
					     sendCursor,
 | 
			
		||||
					     &underrun,
 | 
			
		||||
					     writeTimestamp);
 | 
			
		||||
	assert(num_smpls == sendCursor);
 | 
			
		||||
 | 
			
		||||
	writeTimestamp += (TIMESTAMP) num_smpls;
 | 
			
		||||
	sendCursor = 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,324 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Radio device interface with sample rate conversion
 | 
			
		||||
 * Written by Thomas Tsou <ttsou@vt.edu>
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2011 Free Software Foundation, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 * See the COPYING file in the main directory for details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <radioInterface.h>
 | 
			
		||||
#include <Logger.h>
 | 
			
		||||
 | 
			
		||||
/* New chunk sizes for resampled rate */
 | 
			
		||||
#ifdef INCHUNK
 | 
			
		||||
  #undef INCHUNK
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef OUTCHUNK
 | 
			
		||||
  #undef OUTCHUNK
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Resampling parameters */
 | 
			
		||||
#define INRATE       65 * SAMPSPERSYM
 | 
			
		||||
#define INHISTORY    INRATE * 2
 | 
			
		||||
#define INCHUNK      INRATE * 9
 | 
			
		||||
 | 
			
		||||
#define OUTRATE      96 * SAMPSPERSYM
 | 
			
		||||
#define OUTHISTORY   OUTRATE * 2
 | 
			
		||||
#define OUTCHUNK     OUTRATE * 9
 | 
			
		||||
 | 
			
		||||
/* Resampler low pass filters */
 | 
			
		||||
signalVector *tx_lpf = 0;
 | 
			
		||||
signalVector *rx_lpf = 0;
 | 
			
		||||
 | 
			
		||||
/* Resampler history */
 | 
			
		||||
signalVector *tx_hist = 0;
 | 
			
		||||
signalVector *rx_hist = 0;
 | 
			
		||||
 | 
			
		||||
/* Resampler input buffer */
 | 
			
		||||
signalVector *tx_vec = 0;
 | 
			
		||||
signalVector *rx_vec = 0;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * High rate (device facing) buffers
 | 
			
		||||
 *
 | 
			
		||||
 * Transmit side samples are pushed after each burst so accomodate
 | 
			
		||||
 * a resampled burst plus up to a chunk left over from the previous
 | 
			
		||||
 * resampling operation.
 | 
			
		||||
 *
 | 
			
		||||
 * Receive side samples always pulled with a fixed size.
 | 
			
		||||
 */
 | 
			
		||||
short tx_buf[INCHUNK * 2 * 4];
 | 
			
		||||
short rx_buf[OUTCHUNK * 2 * 2];
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Utilities and Conversions 
 | 
			
		||||
 *
 | 
			
		||||
 * Manipulate signal vectors dynamically for two reasons. For one,
 | 
			
		||||
 * it's simpler. And two, it doesn't make any reasonable difference
 | 
			
		||||
 * relative to the high overhead generated by the resampling.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* Concatenate signal vectors. Deallocate input vectors. */
 | 
			
		||||
signalVector *concat(signalVector *a, signalVector *b)
 | 
			
		||||
{
 | 
			
		||||
	signalVector *vec = new signalVector(*a, *b);
 | 
			
		||||
	delete a;
 | 
			
		||||
	delete b;
 | 
			
		||||
 | 
			
		||||
	return vec;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Segment a signal vector. Deallocate the input vector. */
 | 
			
		||||
signalVector *segment(signalVector *a, int indx, int sz)
 | 
			
		||||
{
 | 
			
		||||
	signalVector *vec = new signalVector(sz);
 | 
			
		||||
	a->segmentCopyTo(*vec, indx, sz);
 | 
			
		||||
	delete a;
 | 
			
		||||
 | 
			
		||||
	return vec;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Create a new signal vector from a short array. */
 | 
			
		||||
signalVector *short_to_sigvec(short *smpls, size_t sz)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	signalVector *vec = new signalVector(sz);
 | 
			
		||||
	signalVector::iterator itr = vec->begin();
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < sz; i++) {
 | 
			
		||||
		*itr++ = Complex<float>(smpls[2 * i + 0], smpls[2 * i + 1]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return vec;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Convert and deallocate a signal vector into a short array. */
 | 
			
		||||
int sigvec_to_short(signalVector *vec, short *smpls)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	signalVector::iterator itr = vec->begin();
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < vec->size(); i++) {
 | 
			
		||||
		smpls[2 * i + 0] = itr->real();
 | 
			
		||||
		smpls[2 * i + 1] = itr->imag();
 | 
			
		||||
		itr++;
 | 
			
		||||
	}
 | 
			
		||||
	delete vec;
 | 
			
		||||
 | 
			
		||||
	return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Create a new signal vector from a float array. */
 | 
			
		||||
signalVector *float_to_sigvec(float *smpls, int sz)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	signalVector *vec = new signalVector(sz);
 | 
			
		||||
	signalVector::iterator itr = vec->begin();
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < sz; i++) {
 | 
			
		||||
		*itr++ = Complex<float>(smpls[2 * i + 0], smpls[2 * i + 1]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return vec;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Convert and deallocate a signal vector into a float array. */
 | 
			
		||||
int sigvec_to_float(signalVector *vec, float *smpls)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	signalVector::iterator itr = vec->begin();
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < vec->size(); i++) {
 | 
			
		||||
		smpls[2 * i + 0] = itr->real();
 | 
			
		||||
		smpls[2 * i + 1] = itr->imag();
 | 
			
		||||
		itr++;
 | 
			
		||||
	}
 | 
			
		||||
	delete vec;
 | 
			
		||||
 | 
			
		||||
	return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Initialize resampling signal vectors */
 | 
			
		||||
void init_resampler(signalVector **lpf,
 | 
			
		||||
	   	    signalVector **buf,
 | 
			
		||||
		    signalVector **hist,
 | 
			
		||||
		    int tx)
 | 
			
		||||
{
 | 
			
		||||
	int P, Q, taps, hist_len;
 | 
			
		||||
	float cutoff_freq;
 | 
			
		||||
 | 
			
		||||
	if (tx) {
 | 
			
		||||
		LOG(INFO) << "Initializing Tx resampler";
 | 
			
		||||
		P = OUTRATE;
 | 
			
		||||
		Q = INRATE;
 | 
			
		||||
		taps = 651;
 | 
			
		||||
		hist_len = INHISTORY;
 | 
			
		||||
	} else {
 | 
			
		||||
		LOG(INFO) << "Initializing Rx resampler";
 | 
			
		||||
		P = INRATE;
 | 
			
		||||
		Q = OUTRATE;
 | 
			
		||||
		taps = 961;
 | 
			
		||||
		hist_len = OUTHISTORY;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!*lpf) {
 | 
			
		||||
		cutoff_freq = (P < Q) ? (1.0/(float) Q) : (1.0/(float) P);
 | 
			
		||||
		*lpf = createLPF(cutoff_freq, taps, P);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!*buf) {
 | 
			
		||||
		*buf = new signalVector();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!*hist);
 | 
			
		||||
		*hist = new signalVector(hist_len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Resample a signal vector
 | 
			
		||||
 *
 | 
			
		||||
 * The input vector is deallocated and the pointer returned with a vector
 | 
			
		||||
 * of any unconverted samples.
 | 
			
		||||
 */
 | 
			
		||||
signalVector *resmpl_sigvec(signalVector *hist, signalVector **vec,
 | 
			
		||||
			    signalVector *lpf, double in_rate,
 | 
			
		||||
			    double out_rate, int chunk_sz)
 | 
			
		||||
{
 | 
			
		||||
	signalVector *resamp_vec;
 | 
			
		||||
	int num_chunks = (*vec)->size() / chunk_sz;
 | 
			
		||||
 | 
			
		||||
	/* Truncate to a chunk multiple */
 | 
			
		||||
	signalVector trunc_vec(num_chunks * chunk_sz);
 | 
			
		||||
	(*vec)->segmentCopyTo(trunc_vec, 0, num_chunks * chunk_sz);
 | 
			
		||||
 | 
			
		||||
	/* Update sample buffer with remainder */
 | 
			
		||||
	*vec = segment(*vec, trunc_vec.size(), (*vec)->size() - trunc_vec.size());
 | 
			
		||||
 | 
			
		||||
	/* Add history and resample */
 | 
			
		||||
	signalVector input_vec(*hist, trunc_vec);
 | 
			
		||||
	resamp_vec = polyphaseResampleVector(input_vec, in_rate,
 | 
			
		||||
					     out_rate, lpf);
 | 
			
		||||
 | 
			
		||||
	/* Update history */
 | 
			
		||||
	trunc_vec.segmentCopyTo(*hist, trunc_vec.size() - hist->size(),
 | 
			
		||||
				hist->size());
 | 
			
		||||
	return resamp_vec;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Wrapper for receive-side integer-to-float array resampling */
 | 
			
		||||
 int rx_resmpl_int_flt(float *smpls_out, short *smpls_in, int num_smpls)
 | 
			
		||||
{
 | 
			
		||||
	int num_resmpld, num_chunks;
 | 
			
		||||
	signalVector *convert_vec, *resamp_vec, *trunc_vec;
 | 
			
		||||
 | 
			
		||||
	if (!rx_lpf || !rx_vec || !rx_hist)
 | 
			
		||||
		init_resampler(&rx_lpf, &rx_vec, &rx_hist, false);
 | 
			
		||||
 | 
			
		||||
	/* Convert and add samples to the receive buffer */
 | 
			
		||||
	convert_vec = short_to_sigvec(smpls_in, num_smpls);
 | 
			
		||||
	rx_vec = concat(rx_vec, convert_vec);
 | 
			
		||||
 | 
			
		||||
	num_chunks = rx_vec->size() / OUTCHUNK;
 | 
			
		||||
	if (num_chunks < 1)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	/* Resample */ 
 | 
			
		||||
	resamp_vec = resmpl_sigvec(rx_hist, &rx_vec, rx_lpf,
 | 
			
		||||
				   INRATE, OUTRATE, OUTCHUNK);
 | 
			
		||||
	/* Truncate */
 | 
			
		||||
	trunc_vec = segment(resamp_vec, INHISTORY,
 | 
			
		||||
                            resamp_vec->size() - INHISTORY);
 | 
			
		||||
	/* Convert */
 | 
			
		||||
	num_resmpld = sigvec_to_float(trunc_vec, smpls_out);
 | 
			
		||||
 | 
			
		||||
	return num_resmpld; 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Wrapper for transmit-side float-to-int array resampling */
 | 
			
		||||
int tx_resmpl_flt_int(short *smpls_out, float *smpls_in, int num_smpls)
 | 
			
		||||
{
 | 
			
		||||
	int num_resmpl, num_chunks;
 | 
			
		||||
	signalVector *convert_vec, *resamp_vec;
 | 
			
		||||
 | 
			
		||||
	if (!tx_lpf || !tx_vec || !tx_hist)
 | 
			
		||||
		init_resampler(&tx_lpf, &tx_vec, &tx_hist, true);
 | 
			
		||||
 | 
			
		||||
	/* Convert and add samples to the transmit buffer */
 | 
			
		||||
	convert_vec = float_to_sigvec(smpls_in, num_smpls);
 | 
			
		||||
	tx_vec = concat(tx_vec, convert_vec);
 | 
			
		||||
 | 
			
		||||
	num_chunks = tx_vec->size() / INCHUNK;
 | 
			
		||||
	if (num_chunks < 1)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	/* Resample and convert to an integer array */
 | 
			
		||||
	resamp_vec = resmpl_sigvec(tx_hist, &tx_vec, tx_lpf,
 | 
			
		||||
				   OUTRATE, INRATE, INCHUNK);
 | 
			
		||||
	num_resmpl = sigvec_to_short(resamp_vec, smpls_out);
 | 
			
		||||
 | 
			
		||||
	return num_resmpl; 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Receive a timestamped chunk from the device */ 
 | 
			
		||||
void RadioInterface::pullBuffer()
 | 
			
		||||
{
 | 
			
		||||
	int num_cv, num_rd;
 | 
			
		||||
	bool local_underrun;
 | 
			
		||||
 | 
			
		||||
	/* Read samples. Fail if we don't get what we want. */
 | 
			
		||||
	num_rd = mRadio->readSamples(rx_buf, OUTCHUNK, &overrun,
 | 
			
		||||
				     readTimestamp, &local_underrun);
 | 
			
		||||
 | 
			
		||||
	LOG(DEBUG) << "Rx read " << num_rd << " samples from device";
 | 
			
		||||
	assert(num_rd == OUTCHUNK);
 | 
			
		||||
 | 
			
		||||
	underrun |= local_underrun;
 | 
			
		||||
	readTimestamp += (TIMESTAMP) num_rd;
 | 
			
		||||
 | 
			
		||||
	/* Convert and resample */
 | 
			
		||||
	num_cv = rx_resmpl_int_flt(rcvBuffer + 2 * rcvCursor,
 | 
			
		||||
				   rx_buf, num_rd);
 | 
			
		||||
 | 
			
		||||
	LOG(DEBUG) << "Rx read " << num_cv << " samples from resampler";
 | 
			
		||||
 | 
			
		||||
	rcvCursor += num_cv;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Send a timestamped chunk to the device */ 
 | 
			
		||||
void RadioInterface::pushBuffer()
 | 
			
		||||
{
 | 
			
		||||
	int num_cv, num_wr;
 | 
			
		||||
 | 
			
		||||
	if (sendCursor < INCHUNK)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	LOG(DEBUG) << "Tx wrote " << sendCursor << " samples to resampler";
 | 
			
		||||
 | 
			
		||||
	/* Resample and convert */
 | 
			
		||||
	num_cv = tx_resmpl_flt_int(tx_buf, sendBuffer, sendCursor);
 | 
			
		||||
	assert(num_cv > sendCursor);
 | 
			
		||||
 | 
			
		||||
	/* Write samples. Fail if we don't get what we want. */
 | 
			
		||||
	num_wr = mRadio->writeSamples(tx_buf + OUTHISTORY * 2,
 | 
			
		||||
				      num_cv - OUTHISTORY,
 | 
			
		||||
				      &underrun,
 | 
			
		||||
				      writeTimestamp);
 | 
			
		||||
 | 
			
		||||
	LOG(DEBUG) << "Tx wrote " << num_wr << " samples to device";
 | 
			
		||||
	assert(num_wr == num_wr);
 | 
			
		||||
 | 
			
		||||
	writeTimestamp += (TIMESTAMP) num_wr;
 | 
			
		||||
	sendCursor = 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
 | 
			
		||||
* Copyright 2008, 2009, 2012 Free Software Foundation, Inc.
 | 
			
		||||
*
 | 
			
		||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
			
		||||
* See the COPYING file in the main directory for details.
 | 
			
		||||
@@ -25,25 +25,43 @@
 | 
			
		||||
#include "radioInterface.h"
 | 
			
		||||
#include <Logger.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include "convert.h"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool started = false;
 | 
			
		||||
 | 
			
		||||
/* Device side buffers */
 | 
			
		||||
static short *rx_buf[CHAN_MAX];
 | 
			
		||||
static short *tx_buf[CHAN_MAX];
 | 
			
		||||
 | 
			
		||||
RadioInterface::RadioInterface(RadioDevice *wRadio,
 | 
			
		||||
			       int wReceiveOffset,
 | 
			
		||||
			       int wRadioOversampling,
 | 
			
		||||
			       int wTransceiverOversampling,
 | 
			
		||||
			       int wChanM,
 | 
			
		||||
			       int wSPS,
 | 
			
		||||
                               int wReceiveOffset,
 | 
			
		||||
			       GSM::Time wStartTime)
 | 
			
		||||
  : underrun(false), sendCursor(0), rcvCursor(0), mOn(false),
 | 
			
		||||
    mRadio(wRadio), receiveOffset(wReceiveOffset),
 | 
			
		||||
    samplesPerSymbol(wRadioOversampling), powerScaling(1.0),
 | 
			
		||||
    loadTest(false)
 | 
			
		||||
  : mChanM(wChanM), underrun(false), sendCursor(0), rcvCursor(0), mOn(false),
 | 
			
		||||
    mRadio(wRadio), receiveOffset(wReceiveOffset), sps(wSPS),
 | 
			
		||||
    powerScaling(1.0), loadTest(false)
 | 
			
		||||
{
 | 
			
		||||
  mClock.set(wStartTime);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RadioInterface::~RadioInterface(void)
 | 
			
		||||
{
 | 
			
		||||
  if (mOn) {
 | 
			
		||||
    mRadio->stop();
 | 
			
		||||
    close();
 | 
			
		||||
 
 | 
			
		||||
    delete mAlignRadioServiceLoopThread;
 | 
			
		||||
 | 
			
		||||
RadioInterface::~RadioInterface(void) {
 | 
			
		||||
  if (rcvBuffer!=NULL) delete rcvBuffer;
 | 
			
		||||
  //mReceiveFIFO.clear();
 | 
			
		||||
    for (int i = 0; i < mChanM; i++) {
 | 
			
		||||
      if (rcvBuffer[i] != NULL)
 | 
			
		||||
        delete rcvBuffer[i];
 | 
			
		||||
      if (sendBuffer[i] != NULL)
 | 
			
		||||
        delete sendBuffer[i];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double RadioInterface::fullScaleInputValue(void) {
 | 
			
		||||
@@ -55,11 +73,11 @@ double RadioInterface::fullScaleOutputValue(void) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void RadioInterface::setPowerAttenuation(double atten)
 | 
			
		||||
void RadioInterface::setPowerAttenuation(double atten, int chan)
 | 
			
		||||
{
 | 
			
		||||
  double rfGain, digAtten;
 | 
			
		||||
 | 
			
		||||
  rfGain = mRadio->setTxGain(mRadio->maxTxGain() - atten);
 | 
			
		||||
  rfGain = mRadio->setTxGain(mRadio->maxTxGain() - atten, chan);
 | 
			
		||||
  digAtten = atten - mRadio->maxTxGain() + rfGain;
 | 
			
		||||
 | 
			
		||||
  if (digAtten < 1.0)
 | 
			
		||||
@@ -90,53 +108,75 @@ int RadioInterface::radioifyVector(signalVector &wVector,
 | 
			
		||||
  return wVector.size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int RadioInterface::unRadioifyVector(float *floatVector,
 | 
			
		||||
				     signalVector& newVector)
 | 
			
		||||
int RadioInterface::unRadioifyVector(float *floatVector, int offset,
 | 
			
		||||
				     signalVector &newVector)
 | 
			
		||||
{
 | 
			
		||||
  int i;
 | 
			
		||||
  signalVector::iterator itr = newVector.begin();
 | 
			
		||||
 | 
			
		||||
  for (i = 0; i < newVector.size(); i++) {
 | 
			
		||||
    *itr++ = Complex<float>(floatVector[2 * i + 0],
 | 
			
		||||
			    floatVector[2 * i + 1]);
 | 
			
		||||
    *itr++ = Complex<float>(floatVector[offset + 2 * i + 0],
 | 
			
		||||
			    floatVector[offset + 2 * i + 1]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return newVector.size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool RadioInterface::tuneTx(double freq)
 | 
			
		||||
bool RadioInterface::tuneTx(double freq, int chan)
 | 
			
		||||
{
 | 
			
		||||
  return mRadio->setTxFreq(freq);
 | 
			
		||||
  return mRadio->setTxFreq(freq, chan);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool RadioInterface::tuneRx(double freq)
 | 
			
		||||
bool RadioInterface::tuneRx(double freq, int chan)
 | 
			
		||||
{
 | 
			
		||||
  return mRadio->setRxFreq(freq);
 | 
			
		||||
  return mRadio->setRxFreq(freq, chan);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void RadioInterface::start()
 | 
			
		||||
bool RadioInterface::start()
 | 
			
		||||
{
 | 
			
		||||
  LOG(INFO) << "starting radio interface...";
 | 
			
		||||
  mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
 | 
			
		||||
                                     (void*)this);
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  if (mOn)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  mOn = true;
 | 
			
		||||
#ifdef USRP1
 | 
			
		||||
  mAlignRadioServiceLoopThread = new Thread(32768);
 | 
			
		||||
  mAlignRadioServiceLoopThread->start((void * (*)(void*))AlignRadioServiceLoopAdapter,
 | 
			
		||||
                                      (void*)this);
 | 
			
		||||
#endif
 | 
			
		||||
  writeTimestamp = mRadio->initialWriteTimestamp();
 | 
			
		||||
  readTimestamp = mRadio->initialReadTimestamp();
 | 
			
		||||
  for (i = 0; i < mChanM; i++) {
 | 
			
		||||
    sendBuffer[i] = new float[8*2*INCHUNK];
 | 
			
		||||
    rcvBuffer[i] = new float[8*2*OUTCHUNK];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Init I/O specific variables if applicable */ 
 | 
			
		||||
  init();
 | 
			
		||||
 | 
			
		||||
  mRadio->start(); 
 | 
			
		||||
  LOG(DEBUG) << "Radio started";
 | 
			
		||||
  mRadio->updateAlignment(writeTimestamp-10000); 
 | 
			
		||||
  mRadio->updateAlignment(writeTimestamp-10000);
 | 
			
		||||
 | 
			
		||||
  sendBuffer = new float[2*2*INCHUNK*samplesPerSymbol];
 | 
			
		||||
  rcvBuffer = new float[2*2*OUTCHUNK*samplesPerSymbol];
 | 
			
		||||
 
 | 
			
		||||
  mOn = true;
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool RadioInterface::stop()
 | 
			
		||||
{
 | 
			
		||||
  if (!mOn)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  mOn = false;
 | 
			
		||||
  mRadio->stop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef USRP1
 | 
			
		||||
void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
 | 
			
		||||
{
 | 
			
		||||
  while (1) {
 | 
			
		||||
  while (radioInterface->on()) {
 | 
			
		||||
    radioInterface->alignRadio();
 | 
			
		||||
    pthread_testcancel();
 | 
			
		||||
  }
 | 
			
		||||
@@ -147,23 +187,55 @@ void RadioInterface::alignRadio() {
 | 
			
		||||
  sleep(60);
 | 
			
		||||
  mRadio->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void RadioInterface::driveTransmitRadio(signalVector &radioBurst, bool zeroBurst) {
 | 
			
		||||
void RadioInterface::driveTransmitRadio(signalVector **radioBurst, bool *zeroBurst)
 | 
			
		||||
{
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  if (!mOn) return;
 | 
			
		||||
  if (!mOn)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  radioifyVector(radioBurst, sendBuffer + 2 * sendCursor, powerScaling, zeroBurst);
 | 
			
		||||
  for (i = 0; i < mChanM; i++) {
 | 
			
		||||
    radioifyVector(*radioBurst[i], sendBuffer[i] + 2 * sendCursor,
 | 
			
		||||
                   powerScaling, zeroBurst[i]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  sendCursor += radioBurst.size();
 | 
			
		||||
  /* 
 | 
			
		||||
   * All bursts should be the same size since all transceivers are
 | 
			
		||||
   * tied with a single clock in the radio interface.
 | 
			
		||||
   */
 | 
			
		||||
  sendCursor += radioBurst[0]->size();
 | 
			
		||||
 | 
			
		||||
  pushBuffer();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RadioInterface::driveReceiveRadio() {
 | 
			
		||||
static inline void shiftRxBuffers(float **buf, int offset, int len, int chanM)
 | 
			
		||||
{
 | 
			
		||||
  for (int i = 0; i < chanM; i++)
 | 
			
		||||
      memmove(buf[i], buf[i] + offset, sizeof(float) * len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  if (!mOn) return;
 | 
			
		||||
void RadioInterface::loadVectors(unsigned tN, int samplesPerBurst,
 | 
			
		||||
                                 int idx, GSM::Time rxClock)
 | 
			
		||||
{
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  if (mReceiveFIFO.size() > 8) return;
 | 
			
		||||
  for (i = 0; i < mChanM; i++) {
 | 
			
		||||
    signalVector rxVector(samplesPerBurst);
 | 
			
		||||
    unRadioifyVector(rcvBuffer[i], idx * 2, rxVector);
 | 
			
		||||
    radioVector *rxBurst = new radioVector(rxVector, rxClock);
 | 
			
		||||
    mReceiveFIFO[i].write(rxBurst);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RadioInterface::driveReceiveRadio()
 | 
			
		||||
{
 | 
			
		||||
  if (!mOn)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  if (mReceiveFIFO[0].size() > 8)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  pullBuffer();
 | 
			
		||||
 | 
			
		||||
@@ -173,71 +245,98 @@ void RadioInterface::driveReceiveRadio() {
 | 
			
		||||
  int rcvSz = rcvCursor;
 | 
			
		||||
  int readSz = 0;
 | 
			
		||||
  const int symbolsPerSlot = gSlotLen + 8;
 | 
			
		||||
  int samplesPerBurst = (symbolsPerSlot + (tN % 4 == 0)) * sps;
 | 
			
		||||
 | 
			
		||||
  // while there's enough data in receive buffer, form received 
 | 
			
		||||
  //    GSM bursts and pass up to Transceiver
 | 
			
		||||
  // Using the 157-156-156-156 symbols per timeslot format.
 | 
			
		||||
  while (rcvSz > (symbolsPerSlot + (tN % 4 == 0))*samplesPerSymbol) {
 | 
			
		||||
    signalVector rxVector((symbolsPerSlot + (tN % 4 == 0))*samplesPerSymbol);
 | 
			
		||||
    unRadioifyVector(rcvBuffer+readSz*2,rxVector);
 | 
			
		||||
    GSM::Time tmpTime = rcvClock;
 | 
			
		||||
  while (rcvSz >= samplesPerBurst) { 
 | 
			
		||||
    if (rcvClock.FN() >= 0) {
 | 
			
		||||
      //LOG(DEBUG) << "FN: " << rcvClock.FN();
 | 
			
		||||
      radioVector *rxBurst = NULL;
 | 
			
		||||
      if (!loadTest)
 | 
			
		||||
        rxBurst = new radioVector(rxVector,tmpTime);
 | 
			
		||||
      else {
 | 
			
		||||
	if (tN % 4 == 0)
 | 
			
		||||
	  rxBurst = new radioVector(*finalVec9,tmpTime);
 | 
			
		||||
        else
 | 
			
		||||
          rxBurst = new radioVector(*finalVec,tmpTime); 
 | 
			
		||||
      }
 | 
			
		||||
      mReceiveFIFO.put(rxBurst); 
 | 
			
		||||
      loadVectors(tN, samplesPerBurst, readSz, rcvClock);
 | 
			
		||||
    }
 | 
			
		||||
    mClock.incTN(); 
 | 
			
		||||
 | 
			
		||||
    mClock.incTN();
 | 
			
		||||
    rcvClock.incTN();
 | 
			
		||||
    //if (mReceiveFIFO.size() >= 16) mReceiveFIFO.wait(8);
 | 
			
		||||
    //LOG(DEBUG) << "receiveFIFO: wrote radio vector at time: " << mClock.get() << ", new size: " << mReceiveFIFO.size() ;
 | 
			
		||||
    readSz += (symbolsPerSlot+(tN % 4 == 0))*samplesPerSymbol;
 | 
			
		||||
    rcvSz -= (symbolsPerSlot+(tN % 4 == 0))*samplesPerSymbol;
 | 
			
		||||
 | 
			
		||||
    readSz += samplesPerBurst;
 | 
			
		||||
    rcvSz -= samplesPerBurst;
 | 
			
		||||
 | 
			
		||||
    tN = rcvClock.TN();
 | 
			
		||||
    samplesPerBurst = (symbolsPerSlot + (tN % 4 == 0)) * sps;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (readSz > 0) {
 | 
			
		||||
    rcvCursor -= readSz;
 | 
			
		||||
    memmove(rcvBuffer,rcvBuffer+2*readSz,sizeof(float) * 2 * rcvCursor);
 | 
			
		||||
    shiftRxBuffers(rcvBuffer, 2 * readSz, 2 * rcvCursor, mChanM);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool RadioInterface::isUnderrun()
 | 
			
		||||
{
 | 
			
		||||
  bool retVal = underrun;
 | 
			
		||||
  underrun = false;
 | 
			
		||||
 | 
			
		||||
  return retVal;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RadioInterface::attach(RadioDevice *wRadio, int wRadioOversampling)
 | 
			
		||||
{
 | 
			
		||||
  if (!mOn) {
 | 
			
		||||
    mRadio = wRadio;
 | 
			
		||||
    mRadioOversampling = SAMPSPERSYM;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double RadioInterface::setRxGain(double dB)
 | 
			
		||||
double RadioInterface::setRxGain(double dB, int chan)
 | 
			
		||||
{
 | 
			
		||||
  if (mRadio)
 | 
			
		||||
    return mRadio->setRxGain(dB);
 | 
			
		||||
    return mRadio->setRxGain(dB, chan);
 | 
			
		||||
  else
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double RadioInterface::getRxGain()
 | 
			
		||||
double RadioInterface::getRxGain(int chan)
 | 
			
		||||
{
 | 
			
		||||
  if (mRadio)
 | 
			
		||||
    return mRadio->getRxGain();
 | 
			
		||||
    return mRadio->getRxGain(chan);
 | 
			
		||||
  else
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool RadioInterface::init()
 | 
			
		||||
{
 | 
			
		||||
	for (int i = 0; i < CHAN_MAX; i++) {
 | 
			
		||||
		rx_buf[i] = new short[2 * OUTCHUNK];
 | 
			
		||||
		tx_buf[i] = new short[4 * 2 * INCHUNK];
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RadioInterface::close()
 | 
			
		||||
{
 | 
			
		||||
	for (int i = 0; i < CHAN_MAX; i++) {
 | 
			
		||||
		delete rx_buf[i];
 | 
			
		||||
		delete tx_buf[i];
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Receive a timestamped chunk from the device */ 
 | 
			
		||||
void RadioInterface::pullBuffer()
 | 
			
		||||
{
 | 
			
		||||
  bool local_underrun;
 | 
			
		||||
 | 
			
		||||
  /* Read samples. Fail if we don't get what we want. */
 | 
			
		||||
  int num_rd = mRadio->readSamples(rx_buf, mChanM, OUTCHUNK, readTimestamp);
 | 
			
		||||
 | 
			
		||||
  LOG(DEBUG) << "Rx read " << num_rd << " samples from device";
 | 
			
		||||
  assert(num_rd == OUTCHUNK);
 | 
			
		||||
 | 
			
		||||
  underrun |= local_underrun;
 | 
			
		||||
  readTimestamp += (TIMESTAMP) num_rd;
 | 
			
		||||
 | 
			
		||||
  for (int i = 0; i < mChanM; i++)
 | 
			
		||||
    convert_short_float(rcvBuffer[i] + 2 * rcvCursor, rx_buf[i], num_rd * 2);
 | 
			
		||||
 | 
			
		||||
  rcvCursor += num_rd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Send timestamped chunk to the device with arbitrary size */ 
 | 
			
		||||
void RadioInterface::pushBuffer()
 | 
			
		||||
{
 | 
			
		||||
  if (sendCursor < INCHUNK)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  for (int i = 0; i < mChanM; i++)
 | 
			
		||||
    convert_float_short(tx_buf[i], sendBuffer[i], 1.0, sendCursor * 2);
 | 
			
		||||
 | 
			
		||||
  /* Write samples. Fail if we don't get what we want. */
 | 
			
		||||
  int num_smpls = mRadio->writeSamples(tx_buf, mChanM, sendCursor,
 | 
			
		||||
                                       writeTimestamp, &underrun);
 | 
			
		||||
  assert(num_smpls == sendCursor);
 | 
			
		||||
 | 
			
		||||
  writeTimestamp += (TIMESTAMP) num_smpls;
 | 
			
		||||
  sendCursor = 0;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright 2008 Free Software Foundation, Inc.
 | 
			
		||||
* Copyright 2008, 2012 Free Software Foundation, Inc.
 | 
			
		||||
*
 | 
			
		||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
			
		||||
*
 | 
			
		||||
@@ -12,7 +12,8 @@
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifndef _RADIOINTEFACE_H_
 | 
			
		||||
#define _RADIOINTEFACE_H_
 | 
			
		||||
 | 
			
		||||
#include "sigProcLib.h"  
 | 
			
		||||
#include "GSMCommon.h"
 | 
			
		||||
@@ -22,29 +23,32 @@
 | 
			
		||||
#include "radioClock.h"
 | 
			
		||||
 | 
			
		||||
/** samples per GSM symbol */
 | 
			
		||||
#define SAMPSPERSYM 1 
 | 
			
		||||
#define SAMPSPERSYM 4
 | 
			
		||||
#define INCHUNK    (625)
 | 
			
		||||
#define OUTCHUNK   (625)
 | 
			
		||||
#define CHAN_MAX    2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static const unsigned gSlotLen = 148;      ///< number of symbols per slot, not counting guard periods
 | 
			
		||||
 | 
			
		||||
/** class to interface the transceiver with the USRP */
 | 
			
		||||
class RadioInterface {
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
protected:
 | 
			
		||||
 | 
			
		||||
  Thread mAlignRadioServiceLoopThread;	      ///< thread that synchronizes transmit and receive sections
 | 
			
		||||
  int mChanM;                                 ///< channelizer width
 | 
			
		||||
  Thread *mAlignRadioServiceLoopThread;	      ///< thread that synchronizes transmit and receive sections
 | 
			
		||||
 | 
			
		||||
  VectorFIFO  mReceiveFIFO;		      ///< FIFO that holds receive  bursts
 | 
			
		||||
  VectorFIFO mReceiveFIFO[CHAN_MAX];	      ///< FIFO that holds receive  bursts
 | 
			
		||||
 | 
			
		||||
  RadioDevice *mRadio;			      ///< the USRP object
 | 
			
		||||
 
 | 
			
		||||
  float *sendBuffer;
 | 
			
		||||
  float *sendBuffer[CHAN_MAX];
 | 
			
		||||
  unsigned sendCursor;
 | 
			
		||||
 | 
			
		||||
  float *rcvBuffer;
 | 
			
		||||
  float *rcvBuffer[CHAN_MAX];
 | 
			
		||||
  unsigned rcvCursor;
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
  bool underrun;			      ///< indicates writes to USRP are too slow
 | 
			
		||||
  bool overrun;				      ///< indicates reads from USRP are too slow
 | 
			
		||||
  TIMESTAMP writeTimestamp;		      ///< sample timestamp of next packet written to USRP
 | 
			
		||||
@@ -52,10 +56,8 @@ private:
 | 
			
		||||
 | 
			
		||||
  RadioClock mClock;                          ///< the basestation clock!
 | 
			
		||||
 | 
			
		||||
  int samplesPerSymbol;			      ///< samples per GSM symbol
 | 
			
		||||
  int sps;                                    ///< samples per GSM symbol
 | 
			
		||||
  int receiveOffset;                          ///< offset b/w transmit and receive GSM timestamps, in timeslots
 | 
			
		||||
  int mRadioOversampling;
 | 
			
		||||
  int mTransceiverOversampling;
 | 
			
		||||
 | 
			
		||||
  bool mOn;				      ///< indicates radio is on
 | 
			
		||||
 | 
			
		||||
@@ -65,6 +67,10 @@ private:
 | 
			
		||||
  int mNumARFCNs;
 | 
			
		||||
  signalVector *finalVec, *finalVec9;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  /** initialize I/O internals */
 | 
			
		||||
  bool init();
 | 
			
		||||
 | 
			
		||||
  /** format samples to USRP */ 
 | 
			
		||||
  int radioifyVector(signalVector &wVector,
 | 
			
		||||
                     float *floatVector,
 | 
			
		||||
@@ -72,64 +78,67 @@ private:
 | 
			
		||||
                     bool zero);
 | 
			
		||||
 | 
			
		||||
  /** format samples from USRP */
 | 
			
		||||
  int unRadioifyVector(float *floatVector, signalVector &wVector);
 | 
			
		||||
  int unRadioifyVector(float *floatVector, int offset, signalVector &wVector);
 | 
			
		||||
 | 
			
		||||
  /** push GSM bursts into the transmit buffer */
 | 
			
		||||
  void pushBuffer(void);
 | 
			
		||||
  virtual void pushBuffer(void);
 | 
			
		||||
 | 
			
		||||
  /** pull GSM bursts from the receive buffer */
 | 
			
		||||
  void pullBuffer(void);
 | 
			
		||||
  virtual void pullBuffer(void);
 | 
			
		||||
 | 
			
		||||
  /** load receive vectors into FIFO's */
 | 
			
		||||
  void loadVectors(unsigned tN, int samplesPerBurst, int index, GSM::Time rxClock);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
  /** start the interface */
 | 
			
		||||
  void start();
 | 
			
		||||
  bool start();
 | 
			
		||||
  bool stop();
 | 
			
		||||
 | 
			
		||||
  bool started() { return mOn; };
 | 
			
		||||
 | 
			
		||||
  /** shutdown interface */
 | 
			
		||||
  void close();
 | 
			
		||||
 | 
			
		||||
  /** constructor */
 | 
			
		||||
  RadioInterface(RadioDevice* wRadio = NULL,
 | 
			
		||||
		 int receiveOffset = 3,
 | 
			
		||||
		 int wRadioOversampling = SAMPSPERSYM,
 | 
			
		||||
		 int wTransceiverOversampling = SAMPSPERSYM,
 | 
			
		||||
		 GSM::Time wStartTime = GSM::Time(0));
 | 
			
		||||
  RadioInterface(RadioDevice* wRadio,
 | 
			
		||||
		 int wChanM = 1,
 | 
			
		||||
		 int wSPS = SAMPSPERSYM,
 | 
			
		||||
                 int receiveOffset = 3,
 | 
			
		||||
		 GSM::Time wStartTime = GSM::Time(0, 0));
 | 
			
		||||
    
 | 
			
		||||
  /** destructor */
 | 
			
		||||
  ~RadioInterface();
 | 
			
		||||
 | 
			
		||||
  void setSamplesPerSymbol(int wSamplesPerSymbol) {if (!mOn) samplesPerSymbol = wSamplesPerSymbol;}
 | 
			
		||||
 | 
			
		||||
  int getSamplesPerSymbol() { return samplesPerSymbol;}
 | 
			
		||||
 | 
			
		||||
  /** check for underrun, resets underrun value */
 | 
			
		||||
  bool isUnderrun();
 | 
			
		||||
  
 | 
			
		||||
  /** attach an existing USRP to this interface */
 | 
			
		||||
  void attach(RadioDevice *wRadio, int wRadioOversampling);
 | 
			
		||||
  void setSamplesPerSymbol(int sps) {if (!mOn) this->sps = sps;}
 | 
			
		||||
 | 
			
		||||
  int getSamplesPerSymbol() { return sps;}
 | 
			
		||||
 
 | 
			
		||||
  /** return the receive FIFO */
 | 
			
		||||
  VectorFIFO* receiveFIFO() { return &mReceiveFIFO;}
 | 
			
		||||
  VectorFIFO* receiveFIFO(int num) { return &mReceiveFIFO[num];}
 | 
			
		||||
 | 
			
		||||
  /** return the basestation clock */
 | 
			
		||||
  RadioClock* getClock(void) { return &mClock;};
 | 
			
		||||
 | 
			
		||||
  /** set transmit frequency */
 | 
			
		||||
  bool tuneTx(double freq);
 | 
			
		||||
  bool tuneTx(double freq, int chan = 0);
 | 
			
		||||
 | 
			
		||||
  /** set receive frequency */
 | 
			
		||||
  bool tuneRx(double freq);
 | 
			
		||||
  bool tuneRx(double freq, int chan = 0);
 | 
			
		||||
 | 
			
		||||
  /** set receive gain */
 | 
			
		||||
  double setRxGain(double dB);
 | 
			
		||||
  double setRxGain(double dB, int chan = 0);
 | 
			
		||||
 | 
			
		||||
  /** get receive gain */
 | 
			
		||||
  double getRxGain(void);
 | 
			
		||||
  double getRxGain(int chan = 0);
 | 
			
		||||
 | 
			
		||||
  /** drive transmission of GSM bursts */
 | 
			
		||||
  void driveTransmitRadio(signalVector &radioBurst, bool zeroBurst);
 | 
			
		||||
  void driveTransmitRadio(signalVector **radioBurst, bool *zeroBurst);
 | 
			
		||||
 | 
			
		||||
  /** drive reception of GSM bursts */
 | 
			
		||||
  void driveReceiveRadio();
 | 
			
		||||
 | 
			
		||||
  void setPowerAttenuation(double atten);
 | 
			
		||||
  void setPowerAttenuation(double atten, int chan = 0);
 | 
			
		||||
 | 
			
		||||
  /** returns the full-scale transmit amplitude **/
 | 
			
		||||
  double fullScaleInputValue();
 | 
			
		||||
@@ -140,20 +149,8 @@ public:
 | 
			
		||||
  /** set thread priority on current thread */
 | 
			
		||||
  void setPriority() { mRadio->setPriority(); }
 | 
			
		||||
 | 
			
		||||
  /** get transport bus type of attached device */ 
 | 
			
		||||
  enum RadioDevice::busType getBus() { return mRadio->getBus(); }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
 | 
			
		||||
  /** drive synchronization of Tx/Rx of USRP */
 | 
			
		||||
  void alignRadio();
 | 
			
		||||
 | 
			
		||||
  /** reset the interface */
 | 
			
		||||
  void reset();
 | 
			
		||||
 | 
			
		||||
  friend void *AlignRadioServiceLoopAdapter(RadioInterface*);
 | 
			
		||||
 | 
			
		||||
  /** get transport window type of attached device */ 
 | 
			
		||||
  enum RadioDevice::TxWindowType getWindowType() { return mRadio->getWindowType(); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** synchronization thread loop */
 | 
			
		||||
void *AlignRadioServiceLoopAdapter(RadioInterface*);
 | 
			
		||||
#endif /* _RADIOINTEFACE_H_ */
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 * Written by Thomas Tsou <ttsou@vt.edu>
 | 
			
		||||
 * Based on code by Harvind S Samra <hssamra@kestrelsp.com>
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2011 Free Software Foundation, Inc.
 | 
			
		||||
 * Copyright 2011, 2012 Free Software Foundation, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
@@ -41,21 +41,6 @@ bool radioVector::operator>(const radioVector& other) const
 | 
			
		||||
	return mTime > other.mTime;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned VectorFIFO::size()
 | 
			
		||||
{
 | 
			
		||||
	return mQ.size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void VectorFIFO::put(radioVector *ptr)
 | 
			
		||||
{
 | 
			
		||||
	mQ.put((void*) ptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
radioVector *VectorFIFO::get()
 | 
			
		||||
{
 | 
			
		||||
	return (radioVector*) mQ.get();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GSM::Time VectorQueue::nextTime() const
 | 
			
		||||
{
 | 
			
		||||
	GSM::Time retVal;
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 * Written by Thomas Tsou <ttsou@vt.edu>
 | 
			
		||||
 * Based on code by Harvind S Samra <hssamra@kestrelsp.com>
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2011 Free Software Foundation, Inc.
 | 
			
		||||
 * Copyright 2011, 2012 Free Software Foundation, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
@@ -37,14 +37,7 @@ private:
 | 
			
		||||
	GSM::Time mTime;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class VectorFIFO {
 | 
			
		||||
public:
 | 
			
		||||
	unsigned size();
 | 
			
		||||
	void put(radioVector *ptr);
 | 
			
		||||
	radioVector *get();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	PointerFIFO mQ;
 | 
			
		||||
class VectorFIFO : public InterthreadQueue<radioVector> {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class VectorQueue : public InterthreadPriorityQueue<radioVector> {
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@
 | 
			
		||||
#include "Transceiver.h"
 | 
			
		||||
#include "radioDevice.h"
 | 
			
		||||
#include "DummyLoad.h"
 | 
			
		||||
#include <fstream>
 | 
			
		||||
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
@@ -36,28 +37,90 @@
 | 
			
		||||
#include <Logger.h>
 | 
			
		||||
#include <Configuration.h>
 | 
			
		||||
 | 
			
		||||
#ifdef RESAMPLE
 | 
			
		||||
  #define DEVICERATE 400e3
 | 
			
		||||
#else
 | 
			
		||||
  #define DEVICERATE 1625e3/6 
 | 
			
		||||
#endif
 | 
			
		||||
#define CONFIGDB            "/etc/OpenBTS/OpenBTS.db"
 | 
			
		||||
 | 
			
		||||
using namespace std;
 | 
			
		||||
 | 
			
		||||
ConfigurationTable gConfig("/etc/OpenBTS/OpenBTS.db");
 | 
			
		||||
 | 
			
		||||
ConfigurationTable gConfig(CONFIGDB);
 | 
			
		||||
 | 
			
		||||
volatile bool gbShutdown = false;
 | 
			
		||||
 | 
			
		||||
static void ctrlCHandler(int signo)
 | 
			
		||||
{
 | 
			
		||||
   cout << "Received shutdown signal" << endl;;
 | 
			
		||||
   gbShutdown = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Attempt to open and test the database file before
 | 
			
		||||
 * accessing the configuration table. We do this because
 | 
			
		||||
 * the global table constructor cannot provide notification
 | 
			
		||||
 * in the event of failure.
 | 
			
		||||
 */
 | 
			
		||||
int testConfig(const char *filename)
 | 
			
		||||
{
 | 
			
		||||
  int rc, val = 9999;
 | 
			
		||||
  bool status;
 | 
			
		||||
  sqlite3 *db;
 | 
			
		||||
  std::string result;
 | 
			
		||||
  std::string test = "sadf732zdvj2";
 | 
			
		||||
 | 
			
		||||
  const char *keys[3] = {
 | 
			
		||||
    "Log.Level",
 | 
			
		||||
    "TRX.Port",
 | 
			
		||||
    "TRX.IP",
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /* Check for file existence */
 | 
			
		||||
  std::ifstream file(filename);
 | 
			
		||||
  if (!file.good()) {
 | 
			
		||||
    std::cerr << "Config: File not readable \""
 | 
			
		||||
              << filename << "\"" << std::endl;
 | 
			
		||||
    return -1;
 | 
			
		||||
  } else {
 | 
			
		||||
    file.close();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Try to open the database  */
 | 
			
		||||
  rc = sqlite3_open(filename, &db);
 | 
			
		||||
  if (rc || !db) {
 | 
			
		||||
    std::cerr << "Config: Database could not be opened" << std::endl;
 | 
			
		||||
    return -1;
 | 
			
		||||
  } else {
 | 
			
		||||
    sqlite3_close(db);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Attempt to set a value in the global config */
 | 
			
		||||
  if (!gConfig.set(test, val)) {
 | 
			
		||||
    std::cerr << "Config: Failed to set test key - "
 | 
			
		||||
              << "permission to access the database?" << std::endl;
 | 
			
		||||
    return -1;
 | 
			
		||||
  } else {
 | 
			
		||||
    gConfig.remove(test);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Attempt to query */
 | 
			
		||||
  for (int i = 0; i < 3; i++) {
 | 
			
		||||
    try {
 | 
			
		||||
      result = gConfig.getStr(keys[i]); 
 | 
			
		||||
    } catch (...) {
 | 
			
		||||
      std::cerr << "Config: Failed query on " << keys[i] << std::endl;
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0; 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
  std::string deviceArgs;
 | 
			
		||||
  int trxPort;
 | 
			
		||||
  std::string deviceArgs, logLevel, trxAddr, txAntenna, rxAntenna;
 | 
			
		||||
 | 
			
		||||
  RadioDevice *usrp;
 | 
			
		||||
  RadioInterface* radio;
 | 
			
		||||
  DriveLoop *drive;
 | 
			
		||||
  Transceiver *trx;
 | 
			
		||||
 | 
			
		||||
  if (argc == 3)
 | 
			
		||||
  {
 | 
			
		||||
@@ -79,63 +142,70 @@ int main(int argc, char *argv[])
 | 
			
		||||
    cerr << "Couldn't install signal handler for SIGTERM" << endl;
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Configure logger.
 | 
			
		||||
  gLogInit("transceiver",gConfig.getStr("Log.Level").c_str(),LOG_LOCAL7);
 | 
			
		||||
  if (testConfig(CONFIGDB) < 0) {
 | 
			
		||||
    std::cerr << "Config: Database failure" << std::endl;
 | 
			
		||||
    return EXIT_FAILURE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int numARFCN=1;
 | 
			
		||||
  logLevel = gConfig.getStr("Log.Level");
 | 
			
		||||
  trxPort = gConfig.getNum("TRX.Port");
 | 
			
		||||
  trxAddr = gConfig.getStr("TRX.IP");
 | 
			
		||||
  gLogInit("transceiver", logLevel.c_str(), LOG_LOCAL7);
 | 
			
		||||
 | 
			
		||||
  LOG(NOTICE) << "starting transceiver with " << numARFCN << " ARFCNs (argc=" << argc << ")";
 | 
			
		||||
  if (gConfig.defines("GSM.Radio.TxAntenna"))
 | 
			
		||||
    txAntenna = gConfig.getStr("GSM.Radio.TxAntenna").c_str();
 | 
			
		||||
  if (gConfig.defines("GSM.Radio.RxAntenna"))
 | 
			
		||||
    rxAntenna = gConfig.getStr("GSM.Radio.RxAntenna").c_str();
 | 
			
		||||
 | 
			
		||||
  if (txAntenna != "")  
 | 
			
		||||
    usrp->setTxAntenna(txAntenna);
 | 
			
		||||
  if (rxAntenna != "")  
 | 
			
		||||
    usrp->setRxAntenna(rxAntenna);
 | 
			
		||||
 | 
			
		||||
  LOG(INFO) << "transceiver using transmit antenna " << usrp->getRxAntenna();
 | 
			
		||||
  LOG(INFO) << "transceiver using receive antenna " << usrp->getTxAntenna();
 | 
			
		||||
 | 
			
		||||
  srandom(time(NULL));
 | 
			
		||||
 | 
			
		||||
  int mOversamplingRate = numARFCN/2 + numARFCN;
 | 
			
		||||
  RadioDevice *usrp = RadioDevice::make(DEVICERATE * SAMPSPERSYM);
 | 
			
		||||
  if (!usrp->open(deviceArgs)) {
 | 
			
		||||
  usrp = RadioDevice::make(SAMPSPERSYM);
 | 
			
		||||
  int radioType = usrp->open(deviceArgs);
 | 
			
		||||
  if (radioType < 0) {
 | 
			
		||||
    LOG(ALERT) << "Transceiver exiting..." << std::endl;
 | 
			
		||||
    return EXIT_FAILURE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  RadioInterface* radio = new RadioInterface(usrp,3,SAMPSPERSYM,mOversamplingRate,false);
 | 
			
		||||
  Transceiver *trx = new Transceiver(gConfig.getNum("TRX.Port"),gConfig.getStr("TRX.IP").c_str(),SAMPSPERSYM,GSM::Time(3,0),radio);
 | 
			
		||||
  trx->receiveFIFO(radio->receiveFIFO());
 | 
			
		||||
/*
 | 
			
		||||
  signalVector *gsmPulse = generateGSMPulse(2,1);
 | 
			
		||||
  BitVector normalBurstSeg = "0000101010100111110010101010010110101110011000111001101010000";
 | 
			
		||||
  BitVector normalBurst(BitVector(normalBurstSeg,gTrainingSequence[0]),normalBurstSeg);
 | 
			
		||||
  signalVector *modBurst = modulateBurst(normalBurst,*gsmPulse,8,1);
 | 
			
		||||
  signalVector *modBurst9 = modulateBurst(normalBurst,*gsmPulse,9,1);
 | 
			
		||||
  signalVector *interpolationFilter = createLPF(0.6/mOversamplingRate,6*mOversamplingRate,1);
 | 
			
		||||
  signalVector totalBurst1(*modBurst,*modBurst9);
 | 
			
		||||
  signalVector totalBurst2(*modBurst,*modBurst);
 | 
			
		||||
  signalVector totalBurst(totalBurst1,totalBurst2);
 | 
			
		||||
  scaleVector(totalBurst,usrp->fullScaleInputValue());
 | 
			
		||||
  double beaconFreq = -1.0*(numARFCN-1)*200e3;
 | 
			
		||||
  signalVector finalVec(625*mOversamplingRate);
 | 
			
		||||
  for (int j = 0; j < numARFCN; j++) {
 | 
			
		||||
	signalVector *frequencyShifter = new signalVector(625*mOversamplingRate);
 | 
			
		||||
	frequencyShifter->fill(1.0);
 | 
			
		||||
	frequencyShift(frequencyShifter,frequencyShifter,2.0*M_PI*(beaconFreq+j*400e3)/(1625.0e3/6.0*mOversamplingRate));
 | 
			
		||||
  	signalVector *interpVec = polyphaseResampleVector(totalBurst,mOversamplingRate,1,interpolationFilter);
 | 
			
		||||
	multVector(*interpVec,*frequencyShifter);
 | 
			
		||||
	addVector(finalVec,*interpVec); 	
 | 
			
		||||
  switch (radioType) {
 | 
			
		||||
  case RadioDevice::NORMAL:
 | 
			
		||||
    radio = new RadioInterface(usrp, 3, SAMPSPERSYM, false);
 | 
			
		||||
    break;
 | 
			
		||||
  case RadioDevice::RESAMP:
 | 
			
		||||
  default:
 | 
			
		||||
    LOG(ALERT) << "Unsupported configuration";
 | 
			
		||||
    return EXIT_FAILURE;
 | 
			
		||||
  }
 | 
			
		||||
  signalVector::iterator itr = finalVec.begin();
 | 
			
		||||
  short finalVecShort[2*finalVec.size()];
 | 
			
		||||
  short *shortItr = finalVecShort;
 | 
			
		||||
  while (itr < finalVec.end()) {
 | 
			
		||||
	*shortItr++ = (short) (itr->real());
 | 
			
		||||
	*shortItr++ = (short) (itr->imag());
 | 
			
		||||
	itr++;
 | 
			
		||||
 | 
			
		||||
  drive = new DriveLoop(SAMPSPERSYM, GSM::Time(3,0), radio);
 | 
			
		||||
  if (!drive->init()) {
 | 
			
		||||
    LOG(ALERT) << "Failed to initialize drive loop";
 | 
			
		||||
  }
 | 
			
		||||
  usrp->loadBurst(finalVecShort,finalVec.size());
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
  trx = new Transceiver(trxPort, trxAddr.c_str(), SAMPSPERSYM, radio, drive, 0);
 | 
			
		||||
  radio->activateChan(0);
 | 
			
		||||
  if (!trx->init()) {
 | 
			
		||||
    LOG(ALERT) << "Failed to initialize transceiver";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  trx->start();
 | 
			
		||||
  //int i = 0;
 | 
			
		||||
  while(!gbShutdown) { sleep(1); }//i++; if (i==60) break;}
 | 
			
		||||
 | 
			
		||||
  while (!gbShutdown)
 | 
			
		||||
    sleep(1);
 | 
			
		||||
 | 
			
		||||
  cout << "Shutting down transceiver..." << endl;
 | 
			
		||||
  trx->shutdown();
 | 
			
		||||
 | 
			
		||||
//  trx->stop();
 | 
			
		||||
  delete trx;
 | 
			
		||||
//  delete radio;
 | 
			
		||||
  delete drive;
 | 
			
		||||
  delete radio;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -27,13 +27,10 @@ enum Symmetry {
 | 
			
		||||
 | 
			
		||||
/** Convolution type indicator */
 | 
			
		||||
enum ConvType {
 | 
			
		||||
  FULL_SPAN = 0,
 | 
			
		||||
  OVERLAP_ONLY = 1,
 | 
			
		||||
  START_ONLY = 2,
 | 
			
		||||
  WITH_TAIL = 3,
 | 
			
		||||
  NO_DELAY = 4,
 | 
			
		||||
  CUSTOM = 5,
 | 
			
		||||
  UNDEFINED = 255
 | 
			
		||||
  START_ONLY,
 | 
			
		||||
  NO_DELAY,
 | 
			
		||||
  CUSTOM,
 | 
			
		||||
  UNDEFINED,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** the core data structure of the Transceiver */
 | 
			
		||||
@@ -44,13 +41,14 @@ class signalVector: public Vector<complex>
 | 
			
		||||
  
 | 
			
		||||
  Symmetry symmetry;   ///< the symmetry of the vector
 | 
			
		||||
  bool realOnly;       ///< true if vector is real-valued, not complex-valued
 | 
			
		||||
  
 | 
			
		||||
  bool aligned;
 | 
			
		||||
 
 | 
			
		||||
 public:
 | 
			
		||||
  
 | 
			
		||||
  /** Constructors */
 | 
			
		||||
  signalVector(int dSize=0, Symmetry wSymmetry = NONE):
 | 
			
		||||
    Vector<complex>(dSize),
 | 
			
		||||
    realOnly(false)
 | 
			
		||||
    realOnly(false), aligned(false)
 | 
			
		||||
    { 
 | 
			
		||||
      symmetry = wSymmetry; 
 | 
			
		||||
    };
 | 
			
		||||
@@ -58,26 +56,45 @@ class signalVector: public Vector<complex>
 | 
			
		||||
  signalVector(complex* wData, size_t start, 
 | 
			
		||||
	       size_t span, Symmetry wSymmetry = NONE):
 | 
			
		||||
    Vector<complex>(NULL,wData+start,wData+start+span),
 | 
			
		||||
    realOnly(false)
 | 
			
		||||
    realOnly(false), aligned(false)
 | 
			
		||||
    { 
 | 
			
		||||
      symmetry = wSymmetry; 
 | 
			
		||||
    };
 | 
			
		||||
      
 | 
			
		||||
  signalVector(const signalVector &vec1, const signalVector &vec2):
 | 
			
		||||
    Vector<complex>(vec1,vec2),
 | 
			
		||||
    realOnly(false)
 | 
			
		||||
    realOnly(false), aligned(false)
 | 
			
		||||
    { 
 | 
			
		||||
      symmetry = vec1.symmetry; 
 | 
			
		||||
    };
 | 
			
		||||
	
 | 
			
		||||
  signalVector(const signalVector &wVector):
 | 
			
		||||
    Vector<complex>(wVector.size()),
 | 
			
		||||
    realOnly(false)
 | 
			
		||||
    realOnly(false), aligned(false)
 | 
			
		||||
    {
 | 
			
		||||
      wVector.copyTo(*this); 
 | 
			
		||||
      symmetry = wVector.getSymmetry();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  signalVector(size_t size, size_t start):
 | 
			
		||||
    Vector<complex>(size + start),
 | 
			
		||||
    realOnly(false), aligned(false)
 | 
			
		||||
    {
 | 
			
		||||
      mStart = mData + start;
 | 
			
		||||
      symmetry = NONE;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  signalVector(const signalVector &wVector, size_t start, size_t tail = 0):
 | 
			
		||||
    Vector<complex>(start + wVector.size() + tail),
 | 
			
		||||
    realOnly(false), aligned(false)
 | 
			
		||||
    {
 | 
			
		||||
      mStart = mData + start;
 | 
			
		||||
      wVector.copyTo(*this);
 | 
			
		||||
      memset(mData, 0, start * sizeof(complex));
 | 
			
		||||
      memset(mStart + wVector.size(), 0, tail * sizeof(complex));
 | 
			
		||||
      symmetry = NONE;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  /** symmetry operators */
 | 
			
		||||
  Symmetry getSymmetry() const { return symmetry;};
 | 
			
		||||
  void setSymmetry(Symmetry wSymmetry) { symmetry = wSymmetry;}; 
 | 
			
		||||
@@ -85,6 +102,10 @@ class signalVector: public Vector<complex>
 | 
			
		||||
  /** real-valued operators */
 | 
			
		||||
  bool isRealOnly() const { return realOnly;};
 | 
			
		||||
  void isRealOnly(bool wOnly) { realOnly = wOnly;};
 | 
			
		||||
 | 
			
		||||
  /** alignment markers */
 | 
			
		||||
  bool isAligned() const { return aligned; };
 | 
			
		||||
  void setAligned(bool aligned) { this->aligned = aligned; };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** Convert a linear number to a dB value */
 | 
			
		||||
@@ -100,7 +121,7 @@ float vectorNorm2(const signalVector &x);
 | 
			
		||||
float vectorPower(const signalVector &x);
 | 
			
		||||
 | 
			
		||||
/** Setup the signal processing library */
 | 
			
		||||
void sigProcLibSetup(int samplesPerSymbol);
 | 
			
		||||
bool sigProcLibSetup(int sps);
 | 
			
		||||
 | 
			
		||||
/** Destroy the signal processing library */
 | 
			
		||||
void sigProcLibDestroy(void);
 | 
			
		||||
@@ -110,23 +131,23 @@ void sigProcLibDestroy(void);
 | 
			
		||||
	@param a,b The vectors to be convolved.
 | 
			
		||||
	@param c, A preallocated vector to hold the convolution result.
 | 
			
		||||
	@param spanType The type/span of the convolution.
 | 
			
		||||
	@return The convolution result.
 | 
			
		||||
	@return The convolution result or NULL on error.
 | 
			
		||||
*/
 | 
			
		||||
signalVector* convolve(const signalVector *a,
 | 
			
		||||
		       const signalVector *b,
 | 
			
		||||
		       signalVector *c,
 | 
			
		||||
		       ConvType spanType,
 | 
			
		||||
		       unsigned startIx = 0,
 | 
			
		||||
		       unsigned len = 0);
 | 
			
		||||
signalVector *convolve(const signalVector *a,
 | 
			
		||||
                       const signalVector *b,
 | 
			
		||||
                       signalVector *c,
 | 
			
		||||
                       ConvType spanType,
 | 
			
		||||
                       int start = 0,
 | 
			
		||||
                       unsigned len = 0,
 | 
			
		||||
                       unsigned step = 1, int offset = 0);
 | 
			
		||||
 | 
			
		||||
/** 
 | 
			
		||||
	Generate the GSM pulse. 
 | 
			
		||||
	@param samplesPerSymbol The number of samples per GSM symbol.
 | 
			
		||||
	@param sps The number of samples per GSM symbol.
 | 
			
		||||
	@param symbolLength The size of the pulse.
 | 
			
		||||
	@return The GSM pulse.
 | 
			
		||||
*/
 | 
			
		||||
signalVector* generateGSMPulse(int samplesPerSymbol,
 | 
			
		||||
			       int symbolLength);
 | 
			
		||||
void generateGSMPulse(int sps, int symbolLength);
 | 
			
		||||
 | 
			
		||||
/** 
 | 
			
		||||
        Frequency shift a vector.
 | 
			
		||||
@@ -163,16 +184,14 @@ bool vectorSlicer(signalVector *x);
 | 
			
		||||
 | 
			
		||||
/** GMSK modulate a GSM burst of bits */
 | 
			
		||||
signalVector *modulateBurst(const BitVector &wBurst,
 | 
			
		||||
			    const signalVector &gsmPulse,
 | 
			
		||||
			    int guardPeriodLength,
 | 
			
		||||
			    int samplesPerSymbol);
 | 
			
		||||
			    int sps, bool emptyPulse = false);
 | 
			
		||||
 | 
			
		||||
/** Sinc function */
 | 
			
		||||
float sinc(float x);
 | 
			
		||||
 | 
			
		||||
/** Delay a vector */
 | 
			
		||||
void delayVector(signalVector &wBurst,
 | 
			
		||||
		 float delay);
 | 
			
		||||
bool delayVector(signalVector &wBurst, float delay);
 | 
			
		||||
 | 
			
		||||
/** Add two vectors in-place */
 | 
			
		||||
bool addVector(signalVector &x,
 | 
			
		||||
@@ -226,21 +245,18 @@ void offsetVector(signalVector &x,
 | 
			
		||||
/**
 | 
			
		||||
        Generate a modulated GSM midamble, stored within the library.
 | 
			
		||||
        @param gsmPulse The GSM pulse used for modulation.
 | 
			
		||||
        @param samplesPerSymbol The number of samples per GSM symbol.
 | 
			
		||||
        @param sps The number of samples per GSM symbol.
 | 
			
		||||
        @param TSC The training sequence [0..7]
 | 
			
		||||
        @return Success.
 | 
			
		||||
*/
 | 
			
		||||
bool generateMidamble(signalVector &gsmPulse,
 | 
			
		||||
		      int samplesPerSymbol,
 | 
			
		||||
		      int TSC);
 | 
			
		||||
bool generateMidamble(int sps, int tsc);
 | 
			
		||||
/**
 | 
			
		||||
        Generate a modulated RACH sequence, stored within the library.
 | 
			
		||||
        @param gsmPulse The GSM pulse used for modulation.
 | 
			
		||||
        @param samplesPerSymbol The number of samples per GSM symbol.
 | 
			
		||||
        @param sps The number of samples per GSM symbol.
 | 
			
		||||
        @return Success.
 | 
			
		||||
*/
 | 
			
		||||
bool generateRACHSequence(signalVector &gsmPulse,
 | 
			
		||||
			  int samplesPerSymbol);
 | 
			
		||||
bool generateRACHSequence(int sps);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
        Energy detector, checks to see if received burst energy is above a threshold.
 | 
			
		||||
@@ -259,41 +275,41 @@ bool energyDetect(signalVector &rxBurst,
 | 
			
		||||
        RACH correlator/detector.
 | 
			
		||||
        @param rxBurst The received GSM burst of interest.
 | 
			
		||||
        @param detectThreshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
 | 
			
		||||
        @param samplesPerSymbol The number of samples per GSM symbol.
 | 
			
		||||
        @param sps The number of samples per GSM symbol.
 | 
			
		||||
        @param amplitude The estimated amplitude of received RACH burst.
 | 
			
		||||
        @param TOA The estimate time-of-arrival of received RACH burst.
 | 
			
		||||
        @return True if burst SNR is larger that the detectThreshold value.
 | 
			
		||||
        @return positive if threshold value is reached, negative on error, zero otherwise
 | 
			
		||||
*/
 | 
			
		||||
bool detectRACHBurst(signalVector &rxBurst,
 | 
			
		||||
		     float detectThreshold,
 | 
			
		||||
		     int samplesPerSymbol,
 | 
			
		||||
		     complex *amplitude,
 | 
			
		||||
		     float* TOA);
 | 
			
		||||
int detectRACHBurst(signalVector &rxBurst,
 | 
			
		||||
                    float detectThreshold,
 | 
			
		||||
                    int sps,
 | 
			
		||||
                    complex *amplitude,
 | 
			
		||||
                    float* TOA);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
        Normal burst correlator, detector, channel estimator.
 | 
			
		||||
        @param rxBurst The received GSM burst of interest.
 | 
			
		||||
 
 | 
			
		||||
        @param detectThreshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
 | 
			
		||||
        @param samplesPerSymbol The number of samples per GSM symbol.
 | 
			
		||||
        @param sps The number of samples per GSM symbol.
 | 
			
		||||
        @param amplitude The estimated amplitude of received TSC burst.
 | 
			
		||||
        @param TOA The estimate time-of-arrival of received TSC burst.
 | 
			
		||||
        @param maxTOA The maximum expected time-of-arrival
 | 
			
		||||
        @param requestChannel Set to true if channel estimation is desired.
 | 
			
		||||
        @param channelResponse The estimated channel.
 | 
			
		||||
        @param channelResponseOffset The time offset b/w the first sample of the channel response and the reported TOA.
 | 
			
		||||
        @return True if burst SNR is larger that the detectThreshold value.
 | 
			
		||||
        @return positive if threshold value is reached, negative on error, zero otherwise
 | 
			
		||||
*/
 | 
			
		||||
bool analyzeTrafficBurst(signalVector &rxBurst,
 | 
			
		||||
			 unsigned TSC,
 | 
			
		||||
			 float detectThreshold,
 | 
			
		||||
			 int samplesPerSymbol,
 | 
			
		||||
			 complex *amplitude,
 | 
			
		||||
			 float *TOA,
 | 
			
		||||
                         unsigned maxTOA,
 | 
			
		||||
                         bool requestChannel = false,
 | 
			
		||||
			 signalVector** channelResponse = NULL,
 | 
			
		||||
			 float *channelResponseOffset = NULL);
 | 
			
		||||
int analyzeTrafficBurst(signalVector &rxBurst,
 | 
			
		||||
			unsigned TSC,
 | 
			
		||||
			float detectThreshold,
 | 
			
		||||
			int sps,
 | 
			
		||||
			complex *amplitude,
 | 
			
		||||
			float *TOA,
 | 
			
		||||
                        unsigned maxTOA,
 | 
			
		||||
                        bool requestChannel = false,
 | 
			
		||||
			signalVector** channelResponse = NULL,
 | 
			
		||||
			float *channelResponseOffset = NULL);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
	Decimate a vector.
 | 
			
		||||
@@ -308,16 +324,13 @@ signalVector *decimateVector(signalVector &wVector,
 | 
			
		||||
        Demodulates a received burst using a soft-slicer.
 | 
			
		||||
	@param rxBurst The burst to be demodulated.
 | 
			
		||||
        @param gsmPulse The GSM pulse.
 | 
			
		||||
        @param samplesPerSymbol The number of samples per GSM symbol.
 | 
			
		||||
        @param sps The number of samples per GSM symbol.
 | 
			
		||||
        @param channel The amplitude estimate of the received burst.
 | 
			
		||||
        @param TOA The time-of-arrival of the received burst.
 | 
			
		||||
        @return The demodulated bit sequence.
 | 
			
		||||
*/
 | 
			
		||||
SoftVector *demodulateBurst(signalVector &rxBurst,
 | 
			
		||||
			 const signalVector &gsmPulse,
 | 
			
		||||
			 int samplesPerSymbol,
 | 
			
		||||
			 complex channel,
 | 
			
		||||
			 float TOA);
 | 
			
		||||
SoftVector *demodulateBurst(signalVector &rxBurst, int sps,
 | 
			
		||||
                            complex channel, float TOA);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
        Creates a simple Kaiser-windowed low-pass FIR filter.
 | 
			
		||||
@@ -372,14 +385,14 @@ bool designDFE(signalVector &channelResponse,
 | 
			
		||||
	Equalize/demodulate a received burst via a decision-feedback equalizer.
 | 
			
		||||
	@param rxBurst The received burst to be demodulated.
 | 
			
		||||
	@param TOA The time-of-arrival of the received burst.
 | 
			
		||||
	@param samplesPerSymbol The number of samples per GSM symbol.
 | 
			
		||||
	@param sps The number of samples per GSM symbol.
 | 
			
		||||
	@param w The feed forward filter of the DFE.
 | 
			
		||||
	@param b The feedback filter of the DFE.
 | 
			
		||||
	@return The demodulated bit sequence.
 | 
			
		||||
*/
 | 
			
		||||
SoftVector *equalizeBurst(signalVector &rxBurst,
 | 
			
		||||
		       float TOA,
 | 
			
		||||
		       int samplesPerSymbol,
 | 
			
		||||
		       int sps,
 | 
			
		||||
		       signalVector &w, 
 | 
			
		||||
		       signalVector &b);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,170 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright 2011 Free Software Foundation, Inc.
 | 
			
		||||
* Copyright 2008, 2010 Kestrel Signal Processing, Inc.
 | 
			
		||||
*
 | 
			
		||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
			
		||||
* See the COPYING file in the main directory for details.
 | 
			
		||||
*
 | 
			
		||||
* This use of this software may be subject to additional restrictions.
 | 
			
		||||
* See the LEGAL file in the main directory for details.
 | 
			
		||||
 | 
			
		||||
	This program is free software: you can redistribute it and/or modify
 | 
			
		||||
	it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
	the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
	(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
	This program is distributed in the hope that it will be useful,
 | 
			
		||||
	but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
	GNU Affero General Public License for more details.
 | 
			
		||||
 | 
			
		||||
	You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Contributors:
 | 
			
		||||
Harvind S. Samra, hssamra@kestrelsp.com
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "sigProcLib.h"
 | 
			
		||||
//#include "radioInterface.h"
 | 
			
		||||
#include <Logger.h>
 | 
			
		||||
#include <Configuration.h>
 | 
			
		||||
#include <GSMCommon.h>
 | 
			
		||||
 | 
			
		||||
using namespace std;
 | 
			
		||||
using namespace GSM;
 | 
			
		||||
 | 
			
		||||
ConfigurationTable gConfig;
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv) {
 | 
			
		||||
 | 
			
		||||
  gLogInit("sigProcLibTest","DEBUG");
 | 
			
		||||
 | 
			
		||||
  int samplesPerSymbol = 1;
 | 
			
		||||
 | 
			
		||||
  int TSC = 2;
 | 
			
		||||
 | 
			
		||||
  sigProcLibSetup(samplesPerSymbol);
 | 
			
		||||
  
 | 
			
		||||
  signalVector *gsmPulse = generateGSMPulse(2,samplesPerSymbol);
 | 
			
		||||
  cout << *gsmPulse << endl;
 | 
			
		||||
 | 
			
		||||
  BitVector RACHBurstStart = "01010101";
 | 
			
		||||
  BitVector RACHBurstRest = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
 | 
			
		||||
 | 
			
		||||
  BitVector RACHBurst(BitVector(RACHBurstStart,gRACHSynchSequence),RACHBurstRest);
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
  signalVector *RACHSeq = modulateBurst(RACHBurst,
 | 
			
		||||
                                        *gsmPulse,
 | 
			
		||||
                                        9,
 | 
			
		||||
                                        samplesPerSymbol);
 | 
			
		||||
 | 
			
		||||
  generateRACHSequence(*gsmPulse,samplesPerSymbol);
 | 
			
		||||
 | 
			
		||||
  complex a; float t;
 | 
			
		||||
  detectRACHBurst(*RACHSeq, 5, samplesPerSymbol,&a,&t); 
 | 
			
		||||
 | 
			
		||||
  //cout << *RACHSeq << endl;
 | 
			
		||||
  //signalVector *autocorr = correlate(RACHSeq,RACHSeq,NULL,NO_DELAY);
 | 
			
		||||
 | 
			
		||||
  //cout << *autocorr;
 | 
			
		||||
 | 
			
		||||
  //exit(1);
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
  /*signalVector x(6500);
 | 
			
		||||
  x.fill(1.0);
 | 
			
		||||
 | 
			
		||||
  frequencyShift(&x,&x,0.48*M_PI);
 | 
			
		||||
 | 
			
		||||
  signalVector *y = polyphaseResampleVector(x,96,65,NULL);
 | 
			
		||||
 | 
			
		||||
  cout << *y << endl;
 | 
			
		||||
 
 | 
			
		||||
  exit(1);*/
 | 
			
		||||
 | 
			
		||||
  //CommSig normalBurstSeg = "0000000000000000000000000000000000000000000000000000000000000";
 | 
			
		||||
 | 
			
		||||
  BitVector normalBurstSeg = "0000101010100111110010101010010110101110011000111001101010000";
 | 
			
		||||
 | 
			
		||||
  BitVector normalBurst(BitVector(normalBurstSeg,gTrainingSequence[TSC]),normalBurstSeg);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  generateMidamble(*gsmPulse,samplesPerSymbol,TSC);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  signalVector *modBurst = modulateBurst(normalBurst,*gsmPulse,
 | 
			
		||||
                                         0,samplesPerSymbol);
 | 
			
		||||
 | 
			
		||||
  
 | 
			
		||||
  //delayVector(*rsVector2,6.932);
 | 
			
		||||
 | 
			
		||||
  complex ampl = 1;
 | 
			
		||||
  float TOA = 0;
 | 
			
		||||
 | 
			
		||||
  //modBurst = rsVector2;
 | 
			
		||||
  //delayVector(*modBurst,0.8);
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
  signalVector channelResponse(4);
 | 
			
		||||
  signalVector::iterator c=channelResponse.begin();
 | 
			
		||||
  *c = (complex) 9000.0; c++;
 | 
			
		||||
  *c = (complex) 0.4*9000.0; c++; c++;
 | 
			
		||||
  *c = (complex) -1.2*0;
 | 
			
		||||
 | 
			
		||||
  signalVector *guhBurst = convolve(modBurst,&channelResponse,NULL,NO_DELAY);
 | 
			
		||||
  delete modBurst; modBurst = guhBurst;
 | 
			
		||||
  */
 | 
			
		||||
 | 
			
		||||
  signalVector *chanResp;
 | 
			
		||||
  /*
 | 
			
		||||
  double noisePwr = 0.001/sqrtf(2);
 | 
			
		||||
  signalVector *noise = gaussianNoise(modBurst->size(),noisePwr);
 | 
			
		||||
  */
 | 
			
		||||
  float chanRespOffset;
 | 
			
		||||
  analyzeTrafficBurst(*modBurst,TSC,8.0,samplesPerSymbol,&l,&TOA,1,true,&chanResp,&chanRespOffset);
 | 
			
		||||
  //addVector(*modBurst,*noise);
 | 
			
		||||
 | 
			
		||||
  cout << "ampl:" << ampl << endl;
 | 
			
		||||
  cout << "TOA: " << TOA << endl;
 | 
			
		||||
  //cout << "chanResp: " << *chanResp << endl;
 | 
			
		||||
  SoftVector *demodBurst = demodulateBurst(*modBurst,*gsmPulse,samplesPerSymbol,(complex) ampl, TOA);
 | 
			
		||||
  
 | 
			
		||||
  cout << *demodBurst << endl;
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
  COUT("chanResp: " << *chanResp);
 | 
			
		||||
 | 
			
		||||
  signalVector *w,*b;
 | 
			
		||||
  designDFE(*chanResp,1.0/noisePwr,7,&w,&b); 
 | 
			
		||||
  COUT("w: " << *w);
 | 
			
		||||
  COUT("b: " << *b);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
  SoftSig *DFEBurst = equalizeBurst(*modBurst,TOA-chanRespOffset,samplesPerSymbol,*w,*b);
 | 
			
		||||
  COUT("DFEBurst: " << *DFEBurst);
 | 
			
		||||
 | 
			
		||||
  delete gsmPulse;
 | 
			
		||||
  delete RACHSeq;
 | 
			
		||||
  delete modBurst;
 | 
			
		||||
  delete sendLPF;
 | 
			
		||||
  delete rcvLPF;
 | 
			
		||||
  delete rsVector;
 | 
			
		||||
  //delete rsVector2;
 | 
			
		||||
  delete autocorr;
 | 
			
		||||
  delete chanResp;
 | 
			
		||||
  delete noise;
 | 
			
		||||
  delete demodBurst;
 | 
			
		||||
  delete w;
 | 
			
		||||
  delete b;
 | 
			
		||||
  delete DFEBurst;  
 | 
			
		||||
  */
 | 
			
		||||
 | 
			
		||||
  sigProcLibDestroy();
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										72
									
								
								config/ax_check_compile_flag.m4
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								config/ax_check_compile_flag.m4
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
# ===========================================================================
 | 
			
		||||
#   http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
 | 
			
		||||
# ===========================================================================
 | 
			
		||||
#
 | 
			
		||||
# SYNOPSIS
 | 
			
		||||
#
 | 
			
		||||
#   AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS])
 | 
			
		||||
#
 | 
			
		||||
# DESCRIPTION
 | 
			
		||||
#
 | 
			
		||||
#   Check whether the given FLAG works with the current language's compiler
 | 
			
		||||
#   or gives an error.  (Warnings, however, are ignored)
 | 
			
		||||
#
 | 
			
		||||
#   ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
 | 
			
		||||
#   success/failure.
 | 
			
		||||
#
 | 
			
		||||
#   If EXTRA-FLAGS is defined, it is added to the current language's default
 | 
			
		||||
#   flags (e.g. CFLAGS) when the check is done.  The check is thus made with
 | 
			
		||||
#   the flags: "CFLAGS EXTRA-FLAGS FLAG".  This can for example be used to
 | 
			
		||||
#   force the compiler to issue an error when a bad flag is given.
 | 
			
		||||
#
 | 
			
		||||
#   NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
 | 
			
		||||
#   macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
 | 
			
		||||
#
 | 
			
		||||
# LICENSE
 | 
			
		||||
#
 | 
			
		||||
#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
 | 
			
		||||
#   Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
 | 
			
		||||
#
 | 
			
		||||
#   This program is free software: you can redistribute it and/or modify it
 | 
			
		||||
#   under the terms of the GNU General Public License as published by the
 | 
			
		||||
#   Free Software Foundation, either version 3 of the License, or (at your
 | 
			
		||||
#   option) any later version.
 | 
			
		||||
#
 | 
			
		||||
#   This program is distributed in the hope that it will be useful, but
 | 
			
		||||
#   WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 | 
			
		||||
#   Public License for more details.
 | 
			
		||||
#
 | 
			
		||||
#   You should have received a copy of the GNU General Public License along
 | 
			
		||||
#   with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
#
 | 
			
		||||
#   As a special exception, the respective Autoconf Macro's copyright owner
 | 
			
		||||
#   gives unlimited permission to copy, distribute and modify the configure
 | 
			
		||||
#   scripts that are the output of Autoconf when processing the Macro. You
 | 
			
		||||
#   need not follow the terms of the GNU General Public License when using
 | 
			
		||||
#   or distributing such scripts, even though portions of the text of the
 | 
			
		||||
#   Macro appear in them. The GNU General Public License (GPL) does govern
 | 
			
		||||
#   all other use of the material that constitutes the Autoconf Macro.
 | 
			
		||||
#
 | 
			
		||||
#   This special exception to the GPL applies to versions of the Autoconf
 | 
			
		||||
#   Macro released by the Autoconf Archive. When you make and distribute a
 | 
			
		||||
#   modified version of the Autoconf Macro, you may extend this special
 | 
			
		||||
#   exception to the GPL to apply to your modified version as well.
 | 
			
		||||
 | 
			
		||||
#serial 2
 | 
			
		||||
 | 
			
		||||
AC_DEFUN([AX_CHECK_COMPILE_FLAG],
 | 
			
		||||
[AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX
 | 
			
		||||
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
 | 
			
		||||
AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
 | 
			
		||||
  ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
 | 
			
		||||
  _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
 | 
			
		||||
  AC_COMPILE_IFELSE([AC_LANG_PROGRAM()],
 | 
			
		||||
    [AS_VAR_SET(CACHEVAR,[yes])],
 | 
			
		||||
    [AS_VAR_SET(CACHEVAR,[no])])
 | 
			
		||||
  _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
 | 
			
		||||
AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes],
 | 
			
		||||
  [m4_default([$2], :)],
 | 
			
		||||
  [m4_default([$3], :)])
 | 
			
		||||
AS_VAR_POPDEF([CACHEVAR])dnl
 | 
			
		||||
])dnl AX_CHECK_COMPILE_FLAGS
 | 
			
		||||
							
								
								
									
										221
									
								
								config/ax_ext.m4
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								config/ax_ext.m4
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,221 @@
 | 
			
		||||
# ===========================================================================
 | 
			
		||||
#          http://www.gnu.org/software/autoconf-archive/ax_ext.html
 | 
			
		||||
# ===========================================================================
 | 
			
		||||
#
 | 
			
		||||
# SYNOPSIS
 | 
			
		||||
#
 | 
			
		||||
#   AX_EXT
 | 
			
		||||
#
 | 
			
		||||
# DESCRIPTION
 | 
			
		||||
#
 | 
			
		||||
#   Find supported SIMD extensions by requesting cpuid. When an SIMD
 | 
			
		||||
#   extension is found, the -m"simdextensionname" is added to SIMD_FLAGS if
 | 
			
		||||
#   compiler supports it. For example, if "sse2" is available, then "-msse2"
 | 
			
		||||
#   is added to SIMD_FLAGS.
 | 
			
		||||
#
 | 
			
		||||
#   This macro calls:
 | 
			
		||||
#
 | 
			
		||||
#     AC_SUBST(SIMD_FLAGS)
 | 
			
		||||
#
 | 
			
		||||
#   And defines:
 | 
			
		||||
#
 | 
			
		||||
#     HAVE_MMX / HAVE_SSE / HAVE_SSE2 / HAVE_SSE3 / HAVE_SSSE3 / HAVE_SSE4.1 / HAVE_SSE4.2 / HAVE_AVX
 | 
			
		||||
#
 | 
			
		||||
# LICENSE
 | 
			
		||||
#
 | 
			
		||||
#   Copyright (c) 2007 Christophe Tournayre <turn3r@users.sourceforge.net>
 | 
			
		||||
#   Copyright (c) 2013 Michael Petch <mpetch@capp-sysware.com>
 | 
			
		||||
#
 | 
			
		||||
#   Copying and distribution of this file, with or without modification, are
 | 
			
		||||
#   permitted in any medium without royalty provided the copyright notice
 | 
			
		||||
#   and this notice are preserved. This file is offered as-is, without any
 | 
			
		||||
#   warranty.
 | 
			
		||||
 | 
			
		||||
#serial 12
 | 
			
		||||
 | 
			
		||||
AC_DEFUN([AX_EXT],
 | 
			
		||||
[
 | 
			
		||||
  AC_REQUIRE([AC_CANONICAL_HOST])
 | 
			
		||||
 | 
			
		||||
  case $host_cpu in
 | 
			
		||||
    i[[3456]]86*|x86_64*|amd64*)
 | 
			
		||||
 | 
			
		||||
      AC_REQUIRE([AX_GCC_X86_CPUID])
 | 
			
		||||
      AC_REQUIRE([AX_GCC_X86_AVX_XGETBV])
 | 
			
		||||
 | 
			
		||||
      AX_GCC_X86_CPUID(0x00000001)
 | 
			
		||||
      ecx=`echo $ax_cv_gcc_x86_cpuid_0x00000001 | cut -d ":" -f 3`
 | 
			
		||||
      edx=`echo $ax_cv_gcc_x86_cpuid_0x00000001 | cut -d ":" -f 4`
 | 
			
		||||
 | 
			
		||||
      AC_CACHE_CHECK([whether mmx is supported], [ax_cv_have_mmx_ext],
 | 
			
		||||
      [
 | 
			
		||||
        ax_cv_have_mmx_ext=no
 | 
			
		||||
        if test "$((0x$edx>>23&0x01))" = 1; then
 | 
			
		||||
          ax_cv_have_mmx_ext=yes
 | 
			
		||||
        fi
 | 
			
		||||
      ])
 | 
			
		||||
 | 
			
		||||
      AC_CACHE_CHECK([whether sse is supported], [ax_cv_have_sse_ext],
 | 
			
		||||
      [
 | 
			
		||||
        ax_cv_have_sse_ext=no
 | 
			
		||||
        if test "$((0x$edx>>25&0x01))" = 1; then
 | 
			
		||||
          ax_cv_have_sse_ext=yes
 | 
			
		||||
        fi
 | 
			
		||||
      ])
 | 
			
		||||
 | 
			
		||||
      AC_CACHE_CHECK([whether sse2 is supported], [ax_cv_have_sse2_ext],
 | 
			
		||||
      [
 | 
			
		||||
        ax_cv_have_sse2_ext=no
 | 
			
		||||
        if test "$((0x$edx>>26&0x01))" = 1; then
 | 
			
		||||
          ax_cv_have_sse2_ext=yes
 | 
			
		||||
        fi
 | 
			
		||||
      ])
 | 
			
		||||
 | 
			
		||||
      AC_CACHE_CHECK([whether sse3 is supported], [ax_cv_have_sse3_ext],
 | 
			
		||||
      [
 | 
			
		||||
        ax_cv_have_sse3_ext=no
 | 
			
		||||
        if test "$((0x$ecx&0x01))" = 1; then
 | 
			
		||||
          ax_cv_have_sse3_ext=yes
 | 
			
		||||
        fi
 | 
			
		||||
      ])
 | 
			
		||||
 | 
			
		||||
      AC_CACHE_CHECK([whether ssse3 is supported], [ax_cv_have_ssse3_ext],
 | 
			
		||||
      [
 | 
			
		||||
        ax_cv_have_ssse3_ext=no
 | 
			
		||||
        if test "$((0x$ecx>>9&0x01))" = 1; then
 | 
			
		||||
          ax_cv_have_ssse3_ext=yes
 | 
			
		||||
        fi
 | 
			
		||||
      ])
 | 
			
		||||
 | 
			
		||||
      AC_CACHE_CHECK([whether sse4.1 is supported], [ax_cv_have_sse41_ext],
 | 
			
		||||
      [
 | 
			
		||||
        ax_cv_have_sse41_ext=no
 | 
			
		||||
        if test "$((0x$ecx>>19&0x01))" = 1; then
 | 
			
		||||
          ax_cv_have_sse41_ext=yes
 | 
			
		||||
        fi
 | 
			
		||||
      ])
 | 
			
		||||
 | 
			
		||||
      AC_CACHE_CHECK([whether sse4.2 is supported], [ax_cv_have_sse42_ext],
 | 
			
		||||
      [
 | 
			
		||||
        ax_cv_have_sse42_ext=no
 | 
			
		||||
        if test "$((0x$ecx>>20&0x01))" = 1; then
 | 
			
		||||
          ax_cv_have_sse42_ext=yes
 | 
			
		||||
        fi
 | 
			
		||||
      ])
 | 
			
		||||
 | 
			
		||||
      AC_CACHE_CHECK([whether avx is supported by processor], [ax_cv_have_avx_cpu_ext],
 | 
			
		||||
      [
 | 
			
		||||
        ax_cv_have_avx_cpu_ext=no
 | 
			
		||||
        if test "$((0x$ecx>>28&0x01))" = 1; then
 | 
			
		||||
          ax_cv_have_avx_cpu_ext=yes
 | 
			
		||||
        fi
 | 
			
		||||
      ])
 | 
			
		||||
 | 
			
		||||
      if test x"$ax_cv_have_avx_cpu_ext" = x"yes"; then
 | 
			
		||||
        AX_GCC_X86_AVX_XGETBV(0x00000000)
 | 
			
		||||
 | 
			
		||||
        xgetbv_eax="0"
 | 
			
		||||
        if test x"$ax_cv_gcc_x86_avx_xgetbv_0x00000000" != x"unknown"; then
 | 
			
		||||
          xgetbv_eax=`echo $ax_cv_gcc_x86_avx_xgetbv_0x00000000 | cut -d ":" -f 1`
 | 
			
		||||
        fi
 | 
			
		||||
 | 
			
		||||
        AC_CACHE_CHECK([whether avx is supported by operating system], [ax_cv_have_avx_ext],
 | 
			
		||||
        [
 | 
			
		||||
          ax_cv_have_avx_ext=no
 | 
			
		||||
 | 
			
		||||
          if test "$((0x$ecx>>27&0x01))" = 1; then
 | 
			
		||||
            if test "$((0x$xgetbv_eax&0x6))" = 6; then
 | 
			
		||||
              ax_cv_have_avx_ext=yes
 | 
			
		||||
            fi
 | 
			
		||||
          fi
 | 
			
		||||
        ])
 | 
			
		||||
        if test x"$ax_cv_have_avx_ext" = x"no"; then
 | 
			
		||||
          AC_MSG_WARN([Your processor supports AVX, but your operating system doesn't])
 | 
			
		||||
        fi
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
      if test "$ax_cv_have_mmx_ext" = yes; then
 | 
			
		||||
        AX_CHECK_COMPILE_FLAG(-mmmx, ax_cv_support_mmx_ext=yes, [])
 | 
			
		||||
        if test x"$ax_cv_support_mmx_ext" = x"yes"; then
 | 
			
		||||
          SIMD_FLAGS="$SIMD_FLAGS -mmmx"
 | 
			
		||||
          AC_DEFINE(HAVE_MMX,,[Support mmx instructions])
 | 
			
		||||
        else
 | 
			
		||||
          AC_MSG_WARN([Your processor supports mmx instructions but not your compiler, can you try another compiler?])
 | 
			
		||||
        fi
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
      if test "$ax_cv_have_sse_ext" = yes; then
 | 
			
		||||
        AX_CHECK_COMPILE_FLAG(-msse, ax_cv_support_sse_ext=yes, [])
 | 
			
		||||
        if test x"$ax_cv_support_sse_ext" = x"yes"; then
 | 
			
		||||
          SIMD_FLAGS="$SIMD_FLAGS -msse"
 | 
			
		||||
          AC_DEFINE(HAVE_SSE,,[Support SSE (Streaming SIMD Extensions) instructions])
 | 
			
		||||
        else
 | 
			
		||||
          AC_MSG_WARN([Your processor supports sse instructions but not your compiler, can you try another compiler?])
 | 
			
		||||
        fi
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
      if test "$ax_cv_have_sse2_ext" = yes; then
 | 
			
		||||
        AX_CHECK_COMPILE_FLAG(-msse2, ax_cv_support_sse2_ext=yes, [])
 | 
			
		||||
        if test x"$ax_cv_support_sse2_ext" = x"yes"; then
 | 
			
		||||
          SIMD_FLAGS="$SIMD_FLAGS -msse2"
 | 
			
		||||
          AC_DEFINE(HAVE_SSE2,,[Support SSE2 (Streaming SIMD Extensions 2) instructions])
 | 
			
		||||
        else
 | 
			
		||||
          AC_MSG_WARN([Your processor supports sse2 instructions but not your compiler, can you try another compiler?])
 | 
			
		||||
        fi
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
      if test "$ax_cv_have_sse3_ext" = yes; then
 | 
			
		||||
        AX_CHECK_COMPILE_FLAG(-msse3, ax_cv_support_sse3_ext=yes, [])
 | 
			
		||||
        if test x"$ax_cv_support_sse3_ext" = x"yes"; then
 | 
			
		||||
          SIMD_FLAGS="$SIMD_FLAGS -msse3"
 | 
			
		||||
          AC_DEFINE(HAVE_SSE3,,[Support SSE3 (Streaming SIMD Extensions 3) instructions])
 | 
			
		||||
        else
 | 
			
		||||
          AC_MSG_WARN([Your processor supports sse3 instructions but not your compiler, can you try another compiler?])
 | 
			
		||||
        fi
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
      if test "$ax_cv_have_ssse3_ext" = yes; then
 | 
			
		||||
        AX_CHECK_COMPILE_FLAG(-mssse3, ax_cv_support_ssse3_ext=yes, [])
 | 
			
		||||
        if test x"$ax_cv_support_ssse3_ext" = x"yes"; then
 | 
			
		||||
          SIMD_FLAGS="$SIMD_FLAGS -mssse3"
 | 
			
		||||
          AC_DEFINE(HAVE_SSSE3,,[Support SSSE3 (Supplemental Streaming SIMD Extensions 3) instructions])
 | 
			
		||||
        else
 | 
			
		||||
          AC_MSG_WARN([Your processor supports ssse3 instructions but not your compiler, can you try another compiler?])
 | 
			
		||||
        fi
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
      if test "$ax_cv_have_sse41_ext" = yes; then
 | 
			
		||||
        AX_CHECK_COMPILE_FLAG(-msse4.1, ax_cv_support_sse41_ext=yes, [])
 | 
			
		||||
        if test x"$ax_cv_support_sse41_ext" = x"yes"; then
 | 
			
		||||
          SIMD_FLAGS="$SIMD_FLAGS -msse4.1"
 | 
			
		||||
          AC_DEFINE(HAVE_SSE4_1,,[Support SSSE4.1 (Streaming SIMD Extensions 4.1) instructions])
 | 
			
		||||
        else
 | 
			
		||||
          AC_MSG_WARN([Your processor supports sse4.1 instructions but not your compiler, can you try another compiler?])
 | 
			
		||||
        fi
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
      if test "$ax_cv_have_sse42_ext" = yes; then
 | 
			
		||||
        AX_CHECK_COMPILE_FLAG(-msse4.2, ax_cv_support_sse42_ext=yes, [])
 | 
			
		||||
        if test x"$ax_cv_support_sse42_ext" = x"yes"; then
 | 
			
		||||
          SIMD_FLAGS="$SIMD_FLAGS -msse4.2"
 | 
			
		||||
          AC_DEFINE(HAVE_SSE4_2,,[Support SSSE4.2 (Streaming SIMD Extensions 4.2) instructions])
 | 
			
		||||
        else
 | 
			
		||||
          AC_MSG_WARN([Your processor supports sse4.2 instructions but not your compiler, can you try another compiler?])
 | 
			
		||||
        fi
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
      if test "$ax_cv_have_avx_ext" = yes; then
 | 
			
		||||
        AX_CHECK_COMPILE_FLAG(-mavx, ax_cv_support_avx_ext=yes, [])
 | 
			
		||||
        if test x"$ax_cv_support_avx_ext" = x"yes"; then
 | 
			
		||||
          SIMD_FLAGS="$SIMD_FLAGS -mavx"
 | 
			
		||||
          AC_DEFINE(HAVE_AVX,,[Support AVX (Advanced Vector Extensions) instructions])
 | 
			
		||||
        else
 | 
			
		||||
          AC_MSG_WARN([Your processor supports avx instructions but not your compiler, can you try another compiler?])
 | 
			
		||||
        fi
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
  ;;
 | 
			
		||||
  esac
 | 
			
		||||
 | 
			
		||||
  AC_SUBST(SIMD_FLAGS)
 | 
			
		||||
])
 | 
			
		||||
							
								
								
									
										79
									
								
								config/ax_gcc_x86_avx_xgetbv.m4
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								config/ax_gcc_x86_avx_xgetbv.m4
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
			
		||||
# ===========================================================================
 | 
			
		||||
#   http://www.gnu.org/software/autoconf-archive/ax_gcc_x86_avx_xgetbv.html
 | 
			
		||||
# ===========================================================================
 | 
			
		||||
#
 | 
			
		||||
# SYNOPSIS
 | 
			
		||||
#
 | 
			
		||||
#   AX_GCC_X86_AVX_XGETBV
 | 
			
		||||
#
 | 
			
		||||
# DESCRIPTION
 | 
			
		||||
#
 | 
			
		||||
#   On later x86 processors with AVX SIMD support, with gcc or a compiler
 | 
			
		||||
#   that has a compatible syntax for inline assembly instructions, run a
 | 
			
		||||
#   small program that executes the xgetbv instruction with input OP. This
 | 
			
		||||
#   can be used to detect if the OS supports AVX instruction usage.
 | 
			
		||||
#
 | 
			
		||||
#   On output, the values of the eax and edx registers are stored as
 | 
			
		||||
#   hexadecimal strings as "eax:edx" in the cache variable
 | 
			
		||||
#   ax_cv_gcc_x86_avx_xgetbv.
 | 
			
		||||
#
 | 
			
		||||
#   If the xgetbv instruction fails (because you are running a
 | 
			
		||||
#   cross-compiler, or because you are not using gcc, or because you are on
 | 
			
		||||
#   a processor that doesn't have this instruction),
 | 
			
		||||
#   ax_cv_gcc_x86_avx_xgetbv_OP is set to the string "unknown".
 | 
			
		||||
#
 | 
			
		||||
#   This macro mainly exists to be used in AX_EXT.
 | 
			
		||||
#
 | 
			
		||||
# LICENSE
 | 
			
		||||
#
 | 
			
		||||
#   Copyright (c) 2013 Michael Petch <mpetch@capp-sysware.com>
 | 
			
		||||
#
 | 
			
		||||
#   This program is free software: you can redistribute it and/or modify it
 | 
			
		||||
#   under the terms of the GNU General Public License as published by the
 | 
			
		||||
#   Free Software Foundation, either version 3 of the License, or (at your
 | 
			
		||||
#   option) any later version.
 | 
			
		||||
#
 | 
			
		||||
#   This program is distributed in the hope that it will be useful, but
 | 
			
		||||
#   WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 | 
			
		||||
#   Public License for more details.
 | 
			
		||||
#
 | 
			
		||||
#   You should have received a copy of the GNU General Public License along
 | 
			
		||||
#   with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
#
 | 
			
		||||
#   As a special exception, the respective Autoconf Macro's copyright owner
 | 
			
		||||
#   gives unlimited permission to copy, distribute and modify the configure
 | 
			
		||||
#   scripts that are the output of Autoconf when processing the Macro. You
 | 
			
		||||
#   need not follow the terms of the GNU General Public License when using
 | 
			
		||||
#   or distributing such scripts, even though portions of the text of the
 | 
			
		||||
#   Macro appear in them. The GNU General Public License (GPL) does govern
 | 
			
		||||
#   all other use of the material that constitutes the Autoconf Macro.
 | 
			
		||||
#
 | 
			
		||||
#   This special exception to the GPL applies to versions of the Autoconf
 | 
			
		||||
#   Macro released by the Autoconf Archive. When you make and distribute a
 | 
			
		||||
#   modified version of the Autoconf Macro, you may extend this special
 | 
			
		||||
#   exception to the GPL to apply to your modified version as well.
 | 
			
		||||
 | 
			
		||||
#serial 1
 | 
			
		||||
 | 
			
		||||
AC_DEFUN([AX_GCC_X86_AVX_XGETBV],
 | 
			
		||||
[AC_REQUIRE([AC_PROG_CC])
 | 
			
		||||
AC_LANG_PUSH([C])
 | 
			
		||||
AC_CACHE_CHECK(for x86-AVX xgetbv $1 output, ax_cv_gcc_x86_avx_xgetbv_$1,
 | 
			
		||||
 [AC_RUN_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>], [
 | 
			
		||||
     int op = $1, eax, edx;
 | 
			
		||||
     FILE *f;
 | 
			
		||||
      /* Opcodes for xgetbv */
 | 
			
		||||
      __asm__(".byte 0x0f, 0x01, 0xd0"
 | 
			
		||||
        : "=a" (eax), "=d" (edx)
 | 
			
		||||
        : "c" (op));
 | 
			
		||||
     f = fopen("conftest_xgetbv", "w"); if (!f) return 1;
 | 
			
		||||
     fprintf(f, "%x:%x\n", eax, edx);
 | 
			
		||||
     fclose(f);
 | 
			
		||||
     return 0;
 | 
			
		||||
])],
 | 
			
		||||
     [ax_cv_gcc_x86_avx_xgetbv_$1=`cat conftest_xgetbv`; rm -f conftest_xgetbv],
 | 
			
		||||
     [ax_cv_gcc_x86_avx_xgetbv_$1=unknown; rm -f conftest_xgetbv],
 | 
			
		||||
     [ax_cv_gcc_x86_avx_xgetbv_$1=unknown])])
 | 
			
		||||
AC_LANG_POP([C])
 | 
			
		||||
])
 | 
			
		||||
							
								
								
									
										79
									
								
								config/ax_gcc_x86_cpuid.m4
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								config/ax_gcc_x86_cpuid.m4
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
			
		||||
# ===========================================================================
 | 
			
		||||
#     http://www.gnu.org/software/autoconf-archive/ax_gcc_x86_cpuid.html
 | 
			
		||||
# ===========================================================================
 | 
			
		||||
#
 | 
			
		||||
# SYNOPSIS
 | 
			
		||||
#
 | 
			
		||||
#   AX_GCC_X86_CPUID(OP)
 | 
			
		||||
#
 | 
			
		||||
# DESCRIPTION
 | 
			
		||||
#
 | 
			
		||||
#   On Pentium and later x86 processors, with gcc or a compiler that has a
 | 
			
		||||
#   compatible syntax for inline assembly instructions, run a small program
 | 
			
		||||
#   that executes the cpuid instruction with input OP. This can be used to
 | 
			
		||||
#   detect the CPU type.
 | 
			
		||||
#
 | 
			
		||||
#   On output, the values of the eax, ebx, ecx, and edx registers are stored
 | 
			
		||||
#   as hexadecimal strings as "eax:ebx:ecx:edx" in the cache variable
 | 
			
		||||
#   ax_cv_gcc_x86_cpuid_OP.
 | 
			
		||||
#
 | 
			
		||||
#   If the cpuid instruction fails (because you are running a
 | 
			
		||||
#   cross-compiler, or because you are not using gcc, or because you are on
 | 
			
		||||
#   a processor that doesn't have this instruction), ax_cv_gcc_x86_cpuid_OP
 | 
			
		||||
#   is set to the string "unknown".
 | 
			
		||||
#
 | 
			
		||||
#   This macro mainly exists to be used in AX_GCC_ARCHFLAG.
 | 
			
		||||
#
 | 
			
		||||
# LICENSE
 | 
			
		||||
#
 | 
			
		||||
#   Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
 | 
			
		||||
#   Copyright (c) 2008 Matteo Frigo
 | 
			
		||||
#
 | 
			
		||||
#   This program is free software: you can redistribute it and/or modify it
 | 
			
		||||
#   under the terms of the GNU General Public License as published by the
 | 
			
		||||
#   Free Software Foundation, either version 3 of the License, or (at your
 | 
			
		||||
#   option) any later version.
 | 
			
		||||
#
 | 
			
		||||
#   This program is distributed in the hope that it will be useful, but
 | 
			
		||||
#   WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 | 
			
		||||
#   Public License for more details.
 | 
			
		||||
#
 | 
			
		||||
#   You should have received a copy of the GNU General Public License along
 | 
			
		||||
#   with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
#
 | 
			
		||||
#   As a special exception, the respective Autoconf Macro's copyright owner
 | 
			
		||||
#   gives unlimited permission to copy, distribute and modify the configure
 | 
			
		||||
#   scripts that are the output of Autoconf when processing the Macro. You
 | 
			
		||||
#   need not follow the terms of the GNU General Public License when using
 | 
			
		||||
#   or distributing such scripts, even though portions of the text of the
 | 
			
		||||
#   Macro appear in them. The GNU General Public License (GPL) does govern
 | 
			
		||||
#   all other use of the material that constitutes the Autoconf Macro.
 | 
			
		||||
#
 | 
			
		||||
#   This special exception to the GPL applies to versions of the Autoconf
 | 
			
		||||
#   Macro released by the Autoconf Archive. When you make and distribute a
 | 
			
		||||
#   modified version of the Autoconf Macro, you may extend this special
 | 
			
		||||
#   exception to the GPL to apply to your modified version as well.
 | 
			
		||||
 | 
			
		||||
#serial 7
 | 
			
		||||
 | 
			
		||||
AC_DEFUN([AX_GCC_X86_CPUID],
 | 
			
		||||
[AC_REQUIRE([AC_PROG_CC])
 | 
			
		||||
AC_LANG_PUSH([C])
 | 
			
		||||
AC_CACHE_CHECK(for x86 cpuid $1 output, ax_cv_gcc_x86_cpuid_$1,
 | 
			
		||||
 [AC_RUN_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>], [
 | 
			
		||||
     int op = $1, eax, ebx, ecx, edx;
 | 
			
		||||
     FILE *f;
 | 
			
		||||
      __asm__("cpuid"
 | 
			
		||||
        : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
 | 
			
		||||
        : "a" (op));
 | 
			
		||||
     f = fopen("conftest_cpuid", "w"); if (!f) return 1;
 | 
			
		||||
     fprintf(f, "%x:%x:%x:%x\n", eax, ebx, ecx, edx);
 | 
			
		||||
     fclose(f);
 | 
			
		||||
     return 0;
 | 
			
		||||
])],
 | 
			
		||||
     [ax_cv_gcc_x86_cpuid_$1=`cat conftest_cpuid`; rm -f conftest_cpuid],
 | 
			
		||||
     [ax_cv_gcc_x86_cpuid_$1=unknown; rm -f conftest_cpuid],
 | 
			
		||||
     [ax_cv_gcc_x86_cpuid_$1=unknown])])
 | 
			
		||||
AC_LANG_POP([C])
 | 
			
		||||
])
 | 
			
		||||
							
								
								
									
										15
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								configure.ac
									
									
									
									
									
								
							@@ -22,6 +22,7 @@ AC_INIT(openbts,P2.8TRUNK)
 | 
			
		||||
AC_PREREQ(2.57)
 | 
			
		||||
AC_CONFIG_SRCDIR([Transceiver52M/Makefile.am])
 | 
			
		||||
AC_CONFIG_AUX_DIR([.])	
 | 
			
		||||
AC_CONFIG_MACRO_DIR([config])
 | 
			
		||||
AM_CONFIG_HEADER(config.h)
 | 
			
		||||
 | 
			
		||||
AC_CANONICAL_BUILD
 | 
			
		||||
@@ -72,11 +73,6 @@ AC_ARG_WITH(singledb, [
 | 
			
		||||
        [enable single daughterboard use on USRP1])
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
AC_ARG_WITH(resamp, [
 | 
			
		||||
    AS_HELP_STRING([--with-resamp],
 | 
			
		||||
        [enable resampling for non-52MHz devices])
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
AC_ARG_WITH(extref, [
 | 
			
		||||
    AS_HELP_STRING([--with-extref],
 | 
			
		||||
        [enable external reference on UHD devices])
 | 
			
		||||
@@ -98,12 +94,10 @@ AS_IF([test "x$with_usrp1" = "xyes"], [
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
AS_IF([test "x$with_uhd" = "xyes"],[
 | 
			
		||||
    PKG_CHECK_MODULES(UHD, uhd >= 003.002.000)
 | 
			
		||||
    PKG_CHECK_MODULES(UHD, uhd >= 003.004.000)
 | 
			
		||||
    AC_DEFINE(USE_UHD, 1, Define to 1 if using UHD)
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
AS_IF([test "x$with_resamp" = "xyes"], [
 | 
			
		||||
    AC_DEFINE(RESAMPLE, 1, Define to 1 for resampling)
 | 
			
		||||
    # Find and define supported SIMD extensions
 | 
			
		||||
    AX_EXT
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
AS_IF([test "x$with_extref" = "xyes"], [
 | 
			
		||||
@@ -114,7 +108,6 @@ AS_IF([test "x$with_singledb" = "xyes"], [
 | 
			
		||||
    AC_DEFINE(SINGLEDB, 1, Define to 1 for single daughterboard)
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
AM_CONDITIONAL(RESAMPLE, [test "x$with_resamp" = "xyes"])
 | 
			
		||||
AM_CONDITIONAL(UHD, [test "x$with_uhd" = "xyes"])
 | 
			
		||||
AM_CONDITIONAL(USRP1, [test "x$with_usrp1" = "xyes"])
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user