import kalibrate 0.4.1

taken from:
http://thre.at/kalibrate/kal-v0.4.1.tar.bz2

Signed-off-by: Steve Markgraf <steve@steve-m.de>
This commit is contained in:
Steve Markgraf 2012-10-08 03:59:57 +02:00
commit c58471bb42
26 changed files with 2818 additions and 0 deletions

3
AUTHORS Normal file
View file

@ -0,0 +1,3 @@
Joshua Lackey <jl@thre.at>
Alexander Chemeris <alexander.chemeris@gmail.com>

24
COPYING Normal file
View file

@ -0,0 +1,24 @@
Copyright (c) 2010, Joshua Lackey
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

0
ChangeLog Normal file
View file

1
Makefile.am Normal file
View file

@ -0,0 +1 @@
SUBDIRS = src

0
NEWS Normal file
View file

7
README Normal file
View file

@ -0,0 +1,7 @@
Kalibrate, or kal, can scan for GSM base stations in a given frequency band and
can use those GSM base stations to calculate the local oscillator frequency
offset.
See http://thre.at/kalibrate
Copyright (c) 2010, Joshua Lackey (jl@thre.at)

8
bootstrap Executable file
View file

@ -0,0 +1,8 @@
rm -rf config.cache autom4te*.cache
#autoreconf --install
libtoolize --automake
aclocal
autoconf
autoheader
automake --add-missing

43
configure.ac Normal file
View file

@ -0,0 +1,43 @@
AC_PREREQ([2.64])
AC_INIT([kalibrate], [0.4.1], [jl@thre.at], [kal])
AC_CONFIG_SRCDIR([src/fcch_detector.cc])
AM_INIT_AUTOMAKE([-Wall -Werror])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CXX
AC_PROG_CC
AC_PROG_LN_S
AC_PROG_RANLIB
# Checks for header files.
AC_CHECK_HEADERS([stdlib.h string.h sys/time.h unistd.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_HEADER_STDBOOL
AC_C_INLINE
# Checks for library functions.
AC_FUNC_STRTOD
AC_CHECK_FUNCS([floor getpagesize memset sqrt strtoul strtol])
# Checks for libraries.
PKG_CHECK_MODULES(FFTW3, fftw3 >= 3.0)
AC_SUBST(FFTW3_LIBS)
AC_SUBST(FFTW3_CFLAGS)
PKG_CHECK_MODULES(USRP, usrp >= 3.3)
AC_SUBST(USRP_LIBS)
AC_SUBST(USRP_CFLAGS)
# OSX doesn't support System V shared memory
AC_CANONICAL_HOST
case "$host_os" in
darwin*)
AC_DEFINE([D_HOST_OSX], [], [building for OSX])
;;
esac
AC_CONFIG_FILES([Makefile
src/Makefile])
AC_OUTPUT

23
src/Makefile.am Normal file
View file

@ -0,0 +1,23 @@
bin_PROGRAMS = kal
kal_SOURCES = \
arfcn_freq.cc \
c0_detect.cc \
circular_buffer.cc \
fcch_detector.cc \
kal.cc \
offset.cc \
usrp_source.cc \
util.cc\
arfcn_freq.h \
c0_detect.h \
circular_buffer.h \
fcch_detector.h \
offset.h \
usrp_complex.h \
usrp_source.h \
util.h\
version.h
kal_CXXFLAGS = $(FFTW3_CFLAGS) $(USRP_CFLAGS)
kal_LDADD = $(FFTW3_LIBS) $(USRP_LIBS)

291
src/arfcn_freq.cc Normal file
View file

@ -0,0 +1,291 @@
/*
* Copyright (c) 2010, Joshua Lackey
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "arfcn_freq.h"
const char *bi_to_str(int bi) {
switch(bi) {
case GSM_850:
return "GSM-850";
case GSM_900:
return "GSM-900";
case GSM_E_900:
return "E-GSM-900";
case DCS_1800:
return "DCS-1800";
case PCS_1900:
return "PCS-1900";
default:
return "unknown band indicator";
}
}
int str_to_bi(char *s) {
if(!strcmp(s, "GSM850") || !strcmp(s, "GSM-850") || !strcmp(s, "850"))
return GSM_850;
if(!strcmp(s, "GSM900") || !strcmp(s, "GSM-900") || !strcmp(s, "900"))
return GSM_900;
if(!strcmp(s, "EGSM") || !strcmp(s, "E-GSM") || !strcmp(s, "EGSM900") ||
!strcmp(s, "E-GSM900") || !strcmp(s, "E-GSM-900"))
return GSM_E_900;
if(!strcmp(s, "DCS") || !strcmp(s, "DCS1800") ||
!strcmp(s, "DCS-1800") || !strcmp(s, "1800"))
return DCS_1800;
if(!strcmp(s, "PCS") || !strcmp(s, "PCS1900") ||
!strcmp(s, "PCS-1900") || !strcmp(s, "1900"))
return PCS_1900;
return -1;
}
double arfcn_to_freq(int n, int *bi) {
if((128 <= n) && (n <= 251)) {
if(bi)
*bi = GSM_850;
return 824.2e6 + 0.2e6 * (n - 128) + 45.0e6;
}
if((1 <= n) && (n <= 124)) {
if(bi)
*bi = GSM_900;
return 890.0e6 + 0.2e6 * n + 45.0e6;
}
if(n == 0) {
if(bi)
*bi = GSM_E_900;
return 935e6;
}
if((975 <= n) && (n <= 1023)) {
if(bi)
*bi = GSM_E_900;
return 890.0e6 + 0.2e6 * (n - 1024) + 45.0e6;
}
if((512 <= n) && (n <= 810)) {
if(!bi) {
fprintf(stderr, "error: ambiguous arfcn: %d\n", n);
return -1.0;
}
if(*bi == DCS_1800)
return 1710.2e6 + 0.2e6 * (n - 512) + 95.0e6;
if(*bi == PCS_1900)
return 1850.2e6 + 0.2e6 * (n - 512) + 80.0e6;
fprintf(stderr, "error: bad (arfcn, band indicator) pair: "
"(%d, %s)\n", n, bi_to_str(*bi));
return -1.0;
}
if((811 <= n) && (n <= 885)) {
if(bi)
*bi = DCS_1800;
return 1710.2e6 + 0.2e6 * (n - 512) + 95.0e6;
}
fprintf(stderr, "error: bad arfcn: %d\n", n);
return -1.0;
}
int freq_to_arfcn(double freq, int *bi) {
if((869.2e6 <= freq) && (freq <= 893.8e6)) {
if(bi)
*bi = GSM_850;
return (int)((freq - 869.2e6) / 0.2e6) + 128;
}
if((935.2e6 <= freq) && (freq <= 959.8e6)) {
if(bi)
*bi = GSM_900;
return (int)((freq - 935e6) / 0.2e6);
}
if(935.0e6 == freq) {
if(bi)
*bi = GSM_E_900;
return 0;
}
if((925.2e6 <= freq) && (freq <= 934.8e6)) {
if(bi)
*bi = GSM_E_900;
return (int)((freq - 935e6) / 0.2e6) + 1024;
}
if((1805.2e6 <= freq) && (freq <= 1879.8e6)) {
if(bi)
*bi = DCS_1800;
return (int)((freq - 1805.2e6) / 0.2e6) + 512;
}
if((1930.2e6 <= freq) && (freq <= 1989.8e6)) {
if(bi)
*bi = PCS_1900;
return (int)((freq - 1930.2e6) / 0.2e6) + 512;
}
fprintf(stderr, "error: bad frequency: %lf\n", freq);
return -1;
}
int first_chan(int bi) {
switch(bi) {
case GSM_850:
return 128;
case GSM_900:
return 1;
case GSM_E_900:
return 0;
case DCS_1800:
return 512;
case PCS_1900:
return 512;
default:
return -1;
}
return -1;
}
int next_chan_loop(int chan, int bi) {
switch(bi) {
case GSM_850:
if((128 <= chan) && (chan < 251))
return chan + 1;
if(chan == 251)
return 128;
return -1;
case GSM_900:
if((1 <= chan) && (chan < 124))
return chan + 1;
if(chan == 124)
return 1;
return -1;
case GSM_E_900:
if((0 <= chan) && (chan < 124))
return chan + 1;
if(chan == 124)
return 975;
if((975 <= chan) && (chan < 1023))
return chan + 1;
if(chan == 1023)
return 0;
return -1;
case DCS_1800:
if((512 <= chan) && (chan < 885))
return chan + 1;
if(chan == 885)
return 512;
return -1;
case PCS_1900:
if((512 <= chan) && (chan < 810))
return chan + 1;
if(chan == 810)
return 512;
return -1;
default:
return -1;
}
return -1;
}
int next_chan(int chan, int bi) {
switch(bi) {
case GSM_850:
if((128 <= chan) && (chan < 251))
return chan + 1;
return -1;
case GSM_900:
if((1 <= chan) && (chan < 124))
return chan + 1;
return -1;
case GSM_E_900:
if((0 <= chan) && (chan < 124))
return chan + 1;
if(chan == 124)
return 975;
if((975 <= chan) && (chan < 1023))
return chan + 1;
return -1;
case DCS_1800:
if((512 <= chan) && (chan < 885))
return chan + 1;
return -1;
case PCS_1900:
if((512 <= chan) && (chan < 810))
return chan + 1;
return -1;
default:
return -1;
}
return -1;
}

42
src/arfcn_freq.h Normal file
View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2010, Joshua Lackey
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
enum {
BI_NOT_DEFINED,
GSM_850,
GSM_900,
GSM_E_900,
DCS_1800,
PCS_1900
};
const char *bi_to_str(int bi);
int str_to_bi(char *s);
double arfcn_to_freq(int n, int *bi = 0);
int freq_to_arfcn(double freq, int *bi = 0);
int first_chan(int bi);
int next_chan(int chan, int bi);

172
src/c0_detect.cc Normal file
View file

@ -0,0 +1,172 @@
/*
* Copyright (c) 2010, Joshua Lackey
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "usrp_source.h"
#include "circular_buffer.h"
#include "fcch_detector.h"
#include "arfcn_freq.h"
#include "util.h"
extern int g_verbosity;
static const float ERROR_DETECT_OFFSET_MAX = 40e3;
static double vectornorm2(const complex *v, const unsigned int len) {
unsigned int i;
double e = 0.0;
for(i = 0; i < len; i++)
e += norm(v[i]);
return e;
}
int c0_detect(usrp_source *u, int bi) {
static const double GSM_RATE = 1625000.0 / 6.0;
static const unsigned int NOTFOUND_MAX = 10;
int i, chan_count;
unsigned int overruns, b_len, frames_len, found_count, notfound_count, r;
float offset, spower[BUFSIZ];
double freq, sps, n, power[BUFSIZ], sum = 0, a;
complex *b;
circular_buffer *ub;
fcch_detector *l = new fcch_detector(u->sample_rate());
if(bi == BI_NOT_DEFINED) {
fprintf(stderr, "error: c0_detect: band not defined\n");
return -1;
}
sps = u->sample_rate() / GSM_RATE;
frames_len = (unsigned int)ceil((12 * 8 * 156.25 + 156.25) * sps);
ub = u->get_buffer();
// first, we calculate the power in each channel
if(g_verbosity > 2) {
fprintf(stderr, "calculate power in each channel:\n");
}
u->start();
u->flush();
for(i = first_chan(bi); i > 0; i = next_chan(i, bi)) {
freq = arfcn_to_freq(i, &bi);
if(!u->tune(freq)) {
fprintf(stderr, "error: usrp_source::tune\n");
return -1;
}
do {
u->flush();
if(u->fill(frames_len, &overruns)) {
fprintf(stderr, "error: usrp_source::fill\n");
return -1;
}
} while(overruns);
b = (complex *)ub->peek(&b_len);
n = sqrt(vectornorm2(b, frames_len));
power[i] = n;
if(g_verbosity > 2) {
fprintf(stderr, "\tchan %d (%.1fMHz):\tpower: %lf\n",
i, freq / 1e6, n);
}
}
/*
* We want to use the average to determine which channels have
* power, and hence a possibility of being channel 0 on a BTS.
* However, some channels in the band can be extremely noisy. (E.g.,
* CDMA traffic in GSM-850.) Hence we won't consider the noisiest
* channels when we construct the average.
*/
chan_count = 0;
for(i = first_chan(bi); i > 0; i = next_chan(i, bi)) {
spower[chan_count++] = power[i];
}
sort(spower, chan_count);
// average the lowest %60
a = avg(spower, chan_count - 4 * chan_count / 10, 0);
if(g_verbosity > 0) {
fprintf(stderr, "channel detect threshold: %lf\n", a);
}
// then we look for fcch bursts
printf("%s:\n", bi_to_str(bi));
found_count = 0;
notfound_count = 0;
sum = 0;
i = first_chan(bi);
do {
if(power[i] <= a) {
i = next_chan(i, bi);
continue;
}
freq = arfcn_to_freq(i, &bi);
if(!u->tune(freq)) {
fprintf(stderr, "error: usrp_source::tune\n");
return -1;
}
do {
u->flush();
if(u->fill(frames_len, &overruns)) {
fprintf(stderr, "error: usrp_source::fill\n");
return -1;
}
} while(overruns);
b = (complex *)ub->peek(&b_len);
r = l->scan(b, b_len, &offset, 0);
if(r && (fabsf(offset - GSM_RATE / 4) < ERROR_DETECT_OFFSET_MAX)) {
// found
printf("\tchan: %d (%.1fMHz ", i, freq / 1e6);
display_freq(offset - GSM_RATE / 4);
printf(")\tpower: %6.2lf\n", power[i]);
notfound_count = 0;
i = next_chan(i, bi);
} else {
// not found
notfound_count += 1;
if(notfound_count >= NOTFOUND_MAX) {
notfound_count = 0;
i = next_chan(i, bi);
}
}
} while(i > 0);
return 0;
}

