mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
				synced 2025-11-03 21:53:18 +00:00 
			
		
		
		
	Compare commits
	
		
			11 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					774a06369a | ||
| 
						 | 
					94ce835050 | ||
| 
						 | 
					09befd7a06 | ||
| 
						 | 
					1303376ad1 | ||
| 
						 | 
					25021dfe5a | ||
| 
						 | 
					e287598e6b | ||
| 
						 | 
					98b1af896c | ||
| 
						 | 
					14bb9c923d | ||
| 
						 | 
					c7f36c282a | ||
| 
						 | 
					f31e4bb089 | ||
| 
						 | 
					1189019c30 | 
@@ -45,6 +45,8 @@ const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000
 | 
			
		||||
 | 
			
		||||
const BitVector GSM::gRACHSynchSequence("01001011011111111001100110101010001111000");
 | 
			
		||||
 | 
			
		||||
const BitVector GSM::gSCHSynchSequence("1011100101100010000001000000111100101101010001010111011000011011");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int32_t GSM::FNDelta(int32_t v1, int32_t v2)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -53,6 +53,8 @@ extern const BitVector gDummyBurst;
 | 
			
		||||
/** Random access burst synch. sequence */
 | 
			
		||||
extern const BitVector gRACHSynchSequence;
 | 
			
		||||
 | 
			
		||||
/** Synchronization burst sync sequence */
 | 
			
		||||
extern const BitVector gSCHSynchSequence;
 | 
			
		||||
 | 
			
		||||
/**@name Modulus operations for frame numbers. */
 | 
			
		||||
//@{
 | 
			
		||||
 
 | 
			
		||||
@@ -56,7 +56,8 @@ COMMON_SOURCES = \
 | 
			
		||||
	radioClock.cpp \
 | 
			
		||||
	sigProcLib.cpp \
 | 
			
		||||
	signalVector.cpp \
 | 
			
		||||
	Transceiver.cpp
 | 
			
		||||
	Transceiver.cpp \
 | 
			
		||||
	sch.c
 | 
			
		||||
 | 
			
		||||
libtransceiver_la_SOURCES = \
 | 
			
		||||
	$(COMMON_SOURCES) \
 | 
			
		||||
@@ -87,7 +88,9 @@ osmo_trx_LDADD = \
 | 
			
		||||
	libtransceiver.la \
 | 
			
		||||
	$(ARCH_LA) \
 | 
			
		||||
	$(GSM_LA) \
 | 
			
		||||
	$(COMMON_LA) $(SQLITE_LA)
 | 
			
		||||
	$(COMMON_LA) \
 | 
			
		||||
	$(SQLITE_LA) \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS)
 | 
			
		||||
 | 
			
		||||
if USRP1 
 | 
			
		||||
libtransceiver_la_SOURCES += USRPDevice.cpp
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,10 @@
 | 
			
		||||
#include "Transceiver.h"
 | 
			
		||||
#include <Logger.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include "sch.h"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#endif
 | 
			
		||||
@@ -39,11 +43,16 @@ using namespace GSM;
 | 
			
		||||
#  define USB_LATENCY_MIN		1,1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Clock indication interval in frames */
 | 
			
		||||
#define CLK_IND_INTERVAL		100
 | 
			
		||||
 | 
			
		||||
/* Number of running values use in noise average */
 | 
			
		||||
#define NOISE_CNT			20
 | 
			
		||||
#define FREQ_CNT			20
 | 
			
		||||
 | 
			
		||||
