├── AUTHORS ├── COPYING ├── ChangeLog ├── Makefile.am ├── NEWS ├── README ├── 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 /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/asdil12/kalibrate-rtl/71e9543fced02b735e15c646ae95fd26e1cb2fcc/ChangeLog -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = src 2 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asdil12/kalibrate-rtl/71e9543fced02b735e15c646ae95fd26e1cb2fcc/NEWS -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Kalibrate, or kal, can scan for GSM base stations in a given frequency band and 2 | can use those GSM base stations to calculate the local oscillator frequency 3 | offset. 4 | 5 | See http://thre.at/kalibrate 6 | 7 | Copyright (c) 2010, Joshua Lackey (jl@thre.at) 8 | 9 | modified for use with rtl-sdr devices, 10 | Copyright (c) 2012, Steve Markgraf (steve@steve-m.de) 11 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | rm -rf config.cache autom4te*.cache 2 | #autoreconf --install 3 | 4 | libtoolize --automake 5 | aclocal 6 | autoconf 7 | autoheader 8 | automake --add-missing 9 | -------------------------------------------------------------------------------- /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]) 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 | 33 | # OSX doesn't support System V shared memory 34 | AC_CANONICAL_HOST 35 | case "$host_os" in 36 | darwin*) 37 | AC_DEFINE([D_HOST_OSX], [], [building for OSX]) 38 | ;; 39 | esac 40 | 41 | AC_CONFIG_FILES([Makefile 42 | src/Makefile]) 43 | AC_OUTPUT 44 | -------------------------------------------------------------------------------- /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) 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 | 32 | #include "usrp_source.h" 33 | #include "circular_buffer.h" 34 | #include "fcch_detector.h" 35 | #include "arfcn_freq.h" 36 | #include "util.h" 37 | 38 | extern int g_verbosity; 39 | 40 | static const float ERROR_DETECT_OFFSET_MAX = 40e3; 41 | 42 | #ifdef _WIN32 43 | #define BUFSIZ 1024 44 | #endif 45 | 46 | static double vectornorm2(const complex *v, const unsigned int len) { 47 | 48 | unsigned int i; 49 | double e = 0.0; 50 | 51 | for(i = 0; i < len; i++) 52 | e += norm(v[i]); 53 | 54 | return e; 55 | } 56 | 57 | 58 | int c0_detect(usrp_source *u, int bi) { 59 | 60 | #define GSM_RATE (1625000.0 / 6.0) 61 | #define NOTFOUND_MAX 10 62 | 63 | int i, chan_count; 64 | unsigned int overruns, b_len, frames_len, found_count, notfound_count, r; 65 | float offset, spower[BUFSIZ]; 66 | double freq, sps, n, power[BUFSIZ], sum = 0, a; 67 | complex *b; 68 | circular_buffer *ub; 69 | fcch_detector *l = new fcch_detector(u->sample_rate()); 70 | 71 | if(bi == BI_NOT_DEFINED) { 72 | fprintf(stderr, "error: c0_detect: band not defined\n"); 73 | return -1; 74 | } 75 | 76 | sps = u->sample_rate() / GSM_RATE; 77 | frames_len = (unsigned int)ceil((12 * 8 * 156.25 + 156.25) * sps); 78 | ub = u->get_buffer(); 79 | 80 | // first, we calculate the power in each channel 81 | if(g_verbosity > 2) { 82 | fprintf(stderr, "calculate power in each channel:\n"); 83 | } 84 | u->start(); 85 | u->flush(); 86 | for(i = first_chan(bi); i >= 0; i = next_chan(i, bi)) { 87 | freq = arfcn_to_freq(i, &bi); 88 | if(!u->tune(freq)) { 89 | fprintf(stderr, "error: usrp_source::tune\n"); 90 | return -1; 91 | } 92 | 93 | do { 94 | u->flush(); 95 | if(u->fill(frames_len, &overruns)) { 96 | fprintf(stderr, "error: usrp_source::fill\n"); 97 | return -1; 98 | } 99 | } while(overruns); 100 | 101 | b = (complex *)ub->peek(&b_len); 102 | n = sqrt(vectornorm2(b, frames_len)); 103 | power[i] = n; 104 | if(g_verbosity > 2) { 105 | fprintf(stderr, "\tchan %d (%.1fMHz):\tpower: %lf\n", 106 | i, freq / 1e6, n); 107 | } 108 | } 109 | 110 | /* 111 | * We want to use the average to determine which channels have 112 | * power, and hence a possibility of being channel 0 on a BTS. 113 | * However, some channels in the band can be extremely noisy. (E.g., 114 | * CDMA traffic in GSM-850.) Hence we won't consider the noisiest 115 | * channels when we construct the average. 116 | */ 117 | chan_count = 0; 118 | for(i = first_chan(bi); i >= 0; i = next_chan(i, bi)) { 119 | spower[chan_count++] = power[i]; 120 | } 121 | sort(spower, chan_count); 122 | 123 | // average the lowest %60 124 | a = avg(spower, chan_count - 4 * chan_count / 10, 0); 125 | 126 | if(g_verbosity > 0) { 127 | fprintf(stderr, "channel detect threshold: %lf\n", a); 128 | } 129 | 130 | // then we look for fcch bursts 131 | printf("%s:\n", bi_to_str(bi)); 132 | found_count = 0; 133 | notfound_count = 0; 134 | sum = 0; 135 | i = first_chan(bi); 136 | do { 137 | if(power[i] <= a) { 138 | i = next_chan(i, bi); 139 | continue; 140 | } 141 | 142 | freq = arfcn_to_freq(i, &bi); 143 | if(!u->tune(freq)) { 144 | fprintf(stderr, "error: usrp_source::tune\n"); 145 | return -1; 146 | } 147 | 148 | do { 149 | u->flush(); 150 | if(u->fill(frames_len, &overruns)) { 151 | fprintf(stderr, "error: usrp_source::fill\n"); 152 | return -1; 153 | } 154 | } while(overruns); 155 | 156 | b = (complex *)ub->peek(&b_len); 157 | r = l->scan(b, b_len, &offset, 0); 158 | if(r && (fabsf(offset - GSM_RATE / 4) < ERROR_DETECT_OFFSET_MAX)) { 159 | // found 160 | printf("\tchan: %d (%.1fMHz ", i, freq / 1e6); 161 | display_freq(offset - GSM_RATE / 4); 162 | printf(")\tpower: %6.2lf\n", power[i]); 163 | notfound_count = 0; 164 | i = next_chan(i, bi); 165 | } else { 166 | // not found 167 | notfound_count += 1; 168 | if(notfound_count >= NOTFOUND_MAX) { 169 | notfound_count = 0; 170 | i = next_chan(i, bi); 171 | } 172 | } 173 | } while(i >= 0); 174 | 175 | return 0; 176 | } 177 | -------------------------------------------------------------------------------- /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 D_HOST_OSX 52 | #include 53 | #endif /* D_HOST_OSX */ 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 | printf("\t-v\tverbose\n"); 96 | printf("\t-D\tenable debug messages\n"); 97 | printf("\t-h\thelp\n"); 98 | exit(-1); 99 | } 100 | 101 | 102 | int main(int argc, char **argv) { 103 | 104 | char *endptr; 105 | int c, antenna = 1, bi = BI_NOT_DEFINED, chan = -1, bts_scan = 0; 106 | int ppm_error = 0; 107 | unsigned int subdev = 0, decimation = 192; 108 | long int fpga_master_clock_freq = 52000000; 109 | float gain = 0; 110 | double freq = -1.0, fd; 111 | usrp_source *u; 112 | 113 | while((c = getopt(argc, argv, "f:c:s:b:R:A:g:e:d:vDh?")) != EOF) { 114 | switch(c) { 115 | case 'f': 116 | freq = strtod(optarg, 0); 117 | break; 118 | 119 | case 'c': 120 | chan = strtoul(optarg, 0, 0); 121 | break; 122 | 123 | case 's': 124 | if((bi = str_to_bi(optarg)) == -1) { 125 | fprintf(stderr, "error: bad band " 126 | "indicator: ``%s''\n", optarg); 127 | usage(argv[0]); 128 | } 129 | bts_scan = 1; 130 | break; 131 | 132 | case 'b': 133 | if((bi = str_to_bi(optarg)) == -1) { 134 | fprintf(stderr, "error: bad band " 135 | "indicator: ``%s''\n", optarg); 136 | usage(argv[0]); 137 | } 138 | break; 139 | 140 | case 'R': 141 | errno = 0; 142 | subdev = strtoul(optarg, &endptr, 0); 143 | if((!errno) && (endptr != optarg)) 144 | break; 145 | if(tolower(*optarg) == 'a') { 146 | subdev = 0; 147 | } else if(tolower(*optarg) == 'b') { 148 | subdev = 1; 149 | } else { 150 | fprintf(stderr, "error: bad side: " 151 | "``%s''\n", 152 | optarg); 153 | usage(argv[0]); 154 | } 155 | break; 156 | 157 | case 'A': 158 | if(!strcmp(optarg, "RX2")) { 159 | antenna = 1; 160 | } else if(!strcmp(optarg, "TX/RX")) { 161 | antenna = 0; 162 | } else { 163 | errno = 0; 164 | antenna = strtoul(optarg, &endptr, 0); 165 | if(errno || (endptr == optarg)) { 166 | fprintf(stderr, "error: bad " 167 | "antenna: ``%s''\n", 168 | optarg); 169 | usage(argv[0]); 170 | } 171 | } 172 | break; 173 | 174 | case 'g': 175 | gain = strtof(optarg, 0) * 10; 176 | break; 177 | 178 | case 'F': 179 | fpga_master_clock_freq = strtol(optarg, 0, 0); 180 | if(!fpga_master_clock_freq) 181 | fpga_master_clock_freq = (long int)strtod(optarg, 0); 182 | 183 | // was answer in MHz? 184 | if(fpga_master_clock_freq < 1000) { 185 | fpga_master_clock_freq *= 1000000; 186 | } 187 | break; 188 | 189 | case 'e': 190 | ppm_error = strtol(optarg, 0, 0); 191 | break; 192 | 193 | case 'd': 194 | subdev = strtol(optarg, 0, 0); 195 | break; 196 | 197 | case 'v': 198 | g_verbosity++; 199 | break; 200 | 201 | case 'D': 202 | g_debug = 1; 203 | break; 204 | 205 | case 'h': 206 | case '?': 207 | default: 208 | usage(argv[0]); 209 | break; 210 | } 211 | 212 | } 213 | 214 | // sanity check frequency / channel 215 | if(bts_scan) { 216 | if(bi == BI_NOT_DEFINED) { 217 | fprintf(stderr, "error: scaning requires band\n"); 218 | usage(argv[0]); 219 | } 220 | } else { 221 | if(freq < 0.0) { 222 | if(chan < 0) { 223 | fprintf(stderr, "error: must enter channel or " 224 | "frequency\n"); 225 | usage(argv[0]); 226 | } 227 | if((freq = arfcn_to_freq(chan, &bi)) < 869e6) 228 | usage(argv[0]); 229 | } 230 | if((freq < 869e6) || (2e9 < freq)) { 231 | fprintf(stderr, "error: bad frequency: %lf\n", freq); 232 | usage(argv[0]); 233 | } 234 | chan = freq_to_arfcn(freq, &bi); 235 | } 236 | 237 | #if 0 238 | // sanity check clock 239 | if(fpga_master_clock_freq < 48000000) { 240 | fprintf(stderr, "error: FPGA master clock too slow: %li\n", fpga_master_clock_freq); 241 | usage(argv[0]); 242 | } 243 | 244 | // calculate decimation -- get as close to GSM rate as we can 245 | fd = (double)fpga_master_clock_freq / GSM_RATE; 246 | decimation = (unsigned int)fd; 247 | #endif 248 | if(g_debug) { 249 | #ifdef D_HOST_OSX 250 | printf("debug: Mac OS X version\n"); 251 | #endif 252 | printf("debug: FPGA Master Clock Freq:\t%li\n", fpga_master_clock_freq); 253 | printf("debug: decimation :\t%u\n", decimation); 254 | printf("debug: RX Subdev Spec :\t%s\n", subdev? "B" : "A"); 255 | printf("debug: Antenna :\t%s\n", antenna? "RX2" : "TX/RX"); 256 | printf("debug: Gain :\t%f\n", gain); 257 | } 258 | 259 | u = new usrp_source(decimation, fpga_master_clock_freq); 260 | if(!u) { 261 | fprintf(stderr, "error: usrp_source\n"); 262 | return -1; 263 | } 264 | if(u->open(subdev) == -1) { 265 | fprintf(stderr, "error: usrp_source::open\n"); 266 | return -1; 267 | } 268 | // u->set_antenna(antenna); 269 | if (gain != 0) { 270 | if(!u->set_gain(gain)) { 271 | fprintf(stderr, "error: usrp_source::set_gain\n"); 272 | return -1; 273 | } 274 | } 275 | 276 | if (ppm_error != 0) { 277 | if(u->set_freq_correction(ppm_error) < 0) { 278 | fprintf(stderr, "error: usrp_source::set_freq_correction\n"); 279 | return -1; 280 | } 281 | } 282 | 283 | if(!bts_scan) { 284 | if(!u->tune(freq)) { 285 | fprintf(stderr, "error: usrp_source::tune\n"); 286 | return -1; 287 | } 288 | 289 | fprintf(stderr, "%s: Calculating clock frequency offset.\n", 290 | basename(argv[0])); 291 | fprintf(stderr, "Using %s channel %d (%.1fMHz)\n", 292 | bi_to_str(bi), chan, freq / 1e6); 293 | 294 | return offset_detect(u); 295 | } 296 | 297 | fprintf(stderr, "%s: Scanning for %s base stations.\n", 298 | basename(argv[0]), bi_to_str(bi)); 299 | 300 | return c0_detect(u, bi); 301 | } 302 | -------------------------------------------------------------------------------- /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) { 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; 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 / 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); 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 | #include 30 | #include 31 | #ifndef _WIN32 32 | #include 33 | #endif 34 | #include 35 | #include 36 | #define _USE_MATH_DEFINES 37 | #include 38 | #include 39 | 40 | #include "usrp_source.h" 41 | 42 | extern int g_verbosity; 43 | 44 | 45 | #ifdef _WIN32 46 | inline double round(double x) { return floor(x + 0.5); } 47 | #endif 48 | 49 | usrp_source::usrp_source(float sample_rate, long int fpga_master_clock_freq) { 50 | 51 | m_fpga_master_clock_freq = fpga_master_clock_freq; 52 | m_desired_sample_rate = sample_rate; 53 | m_sample_rate = 0.0; 54 | m_decimation = 0; 55 | m_cb = new circular_buffer(CB_LEN, sizeof(complex), 0); 56 | 57 | pthread_mutex_init(&m_u_mutex, 0); 58 | } 59 | 60 | 61 | usrp_source::usrp_source(unsigned int decimation, long int fpga_master_clock_freq) { 62 | 63 | m_fpga_master_clock_freq = fpga_master_clock_freq; 64 | m_sample_rate = 0.0; 65 | m_cb = new circular_buffer(CB_LEN, sizeof(complex), 0); 66 | 67 | pthread_mutex_init(&m_u_mutex, 0); 68 | 69 | m_decimation = decimation & ~1; 70 | if(m_decimation < 4) 71 | m_decimation = 4; 72 | if(m_decimation > 256) 73 | m_decimation = 256; 74 | } 75 | 76 | 77 | usrp_source::~usrp_source() { 78 | 79 | stop(); 80 | delete m_cb; 81 | rtlsdr_close(dev); 82 | pthread_mutex_destroy(&m_u_mutex); 83 | } 84 | 85 | 86 | void usrp_source::stop() { 87 | 88 | pthread_mutex_lock(&m_u_mutex); 89 | pthread_mutex_unlock(&m_u_mutex); 90 | } 91 | 92 | 93 | void usrp_source::start() { 94 | 95 | pthread_mutex_lock(&m_u_mutex); 96 | pthread_mutex_unlock(&m_u_mutex); 97 | } 98 | 99 | 100 | void usrp_source::calculate_decimation() { 101 | 102 | float decimation_f; 103 | 104 | // decimation_f = (float)m_u_rx->fpga_master_clock_freq() / m_desired_sample_rate; 105 | m_decimation = (unsigned int)round(decimation_f) & ~1; 106 | 107 | if(m_decimation < 4) 108 | m_decimation = 4; 109 | if(m_decimation > 256) 110 | m_decimation = 256; 111 | } 112 | 113 | 114 | float usrp_source::sample_rate() { 115 | 116 | return m_sample_rate; 117 | 118 | } 119 | 120 | 121 | int usrp_source::tune(double freq) { 122 | 123 | int r = 0; 124 | 125 | pthread_mutex_lock(&m_u_mutex); 126 | if (freq != m_center_freq) { 127 | r = rtlsdr_set_center_freq(dev, (uint32_t)freq); 128 | // fprintf(stderr, "Tuned to %i Hz.\n", (uint32_t)freq); 129 | 130 | if (r < 0) 131 | fprintf(stderr, "Tuning to %lu Hz failed!\n", (uint32_t)freq); 132 | else 133 | m_center_freq = freq; 134 | } 135 | 136 | pthread_mutex_unlock(&m_u_mutex); 137 | 138 | return 1; //(r < 0) ? 0 : 1; 139 | } 140 | 141 | int usrp_source::set_freq_correction(int ppm) { 142 | m_freq_corr = ppm; 143 | return rtlsdr_set_freq_correction(dev, ppm); 144 | } 145 | 146 | bool usrp_source::set_antenna(int antenna) { 147 | 148 | return 0; 149 | } 150 | 151 | bool usrp_source::set_gain(float gain) { 152 | int r, g = gain * 10; 153 | 154 | /* Enable manual gain */ 155 | r = rtlsdr_set_tuner_gain_mode(dev, 1); 156 | if (r < 0) 157 | fprintf(stderr, "WARNING: Failed to enable manual gain.\n"); 158 | 159 | fprintf(stderr, "Setting gain: %.1f dB\n", gain/10); 160 | r = rtlsdr_set_tuner_gain(dev, g); 161 | 162 | return (r < 0) ? 0 : 1; 163 | } 164 | 165 | 166 | /* 167 | * open() should be called before multiple threads access usrp_source. 168 | */ 169 | int usrp_source::open(unsigned int subdev) { 170 | int i, r, device_count, count; 171 | uint32_t dev_index = subdev; 172 | uint32_t samp_rate = 270833; 173 | 174 | m_sample_rate = 270833.002142; 175 | 176 | device_count = rtlsdr_get_device_count(); 177 | if (!device_count) { 178 | fprintf(stderr, "No supported devices found.\n"); 179 | exit(1); 180 | } 181 | 182 | fprintf(stderr, "Found %d device(s):\n", device_count); 183 | for (i = 0; i < device_count; i++) 184 | fprintf(stderr, " %d: %s\n", i, rtlsdr_get_device_name(i)); 185 | fprintf(stderr, "\n"); 186 | 187 | fprintf(stderr, "Using device %d: %s\n", 188 | dev_index, 189 | rtlsdr_get_device_name(dev_index)); 190 | 191 | r = rtlsdr_open(&dev, dev_index); 192 | if (r < 0) { 193 | fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index); 194 | exit(1); 195 | } 196 | 197 | /* Set the sample rate */ 198 | r = rtlsdr_set_sample_rate(dev, samp_rate); 199 | if (r < 0) 200 | fprintf(stderr, "WARNING: Failed to set sample rate.\n"); 201 | 202 | /* Reset endpoint before we start reading from it (mandatory) */ 203 | r = rtlsdr_reset_buffer(dev); 204 | if (r < 0) 205 | fprintf(stderr, "WARNING: Failed to reset buffers.\n"); 206 | 207 | // r = rtlsdr_set_offset_tuning(dev, 1); 208 | // if (r < 0) 209 | // fprintf(stderr, "WARNING: Failed to enable offset tuning\n"); 210 | 211 | return 0; 212 | } 213 | 214 | #define USB_PACKET_SIZE (2 * 16384) 215 | #define FLUSH_SIZE 512 216 | 217 | 218 | int usrp_source::fill(unsigned int num_samples, unsigned int *overrun_i) { 219 | 220 | unsigned char ubuf[USB_PACKET_SIZE]; 221 | unsigned int i, j, space, overruns = 0; 222 | complex *c; 223 | int n_read; 224 | 225 | while((m_cb->data_available() < num_samples) && (m_cb->space_available() > 0)) { 226 | 227 | // read one usb packet from the usrp 228 | pthread_mutex_lock(&m_u_mutex); 229 | 230 | if (rtlsdr_read_sync(dev, ubuf, sizeof(ubuf), &n_read) < 0) { 231 | pthread_mutex_unlock(&m_u_mutex); 232 | fprintf(stderr, "error: usrp_standard_rx::read\n"); 233 | return -1; 234 | } 235 | 236 | pthread_mutex_unlock(&m_u_mutex); 237 | 238 | // write complex input to complex output 239 | c = (complex *)m_cb->poke(&space); 240 | 241 | // set space to number of complex items to copy 242 | space = n_read / 2; 243 | 244 | // write data 245 | for(i = 0, j = 0; i < space; i += 1, j += 2) 246 | c[i] = complex((ubuf[j] - 127) * 256, (ubuf[j + 1] - 127) * 256); 247 | 248 | // update cb 249 | m_cb->wrote(i); 250 | } 251 | 252 | // if the cb is full, we left behind data from the usb packet 253 | if(m_cb->space_available() == 0) { 254 | fprintf(stderr, "warning: local overrun\n"); 255 | overruns++; 256 | } 257 | 258 | if(overrun_i) 259 | *overrun_i = overruns; 260 | 261 | return 0; 262 | } 263 | 264 | 265 | int usrp_source::read(complex *buf, unsigned int num_samples, 266 | unsigned int *samples_read) { 267 | 268 | unsigned int n; 269 | 270 | if(fill(num_samples, 0)) 271 | return -1; 272 | 273 | n = m_cb->read(buf, num_samples); 274 | 275 | if(samples_read) 276 | *samples_read = n; 277 | 278 | return 0; 279 | } 280 | 281 | 282 | /* 283 | * Don't hold a lock on this and use the usrp at the same time. 284 | */ 285 | circular_buffer *usrp_source::get_buffer() { 286 | 287 | return m_cb; 288 | } 289 | 290 | 291 | int usrp_source::flush(unsigned int flush_count) { 292 | 293 | m_cb->flush(); 294 | fill(flush_count * FLUSH_SIZE, 0); 295 | m_cb->flush(); 296 | 297 | return 0; 298 | } 299 | -------------------------------------------------------------------------------- /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 | void start(); 48 | void stop(); 49 | int flush(unsigned int flush_count = FLUSH_COUNT); 50 | circular_buffer *get_buffer(); 51 | 52 | float sample_rate(); 53 | 54 | static const unsigned int side_A = 0; 55 | static const unsigned int side_B = 1; 56 | 57 | double m_center_freq; 58 | int m_freq_corr; 59 | 60 | private: 61 | void calculate_decimation(); 62 | 63 | rtlsdr_dev_t *dev; 64 | 65 | float m_sample_rate; 66 | float m_desired_sample_rate; 67 | unsigned int m_decimation; 68 | 69 | long int m_fpga_master_clock_freq; 70 | 71 | circular_buffer * m_cb; 72 | 73 | /* 74 | * This mutex protects access to the USRP and daughterboards but not 75 | * necessarily to any fields in this class. 76 | */ 77 | pthread_mutex_t m_u_mutex; 78 | 79 | static const unsigned int FLUSH_COUNT = 10; 80 | static const unsigned int CB_LEN = (16 * 16384); 81 | static const int NCHAN = 1; 82 | static const int INITIAL_MUX = -1; 83 | static const int FUSB_BLOCK_SIZE = 1024; 84 | static const int FUSB_NBLOCKS = 16 * 8; 85 | static const char * FPGA_FILENAME() { 86 | return "std_2rxhb_2tx.rbf"; 87 | } 88 | }; 89 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------