28
src/c0_detect.h Normal file
View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2010, Joshua Lackey
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
int c0_detect(usrp_source *u, int bi);

487
src/circular_buffer.cc Normal file
View file

@ -0,0 +1,487 @@
/*
* Copyright (c) 2010, Joshua Lackey
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <stdexcept>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef D_HOST_OSX
#include <sys/shm.h>
#else
#include <sys/mman.h>
#include <fcntl.h>
#endif /* !D_HOST_OSX */
#include "circular_buffer.h"
#ifndef D_HOST_OSX
circular_buffer::circular_buffer(const unsigned int buf_len,
const unsigned int item_size, const unsigned int overwrite) {
int shm_id_temp, shm_id_guard, shm_id_buf;
void *base;
if(!buf_len)
throw std::runtime_error("circular_buffer: buffer len is 0");
if(!item_size)
throw std::runtime_error("circular_buffer: item size is 0");
// calculate buffer size
m_item_size = item_size;
m_buf_size = item_size * buf_len;
m_pagesize = getpagesize();
if(m_buf_size % m_pagesize)
m_buf_size = (m_buf_size + m_pagesize) & ~(m_pagesize - 1);
m_buf_len = m_buf_size / item_size;
// create an address-range that can contain everything
if((shm_id_temp = shmget(IPC_PRIVATE, 2 * m_pagesize + 2 * m_buf_size,
IPC_CREAT | S_IRUSR | S_IWUSR)) == -1) {
perror("shmget");
throw std::runtime_error("circular_buffer: shmget");
}
// create a read-only guard page
if((shm_id_guard = shmget(IPC_PRIVATE, m_pagesize,
IPC_CREAT | S_IRUSR)) == -1) {
shmctl(shm_id_temp, IPC_RMID, 0);
perror("shmget");
throw std::runtime_error("circular_buffer: shmget");
}
// create the data buffer
if((shm_id_buf = shmget(IPC_PRIVATE, m_buf_size, IPC_CREAT | S_IRUSR |
S_IWUSR)) == -1) {
perror("shmget");
shmctl(shm_id_temp, IPC_RMID, 0);
shmctl(shm_id_guard, IPC_RMID, 0);
throw std::runtime_error("circular_buffer: shmget");
}
// attach temporary memory to get an address-range
if((base = shmat(shm_id_temp, 0, 0)) == (void *)(-1)) {
perror("shmat");
shmctl(shm_id_temp, IPC_RMID, 0);
shmctl(shm_id_guard, IPC_RMID, 0);
shmctl(shm_id_buf, IPC_RMID, 0);
throw std::runtime_error("circular_buffer: shmat");
}
// remove the temporary memory id
shmctl(shm_id_temp, IPC_RMID, 0);
// detach and free the temporary memory
shmdt(base);
// race condition here
// map first copy of guard page with previous address
if(shmat(shm_id_guard, base, SHM_RDONLY) == (void *)(-1)) {
perror("shmat");
shmctl(shm_id_guard, IPC_RMID, 0);
shmctl(shm_id_buf, IPC_RMID, 0);
throw std::runtime_error("circular_buffer: shmat");
}
// map first copy of the buffer
if(shmat(shm_id_buf, (char *)base + m_pagesize, 0) == (void *)(-1)) {
perror("shmat");
shmctl(shm_id_guard, IPC_RMID, 0);
shmctl(shm_id_buf, IPC_RMID, 0);
shmdt(base);
throw std::runtime_error("circular_buffer: shmat");
}
// map second copy of the buffer
if(shmat(shm_id_buf, (char *)base + m_pagesize + m_buf_size, 0) ==
(void *)(-1)) {
perror("shmat");
shmctl(shm_id_guard, IPC_RMID, 0);
shmctl(shm_id_buf, IPC_RMID, 0);
shmdt((char *)base + m_pagesize);
shmdt(base);
throw std::runtime_error("circular_buffer: shmat");
}
// map second copy of guard page
if(shmat(shm_id_guard, (char *)base + m_pagesize + 2 * m_buf_size,
SHM_RDONLY) == (void *)(-1)) {
perror("shmat");
shmctl(shm_id_guard, IPC_RMID, 0);
shmctl(shm_id_buf, IPC_RMID, 0);
shmdt((char *)base + m_pagesize + m_buf_size);
shmdt((char *)base + m_pagesize);
shmdt((char *)base);
throw std::runtime_error("circular_buffer: shmat");
}
// remove the id for the guard and buffer, we don't need them anymore
shmctl(shm_id_guard, IPC_RMID, 0);
shmctl(shm_id_buf, IPC_RMID, 0);
// save the base address for detach later
m_base = base;
// save a pointer to the data
m_buf = (char *)base + m_pagesize;
m_r = m_w = 0;
m_read = m_written = 0;
m_item_size = item_size;
m_overwrite = overwrite;
pthread_mutex_init(&m_mutex, 0);
}
circular_buffer::~circular_buffer() {
shmdt((char *)m_base + m_pagesize + 2 * m_buf_size);
shmdt((char *)m_base + m_pagesize + m_buf_size);
shmdt((char *)m_base + m_pagesize);
shmdt((char *)m_base);
}
#else /* !D_HOST_OSX */
/*
* OSX doesn't support System V shared memory. Using GNU Radio as an example,
* we'll implement this for OSX using Posix shared memory. I'm not exactly
* sure why GNU Radio prefers the System V usage, but I seem to recall there
* was a reason.
*/
circular_buffer::circular_buffer(const unsigned int buf_len,
const unsigned int item_size, const unsigned int overwrite) {
int shm_fd;
char shm_name[255]; // XXX should be NAME_MAX
void *base;
if(!buf_len)
throw std::runtime_error("circular_buffer: buffer len is 0");
if(!item_size)
throw std::runtime_error("circular_buffer: item size is 0");
// calculate buffer size
m_item_size = item_size;
m_buf_size = item_size * buf_len;
m_pagesize = getpagesize();
if(m_buf_size % m_pagesize)
m_buf_size = (m_buf_size + m_pagesize) & ~(m_pagesize - 1);
m_buf_len = m_buf_size / item_size;
// create unique-ish name
snprintf(shm_name, sizeof(shm_name), "/kalibrate-%d", getpid());
// create a Posix shared memory object
if((shm_fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) == -1) {
perror("shm_open");
throw std::runtime_error("circular_buffer: shm_open");
}
// create enough space to hold everything
if(ftruncate(shm_fd, 2 * m_pagesize + 2 * m_buf_size) == -1) {
perror("ftruncate");
close(shm_fd);
shm_unlink(shm_name);
throw std::runtime_error("circular_buffer: ftruncate");
}
// get an address for the buffer
if((base = mmap(0, 2 * m_pagesize + 2 * m_buf_size, PROT_NONE, MAP_SHARED, shm_fd, 0)) == MAP_FAILED) {
perror("mmap");
close(shm_fd);
shm_unlink(shm_name);
throw std::runtime_error("circular_buffer: mmap (base)");
}
// unmap everything but the first guard page
if(munmap((char *)base + m_pagesize, m_pagesize + 2 * m_buf_size) == -1) {
perror("munmap");
close(shm_fd);
shm_unlink(shm_name);
throw std::runtime_error("circular_buffer: munmap");
}
// race condition
// map first copy of the buffer
if(mmap((char *)base + m_pagesize, m_buf_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, shm_fd, m_pagesize) == MAP_FAILED) {
perror("mmap");
munmap(base, 2 * m_pagesize + 2 * m_buf_size);
close(shm_fd);
shm_unlink(shm_name);
throw std::runtime_error("circular_buffer: mmap (buf 1)");
}
// map second copy of the buffer
if(mmap((char *)base + m_pagesize + m_buf_size, m_buf_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, shm_fd, m_pagesize) == MAP_FAILED) {
perror("mmap");
munmap(base, 2 * m_pagesize + 2 * m_buf_size);
close(shm_fd);
shm_unlink(shm_name);
throw std::runtime_error("circular_buffer: mmap (buf 2)");
}
// map second copy of the guard page
if(mmap((char *)base + m_pagesize + 2 * m_buf_size, m_pagesize, PROT_NONE, MAP_SHARED | MAP_FIXED, shm_fd, 0) == MAP_FAILED) {
perror("mmap");
munmap(base, 2 * m_pagesize + 2 * m_buf_size);
close(shm_fd);
shm_unlink(shm_name);
throw std::runtime_error("circular_buffer: mmap (guard)");
}
// both the file and name are unnecessary now
close(shm_fd);
shm_unlink(shm_name);
// save the base address for unmap later
m_base = base;
// save a pointer to the data
m_buf = (char *)base + m_pagesize;
m_r = m_w = 0;
m_read = m_written = 0;
m_item_size = item_size;
m_overwrite = overwrite;
pthread_mutex_init(&m_mutex, 0);
}
circular_buffer::~circular_buffer() {
munmap(m_base, 2 * m_pagesize + 2 * m_buf_size);
}
#endif /* !D_HOST_OSX */
/*
* The amount to read can only grow unless someone calls read after this is
* called. No real good way to tie the two together.
*/
unsigned int circular_buffer::data_available() {
unsigned int amt;
pthread_mutex_lock(&m_mutex);
amt = m_written - m_read; // item_size
pthread_mutex_unlock(&m_mutex);
return amt;
}
unsigned int circular_buffer::space_available() {
unsigned int amt;
pthread_mutex_lock(&m_mutex);
amt = m_buf_len - (m_written - m_read);
pthread_mutex_unlock(&m_mutex);
return amt;
}
#ifndef MIN
#define MIN(a, b) ((a)<(b)?(a):(b))
#endif /* !MIN */
/*
* m_buf_size is in terms of bytes
* m_r and m_w are offsets in bytes
* m_buf_len is in terms of m_item_size
* buf_len is in terms of m_item_size
* len, m_written, and m_read are all in terms of m_item_size
*/
unsigned int circular_buffer::read(void *buf, const unsigned int buf_len) {
unsigned int len;
pthread_mutex_lock(&m_mutex);
len = MIN(buf_len, m_written - m_read);
memcpy(buf, (char *)m_buf + m_r, len * m_item_size);
m_read += len;
if(m_read == m_written) {
m_r = m_w = 0;
m_read = m_written = 0;
} else
m_r = (m_r + len * m_item_size) % m_buf_size;
pthread_mutex_unlock(&m_mutex);
return len;
}
/*
* warning:
*
* Don't use read() while you are peek()'ing. write() should be
* okay unless you have an overwrite buffer.
*/
void *circular_buffer::peek(unsigned int *buf_len) {
unsigned int len;
void *p;
pthread_mutex_lock(&m_mutex);
len = m_written - m_read;
p = (char *)m_buf + m_r;
pthread_mutex_unlock(&m_mutex);
if(buf_len)
*buf_len = len;
return p;
}
void *circular_buffer::poke(unsigned int *buf_len) {
unsigned int len;
void *p;
pthread_mutex_lock(&m_mutex);
len = m_buf_len - (m_written - m_read);
p = (char *)m_buf + m_w;
pthread_mutex_unlock(&m_mutex);
if(buf_len)
*buf_len = len;
return p;
}
unsigned int circular_buffer::purge(const unsigned int buf_len) {
unsigned int len;
pthread_mutex_lock(&m_mutex);
len = MIN(buf_len, m_written - m_read);
m_read += len;
if(m_read == m_written) {
m_r = m_w = 0;
m_read = m_written = 0;
} else
m_r = (m_r + len * m_item_size) % m_buf_size;
pthread_mutex_unlock(&m_mutex);
return len;
}
unsigned int circular_buffer::write(const void *buf,
const unsigned int buf_len) {
unsigned int len, buf_off = 0;
pthread_mutex_lock(&m_mutex);
if(m_overwrite) {
if(buf_len > m_buf_len) {
buf_off = buf_len - m_buf_len;
len = m_buf_len;
} else
len = buf_len;
} else
len = MIN(buf_len, m_buf_len - (m_written - m_read));
memcpy((char *)m_buf + m_w, (char *)buf + buf_off * m_item_size,
len * m_item_size);
m_written += len;
m_w = (m_w + len * m_item_size) % m_buf_size;
if(m_written > m_buf_len + m_read) {
m_read = m_written - m_buf_len;
m_r = m_w;
}
pthread_mutex_unlock(&m_mutex);
return len;
}
void circular_buffer::wrote(unsigned int len) {
pthread_mutex_lock(&m_mutex);
m_written += len;
m_w = (m_w + len * m_item_size) % m_buf_size;
pthread_mutex_unlock(&m_mutex);
}
void circular_buffer::flush() {
pthread_mutex_lock(&m_mutex);
m_read = m_written = 0;
m_r = m_w = 0;
pthread_mutex_unlock(&m_mutex);
}
void circular_buffer::flush_nolock() {
m_read = m_written = 0;
m_r = m_w = 0;
}
void circular_buffer::lock() {
pthread_mutex_lock(&m_mutex);
}
void circular_buffer::unlock() {
pthread_mutex_unlock(&m_mutex);
}
unsigned int circular_buffer::buf_len() {
return m_buf_len;
}