TransceiverState::TransceiverState()
 | 
			
		||||
  : mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT)
 | 
			
		||||
  : mRetrans(false), mNoiseLev(0.0),
 | 
			
		||||
    mNoises(NOISE_CNT), mFreqOffsets(FREQ_CNT), mode(Transceiver::TRX_MODE_OFF)
 | 
			
		||||
{
 | 
			
		||||
  for (int i = 0; i < 8; i++) {
 | 
			
		||||
    chanType[i] = Transceiver::NONE;
 | 
			
		||||
@@ -51,6 +60,7 @@ TransceiverState::TransceiverState()
 | 
			
		||||
    chanResponse[i] = NULL;
 | 
			
		||||
    DFEForward[i] = NULL;
 | 
			
		||||
    DFEFeedback[i] = NULL;
 | 
			
		||||
    prevFrame[i] = NULL;
 | 
			
		||||
 | 
			
		||||
    for (int n = 0; n < 102; n++)
 | 
			
		||||
      fillerTable[n][i] = NULL;
 | 
			
		||||
@@ -91,12 +101,12 @@ Transceiver::Transceiver(int wBasePort,
 | 
			
		||||
  : mBasePort(wBasePort), mAddr(TRXAddress),
 | 
			
		||||
    mTransmitLatency(wTransmitLatency), mClockSocket(NULL),
 | 
			
		||||
    mRadioInterface(wRadioInterface), mSPSTx(wSPS), mSPSRx(1), mChans(wChans),
 | 
			
		||||
    mOn(false), mTxFreq(0.0), mRxFreq(0.0), mPower(-10), mMaxExpectedDelay(0)
 | 
			
		||||
    mOn(false), mTxFreq(0.0), mRxFreq(0.0), mPower(-10), mMaxExpectedDelay(0),
 | 
			
		||||
    mBSIC(-1)
 | 
			
		||||
{
 | 
			
		||||
  GSM::Time startTime(random() % gHyperframe,0);
 | 
			
		||||
 | 
			
		||||
  mRxLowerLoopThread = new Thread(32768);
 | 
			
		||||
  mTxLowerLoopThread = new Thread(32768);
 | 
			
		||||
  mLowerLoopThread = new Thread(32768);
 | 
			
		||||
 | 
			
		||||
  mTransmitDeadlineClock = startTime;
 | 
			
		||||
  mLastClockUpdateTime = startTime;
 | 
			
		||||
@@ -105,6 +115,9 @@ Transceiver::Transceiver(int wBasePort,
 | 
			
		||||
 | 
			
		||||
  txFullScale = mRadioInterface->fullScaleInputValue();
 | 
			
		||||
  rxFullScale = mRadioInterface->fullScaleOutputValue();
 | 
			
		||||
 | 
			
		||||
  for (int i = 0; i < 8; i++)
 | 
			
		||||
    mRxSlotMask[i] = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Transceiver::~Transceiver()
 | 
			
		||||
@@ -194,6 +207,9 @@ void Transceiver::addRadioVector(size_t chan, BitVector &bits,
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (mStates[0].mode != TRX_MODE_BTS)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
 | 
			
		||||
  scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
 | 
			
		||||
 | 
			
		||||
@@ -221,7 +237,7 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime)
 | 
			
		||||
  radioVector *burst;
 | 
			
		||||
  TransceiverState *state;
 | 
			
		||||
  std::vector<signalVector *> bursts(mChans);
 | 
			
		||||
  std::vector<bool> zeros(mChans);
 | 
			
		||||
  std::vector<bool> zeros(mChans, false);
 | 
			
		||||
  std::vector<bool> filler(mChans, true);
 | 
			
		||||
 | 
			
		||||
  for (size_t i = 0; i < mChans; i ++) {
 | 
			
		||||
@@ -238,6 +254,7 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime)
 | 
			
		||||
    modFN = nowTime.FN() % state->fillerModulus[TN];
 | 
			
		||||
 | 
			
		||||
    bursts[i] = state->fillerTable[modFN][TN];
 | 
			
		||||
    if (state->mode == TRX_MODE_BTS)
 | 
			
		||||
      zeros[i] = state->chanType[TN] == NONE;
 | 
			
		||||
 | 
			
		||||
    if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
 | 
			
		||||
@@ -299,6 +316,19 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
 | 
			
		||||
  unsigned burstTN = currTime.TN();
 | 
			
		||||
  unsigned burstFN = currTime.FN();
 | 
			
		||||
 | 
			
		||||
  if (state->mode == TRX_MODE_MS_TRACK) {
 | 
			
		||||
    /* 102 modulus case currently unhandled */
 | 
			
		||||
    if (state->fillerModulus[burstTN] > 52)
 | 
			
		||||
      return OFF;
 | 
			
		||||
 | 
			
		||||
    int modFN = burstFN % state->fillerModulus[burstTN];
 | 
			
		||||
    unsigned long long reg = (unsigned long long) 1 << modFN;
 | 
			
		||||
    if (reg & mRxSlotMask[burstTN])
 | 
			
		||||
      return TSC;
 | 
			
		||||
    else
 | 
			
		||||
      return OFF;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  switch (state->chanType[burstTN]) {
 | 
			
		||||
  case NONE:
 | 
			
		||||
    return OFF;
 | 
			
		||||
@@ -376,6 +406,108 @@ bool Transceiver::detectRACH(TransceiverState *state,
 | 
			
		||||
  return detectRACHBurst(burst, threshold, mSPSRx, &, &toa);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Detect SCH synchronization sequence within a burst */
 | 
			
		||||
bool Transceiver::detectSCH(TransceiverState *state,
 | 
			
		||||
                            signalVector &burst,
 | 
			
		||||
                            complex &, float &toa)
 | 
			
		||||
{
 | 
			
		||||
  int shift, full;;
 | 
			
		||||
  float mag, threshold = 7.0;
 | 
			
		||||
 | 
			
		||||
  full = (state->mode == TRX_MODE_MS_TRACK) ?
 | 
			
		||||
	 SCH_DETECT_NARROW : SCH_DETECT_FULL;
 | 
			
		||||
 | 
			
		||||
  if (!detectSCHBurst(burst, threshold, mSPSRx, &, &toa, full))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  std::cout << "SCH : Timing offset     " << toa << " symbols" << std::endl;
 | 
			
		||||
 | 
			
		||||
  mag = fabsf(toa);
 | 
			
		||||
  if (mag < 1.0f)
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
  shift = (int) (mag / 2.0f);
 | 
			
		||||
  if (!shift)
 | 
			
		||||
    shift++;
 | 
			
		||||
 | 
			
		||||
  mRadioInterface->applyOffset(toa > 0 ? shift : -shift);
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define SCH_BIT_SCALE	64
 | 
			
		||||
 | 
			
		||||
/* Decode SCH burst */
 | 
			
		||||
bool Transceiver::decodeSCH(SoftVector *burst, GSM::Time *time)
 | 
			
		||||
{
 | 
			
		||||
  int fn;
 | 
			
		||||
  struct sch_info sch;
 | 
			
		||||
  ubit_t info[GSM_SCH_INFO_LEN];
 | 
			
		||||
  sbit_t data[GSM_SCH_CODED_LEN];
 | 
			
		||||
 | 
			
		||||
  if (burst->size() < 156) {
 | 
			
		||||
    std::cout << "Invalid SCH burst length" << std::endl;
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  float_to_sbit(&(*burst)[3], &data[0], SCH_BIT_SCALE, 39);
 | 
			
		||||
  float_to_sbit(&(*burst)[106], &data[39], SCH_BIT_SCALE, 39);
 | 
			
		||||
 | 
			
		||||
  if (!gsm_sch_decode(info, data)) {
 | 
			
		||||
    gsm_sch_parse(info, &sch);
 | 
			
		||||
 | 
			
		||||
    mBSIC = sch.bsic;
 | 
			
		||||
 | 
			
		||||
    std::cout << "SCH : Decoded values" << std::endl;
 | 
			
		||||
    std::cout << "    BSIC: " << sch.bsic << std::endl;
 | 
			
		||||
    std::cout << "    T1  : " << sch.t1 << std::endl;
 | 
			
		||||
    std::cout << "    T2  : " << sch.t2 << std::endl;
 | 
			
		||||
    std::cout << "    T3p : " << sch.t3p << std::endl;
 | 
			
		||||
    std::cout << "    FN  : " << gsm_sch_to_fn(&sch) << std::endl;
 | 
			
		||||
 | 
			
		||||
    fn = gsm_sch_to_fn(&sch);
 | 
			
		||||
    if (fn < 0) {
 | 
			
		||||
      std::cout << "SCH : Failed to convert FN " << std::endl;
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    time->FN(fn);
 | 
			
		||||
    time->TN(0);
 | 
			
		||||
  } else {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define FCCH_OFFSET_LIMIT	2e3
 | 
			
		||||
#define FCCH_ADJUST_LIMIT	20.0
 | 
			
		||||
 | 
			
		||||
/* Apply FCCH frequency correction */
 | 
			
		||||
bool Transceiver::correctFCCH(TransceiverState *state, signalVector *burst)
 | 
			
		||||
{
 | 
			
		||||
  double offset, avg;
 | 
			
		||||
 | 
			
		||||
  if (!burst)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  offset = gsm_fcch_offset((float *) burst->begin(), burst->size());
 | 
			
		||||
  if (offset > FCCH_OFFSET_LIMIT)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  state->mFreqOffsets.insert(offset);
 | 
			
		||||
  avg = state->mFreqOffsets.avg();
 | 
			
		||||
 | 
			
		||||
  if (state->mFreqOffsets.full())
 | 
			
		||||
    std::cout << "FCCH: Frequency offset  " << avg << " Hz" << std::endl;
 | 
			
		||||
 | 
			
		||||
  if (state->mFreqOffsets.full() && (fabs(avg) > FCCH_ADJUST_LIMIT)) {
 | 
			
		||||
    mRadioInterface->tuneRxOffset(-avg);
 | 
			
		||||
    state->mFreqOffsets.reset();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Detect normal burst training sequence midamble. Update equalization
 | 
			
		||||
 * state information and channel estimate if necessary. Equalization
 | 
			
		||||
@@ -462,18 +594,33 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
 | 
			
		||||
  SoftVector *bits = NULL;
 | 
			
		||||
  TransceiverState *state = &mStates[chan];
 | 
			
		||||
 | 
			
		||||
  GSM::Time sch_time, burst_time, diff_time;
 | 
			
		||||
 | 
			
		||||
  /* Blocking FIFO read */
 | 
			
		||||
  radioVector *radio_burst = mReceiveFIFO[chan]->read();
 | 
			
		||||
  if (!radio_burst)
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
  /* Set time and determine correlation type */
 | 
			
		||||
  GSM::Time time = radio_burst->getTime();
 | 
			
		||||
  CorrType type = expectedCorrType(time, chan);
 | 
			
		||||
  burst_time = radio_burst->getTime();
 | 
			
		||||
  CorrType type = expectedCorrType(burst_time, chan);
 | 
			
		||||
 | 
			
		||||
  if ((type == OFF) || (type == IDLE)) {
 | 
			
		||||
    delete radio_burst;
 | 
			
		||||
    return NULL;
 | 
			
		||||
  switch (state->mode) {
 | 
			
		||||
  case TRX_MODE_MS_ACQUIRE:
 | 
			
		||||
    type = SCH;
 | 
			
		||||
    break;
 | 
			
		||||
  case TRX_MODE_MS_TRACK:
 | 
			
		||||
    if (gsm_sch_check_fn(burst_time.FN()))
 | 
			
		||||
      type = SCH;
 | 
			
		||||
    else if (type == OFF)
 | 
			
		||||
      goto release; 
 | 
			
		||||
    break;
 | 
			
		||||
  case TRX_MODE_BTS:
 | 
			
		||||
    if ((type == TSC) || (type == RACH))
 | 
			
		||||
      break;
 | 
			
		||||
  case TRX_MODE_OFF:
 | 
			
		||||
  default:
 | 
			
		||||
    goto release;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Select the diversity channel with highest energy */
 | 
			
		||||
@@ -488,8 +635,7 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
 | 
			
		||||
 | 
			
		||||
  if (max_i < 0) {
 | 
			
		||||
    LOG(ALERT) << "Received empty burst";
 | 
			
		||||
    delete radio_burst;
 | 
			
		||||
    return NULL;
 | 
			
		||||
    goto release;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Average noise on diversity paths and update global levels */
 | 
			
		||||
@@ -499,30 +645,66 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
 | 
			
		||||
 | 
			
		||||
  /* Detect normal or RACH bursts */
 | 
			
		||||
  if (type == TSC)
 | 
			
		||||
    success = detectTSC(state, *burst, amp, toa, time);
 | 
			
		||||
  else
 | 
			
		||||
    success = detectTSC(state, *burst, amp, toa, burst_time);
 | 
			
		||||
  else if (type == RACH)
 | 
			
		||||
    success = detectRACH(state, *burst, amp, toa);
 | 
			
		||||
  else if (type == SCH)
 | 
			
		||||
    success = detectSCH(state, *burst, amp, toa);
 | 
			
		||||
  else
 | 
			
		||||
    success = false;
 | 
			
		||||
 | 
			
		||||
  if (!success) {
 | 
			
		||||
    state->mNoises.insert(avg);
 | 
			
		||||
    delete radio_burst;
 | 
			
		||||
    return NULL;
 | 
			
		||||
    goto release;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Demodulate and set output info */
 | 
			
		||||
  if (equalize && (type != TSC))
 | 
			
		||||
    equalize = false;
 | 
			
		||||
 | 
			
		||||
  if (avg - state->mNoiseLev > 0.0)
 | 
			
		||||
    bits = demodulate(state, *burst, amp, toa, time.TN(), equalize);
 | 
			
		||||
  /* Ignore noise threshold on MS mode for now */
 | 
			
		||||
  if ((type == SCH) || (avg - state->mNoiseLev > 0.0))
 | 
			
		||||
    bits = demodulate(state, *burst, amp, toa,
 | 
			
		||||
		      burst_time.TN(), equalize);
 | 
			
		||||
 | 
			
		||||
  wTime = time;
 | 
			
		||||
  /* MS: Decode SCH and adjust GSM clock */
 | 
			
		||||
  if ((state->mode == TRX_MODE_MS_ACQUIRE) ||
 | 
			
		||||
      (state->mode == TRX_MODE_MS_TRACK)) {
 | 
			
		||||
    correctFCCH(state, state->prevFrame[burst_time.TN()]->getVector());
 | 
			
		||||
 | 
			
		||||
    if (decodeSCH(bits, &sch_time)) {
 | 
			
		||||
      if (state->mode == TRX_MODE_MS_ACQUIRE) {
 | 
			
		||||
          diff_time = GSM::Time(sch_time.FN() - burst_time.FN(),
 | 
			
		||||
                                -burst_time.TN());
 | 
			
		||||
          mRadioInterface->adjustClock(diff_time);
 | 
			
		||||
          mTransmitDeadlineClock = RadioClock::adjust(
 | 
			
		||||
					mTransmitDeadlineClock,
 | 
			
		||||
					diff_time);
 | 
			
		||||
          state->mode = TRX_MODE_MS_TRACK;
 | 
			
		||||
 | 
			
		||||
          std::cout << "SCH : Locking GSM clock " << std::endl;
 | 
			
		||||
      } else {
 | 
			
		||||
        std::cout << "SCH : Read SCH at FN " << burst_time.FN()
 | 
			
		||||
                  << " FN51 " << burst_time.FN() % 51 << std::endl;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    goto release;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  wTime = burst_time;
 | 
			
		||||
  RSSI = (int) floor(20.0 * log10(rxFullScale / avg));
 | 
			
		||||
  timingOffset = (int) round(toa * 256.0 / mSPSRx);
 | 
			
		||||
 | 
			
		||||
  delete radio_burst;
 | 
			
		||||
  delete state->prevFrame[burst_time.TN()];
 | 
			
		||||
  state->prevFrame[burst_time.TN()] = radio_burst;
 | 
			
		||||
 | 
			
		||||
  return bits;
 | 
			
		||||
 | 
			
		||||
release:
 | 
			
		||||
  delete state->prevFrame[burst_time.TN()];
 | 
			
		||||
  state->prevFrame[burst_time.TN()] = radio_burst;
 | 
			
		||||
  delete bits;
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Transceiver::start()
 | 
			
		||||
@@ -589,10 +771,8 @@ void Transceiver::driveControl(size_t chan)
 | 
			
		||||
        mRadioInterface->start();
 | 
			
		||||
 | 
			
		||||
        // Start radio interface threads.
 | 
			
		||||
        mTxLowerLoopThread->start((void * (*)(void*))
 | 
			
		||||
                                  TxLowerLoopAdapter,(void*) this);
 | 
			
		||||
        mRxLowerLoopThread->start((void * (*)(void*))
 | 
			
		||||
                                  RxLowerLoopAdapter,(void*) this);
 | 
			
		||||
        mLowerLoopThread->start((void * (*)(void*))
 | 
			
		||||
                                LowerLoopAdapter,(void*) this);
 | 
			
		||||
 | 
			
		||||
        for (size_t i = 0; i < mChans; i++) {
 | 
			
		||||
          TransceiverChannel *chan = new TransceiverChannel(this, i);
 | 
			
		||||
@@ -695,6 +875,12 @@ void Transceiver::driveControl(size_t chan)
 | 
			
		||||
      sprintf(response,"RSP SETTSC 0 %d", TSC);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  else if (!strcmp(command,"GETBSIC")) {
 | 
			
		||||
    if (mBSIC < 0)
 | 
			
		||||
      sprintf(response, "RSP GETBSIC 1");
 | 
			
		||||
    else
 | 
			
		||||
      sprintf(response, "RSP GETBSIC 0 %d", mBSIC);
 | 
			
		||||
  }
 | 
			
		||||
  else if (strcmp(command,"SETSLOT")==0) {
 | 
			
		||||
    // set TSC 
 | 
			
		||||
    int  corrCode;
 | 
			
		||||
@@ -708,7 +894,21 @@ void Transceiver::driveControl(size_t chan)
 | 
			
		||||
    mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
 | 
			
		||||
    setModulus(timeslot, chan);
 | 
			
		||||
    sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
  else if (!strcmp(command,"SETRXMASK")) {
 | 
			
		||||
    int slot;
 | 
			
		||||
    unsigned long long mask;
 | 
			
		||||
    sscanf(buffer,"%3s %s %d 0x%llx", cmdcheck, command, &slot, &mask);
 | 
			
		||||
    if ((slot < 0) || (slot > 7)) {
 | 
			
		||||
      sprintf(response, "RSP SETRXMASK 1");
 | 
			
		||||
    } else {
 | 
			
		||||
      mRxSlotMask[slot] = mask;
 | 
			
		||||
      sprintf(response, "RSP SETRXMASK 0 %d 0x%llx", slot, mask);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  else if (!strcmp(command, "SYNC")) {
 | 
			
		||||
    mStates[0].mode = TRX_MODE_MS_ACQUIRE;
 | 
			
		||||
    sprintf(response,"RSP SYNC 0");
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    LOG(WARNING) << "bogus command " << command << " on control interface.";
 | 
			
		||||
@@ -734,15 +934,6 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
 | 
			
		||||
  for (int i = 0; i < 4; i++)
 | 
			
		||||
    frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
 | 
			
		||||
 | 
			
		||||
  // periodically update GSM core clock
 | 
			
		||||
  LOG(DEBUG) << "mTransmitDeadlineClock " << mTransmitDeadlineClock
 | 
			
		||||
		<< " mLastClockUpdateTime " << mLastClockUpdateTime;
 | 
			
		||||
 | 
			
		||||
  if (!chan) {
 | 
			
		||||
    if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
 | 
			
		||||
      writeClockInterface();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
 | 
			
		||||
  
 | 
			
		||||
  int RSSI = (int) buffer[5];
 | 
			
		||||
@@ -848,7 +1039,13 @@ void Transceiver::driveTxFIFO()
 | 
			
		||||
      }
 | 
			
		||||
      // time to push burst to transmit FIFO
 | 
			
		||||
      pushRadioVector(mTransmitDeadlineClock);
 | 
			
		||||
 | 
			
		||||
      mTransmitDeadlineClock.incTN();
 | 
			
		||||
 | 
			
		||||
      if (!mTransmitDeadlineClock.TN() &&
 | 
			
		||||
          !(mTransmitDeadlineClock.FN() % CLK_IND_INTERVAL)) {
 | 
			
		||||
        writeClockInterface();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -887,22 +1084,12 @@ void *RxUpperLoopAdapter(TransceiverChannel *chan)
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *RxLowerLoopAdapter(Transceiver *transceiver)
 | 
			
		||||
void *LowerLoopAdapter(Transceiver *transceiver)
 | 
			
		||||
{
 | 
			
		||||
  transceiver->setPriority(0.45);
 | 
			
		||||
 | 
			
		||||
  while (1) {
 | 
			
		||||
    transceiver->driveReceiveRadio();
 | 
			
		||||
    pthread_testcancel();
 | 
			
		||||
  }
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *TxLowerLoopAdapter(Transceiver *transceiver)
 | 
			
		||||
{
 | 
			
		||||
  transceiver->setPriority(0.44);
 | 
			
		||||
 | 
			
		||||
  while (1) {
 | 
			
		||||
    transceiver->driveTxFIFO();
 | 
			
		||||
    pthread_testcancel();
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -80,7 +80,14 @@ struct TransceiverState {
 | 
			
		||||
 | 
			
		||||
  /* Received noise energy levels */
 | 
			
		||||
  float mNoiseLev;
 | 
			
		||||
  noiseVector mNoises;
 | 
			
		||||
  avgVector mNoises;
 | 
			
		||||
  avgVector mFreqOffsets;
 | 
			
		||||
 | 
			
		||||
  /* Store pointers to previous frame */
 | 
			
		||||
  radioVector *prevFrame[8];
 | 
			
		||||
 | 
			
		||||
  /* Transceiver mode */
 | 
			
		||||
  int mode;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** The Transceiver class, responsible for physical layer of basestation */
 | 
			
		||||
@@ -99,8 +106,7 @@ private:
 | 
			
		||||
  std::vector<VectorFIFO *>  mReceiveFIFO;      ///< radioInterface FIFO of receive bursts
 | 
			
		||||
 | 
			
		||||
  std::vector<Thread *> mRxServiceLoopThreads;  ///< thread to pull bursts into receive FIFO
 | 
			
		||||
  Thread *mRxLowerLoopThread;                   ///< thread to pull bursts into receive FIFO
 | 
			
		||||
  Thread *mTxLowerLoopThread;                   ///< thread to push bursts into transmit FIFO
 | 
			
		||||
  Thread *mLowerLoopThread;                   ///< thread to pull bursts into receive FIFO
 | 
			
		||||
  std::vector<Thread *> mControlServiceLoopThreads;         ///< thread to process control messages from GSM core
 | 
			
		||||
  std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
 | 
			
		||||
 | 
			
		||||
@@ -116,6 +122,7 @@ private:
 | 
			
		||||
    OFF,               ///< timeslot is off
 | 
			
		||||
    TSC,	       ///< timeslot should contain a normal burst
 | 
			
		||||
    RACH,	       ///< timeslot should contain an access burst
 | 
			
		||||
    SCH,	       ///< timeslot should contain a SCH burst
 | 
			
		||||
    IDLE	       ///< timeslot is an idle (or dummy) burst
 | 
			
		||||
  } CorrType;
 | 
			
		||||
 | 
			
		||||
@@ -147,6 +154,13 @@ private:
 | 
			
		||||
                  signalVector &burst,
 | 
			
		||||
                  complex &, float &toa);
 | 
			
		||||
 | 
			
		||||
  bool detectSCH(TransceiverState *state,
 | 
			
		||||
                  signalVector &burst,
 | 
			
		||||
                  complex &, float &toa);
 | 
			
		||||
 | 
			
		||||
  bool decodeSCH(SoftVector *burst, GSM::Time *time);
 | 
			
		||||
  bool correctFCCH(TransceiverState *state, signalVector *burst);
 | 
			
		||||
 | 
			
		||||
  /** Detect normal bursts */
 | 
			
		||||
  bool detectTSC(TransceiverState *state,
 | 
			
		||||
                 signalVector &burst,
 | 
			
		||||
@@ -167,7 +181,9 @@ private:
 | 
			
		||||
  double mRxFreq;                      ///< the receive frequency
 | 
			
		||||
  int mPower;                          ///< the transmit power in dB
 | 
			
		||||
  unsigned mTSC;                       ///< the midamble sequence code
 | 
			
		||||
  unsigned mMaxExpectedDelay;            ///< maximum expected time-of-arrival offset in GSM symbols
 | 
			
		||||
  unsigned mMaxExpectedDelay;          ///< maximum TOA offset in GSM symbols
 | 
			
		||||
  unsigned long long mRxSlotMask[8];   ///< MS - enabled multiframe slot mask
 | 
			
		||||
  int mBSIC;                           ///< MS - detected BSIC
 | 
			
		||||
 | 
			
		||||
  std::vector<TransceiverState> mStates;
 | 
			
		||||
 | 
			
		||||
@@ -226,6 +242,13 @@ public:
 | 
			
		||||
    LOOPBACK            ///< similar go VII, used in loopback testing
 | 
			
		||||
  } ChannelCombination;
 | 
			
		||||
 | 
			
		||||
  enum {
 | 
			
		||||
    TRX_MODE_OFF,
 | 
			
		||||
    TRX_MODE_BTS,
 | 
			
		||||
    TRX_MODE_MS_ACQUIRE,
 | 
			
		||||
    TRX_MODE_MS_TRACK,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
  /** drive lower receive I/O and burst generation */
 | 
			
		||||
  void driveReceiveRadio();
 | 
			
		||||
@@ -249,9 +272,7 @@ protected:
 | 
			
		||||
 | 
			
		||||
  friend void *TxUpperLoopAdapter(TransceiverChannel *);
 | 
			
		||||
 | 
			
		||||
  friend void *RxLowerLoopAdapter(Transceiver *);
 | 
			
		||||
 | 
			
		||||
  friend void *TxLowerLoopAdapter(Transceiver *);
 | 
			
		||||
  friend void *LowerLoopAdapter(Transceiver *);
 | 
			
		||||
 | 
			
		||||
  friend void *ControlServiceLoopAdapter(TransceiverChannel *);
 | 
			
		||||
 | 
			
		||||
@@ -266,8 +287,7 @@ protected:
 | 
			
		||||
void *RxUpperLoopAdapter(TransceiverChannel *);
 | 
			
		||||
 | 
			
		||||
/** Main drive threads */
 | 
			
		||||
void *RxLowerLoopAdapter(Transceiver *);
 | 
			
		||||
void *TxLowerLoopAdapter(Transceiver *);
 | 
			
		||||
void *LowerLoopAdapter(Transceiver *);
 | 
			
		||||
 | 
			
		||||
/** control message handler thread loop */
 | 
			
		||||
void *ControlServiceLoopAdapter(TransceiverChannel *);
 | 
			
		||||
 
 | 
			
		||||
@@ -58,6 +58,11 @@ struct uhd_dev_offset {
 | 
			
		||||
	const std::string desc;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct tune_result {
 | 
			
		||||
	uhd::tune_result_t uhd;
 | 
			
		||||
	double freq;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Tx / Rx sample offset values. In a perfect world, there is no group delay
 | 
			
		||||
 * though analog components, and behaviour through digital filters exactly
 | 
			
		||||
@@ -282,6 +287,7 @@ public:
 | 
			
		||||
 | 
			
		||||
	bool setTxFreq(double wFreq, size_t chan);
 | 
			
		||||
	bool setRxFreq(double wFreq, size_t chan);
 | 
			
		||||
	bool setRxOffset(double wOffset, size_t chan);
 | 
			
		||||
 | 
			
		||||
	inline TIMESTAMP initialWriteTimestamp() { return ts_initial * sps; }
 | 
			
		||||
	inline TIMESTAMP initialReadTimestamp() { return ts_initial; }
 | 
			
		||||
@@ -332,7 +338,7 @@ private:
 | 
			
		||||
	double offset;
 | 
			
		||||
 | 
			
		||||
	std::vector<double> tx_gains, rx_gains;
 | 
			
		||||
	std::vector<double> tx_freqs, rx_freqs;
 | 
			
		||||
	std::vector<tune_result> tx_freqs, rx_freqs;
 | 
			
		||||
	size_t tx_spp, rx_spp;
 | 
			
		||||
 | 
			
		||||
	bool started;
 | 
			
		||||
@@ -886,7 +892,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOG(ERR) << rx_buffers[0]->str_code(rc);
 | 
			
		||||
		LOG(ERR) << rx_buffers[0]->str_status();
 | 
			
		||||
		return 0;
 | 
			
		||||
		return len;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create vector buffer
 | 
			
		||||
@@ -996,15 +1002,17 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
 | 
			
		||||
	return num_smpls;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool uhd_device::updateAlignment(TIMESTAMP timestamp)
 | 
			
		||||
bool uhd_device::updateAlignment(TIMESTAMP)
 | 
			
		||||
{
 | 
			
		||||
	aligned = false;
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
 | 
			
		||||
{
 | 
			
		||||
	double rf_spread, rf_freq;
 | 
			
		||||
	std::vector<double> freqs;
 | 
			
		||||
	std::vector<tune_result> freqs;
 | 
			
		||||
	uhd::tune_request_t treq(freq);
 | 
			
		||||
 | 
			
		||||
	if ((chans == 1) || ((chans == 2) && dev_type == UMTRX)) {
 | 
			
		||||
@@ -1023,17 +1031,17 @@ uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
 | 
			
		||||
		freqs = rx_freqs;
 | 
			
		||||
 | 
			
		||||
	/* Tune directly if other channel isn't tuned */
 | 
			
		||||
	if (freqs[!chan] < 10.0)
 | 
			
		||||
	if (freqs[!chan].freq < 10.0)
 | 
			
		||||
		return treq;
 | 
			
		||||
 | 
			
		||||
	/* Find center frequency between channels */
 | 
			
		||||
	rf_spread = fabs(freqs[!chan] - freq);
 | 
			
		||||
	rf_spread = fabs(freqs[!chan].freq - freq);
 | 
			
		||||
	if (rf_spread > B2XX_CLK_RT) {
 | 
			
		||||
		LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
 | 
			
		||||
		return treq;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rf_freq = (freqs[!chan] + freq) / 2.0f;
 | 
			
		||||
	rf_freq = (freqs[!chan].freq + freq) / 2.0f;
 | 
			
		||||
 | 
			
		||||
	treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
 | 
			
		||||
	treq.target_freq = freq;
 | 
			
		||||
@@ -1050,10 +1058,12 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
 | 
			
		||||
 | 
			
		||||
	if (tx) {
 | 
			
		||||
		tres = usrp_dev->set_tx_freq(treq, chan);
 | 
			
		||||
		tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
 | 
			
		||||
		tx_freqs[chan].uhd = tres;
 | 
			
		||||
		tx_freqs[chan].freq = usrp_dev->get_tx_freq(chan);
 | 
			
		||||
	} else {
 | 
			
		||||
		tres = usrp_dev->set_rx_freq(treq, chan);
 | 
			
		||||
		rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
 | 
			
		||||
		rx_freqs[chan].uhd = tres;
 | 
			
		||||
		rx_freqs[chan].freq = usrp_dev->get_rx_freq(chan);
 | 
			
		||||
	}
 | 
			
		||||
	LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
 | 
			
		||||
 | 
			
		||||
@@ -1066,13 +1076,15 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
 | 
			
		||||
	 */
 | 
			
		||||
	if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
 | 
			
		||||
		if (tx) {
 | 
			
		||||
			treq = select_freq(tx_freqs[!chan], !chan, true);
 | 
			
		||||
			treq = select_freq(tx_freqs[!chan].freq, !chan, true);
 | 
			
		||||
			tres = usrp_dev->set_tx_freq(treq, !chan);
 | 
			
		||||
			tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
 | 
			
		||||
			tx_freqs[!chan].uhd = tres;
 | 
			
		||||
			tx_freqs[!chan].freq = usrp_dev->get_tx_freq(!chan);
 | 
			
		||||
		} else {
 | 
			
		||||
			treq = select_freq(rx_freqs[!chan], !chan, false);
 | 
			
		||||
			treq = select_freq(rx_freqs[!chan].freq, !chan, false);
 | 
			
		||||
			tres = usrp_dev->set_rx_freq(treq, !chan);
 | 
			
		||||
			rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
 | 
			
		||||
			rx_freqs[!chan].uhd = tres;
 | 
			
		||||
			rx_freqs[!chan].freq = usrp_dev->get_rx_freq(!chan);
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
		LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
 | 
			
		||||
@@ -1091,6 +1103,20 @@ bool uhd_device::setTxFreq(double wFreq, size_t chan)
 | 
			
		||||
	return set_freq(wFreq, chan, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool uhd_device::setRxOffset(double wOffset, size_t chan)
 | 
			
		||||
{
 | 
			
		||||
	uhd::tune_result_t tres;
 | 
			
		||||
	uhd::tune_request_t treq(rx_freqs[chan].freq - wOffset);
 | 
			
		||||
 | 
			
		||||
	treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
 | 
			
		||||
	treq.rf_freq = rx_freqs[chan].uhd.actual_rf_freq;
 | 
			
		||||
 | 
			
		||||
	tres = usrp_dev->set_rx_freq(treq, chan);
 | 
			
		||||
	rx_freqs[chan].freq = usrp_dev->get_rx_freq(chan);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool uhd_device::setRxFreq(double wFreq, size_t chan)
 | 
			
		||||
{
 | 
			
		||||
	if (chan >= rx_freqs.size()) {
 | 
			
		||||
@@ -1108,7 +1134,7 @@ double uhd_device::getTxFreq(size_t chan)
 | 
			
		||||
		return 0.0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return tx_freqs[chan];
 | 
			
		||||
	return tx_freqs[chan].freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double uhd_device::getRxFreq(size_t chan)
 | 
			
		||||
@@ -1118,7 +1144,7 @@ double uhd_device::getRxFreq(size_t chan)
 | 
			
		||||
		return 0.0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rx_freqs[chan];
 | 
			
		||||
	return rx_freqs[chan].freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool uhd_device::recv_async_msg()
 | 
			
		||||
 
 | 
			
		||||
@@ -68,6 +68,7 @@ struct trx_config {
 | 
			
		||||
	bool extref;
 | 
			
		||||
	bool filler;
 | 
			
		||||
	bool diversity;
 | 
			
		||||
	bool ms;
 | 
			
		||||
	double offset;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -118,7 +119,7 @@ bool testConfig()
 | 
			
		||||
 */
 | 
			
		||||
bool trx_setup_config(struct trx_config *config)
 | 
			
		||||
{
 | 
			
		||||
	std::string refstr, fillstr, divstr;
 | 
			
		||||
	std::string refstr, fillstr, divstr, msstr;
 | 
			
		||||
 | 
			
		||||
	if (!testConfig())
 | 
			
		||||
		return false;
 | 
			
		||||
@@ -167,6 +168,7 @@ bool trx_setup_config(struct trx_config *config)
 | 
			
		||||
	refstr = config->extref ? "Enabled" : "Disabled";
 | 
			
		||||
	fillstr = config->filler ? "Enabled" : "Disabled";
 | 
			
		||||
	divstr = config->diversity ? "Enabled" : "Disabled";
 | 
			
		||||
	msstr = config->ms ? "Enabled" : "Disabled";
 | 
			
		||||
 | 
			
		||||
	std::ostringstream ost("");
 | 
			
		||||
	ost << "Config Settings" << std::endl;
 | 
			
		||||
@@ -179,6 +181,7 @@ bool trx_setup_config(struct trx_config *config)
 | 
			
		||||
	ost << "   External Reference...... " << refstr << std::endl;
 | 
			
		||||
	ost << "   C0 Filler Table......... " << fillstr << std::endl;
 | 
			
		||||
	ost << "   Diversity............... " << divstr << std::endl;
 | 
			
		||||
	ost << "   MS Mode................. " << msstr << std::endl;
 | 
			
		||||
	ost << "   Tuning offset........... " << config->offset << std::endl;
 | 
			
		||||
	std::cout << ost << std::endl;
 | 
			
		||||
 | 
			
		||||
@@ -196,10 +199,22 @@ RadioInterface *makeRadioInterface(struct trx_config *config,
 | 
			
		||||
                                   RadioDevice *usrp, int type)
 | 
			
		||||
{
 | 
			
		||||
	RadioInterface *radio = NULL;
 | 
			
		||||
	size_t div = 1;
 | 
			
		||||
	int offset = 3;
 | 
			
		||||
 | 
			
		||||
	if (config->ms) {
 | 
			
		||||
		if (type != RadioDevice::NORMAL) {
 | 
			
		||||
			LOG(ALERT) << "Unsupported configuration";
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		offset *= -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (type) {
 | 
			
		||||
	case RadioDevice::NORMAL:
 | 
			
		||||
		radio = new RadioInterface(usrp, config->sps, config->chans);
 | 
			
		||||
		radio = new RadioInterface(usrp, config->sps,
 | 
			
		||||
					   config->chans, div, offset);
 | 
			
		||||
		break;
 | 
			
		||||
	case RadioDevice::RESAMP_64M:
 | 
			
		||||
	case RadioDevice::RESAMP_100M:
 | 
			
		||||
@@ -286,6 +301,7 @@ static void print_help()
 | 
			
		||||
		"  -s    Samples-per-symbol (1 or 4)\n"
 | 
			
		||||
		"  -c    Number of ARFCN channels (default=1)\n"
 | 
			
		||||
		"  -f    Enable C0 filler table\n"
 | 
			
		||||
		"  -m    Enable MS mode\n"
 | 
			
		||||
		"  -o    Set baseband frequency offset (default=auto)\n",
 | 
			
		||||
		"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
 | 
			
		||||
}
 | 
			
		||||
@@ -300,9 +316,10 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
 | 
			
		||||
	config->extref = false;
 | 
			
		||||
	config->filler = false;
 | 
			
		||||
	config->diversity = false;
 | 
			
		||||
	config->ms = false;
 | 
			
		||||
	config->offset = 0.0;
 | 
			
		||||
 | 
			
		||||
	while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:")) != -1) {
 | 
			
		||||
	while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:ms:")) != -1) {
 | 
			
		||||
		switch (option) {
 | 
			
		||||
		case 'h':
 | 
			
		||||
			print_help();
 | 
			
		||||
@@ -335,6 +352,9 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
 | 
			
		||||
		case 'o':
 | 
			
		||||
			config->offset = atof(optarg);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'm':
 | 
			
		||||
			config->ms = true;
 | 
			
		||||
			break;
 | 
			
		||||
		case 's':
 | 
			
		||||
			config->sps = atoi(optarg);
 | 
			
		||||
			if ((config->sps != 1) && (config->sps != 4)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,40 @@ void RadioClock::set(const GSM::Time& wTime)
 | 
			
		||||
	mLock.unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GSM::Time RadioClock::adjust(GSM::Time &wBase, GSM::Time &wOffset)
 | 
			
		||||
{
 | 
			
		||||
	int tn_diff, fn_diff = 0;
 | 
			
		||||
 | 
			
		||||
	/* Modulo TN adustment */
 | 
			
		||||
	tn_diff = wBase.TN() + wOffset.TN();
 | 
			
		||||
	if (tn_diff < 0) {
 | 
			
		||||
		tn_diff += 8;
 | 
			
		||||
		fn_diff--;
 | 
			
		||||
	} else if (tn_diff >= 8) {
 | 
			
		||||
		tn_diff -= 8;
 | 
			
		||||
		fn_diff++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Modulo FN adjustment */
 | 
			
		||||
	fn_diff += wBase.FN() + wOffset.FN();
 | 
			
		||||
	if (fn_diff < 0)
 | 
			
		||||
		fn_diff += GSM::gHyperframe;
 | 
			
		||||
	else if ((unsigned) fn_diff >= GSM::gHyperframe)
 | 
			
		||||
		fn_diff = fn_diff - GSM::gHyperframe;
 | 
			
		||||
 | 
			
		||||
	return GSM::Time(fn_diff, tn_diff);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RadioClock::adjust(GSM::Time& wOffset)
 | 
			
		||||
{
 | 
			
		||||
	mLock.lock();
 | 
			
		||||
 | 
			
		||||
	mClock = adjust(mClock, wOffset); 
 | 
			
		||||
	updateSignal.signal();
 | 
			
		||||
 | 
			
		||||
	mLock.unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RadioClock::incTN()
 | 
			
		||||
{
 | 
			
		||||
	mLock.lock();
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,10 @@
 | 
			
		||||
 | 
			
		||||
class RadioClock {
 | 
			
		||||
public:
 | 
			
		||||
	static GSM::Time adjust(GSM::Time &base, GSM::Time &offset);
 | 
			
		||||
 | 
			
		||||
	void set(const GSM::Time& wTime);
 | 
			
		||||
	void adjust(GSM::Time &wOffset);
 | 
			
		||||
	void incTN();
 | 
			
		||||
	GSM::Time get();
 | 
			
		||||
	void wait();
 | 
			
		||||
 
 | 
			
		||||
@@ -91,6 +91,9 @@ class RadioDevice {
 | 
			
		||||
  /** Set the receiver frequency */
 | 
			
		||||
  virtual bool setRxFreq(double wFreq, size_t chan = 0) = 0;
 | 
			
		||||
 | 
			
		||||
  /** Adjust the receiver offset */
 | 
			
		||||
  virtual bool setRxOffset(double wOffset, size_t chan = 0) = 0;
 | 
			
		||||
 | 
			
		||||
  /** Returns the starting write Timestamp*/
 | 
			
		||||
  virtual TIMESTAMP initialWriteTimestamp(void)=0;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,8 @@ RadioInterface::RadioInterface(RadioDevice *wRadio,
 | 
			
		||||
                               int wReceiveOffset, GSM::Time wStartTime)
 | 
			
		||||
  : mRadio(wRadio), mSPSTx(sps), mSPSRx(1), mChans(chans), mMIMO(diversity),
 | 
			
		||||
    sendCursor(0), recvCursor(0), underrun(false), overrun(false),
 | 
			
		||||
    receiveOffset(wReceiveOffset), mOn(false)
 | 
			
		||||
    receiveOffset(wReceiveOffset), shiftOffset(0), shiftUpdate(false),
 | 
			
		||||
    mOn(false)
 | 
			
		||||
{
 | 
			
		||||
  mClock.set(wStartTime);
 | 
			
		||||
}
 | 
			
		||||
@@ -157,6 +158,11 @@ int RadioInterface::unRadioifyVector(float *floatVector,
 | 
			
		||||
  return newVector.size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RadioInterface::adjustClock(GSM::Time &offset)
 | 
			
		||||
{
 | 
			
		||||
  mClock.adjust(offset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool RadioInterface::tuneTx(double freq, size_t chan)
 | 
			
		||||
{
 | 
			
		||||
  return mRadio->setTxFreq(freq, chan);
 | 
			
		||||
@@ -167,6 +173,10 @@ bool RadioInterface::tuneRx(double freq, size_t chan)
 | 
			
		||||
  return mRadio->setRxFreq(freq, chan);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool RadioInterface::tuneRxOffset(double offset, size_t chan)
 | 
			
		||||
{
 | 
			
		||||
  return mRadio->setRxOffset(offset, chan);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RadioInterface::start()
 | 
			
		||||
{
 | 
			
		||||
@@ -228,7 +238,11 @@ bool RadioInterface::driveReceiveRadio()
 | 
			
		||||
  pullBuffer();
 | 
			
		||||
 | 
			
		||||
  GSM::Time rcvClock = mClock.get();
 | 
			
		||||
  if (receiveOffset < 0)
 | 
			
		||||
    rcvClock.incTN(-receiveOffset);
 | 
			
		||||
  else
 | 
			
		||||
    rcvClock.decTN(receiveOffset);
 | 
			
		||||
 | 
			
		||||
  unsigned tN = rcvClock.TN();
 | 
			
		||||
  int recvSz = recvCursor;
 | 
			
		||||
  int readSz = 0;
 | 
			
		||||
@@ -292,6 +306,12 @@ bool RadioInterface::isUnderrun()
 | 
			
		||||
  return retVal;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RadioInterface::applyOffset(int offset)
 | 
			
		||||
{
 | 
			
		||||
  shiftOffset += offset;
 | 
			
		||||
  shiftUpdate = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VectorFIFO* RadioInterface::receiveFIFO(size_t chan)
 | 
			
		||||
{
 | 
			
		||||
  if (chan >= mReceiveFIFO.size())
 | 
			
		||||
@@ -330,7 +350,7 @@ void RadioInterface::pullBuffer()
 | 
			
		||||
  num_recv = mRadio->readSamples(convertRecvBuffer,
 | 
			
		||||
                                 CHUNK,
 | 
			
		||||
                                 &overrun,
 | 
			
		||||
                                 readTimestamp,
 | 
			
		||||
                                 readTimestamp + shiftOffset,
 | 
			
		||||
                                 &local_underrun);
 | 
			
		||||
  if (num_recv != CHUNK) {
 | 
			
		||||
          LOG(ALERT) << "Receive error " << num_recv;
 | 
			
		||||
@@ -365,11 +385,16 @@ void RadioInterface::pushBuffer()
 | 
			
		||||
                        powerScaling[i], 2 * sendCursor);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (shiftUpdate) {
 | 
			
		||||
    mRadio->updateAlignment(0);
 | 
			
		||||
    shiftUpdate = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Send the all samples in the send buffer */ 
 | 
			
		||||
  num_sent = mRadio->writeSamples(convertSendBuffer,
 | 
			
		||||
                                  sendCursor,
 | 
			
		||||
                                  &underrun,
 | 
			
		||||
                                  writeTimestamp);
 | 
			
		||||
                                  writeTimestamp + mSPSTx * shiftOffset);
 | 
			
		||||
  writeTimestamp += num_sent;
 | 
			
		||||
  sendCursor = 0;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -56,7 +56,8 @@ protected:
 | 
			
		||||
  RadioClock mClock;                          ///< the basestation clock!
 | 
			
		||||
 | 
			
		||||
  int receiveOffset;                          ///< offset b/w transmit and receive GSM timestamps, in timeslots
 | 
			
		||||
 | 
			
		||||
  int shiftOffset;
 | 
			
		||||
  bool shiftUpdate;
 | 
			
		||||
  bool mOn;				      ///< indicates radio is on
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
@@ -94,6 +95,7 @@ public:
 | 
			
		||||
 | 
			
		||||
  /** check for underrun, resets underrun value */
 | 
			
		||||
  bool isUnderrun();
 | 
			
		||||
  void applyOffset(int offset);
 | 
			
		||||
 | 
			
		||||
  /** return the receive FIFO */
 | 
			
		||||
  VectorFIFO* receiveFIFO(size_t chan = 0);
 | 
			
		||||
@@ -101,12 +103,18 @@ public:
 | 
			
		||||
  /** return the basestation clock */
 | 
			
		||||
  RadioClock* getClock(void) { return &mClock;};
 | 
			
		||||
 | 
			
		||||
  /** apply an offset to the main clock */
 | 
			
		||||
  void adjustClock(GSM::Time &offset);
 | 
			
		||||
 | 
			
		||||
  /** set transmit frequency */
 | 
			
		||||
  bool tuneTx(double freq, size_t chan = 0);
 | 
			
		||||
 | 
			
		||||
  /** set receive frequency */
 | 
			
		||||
  virtual bool tuneRx(double freq, size_t chan = 0);
 | 
			
		||||
 | 
			
		||||
  /** set frequency correction */
 | 
			
		||||
  virtual bool tuneRxOffset(double offset, size_t chan = 0);
 | 
			
		||||
 | 
			
		||||
  /** set receive gain */
 | 
			
		||||
  double setRxGain(double dB, size_t chan = 0);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -189,7 +189,7 @@ void RadioInterfaceDiversity::pullBuffer()
 | 
			
		||||
	num = mRadio->readSamples(convertRecvBuffer,
 | 
			
		||||
				  resamp_outchunk,
 | 
			
		||||
				  &overrun,
 | 
			
		||||
				  readTimestamp,
 | 
			
		||||
				  readTimestamp + shiftOffset,
 | 
			
		||||
				  &local_underrun);
 | 
			
		||||
	if ((size_t) num != resamp_outchunk) {
 | 
			
		||||
		LOG(ALERT) << "Receive error " << num;
 | 
			
		||||
 
 | 
			
		||||
@@ -188,7 +188,7 @@ void RadioInterfaceResamp::pullBuffer()
 | 
			
		||||
	num_recv = mRadio->readSamples(convertRecvBuffer,
 | 
			
		||||
				       resamp_outchunk,
 | 
			
		||||
				       &overrun,
 | 
			
		||||
				       readTimestamp,
 | 
			
		||||
				       readTimestamp + shiftOffset,
 | 
			
		||||
				       &local_underrun);
 | 
			
		||||
	if (num_recv != (int) resamp_outchunk) {
 | 
			
		||||
		LOG(ALERT) << "Receive error " << num_recv;
 | 
			
		||||
 
 | 
			
		||||
@@ -74,25 +74,31 @@ bool radioVector::setVector(signalVector *vector, size_t chan)
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
noiseVector::noiseVector(size_t size)
 | 
			
		||||
	: std::vector<float>(size), itr(0)
 | 
			
		||||
avgVector::avgVector(size_t max)
 | 
			
		||||
	: std::vector<float>(0), itr(0)
 | 
			
		||||
{
 | 
			
		||||
	this->max = max;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float noiseVector::avg() const
 | 
			
		||||
float avgVector::avg() const
 | 
			
		||||
{
 | 
			
		||||
	float val = 0.0;
 | 
			
		||||
 | 
			
		||||
	if (!size())
 | 
			
		||||
		return 0.0f;
 | 
			
		||||
 | 
			
		||||
	for (size_t i = 0; i < size(); i++)
 | 
			
		||||
		val += (*this)[i];
 | 
			
		||||
 | 
			
		||||
	return val / (float) size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool noiseVector::insert(float val)
 | 
			
		||||
bool avgVector::insert(float val)
 | 
			
		||||
{
 | 
			
		||||
	if (!size())
 | 
			
		||||
		return false;
 | 
			
		||||
	if (size() < max) {
 | 
			
		||||
		push_back(val);
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (itr >= this->size())
 | 
			
		||||
		itr = 0;
 | 
			
		||||
@@ -102,6 +108,16 @@ bool noiseVector::insert(float val)
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool avgVector::full() const
 | 
			
		||||
{
 | 
			
		||||
	return size() >= max;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void avgVector::reset()
 | 
			
		||||
{
 | 
			
		||||
	resize(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GSM::Time VectorQueue::nextTime() const
 | 
			
		||||
{
 | 
			
		||||
	GSM::Time retVal;
 | 
			
		||||
 
 | 
			
		||||
@@ -46,14 +46,17 @@ private:
 | 
			
		||||
	GSM::Time mTime;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class noiseVector : std::vector<float> {
 | 
			
		||||
class avgVector : std::vector<float> {
 | 
			
		||||
public:
 | 
			
		||||
	noiseVector(size_t size = 0);
 | 
			
		||||
	avgVector(size_t size = 0);
 | 
			
		||||
	bool insert(float val);
 | 
			
		||||
	bool full() const;
 | 
			
		||||
	float avg() const;
 | 
			
		||||
	void reset();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	size_t itr;
 | 
			
		||||
	size_t max;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class VectorFIFO : public InterthreadQueue<radioVector> { };
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										219
									
								
								Transceiver52M/sch.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								Transceiver52M/sch.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,219 @@
 | 
			
		||||
#include <complex.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/bits.h>
 | 
			
		||||
#include <osmocom/core/conv.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/crcgen.h>
 | 
			
		||||
 | 
			
		||||
#include "sch.h"
 | 
			
		||||
 | 
			
		||||
/* GSM 04.08, 9.1.30 Synchronization channel information */
 | 
			
		||||
struct sch_packed_info {
 | 
			
		||||
	ubit_t t1_hi[2];
 | 
			
		||||
	ubit_t bsic[6];
 | 
			
		||||
	ubit_t t1_md[8];
 | 
			
		||||
	ubit_t t3p_hi[2];
 | 
			
		||||
	ubit_t t2[5];
 | 
			
		||||
	ubit_t t1_lo[1];
 | 
			
		||||
	ubit_t t3p_lo[1];
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
struct sch_burst {
 | 
			
		||||
	sbit_t tail0[3];
 | 
			
		||||
	sbit_t data0[39];
 | 
			
		||||
	sbit_t etsc[64];
 | 
			
		||||
	sbit_t data1[39];
 | 
			
		||||
	sbit_t tail1[3];
 | 
			
		||||
	sbit_t guard[8];
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
static const uint8_t sch_next_output[][2] = {
 | 
			
		||||
	{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
 | 
			
		||||
	{ 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
 | 
			
		||||
	{ 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
 | 
			
		||||
	{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const uint8_t sch_next_state[][2] = {
 | 
			
		||||
	{  0,  1 }, {  2,  3 }, {  4,  5 }, {  6,  7 },
 | 
			
		||||
	{  8,  9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
 | 
			
		||||
	{  0,  1 }, {  2,  3 }, {  4,  5 }, {  6,  7 },
 | 
			
		||||
	{  8,  9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct osmo_conv_code gsm_conv_sch = {
 | 
			
		||||
	.N = 2,
 | 
			
		||||
	.K = 5,
 | 
			
		||||
	.len = GSM_SCH_UNCODED_LEN,
 | 
			
		||||
	.next_output = sch_next_output,
 | 
			
		||||
	.next_state  = sch_next_state,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const struct osmo_crc16gen_code gsm0503_sch_crc10 = {
 | 
			
		||||
	.bits = 10,
 | 
			
		||||
	.poly = 0x175,
 | 
			
		||||
	.init = 0x000,
 | 
			
		||||
	.remainder = 0x3ff,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define GSM_MAX_BURST_LEN	157
 | 
			
		||||
#define GSM_SYM_RATE		(1625e3 / 6)
 | 
			
		||||
 | 
			
		||||
/* Pre-generated FCCH measurement tone */
 | 
			
		||||
static complex float fcch_ref[GSM_MAX_BURST_LEN];
 | 
			
		||||
 | 
			
		||||
int float_to_sbit(const float *in, sbit_t *out, float scale, int len)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < len; i++) {
 | 
			
		||||
		out[i] = (in[i] - 0.5f) * scale;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if FN contains a SCH burst */
 | 
			
		||||
int gsm_sch_check_fn(int fn)
 | 
			
		||||
{
 | 
			
		||||
	int fn51 = fn % 51;
 | 
			
		||||
 | 
			
		||||
	switch (fn51) {
 | 
			
		||||
	case 1:
 | 
			
		||||
	case 11:
 | 
			
		||||
	case 21:
 | 
			
		||||
	case 31:
 | 
			
		||||
	case 41:
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* SCH (T1, T2, T3p) to full FN value */
 | 
			
		||||
int gsm_sch_to_fn(struct sch_info *sch)
 | 
			
		||||
{
 | 
			
		||||
	int t1 = sch->t1;
 | 
			
		||||
	int t2 = sch->t2;
 | 
			
		||||
	int t3p = sch->t3p;
 | 
			
		||||
 | 
			
		||||
	if ((t1 < 0) || (t2 < 0) || (t3p < 0))
 | 
			
		||||
		return -1;
 | 
			
		||||
	int tt;
 | 
			
		||||
	int t3 = t3p * 10 + 1;
 | 
			
		||||
 | 
			
		||||
	if (t3 < t2)
 | 
			
		||||
		tt = (t3 + 26) - t2;
 | 
			
		||||
	else
 | 
			
		||||
		tt = (t3 - t2) % 26;
 | 
			
		||||
 | 
			
		||||
	return t1 * 51 * 26 + tt * 51 + t3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Parse encoded SCH message */
 | 
			
		||||
int gsm_sch_parse(const uint8_t *info, struct sch_info *desc)
 | 
			
		||||
{
 | 
			
		||||
	struct sch_packed_info *p = (struct sch_packed_info *) info;
 | 
			
		||||
 | 
			
		||||
	desc->bsic = (p->bsic[0] << 0) | (p->bsic[1] << 1) |
 | 
			
		||||
		     (p->bsic[2] << 2) | (p->bsic[3] << 3) |
 | 
			
		||||
		     (p->bsic[4] << 4);
 | 
			
		||||
 | 
			
		||||
	desc->t1 = (p->t1_lo[0] << 0) | (p->t1_md[0] << 1) |
 | 
			
		||||
		   (p->t1_md[1] << 2) | (p->t1_md[2] << 3) |
 | 
			
		||||
		   (p->t1_md[3] << 4) | (p->t1_md[4] << 5) |
 | 
			
		||||
		   (p->t1_md[5] << 6) | (p->t1_md[6] << 7) |
 | 
			
		||||
		   (p->t1_md[7] << 8) | (p->t1_hi[0] << 9) |
 | 
			
		||||
		   (p->t1_hi[1] << 10);
 | 
			
		||||
 | 
			
		||||
	desc->t2 = (p->t2[0] << 0) | (p->t2[1] << 1) |
 | 
			
		||||
		   (p->t2[2] << 2) | (p->t2[3] << 3) |
 | 
			
		||||
		   (p->t2[4] << 4);
 | 
			
		||||
 | 
			
		||||
	desc->t3p = (p->t3p_lo[0] << 0) | (p->t3p_hi[0] << 1) |
 | 
			
		||||
		    (p->t3p_hi[1] << 2);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* From osmo-bts */
 | 
			
		||||
int gsm_sch_decode(uint8_t *info, sbit_t *data)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	ubit_t uncoded[GSM_SCH_UNCODED_LEN];
 | 
			
		||||
 | 
			
		||||
	osmo_conv_decode(&gsm_conv_sch, data, uncoded);
 | 
			
		||||
 | 
			
		||||
	rc = osmo_crc16gen_check_bits(&gsm0503_sch_crc10,
 | 
			
		||||
				      uncoded, GSM_SCH_INFO_LEN,
 | 
			
		||||
				      uncoded + GSM_SCH_INFO_LEN);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	memcpy(info, uncoded, GSM_SCH_INFO_LEN * sizeof(ubit_t));
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define FCCH_TAIL_BITS_LEN	3
 | 
			
		||||
#define FCCH_DATA_LEN		142
 | 
			
		||||
 | 
			
		||||
/* Compute FCCH frequency offset */
 | 
			
		||||
double gsm_fcch_offset(float *burst, int len)
 | 
			
		||||
{
 | 
			
		||||
	int i, start, end;
 | 
			
		||||
	float a, b, c, d, ang, avg = 0.0f;
 | 
			
		||||
	double freq;
 | 
			
		||||
 | 
			
		||||
	if (len > GSM_MAX_BURST_LEN)
 | 
			
		||||
		len = GSM_MAX_BURST_LEN;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < len; i++) {
 | 
			
		||||
		a = burst[2 * i + 0];
 | 
			
		||||
		b = burst[2 * i + 1];
 | 
			
		||||
		c = crealf(fcch_ref[i]);
 | 
			
		||||
		d = cimagf(fcch_ref[i]);
 | 
			
		||||
 | 
			
		||||
		burst[2 * i + 0] = a * c - b * d;
 | 
			
		||||
		burst[2 * i + 1] = a * d + b * c;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	start = FCCH_TAIL_BITS_LEN;
 | 
			
		||||
	end = start + FCCH_DATA_LEN;
 | 
			
		||||
 | 
			
		||||
	for (i = start; i < end; i++) {
 | 
			
		||||
		a = cargf(burst[2 * (i - 1) + 0] +
 | 
			
		||||
			  burst[2 * (i - 1) + 1] * I);
 | 
			
		||||
		b = cargf(burst[2 * i + 0] +
 | 
			
		||||
			  burst[2 * i + 1] * I);
 | 
			
		||||
 | 
			
		||||
		ang = b - a;
 | 
			
		||||
 | 
			
		||||
		if (ang > M_PI)
 | 
			
		||||
			ang -= 2 * M_PI;
 | 
			
		||||
		else if (ang < -M_PI)
 | 
			
		||||
			ang += 2 * M_PI;
 | 
			
		||||
 | 
			
		||||
		avg += ang;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	avg /= (float) (end - start);
 | 
			
		||||
	freq = avg / (2 * M_PI) * GSM_SYM_RATE;
 | 
			
		||||
 | 
			
		||||
	return freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Generate FCCH measurement tone */
 | 
			
		||||
static __attribute__((constructor)) void init()
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	double freq = 0.25;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < GSM_MAX_BURST_LEN; i++) {
 | 
			
		||||
		fcch_ref[i] = sin(2 * M_PI * freq * (double) i) +
 | 
			
		||||
			      cos(2 * M_PI * freq * (double) i) * I;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								Transceiver52M/sch.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								Transceiver52M/sch.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
#ifndef _SCH_H_
 | 
			
		||||
#define _SCH_H_
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/bits.h>
 | 
			
		||||
 | 
			
		||||
struct sch_info  {
 | 
			
		||||
	int bsic;
 | 
			
		||||
	int t1;
 | 
			
		||||
	int t2;
 | 
			
		||||
	int t3p;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define GSM_SCH_INFO_LEN		25
 | 
			
		||||
#define GSM_SCH_UNCODED_LEN		35
 | 
			
		||||
#define GSM_SCH_CODED_LEN		78
 | 
			
		||||
 | 
			
		||||
int gsm_sch_decode(uint8_t *sb_info, sbit_t *burst);
 | 
			
		||||
int gsm_sch_parse(const uint8_t *sb_info, struct sch_info *desc);
 | 
			
		||||
int gsm_sch_to_fn(struct sch_info *sch);
 | 
			
		||||
int gsm_sch_check_fn(int fn);
 | 
			
		||||
 | 
			
		||||
double gsm_fcch_offset(float *burst, int len);
 | 
			
		||||
 | 
			
		||||
int float_to_sbit(const float *in, sbit_t *out, float scale, int len);
 | 
			
		||||
 | 
			
		||||
#endif /* _SCH_H_ */
 | 
			
		||||
@@ -78,6 +78,7 @@ struct CorrelationSequence {
 | 
			
		||||
 | 
			
		||||
  signalVector *sequence;
 | 
			
		||||
  void         *buffer;
 | 
			
		||||
  void         *history;
 | 
			
		||||
  float        toa;
 | 
			
		||||
  complex      gain;
 | 
			
		||||
};
 | 
			
		||||
@@ -111,6 +112,7 @@ struct PulseSequence {
 | 
			
		||||
 | 
			
		||||
CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
 | 
			
		||||
CorrelationSequence *gRACHSequence = NULL;
 | 
			
		||||
CorrelationSequence *gSCHSequence = NULL;
 | 
			
		||||
PulseSequence *GSMPulse = NULL;
 | 
			
		||||
PulseSequence *GSMPulse1 = NULL;
 | 
			
		||||
 | 
			
		||||
@@ -131,6 +133,7 @@ void sigProcLibDestroy()
 | 
			
		||||
  delete GMSKRotation1;
 | 
			
		||||
  delete GMSKReverseRotation1;
 | 
			
		||||
  delete gRACHSequence;
 | 
			
		||||
  delete gSCHSequence;
 | 
			
		||||
  delete GSMPulse;
 | 
			
		||||
  delete GSMPulse1;
 | 
			
		||||
 | 
			
		||||
@@ -139,6 +142,7 @@ void sigProcLibDestroy()
 | 
			
		||||
  GMSKReverseRotationN = NULL;
 | 
			
		||||
  GMSKReverseRotation1 = NULL;
 | 
			
		||||
  gRACHSequence = NULL;
 | 
			
		||||
  gSCHSequence = NULL;
 | 
			
		||||
  GSMPulse = NULL;
 | 
			
		||||
  GSMPulse1 = NULL;
 | 
			
		||||
}
 | 
			
		||||
@@ -395,9 +399,11 @@ signalVector *convolve(const signalVector *x,
 | 
			
		||||
    break;
 | 
			
		||||
  case CUSTOM:
 | 
			
		||||
    if (start < h->size() - 1) {
 | 
			
		||||
      if (x->getStart() < h->size() - 1) {
 | 
			
		||||
        head = h->size() - start;
 | 
			
		||||
        append = true;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (start + len > x->size()) {
 | 
			
		||||
      tail = start + len - x->size();
 | 
			
		||||
      append = true;
 | 
			
		||||
@@ -1274,6 +1280,69 @@ release:
 | 
			
		||||
  return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool generateSCHSequence(int sps)
 | 
			
		||||
{
 | 
			
		||||
  bool status = true;
 | 
			
		||||
  float toa;
 | 
			
		||||
  complex *data = NULL;
 | 
			
		||||
  signalVector *autocorr = NULL;
 | 
			
		||||
  signalVector *seq0 = NULL, *seq1 = NULL, *_seq1 = NULL;
 | 
			
		||||
 | 
			
		||||
  delete gSCHSequence;
 | 
			
		||||
 | 
			
		||||
  seq0 = modulateBurst(gSCHSynchSequence, 0, sps, false);
 | 
			
		||||
  if (!seq0)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  seq1 = modulateBurst(gSCHSynchSequence, 0, sps, true);
 | 
			
		||||
  if (!seq1) {
 | 
			
		||||
    status = false;
 | 
			
		||||
    goto release;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  conjugateVector(*seq1);
 | 
			
		||||
 | 
			
		||||
  /* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */
 | 
			
		||||
  data = (complex *) convolve_h_alloc(seq1->size());
 | 
			
		||||
  _seq1 = new signalVector(data, 0, seq1->size());
 | 
			
		||||
  _seq1->setAligned(true);
 | 
			
		||||
  memcpy(_seq1->begin(), seq1->begin(), seq1->size() * sizeof(complex));
 | 
			
		||||
 | 
			
		||||
  autocorr = convolve(seq0, _seq1, autocorr, NO_DELAY);
 | 
			
		||||
  if (!autocorr) {
 | 
			
		||||
    status = false;
 | 
			
		||||
    goto release;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  gSCHSequence = new CorrelationSequence;
 | 
			
		||||
  gSCHSequence->sequence = _seq1;
 | 
			
		||||
  gSCHSequence->buffer = data;
 | 
			
		||||
  gSCHSequence->gain = peakDetect(*autocorr, &toa, NULL);
 | 
			
		||||
  gSCHSequence->history = new complex[_seq1->size()];
 | 
			
		||||
 | 
			
		||||
  /* For 1 sps only
 | 
			
		||||
   *     (Half of correlation length - 1) + midpoint of pulse shaping filer
 | 
			
		||||
   *     20.5 = (64 / 2 - 1) + 1.5
 | 
			
		||||
   */
 | 
			
		||||
  if (sps == 1)
 | 
			
		||||
    gSCHSequence->toa = toa - 32.5;
 | 
			
		||||
  else
 | 
			
		||||
    gSCHSequence->toa = 0.0;
 | 
			
		||||
 | 
			
		||||
release:
 | 
			
		||||
  delete autocorr;
 | 
			
		||||
  delete seq0;
 | 
			
		||||
  delete seq1;
 | 
			
		||||
 | 
			
		||||
  if (!status) {
 | 
			
		||||
    delete _seq1;
 | 
			
		||||
    free(data);
 | 
			
		||||
    gSCHSequence = NULL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static float computePeakRatio(signalVector *corr,
 | 
			
		||||
                              int sps, float toa, complex amp)
 | 
			
		||||
{
 | 
			
		||||
@@ -1424,6 +1493,71 @@ int detectRACHBurst(signalVector &rxBurst,
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int detectSCHBurst(signalVector &burst,
 | 
			
		||||
		    float thresh,
 | 
			
		||||
		    int sps,
 | 
			
		||||
		    complex *amp,
 | 
			
		||||
		    float *toa, int state)
 | 
			
		||||
{
 | 
			
		||||
  int rc, start, target, head, tail, len;
 | 
			
		||||
  float _toa;
 | 
			
		||||
  complex _amp;
 | 
			
		||||
  signalVector *corr, *_burst;
 | 
			
		||||
  CorrelationSequence *sync;
 | 
			
		||||
 | 
			
		||||
  if ((sps != 1) && (sps != 4))
 | 
			
		||||
    return -1;
 | 
			
		||||
 | 
			
		||||
  target = 3 + 39 + 64;
 | 
			
		||||
 | 
			
		||||
  switch (state) {
 | 
			
		||||
  case SCH_DETECT_NARROW:
 | 
			
		||||
    head = 4;
 | 
			
		||||
    tail = 4;
 | 
			
		||||
    break;
 | 
			
		||||
  case SCH_DETECT_FULL:
 | 
			
		||||
  default:
 | 
			
		||||
    head = target - 1;
 | 
			
		||||
    tail = 39 + 3 + 9;
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  start = (target - head) * sps - 1;
 | 
			
		||||
  len = (head + tail) * sps;
 | 
			
		||||
  sync = gSCHSequence;
 | 
			
		||||
  corr = new signalVector(len);
 | 
			
		||||
 | 
			
		||||
  _burst = new signalVector(burst, sync->sequence->size(), 5);
 | 
			
		||||
 | 
			
		||||
  memcpy(_burst->begin() - sync->sequence->size(), sync->history,
 | 
			
		||||
         sync->sequence->size() * sizeof(complex));
 | 
			
		||||
 | 
			
		||||
  memcpy(sync->history, &burst.begin()[burst.size() - sync->sequence->size()],
 | 
			
		||||
         sync->sequence->size() * sizeof(complex));
 | 
			
		||||
 | 
			
		||||
  rc = detectBurst(*_burst, *corr, sync,
 | 
			
		||||
                   thresh, sps, &_amp, &_toa, start, len);
 | 
			
		||||
  delete corr;
 | 
			
		||||
 | 
			
		||||
  if (rc < 0) {
 | 
			
		||||
    return -1;
 | 
			
		||||
  } else if (!rc) {
 | 
			
		||||
    if (amp)
 | 
			
		||||
      *amp = 0.0f;
 | 
			
		||||
    if (toa)
 | 
			
		||||
      *toa = 0.0f;
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Subtract forward search bits from delay */
 | 
			
		||||
  if (toa)
 | 
			
		||||
    *toa = _toa - head * sps;
 | 
			
		||||
  if (amp)
 | 
			
		||||
    *amp = _amp;
 | 
			
		||||
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Normal burst detection
 | 
			
		||||
 *
 | 
			
		||||
@@ -1717,6 +1851,11 @@ bool sigProcLibSetup(int sps)
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!generateSCHSequence(1)) {
 | 
			
		||||
    sigProcLibDestroy();
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  generateDelayFilters();
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
 
 | 
			
		||||
@@ -158,6 +158,7 @@ bool generateMidamble(int sps, int tsc);
 | 
			
		||||
        @return Success.
 | 
			
		||||
*/
 | 
			
		||||
bool generateRACHSequence(int sps);
 | 
			
		||||
bool generateSCHSequence(int sps);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
        Energy detector, checks to see if received burst energy is above a threshold.
 | 
			
		||||
@@ -187,6 +188,17 @@ int detectRACHBurst(signalVector &rxBurst,
 | 
			
		||||
                    complex *amplitude,
 | 
			
		||||
                    float* TOA);
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	SCH_DETECT_FULL,
 | 
			
		||||
	SCH_DETECT_NARROW,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int detectSCHBurst(signalVector &rxBurst,
 | 
			
		||||
                    float detectThreshold,
 | 
			
		||||
                    int sps,
 | 
			
		||||
                    complex *amplitude,
 | 
			
		||||
                    float* TOA, int state);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
        Normal burst correlator, detector, channel estimator.
 | 
			
		||||
        @param rxBurst The received GSM burst of interest.
 | 
			
		||||
 
 | 
			
		||||
@@ -58,6 +58,9 @@ AC_TYPE_SIZE_T
 | 
			
		||||
AC_HEADER_TIME
 | 
			
		||||
AC_C_BIGENDIAN
 | 
			
		||||
 | 
			
		||||
dnl checks for libraries
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore  >= 0.3.9)
 | 
			
		||||
 | 
			
		||||
AC_ARG_WITH(usrp1, [
 | 
			
		||||
    AS_HELP_STRING([--with-usrp1],
 | 
			
		||||
        [enable USRP1 gnuradio based transceiver])
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user