mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-11-02 13:13:17 +00:00
Compare commits
40 Commits
ms
...
achemeris/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff9b59c223 | ||
|
|
34e5a3807f | ||
|
|
b49874aa64 | ||
|
|
030951695c | ||
|
|
b721d6104d | ||
|
|
c19d1f6c36 | ||
|
|
f0d8a581b4 | ||
|
|
37b445d4c8 | ||
|
|
df127bc74e | ||
|
|
6512812e43 | ||
|
|
ded68da44f | ||
|
|
37bbfa2125 | ||
|
|
fdbf914584 | ||
|
|
bbef7e4d70 | ||
|
|
351fd76706 | ||
|
|
6a2bf0d87b | ||
|
|
2966048b07 | ||
|
|
f5fd578d60 | ||
|
|
57ef87d061 | ||
|
|
5721920a08 | ||
|
|
194a9b1982 | ||
|
|
1fe5282133 | ||
|
|
4438a9fd8f | ||
|
|
64ad712daa | ||
|
|
5c7c178369 | ||
|
|
90f7a01d1d | ||
|
|
e171425b99 | ||
|
|
4d029d8965 | ||
|
|
6613331459 | ||
|
|
577cd020c1 | ||
|
|
88bbf1aafd | ||
|
|
2cc2ddda41 | ||
|
|
d7610cf0b8 | ||
|
|
722d4f70a4 | ||
|
|
93b7f37309 | ||
|
|
4ad9ea69ab | ||
|
|
eb54bddf47 | ||
|
|
a4d1a41244 | ||
|
|
b999759175 | ||
|
|
1ae25561fa |
@@ -67,7 +67,8 @@ const char *levelNames[] = {
|
||||
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
|
||||
};
|
||||
int numLevels = 8;
|
||||
bool gLogToConsole = 0;
|
||||
bool gLogToConsole = true;
|
||||
bool gLogToSyslog = false;
|
||||
FILE *gLogToFile = NULL;
|
||||
Mutex gLogToLock;
|
||||
|
||||
@@ -196,14 +197,16 @@ Log::~Log()
|
||||
if (sLoggerInited) addAlarm(mStream.str().c_str());
|
||||
cerr << mStream.str() << endl;
|
||||
}
|
||||
// Current logging level was already checked by the macro.
|
||||
// So just log.
|
||||
syslog(mPriority, "%s", mStream.str().c_str());
|
||||
// pat added for easy debugging.
|
||||
// Current logging level was already checked by the macro. So just log.
|
||||
// Log to syslog
|
||||
if (gLogToSyslog) {
|
||||
syslog(mPriority, "%s", mStream.str().c_str());
|
||||
}
|
||||
// Log to file and console
|
||||
if (gLogToConsole||gLogToFile) {
|
||||
int mlen = mStream.str().size();
|
||||
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
|
||||
gLogToLock.lock();
|
||||
ScopedLock lock(gLogToLock);
|
||||
if (gLogToConsole) {
|
||||
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
|
||||
// so just use std::cout.
|
||||
@@ -215,7 +218,6 @@ Log::~Log()
|
||||
if (neednl) {fputc('\n',gLogToFile);}
|
||||
fflush(gLogToFile);
|
||||
}
|
||||
gLogToLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,10 +245,9 @@ void gLogInit(const char* name, const char* level, int facility)
|
||||
gConfig.set("Log.Level",level);
|
||||
}
|
||||
|
||||
// Pat added, tired of the syslog facility.
|
||||
// Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file:
|
||||
string str = gConfig.getStr("Log.File");
|
||||
if (gLogToFile==0 && str.length() && 0==strncmp(gCmdName,"Open",4)) {
|
||||
if (gLogToFile==NULL && str.length() && 0==strncmp(gCmdName,"Open",4)) {
|
||||
const char *fn = str.c_str();
|
||||
if (fn && *fn && strlen(fn)>3) { // strlen because a garbage char is getting in sometimes.
|
||||
gLogToFile = fopen(fn,"w"); // New log file each time we start.
|
||||
|
||||
@@ -116,7 +116,8 @@ class Log {
|
||||
|
||||
std::ostringstream& get();
|
||||
};
|
||||
extern bool gLogToConsole; // Pat added for easy debugging.
|
||||
extern bool gLogToConsole; // Output log messages to stdout
|
||||
extern bool gLogToSyslog; // Output log messages to syslog
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -172,8 +172,15 @@ class Thread {
|
||||
void start(void *(*task)(void*), void *arg);
|
||||
|
||||
/** Join a thread that will stop on its own. */
|
||||
void join() { int s = pthread_join(mThread,NULL); assert(!s); mThread = 0; }
|
||||
void join() {
|
||||
if (mThread) {
|
||||
int s = pthread_join(mThread, NULL);
|
||||
assert(!s);
|
||||
}
|
||||
}
|
||||
|
||||
/** Send cancelation to thread */
|
||||
void cancel() { pthread_cancel(mThread); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -45,8 +45,6 @@ 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,8 +53,6 @@ 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. */
|
||||
//@{
|
||||
|
||||
260
README
260
README
@@ -1,168 +1,116 @@
|
||||
Welcome to the OpenBTS source code.
|
||||
This is the interface to the transcevier.
|
||||
|
||||
|
||||
For free support, please subscribe to openbts-discuss@lists.sourceforge.net.
|
||||
See http://sourceforge.net/mailarchive/forum.php?forum_name=openbts-discuss
|
||||
and https://lists.sourceforge.net/lists/listinfo/openbts-discuss for details.
|
||||
|
||||
For additional information, refer to http://openbts.org.
|
||||
|
||||
|
||||
These are the directories:
|
||||
|
||||
AsteriskConfig Asterisk configuration files for use with OpenBTS.
|
||||
CommonLib Common-use libraries, mostly C++ wrappers for basic facilities.
|
||||
Control Control-layer functions for the protocols of GSM 04.08 and SIP.
|
||||
GSM The GSM stack.
|
||||
SIP Components of the SIP state machines ued by the control layer.
|
||||
SMS The SMS stack.
|
||||
SR The subscriber registry.
|
||||
TRXManager The interface between the GSM stack and the radio.
|
||||
Transceiver The software transceiver and specific installation tests.
|
||||
apps OpenBTS application binaries.
|
||||
doc Project documentation.
|
||||
tests Test fixtures for subsets of OpenBTS components.
|
||||
smqueue RFC-3428 store-and-forward server for SMS
|
||||
Each TRX Manager UDP socket interface represents a single ARFCN.
|
||||
Each of these per-ARFCN interfaces is a pair of UDP sockets, one for control and one for data.
|
||||
Give a base port B (5700), the master clock interface is at port P=B.
|
||||
The TRX-side control interface for C(N) is on port P=B+2N+1 and the data interface is on an odd numbered port P=B+2N+2.
|
||||
The corresponding core-side interface for every socket is at P+100.
|
||||
For any given build, the number of ARFCN interfaces can be fixed.
|
||||
|
||||
|
||||
|
||||
By default, OpenBTS assumes the following UDP port assignments:
|
||||
Indications on the Master Clock Interface
|
||||
|
||||
5060 -- Asterisk SIP interface
|
||||
5061 -- local SIP softphone
|
||||
5062 -- OpenBTS SIP interface
|
||||
5063 -- smqueue SIP interface
|
||||
5064 -- subscriber registry SIP interface
|
||||
5700-range -- OpenBTS-transceiver interface
|
||||
The master clock interface is output only (from the radio).
|
||||
Messages are "indications".
|
||||
|
||||
These can be controlled in the CONFIG table in /etc/OpenBTS.db.
|
||||
CLOCK gives the current value of the transceiver clock to be used by the core.
|
||||
This message is sent whenever a trasmission packet arrives that is too late or too early. The clock value is NOT the current transceiver time. It is a time setting the the core should use to give better packet arrival times.
|
||||
IND CLOCK <totalFrames>
|
||||
|
||||
Standrd paths:
|
||||
/OpenBTS -- Binary installation.
|
||||
/etc/OpenBTS -- Configuration databases.
|
||||
/var/run/OpenBTS -- Real-time reporting databases.
|
||||
|
||||
The script apps/setUpFiles.sh will create these directories and install the
|
||||
correct files in them.
|
||||
|
||||
Commands on the Per-ARFCN Control Interface
|
||||
|
||||
The per-ARFCN control interface uses a command-reponse protocol.
|
||||
Commands are NULL-terminated ASCII strings, one per UDP socket.
|
||||
Each command has a corresponding response.
|
||||
Every command is of the form:
|
||||
|
||||
CMD <cmdtype> [params]
|
||||
|
||||
The <cmdtype> is the actual command.
|
||||
Parameters are optional depending on the commands type.
|
||||
Every response is of the form:
|
||||
|
||||
RSP <cmdtype> <status> [result]
|
||||
|
||||
The <status> is 0 for success and a non-zero error code for failure.
|
||||
Successful responses may include results, depending on the command type.
|
||||
|
||||
|
||||
Power Control
|
||||
|
||||
POWEROFF shuts off transmitter power and stops the demodulator.
|
||||
CMD POWEROFF
|
||||
RSP POWEROFF <status>
|
||||
|
||||
POWERON starts the transmitter and starts the demodulator. Initial power level is very low.
|
||||
This command fails if the transmitter and receiver are not yet tuned.
|
||||
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
|
||||
If the transceiver is already on, it response with success to this command.
|
||||
CMD POWERON
|
||||
RSP POWERON <status>
|
||||
|
||||
SETPOWER sets output power in dB wrt full scale.
|
||||
This command fails if the transmitter and receiver are not running.
|
||||
CMD SETPOWER <dB>
|
||||
RSP SETPOWER <status> <dB>
|
||||
|
||||
ADJPOWER adjusts power by the given dB step. Response returns resulting power level wrt full scale.
|
||||
This command fails if the transmitter and receiver are not running.
|
||||
CMD ADJPOWER <dBStep>
|
||||
RSP ADJPOWER <status> <dBLevel>
|
||||
|
||||
|
||||
Tuning Control
|
||||
|
||||
RXTUNE tunes the receiver to a given frequency in kHz.
|
||||
This command fails if the receiver is already running.
|
||||
(To re-tune you stop the radio, re-tune, and restart.)
|
||||
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
|
||||
CMD RXTUNE <kHz>
|
||||
RSP RXTUNE <status> <kHz>
|
||||
|
||||
TXTUNE tunes the transmitter to a given frequency in kHz.
|
||||
This command fails if the transmitter is already running.
|
||||
(To re-tune you stop the radio, re-tune, and restart.)
|
||||
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
|
||||
CMD TXTUNE <kHz>
|
||||
RSP TXTUNE <status> <kHz>
|
||||
|
||||
|
||||
Timeslot Control
|
||||
|
||||
SETSLOT sets the format of the uplink timeslots in the ARFCN.
|
||||
The <timeslot> indicates the timeslot of interest.
|
||||
The <chantype> indicates the type of channel that occupies the timeslot.
|
||||
A chantype of zero indicates the timeslot is off.
|
||||
CMD SETSLOT <timeslot> <chantype>
|
||||
RSP SETSLOT <status> <timeslot> <chantype>
|
||||
|
||||
|
||||
Messages on the per-ARFCN Data Interface
|
||||
|
||||
Messages on the data interface carry one radio burst per UDP message.
|
||||
|
||||
|
||||
Received Data Burst
|
||||
|
||||
1 byte timeslot index
|
||||
4 bytes GSM frame number, big endian
|
||||
1 byte RSSI in -dBm
|
||||
2 bytes correlator timing offset in 1/256 symbol steps, 2's-comp, big endian
|
||||
148 bytes soft symbol estimates, 0 -> definite "0", 255 -> definite "1"
|
||||
|
||||
|
||||
Transmit Data Burst
|
||||
|
||||
1 byte timeslot index
|
||||
4 bytes GSM frame number, big endian
|
||||
1 byte transmit level wrt ARFCN max, -dB (attenuation)
|
||||
148 bytes output symbol values, 0 & 1
|
||||
|
||||
|
||||
|
||||
|
||||
Release history:
|
||||
|
||||
Release Name SVN Reposiory SVN Rev Comments
|
||||
|
||||
1.0 (none) SF.net ?? completed L1, L2
|
||||
|
||||
1.1 Arnaudville GNU Radio r10019 (trunk)
|
||||
|
||||
1.2 Breaux Bridge GNU Radio r10088 (trunk) GNU Build, very early assignment
|
||||
|
||||
1.3 Carencro KSP r1 (trunk) first post-injunction release
|
||||
|
||||
1.4 Donaldsonville KSP r23 (trunk) fixed Ubuntu build error
|
||||
|
||||
1.5 Eunice KSP r39 (trunk) fixed L2 bugs related to segmentation
|
||||
removed incomplete SMS directory
|
||||
moved "abort" calls into L3 subclasses
|
||||
|
||||
1.6 New Iberia KSP r130 (trunk) import of all 2.2 improvements to non-SMS release
|
||||
|
||||
|
||||
2.0 St. Francisville KSP r54 (smswork) SMS support
|
||||
file-based configuration
|
||||
|
||||
2.1 Grand Coteau KSP r70 (smswork) DTMF support
|
||||
fixed more Linux-related build errors
|
||||
-lpthread
|
||||
TLMessage constructor
|
||||
expanded stack to prevent overflows in Linux
|
||||
moved gSIPInterface to main app
|
||||
fixed iterator bug in Pager
|
||||
|
||||
2.2 Houma KSP r122 (smswork) added LEGAL notice
|
||||
removed Assert classes
|
||||
stop paging on page response
|
||||
fixed Pager-spin bug
|
||||
fixed Transceiver spin bugs
|
||||
fixed 2^32 microsecond rollover bug
|
||||
reduced stack footprints in Transceiver
|
||||
fixed SMS timestamps
|
||||
check LAI before using TMSI in LUR
|
||||
reduced memory requirement by 75%
|
||||
removed PagerTest
|
||||
fixed stale-transaction bug in paging handler
|
||||
fixed USRP clock rollover bug
|
||||
faster call connection
|
||||
new USRPDevice design
|
||||
|
||||
2.3 Jean Lafitte KSP r190? (trunk) check for out-of-date RACH bursts
|
||||
better TRX-GSM clock sync
|
||||
formal logging system
|
||||
command line interface
|
||||
emergency call setup
|
||||
|
||||
2.4 Kinder KSP r208? (trunk) fixed BCCH neighbor list bug
|
||||
support for neighbor lists
|
||||
fixed support for non-local Asterisk servers
|
||||
cleaner configuration management
|
||||
more realtime control of BCCH parameters
|
||||
proper rejection of Hold messages
|
||||
fixed L3 hanging bug in MTDCheckBYE
|
||||
|
||||
2.4.1 Kinder KSP r462 fixed lots of valgrind errors
|
||||
|
||||
2.4.2 Kinder KSP r482 zero-length calling party number bug
|
||||
g++ 4.4 #includes
|
||||
|
||||
2.5 Lacassine KSP r551 imported Joshua Lackey patches
|
||||
SIP fixes from Anne Kwong
|
||||
SIP fixes from testing with SMS server
|
||||
L3 TI handling fixes
|
||||
SMS server support
|
||||
GNU Radio 3.2 compatibility
|
||||
configurable max range and LU-reject cause
|
||||
"page" & "testcall" CLI features
|
||||
|
||||
2.5.1 Lacassine KSP r595 fixed some build bugs for some Linux distros
|
||||
|
||||
2.5.2 Lacassine KSP r630 fixed channel assignment bug for Nokia DCT4+ handsets
|
||||
|
||||
2.5.3 Lacassine KSP r756 merged fix for transceiver startup crash
|
||||
due to use of uninitialized variables (r646)
|
||||
merged fix for fusb bug from trunk (r582)
|
||||
|
||||
2.5.4 Lacassine KSP r812 merged fixes to build under latest Fedora and
|
||||
to build with git GnuRadio (r814)
|
||||
|
||||
2.6 Mamou KSP r886 fixed infamous fusb bug (r582)
|
||||
fixed idle-filling table size bug
|
||||
smoother uplink power control
|
||||
load-limiting downlink power control
|
||||
new "config" features (optional, static)
|
||||
IMEI interrogation
|
||||
fixed MOD "missing FIFO" bug
|
||||
configurable short code features
|
||||
fixed transceiver startup crash (r646)
|
||||
readline support is back
|
||||
fixed timing advance bug (r844)
|
||||
added CLI "chans" command
|
||||
track time-of-use in TMSI table (r844)
|
||||
added CLI "noise" command (r844)
|
||||
added CLI "rxpower" command (r844)
|
||||
added CLI "unconfig" command
|
||||
|
||||
2.7 Natchitoches Range rxxx (never released publicly)
|
||||
converted TMSITable to sqlite3 (r902)
|
||||
sqlite3-based configuration (r???)
|
||||
converted Logger to syslogd (r903)
|
||||
added support for rest octets (r1022)
|
||||
external database for transaction reporting (r1184)
|
||||
external database for channel status reporting (r1203)
|
||||
in-call delivery and submission of text messages (r1231)
|
||||
RFC-2833 DMTF (r1249)
|
||||
|
||||
2.8 Opelousas Range rxxx move databases to /etc and /var
|
||||
RRLP aiding support
|
||||
|
||||
|
||||
|
||||
@@ -56,8 +56,7 @@ COMMON_SOURCES = \
|
||||
radioClock.cpp \
|
||||
sigProcLib.cpp \
|
||||
signalVector.cpp \
|
||||
Transceiver.cpp \
|
||||
sch.c
|
||||
Transceiver.cpp
|
||||
|
||||
libtransceiver_la_SOURCES = \
|
||||
$(COMMON_SOURCES) \
|
||||
@@ -88,9 +87,7 @@ osmo_trx_LDADD = \
|
||||
libtransceiver.la \
|
||||
$(ARCH_LA) \
|
||||
$(GSM_LA) \
|
||||
$(COMMON_LA) \
|
||||
$(SQLITE_LA) \
|
||||
$(LIBOSMOCORE_LIBS)
|
||||
$(COMMON_LA) $(SQLITE_LA)
|
||||
|
||||
if USRP1
|
||||
libtransceiver_la_SOURCES += USRPDevice.cpp
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -54,7 +54,7 @@ struct TransceiverState {
|
||||
~TransceiverState();
|
||||
|
||||
/* Initialize a multiframe slot in the filler table */
|
||||
void init(size_t slot, signalVector *burst, bool fill);
|
||||
bool init(int filler, size_t sps, float scale, size_t rtsc);
|
||||
|
||||
int chanType[8];
|
||||
|
||||
@@ -80,115 +80,15 @@ struct TransceiverState {
|
||||
|
||||
/* Received noise energy levels */
|
||||
float mNoiseLev;
|
||||
avgVector mNoises;
|
||||
avgVector mFreqOffsets;
|
||||
noiseVector mNoises;
|
||||
|
||||
/* Store pointers to previous frame */
|
||||
radioVector *prevFrame[8];
|
||||
|
||||
/* Transceiver mode */
|
||||
int mode;
|
||||
/* Shadowed downlink attenuation */
|
||||
int mPower;
|
||||
};
|
||||
|
||||
/** The Transceiver class, responsible for physical layer of basestation */
|
||||
class Transceiver {
|
||||
private:
|
||||
int mBasePort;
|
||||
std::string mAddr;
|
||||
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
|
||||
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
|
||||
|
||||
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
|
||||
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
|
||||
UDPSocket *mClockSocket; ///< socket for writing clock updates to GSM core
|
||||
|
||||
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
|
||||
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
|
||||
|
||||
std::vector<Thread *> mRxServiceLoopThreads; ///< thread to pull bursts into receive 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
|
||||
|
||||
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
|
||||
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
|
||||
|
||||
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
|
||||
SCH, ///< timeslot should contain a SCH burst
|
||||
IDLE ///< timeslot is an idle (or dummy) burst
|
||||
} CorrType;
|
||||
|
||||
/** modulate and add a burst to the transmit queue */
|
||||
void addRadioVector(size_t chan, BitVector &bits,
|
||||
int RSSI, GSM::Time &wTime);
|
||||
|
||||
/** Update filler table */
|
||||
void updateFillerTable(size_t chan, radioVector *burst);
|
||||
|
||||
/** 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, size_t chan = 0);
|
||||
|
||||
/** Set modulus for specific timeslot */
|
||||
void setModulus(size_t timeslot, size_t chan);
|
||||
|
||||
/** return the expected burst type for the specified timestamp */
|
||||
CorrType expectedCorrType(GSM::Time currTime, size_t chan);
|
||||
|
||||
/** send messages over the clock socket */
|
||||
void writeClockInterface(void);
|
||||
|
||||
/** Detect RACH bursts */
|
||||
bool detectRACH(TransceiverState *state,
|
||||
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,
|
||||
complex &, float &toa, GSM::Time &time);
|
||||
|
||||
/** Demodulat burst and output soft bits */
|
||||
SoftVector *demodulate(TransceiverState *state,
|
||||
signalVector &burst, complex amp,
|
||||
float toa, size_t tn, bool equalize);
|
||||
|
||||
|
||||
int mSPSTx; ///< number of samples per Tx symbol
|
||||
int mSPSRx; ///< number of samples per Rx symbol
|
||||
size_t mChans;
|
||||
|
||||
bool mOn; ///< flag to indicate that transceiver is powered on
|
||||
double mTxFreq; ///< the transmit frequency
|
||||
double mRxFreq; ///< the receive frequency
|
||||
int mPower; ///< the transmit power in dB
|
||||
unsigned mTSC; ///< the midamble sequence code
|
||||
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;
|
||||
|
||||
public:
|
||||
|
||||
/** Transceiver constructor
|
||||
@param wBasePort base port number of UDP sockets
|
||||
@param TRXAddress IP address of the TRX manager, as a string
|
||||
@@ -197,17 +97,17 @@ public:
|
||||
@param radioInterface associated radioInterface object
|
||||
*/
|
||||
Transceiver(int wBasePort,
|
||||
const char *TRXAddress,
|
||||
size_t wSPS, size_t chans,
|
||||
GSM::Time wTransmitLatency,
|
||||
RadioInterface *wRadioInterface);
|
||||
const char *TRXAddress,
|
||||
size_t wSPS, size_t chans,
|
||||
GSM::Time wTransmitLatency,
|
||||
RadioInterface *wRadioInterface,
|
||||
double wRssiOffset);
|
||||
|
||||
/** Destructor */
|
||||
~Transceiver();
|
||||
|
||||
/** start the Transceiver */
|
||||
void start();
|
||||
bool init(bool filler);
|
||||
/** Start the control loop */
|
||||
bool init(int filler, size_t rtsc);
|
||||
|
||||
/** attach the radioInterface receive FIFO */
|
||||
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
|
||||
@@ -242,13 +142,107 @@ 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,
|
||||
/** 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;
|
||||
|
||||
enum FillerType {
|
||||
FILLER_DUMMY,
|
||||
FILLER_ZERO,
|
||||
FILLER_RAND,
|
||||
};
|
||||
|
||||
private:
|
||||
int mBasePort;
|
||||
std::string mAddr;
|
||||
|
||||
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
|
||||
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
|
||||
UDPSocket mClockSocket; ///< socket for writing clock updates to GSM core
|
||||
bool mSendEmptyBursts; ///< send RSSI to the GSM core even if burst has not been demodulated
|
||||
|
||||
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
|
||||
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
|
||||
std::vector<Thread *> mControlServiceLoopThreads; ///< thread to process control messages from GSM core
|
||||
std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
|
||||
|
||||
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
|
||||
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
|
||||
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
|
||||
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
|
||||
|
||||
RadioInterface *mRadioInterface; ///< associated radioInterface object
|
||||
double txFullScale; ///< full scale input to radio
|
||||
double rxFullScale; ///< full scale output to radio
|
||||
|
||||
double rssiOffset; ///< RSSI to dBm conversion offset
|
||||
|
||||
/** modulate and add a burst to the transmit queue */
|
||||
void addRadioVector(size_t chan, BitVector &bits,
|
||||
int RSSI, GSM::Time &wTime);
|
||||
|
||||
/** Update filler table */
|
||||
void updateFillerTable(size_t chan, radioVector *burst);
|
||||
|
||||
/** 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, double &RSSI, bool &isRssiValid,
|
||||
double &timingOffset, double &noise,
|
||||
size_t chan = 0);
|
||||
|
||||
/** Set modulus for specific timeslot */
|
||||
void setModulus(size_t timeslot, size_t chan);
|
||||
|
||||
/** return the expected burst type for the specified timestamp */
|
||||
CorrType expectedCorrType(GSM::Time currTime, size_t chan);
|
||||
|
||||
/** send messages over the clock socket */
|
||||
void writeClockInterface(void);
|
||||
|
||||
/** Detect RACH bursts */
|
||||
int detectRACH(TransceiverState *state,
|
||||
signalVector &burst,
|
||||
complex &, float &toa);
|
||||
|
||||
/** Detect normal bursts */
|
||||
int detectTSC(TransceiverState *state,
|
||||
signalVector &burst,
|
||||
complex &, float &toa, GSM::Time &time);
|
||||
|
||||
/** Demodulat burst and output soft bits */
|
||||
SoftVector *demodulate(TransceiverState *state,
|
||||
signalVector &burst, complex amp,
|
||||
float toa, size_t tn, bool equalize);
|
||||
|
||||
int mSPSTx; ///< number of samples per Tx symbol
|
||||
int mSPSRx; ///< number of samples per Rx symbol
|
||||
size_t mChans;
|
||||
|
||||
bool mOn; ///< flag to indicate that transceiver is powered on
|
||||
double mTxFreq; ///< the transmit frequency
|
||||
double mRxFreq; ///< the receive frequency
|
||||
unsigned mTSC; ///< the midamble sequence code
|
||||
unsigned mMaxExpectedDelay; ///< maximum expected time-of-arrival offset in GSM symbols
|
||||
|
||||
std::vector<TransceiverState> mStates;
|
||||
|
||||
/** Start and stop I/O threads through the control socket API */
|
||||
bool start();
|
||||
void stop();
|
||||
|
||||
/** Protect destructor accessable stop call */
|
||||
Mutex mLock;
|
||||
|
||||
protected:
|
||||
/** drive lower receive I/O and burst generation */
|
||||
void driveReceiveRadio();
|
||||
@@ -272,7 +266,9 @@ protected:
|
||||
|
||||
friend void *TxUpperLoopAdapter(TransceiverChannel *);
|
||||
|
||||
friend void *LowerLoopAdapter(Transceiver *);
|
||||
friend void *RxLowerLoopAdapter(Transceiver *);
|
||||
|
||||
friend void *TxLowerLoopAdapter(Transceiver *);
|
||||
|
||||
friend void *ControlServiceLoopAdapter(TransceiverChannel *);
|
||||
|
||||
@@ -287,7 +283,8 @@ protected:
|
||||
void *RxUpperLoopAdapter(TransceiverChannel *);
|
||||
|
||||
/** Main drive threads */
|
||||
void *LowerLoopAdapter(Transceiver *);
|
||||
void *RxLowerLoopAdapter(Transceiver *);
|
||||
void *TxLowerLoopAdapter(Transceiver *);
|
||||
|
||||
/** control message handler thread loop */
|
||||
void *ControlServiceLoopAdapter(TransceiverChannel *);
|
||||
|
||||
@@ -34,12 +34,25 @@
|
||||
|
||||
#define B2XX_CLK_RT 26e6
|
||||
#define E1XX_CLK_RT 52e6
|
||||
#define B2XX_BASE_RT GSMRATE
|
||||
#define B100_BASE_RT 400000
|
||||
#define USRP2_BASE_RT 390625
|
||||
#define TX_AMPL 0.3
|
||||
#define USRP_TX_AMPL 0.3
|
||||
#define UMTRX_TX_AMPL 0.7
|
||||
#define SAMPLE_BUF_SZ (1 << 20)
|
||||
|
||||
/*
|
||||
* UHD timeout value on streaming (re)start
|
||||
*
|
||||
* Allow some time for streaming to commence after the start command is issued,
|
||||
* but consider a wait beyond one second to be a definite error condition.
|
||||
*/
|
||||
#define UHD_RESTART_TIMEOUT 1.0
|
||||
|
||||
/*
|
||||
* UmTRX specific settings
|
||||
*/
|
||||
#define UMTRX_VGA1_DEF -18
|
||||
|
||||
enum uhd_dev_type {
|
||||
USRP1,
|
||||
USRP2,
|
||||
@@ -47,6 +60,8 @@ enum uhd_dev_type {
|
||||
B200,
|
||||
B210,
|
||||
E1XX,
|
||||
E3XX,
|
||||
X3XX,
|
||||
UMTRX,
|
||||
NUM_USRP_TYPES,
|
||||
};
|
||||
@@ -58,11 +73,6 @@ 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
|
||||
@@ -86,6 +96,10 @@ static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 2] = {
|
||||
{ B210, 4, 6.9248e-5, "B210 4 SPS" },
|
||||
{ E1XX, 1, 9.5192e-5, "E1XX 1 SPS" },
|
||||
{ E1XX, 4, 6.5571e-5, "E1XX 4 SPS" },
|
||||
{ E3XX, 1, 1.5000e-4, "E3XX 1 SPS" },
|
||||
{ E3XX, 4, 1.2740e-4, "E3XX 4 SPS" },
|
||||
{ X3XX, 1, 1.5360e-4, "X3XX 1 SPS"},
|
||||
{ X3XX, 4, 1.1264e-4, "X3XX 4 SPS"},
|
||||
{ UMTRX, 1, 9.9692e-5, "UmTRX 1 SPS" },
|
||||
{ UMTRX, 4, 7.3846e-5, "UmTRX 4 SPS" },
|
||||
};
|
||||
@@ -102,7 +116,7 @@ static struct uhd_dev_offset special_offsets[] = {
|
||||
static double get_dev_offset(enum uhd_dev_type type,
|
||||
int sps, bool diversity = false)
|
||||
{
|
||||
struct uhd_dev_offset *offset;
|
||||
struct uhd_dev_offset *offset = NULL;
|
||||
|
||||
/* Reject USRP1 */
|
||||
if (type == USRP1) {
|
||||
@@ -126,17 +140,21 @@ static double get_dev_offset(enum uhd_dev_type type,
|
||||
offset = &special_offsets[1];
|
||||
}
|
||||
} else {
|
||||
/* Normal operation */
|
||||
switch (sps) {
|
||||
case 1:
|
||||
offset = &uhd_offsets[2 * type + 0];
|
||||
break;
|
||||
case 4:
|
||||
default:
|
||||
offset = &uhd_offsets[2 * type + 1];
|
||||
/* Search for matching offset value */
|
||||
for (int i = 0; i < NUM_USRP_TYPES * 2; i++) {
|
||||
if ((type == uhd_offsets[i].type) &&
|
||||
(sps == uhd_offsets[i].sps)) {
|
||||
offset = &uhd_offsets[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!offset) {
|
||||
LOG(ERR) << "Invalid device configuration";
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
std::cout << "-- Setting " << offset->desc << std::endl;
|
||||
|
||||
return offset->offset;
|
||||
@@ -161,12 +179,14 @@ static double select_rate(uhd_dev_type type, int sps, bool diversity = false)
|
||||
|
||||
switch (type) {
|
||||
case USRP2:
|
||||
case X3XX:
|
||||
return USRP2_BASE_RT * sps;
|
||||
case B100:
|
||||
return B100_BASE_RT * sps;
|
||||
case B200:
|
||||
case B210:
|
||||
case E1XX:
|
||||
case E3XX:
|
||||
case UMTRX:
|
||||
return GSMRATE * sps;
|
||||
default:
|
||||
@@ -190,8 +210,7 @@ uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
|
||||
|
||||
TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
|
||||
{
|
||||
TIMESTAMP ticks = ts.get_full_secs() * rate;
|
||||
return ts.get_tick_count(rate) + ticks;
|
||||
return (TIMESTAMP)(ts.get_real_secs() * rate + 0.5);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -230,7 +249,7 @@ public:
|
||||
/** Buffer status string
|
||||
@return a formatted string describing internal buffer state
|
||||
*/
|
||||
std::string str_status() const;
|
||||
std::string str_status(size_t ts) const;
|
||||
|
||||
/** Formatted error string
|
||||
@param code an error code
|
||||
@@ -270,10 +289,10 @@ public:
|
||||
uhd_device(size_t sps, size_t chans, bool diversity, double offset);
|
||||
~uhd_device();
|
||||
|
||||
int open(const std::string &args, bool extref);
|
||||
int open(const std::string &args, bool extref, bool swap_channels);
|
||||
bool start();
|
||||
bool stop();
|
||||
void restart();
|
||||
bool restart();
|
||||
void setPriority(float prio);
|
||||
enum TxWindowType getWindowType() { return tx_window; }
|
||||
|
||||
@@ -287,13 +306,12 @@ 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; }
|
||||
|
||||
inline double fullScaleInputValue() { return 32000 * TX_AMPL; }
|
||||
inline double fullScaleOutputValue() { return 32000; }
|
||||
double fullScaleInputValue();
|
||||
double fullScaleOutputValue();
|
||||
|
||||
double setRxGain(double db, size_t chan);
|
||||
double getRxGain(size_t chan);
|
||||
@@ -319,8 +337,9 @@ public:
|
||||
|
||||
enum err_code {
|
||||
ERROR_TIMING = -1,
|
||||
ERROR_UNRECOVERABLE = -2,
|
||||
ERROR_UNHANDLED = -3,
|
||||
ERROR_TIMEOUT = -2,
|
||||
ERROR_UNRECOVERABLE = -3,
|
||||
ERROR_UNHANDLED = -4,
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -338,7 +357,7 @@ private:
|
||||
double offset;
|
||||
|
||||
std::vector<double> tx_gains, rx_gains;
|
||||
std::vector<tune_result> tx_freqs, rx_freqs;
|
||||
std::vector<double> tx_freqs, rx_freqs;
|
||||
size_t tx_spp, rx_spp;
|
||||
|
||||
bool started;
|
||||
@@ -364,8 +383,9 @@ private:
|
||||
uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
|
||||
bool set_freq(double freq, size_t chan, bool tx);
|
||||
|
||||
Thread async_event_thrd;
|
||||
Thread *async_event_thrd;
|
||||
bool diversity;
|
||||
Mutex tune_lock;
|
||||
};
|
||||
|
||||
void *async_event_loop(uhd_device *dev)
|
||||
@@ -402,6 +422,12 @@ void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
|
||||
}
|
||||
}
|
||||
|
||||
static void thread_enable_cancel(bool cancel)
|
||||
{
|
||||
cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
|
||||
}
|
||||
|
||||
uhd_device::uhd_device(size_t sps, size_t chans, bool diversity, double offset)
|
||||
: tx_gain_min(0.0), tx_gain_max(0.0),
|
||||
rx_gain_min(0.0), rx_gain_max(0.0),
|
||||
@@ -427,9 +453,25 @@ void uhd_device::init_gains()
|
||||
{
|
||||
uhd::gain_range_t range;
|
||||
|
||||
range = usrp_dev->get_tx_gain_range();
|
||||
tx_gain_min = range.start();
|
||||
tx_gain_max = range.stop();
|
||||
if (dev_type == UMTRX) {
|
||||
std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
|
||||
if (gain_stages[0] == "VGA") {
|
||||
LOG(WARNING) << "Update your UHD version for a proper Tx gain support";
|
||||
}
|
||||
if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
|
||||
range = usrp_dev->get_tx_gain_range();
|
||||
tx_gain_min = range.start();
|
||||
tx_gain_max = range.stop();
|
||||
} else {
|
||||
range = usrp_dev->get_tx_gain_range("VGA2");
|
||||
tx_gain_min = UMTRX_VGA1_DEF + range.start();
|
||||
tx_gain_max = UMTRX_VGA1_DEF + range.stop();
|
||||
}
|
||||
} else {
|
||||
range = usrp_dev->get_tx_gain_range();
|
||||
tx_gain_min = range.start();
|
||||
tx_gain_max = range.stop();
|
||||
}
|
||||
|
||||
range = usrp_dev->get_rx_gain_range();
|
||||
rx_gain_min = range.start();
|
||||
@@ -480,7 +522,7 @@ int uhd_device::set_rates(double tx_rate, double rx_rate)
|
||||
double tx_offset, rx_offset;
|
||||
|
||||
/* B2XX and E1xx are the only device where we set FPGA clocking */
|
||||
if ((dev_type == B200) || (dev_type == B210)) {
|
||||
if ((dev_type == B200) || (dev_type == B210) || (dev_type == E3XX)) {
|
||||
if (set_master_clk(B2XX_CLK_RT) < 0)
|
||||
return -1;
|
||||
}
|
||||
@@ -520,7 +562,23 @@ double uhd_device::setTxGain(double db, size_t chan)
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
usrp_dev->set_tx_gain(db, chan);
|
||||
if (dev_type == UMTRX) {
|
||||
std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
|
||||
if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
|
||||
usrp_dev->set_tx_gain(db, chan);
|
||||
} else {
|
||||
// New UHD versions support split configuration of
|
||||
// Tx gain stages. We utilize this to set the gain
|
||||
// configuration, optimal for the Tx signal quality.
|
||||
// From our measurements, VGA1 must be 18dB plus-minus
|
||||
// one and VGA2 is the best when 23dB or lower.
|
||||
usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
|
||||
usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
|
||||
}
|
||||
} else {
|
||||
usrp_dev->set_tx_gain(db, chan);
|
||||
}
|
||||
|
||||
tx_gains[chan] = usrp_dev->get_tx_gain(chan);
|
||||
|
||||
LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB";
|
||||
@@ -563,8 +621,8 @@ bool uhd_device::parse_dev_type()
|
||||
{
|
||||
std::string mboard_str, dev_str;
|
||||
uhd::property_tree::sptr prop_tree;
|
||||
size_t usrp1_str, usrp2_str, e100_str, e110_str,
|
||||
b100_str, b200_str, b210_str, umtrx_str;
|
||||
size_t usrp1_str, usrp2_str, e100_str, e110_str, e310_str,
|
||||
b100_str, b200_str, b210_str, x300_str, x310_str, umtrx_str;
|
||||
|
||||
prop_tree = usrp_dev->get_device()->get_tree();
|
||||
dev_str = prop_tree->access<std::string>("/name").get();
|
||||
@@ -577,6 +635,9 @@ bool uhd_device::parse_dev_type()
|
||||
b210_str = mboard_str.find("B210");
|
||||
e100_str = mboard_str.find("E100");
|
||||
e110_str = mboard_str.find("E110");
|
||||
e310_str = mboard_str.find("E310");
|
||||
x300_str = mboard_str.find("X300");
|
||||
x310_str = mboard_str.find("X310");
|
||||
umtrx_str = dev_str.find("UmTRX");
|
||||
|
||||
if (usrp1_str != std::string::npos) {
|
||||
@@ -604,6 +665,15 @@ bool uhd_device::parse_dev_type()
|
||||
} else if (usrp2_str != std::string::npos) {
|
||||
tx_window = TX_WINDOW_FIXED;
|
||||
dev_type = USRP2;
|
||||
} else if (e310_str != std::string::npos) {
|
||||
tx_window = TX_WINDOW_FIXED;
|
||||
dev_type = E3XX;
|
||||
} else if (x300_str != std::string::npos) {
|
||||
tx_window = TX_WINDOW_FIXED;
|
||||
dev_type = X3XX;
|
||||
} else if (x310_str != std::string::npos) {
|
||||
tx_window = TX_WINDOW_FIXED;
|
||||
dev_type = X3XX;
|
||||
} else if (umtrx_str != std::string::npos) {
|
||||
tx_window = TX_WINDOW_FIXED;
|
||||
dev_type = UMTRX;
|
||||
@@ -623,7 +693,7 @@ bool uhd_device::parse_dev_type()
|
||||
return true;
|
||||
}
|
||||
|
||||
int uhd_device::open(const std::string &args, bool extref)
|
||||
int uhd_device::open(const std::string &args, bool extref, bool swap_channels)
|
||||
{
|
||||
// Find UHD devices
|
||||
uhd::device_addr_t addr(args);
|
||||
@@ -636,9 +706,9 @@ int uhd_device::open(const std::string &args, bool extref)
|
||||
// Use the first found device
|
||||
LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
|
||||
try {
|
||||
usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]);
|
||||
usrp_dev = uhd::usrp::multi_usrp::make(addr);
|
||||
} catch(...) {
|
||||
LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string();
|
||||
LOG(ALERT) << "UHD make failed, device " << args;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -649,7 +719,7 @@ int uhd_device::open(const std::string &args, bool extref)
|
||||
// Verify and set channels
|
||||
if ((dev_type == B210) && (chans == 2)) {
|
||||
} else if ((dev_type == UMTRX) && (chans == 2)) {
|
||||
uhd::usrp::subdev_spec_t subdev_spec("A:0 B:0");
|
||||
uhd::usrp::subdev_spec_t subdev_spec(swap_channels?"B:0 A:0":"A:0 B:0");
|
||||
usrp_dev->set_tx_subdev_spec(subdev_spec);
|
||||
usrp_dev->set_rx_subdev_spec(subdev_spec);
|
||||
} else if (chans != 1) {
|
||||
@@ -679,6 +749,16 @@ int uhd_device::open(const std::string &args, bool extref)
|
||||
if (set_rates(_tx_rate, _rx_rate) < 0)
|
||||
return -1;
|
||||
|
||||
// Set RF frontend bandwidth
|
||||
if (dev_type == UMTRX) {
|
||||
// Setting LMS6002D LPF to 500kHz gives us the best signal quality
|
||||
for (size_t i = 0; i < chans; i++) {
|
||||
usrp_dev->set_tx_bandwidth(500*1000*2, i);
|
||||
if (!diversity)
|
||||
usrp_dev->set_rx_bandwidth(500*1000*2, i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create TX and RX streamers */
|
||||
uhd::stream_args_t stream_args("sc16");
|
||||
for (size_t i = 0; i < chans; i++)
|
||||
@@ -718,10 +798,12 @@ int uhd_device::open(const std::string &args, bool extref)
|
||||
case B100:
|
||||
return RESAMP_64M;
|
||||
case USRP2:
|
||||
case X3XX:
|
||||
return RESAMP_100M;
|
||||
case B200:
|
||||
case B210:
|
||||
case E1XX:
|
||||
case E3XX:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -733,7 +815,7 @@ bool uhd_device::flush_recv(size_t num_pkts)
|
||||
{
|
||||
uhd::rx_metadata_t md;
|
||||
size_t num_smpls;
|
||||
float timeout = 0.1f;
|
||||
float timeout = UHD_RESTART_TIMEOUT;
|
||||
|
||||
std::vector<std::vector<short> >
|
||||
pkt_bufs(chans, std::vector<short>(2 * rx_spp));
|
||||
@@ -749,6 +831,8 @@ bool uhd_device::flush_recv(size_t num_pkts)
|
||||
if (!num_smpls) {
|
||||
switch (md.error_code) {
|
||||
case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
|
||||
LOG(ALERT) << "Device timed out";
|
||||
return false;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
@@ -762,7 +846,7 @@ bool uhd_device::flush_recv(size_t num_pkts)
|
||||
return true;
|
||||
}
|
||||
|
||||
void uhd_device::restart()
|
||||
bool uhd_device::restart()
|
||||
{
|
||||
/* Allow 100 ms delay to align multi-channel streams */
|
||||
double delay = 0.1;
|
||||
@@ -777,7 +861,7 @@ void uhd_device::restart()
|
||||
|
||||
usrp_dev->issue_stream_cmd(cmd);
|
||||
|
||||
flush_recv(1);
|
||||
return flush_recv(10);
|
||||
}
|
||||
|
||||
bool uhd_device::start()
|
||||
@@ -793,10 +877,12 @@ bool uhd_device::start()
|
||||
uhd::msg::register_handler(&uhd_msg_handler);
|
||||
|
||||
// Start asynchronous event (underrun check) loop
|
||||
async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
|
||||
async_event_thrd = new Thread();
|
||||
async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
|
||||
|
||||
// Start streaming
|
||||
restart();
|
||||
if (!restart())
|
||||
return false;
|
||||
|
||||
// Display usrp time
|
||||
double time_now = usrp_dev->get_time_now().get_real_secs();
|
||||
@@ -816,6 +902,10 @@ bool uhd_device::stop()
|
||||
|
||||
usrp_dev->issue_stream_cmd(stream_cmd);
|
||||
|
||||
async_event_thrd->cancel();
|
||||
async_event_thrd->join();
|
||||
delete async_event_thrd;
|
||||
|
||||
started = false;
|
||||
return true;
|
||||
}
|
||||
@@ -836,6 +926,7 @@ int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
|
||||
switch (md.error_code) {
|
||||
case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
|
||||
LOG(ALERT) << "UHD: Receive timed out";
|
||||
return ERROR_TIMEOUT;
|
||||
case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
|
||||
case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
|
||||
case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
|
||||
@@ -891,8 +982,8 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
rc = rx_buffers[0]->avail_smpls(timestamp);
|
||||
if (rc < 0) {
|
||||
LOG(ERR) << rx_buffers[0]->str_code(rc);
|
||||
LOG(ERR) << rx_buffers[0]->str_status();
|
||||
return len;
|
||||
LOG(ERR) << rx_buffers[0]->str_status(timestamp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Create vector buffer
|
||||
@@ -905,8 +996,11 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
|
||||
// Receive samples from the usrp until we have enough
|
||||
while (rx_buffers[0]->avail_smpls(timestamp) < len) {
|
||||
thread_enable_cancel(false);
|
||||
size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
|
||||
metadata, 0.1, true);
|
||||
thread_enable_cancel(true);
|
||||
|
||||
rx_pkt_cnt++;
|
||||
|
||||
// Check for errors
|
||||
@@ -916,6 +1010,9 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
|
||||
LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
|
||||
exit(-1);
|
||||
case ERROR_TIMEOUT:
|
||||
// Assume stopping condition
|
||||
return 0;
|
||||
case ERROR_TIMING:
|
||||
restart();
|
||||
case ERROR_UNHANDLED:
|
||||
@@ -933,7 +1030,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
// Continue on local overrun, exit on other errors
|
||||
if ((rc < 0)) {
|
||||
LOG(ERR) << rx_buffers[i]->str_code(rc);
|
||||
LOG(ERR) << rx_buffers[i]->str_status();
|
||||
LOG(ERR) << rx_buffers[i]->str_status(timestamp);
|
||||
if (rc != smpl_buf::ERROR_OVERFLOW)
|
||||
return 0;
|
||||
}
|
||||
@@ -945,7 +1042,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
rc = rx_buffers[i]->read(bufs[i], len, timestamp);
|
||||
if ((rc < 0) || (rc != len)) {
|
||||
LOG(ERR) << rx_buffers[i]->str_code(rc);
|
||||
LOG(ERR) << rx_buffers[i]->str_status();
|
||||
LOG(ERR) << rx_buffers[i]->str_status(timestamp);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -994,7 +1091,10 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
|
||||
}
|
||||
}
|
||||
|
||||
thread_enable_cancel(false);
|
||||
size_t num_smpls = tx_stream->send(bufs, len, metadata);
|
||||
thread_enable_cancel(true);
|
||||
|
||||
if (num_smpls != (unsigned) len) {
|
||||
LOG(ALERT) << "UHD: Device send timed out";
|
||||
}
|
||||
@@ -1002,20 +1102,30 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
|
||||
return num_smpls;
|
||||
}
|
||||
|
||||
bool uhd_device::updateAlignment(TIMESTAMP)
|
||||
bool uhd_device::updateAlignment(TIMESTAMP 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<tune_result> freqs;
|
||||
std::vector<double> freqs;
|
||||
uhd::tune_request_t treq(freq);
|
||||
|
||||
if ((chans == 1) || ((chans == 2) && dev_type == UMTRX)) {
|
||||
if (dev_type == UMTRX) {
|
||||
if (offset > 0.0)
|
||||
return uhd::tune_request_t(freq, offset);
|
||||
|
||||
// Don't use DSP tuning, because LMS6002D PLL steps are small enough.
|
||||
// We end up with DSP tuning just for 2-3Hz, which is meaningless and
|
||||
// only distort the signal (because cordic is not ideal).
|
||||
treq.target_freq = freq;
|
||||
treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
|
||||
treq.rf_freq = freq;
|
||||
treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
|
||||
treq.dsp_freq = 0.0;
|
||||
} else if (chans == 1) {
|
||||
if (offset == 0.0)
|
||||
return treq;
|
||||
|
||||
@@ -1031,17 +1141,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].freq < 10.0)
|
||||
if (freqs[!chan] < 10.0)
|
||||
return treq;
|
||||
|
||||
/* Find center frequency between channels */
|
||||
rf_spread = fabs(freqs[!chan].freq - freq);
|
||||
rf_spread = fabs(freqs[!chan] - freq);
|
||||
if (rf_spread > B2XX_CLK_RT) {
|
||||
LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
|
||||
return treq;
|
||||
}
|
||||
|
||||
rf_freq = (freqs[!chan].freq + freq) / 2.0f;
|
||||
rf_freq = (freqs[!chan] + freq) / 2.0f;
|
||||
|
||||
treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
|
||||
treq.target_freq = freq;
|
||||
@@ -1058,12 +1168,10 @@ 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].uhd = tres;
|
||||
tx_freqs[chan].freq = usrp_dev->get_tx_freq(chan);
|
||||
tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
|
||||
} else {
|
||||
tres = usrp_dev->set_rx_freq(treq, chan);
|
||||
rx_freqs[chan].uhd = tres;
|
||||
rx_freqs[chan].freq = usrp_dev->get_rx_freq(chan);
|
||||
rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
|
||||
}
|
||||
LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
|
||||
|
||||
@@ -1076,15 +1184,13 @@ 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].freq, !chan, true);
|
||||
treq = select_freq(tx_freqs[!chan], !chan, true);
|
||||
tres = usrp_dev->set_tx_freq(treq, !chan);
|
||||
tx_freqs[!chan].uhd = tres;
|
||||
tx_freqs[!chan].freq = usrp_dev->get_tx_freq(!chan);
|
||||
tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
|
||||
} else {
|
||||
treq = select_freq(rx_freqs[!chan].freq, !chan, false);
|
||||
treq = select_freq(rx_freqs[!chan], !chan, false);
|
||||
tres = usrp_dev->set_rx_freq(treq, !chan);
|
||||
rx_freqs[!chan].uhd = tres;
|
||||
rx_freqs[!chan].freq = usrp_dev->get_rx_freq(!chan);
|
||||
rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
|
||||
|
||||
}
|
||||
LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
|
||||
@@ -1099,30 +1205,18 @@ bool uhd_device::setTxFreq(double wFreq, size_t chan)
|
||||
LOG(ALERT) << "Requested non-existent channel " << chan;
|
||||
return false;
|
||||
}
|
||||
ScopedLock lock(tune_lock);
|
||||
|
||||
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()) {
|
||||
LOG(ALERT) << "Requested non-existent channel " << chan;
|
||||
return false;
|
||||
}
|
||||
ScopedLock lock(tune_lock);
|
||||
|
||||
return set_freq(wFreq, chan, false);
|
||||
}
|
||||
@@ -1134,7 +1228,7 @@ double uhd_device::getTxFreq(size_t chan)
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return tx_freqs[chan].freq;
|
||||
return tx_freqs[chan];
|
||||
}
|
||||
|
||||
double uhd_device::getRxFreq(size_t chan)
|
||||
@@ -1144,13 +1238,30 @@ double uhd_device::getRxFreq(size_t chan)
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return rx_freqs[chan].freq;
|
||||
return rx_freqs[chan];
|
||||
}
|
||||
|
||||
double uhd_device::fullScaleInputValue()
|
||||
{
|
||||
if (dev_type == UMTRX)
|
||||
return (double) SHRT_MAX * UMTRX_TX_AMPL;
|
||||
else
|
||||
return (double) SHRT_MAX * USRP_TX_AMPL;
|
||||
}
|
||||
|
||||
double uhd_device::fullScaleOutputValue()
|
||||
{
|
||||
return (double) SHRT_MAX;
|
||||
}
|
||||
|
||||
bool uhd_device::recv_async_msg()
|
||||
{
|
||||
uhd::async_metadata_t md;
|
||||
if (!usrp_dev->get_device()->recv_async_msg(md))
|
||||
|
||||
thread_enable_cancel(false);
|
||||
bool rc = usrp_dev->get_device()->recv_async_msg(md);
|
||||
thread_enable_cancel(true);
|
||||
if (!rc)
|
||||
return false;
|
||||
|
||||
// Assume that any error requires resynchronization
|
||||
@@ -1318,6 +1429,19 @@ ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
|
||||
if ((timestamp + len) <= time_end)
|
||||
return ERROR_TIMESTAMP;
|
||||
|
||||
if (timestamp < time_end) {
|
||||
LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
|
||||
uhd::time_spec_t ts = convert_time(timestamp, clk_rt);
|
||||
LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << convert_time(ts, clk_rt) << ") rate=" << clk_rt;
|
||||
// Do not return error here, because it's a rounding error and is not fatal
|
||||
}
|
||||
if (timestamp > time_end && time_end != 0) {
|
||||
LOG(ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
|
||||
uhd::time_spec_t ts = convert_time(timestamp, clk_rt);
|
||||
LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << convert_time(ts, clk_rt) << ") rate=" << clk_rt;
|
||||
// Do not return error here, because it's a rounding error and is not fatal
|
||||
}
|
||||
|
||||
// Starting index
|
||||
size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
|
||||
|
||||
@@ -1352,11 +1476,12 @@ ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
|
||||
return write(buf, len, convert_time(ts, clk_rt));
|
||||
}
|
||||
|
||||
std::string smpl_buf::str_status() const
|
||||
std::string smpl_buf::str_status(size_t ts) const
|
||||
{
|
||||
std::ostringstream ost("Sample buffer: ");
|
||||
|
||||
ost << "length = " << buf_len;
|
||||
ost << "timestamp = " << ts;
|
||||
ost << ", length = " << buf_len;
|
||||
ost << ", time_start = " << time_start;
|
||||
ost << ", time_end = " << time_end;
|
||||
ost << ", data_start = " << data_start;
|
||||
|
||||
@@ -89,7 +89,7 @@ USRPDevice::USRPDevice(size_t sps, size_t, bool)
|
||||
#endif
|
||||
}
|
||||
|
||||
int USRPDevice::open(const std::string &, bool)
|
||||
int USRPDevice::open(const std::string &, bool, bool)
|
||||
{
|
||||
writeLock.unlock();
|
||||
|
||||
@@ -600,7 +600,7 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
|
||||
bool USRPDevice::setRxFreq(double wFreq) { return true;};
|
||||
#endif
|
||||
|
||||
RadioDevice *RadioDevice::make(size_t sps, size_t chans, bool diversity)
|
||||
RadioDevice *RadioDevice::make(size_t sps, size_t chans, bool diversity, double)
|
||||
{
|
||||
return new USRPDevice(sps, chans, diversity);
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ private:
|
||||
USRPDevice(size_t sps, size_t chans = 1, bool diversity = false);
|
||||
|
||||
/** Instantiate the USRP */
|
||||
int open(const std::string &, bool);
|
||||
int open(const std::string &, bool, bool);
|
||||
|
||||
/** Start the USRP */
|
||||
bool start();
|
||||
|
||||
@@ -65,11 +65,13 @@ struct trx_config {
|
||||
unsigned port;
|
||||
unsigned sps;
|
||||
unsigned chans;
|
||||
unsigned rtsc;
|
||||
bool extref;
|
||||
bool filler;
|
||||
Transceiver::FillerType filler;
|
||||
bool diversity;
|
||||
bool ms;
|
||||
double offset;
|
||||
double rssi_offset;
|
||||
bool swap_channels;
|
||||
};
|
||||
|
||||
ConfigurationTable gConfig;
|
||||
@@ -119,7 +121,7 @@ bool testConfig()
|
||||
*/
|
||||
bool trx_setup_config(struct trx_config *config)
|
||||
{
|
||||
std::string refstr, fillstr, divstr, msstr;
|
||||
std::string refstr, fillstr, divstr;
|
||||
|
||||
if (!testConfig())
|
||||
return false;
|
||||
@@ -155,20 +157,23 @@ bool trx_setup_config(struct trx_config *config)
|
||||
config->diversity = DEFAULT_DIVERSITY;
|
||||
}
|
||||
|
||||
if (!config->sps)
|
||||
config->sps = DEFAULT_SPS;
|
||||
|
||||
if (!config->chans)
|
||||
config->chans = DEFAULT_CHANS;
|
||||
|
||||
/* Diversity only supported on 2 channels */
|
||||
if (config->diversity)
|
||||
config->chans = 2;
|
||||
|
||||
refstr = config->extref ? "Enabled" : "Disabled";
|
||||
fillstr = config->filler ? "Enabled" : "Disabled";
|
||||
divstr = config->diversity ? "Enabled" : "Disabled";
|
||||
msstr = config->ms ? "Enabled" : "Disabled";
|
||||
switch (config->filler) {
|
||||
case Transceiver::FILLER_DUMMY:
|
||||
fillstr = "Dummy bursts";
|
||||
break;
|
||||
case Transceiver::FILLER_ZERO:
|
||||
fillstr = "Disabled";
|
||||
break;
|
||||
case Transceiver::FILLER_RAND:
|
||||
fillstr = "Normal busrts with random payload";
|
||||
break;
|
||||
}
|
||||
|
||||
std::ostringstream ost("");
|
||||
ost << "Config Settings" << std::endl;
|
||||
@@ -181,8 +186,9 @@ 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;
|
||||
ost << " RSSI to dBm offset...... " << config->rssi_offset << std::endl;
|
||||
ost << " Swap channels........... " << config->swap_channels << std::endl;
|
||||
std::cout << ost << std::endl;
|
||||
|
||||
return true;
|
||||
@@ -199,22 +205,10 @@ 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, div, offset);
|
||||
radio = new RadioInterface(usrp, config->sps, config->chans);
|
||||
break;
|
||||
case RadioDevice::RESAMP_64M:
|
||||
case RadioDevice::RESAMP_100M:
|
||||
@@ -250,8 +244,8 @@ Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio)
|
||||
VectorFIFO *fifo;
|
||||
|
||||
trx = new Transceiver(config->port, config->addr.c_str(), config->sps,
|
||||
config->chans, GSM::Time(3,0), radio);
|
||||
if (!trx->init(config->filler)) {
|
||||
config->chans, GSM::Time(3,0), radio, config->rssi_offset);
|
||||
if (!trx->init(config->filler, config->rtsc)) {
|
||||
LOG(ALERT) << "Failed to initialize transceiver";
|
||||
delete trx;
|
||||
return NULL;
|
||||
@@ -301,8 +295,10 @@ 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",
|
||||
" -o Set baseband frequency offset (default=auto)\n"
|
||||
" -r Random burst test mode with TSC\n"
|
||||
" -R RSSI to dBm offset in dB (default=0)\n"
|
||||
" -S Swap channels (UmTRX only)\n",
|
||||
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
|
||||
}
|
||||
|
||||
@@ -311,15 +307,17 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
||||
int option;
|
||||
|
||||
config->port = 0;
|
||||
config->sps = 0;
|
||||
config->chans = 0;
|
||||
config->sps = DEFAULT_SPS;
|
||||
config->chans = DEFAULT_CHANS;
|
||||
config->rtsc = 0;
|
||||
config->extref = false;
|
||||
config->filler = false;
|
||||
config->filler = Transceiver::FILLER_ZERO;
|
||||
config->diversity = false;
|
||||
config->ms = false;
|
||||
config->offset = 0.0;
|
||||
config->rssi_offset = 0.0;
|
||||
config->swap_channels = false;
|
||||
|
||||
while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:ms:")) != -1) {
|
||||
while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:r:R:S")) != -1) {
|
||||
switch (option) {
|
||||
case 'h':
|
||||
print_help();
|
||||
@@ -347,27 +345,41 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
||||
config->extref = true;
|
||||
break;
|
||||
case 'f':
|
||||
config->filler = true;
|
||||
config->filler = Transceiver::FILLER_DUMMY;
|
||||
break;
|
||||
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)) {
|
||||
printf("Unsupported samples-per-symbol\n\n");
|
||||
print_help();
|
||||
exit(0);
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
config->rtsc = atoi(optarg);
|
||||
config->filler = Transceiver::FILLER_RAND;
|
||||
break;
|
||||
case 'R':
|
||||
config->rssi_offset = atof(optarg);
|
||||
break;
|
||||
case 'S':
|
||||
config->swap_channels = true;
|
||||
break;
|
||||
default:
|
||||
print_help();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
if ((config->sps != 1) && (config->sps != 4)) {
|
||||
printf("Unsupported samples-per-symbol %i\n\n", config->sps);
|
||||
print_help();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (config->rtsc > 7) {
|
||||
printf("Invalid training sequence %i\n\n", config->rtsc);
|
||||
print_help();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
@@ -395,7 +407,7 @@ int main(int argc, char *argv[])
|
||||
/* Create the low level device object */
|
||||
usrp = RadioDevice::make(config.sps, config.chans,
|
||||
config.diversity, config.offset);
|
||||
type = usrp->open(config.dev_args, config.extref);
|
||||
type = usrp->open(config.dev_args, config.extref, config.swap_channels);
|
||||
if (type < 0) {
|
||||
LOG(ALERT) << "Failed to create radio device" << std::endl;
|
||||
goto shutdown;
|
||||
@@ -411,8 +423,6 @@ int main(int argc, char *argv[])
|
||||
if (!trx)
|
||||
goto shutdown;
|
||||
|
||||
trx->start();
|
||||
|
||||
chans = trx->numChans();
|
||||
std::cout << "-- Transceiver active with "
|
||||
<< chans << " channel(s)" << std::endl;
|
||||
|
||||
@@ -23,66 +23,27 @@
|
||||
|
||||
void RadioClock::set(const GSM::Time& wTime)
|
||||
{
|
||||
mLock.lock();
|
||||
ScopedLock lock(mLock);
|
||||
mClock = wTime;
|
||||
updateSignal.signal();
|
||||
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();
|
||||
ScopedLock lock(mLock);
|
||||
mClock.incTN();
|
||||
updateSignal.signal();
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
GSM::Time RadioClock::get()
|
||||
{
|
||||
mLock.lock();
|
||||
ScopedLock lock(mLock);
|
||||
GSM::Time retVal = mClock;
|
||||
mLock.unlock();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void RadioClock::wait()
|
||||
{
|
||||
mLock.lock();
|
||||
ScopedLock lock(mLock);
|
||||
updateSignal.wait(mLock,1);
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
@@ -26,10 +26,7 @@
|
||||
|
||||
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();
|
||||
|
||||
@@ -41,7 +41,7 @@ class RadioDevice {
|
||||
bool diversity = false, double offset = 0.0);
|
||||
|
||||
/** Initialize the USRP */
|
||||
virtual int open(const std::string &args = "", bool extref = false)=0;
|
||||
virtual int open(const std::string &args = "", bool extref = false, bool swap_channels = false)=0;
|
||||
|
||||
virtual ~RadioDevice() { }
|
||||
|
||||
@@ -91,9 +91,6 @@ 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,8 +38,7 @@ 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), shiftOffset(0), shiftUpdate(false),
|
||||
mOn(false)
|
||||
receiveOffset(wReceiveOffset), mOn(false)
|
||||
{
|
||||
mClock.set(wStartTime);
|
||||
}
|
||||
@@ -107,23 +106,27 @@ double RadioInterface::fullScaleOutputValue(void) {
|
||||
return mRadio->fullScaleOutputValue();
|
||||
}
|
||||
|
||||
|
||||
void RadioInterface::setPowerAttenuation(double atten, size_t chan)
|
||||
int RadioInterface::setPowerAttenuation(int atten, size_t chan)
|
||||
{
|
||||
double rfGain, digAtten;
|
||||
|
||||
if (chan >= mChans) {
|
||||
LOG(ALERT) << "Invalid channel requested";
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rfGain = mRadio->setTxGain(mRadio->maxTxGain() - atten, chan);
|
||||
digAtten = atten - mRadio->maxTxGain() + rfGain;
|
||||
if (atten < 0.0)
|
||||
atten = 0.0;
|
||||
|
||||
rfGain = mRadio->setTxGain(mRadio->maxTxGain() - (double) atten, chan);
|
||||
digAtten = (double) atten - mRadio->maxTxGain() + rfGain;
|
||||
|
||||
if (digAtten < 1.0)
|
||||
powerScaling[chan] = 1.0;
|
||||
else
|
||||
powerScaling[chan] = 1.0 / sqrt(pow(10, digAtten / 10.0));
|
||||
|
||||
return atten;
|
||||
}
|
||||
|
||||
int RadioInterface::radioifyVector(signalVector &wVector,
|
||||
@@ -158,11 +161,6 @@ 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);
|
||||
@@ -173,19 +171,23 @@ bool RadioInterface::tuneRx(double freq, size_t chan)
|
||||
return mRadio->setRxFreq(freq, chan);
|
||||
}
|
||||
|
||||
bool RadioInterface::tuneRxOffset(double offset, size_t chan)
|
||||
bool RadioInterface::start()
|
||||
{
|
||||
return mRadio->setRxOffset(offset, chan);
|
||||
}
|
||||
if (mOn)
|
||||
return true;
|
||||
|
||||
void RadioInterface::start()
|
||||
{
|
||||
LOG(INFO) << "Starting radio";
|
||||
LOG(INFO) << "Starting radio device";
|
||||
#ifdef USRP1
|
||||
mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
|
||||
(void*)this);
|
||||
#endif
|
||||
mRadio->start();
|
||||
|
||||
if (!mRadio->start())
|
||||
return false;
|
||||
|
||||
recvCursor = 0;
|
||||
sendCursor = 0;
|
||||
|
||||
writeTimestamp = mRadio->initialWriteTimestamp();
|
||||
readTimestamp = mRadio->initialReadTimestamp();
|
||||
|
||||
@@ -194,6 +196,23 @@ void RadioInterface::start()
|
||||
|
||||
mOn = true;
|
||||
LOG(INFO) << "Radio started";
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stop the radio device
|
||||
*
|
||||
* This is a pass-through call to the device interface. Because the underlying
|
||||
* stop command issuance generally doesn't return confirmation on device status,
|
||||
* this call will only return false if the device is already stopped.
|
||||
*/
|
||||
bool RadioInterface::stop()
|
||||
{
|
||||
if (!mOn || !mRadio->stop())
|
||||
return false;
|
||||
|
||||
mOn = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef USRP1
|
||||
@@ -238,11 +257,7 @@ bool RadioInterface::driveReceiveRadio()
|
||||
pullBuffer();
|
||||
|
||||
GSM::Time rcvClock = mClock.get();
|
||||
if (receiveOffset < 0)
|
||||
rcvClock.incTN(-receiveOffset);
|
||||
else
|
||||
rcvClock.decTN(receiveOffset);
|
||||
|
||||
rcvClock.decTN(receiveOffset);
|
||||
unsigned tN = rcvClock.TN();
|
||||
int recvSz = recvCursor;
|
||||
int readSz = 0;
|
||||
@@ -306,12 +321,6 @@ bool RadioInterface::isUnderrun()
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void RadioInterface::applyOffset(int offset)
|
||||
{
|
||||
shiftOffset += offset;
|
||||
shiftUpdate = true;
|
||||
}
|
||||
|
||||
VectorFIFO* RadioInterface::receiveFIFO(size_t chan)
|
||||
{
|
||||
if (chan >= mReceiveFIFO.size())
|
||||
@@ -350,7 +359,7 @@ void RadioInterface::pullBuffer()
|
||||
num_recv = mRadio->readSamples(convertRecvBuffer,
|
||||
CHUNK,
|
||||
&overrun,
|
||||
readTimestamp + shiftOffset,
|
||||
readTimestamp,
|
||||
&local_underrun);
|
||||
if (num_recv != CHUNK) {
|
||||
LOG(ALERT) << "Receive error " << num_recv;
|
||||
@@ -385,16 +394,11 @@ 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 + mSPSTx * shiftOffset);
|
||||
writeTimestamp);
|
||||
writeTimestamp += num_sent;
|
||||
sendCursor = 0;
|
||||
}
|
||||
|
||||
@@ -56,8 +56,7 @@ 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:
|
||||
@@ -79,7 +78,8 @@ private:
|
||||
public:
|
||||
|
||||
/** start the interface */
|
||||
void start();
|
||||
bool start();
|
||||
bool stop();
|
||||
|
||||
/** intialization */
|
||||
virtual bool init(int type);
|
||||
@@ -95,7 +95,6 @@ public:
|
||||
|
||||
/** check for underrun, resets underrun value */
|
||||
bool isUnderrun();
|
||||
void applyOffset(int offset);
|
||||
|
||||
/** return the receive FIFO */
|
||||
VectorFIFO* receiveFIFO(size_t chan = 0);
|
||||
@@ -103,18 +102,12 @@ 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);
|
||||
|
||||
@@ -128,7 +121,7 @@ public:
|
||||
/** drive reception of GSM bursts */
|
||||
bool driveReceiveRadio();
|
||||
|
||||
void setPowerAttenuation(double atten, size_t chan = 0);
|
||||
int setPowerAttenuation(int atten, size_t chan = 0);
|
||||
|
||||
/** returns the full-scale transmit amplitude **/
|
||||
double fullScaleInputValue();
|
||||
|
||||
@@ -189,7 +189,7 @@ void RadioInterfaceDiversity::pullBuffer()
|
||||
num = mRadio->readSamples(convertRecvBuffer,
|
||||
resamp_outchunk,
|
||||
&overrun,
|
||||
readTimestamp + shiftOffset,
|
||||
readTimestamp,
|
||||
&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 + shiftOffset,
|
||||
readTimestamp,
|
||||
&local_underrun);
|
||||
if (num_recv != (int) resamp_outchunk) {
|
||||
LOG(ALERT) << "Receive error " << num_recv;
|
||||
|
||||
@@ -74,31 +74,25 @@ bool radioVector::setVector(signalVector *vector, size_t chan)
|
||||
return true;
|
||||
}
|
||||
|
||||
avgVector::avgVector(size_t max)
|
||||
: std::vector<float>(0), itr(0)
|
||||
noiseVector::noiseVector(size_t size)
|
||||
: std::vector<float>(size), itr(0)
|
||||
{
|
||||
this->max = max;
|
||||
}
|
||||
|
||||
float avgVector::avg() const
|
||||
float noiseVector::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 avgVector::insert(float val)
|
||||
bool noiseVector::insert(float val)
|
||||
{
|
||||
if (size() < max) {
|
||||
push_back(val);
|
||||
return true;
|
||||
}
|
||||
if (!size())
|
||||
return false;
|
||||
|
||||
if (itr >= this->size())
|
||||
itr = 0;
|
||||
@@ -108,16 +102,6 @@ bool avgVector::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,17 +46,14 @@ private:
|
||||
GSM::Time mTime;
|
||||
};
|
||||
|
||||
class avgVector : std::vector<float> {
|
||||
class noiseVector : std::vector<float> {
|
||||
public:
|
||||
avgVector(size_t size = 0);
|
||||
noiseVector(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> { };
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#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_ */
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "sigProcLib.h"
|
||||
#include "GSMCommon.h"
|
||||
#include "Logger.h"
|
||||
|
||||
extern "C" {
|
||||
#include "convolve.h"
|
||||
@@ -40,6 +41,9 @@ using namespace GSM;
|
||||
#define TABLESIZE 1024
|
||||
#define DELAYFILTS 64
|
||||
|
||||
/* Clipping detection threshold */
|
||||
#define CLIP_THRESH 30000.0f
|
||||
|
||||
/** Lookup tables for trigonometric approximation */
|
||||
float cosTable[TABLESIZE+1]; // add 1 element for wrap around
|
||||
float sinTable[TABLESIZE+1];
|
||||
@@ -78,7 +82,6 @@ struct CorrelationSequence {
|
||||
|
||||
signalVector *sequence;
|
||||
void *buffer;
|
||||
void *history;
|
||||
float toa;
|
||||
complex gain;
|
||||
};
|
||||
@@ -112,7 +115,6 @@ struct PulseSequence {
|
||||
|
||||
CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
|
||||
CorrelationSequence *gRACHSequence = NULL;
|
||||
CorrelationSequence *gSCHSequence = NULL;
|
||||
PulseSequence *GSMPulse = NULL;
|
||||
PulseSequence *GSMPulse1 = NULL;
|
||||
|
||||
@@ -133,7 +135,6 @@ void sigProcLibDestroy()
|
||||
delete GMSKRotation1;
|
||||
delete GMSKReverseRotation1;
|
||||
delete gRACHSequence;
|
||||
delete gSCHSequence;
|
||||
delete GSMPulse;
|
||||
delete GSMPulse1;
|
||||
|
||||
@@ -142,7 +143,6 @@ void sigProcLibDestroy()
|
||||
GMSKReverseRotationN = NULL;
|
||||
GMSKReverseRotation1 = NULL;
|
||||
gRACHSequence = NULL;
|
||||
gSCHSequence = NULL;
|
||||
GSMPulse = NULL;
|
||||
GSMPulse1 = NULL;
|
||||
}
|
||||
@@ -399,10 +399,8 @@ 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;
|
||||
}
|
||||
head = h->size() - start;
|
||||
append = true;
|
||||
}
|
||||
if (start + len > x->size()) {
|
||||
tail = start + len - x->size();
|
||||
@@ -729,7 +727,7 @@ static signalVector *modulateBurstLaurent(const BitVector &bits,
|
||||
c1_itr = c1_burst->begin();
|
||||
|
||||
/* Padded differential start bits */
|
||||
*c0_itr = 2.0 * (0x00 & 0x01) - 1.0;
|
||||
*c0_itr = 2.0 * (0x01 & 0x01) - 1.0;
|
||||
c0_itr += sps;
|
||||
|
||||
/* Main burst bits */
|
||||
@@ -1280,69 +1278,6 @@ 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)
|
||||
{
|
||||
@@ -1350,12 +1285,12 @@ static float computePeakRatio(signalVector *corr,
|
||||
complex *peak;
|
||||
float rms, avg = 0.0;
|
||||
|
||||
peak = corr->begin() + (int) rint(toa);
|
||||
|
||||
/* Check for bogus results */
|
||||
if ((toa < 0.0) || (toa > corr->size()))
|
||||
return 0.0;
|
||||
|
||||
peak = corr->begin() + (int) rint(toa);
|
||||
|
||||
for (int i = 2 * sps; i <= 5 * sps; i++) {
|
||||
if (peak - i >= corr->begin()) {
|
||||
avg += (peak - i)->norm2();
|
||||
@@ -1438,6 +1373,74 @@ static int detectBurst(signalVector &burst,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static float maxAmplitude(signalVector &burst)
|
||||
{
|
||||
float max = 0.0;
|
||||
for (size_t i = 0; i < burst.size(); i++) {
|
||||
if (fabs(burst[i].real()) > max)
|
||||
max = fabs(burst[i].real());
|
||||
if (fabs(burst[i].imag()) > max)
|
||||
max = fabs(burst[i].imag());
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
/*
|
||||
* RACH/Normal burst detection with clipping detection
|
||||
*
|
||||
* Correlation window parameters:
|
||||
* target: Tail bits + burst length
|
||||
* head: Search symbols before target
|
||||
* tail: Search symbols after target
|
||||
*/
|
||||
int detectGeneralBurst(signalVector &rxBurst,
|
||||
float thresh,
|
||||
int sps,
|
||||
complex &,
|
||||
float &toa,
|
||||
int target, int head, int tail,
|
||||
CorrelationSequence *sync)
|
||||
{
|
||||
int rc, start, len;
|
||||
bool clipping = false;
|
||||
signalVector *corr;
|
||||
|
||||
if ((sps != 1) && (sps != 4))
|
||||
return -SIGERR_UNSUPPORTED;
|
||||
|
||||
// Detect potential clipping
|
||||
// We still may be able to demod the burst, so we'll give it a try
|
||||
// and only report clipping if we can't demod.
|
||||
float maxAmpl = maxAmplitude(rxBurst);
|
||||
if (maxAmpl > CLIP_THRESH) {
|
||||
LOG(DEBUG) << "max burst amplitude: " << maxAmpl << " is above the clipping threshold: " << CLIP_THRESH << std::endl;
|
||||
clipping = true;
|
||||
}
|
||||
|
||||
start = (target - head) * sps - 1;
|
||||
len = (head + tail) * sps;
|
||||
corr = new signalVector(len);
|
||||
|
||||
rc = detectBurst(rxBurst, *corr, sync,
|
||||
thresh, sps, &, &toa, start, len);
|
||||
delete corr;
|
||||
|
||||
if (rc < 0) {
|
||||
return -SIGERR_INTERNAL;
|
||||
} else if (!rc) {
|
||||
amp = 0.0f;
|
||||
toa = 0.0f;
|
||||
return clipping?-SIGERR_CLIP:SIGERR_NONE;
|
||||
}
|
||||
|
||||
/* Subtract forward search bits from delay */
|
||||
toa -= head * sps;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RACH burst detection
|
||||
*
|
||||
@@ -1447,115 +1450,23 @@ static int detectBurst(signalVector &burst,
|
||||
* tail: Search 10 symbols after target
|
||||
*/
|
||||
int detectRACHBurst(signalVector &rxBurst,
|
||||
float thresh,
|
||||
int sps,
|
||||
complex *amp,
|
||||
float *toa)
|
||||
float thresh,
|
||||
int sps,
|
||||
complex &,
|
||||
float &toa)
|
||||
{
|
||||
int rc, start, target, head, tail, len;
|
||||
float _toa;
|
||||
complex _amp;
|
||||
signalVector *corr;
|
||||
int rc, target, head, tail;
|
||||
CorrelationSequence *sync;
|
||||
|
||||
if ((sps != 1) && (sps != 4))
|
||||
return -1;
|
||||
|
||||
target = 8 + 40;
|
||||
head = 4;
|
||||
tail = 10;
|
||||
|
||||
start = (target - head) * sps - 1;
|
||||
len = (head + tail) * sps;
|
||||
sync = gRACHSequence;
|
||||
corr = new signalVector(len);
|
||||
|
||||
rc = detectBurst(rxBurst, *corr, sync,
|
||||
thresh, sps, &_amp, &_toa, start, len);
|
||||
delete corr;
|
||||
rc = detectGeneralBurst(rxBurst, thresh, sps, amp, toa,
|
||||
target, head, tail, sync);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1567,57 +1478,32 @@ int detectSCHBurst(signalVector &burst,
|
||||
* tail: Search 4 symbols + maximum expected delay
|
||||
*/
|
||||
int analyzeTrafficBurst(signalVector &rxBurst, unsigned tsc, float thresh,
|
||||
int sps, complex *amp, float *toa, unsigned max_toa,
|
||||
int sps, complex &, float &toa, unsigned max_toa,
|
||||
bool chan_req, signalVector **chan, float *chan_offset)
|
||||
{
|
||||
int rc, start, target, head, tail, len;
|
||||
complex _amp;
|
||||
float _toa;
|
||||
signalVector *corr;
|
||||
int rc, target, head, tail;
|
||||
CorrelationSequence *sync;
|
||||
|
||||
if ((tsc < 0) || (tsc > 7) || ((sps != 1) && (sps != 4)))
|
||||
return -1;
|
||||
if ((tsc < 0) || (tsc > 7))
|
||||
return -SIGERR_UNSUPPORTED;
|
||||
|
||||
target = 3 + 58 + 16 + 5;
|
||||
head = 4;
|
||||
tail = 4 + max_toa;
|
||||
|
||||
start = (target - head) * sps - 1;
|
||||
len = (head + tail) * sps;
|
||||
sync = gMidambles[tsc];
|
||||
corr = new signalVector(len);
|
||||
|
||||
rc = detectBurst(rxBurst, *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 */
|
||||
_toa -= head * sps;
|
||||
if (toa)
|
||||
*toa = _toa;
|
||||
if (amp)
|
||||
*amp = _amp;
|
||||
rc = detectGeneralBurst(rxBurst, thresh, sps, amp, toa,
|
||||
target, head, tail, sync);
|
||||
|
||||
/* Equalization not currently supported */
|
||||
if (chan_req) {
|
||||
if (rc > 0 && chan_req) {
|
||||
*chan = new signalVector(6 * sps);
|
||||
|
||||
if (chan_offset)
|
||||
*chan_offset = 0.0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
signalVector *decimateVector(signalVector &wVector, size_t factor)
|
||||
@@ -1851,11 +1737,6 @@ bool sigProcLibSetup(int sps)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!generateSCHSequence(1)) {
|
||||
sigProcLibDestroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
generateDelayFilters();
|
||||
|
||||
return true;
|
||||
|
||||
@@ -28,6 +28,14 @@ enum ConvType {
|
||||
UNDEFINED,
|
||||
};
|
||||
|
||||
enum signalError {
|
||||
SIGERR_NONE,
|
||||
SIGERR_BOUNDS,
|
||||
SIGERR_CLIP,
|
||||
SIGERR_UNSUPPORTED,
|
||||
SIGERR_INTERNAL,
|
||||
};
|
||||
|
||||
/** Convert a linear number to a dB value */
|
||||
float dB(float x);
|
||||
|
||||
@@ -158,7 +166,6 @@ 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.
|
||||
@@ -185,19 +192,8 @@ bool energyDetect(signalVector &rxBurst,
|
||||
int detectRACHBurst(signalVector &rxBurst,
|
||||
float detectThreshold,
|
||||
int sps,
|
||||
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);
|
||||
complex &litude,
|
||||
float &TOA);
|
||||
|
||||
/**
|
||||
Normal burst correlator, detector, channel estimator.
|
||||
@@ -214,15 +210,15 @@ int detectSCHBurst(signalVector &rxBurst,
|
||||
@return positive if threshold value is reached, negative on error, zero otherwise
|
||||
*/
|
||||
int analyzeTrafficBurst(signalVector &rxBurst,
|
||||
unsigned TSC,
|
||||
float detectThreshold,
|
||||
int sps,
|
||||
complex *amplitude,
|
||||
float *TOA,
|
||||
unsigned TSC,
|
||||
float detectThreshold,
|
||||
int sps,
|
||||
complex &litude,
|
||||
float &TOA,
|
||||
unsigned maxTOA,
|
||||
bool requestChannel = false,
|
||||
signalVector** channelResponse = NULL,
|
||||
float *channelResponseOffset = NULL);
|
||||
signalVector** channelResponse = NULL,
|
||||
float *channelResponseOffset = NULL);
|
||||
|
||||
/**
|
||||
Decimate a vector.
|
||||
|
||||
14
configure.ac
14
configure.ac
@@ -29,7 +29,7 @@ AC_CANONICAL_BUILD
|
||||
AC_CANONICAL_HOST
|
||||
AC_CANONICAL_TARGET
|
||||
|
||||
AM_INIT_AUTOMAKE
|
||||
AM_INIT_AUTOMAKE([subdir-objects])
|
||||
|
||||
dnl Linux kernel KBuild style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
@@ -58,9 +58,6 @@ 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])
|
||||
@@ -81,6 +78,11 @@ AC_ARG_WITH(neon-vfpv4, [
|
||||
[enable ARM NEON FMA support])
|
||||
])
|
||||
|
||||
AC_ARG_WITH(sse, [
|
||||
AS_HELP_STRING([--with-sse],
|
||||
[enable x86 SSE support (default)])
|
||||
])
|
||||
|
||||
AS_IF([test "x$with_neon" = "xyes"], [
|
||||
AC_DEFINE(HAVE_NEON, 1, Support ARM NEON)
|
||||
])
|
||||
@@ -104,7 +106,9 @@ AS_IF([test "x$with_singledb" = "xyes"], [
|
||||
])
|
||||
|
||||
# Find and define supported SIMD extensions
|
||||
AX_EXT
|
||||
AS_IF([test "x$with_sse" != "xno"], [
|
||||
AX_EXT
|
||||
])
|
||||
|
||||
AM_CONDITIONAL(USRP1, [test "x$with_usrp1" = "xyes"])
|
||||
AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
|
||||
|
||||
3
utils/clockdump.sh
Executable file
3
utils/clockdump.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
sudo tcpdump -i lo0 -A udp port 5700
|
||||
|
||||
Reference in New Issue
Block a user