79
src/circular_buffer.h Normal file
View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2010, Joshua Lackey
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* circular_buffer
*
* This class is based heavily on the GNU Radio circular buffer. While this
* class was written from scratch and contains ideas not present in the GNU
* Radio implementation, the GNU Radio circular buffers were used as a
* reference while developing this class.
*
* This is more a warning that the above BSD-style license may not be the only
* copyright that applies.
*/
#pragma once
/*
* XXX If read doesn't catch up with write before 2**64 bytes are written, this
* will break.
*/
#include <pthread.h>
class circular_buffer {
public:
circular_buffer(const unsigned int buf_len, const unsigned int item_size = 1, const unsigned int overwrite = 0);
~circular_buffer();
unsigned int read(void *buf, const unsigned int buf_len);
void *peek(unsigned int *buf_len);
unsigned int purge(const unsigned int buf_len);
void *poke(unsigned int *buf_len);
void wrote(unsigned int len);
unsigned int write(const void *buf, const unsigned int buf_len);
unsigned int data_available();
unsigned int space_available();
void flush();
void flush_nolock();
void lock();
void unlock();
unsigned int buf_len();
private:
void *m_buf;
unsigned int m_buf_len, m_buf_size, m_r, m_w, m_item_size;
unsigned long long m_read, m_written;
unsigned int m_overwrite;
void *m_base;
unsigned int m_pagesize;
pthread_mutex_t m_mutex;
};

