├── .gitignore ├── .travis.yml ├── AUTHORS ├── COPYING ├── ChangeLog ├── Makefile.am ├── NEWS ├── README.md ├── bootstrap ├── configure.ac └── src ├── Makefile.am ├── arfcn_freq.cc ├── arfcn_freq.h ├── c0_detect.cc ├── c0_detect.h ├── circular_buffer.cc ├── circular_buffer.h ├── fcch_detector.cc ├── fcch_detector.h ├── kal.cc ├── offset.cc ├── offset.h ├── usrp_complex.h ├── usrp_source.cc ├── usrp_source.h ├── util.cc ├── util.h └── version.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.sw[op] 3 | Makefile 4 | Makefile.in 5 | configure 6 | autom4te.cache/ 7 | compile 8 | config.guess 9 | config.h 10 | config.h.in 11 | config.in 12 | config.log 13 | config.sub 14 | config.status 15 | src/.deps/ 16 | depcomp 17 | src/kal 18 | missing 19 | stamp-h1 20 | install-sh 21 | INSTALL 22 | aclocal.m4 23 | ltmain.sh 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: 3 | - gcc 4 | - clang 5 | os: 6 | - osx 7 | - linux 8 | 9 | script: ./bootstrap && ./configure && make 10 | 11 | addons: 12 | apt: 13 | packages: 14 | - libfftw3-dev 15 | - librtlsdr-dev 16 | - libusb-1.0-0-dev 17 | 18 | before_install: 19 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi 20 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install fftw librtlsdr libusb ; fi 21 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Joshua Lackey 2 | Alexander Chemeris 3 | Steve Markgraf 4 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Joshua Lackey 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viraptor/kalibrate-rtl/8e7d26c1b142510b20e78d8057eea8a33a12b816/ChangeLog -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = src 2 | 3 | README: README.md 4 | cp README.md README 5 | 6 | CLEANFILES = README 7 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viraptor/kalibrate-rtl/8e7d26c1b142510b20e78d8057eea8a33a12b816/NEWS -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SUMMARY 2 | ======= 3 | 4 | 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. 5 | 6 | The description below is for original kal - this version has been modified for RTL-SDR dongles. 7 | 8 | This project can work only correct the a small offset of ppm. You need to either 9 | get a rough initial estimate yourself, or use a project which can correct larger 10 | offsets automatically. For example https://github.com/viraptor/fm_tune or 11 | https://github.com/JiaoXianjun/LTE-Cell-Scanner 12 | 13 | WHAT 14 | ==== 15 | 16 | On Clocks 17 | --------- 18 | 19 | The USRP1, similar to a number of devices, has a built-in oscillator which is only guaranteed accurate to within 20 parts per million (ppm). (Although in practice the USRP oscillator is normally closer to 3ppm.) This is sufficient for a number of purposes, but not for others. Fortunately, the USRP also supports an external oscillator. For example, the Clock Tamer, the KSP 52MHz and the FA-SY 1 are all accurate clocks that work well with the USRP. 20 | 21 | Normally, these external clocks must be calibrated to ensure they are set as accurately as possible. An oscilloscope is probably the best way to determine the accuracy of these clocks. However a good oscilloscope is expensive and not everyone has access to one. There are other ways to calibrate these devices -- the FA-SY 1 documentation discusses using the WWV time signals. Similarly, GSM base stations are required to be accurate to within 0.05ppm and so they can also provide an accurate reference clock. 22 | 23 | If you own a USRP daughterboard that covers the GSM frequencies in your area, using a GSM base station is a particularly convenient. 24 | 25 | Description 26 | ----------- 27 | 28 | A base station transmits a frequency correction burst on the Frequency Correction CHannel (FCCH) in regular positions. The FCCH repeats every 51 TDMA frames and the frequency correction burst is located in timeslot 0 of frames 0, 10, 20, 30, and 40. 29 | 30 | A frequency correction burst consists of a certain bit pattern which, after modulation, is a sine wave with frequency one quarter that of the GSM bit rate. I.e., `(1625000 / 6) / 4 = 67708.3 Hz`. By searching a channel for this pure tone, a mobile station can determine its clock offset by determining how far away from 67708.3Hz the received frequency is. 31 | 32 | There are many ways which a pure tone can be identified. For example, one can capture a burst of data and then examine it with a Fourier transformation to determine if most of the power in the signal is at a certain frequency. Alternatively, one can define a band-pass filter at 67708.3 Hz and then compare the power of the signal before and after it passes through the filter. However, both of these methods have drawbacks. The FFT method requires significant resources and cannot easily detect the frequency correction burst edge. The filter method either isn't accurate or cannot detect larger offsets. Multiple filters can be used, but that requires significant resources. 33 | 34 | `kal` uses a hybrid of the FFT and filter methods. The filter kal uses is an adaptive filter as described in the paper: 35 | 36 | G. Narendra Varma, Usha Sahu, G. Prabhu Charan, *Robust Frequency Burst Detection Algorithm for GSM/GPRS*. 37 | 38 | An Adaptive Line Equalizer (ALE) attempts to predict the input signal by adapting its filter coefficients. The prediction is compared with the actual signal and the coefficients are adapted so that the error is minimized. If the input contains a strong narrow-band signal embedded in wide-band noise, the filter output will be a pure sine at the same frequency and almost free of the wide-band noise. 39 | 40 | `kal` applies the ALE to the buffer and calculates the error between the ALE prediction and the input signal at each point in the signal. kal then calculates the average of all the errors. When the error drops below the average for the length of a frequency correction burst, this indicates a detected burst. 41 | 42 | Once we have the location of the frequency correction burst, we must determine what frequency it is at so we can calculate the offset from 67708.3 Hz. We take the input signal corresponding to the low error levels and run that through a FFT. The largest peak in the FFT output corresponds to the detected frequency of the frequency correction burst. This peak is then used to determine the frequency offset. 43 | 44 | Any noise in the system affects the measurements and so kal averages the results a number of times before displaying the offset. The range of values as well as the standard deviation is displayed so that an estimate of the measurement accuracy can be made. 45 | 46 | HOW 47 | === 48 | 49 | kal uses the GNU Autoconf system and should be easily built on most \*nix platforms. 50 | 51 | ``` 52 | jl@thinkfoo:~/src$ cd kal-v0.4.1 53 | jl@thinkfoo:~/src/kal-v0.4.1$ ./bootstrap && CXXFLAGS='-W -Wall -O3' ./configure && make 54 | [...] 55 | jl@thinkfoo:~/src/kal-v0.4.1$ src/kal -h 56 | kalibrate v0.4.1, Copyright (c) 2010, Joshua Lackey 57 | 58 | Usage: 59 | GSM Base Station Scan: 60 | kal <-s band indicator> [options] 61 | 62 | Clock Offset Calculation: 63 | kal <-f frequency | -c channel> [options] 64 | 65 | Where options are: 66 | -s band to scan (GSM850, GSM900, EGSM, DCS, PCS) 67 | -f frequency of nearby GSM base station 68 | -c channel of nearby GSM base station 69 | -b band indicator (GSM850, GSM900, EGSM, DCS, PCS) 70 | -R side A (0) or B (1), defaults to B 71 | -A antenna TX/RX (0) or RX2 (1), defaults to RX2 72 | -g gain as % of range, defaults to 45% 73 | -F FPGA master clock frequency, defaults to 52MHz 74 | -v verbose 75 | -D enable debug messages 76 | -h help 77 | jl@thinkfoo:~/src/kal-v0.4.1$ src/kal -s 850 78 | kal: Scanning for GSM-850 base stations. 79 | GSM-850: 80 | chan: 128 (869.2MHz - 13Hz) power: 9400.80 81 | chan: 131 (869.8MHz + 7Hz) power: 4081.75 82 | chan: 139 (871.4MHz + 10Hz) power: 2936.20 83 | chan: 145 (872.6MHz - 7Hz) power: 33605.48 84 | jl@thinkfoo:~/src/kal-v0.4.1$ src/kal -c 145 85 | kal: Calculating clock frequency offset. 86 | Using GSM-850 channel 145 (872.6MHz) 87 | average [min, max] (range, stddev) 88 | - 1Hz [-8, 7] (14, 3.948722) 89 | overruns: 0 90 | not found: 0 91 | ``` 92 | 93 | WHO 94 | === 95 | 96 | I'd like to thank Alexander Chemeris for his valuable input, ideas, and his testing of a large number of alpha versions. I'd also like to thank Mark J. Blair for his help getting kal to work with Mac OS X. 97 | 98 | I'd also like to thank Mark J. Blair for his help getting kal to work with Mac OS X. (And also for helping me realize that I never actually used the -F parameter...) 99 | 100 | Copyright (c) 2010, Joshua Lackey (jl@thre.at) modified for use with rtl-sdr devices, 101 | 102 | Copyright (c) 2012, Steve Markgraf (steve@steve-m.de) 103 | 104 | Current maintenance: Stan Pitucha (viraptor@gmail.com) 105 | 106 | 107 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | rm -rf config.cache autom4te*.cache 6 | #autoreconf --install 7 | 8 | case `uname` in 9 | Darwin*) 10 | glibtoolize --automake ;; 11 | *) 12 | libtoolize --automake ;; 13 | esac 14 | 15 | aclocal 16 | autoconf 17 | autoheader 18 | automake --add-missing 19 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ([2.64]) 2 | AC_INIT([kalibrate], [0.4.1], [jl@thre.at], [kal]) 3 | AC_CONFIG_SRCDIR([src/fcch_detector.cc]) 4 | AM_INIT_AUTOMAKE([-Wall -Werror]) 5 | AC_CONFIG_HEADERS([config.h]) 6 | 7 | # Checks for programs. 8 | AC_PROG_CXX 9 | AC_PROG_CC 10 | AC_PROG_LN_S 11 | AC_PROG_RANLIB 12 | 13 | # Checks for header files. 14 | AC_CHECK_HEADERS([stdlib.h string.h sys/time.h unistd.h libgen.h]) 15 | 16 | # Checks for typedefs, structures, and compiler characteristics. 17 | AC_HEADER_STDBOOL 18 | AC_C_INLINE 19 | 20 | # Checks for library functions. 21 | AC_FUNC_STRTOD 22 | AC_CHECK_FUNCS([floor getpagesize memset sqrt strtoul strtol]) 23 | 24 | # Checks for libraries. 25 | PKG_CHECK_MODULES(FFTW3, fftw3 >= 3.0) 26 | AC_SUBST(FFTW3_LIBS) 27 | AC_SUBST(FFTW3_CFLAGS) 28 | 29 | PKG_CHECK_MODULES(LIBRTLSDR, librtlsdr) 30 | AC_SUBST(LIBRTLSDR_LIBS) 31 | AC_SUBST(LIBRTLSDR_CFLAGS) 32 | AC_CHECK_LIB([rtlsdr], [rtlsdr_set_dithering], 33 | [AC_DEFINE([HAVE_DITHERING], [1], [Dithering available in rtlsdr])], 34 | [AC_DEFINE([HAVE_DITHERING], [0], [Dithering not available in rtlsdr])]) 35 | 36 | # OSX doesn't support System V shared memory 37 | AC_CANONICAL_HOST 38 | case "$host_os" in 39 | darwin*) 40 | AC_DEFINE([D_HOST_OSX], [], [building for OSX]) 41 | LRT_FLAGS="" 42 | AC_SUBST(LRT_FLAGS) 43 | ;; 44 | *) 45 | LRT_FLAGS="-lrt" 46 | AC_SUBST(LRT_FLAGS) 47 | ;; 48 | esac 49 | 50 | case "$host_cpu" in 51 | arm*) 52 | AC_DEFINE([D_HOST_OSX], [], [building without shared memory]) 53 | ;; 54 | esac 55 | 56 | AC_CONFIG_FILES([Makefile 57 | src/Makefile]) 58 | AC_OUTPUT 59 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = kal 2 | 3 | kal_SOURCES = \ 4 | arfcn_freq.cc \ 5 | c0_detect.cc \ 6 | circular_buffer.cc \ 7 | fcch_detector.cc \ 8 | kal.cc \ 9 | offset.cc \ 10 | usrp_source.cc \ 11 | util.cc\ 12 | arfcn_freq.h \ 13 | c0_detect.h \ 14 | circular_buffer.h \ 15 | fcch_detector.h \ 16 | offset.h \ 17 | usrp_complex.h \ 18 | usrp_source.h \ 19 | util.h\ 20 | version.h 21 | 22 | kal_CXXFLAGS = $(FFTW3_CFLAGS) $(LIBRTLSDR_CFLAGS) 23 | kal_LDADD = $(FFTW3_LIBS) $(LIBRTLSDR_LIBS) $(LRT_FLAGS) 24 | -------------------------------------------------------------------------------- /src/arfcn_freq.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Joshua Lackey 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #ifndef _WIN32 31 | #include 32 | #endif 33 | #include 34 | #include "arfcn_freq.h" 35 | 36 | 37 | const char *bi_to_str(int bi) { 38 | 39 | switch(bi) { 40 | case GSM_850: 41 | return "GSM-850"; 42 | 43 | case GSM_R_900: 44 | return "GSM-R-900"; 45 | 46 | case GSM_900: 47 | return "GSM-900"; 48 | 49 | case GSM_E_900: 50 | return "E-GSM-900"; 51 | 52 | case DCS_1800: 53 | return "DCS-1800"; 54 | 55 | case PCS_1900: 56 | return "PCS-1900"; 57 | 58 | default: 59 | return "unknown band indicator"; 60 | } 61 | } 62 | 63 | 64 | int str_to_bi(char *s) { 65 | 66 | if(!strcmp(s, "GSM850") || !strcmp(s, "GSM-850") || !strcmp(s, "850")) 67 | return GSM_850; 68 | 69 | if(!strcmp(s, "GSM-R") || !strcmp(s, "R-GSM")) 70 | return GSM_R_900; 71 | 72 | if(!strcmp(s, "GSM900") || !strcmp(s, "GSM-900") || !strcmp(s, "900")) 73 | return GSM_900; 74 | 75 | if(!strcmp(s, "EGSM") || !strcmp(s, "E-GSM") || !strcmp(s, "EGSM900") || 76 | !strcmp(s, "E-GSM900") || !strcmp(s, "E-GSM-900")) 77 | return GSM_E_900; 78 | 79 | if(!strcmp(s, "DCS") || !strcmp(s, "DCS1800") || 80 | !strcmp(s, "DCS-1800") || !strcmp(s, "1800")) 81 | return DCS_1800; 82 | 83 | if(!strcmp(s, "PCS") || !strcmp(s, "PCS1900") || 84 | !strcmp(s, "PCS-1900") || !strcmp(s, "1900")) 85 | return PCS_1900; 86 | 87 | return -1; 88 | } 89 | 90 | 91 | double arfcn_to_freq(int n, int *bi) { 92 | 93 | if((128 <= n) && (n <= 251)) { 94 | if(bi) 95 | *bi = GSM_850; 96 | return 824.2e6 + 0.2e6 * (n - 128) + 45.0e6; 97 | } 98 | 99 | if((1 <= n) && (n <= 124)) { 100 | if(bi && (*bi != GSM_E_900)) 101 | *bi = GSM_900; 102 | return 890.0e6 + 0.2e6 * n + 45.0e6; 103 | } 104 | 105 | if(n == 0) { 106 | if(bi) 107 | *bi = GSM_E_900; 108 | return 935e6; 109 | } 110 | if((955 <= n) && (n <= 1023)) { 111 | if(bi) { 112 | if (975 <= n) 113 | *bi = GSM_E_900; 114 | else 115 | *bi = GSM_R_900; 116 | } 117 | return 890.0e6 + 0.2e6 * (n - 1024) + 45.0e6; 118 | } 119 | 120 | if((512 <= n) && (n <= 810)) { 121 | if(!bi) { 122 | fprintf(stderr, "error: ambiguous arfcn: %d\n", n); 123 | return -1.0; 124 | } 125 | 126 | if(*bi == DCS_1800) 127 | return 1710.2e6 + 0.2e6 * (n - 512) + 95.0e6; 128 | 129 | if(*bi == PCS_1900) 130 | return 1850.2e6 + 0.2e6 * (n - 512) + 80.0e6; 131 | 132 | fprintf(stderr, "error: bad (arfcn, band indicator) pair: " 133 | "(%d, %s)\n", n, bi_to_str(*bi)); 134 | return -1.0; 135 | } 136 | 137 | if((811 <= n) && (n <= 885)) { 138 | if(bi) 139 | *bi = DCS_1800; 140 | return 1710.2e6 + 0.2e6 * (n - 512) + 95.0e6; 141 | } 142 | 143 | fprintf(stderr, "error: bad arfcn: %d\n", n); 144 | return -1.0; 145 | } 146 | 147 | 148 | int freq_to_arfcn(double freq, int *bi) { 149 | 150 | if((869.2e6 <= freq) && (freq <= 893.8e6)) { 151 | if(bi) 152 | *bi = GSM_850; 153 | return (int)((freq - 869.2e6) / 0.2e6) + 128; 154 | } 155 | 156 | if((921.2e6 <= freq) && (freq <= 925.0e6)) { 157 | if(bi) 158 | *bi = GSM_R_900; 159 | return (int)((freq - 935e6) / 0.2e6) + 1024; 160 | } 161 | 162 | if((935.2e6 <= freq) && (freq <= 959.8e6)) { 163 | if(bi) 164 | *bi = GSM_900; 165 | return (int)((freq - 935e6) / 0.2e6); 166 | } 167 | 168 | if(935.0e6 == freq) { 169 | if(bi) 170 | *bi = GSM_E_900; 171 | return 0; 172 | } 173 | if((925.2e6 <= freq) && (freq <= 934.8e6)) { 174 | if(bi) 175 | *bi = GSM_E_900; 176 | return (int)((freq - 935e6) / 0.2e6) + 1024; 177 | } 178 | 179 | if((1805.2e6 <= freq) && (freq <= 1879.8e6)) { 180 | if(bi) 181 | *bi = DCS_1800; 182 | return (int)((freq - 1805.2e6) / 0.2e6) + 512; 183 | } 184 | 185 | if((1930.2e6 <= freq) && (freq <= 1989.8e6)) { 186 | if(bi) 187 | *bi = PCS_1900; 188 | return (int)((freq - 1930.2e6) / 0.2e6) + 512; 189 | } 190 | 191 | fprintf(stderr, "error: bad frequency: %lf\n", freq); 192 | return -1; 193 | } 194 | 195 | 196 | int first_chan(int bi) { 197 | 198 | switch(bi) { 199 | case GSM_850: 200 | return 128; 201 | 202 | case GSM_R_900: 203 | return 955; 204 | 205 | case GSM_900: 206 | return 1; 207 | 208 | case GSM_E_900: 209 | return 0; 210 | 211 | case DCS_1800: 212 | return 512; 213 | 214 | case PCS_1900: 215 | return 512; 216 | 217 | default: 218 | return -1; 219 | } 220 | 221 | return -1; 222 | } 223 | 224 | 225 | int next_chan_loop(int chan, int bi) { 226 | 227 | switch(bi) { 228 | case GSM_850: 229 | if((128 <= chan) && (chan < 251)) 230 | return chan + 1; 231 | if(chan == 251) 232 | return 128; 233 | return -1; 234 | 235 | case GSM_R_900: 236 | if((955 <= chan) && (chan < 974)) 237 | return chan + 1; 238 | if(chan == 974) 239 | return 955; 240 | return -1; 241 | 242 | case GSM_900: 243 | if((1 <= chan) && (chan < 124)) 244 | return chan + 1; 245 | if(chan == 124) 246 | return 1; 247 | return -1; 248 | 249 | case GSM_E_900: 250 | if((0 <= chan) && (chan < 124)) 251 | return chan + 1; 252 | if(chan == 124) 253 | return 975; 254 | if((975 <= chan) && (chan < 1023)) 255 | return chan + 1; 256 | if(chan == 1023) 257 | return 0; 258 | return -1; 259 | 260 | case DCS_1800: 261 | if((512 <= chan) && (chan < 885)) 262 | return chan + 1; 263 | if(chan == 885) 264 | return 512; 265 | return -1; 266 | 267 | case PCS_1900: 268 | if((512 <= chan) && (chan < 810)) 269 | return chan + 1; 270 | if(chan == 810) 271 | return 512; 272 | return -1; 273 | 274 | default: 275 | return -1; 276 | } 277 | 278 | return -1; 279 | } 280 | 281 | 282 | int next_chan(int chan, int bi) { 283 | 284 | switch(bi) { 285 | case GSM_850: 286 | if((128 <= chan) && (chan < 251)) 287 | return chan + 1; 288 | return -1; 289 | 290 | case GSM_R_900: 291 | if((955 <= chan) && (chan < 974)) 292 | return chan + 1; 293 | return -1; 294 | 295 | case GSM_900: 296 | if((1 <= chan) && (chan < 124)) 297 | return chan + 1; 298 | return -1; 299 | 300 | case GSM_E_900: 301 | if((0 <= chan) && (chan < 124)) 302 | return chan + 1; 303 | if(chan == 124) 304 | return 975; 305 | if((975 <= chan) && (chan < 1023)) 306 | return chan + 1; 307 | return -1; 308 | 309 | case DCS_1800: 310 | if((512 <= chan) && (chan < 885)) 311 | return chan + 1; 312 | return -1; 313 | 314 | case PCS_1900: 315 | if((512 <= chan) && (chan < 810)) 316 | return chan + 1; 317 | return -1; 318 | 319 | default: 320 | return -1; 321 | } 322 | 323 | return -1; 324 | } 325 | -------------------------------------------------------------------------------- /src/arfcn_freq.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Joshua Lackey 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | enum { 29 | BI_NOT_DEFINED, 30 | GSM_850, 31 | GSM_R_900, 32 | GSM_900, 33 | GSM_E_900, 34 | DCS_1800, 35 | PCS_1900 36 | }; 37 | 38 | const char *bi_to_str(int bi); 39 | int str_to_bi(char *s); 40 | double arfcn_to_freq(int n, int *bi = 0); 41 | int freq_to_arfcn(double freq, int *bi = 0); 42 | int first_chan(int bi); 43 | int next_chan(int chan, int bi); 44 | -------------------------------------------------------------------------------- /src/c0_detect.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Joshua Lackey 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "usrp_source.h" 34 | #include "circular_buffer.h" 35 | #include "fcch_detector.h" 36 | #include "arfcn_freq.h" 37 | #include "util.h" 38 | 39 | extern int g_verbosity; 40 | 41 | static const float ERROR_DETECT_OFFSET_MAX = 40e3; 42 | 43 | #ifdef _WIN32 44 | #define BUFSIZ 1024 45 | #endif 46 | 47 | static double vectornorm2(const complex *v, const unsigned int len) { 48 | 49 | unsigned int i; 50 | double e = 0.0; 51 | 52 | for(i = 0; i < len; i++) 53 | e += norm(v[i]); 54 | 55 | return e; 56 | } 57 | 58 | 59 | int c0_detect(usrp_source *u, int bi) { 60 | 61 | #define GSM_RATE (1625000.0 / 6.0) 62 | #define NOTFOUND_MAX 10 63 | 64 | int i, chan_count; 65 | unsigned int overruns, b_len, frames_len, found_count, notfound_count, r; 66 | float offset, spower[BUFSIZ], effective_offset, min_offset, max_offset; 67 | double freq, sps, n, power[BUFSIZ], sum = 0, a; 68 | complex *b; 69 | circular_buffer *ub; 70 | fcch_detector *detector = new fcch_detector(u->sample_rate()); 71 | 72 | if(bi == BI_NOT_DEFINED) { 73 | fprintf(stderr, "error: c0_detect: band not defined\n"); 74 | return -1; 75 | } 76 | 77 | sps = u->sample_rate() / GSM_RATE; 78 | frames_len = (unsigned int)ceil((12 * 8 * 156.25 + 156.25) * sps); 79 | ub = u->get_buffer(); 80 | 81 | // first, we calculate the power in each channel 82 | if(g_verbosity > 2) { 83 | fprintf(stderr, "calculate power in each channel:\n"); 84 | } 85 | u->start(); 86 | u->flush(); 87 | for(i = first_chan(bi); i >= 0; i = next_chan(i, bi)) { 88 | freq = arfcn_to_freq(i, &bi); 89 | if(!u->tune(freq)) { 90 | fprintf(stderr, "error: usrp_source::tune\n"); 91 | return -1; 92 | } 93 | 94 | do { 95 | u->flush(); 96 | if(u->fill(frames_len, &overruns)) { 97 | fprintf(stderr, "error: usrp_source::fill\n"); 98 | return -1; 99 | } 100 | } while(overruns); 101 | 102 | b = (complex *)ub->peek(&b_len); 103 | n = sqrt(vectornorm2(b, frames_len)); 104 | power[i] = n; 105 | if(g_verbosity > 2) { 106 | fprintf(stderr, "\tchan %d (%.1fMHz):\tpower: %lf\n", 107 | i, freq / 1e6, n); 108 | } 109 | } 110 | 111 | /* 112 | * We want to use the average to determine which channels have 113 | * power, and hence a possibility of being channel 0 on a BTS. 114 | * However, some channels in the band can be extremely noisy. (E.g., 115 | * CDMA traffic in GSM-850.) Hence we won't consider the noisiest 116 | * channels when we construct the average. 117 | */ 118 | chan_count = 0; 119 | for(i = first_chan(bi); i >= 0; i = next_chan(i, bi)) { 120 | spower[chan_count++] = power[i]; 121 | } 122 | sort(spower, chan_count); 123 | 124 | // average the lowest %60 125 | a = avg(spower, chan_count - 4 * chan_count / 10, 0); 126 | 127 | if(g_verbosity > 0) { 128 | fprintf(stderr, "channel detect threshold: %lf\n", a); 129 | } 130 | 131 | // then we look for fcch bursts 132 | printf("%s:\n", bi_to_str(bi)); 133 | found_count = 0; 134 | notfound_count = 0; 135 | sum = 0; 136 | i = first_chan(bi); 137 | do { 138 | if(power[i] <= a) { 139 | i = next_chan(i, bi); 140 | continue; 141 | } 142 | if (isatty(1)) { 143 | printf("...chan %i\r", i); 144 | fflush(stdout); 145 | } 146 | 147 | freq = arfcn_to_freq(i, &bi); 148 | if(!u->tune(freq)) { 149 | fprintf(stderr, "error: usrp_source::tune\n"); 150 | return -1; 151 | } 152 | 153 | do { 154 | u->flush(); 155 | if(u->fill(frames_len, &overruns)) { 156 | fprintf(stderr, "error: usrp_source::fill\n"); 157 | return -1; 158 | } 159 | } while(overruns); 160 | 161 | b = (complex *)ub->peek(&b_len); 162 | r = detector->scan(b, b_len, &offset, 0); 163 | effective_offset = offset - GSM_RATE / 4; 164 | if(r && (fabsf(effective_offset) < ERROR_DETECT_OFFSET_MAX)) { 165 | // found 166 | if (found_count) { 167 | min_offset = fmin(min_offset, effective_offset); 168 | max_offset = fmax(max_offset, effective_offset); 169 | } else { 170 | min_offset = max_offset = effective_offset; 171 | } 172 | found_count++; 173 | printf(" chan: %4d (%.1fMHz ", i, freq / 1e6); 174 | display_freq(effective_offset); 175 | printf(") power: %10.2f\n", power[i]); 176 | notfound_count = 0; 177 | i = next_chan(i, bi); 178 | } else { 179 | // not found 180 | notfound_count += 1; 181 | if(notfound_count >= NOTFOUND_MAX) { 182 | notfound_count = 0; 183 | i = next_chan(i, bi); 184 | } 185 | } 186 | } while(i >= 0); 187 | 188 | if (found_count == 1) { 189 | printf("\n"); 190 | printf("Only one channel was found. This is unlikely and may " 191 | "indicate you need to provide a rough estimate of the initial " 192 | "PPM. It can be provided with the '-e' option. Try tuning against " 193 | "a local FM radio or other known frequency first.\n"); 194 | } 195 | /* 196 | * If the difference in offsets found is strangely large 197 | */ 198 | if (found_count > 1 && max_offset - min_offset > 1000) { 199 | printf("\n"); 200 | printf("Difference of offsets between channels is >1kHz. This likely " 201 | "means that the correct PPM is too far away and you need to provide " 202 | "a rough estimate using the '-e' option. Try tuning against " 203 | "a local FM radio or other known frequency first.\n"); 204 | } 205 | return 0; 206 | } 207 | -------------------------------------------------------------------------------- /src/c0_detect.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Joshua Lackey 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | int c0_detect(usrp_source *u, int bi); 29 | -------------------------------------------------------------------------------- /src/circular_buffer.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Joshua Lackey 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifdef HAVE_CONFIG_H 29 | #include "config.h" 30 | #endif /* HAVE_CONFIG_H */ 31 | 32 | #include 33 | #include 34 | #ifndef _WIN32 35 | #include 36 | #include 37 | #endif 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #ifndef D_HOST_OSX 44 | #ifndef _WIN32 45 | #include 46 | #endif 47 | #else 48 | #include 49 | #include 50 | #endif /* !D_HOST_OSX */ 51 | 52 | #include "circular_buffer.h" 53 | //#include 54 | 55 | #ifndef D_HOST_OSX 56 | #ifndef _WIN32 57 | circular_buffer::circular_buffer(const unsigned int buf_len, 58 | const unsigned int item_size, const unsigned int overwrite) { 59 | 60 | int shm_id_temp, shm_id_guard, shm_id_buf; 61 | void *base; 62 | 63 | if(!buf_len) 64 | throw std::runtime_error("circular_buffer: buffer len is 0"); 65 | 66 | if(!item_size) 67 | throw std::runtime_error("circular_buffer: item size is 0"); 68 | 69 | // calculate buffer size 70 | m_item_size = item_size; 71 | m_buf_size = item_size * buf_len; 72 | 73 | m_pagesize = getpagesize(); 74 | if(m_buf_size % m_pagesize) 75 | m_buf_size = (m_buf_size + m_pagesize) & ~(m_pagesize - 1); 76 | m_buf_len = m_buf_size / item_size; 77 | 78 | // create an address-range that can contain everything 79 | if((shm_id_temp = shmget(IPC_PRIVATE, 2 * m_pagesize + 2 * m_buf_size, 80 | IPC_CREAT | S_IRUSR | S_IWUSR)) == -1) { 81 | perror("shmget"); 82 | throw std::runtime_error("circular_buffer: shmget"); 83 | } 84 | 85 | // create a read-only guard page 86 | if((shm_id_guard = shmget(IPC_PRIVATE, m_pagesize, 87 | IPC_CREAT | S_IRUSR)) == -1) { 88 | shmctl(shm_id_temp, IPC_RMID, 0); 89 | perror("shmget"); 90 | throw std::runtime_error("circular_buffer: shmget"); 91 | } 92 | 93 | // create the data buffer 94 | if((shm_id_buf = shmget(IPC_PRIVATE, m_buf_size, IPC_CREAT | S_IRUSR | 95 | S_IWUSR)) == -1) { 96 | perror("shmget"); 97 | shmctl(shm_id_temp, IPC_RMID, 0); 98 | shmctl(shm_id_guard, IPC_RMID, 0); 99 | throw std::runtime_error("circular_buffer: shmget"); 100 | } 101 | 102 | // attach temporary memory to get an address-range 103 | if((base = shmat(shm_id_temp, 0, 0)) == (void *)(-1)) { 104 | perror("shmat"); 105 | shmctl(shm_id_temp, IPC_RMID, 0); 106 | shmctl(shm_id_guard, IPC_RMID, 0); 107 | shmctl(shm_id_buf, IPC_RMID, 0); 108 | throw std::runtime_error("circular_buffer: shmat"); 109 | } 110 | 111 | // remove the temporary memory id 112 | shmctl(shm_id_temp, IPC_RMID, 0); 113 | 114 | // detach and free the temporary memory 115 | shmdt(base); 116 | 117 | // race condition here 118 | 119 | // map first copy of guard page with previous address 120 | if(shmat(shm_id_guard, base, SHM_RDONLY) == (void *)(-1)) { 121 | perror("shmat"); 122 | shmctl(shm_id_guard, IPC_RMID, 0); 123 | shmctl(shm_id_buf, IPC_RMID, 0); 124 | throw std::runtime_error("circular_buffer: shmat"); 125 | } 126 | 127 | // map first copy of the buffer 128 | if(shmat(shm_id_buf, (char *)base + m_pagesize, 0) == (void *)(-1)) { 129 | perror("shmat"); 130 | shmctl(shm_id_guard, IPC_RMID, 0); 131 | shmctl(shm_id_buf, IPC_RMID, 0); 132 | shmdt(base); 133 | throw std::runtime_error("circular_buffer: shmat"); 134 | } 135 | 136 | // map second copy of the buffer 137 | if(shmat(shm_id_buf, (char *)base + m_pagesize + m_buf_size, 0) == 138 | (void *)(-1)) { 139 | perror("shmat"); 140 | shmctl(shm_id_guard, IPC_RMID, 0); 141 | shmctl(shm_id_buf, IPC_RMID, 0); 142 | shmdt((char *)base + m_pagesize); 143 | shmdt(base); 144 | throw std::runtime_error("circular_buffer: shmat"); 145 | } 146 | 147 | // map second copy of guard page 148 | if(shmat(shm_id_guard, (char *)base + m_pagesize + 2 * m_buf_size, 149 | SHM_RDONLY) == (void *)(-1)) { 150 | perror("shmat"); 151 | shmctl(shm_id_guard, IPC_RMID, 0); 152 | shmctl(shm_id_buf, IPC_RMID, 0); 153 | shmdt((char *)base + m_pagesize + m_buf_size); 154 | shmdt((char *)base + m_pagesize); 155 | shmdt((char *)base); 156 | throw std::runtime_error("circular_buffer: shmat"); 157 | } 158 | 159 | // remove the id for the guard and buffer, we don't need them anymore 160 | shmctl(shm_id_guard, IPC_RMID, 0); 161 | shmctl(shm_id_buf, IPC_RMID, 0); 162 | 163 | // save the base address for detach later 164 | m_base = base; 165 | 166 | // save a pointer to the data 167 | m_buf = (char *)base + m_pagesize; 168 | 169 | m_r = m_w = 0; 170 | m_read = m_written = 0; 171 | 172 | m_item_size = item_size; 173 | 174 | m_overwrite = overwrite; 175 | 176 | pthread_mutex_init(&m_mutex, 0); 177 | } 178 | 179 | circular_buffer::~circular_buffer() { 180 | 181 | shmdt((char *)m_base + m_pagesize + 2 * m_buf_size); 182 | shmdt((char *)m_base + m_pagesize + m_buf_size); 183 | shmdt((char *)m_base + m_pagesize); 184 | shmdt((char *)m_base); 185 | } 186 | 187 | #else 188 | circular_buffer::circular_buffer(const unsigned int buf_len, 189 | const unsigned int item_size, const unsigned int overwrite) { 190 | 191 | if(!buf_len) 192 | throw std::runtime_error("circular_buffer: buffer len is 0"); 193 | 194 | if(!item_size) 195 | throw std::runtime_error("circular_buffer: item size is 0"); 196 | 197 | // calculate buffer size 198 | m_item_size = item_size; 199 | m_buf_size = item_size * buf_len; 200 | m_buf_len = m_buf_size / item_size; 201 | 202 | 203 | d_handle = CreateFileMapping(INVALID_HANDLE_VALUE, // use paging file 204 | NULL, // default security 205 | PAGE_READWRITE, // read/write access 206 | 0, // max. object size 207 | m_buf_size, // buffer size 208 | NULL); // name of mapping object 209 | 210 | 211 | if (d_handle == NULL || d_handle == INVALID_HANDLE_VALUE){ 212 | throw std::runtime_error ("gr_vmcircbuf_mmap_createfilemapping"); 213 | } 214 | 215 | // Allocate virtual memory of the needed size, then free it so we can use it 216 | LPVOID first_tmp; 217 | first_tmp = VirtualAlloc( NULL, 2*m_buf_size, MEM_RESERVE, PAGE_NOACCESS ); 218 | if (first_tmp == NULL){ 219 | CloseHandle(d_handle); // cleanup 220 | throw std::runtime_error ("gr_vmcircbuf_mmap_createfilemapping"); 221 | } 222 | 223 | if (VirtualFree(first_tmp, 0, MEM_RELEASE) == 0){ 224 | CloseHandle(d_handle); // cleanup 225 | throw std::runtime_error ("gr_vmcircbuf_mmap_createfilemapping"); 226 | } 227 | 228 | d_first_copy = MapViewOfFileEx((HANDLE)d_handle, // handle to map object 229 | FILE_MAP_WRITE, // read/write permission 230 | 0, 231 | 0, 232 | m_buf_size, 233 | first_tmp); 234 | if (d_first_copy != first_tmp){ 235 | CloseHandle(d_handle); // cleanup 236 | throw std::runtime_error ("gr_vmcircbuf_mmap_createfilemapping"); 237 | } 238 | 239 | d_second_copy = MapViewOfFileEx((HANDLE)d_handle, // handle to map object 240 | FILE_MAP_WRITE, // read/write permission 241 | 0, 242 | 0, 243 | m_buf_size, 244 | (char *)first_tmp + m_buf_size);//(LPVOID) ((char *)d_first_copy + size)); 245 | 246 | if (d_second_copy != (char *)first_tmp + m_buf_size){ 247 | UnmapViewOfFile(d_first_copy); 248 | CloseHandle(d_handle); // cleanup 249 | throw std::runtime_error ("gr_vmcircbuf_mmap_createfilemapping"); 250 | } 251 | 252 | // save a pointer to the data 253 | m_buf = d_first_copy;// (char *)base + m_pagesize; 254 | 255 | m_r = m_w = 0; 256 | m_read = m_written = 0; 257 | 258 | m_item_size = item_size; 259 | 260 | m_overwrite = overwrite; 261 | 262 | pthread_mutex_init(&m_mutex, 0); 263 | 264 | } 265 | 266 | circular_buffer::~circular_buffer() { 267 | UnmapViewOfFile(d_first_copy); 268 | UnmapViewOfFile(d_second_copy); 269 | CloseHandle(d_handle); 270 | } 271 | #endif 272 | #else /* !D_HOST_OSX */ 273 | 274 | 275 | /* 276 | * OSX doesn't support System V shared memory. Using GNU Radio as an example, 277 | * we'll implement this for OSX using Posix shared memory. I'm not exactly 278 | * sure why GNU Radio prefers the System V usage, but I seem to recall there 279 | * was a reason. 280 | */ 281 | circular_buffer::circular_buffer(const unsigned int buf_len, 282 | const unsigned int item_size, const unsigned int overwrite) { 283 | 284 | int shm_fd; 285 | char shm_name[255]; // XXX should be NAME_MAX 286 | void *base; 287 | 288 | if(!buf_len) 289 | throw std::runtime_error("circular_buffer: buffer len is 0"); 290 | 291 | if(!item_size) 292 | throw std::runtime_error("circular_buffer: item size is 0"); 293 | 294 | // calculate buffer size 295 | m_item_size = item_size; 296 | m_buf_size = item_size * buf_len; 297 | 298 | m_pagesize = getpagesize(); 299 | if(m_buf_size % m_pagesize) 300 | m_buf_size = (m_buf_size + m_pagesize) & ~(m_pagesize - 1); 301 | m_buf_len = m_buf_size / item_size; 302 | 303 | // create unique-ish name 304 | snprintf(shm_name, sizeof(shm_name), "/kalibrate-%d", getpid()); 305 | 306 | // create a Posix shared memory object 307 | if((shm_fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) == -1) { 308 | perror("shm_open"); 309 | throw std::runtime_error("circular_buffer: shm_open"); 310 | } 311 | 312 | // create enough space to hold everything 313 | if(ftruncate(shm_fd, 2 * m_pagesize + 2 * m_buf_size) == -1) { 314 | perror("ftruncate"); 315 | close(shm_fd); 316 | shm_unlink(shm_name); 317 | throw std::runtime_error("circular_buffer: ftruncate"); 318 | } 319 | 320 | // get an address for the buffer 321 | if((base = mmap(0, 2 * m_pagesize + 2 * m_buf_size, PROT_NONE, MAP_SHARED, shm_fd, 0)) == MAP_FAILED) { 322 | perror("mmap"); 323 | close(shm_fd); 324 | shm_unlink(shm_name); 325 | throw std::runtime_error("circular_buffer: mmap (base)"); 326 | } 327 | 328 | // unmap everything but the first guard page 329 | if(munmap((char *)base + m_pagesize, m_pagesize + 2 * m_buf_size) == -1) { 330 | perror("munmap"); 331 | close(shm_fd); 332 | shm_unlink(shm_name); 333 | throw std::runtime_error("circular_buffer: munmap"); 334 | } 335 | 336 | // race condition 337 | 338 | // map first copy of the buffer 339 | if(mmap((char *)base + m_pagesize, m_buf_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, shm_fd, m_pagesize) == MAP_FAILED) { 340 | perror("mmap"); 341 | munmap(base, 2 * m_pagesize + 2 * m_buf_size); 342 | close(shm_fd); 343 | shm_unlink(shm_name); 344 | throw std::runtime_error("circular_buffer: mmap (buf 1)"); 345 | } 346 | 347 | // map second copy of the buffer 348 | 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) { 349 | perror("mmap"); 350 | munmap(base, 2 * m_pagesize + 2 * m_buf_size); 351 | close(shm_fd); 352 | shm_unlink(shm_name); 353 | throw std::runtime_error("circular_buffer: mmap (buf 2)"); 354 | } 355 | 356 | // map second copy of the guard page 357 | if(mmap((char *)base + m_pagesize + 2 * m_buf_size, m_pagesize, PROT_NONE, MAP_SHARED | MAP_FIXED, shm_fd, 0) == MAP_FAILED) { 358 | perror("mmap"); 359 | munmap(base, 2 * m_pagesize + 2 * m_buf_size); 360 | close(shm_fd); 361 | shm_unlink(shm_name); 362 | throw std::runtime_error("circular_buffer: mmap (guard)"); 363 | } 364 | 365 | // both the file and name are unnecessary now 366 | close(shm_fd); 367 | shm_unlink(shm_name); 368 | 369 | // save the base address for unmap later 370 | m_base = base; 371 | 372 | // save a pointer to the data 373 | m_buf = (char *)base + m_pagesize; 374 | 375 | m_r = m_w = 0; 376 | m_read = m_written = 0; 377 | 378 | m_item_size = item_size; 379 | 380 | m_overwrite = overwrite; 381 | 382 | pthread_mutex_init(&m_mutex, 0); 383 | } 384 | 385 | 386 | circular_buffer::~circular_buffer() { 387 | 388 | munmap(m_base, 2 * m_pagesize + 2 * m_buf_size); 389 | } 390 | #endif /* !D_HOST_OSX */ 391 | 392 | 393 | /* 394 | * The amount to read can only grow unless someone calls read after this is 395 | * called. No real good way to tie the two together. 396 | */ 397 | unsigned int circular_buffer::data_available() { 398 | 399 | unsigned int amt; 400 | 401 | pthread_mutex_lock(&m_mutex); 402 | amt = m_written - m_read; // item_size 403 | pthread_mutex_unlock(&m_mutex); 404 | 405 | return amt; 406 | } 407 | 408 | 409 | unsigned int circular_buffer::space_available() { 410 | 411 | unsigned int amt; 412 | 413 | pthread_mutex_lock(&m_mutex); 414 | amt = m_buf_len - (m_written - m_read); 415 | pthread_mutex_unlock(&m_mutex); 416 | 417 | return amt; 418 | } 419 | 420 | 421 | #ifndef MIN 422 | #define MIN(a, b) ((a)<(b)?(a):(b)) 423 | #endif /* !MIN */ 424 | 425 | /* 426 | * m_buf_size is in terms of bytes 427 | * m_r and m_w are offsets in bytes 428 | * m_buf_len is in terms of m_item_size 429 | * buf_len is in terms of m_item_size 430 | * len, m_written, and m_read are all in terms of m_item_size 431 | */ 432 | unsigned int circular_buffer::read(void *buf, const unsigned int buf_len) { 433 | 434 | unsigned int len; 435 | 436 | pthread_mutex_lock(&m_mutex); 437 | len = MIN(buf_len, m_written - m_read); 438 | memcpy(buf, (char *)m_buf + m_r, len * m_item_size); 439 | m_read += len; 440 | if(m_read == m_written) { 441 | m_r = m_w = 0; 442 | m_read = m_written = 0; 443 | } else 444 | m_r = (m_r + len * m_item_size) % m_buf_size; 445 | pthread_mutex_unlock(&m_mutex); 446 | 447 | return len; 448 | } 449 | 450 | 451 | /* 452 | * warning: 453 | * 454 | * Don't use read() while you are peek()'ing. write() should be 455 | * okay unless you have an overwrite buffer. 456 | */ 457 | void *circular_buffer::peek(unsigned int *buf_len) { 458 | 459 | unsigned int len; 460 | void *p; 461 | 462 | pthread_mutex_lock(&m_mutex); 463 | len = m_written - m_read; 464 | p = (char *)m_buf + m_r; 465 | pthread_mutex_unlock(&m_mutex); 466 | 467 | if(buf_len) 468 | *buf_len = len; 469 | 470 | return p; 471 | } 472 | 473 | 474 | void *circular_buffer::poke(unsigned int *buf_len) { 475 | 476 | unsigned int len; 477 | void *p; 478 | 479 | pthread_mutex_lock(&m_mutex); 480 | len = m_buf_len - (m_written - m_read); 481 | p = (char *)m_buf + m_w; 482 | pthread_mutex_unlock(&m_mutex); 483 | 484 | if(buf_len) 485 | *buf_len = len; 486 | 487 | return p; 488 | } 489 | 490 | 491 | unsigned int circular_buffer::purge(const unsigned int buf_len) { 492 | 493 | unsigned int len; 494 | 495 | pthread_mutex_lock(&m_mutex); 496 | len = MIN(buf_len, m_written - m_read); 497 | m_read += len; 498 | if(m_read == m_written) { 499 | m_r = m_w = 0; 500 | m_read = m_written = 0; 501 | } else 502 | m_r = (m_r + len * m_item_size) % m_buf_size; 503 | pthread_mutex_unlock(&m_mutex); 504 | 505 | return len; 506 | } 507 | 508 | 509 | unsigned int circular_buffer::write(const void *buf, 510 | const unsigned int buf_len) { 511 | 512 | unsigned int len, buf_off = 0; 513 | 514 | pthread_mutex_lock(&m_mutex); 515 | if(m_overwrite) { 516 | if(buf_len > m_buf_len) { 517 | buf_off = buf_len - m_buf_len; 518 | len = m_buf_len; 519 | } else 520 | len = buf_len; 521 | } else 522 | len = MIN(buf_len, m_buf_len - (m_written - m_read)); 523 | memcpy((char *)m_buf + m_w, (char *)buf + buf_off * m_item_size, 524 | len * m_item_size); 525 | m_written += len; 526 | m_w = (m_w + len * m_item_size) % m_buf_size; 527 | if(m_written > m_buf_len + m_read) { 528 | m_read = m_written - m_buf_len; 529 | m_r = m_w; 530 | } 531 | pthread_mutex_unlock(&m_mutex); 532 | 533 | return len; 534 | } 535 | 536 | 537 | void circular_buffer::wrote(unsigned int len) { 538 | 539 | pthread_mutex_lock(&m_mutex); 540 | m_written += len; 541 | m_w = (m_w + len * m_item_size) % m_buf_size; 542 | pthread_mutex_unlock(&m_mutex); 543 | } 544 | 545 | 546 | void circular_buffer::flush() { 547 | 548 | pthread_mutex_lock(&m_mutex); 549 | m_read = m_written = 0; 550 | m_r = m_w = 0; 551 | pthread_mutex_unlock(&m_mutex); 552 | } 553 | 554 | 555 | void circular_buffer::flush_nolock() { 556 | 557 | m_read = m_written = 0; 558 | m_r = m_w = 0; 559 | } 560 | 561 | 562 | void circular_buffer::lock() { 563 | 564 | pthread_mutex_lock(&m_mutex); 565 | } 566 | 567 | 568 | void circular_buffer::unlock() { 569 | 570 | pthread_mutex_unlock(&m_mutex); 571 | } 572 | 573 | 574 | unsigned int circular_buffer::buf_len() { 575 | 576 | return m_buf_len; 577 | } 578 | -------------------------------------------------------------------------------- /src/circular_buffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Joshua Lackey 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | /* 29 | * circular_buffer 30 | * 31 | * This class is based heavily on the GNU Radio circular buffer. While this 32 | * class was written from scratch and contains ideas not present in the GNU 33 | * Radio implementation, the GNU Radio circular buffers were used as a 34 | * reference while developing this class. 35 | * 36 | * This is more a warning that the above BSD-style license may not be the only 37 | * copyright that applies. 38 | */ 39 | 40 | #pragma once 41 | 42 | /* 43 | * XXX If read doesn't catch up with write before 2**64 bytes are written, this 44 | * will break. 45 | */ 46 | 47 | #include 48 | #ifdef _WIN32 49 | #include 50 | #endif 51 | 52 | class circular_buffer { 53 | public: 54 | circular_buffer(const unsigned int buf_len, const unsigned int item_size = 1, const unsigned int overwrite = 0); 55 | ~circular_buffer(); 56 | 57 | unsigned int read(void *buf, const unsigned int buf_len); 58 | void *peek(unsigned int *buf_len); 59 | unsigned int purge(const unsigned int buf_len); 60 | void *poke(unsigned int *buf_len); 61 | void wrote(unsigned int len); 62 | unsigned int write(const void *buf, const unsigned int buf_len); 63 | unsigned int data_available(); 64 | unsigned int space_available(); 65 | void flush(); 66 | void flush_nolock(); 67 | void lock(); 68 | void unlock(); 69 | unsigned int buf_len(); 70 | 71 | private: 72 | #ifdef _WIN32 73 | HANDLE d_handle; 74 | LPVOID d_first_copy; 75 | LPVOID d_second_copy; 76 | #endif 77 | void *m_buf; 78 | unsigned int m_buf_len, m_buf_size, m_r, m_w, m_item_size; 79 | unsigned long long m_read, m_written; 80 | 81 | unsigned int m_overwrite; 82 | 83 | void *m_base; 84 | unsigned int m_pagesize; 85 | 86 | pthread_mutex_t m_mutex; 87 | }; 88 | -------------------------------------------------------------------------------- /src/fcch_detector.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Joshua Lackey 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | /* 29 | * This is based on the algorithm found in the paper, 30 | * 31 | * Varma, G. Narendra, Usha Sahu, and G. Prabhu Charan. "Robust 32 | * Frequency Burst Detection Algorithm for GSM / GPRS." 33 | * 34 | * The algorithm uses an adaptive filter to calculate the error difference from 35 | * a pure tone. When the error goes low, the tone is detected. When it goes 36 | * back high, the scan function returns and indicates the number of samples the 37 | * error was low. 38 | * 39 | * The following code is an original work and the above BSD-license should 40 | * apply. However, the algorithm itself may be patented and any use of this 41 | * code should take that into consideration. 42 | */ 43 | 44 | #include // for debug 45 | #include 46 | 47 | #include 48 | #include 49 | #include "fcch_detector.h" 50 | 51 | extern int g_debug; 52 | 53 | static const char * const fftw_plan_name = ".kal_fftw_plan"; 54 | 55 | 56 | fcch_detector::fcch_detector(const float sample_rate, const unsigned int D, 57 | const float p, const float G) { 58 | 59 | FILE *plan_fp; 60 | char plan_name[BUFSIZ]; 61 | const char *home; 62 | 63 | 64 | m_D = D; 65 | m_p = p; 66 | m_G = G; 67 | m_e = 0.0; 68 | 69 | m_sample_rate = sample_rate; 70 | m_fcch_burst_len = 71 | (unsigned int)(148.0 * (m_sample_rate / GSM_RATE)); 72 | 73 | m_filter_delay = 8; 74 | m_w_len = 2 * m_filter_delay + 1; 75 | m_w = new complex[m_w_len]; 76 | memset(m_w, 0, sizeof(complex) * m_w_len); 77 | 78 | m_x_cb = new circular_buffer(8192, sizeof(complex), 0); 79 | m_y_cb = new circular_buffer(8192, sizeof(complex), 1); 80 | m_e_cb = new circular_buffer(1015808, sizeof(float), 0); 81 | 82 | m_in = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * FFT_SIZE); 83 | m_out = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * FFT_SIZE); 84 | if((!m_in) || (!m_out)) 85 | throw std::runtime_error("fcch_detector: fftw_malloc failed!"); 86 | #ifndef _WIN32 87 | home = getenv("HOME"); 88 | if(strlen(home) + strlen(fftw_plan_name) + 2 < sizeof(plan_name)) { 89 | strcpy(plan_name, home); 90 | strcat(plan_name, "/"); 91 | strcat(plan_name, fftw_plan_name); 92 | if((plan_fp = fopen(plan_name, "r"))) { 93 | fftw_import_wisdom_from_file(plan_fp); 94 | fclose(plan_fp); 95 | } 96 | m_plan = fftw_plan_dft_1d(FFT_SIZE, m_in, m_out, FFTW_FORWARD, 97 | FFTW_MEASURE); 98 | if((plan_fp = fopen(plan_name, "w"))) { 99 | fftw_export_wisdom_to_file(plan_fp); 100 | fclose(plan_fp); 101 | } 102 | } else 103 | #endif 104 | m_plan = fftw_plan_dft_1d(FFT_SIZE, m_in, m_out, FFTW_FORWARD, 105 | FFTW_ESTIMATE); 106 | if(!m_plan) 107 | throw std::runtime_error("fcch_detector: fftw plan failed!"); 108 | } 109 | 110 | 111 | fcch_detector::~fcch_detector() { 112 | 113 | if(m_w) { 114 | delete[] m_w; 115 | m_w = 0; 116 | } 117 | if(m_x_cb) { 118 | delete m_x_cb; 119 | m_x_cb = 0; 120 | } 121 | if(m_y_cb) { 122 | delete m_y_cb; 123 | m_y_cb = 0; 124 | } 125 | if(m_e_cb) { 126 | delete m_e_cb; 127 | m_e_cb = 0; 128 | } 129 | } 130 | 131 | 132 | enum { 133 | LOW = 0, 134 | HIGH = 1 135 | }; 136 | 137 | static unsigned int g_count = 0, 138 | g_block_s = HIGH; 139 | 140 | 141 | static inline void low_to_high_init() { 142 | 143 | g_count = 0; 144 | g_block_s = HIGH; 145 | } 146 | 147 | 148 | static inline unsigned int low_to_high(float e, float a) { 149 | 150 | unsigned int r = 0; 151 | 152 | if(e > a) { 153 | if(g_block_s == LOW) { 154 | r = g_count; 155 | g_block_s = HIGH; 156 | g_count = 0; 157 | } 158 | g_count += 1; 159 | } else { 160 | if(g_block_s == HIGH) { 161 | g_block_s = LOW; 162 | g_count = 0; 163 | } 164 | g_count += 1; 165 | } 166 | 167 | return r; 168 | } 169 | 170 | 171 | static inline int peak_valley(complex *c, unsigned int c_len, complex peak, unsigned int peak_i, unsigned int width, float *p2m) { 172 | 173 | float valley = 0.0; 174 | unsigned int i, valley_count = 0; 175 | 176 | // these constants aren't the best for all burst types 177 | for(i = 2; i < 2 + width; i++) { 178 | if(i <= peak_i) { 179 | valley += norm(c[peak_i - i]); 180 | valley_count += 1; 181 | } 182 | if(peak_i + i < c_len) { 183 | valley += norm(c[peak_i + i]); 184 | valley_count += 1; 185 | } 186 | } 187 | 188 | if(valley_count < 2) { 189 | fprintf(stderr, "error: bad valley_count\n"); 190 | return -1; 191 | } 192 | valley = sqrtf(valley / (float)valley_count) + 0.00001; 193 | 194 | if(p2m) 195 | *p2m = sqrtf(norm(peak)) / valley; 196 | 197 | return 0; 198 | } 199 | 200 | 201 | static inline float sinc(const float x) { 202 | 203 | if((x <= -0.0001) || (0.0001 <= x)) 204 | return sinf(x) / x; 205 | return 1.0; 206 | } 207 | 208 | 209 | static inline complex interpolate_point(const complex *s, const unsigned int s_len, const float s_i) { 210 | 211 | static const unsigned int filter_len = 21; 212 | 213 | int start, end, i; 214 | unsigned int d; 215 | complex point; 216 | 217 | d = (filter_len - 1) / 2; 218 | start = (int)(floor(s_i) - d); 219 | end = (int)(floor(s_i) + d + 1); 220 | if(start < 0) 221 | start = 0; 222 | if(end > (int)(s_len - 1)) 223 | end = s_len - 1; 224 | for(point = 0.0, i = start; i <= end; i++) 225 | point += s[i] * sinc(M_PI * (i - s_i)); 226 | return point; 227 | } 228 | 229 | 230 | static inline float peak_detect(const complex *s, const unsigned int s_len, complex *peak, float *avg_power) { 231 | 232 | unsigned int i; 233 | float max = -1.0, max_i = -1.0, sample_power, sum_power, early_i, late_i, incr; 234 | complex early_p, late_p, cmax; 235 | 236 | sum_power = 0; 237 | for(i = 0; i < s_len; i++) { 238 | sample_power = norm(s[i]); 239 | sum_power += sample_power; 240 | if(sample_power > max) { 241 | max = sample_power; 242 | max_i = i; 243 | } 244 | } 245 | early_i = (1 <= max_i)? (max_i - 1) : 0; 246 | late_i = (max_i + 1 < s_len)? (max_i + 1) : s_len - 1; 247 | 248 | incr = 0.5; 249 | while(incr > 1.0 / 1024.0) { 250 | early_p = interpolate_point(s, s_len, early_i); 251 | late_p = interpolate_point(s, s_len, late_i); 252 | if(norm(early_p) < norm(late_p)) 253 | early_i += incr; 254 | else if(norm(early_p) > norm(late_p)) 255 | early_i -= incr; 256 | else 257 | break; 258 | incr /= 2.0; 259 | late_i = early_i + 2.0; 260 | } 261 | max_i = early_i + 1.0; 262 | cmax = interpolate_point(s, s_len, max_i); 263 | 264 | if(peak) 265 | *peak = cmax; 266 | 267 | if(avg_power) 268 | *avg_power = (sum_power - norm(cmax)) / (s_len - 1); 269 | 270 | return max_i; 271 | } 272 | 273 | 274 | static inline float itof(float index, float sample_rate, unsigned int fft_size) { 275 | 276 | double r = index * (sample_rate / (double)fft_size); 277 | 278 | /* 279 | if(index > (double)fft_size / 2.0) 280 | return r - sample_rate; 281 | else 282 | return r; 283 | */ 284 | return r; 285 | } 286 | 287 | 288 | static inline unsigned int ftoi(float frequency, float sample_rate, unsigned int fft_size) { 289 | 290 | unsigned int r = (frequency / sample_rate) * fft_size; 291 | 292 | return r; 293 | } 294 | 295 | 296 | #ifndef MIN 297 | #define MIN(a, b) (a)<(b)?(a):(b) 298 | #endif /* !MIN */ 299 | 300 | 301 | float fcch_detector::freq_detect(const complex *s, const unsigned int s_len, float *pm) { 302 | 303 | unsigned int i, len; 304 | float max_i, avg_power; 305 | complex fft[FFT_SIZE], peak; 306 | 307 | len = MIN(s_len, FFT_SIZE); 308 | for(i = 0; i < len; i++) { 309 | m_in[i][0] = s[i].real(); 310 | m_in[i][1] = s[i].imag(); 311 | } 312 | for(i = len; i < FFT_SIZE; i++) { 313 | m_in[i][0] = 0; 314 | m_in[i][1] = 0; 315 | } 316 | 317 | fftw_execute(m_plan); 318 | 319 | for(i = 0; i < FFT_SIZE; i++) { 320 | fft[i] = complex(m_out[i][0], m_out[i][1]); 321 | } 322 | 323 | max_i = peak_detect(fft, FFT_SIZE, &peak, &avg_power); 324 | if(pm) 325 | *pm = norm(peak) / avg_power; 326 | return itof(max_i, m_sample_rate, FFT_SIZE); 327 | } 328 | 329 | 330 | static inline void display_complex(const complex *s, unsigned int s_len) { 331 | 332 | for(unsigned int i = 0; i < s_len; i++) { 333 | printf("%f\n", s[i].real()); 334 | fprintf(stderr, "%f\n", s[i].imag()); 335 | } 336 | } 337 | 338 | 339 | /* 340 | * scan: 341 | * 1. calculate average error 342 | * 2. find neighborhoods with low error that satisfy minimum length 343 | * 3. for each such neighborhood, take fft and calculate peak/mean 344 | * 4. if peak/mean > 50, then this is a valid finding. 345 | */ 346 | unsigned int fcch_detector::scan(const complex *s, const unsigned int s_len, float *offset, unsigned int *consumed) { 347 | 348 | static const float sps = m_sample_rate / (1625000.0 / 6.0); 349 | static const unsigned int MIN_FB_LEN = 100 * sps; 350 | static const unsigned int MIN_PM = 50; // XXX arbitrary, depends on decimation 351 | 352 | unsigned int len = 0, t, e_count, i, l_count, y_offset, y_len; 353 | float e, *a, loff = 0, pm; 354 | double sum = 0.0, avg, limit; 355 | const complex *y; 356 | 357 | // calculate the error for each sample 358 | while(len < s_len) { 359 | t = m_x_cb->write(s + len, 1); 360 | len += t; 361 | if(!next_norm_error(&e)) { 362 | m_e_cb->write(&e, 1); 363 | sum += e; 364 | } 365 | } 366 | if(consumed) 367 | *consumed = len; 368 | 369 | // calculate average error over entire buffer 370 | a = (float *)m_e_cb->peek(&e_count); 371 | avg = sum / (double)e_count; 372 | limit = 0.7 * avg; 373 | 374 | if(g_debug) { 375 | printf("debug: error limit: %.1lf\n", limit); 376 | } 377 | 378 | // find neighborhoods where the error is smaller than the limit 379 | low_to_high_init(); 380 | for(i = 0; i < e_count; i++) { 381 | l_count = low_to_high(a[i], limit); 382 | 383 | // see if p/m indicates a pure tone 384 | pm = 0; 385 | if(l_count >= MIN_FB_LEN) { 386 | y_offset = i - l_count; 387 | y_len = (l_count < m_fcch_burst_len)? l_count : m_fcch_burst_len; 388 | y = s + y_offset; 389 | loff = freq_detect(y, y_len, &pm); 390 | if(g_debug) 391 | printf("debug: %.0f\t%f\t%f\n", (double)l_count / sps, pm, loff); 392 | if(pm > MIN_PM) 393 | break; 394 | } 395 | } 396 | // empty buffers for next call 397 | m_e_cb->flush(); 398 | m_x_cb->flush(); 399 | m_y_cb->flush(); 400 | 401 | if(pm <= MIN_PM) 402 | return 0; 403 | 404 | if(offset) 405 | *offset = loff; 406 | 407 | if(g_debug) { 408 | printf("debug: fcch_detector finished -----------------------------\n"); 409 | } 410 | 411 | return 1; 412 | } 413 | 414 | 415 | unsigned int fcch_detector::update(const complex *s, const unsigned int s_len) { 416 | 417 | return m_x_cb->write(s, s_len); 418 | } 419 | 420 | 421 | unsigned int fcch_detector::get_delay() { 422 | 423 | return m_w_len - 1 + m_D; 424 | } 425 | 426 | 427 | unsigned int fcch_detector::filter_len() { 428 | 429 | return m_w_len; 430 | } 431 | 432 | 433 | static float vectornorm2(const complex *v, const unsigned int len) { 434 | 435 | unsigned int i; 436 | float e = 0.0; 437 | 438 | for(i = 0; i < len; i++) 439 | e += norm(v[i]); 440 | 441 | return e; 442 | } 443 | 444 | 445 | /* 446 | * First y value comes out at sample x[n + m_D] = x[w_len - 1 + m_D]. 447 | * 448 | * y[0] = X(x[0], ..., x[w_len - 1 + m_D]) 449 | * 450 | * So y and e are delayed by w_len - 1 + m_D. 451 | */ 452 | int fcch_detector::next_norm_error(float *error) { 453 | 454 | unsigned int i, n, max; 455 | float E; 456 | complex *x, y, e; 457 | 458 | // n is "current" sample 459 | n = m_w_len - 1; 460 | 461 | // ensure there are enough samples in the buffer 462 | x = (complex *)m_x_cb->peek(&max); 463 | if(n + m_D >= max) 464 | return n + m_D - max + 1; 465 | 466 | // update G 467 | E = vectornorm2(x, m_w_len); 468 | if(m_G >= 2.0 / E) 469 | m_G = 1.0 / E; 470 | 471 | // calculate filtered value 472 | y = 0.0; 473 | for(i = 0; i < m_w_len; i++) 474 | y += std::conj(m_w[i]) * x[n - i]; 475 | // m_y_cb->write(&y, 1); 476 | m_y_cb->write(x + n + m_D, 1); // XXX save filtered value? 477 | 478 | // calculate error from desired signal 479 | e = x[n + m_D] - y; 480 | 481 | // update filters with opposite gradient 482 | for(i = 0; i < m_w_len; i++) 483 | m_w[i] += m_G * std::conj(e) * x[n - i]; 484 | 485 | // update error average power 486 | E /= m_w_len; 487 | m_e = (1.0 - m_p) * m_e + m_p * norm(e); 488 | 489 | // return error ratio 490 | if(error) 491 | *error = m_e / E; 492 | 493 | // remove the processed sample from the buffer 494 | m_x_cb->purge(1); 495 | 496 | return 0; 497 | } 498 | 499 | 500 | complex *fcch_detector::dump_x(unsigned int *x_len) { 501 | 502 | return (complex *)m_x_cb->peek(x_len); 503 | } 504 | 505 | 506 | complex *fcch_detector::dump_y(unsigned int *y_len) { 507 | 508 | return (complex *)m_y_cb->peek(y_len); 509 | } 510 | 511 | 512 | unsigned int fcch_detector::y_buf_len() { 513 | 514 | return m_y_cb->buf_len(); 515 | } 516 | 517 | 518 | unsigned int fcch_detector::x_buf_len() { 519 | 520 | return m_x_cb->buf_len(); 521 | } 522 | 523 | 524 | unsigned int fcch_detector::x_purge(unsigned int len) { 525 | 526 | return m_x_cb->purge(len); 527 | } 528 | -------------------------------------------------------------------------------- /src/fcch_detector.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Joshua Lackey 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | /* 29 | * This is based on the algorithm found in the paper, 30 | * 31 | * Varma, G. Narendra, Usha Sahu, and G. Prabhu Charan. "Robust 32 | * Frequency Burst Detection Algorithm for GSM / GPRS." 33 | * 34 | * The algorithm uses an adaptive filter to calculate the error difference from 35 | * a pure tone. When the error goes low, the tone is detected. When it goes 36 | * back high, the scan function returns and indicates the number of samples the 37 | * error was low. 38 | * 39 | * The following code is an original work and the above BSD-license should 40 | * apply. However, the algorithm itself may be patented and any use of this 41 | * code should take that into consideration. 42 | */ 43 | 44 | #include 45 | 46 | #include "circular_buffer.h" 47 | #include "usrp_complex.h" 48 | 49 | class fcch_detector { 50 | 51 | public: 52 | 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); 53 | ~fcch_detector(); 54 | unsigned int scan(const complex *s, const unsigned int s_len, float *offset, unsigned int *consumed); 55 | float freq_detect(const complex *s, const unsigned int s_len, float *pm); 56 | unsigned int update(const complex *s, unsigned int s_len); 57 | int next_norm_error(float *error); 58 | complex *dump_x(unsigned int *); 59 | complex *dump_y(unsigned int *); 60 | unsigned int filter_delay() { return m_filter_delay; }; 61 | unsigned int get_delay(); 62 | unsigned int filter_len(); 63 | unsigned int x_buf_len(); 64 | unsigned int y_buf_len(); 65 | unsigned int x_purge(unsigned int); 66 | 67 | private: 68 | #define GSM_RATE (1625000.0 / 6.0) 69 | #define FFT_SIZE 1024 70 | 71 | unsigned int m_w_len, 72 | m_D, 73 | m_check_G, 74 | m_filter_delay, 75 | m_lpf_len, 76 | m_fcch_burst_len; 77 | float m_sample_rate, 78 | m_p, 79 | m_G, 80 | m_e; 81 | complex *m_w; 82 | circular_buffer *m_x_cb, 83 | *m_y_cb, 84 | *m_e_cb; 85 | 86 | fftw_complex *m_in, *m_out; 87 | fftw_plan m_plan; 88 | }; 89 | -------------------------------------------------------------------------------- /src/kal.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Joshua Lackey 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | /* 29 | * kal 30 | * 31 | * Two functions: 32 | * 33 | * 1. Calculates the frequency offset between a local GSM tower and the 34 | * USRP clock. 35 | * 36 | * 2. Identifies the frequency of all GSM base stations in a given band. 37 | */ 38 | 39 | #ifdef HAVE_CONFIG_H 40 | #include "config.h" 41 | #else 42 | #define PACKAGE_VERSION "custom build" 43 | #endif /* HAVE_CONFIG_H */ 44 | 45 | #include 46 | #include 47 | #ifndef _WIN32 48 | #include 49 | #include 50 | #endif 51 | #ifdef HAVE_LIBGEN_H 52 | #include 53 | #endif 54 | #include 55 | 56 | #include 57 | 58 | #include "usrp_source.h" 59 | #include "fcch_detector.h" 60 | #include "arfcn_freq.h" 61 | #include "offset.h" 62 | #include "c0_detect.h" 63 | #include "version.h" 64 | #ifdef _WIN32 65 | #include 66 | #define basename(x) "meh" 67 | #define strtof strtod 68 | #endif 69 | 70 | #define GSM_RATE (1625000.0 / 6.0) 71 | 72 | 73 | int g_verbosity = 0; 74 | int g_debug = 0; 75 | 76 | void usage(char *prog) { 77 | 78 | printf("kalibrate v%s-rtl, Copyright (c) 2010, Joshua Lackey\n", kal_version_string); 79 | printf("modified for use with rtl-sdr devices, Copyright (c) 2012, Steve Markgraf"); 80 | printf("\nUsage:\n"); 81 | printf("\tGSM Base Station Scan:\n"); 82 | printf("\t\t%s <-s band indicator> [options]\n", basename(prog)); 83 | printf("\n"); 84 | printf("\tClock Offset Calculation:\n"); 85 | printf("\t\t%s <-f frequency | -c channel> [options]\n", basename(prog)); 86 | printf("\n"); 87 | printf("Where options are:\n"); 88 | printf("\t-s\tband to scan (GSM850, GSM-R, GSM900, EGSM, DCS, PCS)\n"); 89 | printf("\t-f\tfrequency of nearby GSM base station\n"); 90 | printf("\t-c\tchannel of nearby GSM base station\n"); 91 | printf("\t-b\tband indicator (GSM850, GSM-R, GSM900, EGSM, DCS, PCS)\n"); 92 | printf("\t-g\tgain in dB\n"); 93 | printf("\t-d\trtl-sdr device index\n"); 94 | printf("\t-e\tinitial frequency error in ppm\n"); 95 | #if HAVE_DITHERING == 1 96 | printf("\t-N\tdisable dithering (default: dithering enabled)\n"); 97 | #endif 98 | printf("\t-E\tmanual frequency offset in hz\n"); 99 | printf("\t-v\tverbose\n"); 100 | printf("\t-D\tenable debug messages\n"); 101 | printf("\t-h\thelp\n"); 102 | exit(-1); 103 | } 104 | 105 | 106 | int main(int argc, char **argv) { 107 | 108 | char *endptr; 109 | int c, antenna = 1, bi = BI_NOT_DEFINED, chan = -1, bts_scan = 0; 110 | int ppm_error = 0, hz_adjust = 0; 111 | int dithering = true; 112 | unsigned int subdev = 0, decimation = 192; 113 | long int fpga_master_clock_freq = 52000000; 114 | float gain = 0; 115 | double freq = -1.0, fd; 116 | usrp_source *u; 117 | 118 | while((c = getopt(argc, argv, "f:c:s:b:R:A:g:e:E:Nd:vDh?")) != EOF) { 119 | switch(c) { 120 | case 'f': 121 | freq = strtod(optarg, 0); 122 | break; 123 | 124 | case 'c': 125 | chan = strtoul(optarg, 0, 0); 126 | break; 127 | 128 | case 's': 129 | if((bi = str_to_bi(optarg)) == -1) { 130 | fprintf(stderr, "error: bad band " 131 | "indicator: ``%s''\n", optarg); 132 | usage(argv[0]); 133 | } 134 | bts_scan = 1; 135 | break; 136 | 137 | case 'b': 138 | if((bi = str_to_bi(optarg)) == -1) { 139 | fprintf(stderr, "error: bad band " 140 | "indicator: ``%s''\n", optarg); 141 | usage(argv[0]); 142 | } 143 | break; 144 | 145 | case 'R': 146 | errno = 0; 147 | subdev = strtoul(optarg, &endptr, 0); 148 | if((!errno) && (endptr != optarg)) 149 | break; 150 | if(tolower(*optarg) == 'a') { 151 | subdev = 0; 152 | } else if(tolower(*optarg) == 'b') { 153 | subdev = 1; 154 | } else { 155 | fprintf(stderr, "error: bad side: " 156 | "``%s''\n", 157 | optarg); 158 | usage(argv[0]); 159 | } 160 | break; 161 | 162 | case 'A': 163 | if(!strcmp(optarg, "RX2")) { 164 | antenna = 1; 165 | } else if(!strcmp(optarg, "TX/RX")) { 166 | antenna = 0; 167 | } else { 168 | errno = 0; 169 | antenna = strtoul(optarg, &endptr, 0); 170 | if(errno || (endptr == optarg)) { 171 | fprintf(stderr, "error: bad " 172 | "antenna: ``%s''\n", 173 | optarg); 174 | usage(argv[0]); 175 | } 176 | } 177 | break; 178 | 179 | case 'g': 180 | gain = strtof(optarg, 0) * 10; 181 | break; 182 | 183 | case 'F': 184 | fpga_master_clock_freq = strtol(optarg, 0, 0); 185 | if(!fpga_master_clock_freq) 186 | fpga_master_clock_freq = (long int)strtod(optarg, 0); 187 | 188 | // was answer in MHz? 189 | if(fpga_master_clock_freq < 1000) { 190 | fpga_master_clock_freq *= 1000000; 191 | } 192 | break; 193 | 194 | case 'e': 195 | ppm_error = strtol(optarg, 0, 0); 196 | break; 197 | 198 | case 'N': 199 | dithering = false; 200 | break; 201 | 202 | case 'E': 203 | hz_adjust = strtol(optarg, 0, 0); 204 | break; 205 | 206 | case 'd': 207 | subdev = strtol(optarg, 0, 0); 208 | break; 209 | 210 | case 'v': 211 | g_verbosity++; 212 | break; 213 | 214 | case 'D': 215 | g_debug = 1; 216 | break; 217 | 218 | case 'h': 219 | case '?': 220 | default: 221 | usage(argv[0]); 222 | break; 223 | } 224 | 225 | } 226 | 227 | // sanity check frequency / channel 228 | if(bts_scan) { 229 | if(bi == BI_NOT_DEFINED) { 230 | fprintf(stderr, "error: scaning requires band\n"); 231 | usage(argv[0]); 232 | } 233 | } else { 234 | if(freq < 0.0) { 235 | if(chan < 0) { 236 | fprintf(stderr, "error: must enter channel or " 237 | "frequency\n"); 238 | usage(argv[0]); 239 | } 240 | if((freq = arfcn_to_freq(chan, &bi)) < 869e6) 241 | usage(argv[0]); 242 | } 243 | if((freq < 869e6) || (2e9 < freq)) { 244 | fprintf(stderr, "error: bad frequency: %lf\n", freq); 245 | usage(argv[0]); 246 | } 247 | chan = freq_to_arfcn(freq, &bi); 248 | } 249 | 250 | #if 0 251 | // sanity check clock 252 | if(fpga_master_clock_freq < 48000000) { 253 | fprintf(stderr, "error: FPGA master clock too slow: %li\n", fpga_master_clock_freq); 254 | usage(argv[0]); 255 | } 256 | 257 | // calculate decimation -- get as close to GSM rate as we can 258 | fd = (double)fpga_master_clock_freq / GSM_RATE; 259 | decimation = (unsigned int)fd; 260 | #endif 261 | if(g_debug) { 262 | #ifdef D_HOST_OSX 263 | printf("debug: Mac OS X version\n"); 264 | #endif 265 | printf("debug: FPGA Master Clock Freq:\t%li\n", fpga_master_clock_freq); 266 | printf("debug: decimation :\t%u\n", decimation); 267 | printf("debug: RX Subdev Spec :\t%s\n", subdev? "B" : "A"); 268 | printf("debug: Antenna :\t%s\n", antenna? "RX2" : "TX/RX"); 269 | printf("debug: Gain :\t%f\n", gain); 270 | } 271 | 272 | u = new usrp_source(decimation, fpga_master_clock_freq); 273 | if(!u) { 274 | fprintf(stderr, "error: usrp_source\n"); 275 | return -1; 276 | } 277 | if(u->open(subdev) == -1) { 278 | fprintf(stderr, "error: usrp_source::open\n"); 279 | return -1; 280 | } 281 | 282 | /* Enable/disable dithering */ 283 | if (!u->set_dithering(dithering)) { 284 | fprintf(stderr, "error: usrp_source::set_dithering\n"); 285 | } 286 | 287 | // u->set_antenna(antenna); 288 | if (gain != 0) { 289 | if(!u->set_gain(gain)) { 290 | fprintf(stderr, "error: usrp_source::set_gain\n"); 291 | return -1; 292 | } 293 | } 294 | 295 | if (ppm_error != 0) { 296 | if(u->set_freq_correction(ppm_error) < 0) { 297 | fprintf(stderr, "error: usrp_source::set_freq_correction\n"); 298 | return -1; 299 | } 300 | } 301 | 302 | if(!bts_scan) { 303 | if(!u->tune(freq+hz_adjust)) { 304 | fprintf(stderr, "error: usrp_source::tune\n"); 305 | return -1; 306 | } 307 | 308 | double tuner_error = u->m_center_freq - freq; 309 | 310 | fprintf(stderr, "%s: Calculating clock frequency offset.\n", 311 | basename(argv[0])); 312 | fprintf(stderr, "Using %s channel %d (%.1fMHz)\n", 313 | bi_to_str(bi), chan, freq / 1e6); 314 | fprintf(stderr, "Tuned to %.6fMHz (reported tuner error: %.0fHz)\n", 315 | u->m_center_freq / 1e6, tuner_error); 316 | 317 | return offset_detect(u, hz_adjust, tuner_error); 318 | } 319 | 320 | fprintf(stderr, "%s: Scanning for %s base stations.\n", 321 | basename(argv[0]), bi_to_str(bi)); 322 | 323 | return c0_detect(u, bi); 324 | } 325 | -------------------------------------------------------------------------------- /src/offset.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Joshua Lackey 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include "usrp_source.h" 29 | #include "fcch_detector.h" 30 | #include "util.h" 31 | 32 | #ifdef _WIN32 33 | inline double round(double x) { return floor(x + 0.5); } 34 | #endif 35 | 36 | static const unsigned int AVG_COUNT = 100; 37 | static const unsigned int AVG_THRESHOLD = (AVG_COUNT / 10); 38 | static const float OFFSET_MAX = 40e3; 39 | 40 | extern int g_verbosity; 41 | 42 | 43 | int offset_detect(usrp_source *u, int hz_adjust, float tuner_error) { 44 | 45 | #define GSM_RATE (1625000.0 / 6.0) 46 | 47 | unsigned int new_overruns = 0, overruns = 0; 48 | int notfound = 0; 49 | unsigned int s_len, b_len, consumed, count; 50 | float offset = 0.0, min = 0.0, max = 0.0, avg_offset = 0.0, 51 | stddev = 0.0, sps, offsets[AVG_COUNT]; 52 | double total_ppm; 53 | complex *cbuf; 54 | fcch_detector *l; 55 | circular_buffer *cb; 56 | 57 | l = new fcch_detector(u->sample_rate()); 58 | 59 | /* 60 | * We deliberately grab 12 frames and 1 burst. We are guaranteed to 61 | * find at least one FCCH burst in this much data. 62 | */ 63 | sps = u->sample_rate() / GSM_RATE; 64 | s_len = (unsigned int)ceil((12 * 8 * 156.25 + 156.25) * sps); 65 | cb = u->get_buffer(); 66 | 67 | u->start(); 68 | u->flush(); 69 | count = 0; 70 | while(count < AVG_COUNT) { 71 | 72 | // ensure at least s_len contiguous samples are read from usrp 73 | do { 74 | if(u->fill(s_len, &new_overruns)) { 75 | return -1; 76 | } 77 | if(new_overruns) { 78 | overruns += new_overruns; 79 | u->flush(); 80 | } 81 | } while(new_overruns); 82 | 83 | // get a pointer to the next samples 84 | cbuf = (complex *)cb->peek(&b_len); 85 | 86 | // search the buffer for a pure tone 87 | if(l->scan(cbuf, b_len, &offset, &consumed)) { 88 | 89 | // FCH is a sine wave at GSM_RATE / 4 90 | offset = offset - GSM_RATE / 4 - tuner_error; 91 | 92 | // sanity check offset 93 | if(fabs(offset) < OFFSET_MAX) { 94 | 95 | offsets[count] = offset; 96 | count += 1; 97 | 98 | if(g_verbosity > 0) { 99 | fprintf(stderr, "\toffset %3u: %.2f\n", count, offset); 100 | } 101 | } 102 | } else { 103 | ++notfound; 104 | } 105 | 106 | // consume used samples 107 | cb->purge(consumed); 108 | } 109 | 110 | u->stop(); 111 | delete l; 112 | 113 | // construct stats 114 | sort(offsets, AVG_COUNT); 115 | avg_offset = avg(offsets + AVG_THRESHOLD, AVG_COUNT - 2 * AVG_THRESHOLD, &stddev); 116 | min = offsets[AVG_THRESHOLD]; 117 | max = offsets[AVG_COUNT - AVG_THRESHOLD - 1]; 118 | 119 | printf("average\t\t[min, max]\t(range, stddev)\n"); 120 | display_freq(avg_offset); 121 | printf("\t\t[%d, %d]\t(%d, %f)\n", (int)round(min), (int)round(max), (int)round(max - min), stddev); 122 | printf("overruns: %u\n", overruns); 123 | printf("not found: %u\n", notfound); 124 | 125 | total_ppm = u->m_freq_corr - ((avg_offset + hz_adjust) / u->m_center_freq) * 1000000; 126 | 127 | printf("average absolute error: %.3f ppm\n", total_ppm); 128 | return 0; 129 | } 130 | -------------------------------------------------------------------------------- /src/offset.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Joshua Lackey 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | int offset_detect(usrp_source *u, int hz_adjust, float tuner_error); 29 | -------------------------------------------------------------------------------- /src/usrp_complex.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Joshua Lackey 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | 30 | typedef std::complex complex; 31 | 32 | -------------------------------------------------------------------------------- /src/usrp_source.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Joshua Lackey 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | 29 | #ifdef HAVE_CONFIG_H 30 | #include "config.h" 31 | #endif 32 | 33 | #include 34 | #include 35 | #ifndef _WIN32 36 | #include 37 | #endif 38 | #include 39 | #include 40 | #define _USE_MATH_DEFINES 41 | #include 42 | #include 43 | 44 | #include "usrp_source.h" 45 | 46 | extern int g_verbosity; 47 | 48 | 49 | #ifdef _WIN32 50 | inline double round(double x) { return floor(x + 0.5); } 51 | #endif 52 | 53 | usrp_source::usrp_source(float sample_rate, long int fpga_master_clock_freq) { 54 | 55 | m_fpga_master_clock_freq = fpga_master_clock_freq; 56 | m_desired_sample_rate = sample_rate; 57 | m_center_freq = 0.0; 58 | m_sample_rate = 0.0; 59 | m_decimation = 0; 60 | m_cb = new circular_buffer(CB_LEN, sizeof(complex), 0); 61 | m_freq_corr = 0; 62 | 63 | pthread_mutex_init(&m_u_mutex, 0); 64 | } 65 | 66 | 67 | usrp_source::usrp_source(unsigned int decimation, long int fpga_master_clock_freq) { 68 | 69 | m_fpga_master_clock_freq = fpga_master_clock_freq; 70 | m_center_freq = 0.0; 71 | m_sample_rate = 0.0; 72 | m_cb = new circular_buffer(CB_LEN, sizeof(complex), 0); 73 | m_freq_corr = 0; 74 | 75 | pthread_mutex_init(&m_u_mutex, 0); 76 | 77 | m_decimation = decimation & ~1; 78 | if(m_decimation < 4) 79 | m_decimation = 4; 80 | if(m_decimation > 256) 81 | m_decimation = 256; 82 | } 83 | 84 | 85 | usrp_source::~usrp_source() { 86 | 87 | stop(); 88 | delete m_cb; 89 | rtlsdr_close(dev); 90 | pthread_mutex_destroy(&m_u_mutex); 91 | } 92 | 93 | 94 | void usrp_source::stop() { 95 | 96 | pthread_mutex_lock(&m_u_mutex); 97 | pthread_mutex_unlock(&m_u_mutex); 98 | } 99 | 100 | 101 | void usrp_source::start() { 102 | 103 | pthread_mutex_lock(&m_u_mutex); 104 | pthread_mutex_unlock(&m_u_mutex); 105 | } 106 | 107 | 108 | void usrp_source::calculate_decimation() { 109 | 110 | float decimation_f; 111 | 112 | // decimation_f = (float)m_u_rx->fpga_master_clock_freq() / m_desired_sample_rate; 113 | m_decimation = (unsigned int)round(decimation_f) & ~1; 114 | 115 | if(m_decimation < 4) 116 | m_decimation = 4; 117 | if(m_decimation > 256) 118 | m_decimation = 256; 119 | } 120 | 121 | 122 | float usrp_source::sample_rate() { 123 | 124 | return m_sample_rate; 125 | 126 | } 127 | 128 | 129 | int usrp_source::tune(double freq) { 130 | 131 | int r = 0; 132 | 133 | pthread_mutex_lock(&m_u_mutex); 134 | if (freq != m_center_freq) { 135 | r = rtlsdr_set_center_freq(dev, (uint32_t)freq); 136 | 137 | if (r < 0) 138 | fprintf(stderr, "Tuning to %u Hz failed!\n", (uint32_t)freq); 139 | else 140 | m_center_freq = rtlsdr_get_center_freq(dev); 141 | } 142 | 143 | pthread_mutex_unlock(&m_u_mutex); 144 | 145 | return 1; //(r < 0) ? 0 : 1; 146 | } 147 | 148 | int usrp_source::set_freq_correction(int ppm) { 149 | m_freq_corr = ppm; 150 | return rtlsdr_set_freq_correction(dev, ppm); 151 | } 152 | 153 | bool usrp_source::set_antenna(int antenna) { 154 | 155 | return 0; 156 | } 157 | 158 | bool usrp_source::set_dithering(bool enable) { 159 | #if HAVE_DITHERING == 1 160 | return (bool)(!rtlsdr_set_dithering(dev, (int)enable)); 161 | #else 162 | return true; 163 | #endif 164 | } 165 | 166 | bool usrp_source::set_gain(float gain) { 167 | int r, g = gain * 10; 168 | 169 | /* Enable manual gain */ 170 | r = rtlsdr_set_tuner_gain_mode(dev, 1); 171 | if (r < 0) 172 | fprintf(stderr, "WARNING: Failed to enable manual gain.\n"); 173 | 174 | fprintf(stderr, "Setting gain: %.1f dB\n", gain/10); 175 | r = rtlsdr_set_tuner_gain(dev, g); 176 | 177 | return (r < 0) ? 0 : 1; 178 | } 179 | 180 | 181 | /* 182 | * open() should be called before multiple threads access usrp_source. 183 | */ 184 | int usrp_source::open(unsigned int subdev) { 185 | int i, r, device_count, count; 186 | uint32_t dev_index = subdev; 187 | uint32_t samp_rate = 270833; 188 | 189 | m_sample_rate = 270833.002142; 190 | 191 | device_count = rtlsdr_get_device_count(); 192 | if (!device_count) { 193 | fprintf(stderr, "No supported devices found.\n"); 194 | exit(1); 195 | } 196 | 197 | fprintf(stderr, "Found %d device(s):\n", device_count); 198 | for (i = 0; i < device_count; i++) 199 | fprintf(stderr, " %d: %s\n", i, rtlsdr_get_device_name(i)); 200 | fprintf(stderr, "\n"); 201 | 202 | fprintf(stderr, "Using device %d: %s\n", 203 | dev_index, 204 | rtlsdr_get_device_name(dev_index)); 205 | 206 | r = rtlsdr_open(&dev, dev_index); 207 | if (r < 0) { 208 | fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index); 209 | exit(1); 210 | } 211 | 212 | /* Set the sample rate */ 213 | r = rtlsdr_set_sample_rate(dev, samp_rate); 214 | if (r < 0) 215 | fprintf(stderr, "WARNING: Failed to set sample rate.\n"); 216 | 217 | /* Reset endpoint before we start reading from it (mandatory) */ 218 | r = rtlsdr_reset_buffer(dev); 219 | if (r < 0) 220 | fprintf(stderr, "WARNING: Failed to reset buffers.\n"); 221 | 222 | // r = rtlsdr_set_offset_tuning(dev, 1); 223 | // if (r < 0) 224 | // fprintf(stderr, "WARNING: Failed to enable offset tuning\n"); 225 | 226 | return 0; 227 | } 228 | 229 | #define USB_PACKET_SIZE (2 * 16384) 230 | #define FLUSH_SIZE 512 231 | 232 | 233 | int usrp_source::fill(unsigned int num_samples, unsigned int *overrun_i) { 234 | 235 | unsigned char ubuf[USB_PACKET_SIZE]; 236 | unsigned int i, j, space, overruns = 0; 237 | complex *c; 238 | int n_read; 239 | 240 | while((m_cb->data_available() < num_samples) && (m_cb->space_available() > 0)) { 241 | 242 | // read one usb packet from the usrp 243 | pthread_mutex_lock(&m_u_mutex); 244 | 245 | if (rtlsdr_read_sync(dev, ubuf, sizeof(ubuf), &n_read) < 0) { 246 | pthread_mutex_unlock(&m_u_mutex); 247 | fprintf(stderr, "error: usrp_standard_rx::read\n"); 248 | return -1; 249 | } 250 | 251 | pthread_mutex_unlock(&m_u_mutex); 252 | 253 | // write complex input to complex output 254 | c = (complex *)m_cb->poke(&space); 255 | 256 | // set space to number of complex items to copy 257 | space = n_read / 2; 258 | 259 | // write data 260 | for(i = 0, j = 0; i < space; i += 1, j += 2) 261 | c[i] = complex((ubuf[j] - 127) * 256, (ubuf[j + 1] - 127) * 256); 262 | 263 | // update cb 264 | m_cb->wrote(i); 265 | } 266 | 267 | // if the cb is full, we left behind data from the usb packet 268 | if(m_cb->space_available() == 0) { 269 | fprintf(stderr, "warning: local overrun\n"); 270 | overruns++; 271 | } 272 | 273 | if(overrun_i) 274 | *overrun_i = overruns; 275 | 276 | return 0; 277 | } 278 | 279 | 280 | int usrp_source::read(complex *buf, unsigned int num_samples, 281 | unsigned int *samples_read) { 282 | 283 | unsigned int n; 284 | 285 | if(fill(num_samples, 0)) 286 | return -1; 287 | 288 | n = m_cb->read(buf, num_samples); 289 | 290 | if(samples_read) 291 | *samples_read = n; 292 | 293 | return 0; 294 | } 295 | 296 | 297 | /* 298 | * Don't hold a lock on this and use the usrp at the same time. 299 | */ 300 | circular_buffer *usrp_source::get_buffer() { 301 | 302 | return m_cb; 303 | } 304 | 305 | 306 | int usrp_source::flush(unsigned int flush_count) { 307 | 308 | m_cb->flush(); 309 | fill(flush_count * FLUSH_SIZE, 0); 310 | m_cb->flush(); 311 | 312 | return 0; 313 | } 314 | -------------------------------------------------------------------------------- /src/usrp_source.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Joshua Lackey 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | 30 | #include "usrp_complex.h" 31 | #include "circular_buffer.h" 32 | 33 | 34 | class usrp_source { 35 | public: 36 | usrp_source(float sample_rate, long int fpga_master_clock_freq = 52000000); 37 | usrp_source(unsigned int decimation, long int fpga_master_clock_freq = 52000000); 38 | ~usrp_source(); 39 | 40 | int open(unsigned int subdev); 41 | int read(complex *buf, unsigned int num_samples, unsigned int *samples_read); 42 | int fill(unsigned int num_samples, unsigned int *overrun); 43 | int tune(double freq); 44 | int set_freq_correction(int ppm); 45 | bool set_antenna(int antenna); 46 | bool set_gain(float gain); 47 | bool set_dithering(bool enable); 48 | void start(); 49 | void stop(); 50 | int flush(unsigned int flush_count = FLUSH_COUNT); 51 | circular_buffer *get_buffer(); 52 | 53 | float sample_rate(); 54 | 55 | static const unsigned int side_A = 0; 56 | static const unsigned int side_B = 1; 57 | 58 | double m_center_freq; 59 | int m_freq_corr; 60 | 61 | private: 62 | void calculate_decimation(); 63 | 64 | rtlsdr_dev_t *dev; 65 | 66 | float m_sample_rate; 67 | float m_desired_sample_rate; 68 | unsigned int m_decimation; 69 | 70 | long int m_fpga_master_clock_freq; 71 | 72 | circular_buffer * m_cb; 73 | 74 | /* 75 | * This mutex protects access to the USRP and daughterboards but not 76 | * necessarily to any fields in this class. 77 | */ 78 | pthread_mutex_t m_u_mutex; 79 | 80 | static const unsigned int FLUSH_COUNT = 10; 81 | static const unsigned int CB_LEN = (16 * 16384); 82 | static const int NCHAN = 1; 83 | static const int INITIAL_MUX = -1; 84 | static const int FUSB_BLOCK_SIZE = 1024; 85 | static const int FUSB_NBLOCKS = 16 * 8; 86 | static const char * FPGA_FILENAME() { 87 | return "std_2rxhb_2tx.rbf"; 88 | } 89 | }; 90 | -------------------------------------------------------------------------------- /src/util.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Joshua Lackey 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #define _USE_MATH_DEFINES 31 | #include 32 | 33 | 34 | void display_freq(float f) { 35 | 36 | if(f >= 0) { 37 | printf("+ "); 38 | } else { 39 | printf("- "); 40 | f = -f; 41 | } 42 | if(fabs(f) >= 1e9) { 43 | printf("%.3fGHz", f / 1e9); 44 | return; 45 | } 46 | if(fabs(f) >= 1e6) { 47 | printf("%.1fMHz", f / 1e6); 48 | return; 49 | } 50 | if(fabs(f) >= 1e3) { 51 | printf("%.3fkHz", f / 1e3); 52 | return; 53 | } 54 | if(fabs(f) >= 1e2) { 55 | printf("%.0fHz", f); 56 | return; 57 | } 58 | if(fabs(f) >= 1e1) { 59 | printf(" %.0fHz", f); 60 | return; 61 | } 62 | printf(" %.0fHz", f); 63 | } 64 | 65 | 66 | void sort(float *b, unsigned int len) { 67 | 68 | for(unsigned int i = 0; i < len; i++) { 69 | for(unsigned int j = i + 1; j < len; j++) { 70 | if(b[j] < b[i]) { 71 | float t = b[i]; 72 | b[i] = b[j]; 73 | b[j] = t; 74 | } 75 | } 76 | } 77 | } 78 | 79 | 80 | double avg(float *b, unsigned int len, float *stddev) { 81 | 82 | unsigned int i; 83 | double t = 0.0, a = 0.0, s = 0.0; 84 | 85 | for(i = 0; i < len; i++) 86 | t += b[i]; 87 | a = t / len; 88 | if(stddev) { 89 | for(i = 0; i < len; i++) 90 | s += (b[i] - a) * (b[i] - a); 91 | s /= len; 92 | s = sqrt(s); 93 | *stddev = s; 94 | } 95 | 96 | return a; 97 | } 98 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Joshua Lackey 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | void display_freq(float f); 29 | void sort(float *b, unsigned int len); 30 | double avg(float *b, unsigned int len, float *stddev); 31 | -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Joshua Lackey 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #pragma once 29 | static const char * const kal_version_string = PACKAGE_VERSION; 30 | --------------------------------------------------------------------------------