526
src/fcch_detector.cc Normal file
View file

@ -0,0 +1,526 @@
/*
* Copyright (c) 2010, Joshua Lackey
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This is based on the algorithm found in the paper,
*
* Varma, G. Narendra, Usha Sahu, and G. Prabhu Charan. "Robust
* Frequency Burst Detection Algorithm for GSM / GPRS."
*
* The algorithm uses an adaptive filter to calculate the error difference from
* a pure tone. When the error goes low, the tone is detected. When it goes
* back high, the scan function returns and indicates the number of samples the
* error was low.
*
* The following code is an original work and the above BSD-license should
* apply. However, the algorithm itself may be patented and any use of this
* code should take that into consideration.
*/
#include <stdio.h> // for debug
#include <stdlib.h>
#include <stdexcept>
#include <string.h>
#include "fcch_detector.h"
extern int g_debug;
static const char * const fftw_plan_name = ".kal_fftw_plan";
fcch_detector::fcch_detector(const float sample_rate, const unsigned int D,
const float p, const float G) {
FILE *plan_fp;
char plan_name[BUFSIZ];
const char *home;
m_D = D;
m_p = p;
m_G = G;
m_e = 0.0;
m_sample_rate = sample_rate;
m_fcch_burst_len =
(unsigned int)(148.0 * (m_sample_rate / GSM_RATE));
m_filter_delay = 8;
m_w_len = 2 * m_filter_delay + 1;
m_w = new complex[m_w_len];
memset(m_w, 0, sizeof(complex) * m_w_len);
m_x_cb = new circular_buffer(1024, sizeof(complex), 0);
m_y_cb = new circular_buffer(1024, sizeof(complex), 1);
m_e_cb = new circular_buffer(1000000, sizeof(float), 0);
m_in = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * FFT_SIZE);
m_out = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * FFT_SIZE);
if((!m_in) || (!m_out))
throw std::runtime_error("fcch_detector: fftw_malloc failed!");
home = getenv("HOME");
if(strlen(home) + strlen(fftw_plan_name) + 2 < sizeof(plan_name)) {
strcpy(plan_name, home);
strcat(plan_name, "/");
strcat(plan_name, fftw_plan_name);
if((plan_fp = fopen(plan_name, "r"))) {
fftw_import_wisdom_from_file(plan_fp);
fclose(plan_fp);
}
m_plan = fftw_plan_dft_1d(FFT_SIZE, m_in, m_out, FFTW_FORWARD,
FFTW_MEASURE);
if((plan_fp = fopen(plan_name, "w"))) {
fftw_export_wisdom_to_file(plan_fp);
fclose(plan_fp);
}
} else
m_plan = fftw_plan_dft_1d(FFT_SIZE, m_in, m_out, FFTW_FORWARD,
FFTW_ESTIMATE);
if(!m_plan)
throw std::runtime_error("fcch_detector: fftw plan failed!");
}
fcch_detector::~fcch_detector() {
if(m_w) {
delete[] m_w;
m_w = 0;
}
if(m_x_cb) {
delete m_x_cb;
m_x_cb = 0;
}
if(m_y_cb) {
delete m_y_cb;
m_y_cb = 0;
}
if(m_e_cb) {
delete m_e_cb;
m_e_cb = 0;
}
}
enum {
LOW = 0,
HIGH = 1
};
static unsigned int g_count = 0,
g_block_s = HIGH;
static inline void low_to_high_init() {
g_count = 0;
g_block_s = HIGH;
}
static inline unsigned int low_to_high(float e, float a) {
unsigned int r = 0;
if(e > a) {
if(g_block_s == LOW) {
r = g_count;
g_block_s = HIGH;
g_count = 0;
}
g_count += 1;
} else {
if(g_block_s == HIGH) {
g_block_s = LOW;
g_count = 0;
}
g_count += 1;
}
return r;
}
static inline int peak_valley(complex *c, unsigned int c_len, complex peak, unsigned int peak_i, unsigned int width, float *p2m) {
float valley = 0.0;
unsigned int i, valley_count = 0;
// these constants aren't the best for all burst types
for(i = 2; i < 2 + width; i++) {
if(i <= peak_i) {
valley += norm(c[peak_i - i]);
valley_count += 1;
}
if(peak_i + i < c_len) {
valley += norm(c[peak_i + i]);
valley_count += 1;
}
}
if(valley_count < 2) {
fprintf(stderr, "error: bad valley_count\n");
return -1;
}
valley = sqrtf(valley / (float)valley_count) + 0.00001;
if(p2m)
*p2m = sqrtf(norm(peak)) / valley;
return 0;
}
static inline float sinc(const float x) {
if((x <= -0.0001) || (0.0001 <= x))
return sinf(x) / x;
return 1.0;
}
static inline complex interpolate_point(const complex *s, const unsigned int s_len, const float s_i) {
static const unsigned int filter_len = 21;
int start, end, i;
unsigned int d;
complex point;
d = (filter_len - 1) / 2;
start = (int)(floor(s_i) - d);
end = (int)(floor(s_i) + d + 1);
if(start < 0)
start = 0;
if(end > (int)(s_len - 1))
end = s_len - 1;
for(point = 0.0, i = start; i <= end; i++)
point += s[i] * sinc(M_PI * (i - s_i));
return point;
}
static inline float peak_detect(const complex *s, const unsigned int s_len, complex *peak, float *avg_power) {
unsigned int i;
float max = -1.0, max_i = -1.0, sample_power, sum_power, early_i, late_i, incr;
complex early_p, late_p, cmax;
sum_power = 0;
for(i = 0; i < s_len; i++) {
sample_power = norm(s[i]);
sum_power += sample_power;
if(sample_power > max) {
max = sample_power;
max_i = i;
}
}
early_i = (1 <= max_i)? (max_i - 1) : 0;
late_i = (max_i + 1 < s_len)? (max_i + 1) : s_len - 1;
incr = 0.5;
while(incr > 1.0 / 1024.0) {
early_p = interpolate_point(s, s_len, early_i);
late_p = interpolate_point(s, s_len, late_i);
if(norm(early_p) < norm(late_p))
early_i += incr;
else if(norm(early_p) > norm(late_p))
early_i -= incr;
else
break;
incr /= 2.0;
late_i = early_i + 2.0;
}
max_i = early_i + 1.0;
cmax = interpolate_point(s, s_len, max_i);
if(peak)
*peak = cmax;
if(avg_power)
*avg_power = (sum_power - norm(cmax)) / (s_len - 1);
return max_i;
}
static inline float itof(float index, float sample_rate, unsigned int fft_size) {
double r = index * (sample_rate / (double)fft_size);
/*
if(index > (double)fft_size / 2.0)
return r - sample_rate;
else
return r;
*/
return r;
}
static inline unsigned int ftoi(float frequency, float sample_rate, unsigned int fft_size) {
unsigned int r = (frequency / sample_rate) * fft_size;
return r;
}
#ifndef MIN
#define MIN(a, b) (a)<(b)?(a):(b)
#endif /* !MIN */
float fcch_detector::freq_detect(const complex *s, const unsigned int s_len, float *pm) {
unsigned int i, len;
float max_i, avg_power;
complex fft[FFT_SIZE], peak;
len = MIN(s_len, FFT_SIZE);
for(i = 0; i < len; i++) {
m_in[i][0] = s[i].real();
m_in[i][1] = s[i].imag();
}
for(i = len; i < FFT_SIZE; i++) {
m_in[i][0] = 0;
m_in[i][1] = 0;
}
fftw_execute(m_plan);
for(i = 0; i < FFT_SIZE; i++) {
fft[i].real() = m_out[i][0];
fft[i].imag() = m_out[i][1];
}
max_i = peak_detect(fft, FFT_SIZE, &peak, &avg_power);
if(pm)
*pm = norm(peak) / avg_power;
return itof(max_i, m_sample_rate, FFT_SIZE);
}
static inline void display_complex(const complex *s, unsigned int s_len) {
for(unsigned int i = 0; i < s_len; i++) {
printf("%f\n", s[i].real());
fprintf(stderr, "%f\n", s[i].imag());
}
}
/*
* scan:
* 1. calculate average error
* 2. find neighborhoods with low error that satisfy minimum length
* 3. for each such neighborhood, take fft and calculate peak/mean
* 4. if peak/mean > 50, then this is a valid finding.
*/
unsigned int fcch_detector::scan(const complex *s, const unsigned int s_len, float *offset, unsigned int *consumed) {
static const float sps = m_sample_rate / (1625000.0 / 6.0);
static const unsigned int MIN_FB_LEN = 100 * sps;
static const unsigned int MIN_PM = 50; // XXX arbitrary, depends on decimation
unsigned int len = 0, t, e_count, i, l_count, y_offset, y_len;
float e, *a, loff = 0, pm;
double sum = 0.0, avg, limit;
const complex *y;
// calculate the error for each sample
while(len < s_len) {
t = m_x_cb->write(s + len, 1);
len += t;
if(!next_norm_error(&e)) {
m_e_cb->write(&e, 1);
sum += e;
}
}
if(consumed)
*consumed = len;
// calculate average error over entire buffer
a = (float *)m_e_cb->peek(&e_count);
avg = sum / (double)e_count;
limit = 0.7 * avg;
if(g_debug) {
printf("debug: error limit: %.1lf\n", limit);
}
// find neighborhoods where the error is smaller than the limit
low_to_high_init();
for(i = 0; i < e_count; i++) {
l_count = low_to_high(a[i], limit);
// see if p/m indicates a pure tone
pm = 0;
if(l_count >= MIN_FB_LEN) {
y_offset = i - l_count;
y_len = (l_count < m_fcch_burst_len)? l_count : m_fcch_burst_len;
y = s + y_offset;
loff = freq_detect(y, y_len, &pm);
if(g_debug)
printf("debug: %.0f\t%f\t%f\n", (double)l_count / sps, pm, loff);
if(pm > MIN_PM)
break;
}
}
// empty buffers for next call
m_e_cb->flush();
m_x_cb->flush();
m_y_cb->flush();
if(pm <= MIN_PM)
return 0;
if(offset)
*offset = loff;
if(g_debug) {
printf("debug: fcch_detector finished -----------------------------\n");
}
return 1;
}
unsigned int fcch_detector::update(const complex *s, const unsigned int s_len) {
return m_x_cb->write(s, s_len);
}
unsigned int fcch_detector::get_delay() {
return m_w_len - 1 + m_D;
}
unsigned int fcch_detector::filter_len() {
return m_w_len;
}
static float vectornorm2(const complex *v, const unsigned int len) {
unsigned int i;
float e = 0.0;
for(i = 0; i < len; i++)
e += norm(v[i]);
return e;
}
/*
* First y value comes out at sample x[n + m_D] = x[w_len - 1 + m_D].
*
* y[0] = X(x[0], ..., x[w_len - 1 + m_D])
*
* So y and e are delayed by w_len - 1 + m_D.
*/
int fcch_detector::next_norm_error(float *error) {
unsigned int i, n, max;
float E;
complex *x, y, e;
// n is "current" sample
n = m_w_len - 1;
// ensure there are enough samples in the buffer
x = (complex *)m_x_cb->peek(&max);
if(n + m_D >= max)
return n + m_D - max + 1;
// update G
E = vectornorm2(x, m_w_len);
if(m_G >= 2.0 / E)
m_G = 1.0 / E;
// calculate filtered value
y = 0.0;
for(i = 0; i < m_w_len; i++)
y += std::conj(m_w[i]) * x[n - i];
// m_y_cb->write(&y, 1);
m_y_cb->write(x + n + m_D, 1); // XXX save filtered value?
// calculate error from desired signal
e = x[n + m_D] - y;
// update filters with opposite gradient
for(i = 0; i < m_w_len; i++)
m_w[i] += m_G * std::conj(e) * x[n - i];
// update error average power
E /= m_w_len;
m_e = (1.0 - m_p) * m_e + m_p * norm(e);
// return error ratio
if(error)
*error = m_e / E;
// remove the processed sample from the buffer
m_x_cb->purge(1);
return 0;
}
complex *fcch_detector::dump_x(unsigned int *x_len) {
return (complex *)m_x_cb->peek(x_len);
}
complex *fcch_detector::dump_y(unsigned int *y_len) {
return (complex *)m_y_cb->peek(y_len);
}
unsigned int fcch_detector::y_buf_len() {
return m_y_cb->buf_len();
}
unsigned int fcch_detector::x_buf_len() {
return m_x_cb->buf_len();
}
unsigned int fcch_detector::x_purge(unsigned int len) {
return m_x_cb->purge(len);
}

88
src/fcch_detector.h Normal file
View file

@ -0,0 +1,88 @@
/*
* Copyright (c) 2010, Joshua Lackey
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This is based on the algorithm found in the paper,
*
* Varma, G. Narendra, Usha Sahu, and G. Prabhu Charan. "Robust
* Frequency Burst Detection Algorithm for GSM / GPRS."
*
* The algorithm uses an adaptive filter to calculate the error difference from
* a pure tone. When the error goes low, the tone is detected. When it goes
* back high, the scan function returns and indicates the number of samples the
* error was low.
*
* The following code is an original work and the above BSD-license should
* apply. However, the algorithm itself may be patented and any use of this
* code should take that into consideration.
*/
#include <fftw3.h>
#include "circular_buffer.h"
#include "usrp_complex.h"
class fcch_detector {
public:
fcch_detector(const float sample_rate, const unsigned int D = 8, const float p = 1.0 / 32.0, const float G = 1.0 / 12.5);
~fcch_detector();
unsigned int scan(const complex *s, const unsigned int s_len, float *offset, unsigned int *consumed);
float freq_detect(const complex *s, const unsigned int s_len, float *pm);
unsigned int update(const complex *s, unsigned int s_len);
int next_norm_error(float *error);
complex *dump_x(unsigned int *);
complex *dump_y(unsigned int *);
unsigned int filter_delay() { return m_filter_delay; };
unsigned int get_delay();
unsigned int filter_len();
unsigned int x_buf_len();
unsigned int y_buf_len();
unsigned int x_purge(unsigned int);
private:
static const double GSM_RATE = 1625000.0 / 6.0;
static const unsigned int FFT_SIZE = 1024;
unsigned int m_w_len,
m_D,
m_check_G,
m_filter_delay,
m_lpf_len,
m_fcch_burst_len;
float m_sample_rate,
m_p,
m_G,
m_e;
complex *m_w;
circular_buffer *m_x_cb,
*m_y_cb,
*m_e_cb;
fftw_complex *m_in, *m_out;
fftw_plan m_plan;
};

278
src/kal.cc Normal file
View file

@ -0,0 +1,278 @@
/*
* Copyright (c) 2010, Joshua Lackey
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* kal
*
* Two functions:
*
* 1. Calculates the frequency offset between a local GSM tower and the
* USRP clock.
*
* 2. Identifies the frequency of all GSM base stations in a given band.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#else
#define PACKAGE_VERSION "custom build"
#endif /* HAVE_CONFIG_H */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#ifdef D_HOST_OSX
#include <libgen.h>
#endif /* D_HOST_OSX */
#include <string.h>
#include <sys/time.h>
#include <errno.h>
#include "usrp_source.h"
#include "fcch_detector.h"
#include "arfcn_freq.h"
#include "offset.h"
#include "c0_detect.h"
#include "version.h"
static const double GSM_RATE = 1625000.0 / 6.0;
int g_verbosity = 0;
int g_debug = 0;
void usage(char *prog) {
printf("kalibrate v%s, Copyright (c) 2010, Joshua Lackey\n", kal_version_string);
printf("\nUsage:\n");
printf("\tGSM Base Station Scan:\n");
printf("\t\t%s <-s band indicator> [options]\n", basename(prog));
printf("\n");
printf("\tClock Offset Calculation:\n");
printf("\t\t%s <-f frequency | -c channel> [options]\n", basename(prog));
printf("\n");
printf("Where options are:\n");
printf("\t-s\tband to scan (GSM850, GSM900, EGSM, DCS, PCS)\n");
printf("\t-f\tfrequency of nearby GSM base station\n");
printf("\t-c\tchannel of nearby GSM base station\n");
printf("\t-b\tband indicator (GSM850, GSM900, EGSM, DCS, PCS)\n");
printf("\t-R\tside A (0) or B (1), defaults to B\n");
printf("\t-A\tantenna TX/RX (0) or RX2 (1), defaults to RX2\n");
printf("\t-g\tgain as %% of range, defaults to 45%%\n");
printf("\t-F\tFPGA master clock frequency, defaults to 52MHz\n");
printf("\t-v\tverbose\n");
printf("\t-D\tenable debug messages\n");
printf("\t-h\thelp\n");
exit(-1);
}
int main(int argc, char **argv) {
char *endptr;
int c, antenna = 1, bi = BI_NOT_DEFINED, chan = -1, bts_scan = 0;
unsigned int subdev = 1, decimation = 192;
long int fpga_master_clock_freq = 52000000;
float gain = 0.45;
double freq = -1.0, fd;
usrp_source *u;
while((c = getopt(argc, argv, "f:c:s:b:R:A:g:F:vDh?")) != EOF) {
switch(c) {
case 'f':
freq = strtod(optarg, 0);
break;
case 'c':
chan = strtoul(optarg, 0, 0);
break;
case 's':
if((bi = str_to_bi(optarg)) == -1) {
fprintf(stderr, "error: bad band "
"indicator: ``%s''\n", optarg);
usage(argv[0]);
}
bts_scan = 1;
break;
case 'b':
if((bi = str_to_bi(optarg)) == -1) {
fprintf(stderr, "error: bad band "
"indicator: ``%s''\n", optarg);
usage(argv[0]);
}
break;
case 'R':
errno = 0;
subdev = strtoul(optarg, &endptr, 0);
if((!errno) && (endptr != optarg))
break;
if(tolower(*optarg) == 'a') {
subdev = 0;
} else if(tolower(*optarg) == 'b') {
subdev = 1;
} else {
fprintf(stderr, "error: bad side: "
"``%s''\n",
optarg);
usage(argv[0]);
}
break;
case 'A':
if(!strcmp(optarg, "RX2")) {
antenna = 1;
} else if(!strcmp(optarg, "TX/RX")) {
antenna = 0;
} else {
errno = 0;
antenna = strtoul(optarg, &endptr, 0);
if(errno || (endptr == optarg)) {
fprintf(stderr, "error: bad "
"antenna: ``%s''\n",
optarg);
usage(argv[0]);
}
}
break;
case 'g':
gain = strtod(optarg, 0);
if((gain > 1.0) && (gain <= 100.0))
gain /= 100.0;
if((gain < 0.0) || (1.0 < gain))
usage(argv[0]);
break;
case 'F':
fpga_master_clock_freq = strtol(optarg, 0, 0);
if(!fpga_master_clock_freq)
fpga_master_clock_freq = (long int)strtod(optarg, 0);
// was answer in MHz?
if(fpga_master_clock_freq < 1000) {
fpga_master_clock_freq *= 1000000;
}
break;
case 'v':
g_verbosity++;
break;
case 'D':
g_debug = 1;
break;
case 'h':
case '?':
default:
usage(argv[0]);
break;
}
}
// sanity check frequency / channel
if(bts_scan) {
if(bi == BI_NOT_DEFINED) {
fprintf(stderr, "error: scaning requires band\n");
usage(argv[0]);
}
} else {
if(freq < 0.0) {
if(chan < 0) {
fprintf(stderr, "error: must enter channel or "
"frequency\n");
usage(argv[0]);
}
if((freq = arfcn_to_freq(chan, &bi)) < 869e6)
usage(argv[0]);
}
if((freq < 869e6) || (2e9 < freq)) {
fprintf(stderr, "error: bad frequency: %lf\n", freq);
usage(argv[0]);
}
chan = freq_to_arfcn(freq, &bi);
}
// sanity check clock
if(fpga_master_clock_freq < 48000000) {
fprintf(stderr, "error: FPGA master clock too slow: %li\n", fpga_master_clock_freq);
usage(argv[0]);
}
// calculate decimation -- get as close to GSM rate as we can
fd = (double)fpga_master_clock_freq / GSM_RATE;
decimation = (unsigned int)fd;
if(g_debug) {
#ifdef D_HOST_OSX
printf("debug: Mac OS X version\n");
#endif
printf("debug: FPGA Master Clock Freq:\t%li\n", fpga_master_clock_freq);
printf("debug: decimation :\t%u\n", decimation);
printf("debug: RX Subdev Spec :\t%s\n", subdev? "B" : "A");
printf("debug: Antenna :\t%s\n", antenna? "RX2" : "TX/RX");
printf("debug: Gain :\t%f\n", gain);
}
u = new usrp_source(decimation, fpga_master_clock_freq);
if(!u) {
fprintf(stderr, "error: usrp_source\n");
return -1;
}
if(u->open(subdev) == -1) {
fprintf(stderr, "error: usrp_source::open\n");
return -1;
}
u->set_antenna(antenna);
if(!u->set_gain(gain)) {
fprintf(stderr, "error: usrp_source::set_gain\n");
return -1;
}
if(!bts_scan) {
if(!u->tune(freq)) {
fprintf(stderr, "error: usrp_source::tune\n");
return -1;
}
fprintf(stderr, "%s: Calculating clock frequency offset.\n",
basename(argv[0]));
fprintf(stderr, "Using %s channel %d (%.1fMHz)\n",
bi_to_str(bi), chan, freq / 1e6);
return offset_detect(u);
}
fprintf(stderr, "%s: Scanning for %s base stations.\n",
basename(argv[0]), bi_to_str(bi));
return c0_detect(u, bi);
}

122
src/offset.cc Normal file
View file

@ -0,0 +1,122 @@
/*
* Copyright (c) 2010, Joshua Lackey
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "usrp_source.h"
#include "fcch_detector.h"
#include "util.h"
static const unsigned int AVG_COUNT = 100;
static const unsigned int AVG_THRESHOLD = (AVG_COUNT / 10);
static const float OFFSET_MAX = 40e3;
extern int g_verbosity;
int offset_detect(usrp_source *u) {
static const double GSM_RATE = 1625000.0 / 6.0;
unsigned int new_overruns = 0, overruns = 0;
int notfound = 0;
unsigned int s_len, b_len, consumed, count;
float offset = 0.0, min = 0.0, max = 0.0, avg_offset = 0.0,
stddev = 0.0, sps, offsets[AVG_COUNT];
complex *cbuf;
fcch_detector *l;
circular_buffer *cb;
l = new fcch_detector(u->sample_rate());
/*
* We deliberately grab 12 frames and 1 burst. We are guaranteed to
* find at least one FCCH burst in this much data.
*/
sps = u->sample_rate() / GSM_RATE;
s_len = (unsigned int)ceil((12 * 8 * 156.25 + 156.25) * sps);
cb = u->get_buffer();
u->start();
u->flush();
count = 0;
while(count < AVG_COUNT) {
// ensure at least s_len contiguous samples are read from usrp
do {
if(u->fill(s_len, &new_overruns)) {
return -1;
}
if(new_overruns) {
overruns += new_overruns;
u->flush();
}
} while(new_overruns);
// get a pointer to the next samples
cbuf = (complex *)cb->peek(&b_len);
// search the buffer for a pure tone
if(l->scan(cbuf, b_len, &offset, &consumed)) {
// FCH is a sine wave at GSM_RATE / 4
offset = offset - GSM_RATE / 4;
// sanity check offset
if(fabs(offset) < OFFSET_MAX) {
offsets[count] = offset;
count += 1;
if(g_verbosity > 0) {
fprintf(stderr, "\toffset %3u: %.2f\n", count, offset);
}
}
} else {
++notfound;
}
// consume used samples
cb->purge(consumed);
}
u->stop();
delete l;
// construct stats
sort(offsets, AVG_COUNT);
avg_offset = avg(offsets + AVG_THRESHOLD, AVG_COUNT - 2 * AVG_THRESHOLD, &stddev);
min = offsets[AVG_THRESHOLD];
max = offsets[AVG_COUNT - AVG_THRESHOLD - 1];
printf("average\t\t[min, max]\t(range, stddev)\n");
display_freq(avg_offset);
printf("\t\t[%d, %d]\t(%d, %f)\n", (int)round(min), (int)round(max), (int)round(max - min), stddev);
printf("overruns: %u\n", overruns);
printf("not found: %u\n", notfound);
return 0;
}

28
src/offset.h Normal file
View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2010, Joshua Lackey
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
int offset_detect(usrp_source *u);

31
src/usrp_complex.h Normal file
View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2010, Joshua Lackey
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <complex>
typedef std::complex<float> complex;

297
src/usrp_source.cc Normal file
View file

@ -0,0 +1,297 @@
/*
* Copyright (c) 2010, Joshua Lackey
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <math.h>
#include <complex>
#include <usrp/usrp_standard.h>
#include <usrp/usrp_subdev_spec.h>
#include <usrp/usrp_dbid.h>
#include "usrp_source.h"
extern int g_verbosity;
usrp_source::usrp_source(float sample_rate, long int fpga_master_clock_freq) {
m_fpga_master_clock_freq = fpga_master_clock_freq;
m_desired_sample_rate = sample_rate;
m_sample_rate = 0.0;
m_decimation = 0;
m_u_rx.reset();
m_db_rx.reset();
m_cb = new circular_buffer(CB_LEN, sizeof(complex), 0);
pthread_mutex_init(&m_u_mutex, 0);
}
usrp_source::usrp_source(unsigned int decimation, long int fpga_master_clock_freq) {
m_fpga_master_clock_freq = fpga_master_clock_freq;
m_sample_rate = 0.0;
m_u_rx.reset();
m_db_rx.reset();
m_cb = new circular_buffer(CB_LEN, sizeof(complex), 0);
pthread_mutex_init(&m_u_mutex, 0);
m_decimation = decimation & ~1;
if(m_decimation < 4)
m_decimation = 4;
if(m_decimation > 256)
m_decimation = 256;
}
usrp_source::~usrp_source() {
stop();
delete m_cb;
pthread_mutex_destroy(&m_u_mutex);
}
void usrp_source::stop() {
pthread_mutex_lock(&m_u_mutex);
if(m_db_rx)
m_db_rx->set_enable(0);
if(m_u_rx)
m_u_rx->stop();
pthread_mutex_unlock(&m_u_mutex);
}
void usrp_source::start() {
pthread_mutex_lock(&m_u_mutex);
if(m_db_rx)
m_db_rx->set_enable(1);
if(m_u_rx)
m_u_rx->start();
pthread_mutex_unlock(&m_u_mutex);
}
void usrp_source::calculate_decimation() {
float decimation_f;
decimation_f = (float)m_u_rx->fpga_master_clock_freq() / m_desired_sample_rate;
m_decimation = (unsigned int)round(decimation_f) & ~1;
if(m_decimation < 4)
m_decimation = 4;
if(m_decimation > 256)
m_decimation = 256;
}
float usrp_source::sample_rate() {
return m_sample_rate;
}
int usrp_source::tune(double freq) {
int r;
usrp_tune_result tr;
pthread_mutex_lock(&m_u_mutex);
r = m_u_rx->tune(0, m_db_rx, freq, &tr);
pthread_mutex_unlock(&m_u_mutex);
return r;
}
bool usrp_source::set_antenna(int antenna) {
return m_db_rx->select_rx_antenna(antenna);
}
bool usrp_source::set_gain(float gain) {
float min = m_db_rx->gain_min(), max = m_db_rx->gain_max();
if((gain < 0.0) || (1.0 < gain))
return false;
return m_db_rx->set_gain(min + gain * (max - min));
}
/*
* open() should be called before multiple threads access usrp_source.
*/
int usrp_source::open(unsigned int subdev) {
int do_set_decim = 0;
usrp_subdev_spec ss(subdev, 0);
if(!m_u_rx) {
if(!m_decimation) {
do_set_decim = 1;
m_decimation = 4;
}
if(!(m_u_rx = usrp_standard_rx::make(0, m_decimation,
NCHAN, INITIAL_MUX, usrp_standard_rx::FPGA_MODE_NORMAL,
FUSB_BLOCK_SIZE, FUSB_NBLOCKS, FPGA_FILENAME()))) {
fprintf(stderr, "error: usrp_standard_rx::make: "
"failed!\n");
return -1;
}
m_u_rx->set_fpga_master_clock_freq(m_fpga_master_clock_freq);
m_u_rx->stop();
if(do_set_decim) {
calculate_decimation();
}
m_u_rx->set_decim_rate(m_decimation);
m_sample_rate = (double)m_u_rx->fpga_master_clock_freq() / m_decimation;
if(g_verbosity > 1) {
fprintf(stderr, "FPGA clock : %ld\n", m_u_rx->fpga_master_clock_freq());
fprintf(stderr, "Decimation : %u\n", m_decimation);
fprintf(stderr, "Sample rate: %f\n", m_sample_rate);
}
}
if(!m_u_rx->is_valid(ss)) {
fprintf(stderr, "error: invalid daughterboard\n");
return -1;
}
if(!(m_db_rx = m_u_rx->selected_subdev(ss))) {
fprintf(stderr, "error: no daughterboard\n");
return -1;
}
m_u_rx->set_mux(m_u_rx->determine_rx_mux_value(ss));
set_gain(0.45);
m_db_rx->select_rx_antenna(1); // this is a nop for most db
return 0;
}
#define USB_PACKET_SIZE 512
int usrp_source::fill(unsigned int num_samples, unsigned int *overrun_i) {
bool overrun;
unsigned char ubuf[USB_PACKET_SIZE];
short *s = (short *)ubuf;
unsigned int i, j, space, overruns = 0;
complex *c;
while((m_cb->data_available() < num_samples) && (m_cb->space_available() > 0)) {
// read one usb packet from the usrp
pthread_mutex_lock(&m_u_mutex);
if(m_u_rx->read(ubuf, sizeof(ubuf), &overrun) != sizeof(ubuf)) {
pthread_mutex_unlock(&m_u_mutex);
fprintf(stderr, "error: usrp_standard_rx::read\n");
return -1;
}
pthread_mutex_unlock(&m_u_mutex);
if(overrun)
overruns++;
// write complex<short> input to complex<float> output
c = (complex *)m_cb->poke(&space);
// set space to number of complex items to copy
if(space > (USB_PACKET_SIZE >> 2))
space = USB_PACKET_SIZE >> 2;
// write data
for(i = 0, j = 0; i < space; i += 1, j += 2)
c[i] = complex(s[j], s[j + 1]);
// update cb
m_cb->wrote(i);
}
// if the cb is full, we left behind data from the usb packet
if(m_cb->space_available() == 0) {
fprintf(stderr, "warning: local overrun\n");
overruns++;
}
if(overrun_i)
*overrun_i = overruns;
return 0;
}
int usrp_source::read(complex *buf, unsigned int num_samples,
unsigned int *samples_read) {
unsigned int n;
if(fill(num_samples, 0))
return -1;
n = m_cb->read(buf, num_samples);
if(samples_read)
*samples_read = n;
return 0;
}
/*
* Don't hold a lock on this and use the usrp at the same time.
*/
circular_buffer *usrp_source::get_buffer() {
return m_cb;
}
int usrp_source::flush(unsigned int flush_count) {
m_cb->flush();
fill(flush_count * USB_PACKET_SIZE, 0);
m_cb->flush();
return 0;
}

85
src/usrp_source.h Normal file
View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2010, Joshua Lackey
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <usrp/usrp_standard.h>
#include "usrp_complex.h"
#include "circular_buffer.h"
class usrp_source {
public:
usrp_source(float sample_rate, long int fpga_master_clock_freq = 52000000);
usrp_source(unsigned int decimation, long int fpga_master_clock_freq = 52000000);
~usrp_source();
int open(unsigned int subdev);
int read(complex *buf, unsigned int num_samples, unsigned int *samples_read);
int fill(unsigned int num_samples, unsigned int *overrun);
int tune(double freq);
bool set_antenna(int antenna);
bool set_gain(float gain);
void start();
void stop();
int flush(unsigned int flush_count = FLUSH_COUNT);
circular_buffer *get_buffer();
float sample_rate();
static const unsigned int side_A = 0;
static const unsigned int side_B = 1;
private:
void calculate_decimation();
usrp_standard_rx_sptr m_u_rx;
db_base_sptr m_db_rx;
float m_sample_rate;
float m_desired_sample_rate;
unsigned int m_decimation;
long int m_fpga_master_clock_freq;
circular_buffer * m_cb;
/*
* This mutex protects access to the USRP and daughterboards but not
* necessarily to any fields in this class.
*/
pthread_mutex_t m_u_mutex;
static const unsigned int FLUSH_COUNT = 10;
static const unsigned int CB_LEN = (1 << 20);
static const int NCHAN = 1;
static const int INITIAL_MUX = -1;
static const int FUSB_BLOCK_SIZE = 1024;
static const int FUSB_NBLOCKS = 16 * 8;
static const char * FPGA_FILENAME() {
return "std_2rxhb_2tx.rbf";
}
};

96
src/util.cc Normal file
View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2010, Joshua Lackey
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
void display_freq(float f) {
if(f >= 0) {
printf("+ ");
} else {
printf("- ");
f = -f;
}
if(fabs(f) >= 1e9) {
printf("%.3fGHz", f / 1e9);
return;
}
if(fabs(f) >= 1e6) {
printf("%.1fMHz", f / 1e6);
return;
}
if(fabs(f) >= 1e3) {
printf("%.3fkHz", f / 1e3);
return;
}
if(fabs(f) >= 1e2) {
printf("%.0fHz", f);
return;
}
if(fabs(f) >= 1e1) {
printf(" %.0fHz", f);
return;
}
printf(" %.0fHz", f);
}
void sort(float *b, unsigned int len) {
for(unsigned int i = 0; i < len; i++) {
for(unsigned int j = i + 1; j < len; j++) {
if(b[j] < b[i]) {
float t = b[i];
b[i] = b[j];
b[j] = t;
}
}
}
}
double avg(float *b, unsigned int len, float *stddev) {
unsigned int i;
double t = 0.0, a = 0.0, s = 0.0;
for(i = 0; i < len; i++)
t += b[i];
a = t / len;
if(stddev) {
for(i = 0; i < len; i++)
s += (b[i] - a) * (b[i] - a);
s /= len;
s = sqrt(s);
*stddev = s;
}
return a;
}

30
src/util.h Normal file
View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2010, Joshua Lackey
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
void display_freq(float f);
void sort(float *b, unsigned int len);
double avg(float *b, unsigned int len, float *stddev);

29
src/version.h Normal file
View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2010, Joshua Lackey
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
static const char * const kal_version_string = PACKAGE_VERSION;