├── .gitignore ├── AUTHORS ├── COPYING ├── ChangeLog ├── INSTALL.homebrew ├── Makefile.am ├── NEWS ├── README ├── README.md ├── bootstrap ├── configure.ac └── src ├── Makefile.am ├── arfcn_freq.cc ├── arfcn_freq.h ├── c0_detect.cc ├── c0_detect.h ├── circular_buffer.cc ├── circular_buffer.h ├── fcch_detector.cc ├── fcch_detector.h ├── kal.cc ├── offset.cc ├── offset.h ├── usrp_complex.h ├── usrp_source.cc ├── usrp_source.h ├── util.cc ├── util.h └── version.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *~ 3 | .*.swp 4 | .*.swo 5 | .*.swn 6 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Joshua Lackey 2 | Alexander Chemeris 3 | Steve Markgraf 4 | Wang Kang 5 | -------------------------------------------------------------------------------- /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/scateu/kalibrate-hackrf/5d907327a490cc6b8ede7862d73c5e7394a5a253/ChangeLog -------------------------------------------------------------------------------- /INSTALL.homebrew: -------------------------------------------------------------------------------- 1 | $ brew install fftw hackrf libtool 2 | $ ./bootstrap 3 | $ ./configure 4 | $ make 5 | 6 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = src 2 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scateu/kalibrate-hackrf/5d907327a490cc6b8ede7862d73c5e7394a5a253/NEWS -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | README.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kalibrate-hackrf 2 | 3 | Kalibrate, or `kal`, can scan for GSM base stations in a given frequency band and 4 | can use those GSM base stations to calculate the local oscillator frequency 5 | offset. 6 | 7 | Kalibrate-hackrf is a fork of Joshua Lackey's kalibrate originally for USRP, ported 8 | to support the [HackRF](https://greatscottgadgets.com/hackrf/). 9 | 10 | The documentation below is adapted from the original website at 11 | [http://thre.at/kalibrate](https://web.archive.org/web/20131226204943/http://thre.at/kalibrate) 12 | (defunct since 2013). 13 | 14 | 15 | ## On Clocks 16 | 17 | SDRs have a built-in 18 | [oscillator](http://en.wikipedia.org/wiki/Electronic_oscillator) which is only 19 | guaranteed accurate to within some range (for example, the USRP1 to 20 parts per million (ppm). (Although in practice 20 | the USRP oscillator is normally closer to 3ppm.) This is sufficient for a number 21 | of purposes, but not for others. Some SDRs (including the HackRF and USRP) also support external 22 | oscillators. For example, the [Clock Tamer](http://code.google.com/p/clock-tamer/), 23 | the [KSP 52MHz](https://web.archive.org/web/20100518194722/http://kestrelsignalprocessing.mybigcommerce.com/products/52MHz-clock-generator.html) 24 | and the [FA-SY 1](https://web.archive.org/web/20101221022021/http://www.box73.de/catalog/product_info.php?products_id=1869) 25 | are all accurate clocks. 26 | 27 | Normally, these external clocks must be calibrated to ensure they are set as 28 | accurately as possible. An oscilloscope is probably the best way to determine 29 | the accuracy of these clocks. However a good oscilloscope is expensive and not 30 | everyone has access to one. There are other ways to calibrate these devices -- 31 | the FA-SY 1 documentation discusses using the [WWVB](http://en.wikipedia.org/wiki/WWVB) 32 | time signals. Similarly, GSM base stations are required to be accurate to within 33 | 0.05ppm and so they can also provide an accurate reference clock. 34 | 35 | If you own a USRP daughterboard that covers the GSM frequencies in your area, 36 | using a GSM base station is a particularly convenient. 37 | 38 | ## Description 39 | 40 | A base station transmits a frequency correction burst on the Frequency Correction 41 | CHannel (FCCH) in regular positions. The FCCH repeats every 51 TDMA frames and the 42 | frequency correction burst is located in timeslot 0 of frames 0, 10, 20, 30, and 40. 43 | 44 | A frequency correction burst consists of a certain bit pattern which, after 45 | modulation, is a sine wave with frequency one quarter that of the GSM bit rate. 46 | I.e., (1625000 / 6) / 4 = 67708.3 Hz. By searching a channel for this pure tone, 47 | a mobile station can determine its clock offset by determining how far away from 48 | 67708.3Hz the received frequency is. 49 | 50 | There are many ways which a pure tone can be identified. For example, one can 51 | capture a burst of data and then examine it with a Fourier transformation to 52 | determine if most of the power in the signal is at a certain frequency. 53 | Alternatively, one can define a band-pass filter at 67708.3 Hz and then compare 54 | the power of the signal before and after it passes through the filter. However, 55 | both of these methods have drawbacks. The FFT method requires significant resources 56 | and cannot easily detect the frequency correction burst edge. The filter method 57 | either isn't accurate or cannot detect larger offsets. Multiple filters can be 58 | used, but that requires significant resources. 59 | 60 | `kal` uses a hybrid of the FFT and filter methods. The filter `kal` uses is an 61 | adaptive filter as described in the paper: 62 | 63 | G. Narendra Varma, Usha Sahu, G. Prabhu Charan, _Robust Frequency Burst Detection Algorithm for GSM/GPRS_. 64 | 65 | An Adaptive Line Equalizer (ALE) attempts to predict the input signal byadapting 66 | its filter coefficients. The prediction is compared with the actual signal and the 67 | coefficients are adapted so that the error is minimized. If the input contains a 68 | strong narrow-band signal embedded in wide-band noise, the filter output will be 69 | a pure sine at the same frequency and almost free of the wide-band noise. 70 | 71 | `kal` applies the ALE to the buffer and calculates the error between the ALE prediction 72 | and the input signal at each point in the signal. `kal` then calculates the average 73 | of all the errors. When the error drops below the average for the length of a 74 | frequency correction burst, this indicates a detected burst. 75 | 76 | Once we have the location of the frequency correction burst, we must determine what 77 | frequency it is at so we can calculate the offset from 67708.3 Hz. We take the 78 | input signal corresponding to the low error levels and run that through a FFT. 79 | The largest peak in the FFT output corresponds to the detected frequency of the 80 | frequency correction burst. This peak is then used to determine the frequency offset. 81 | 82 | Any noise in the system affects the measurements and so `kal` averages the results 83 | a number of times before displaying the offset. The range of values as well as the 84 | standard deviation is displayed so that an estimate of the measurement accuracy 85 | can be made. 86 | 87 | ## How 88 | 89 | `kal` requires fftw3 and version 3.2 or higher of libusrp. `kal` also requires a 90 | USRP and daughterboards appropriate for the desired GSM frequency band. An external clock is not required; `kal` can also calculate the offset of the built-in USRP clock. 91 | 92 | `kal` uses the GNU Autoconf system and should be easily built on most *nix platforms. 93 | 94 | jl@thinkfoo:~/src$ cd kal-v0.4.1 95 | jl@thinkfoo:~/src/kal-v0.4.1$ ./bootstrap && CXXFLAGS='-W -Wall -O3' ./configure && make 96 | [...] 97 | jl@thinkfoo:~/src/kal-v0.4.1$ src/kal -h 98 | kalibrate v0.4.1, Copyright (c) 2010, Joshua Lackey 99 | 100 | Usage: 101 | GSM Base Station Scan: 102 | kal <-s band indicator> [options] 103 | 104 | Clock Offset Calculation: 105 | kal <-f frequency | -c channel> [options] 106 | 107 | Where options are: 108 | -s band to scan (GSM850, GSM900, EGSM, DCS, PCS) 109 | -f frequency of nearby GSM base station 110 | -c channel of nearby GSM base station 111 | -b band indicator (GSM850, GSM900, EGSM, DCS, PCS) 112 | -R side A (0) or B (1), defaults to B 113 | -A antenna TX/RX (0) or RX2 (1), defaults to RX2 114 | -g gain as % of range, defaults to 45% 115 | -F FPGA master clock frequency, defaults to 52MHz 116 | -v verbose 117 | -D enable debug messages 118 | -h help 119 | jl@thinkfoo:~/src/kal-v0.4.1$ src/kal -s 850 120 | kal: Scanning for GSM-850 base stations. 121 | GSM-850: 122 | chan: 128 (869.2MHz - 13Hz) power: 9400.80 123 | chan: 131 (869.8MHz + 7Hz) power: 4081.75 124 | chan: 139 (871.4MHz + 10Hz) power: 2936.20 125 | chan: 145 (872.6MHz - 7Hz) power: 33605.48 126 | jl@thinkfoo:~/src/kal-v0.4.1$ src/kal -c 145 127 | kal: Calculating clock frequency offset. 128 | Using GSM-850 channel 145 (872.6MHz) 129 | average [min, max] (range, stddev) 130 | - 1Hz [-8, 7] (14, 3.948722) 131 | overruns: 0 132 | not found: 0 133 | 134 | 135 | ## ChangeLog 136 | 137 | 2016-6-6 Update: 138 | 139 | Fix library not found for -lrt on OS X. 140 | 141 | Author: @rxseger 142 | 143 | 2015-3-3 Update: 144 | 145 | Add ability to specify channel when scanning to check power output for a single channel. 146 | 147 | Author: [xorrbit](https://github.com/xorrbit) 148 | 149 | 2014-8-23 Update: 150 | 151 | Thanks to chris.kuethe@gmail.com who did a bunch of tinkering with this port of kalibrate. 152 | 153 | https://github.com/ckuethe/kalibrate-hackrf/ 154 | 155 | This now runs as fast as kalibrate-rtl 156 | 157 | v0.4.1\. This version contains the following fixes: 158 | 159 | * Support for Mac OS X (i.e., support for POSIX shared memory rather than just SysV shared memory) 160 | * Specifying the FPGA master clock frequency actually works now. 161 | * Some constants subtly depend on the sample rate. I've removed the decimation option and now calculate that automatically. 162 | 163 | The latest version should be the most accurate. I'd urge anyone running an old version to update. 164 | 165 | Versions of `kal` previous to 0.4 used an algorithm that was extremely sensitive to noise. Version 0.4 of `kal` is more processor intensive, but also much more accurate. 166 | 167 | 168 | ## Credits 169 | 170 | I'd like to thank Alexander Chemeris for his valuable input, ideas, and his testing of a large number of alpha versions. I'd also like to thank Mark J. Blair for his help getting `kal` to work with Mac OS X. 171 | 172 | I'd also like to thank Mark J. Blair for his help getting `kal` to work with Mac OS X. (And also for helping me realize that I never actually used the `-F` parameter...) 173 | 174 | Copyright (c) 2010, Joshua Lackey ([jl@thre.at](mailto:jl@thre.at)) 175 | 176 | Copyright (c) 2010, Joshua Lackey (jl@thre.at) 177 | 178 | Copyright (c) 2012, Steve Markgraf (steve@steve-m.de) 179 | 180 | 181 | modified for use with hackrf devices, 182 | 183 | Copyright (c) 2014, Wang Kang (scateu@gmail.com) 184 | 185 | http://hackrf.net : A Chinese HackRF Community 186 | 187 | 188 | ## Windows pre-compiled version 189 | 190 | Thanks to ikaya1965: 191 | - 192 | 193 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | rm -rf config.cache autom4te*.cache 2 | #autoreconf --install 3 | 4 | case `uname` in Darwin*) glibtoolize --automake ;; 5 | *) libtoolize --automake ;; esac 6 | aclocal 7 | autoconf 8 | autoheader 9 | automake --add-missing 10 | -------------------------------------------------------------------------------- /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 | # Checks for hackrf 30 | PKG_CHECK_MODULES(LIBHACKRF, libhackrf) 31 | AC_SUBST(LIBHACKRF_LIBS) 32 | AC_SUBST(LIBHACKRF_CFLAGS) 33 | 34 | AM_CONDITIONAL([DARWIN], false) 35 | 36 | # OSX doesn't support System V shared memory 37 | AC_CANONICAL_HOST 38 | case "$host_os" in 39 | darwin*) 40 | AC_DEFINE([D_HOST_OSX], [], [building for OSX]) 41 | AM_CONDITIONAL([DARWIN], true) 42 | ;; 43 | esac 44 | 45 | case "$host_cpu" in 46 | arm*) 47 | AC_DEFINE([D_HOST_OSX], [], [building without shared memory]) 48 | ;; 49 | esac 50 | 51 | AC_CONFIG_FILES([Makefile 52 | src/Makefile]) 53 | AC_OUTPUT 54 | -------------------------------------------------------------------------------- /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) $(LIBHACKRF_CFLAGS) 23 | 24 | if DARWIN 25 | kal_LDADD = $(FFTW3_LIBS) $(LIBHACKRF_LIBS) 26 | else 27 | kal_LDADD = $(FFTW3_LIBS) $(LIBHACKRF_LIBS) -lrt 28 | endif 29 | -------------------------------------------------------------------------------- /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 * 38 | bi_to_str (int bi) 39 | { 40 | 41 | switch (bi) 42 | { 43 | case GSM_850: 44 | return "GSM-850"; 45 | 46 | case GSM_R_900: 47 | return "GSM-R-900"; 48 | 49 | case GSM_900: 50 | return "GSM-900"; 51 | 52 | case GSM_E_900: 53 | return "E-GSM-900"; 54 | 55 | case DCS_1800: 56 | return "DCS-1800"; 57 | 58 | case PCS_1900: 59 | return "PCS-1900"; 60 | 61 | default: 62 | return "unknown band indicator"; 63 | } 64 | } 65 | 66 | 67 | int 68 | str_to_bi (char *s) 69 | { 70 | 71 | if (!strcmp (s, "GSM850") || !strcmp (s, "GSM-850") || !strcmp (s, "850")) 72 | return GSM_850; 73 | 74 | if (!strcmp (s, "GSM-R") || !strcmp (s, "R-GSM")) 75 | return GSM_R_900; 76 | 77 | if (!strcmp (s, "GSM900") || !strcmp (s, "GSM-900") || !strcmp (s, "900")) 78 | return GSM_900; 79 | 80 | if (!strcmp (s, "EGSM") || !strcmp (s, "E-GSM") || !strcmp (s, "EGSM900") || 81 | !strcmp (s, "E-GSM900") || !strcmp (s, "E-GSM-900")) 82 | return GSM_E_900; 83 | 84 | if (!strcmp (s, "DCS") || !strcmp (s, "DCS1800") || 85 | !strcmp (s, "DCS-1800") || !strcmp (s, "1800")) 86 | return DCS_1800; 87 | 88 | if (!strcmp (s, "PCS") || !strcmp (s, "PCS1900") || 89 | !strcmp (s, "PCS-1900") || !strcmp (s, "1900")) 90 | return PCS_1900; 91 | 92 | return -1; 93 | } 94 | 95 | 96 | double 97 | arfcn_to_freq (int n, int *bi) 98 | { 99 | 100 | if ((128 <= n) && (n <= 251)) 101 | { 102 | if (bi) 103 | *bi = GSM_850; 104 | return 824.2e6 + 0.2e6 * (n - 128) + 45.0e6; 105 | } 106 | 107 | if ((1 <= n) && (n <= 124)) 108 | { 109 | if (bi && (*bi != GSM_E_900)) 110 | *bi = GSM_900; 111 | return 890.0e6 + 0.2e6 * n + 45.0e6; 112 | } 113 | 114 | if (n == 0) 115 | { 116 | if (bi) 117 | *bi = GSM_E_900; 118 | return 935e6; 119 | } 120 | if ((955 <= n) && (n <= 1023)) 121 | { 122 | if (bi) 123 | { 124 | if (975 <= n) 125 | *bi = GSM_E_900; 126 | else 127 | *bi = GSM_R_900; 128 | } 129 | return 890.0e6 + 0.2e6 * (n - 1024) + 45.0e6; 130 | } 131 | 132 | if ((512 <= n) && (n <= 810)) 133 | { 134 | if (!bi) 135 | { 136 | fprintf (stderr, "error: ambiguous arfcn: %d\n", n); 137 | return -1.0; 138 | } 139 | 140 | if (*bi == DCS_1800) 141 | return 1710.2e6 + 0.2e6 * (n - 512) + 95.0e6; 142 | 143 | if (*bi == PCS_1900) 144 | return 1850.2e6 + 0.2e6 * (n - 512) + 80.0e6; 145 | 146 | fprintf (stderr, "error: bad (arfcn, band indicator) pair: " 147 | "(%d, %s)\n", n, bi_to_str (*bi)); 148 | return -1.0; 149 | } 150 | 151 | if ((811 <= n) && (n <= 885)) 152 | { 153 | if (bi) 154 | *bi = DCS_1800; 155 | return 1710.2e6 + 0.2e6 * (n - 512) + 95.0e6; 156 | } 157 | 158 | fprintf (stderr, "error: bad arfcn: %d\n", n); 159 | return -1.0; 160 | } 161 | 162 | 163 | int 164 | freq_to_arfcn (double freq, int *bi) 165 | { 166 | 167 | if ((869.2e6 <= freq) && (freq <= 893.8e6)) 168 | { 169 | if (bi) 170 | *bi = GSM_850; 171 | return (int) ((freq - 869.2e6) / 0.2e6) + 128; 172 | } 173 | 174 | if ((921.2e6 <= freq) && (freq <= 925.0e6)) 175 | { 176 | if (bi) 177 | *bi = GSM_R_900; 178 | return (int) ((freq - 935e6) / 0.2e6) + 1024; 179 | } 180 | 181 | if ((935.2e6 <= freq) && (freq <= 959.8e6)) 182 | { 183 | if (bi) 184 | *bi = GSM_900; 185 | return (int) ((freq - 935e6) / 0.2e6); 186 | } 187 | 188 | if (935.0e6 == freq) 189 | { 190 | if (bi) 191 | *bi = GSM_E_900; 192 | return 0; 193 | } 194 | if ((925.2e6 <= freq) && (freq <= 934.8e6)) 195 | { 196 | if (bi) 197 | *bi = GSM_E_900; 198 | return (int) ((freq - 935e6) / 0.2e6) + 1024; 199 | } 200 | 201 | if ((1805.2e6 <= freq) && (freq <= 1879.8e6)) 202 | { 203 | if (bi) 204 | *bi = DCS_1800; 205 | return (int) ((freq - 1805.2e6) / 0.2e6) + 512; 206 | } 207 | 208 | if ((1930.2e6 <= freq) && (freq <= 1989.8e6)) 209 | { 210 | if (bi) 211 | *bi = PCS_1900; 212 | return (int) ((freq - 1930.2e6) / 0.2e6) + 512; 213 | } 214 | 215 | fprintf (stderr, "error: bad frequency: %lf\n", freq); 216 | return -1; 217 | } 218 | 219 | 220 | int 221 | first_chan (int bi) 222 | { 223 | 224 | switch (bi) 225 | { 226 | case GSM_850: 227 | return 128; 228 | 229 | case GSM_R_900: 230 | return 955; 231 | 232 | case GSM_900: 233 | return 1; 234 | 235 | case GSM_E_900: 236 | return 0; 237 | 238 | case DCS_1800: 239 | return 512; 240 | 241 | case PCS_1900: 242 | return 512; 243 | 244 | default: 245 | return -1; 246 | } 247 | 248 | return -1; 249 | } 250 | 251 | 252 | int 253 | next_chan_loop (int chan, int bi) 254 | { 255 | 256 | switch (bi) 257 | { 258 | case GSM_850: 259 | if ((128 <= chan) && (chan < 251)) 260 | return chan + 1; 261 | if (chan == 251) 262 | return 128; 263 | return -1; 264 | 265 | case GSM_R_900: 266 | if ((955 <= chan) && (chan < 974)) 267 | return chan + 1; 268 | if (chan == 974) 269 | return 955; 270 | return -1; 271 | 272 | case GSM_900: 273 | if ((1 <= chan) && (chan < 124)) 274 | return chan + 1; 275 | if (chan == 124) 276 | return 1; 277 | return -1; 278 | 279 | case GSM_E_900: 280 | if ((0 <= chan) && (chan < 124)) 281 | return chan + 1; 282 | if (chan == 124) 283 | return 975; 284 | if ((975 <= chan) && (chan < 1023)) 285 | return chan + 1; 286 | if (chan == 1023) 287 | return 0; 288 | return -1; 289 | 290 | case DCS_1800: 291 | if ((512 <= chan) && (chan < 885)) 292 | return chan + 1; 293 | if (chan == 885) 294 | return 512; 295 | return -1; 296 | 297 | case PCS_1900: 298 | if ((512 <= chan) && (chan < 810)) 299 | return chan + 1; 300 | if (chan == 810) 301 | return 512; 302 | return -1; 303 | 304 | default: 305 | return -1; 306 | } 307 | 308 | return -1; 309 | } 310 | 311 | 312 | int 313 | next_chan (int chan, int bi) 314 | { 315 | 316 | switch (bi) 317 | { 318 | case GSM_850: 319 | if ((128 <= chan) && (chan < 251)) 320 | return chan + 1; 321 | return -1; 322 | 323 | case GSM_R_900: 324 | if ((955 <= chan) && (chan < 974)) 325 | return chan + 1; 326 | return -1; 327 | 328 | case GSM_900: 329 | if ((1 <= chan) && (chan < 124)) 330 | return chan + 1; 331 | return -1; 332 | 333 | case GSM_E_900: 334 | if ((0 <= chan) && (chan < 124)) 335 | return chan + 1; 336 | if (chan == 124) 337 | return 975; 338 | if ((975 <= chan) && (chan < 1023)) 339 | return chan + 1; 340 | return -1; 341 | 342 | case DCS_1800: 343 | if ((512 <= chan) && (chan < 885)) 344 | return chan + 1; 345 | return -1; 346 | 347 | case PCS_1900: 348 | if ((512 <= chan) && (chan < 810)) 349 | return chan + 1; 350 | return -1; 351 | 352 | default: 353 | return -1; 354 | } 355 | 356 | return -1; 357 | } 358 | -------------------------------------------------------------------------------- /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 | { 30 | BI_NOT_DEFINED, 31 | GSM_850, 32 | GSM_R_900, 33 | GSM_900, 34 | GSM_E_900, 35 | DCS_1800, 36 | PCS_1900 37 | }; 38 | 39 | const char *bi_to_str (int bi); 40 | int str_to_bi (char *s); 41 | double arfcn_to_freq (int n, int *bi = 0); 42 | int freq_to_arfcn (double freq, int *bi = 0); 43 | int first_chan (int bi); 44 | int next_chan (int chan, int bi); 45 | -------------------------------------------------------------------------------- /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 47 | vectornorm2 (const complex * v, const unsigned int len) 48 | { 49 | 50 | unsigned int i; 51 | double e = 0.0; 52 | 53 | for (i = 0; i < len; i++) 54 | e += norm (v[i]); 55 | 56 | return e; 57 | } 58 | 59 | 60 | int 61 | c0_detect (usrp_source * u, int bi, int chan) 62 | { 63 | 64 | #define GSM_RATE (1625000.0 / 6.0) 65 | #define NOTFOUND_MAX 10 66 | 67 | int i, chan_count; 68 | unsigned int overruns, b_len, frames_len, found_count, notfound_count, r; 69 | float offset, spower[BUFSIZ]; 70 | double freq, sps, n, power[BUFSIZ], sum = 0, a; 71 | complex *b; 72 | circular_buffer *ub; 73 | fcch_detector *l = new fcch_detector (u->sample_rate ()); 74 | 75 | if (bi == BI_NOT_DEFINED) 76 | { 77 | fprintf (stderr, "error: c0_detect: band not defined\n"); 78 | return -1; 79 | } 80 | 81 | sps = u->sample_rate () / GSM_RATE; 82 | frames_len = (unsigned int) ceil ((12 * 8 * 156.25 + 156.25) * sps); 83 | ub = u->get_buffer (); 84 | 85 | // first, we calculate the power in each channel 86 | if (g_verbosity > 2) 87 | { 88 | fprintf (stderr, "calculate power in each channel:\n"); 89 | } 90 | u->start (); 91 | u->flush (); 92 | for (i = first_chan (bi); i >= 0; i = next_chan (i, bi)) 93 | { 94 | if (chan > -1 && chan != i) 95 | { 96 | power[i] = 0; 97 | continue; 98 | } 99 | freq = arfcn_to_freq (i, &bi); 100 | if (!u->tune (freq)) 101 | { 102 | fprintf (stderr, "error: usrp_source::tune\n"); 103 | return -1; 104 | } 105 | 106 | do 107 | { 108 | u->flush (); 109 | if (u->fill (frames_len, &overruns)) 110 | { 111 | fprintf (stderr, "error: usrp_source::fill\n"); 112 | return -1; 113 | } 114 | } 115 | while (overruns); 116 | 117 | b = (complex *) ub->peek (&b_len); 118 | n = sqrt (vectornorm2 (b, frames_len)); 119 | power[i] = n; 120 | if (g_verbosity > 2) 121 | { 122 | fprintf (stderr, "\tchan %d (%.1fMHz):\tpower: %lf\n", 123 | i, freq / 1e6, n); 124 | } 125 | } 126 | 127 | /* 128 | * We want to use the average to determine which channels have 129 | * power, and hence a possibility of being channel 0 on a BTS. 130 | * However, some channels in the band can be extremely noisy. (E.g., 131 | * CDMA traffic in GSM-850.) Hence we won't consider the noisiest 132 | * channels when we construct the average. 133 | */ 134 | chan_count = 0; 135 | for (i = first_chan (bi); i >= 0; i = next_chan (i, bi)) 136 | { 137 | spower[chan_count++] = power[i]; 138 | } 139 | sort (spower, chan_count); 140 | 141 | // average the lowest %60 142 | a = avg (spower, chan_count - 4 * chan_count / 10, 0); 143 | 144 | if (g_verbosity > 0) 145 | { 146 | fprintf (stderr, "channel detect threshold: %lf\n", a); 147 | } 148 | 149 | // then we look for fcch bursts 150 | printf ("%s:\n", bi_to_str (bi)); 151 | found_count = 0; 152 | notfound_count = 0; 153 | sum = 0; 154 | i = first_chan (bi); 155 | do 156 | { 157 | if ((chan > -1 && i != chan) || (chan == -1 && power[i] <= a)) 158 | { 159 | i = next_chan (i, bi); 160 | continue; 161 | } 162 | 163 | freq = arfcn_to_freq (i, &bi); 164 | if (!u->tune (freq)) 165 | { 166 | fprintf (stderr, "error: usrp_source::tune\n"); 167 | return -1; 168 | } 169 | 170 | do 171 | { 172 | u->flush (); 173 | if (u->fill (frames_len, &overruns)) 174 | { 175 | fprintf (stderr, "error: usrp_source::fill\n"); 176 | return -1; 177 | } 178 | } 179 | while (overruns); 180 | 181 | b = (complex *) ub->peek (&b_len); 182 | r = l->scan (b, b_len, &offset, 0); 183 | if (r && (fabs (offset - GSM_RATE / 4) < ERROR_DETECT_OFFSET_MAX)) 184 | { 185 | // found 186 | printf ("\tchan: %4d (%.1fMHz ", i, freq / 1e6); 187 | display_freq (offset - GSM_RATE / 4); 188 | printf (")\tpower: %10.2f\n", power[i]); 189 | notfound_count = 0; 190 | i = next_chan (i, bi); 191 | } 192 | else 193 | { 194 | // not found 195 | notfound_count += 1; 196 | if (notfound_count >= NOTFOUND_MAX) 197 | { 198 | notfound_count = 0; 199 | i = next_chan (i, bi); 200 | } 201 | } 202 | } 203 | while (i >= 0); 204 | 205 | return 0; 206 | } 207 | -------------------------------------------------------------------------------- /src/c0_detect.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Joshua Lackey 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | int c0_detect (usrp_source * u, int bi, int channel); 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, 59 | const unsigned int overwrite) 60 | { 61 | 62 | int shm_id_temp, shm_id_guard, shm_id_buf; 63 | void *base; 64 | 65 | if (!buf_len) 66 | throw std::runtime_error ("circular_buffer: buffer len is 0"); 67 | 68 | if (!item_size) 69 | throw std::runtime_error ("circular_buffer: item size is 0"); 70 | 71 | // calculate buffer size 72 | m_item_size = item_size; 73 | m_buf_size = item_size * buf_len; 74 | 75 | m_pagesize = getpagesize (); 76 | if (m_buf_size % m_pagesize) 77 | m_buf_size = (m_buf_size + m_pagesize) & ~(m_pagesize - 1); 78 | m_buf_len = m_buf_size / item_size; 79 | 80 | // create an address-range that can contain everything 81 | if ((shm_id_temp = shmget (IPC_PRIVATE, 2 * m_pagesize + 2 * m_buf_size, 82 | IPC_CREAT | S_IRUSR | S_IWUSR)) == -1) 83 | { 84 | perror ("shmget"); 85 | throw std::runtime_error ("circular_buffer: shmget"); 86 | } 87 | 88 | // create a read-only guard page 89 | if ((shm_id_guard = shmget (IPC_PRIVATE, m_pagesize, 90 | IPC_CREAT | S_IRUSR)) == -1) 91 | { 92 | shmctl (shm_id_temp, IPC_RMID, 0); 93 | perror ("shmget"); 94 | throw std::runtime_error ("circular_buffer: shmget"); 95 | } 96 | 97 | // create the data buffer 98 | if ((shm_id_buf = shmget (IPC_PRIVATE, m_buf_size, IPC_CREAT | S_IRUSR | 99 | S_IWUSR)) == -1) 100 | { 101 | perror ("shmget"); 102 | shmctl (shm_id_temp, IPC_RMID, 0); 103 | shmctl (shm_id_guard, IPC_RMID, 0); 104 | throw std::runtime_error ("circular_buffer: shmget"); 105 | } 106 | 107 | // attach temporary memory to get an address-range 108 | if ((base = shmat (shm_id_temp, 0, 0)) == (void *) (-1)) 109 | { 110 | perror ("shmat"); 111 | shmctl (shm_id_temp, IPC_RMID, 0); 112 | shmctl (shm_id_guard, IPC_RMID, 0); 113 | shmctl (shm_id_buf, IPC_RMID, 0); 114 | throw std::runtime_error ("circular_buffer: shmat"); 115 | } 116 | 117 | // remove the temporary memory id 118 | shmctl (shm_id_temp, IPC_RMID, 0); 119 | 120 | // detach and free the temporary memory 121 | shmdt (base); 122 | 123 | // race condition here 124 | 125 | // map first copy of guard page with previous address 126 | if (shmat (shm_id_guard, base, SHM_RDONLY) == (void *) (-1)) 127 | { 128 | perror ("shmat"); 129 | shmctl (shm_id_guard, IPC_RMID, 0); 130 | shmctl (shm_id_buf, IPC_RMID, 0); 131 | throw std::runtime_error ("circular_buffer: shmat"); 132 | } 133 | 134 | // map first copy of the buffer 135 | if (shmat (shm_id_buf, (char *) base + m_pagesize, 0) == (void *) (-1)) 136 | { 137 | perror ("shmat"); 138 | shmctl (shm_id_guard, IPC_RMID, 0); 139 | shmctl (shm_id_buf, IPC_RMID, 0); 140 | shmdt (base); 141 | throw std::runtime_error ("circular_buffer: shmat"); 142 | } 143 | 144 | // map second copy of the buffer 145 | if (shmat (shm_id_buf, (char *) base + m_pagesize + m_buf_size, 0) == 146 | (void *) (-1)) 147 | { 148 | perror ("shmat"); 149 | shmctl (shm_id_guard, IPC_RMID, 0); 150 | shmctl (shm_id_buf, IPC_RMID, 0); 151 | shmdt ((char *) base + m_pagesize); 152 | shmdt (base); 153 | throw std::runtime_error ("circular_buffer: shmat"); 154 | } 155 | 156 | // map second copy of guard page 157 | if (shmat (shm_id_guard, (char *) base + m_pagesize + 2 * m_buf_size, 158 | SHM_RDONLY) == (void *) (-1)) 159 | { 160 | perror ("shmat"); 161 | shmctl (shm_id_guard, IPC_RMID, 0); 162 | shmctl (shm_id_buf, IPC_RMID, 0); 163 | shmdt ((char *) base + m_pagesize + m_buf_size); 164 | shmdt ((char *) base + m_pagesize); 165 | shmdt ((char *) base); 166 | throw std::runtime_error ("circular_buffer: shmat"); 167 | } 168 | 169 | // remove the id for the guard and buffer, we don't need them anymore 170 | shmctl (shm_id_guard, IPC_RMID, 0); 171 | shmctl (shm_id_buf, IPC_RMID, 0); 172 | 173 | // save the base address for detach later 174 | m_base = base; 175 | 176 | // save a pointer to the data 177 | m_buf = (char *) base + m_pagesize; 178 | 179 | m_r = m_w = 0; 180 | m_read = m_written = 0; 181 | 182 | m_item_size = item_size; 183 | 184 | m_overwrite = overwrite; 185 | 186 | pthread_mutex_init (&m_mutex, 0); 187 | } 188 | 189 | circular_buffer::~circular_buffer () 190 | { 191 | 192 | shmdt ((char *) m_base + m_pagesize + 2 * m_buf_size); 193 | shmdt ((char *) m_base + m_pagesize + m_buf_size); 194 | shmdt ((char *) m_base + m_pagesize); 195 | shmdt ((char *) m_base); 196 | } 197 | 198 | #else 199 | circular_buffer::circular_buffer (const unsigned int buf_len, 200 | const unsigned int item_size, 201 | const unsigned int overwrite) 202 | { 203 | 204 | if (!buf_len) 205 | throw std::runtime_error ("circular_buffer: buffer len is 0"); 206 | 207 | if (!item_size) 208 | throw std::runtime_error ("circular_buffer: item size is 0"); 209 | 210 | // calculate buffer size 211 | m_item_size = item_size; 212 | m_buf_size = item_size * buf_len; 213 | m_buf_len = m_buf_size / item_size; 214 | 215 | 216 | d_handle = CreateFileMapping (INVALID_HANDLE_VALUE, // use paging file 217 | NULL, // default security 218 | PAGE_READWRITE, // read/write access 219 | 0, // max. object size 220 | m_buf_size, // buffer size 221 | NULL); // name of mapping object 222 | 223 | 224 | if (d_handle == NULL || d_handle == INVALID_HANDLE_VALUE) 225 | { 226 | throw std::runtime_error ("gr_vmcircbuf_mmap_createfilemapping"); 227 | } 228 | 229 | // Allocate virtual memory of the needed size, then free it so we can use it 230 | LPVOID first_tmp; 231 | first_tmp = VirtualAlloc (NULL, 2 * m_buf_size, MEM_RESERVE, PAGE_NOACCESS); 232 | if (first_tmp == NULL) 233 | { 234 | CloseHandle (d_handle); // cleanup 235 | throw std::runtime_error ("gr_vmcircbuf_mmap_createfilemapping"); 236 | } 237 | 238 | if (VirtualFree (first_tmp, 0, MEM_RELEASE) == 0) 239 | { 240 | CloseHandle (d_handle); // cleanup 241 | throw std::runtime_error ("gr_vmcircbuf_mmap_createfilemapping"); 242 | } 243 | 244 | d_first_copy = MapViewOfFileEx ((HANDLE) d_handle, // handle to map object 245 | FILE_MAP_WRITE, // read/write permission 246 | 0, 0, m_buf_size, first_tmp); 247 | if (d_first_copy != first_tmp) 248 | { 249 | CloseHandle (d_handle); // cleanup 250 | throw std::runtime_error ("gr_vmcircbuf_mmap_createfilemapping"); 251 | } 252 | 253 | d_second_copy = MapViewOfFileEx ((HANDLE) d_handle, // handle to map object 254 | FILE_MAP_WRITE, // read/write permission 255 | 0, 0, m_buf_size, (char *) first_tmp + m_buf_size); //(LPVOID) ((char *)d_first_copy + size)); 256 | 257 | if (d_second_copy != (char *) first_tmp + m_buf_size) 258 | { 259 | UnmapViewOfFile (d_first_copy); 260 | CloseHandle (d_handle); // cleanup 261 | throw std::runtime_error ("gr_vmcircbuf_mmap_createfilemapping"); 262 | } 263 | 264 | // save a pointer to the data 265 | m_buf = d_first_copy; // (char *)base + m_pagesize; 266 | 267 | m_r = m_w = 0; 268 | m_read = m_written = 0; 269 | 270 | m_item_size = item_size; 271 | 272 | m_overwrite = overwrite; 273 | 274 | pthread_mutex_init (&m_mutex, 0); 275 | 276 | } 277 | 278 | circular_buffer::~circular_buffer () 279 | { 280 | UnmapViewOfFile (d_first_copy); 281 | UnmapViewOfFile (d_second_copy); 282 | CloseHandle (d_handle); 283 | } 284 | #endif 285 | #else /* !D_HOST_OSX */ 286 | 287 | 288 | /* 289 | * OSX doesn't support System V shared memory. Using GNU Radio as an example, 290 | * we'll implement this for OSX using Posix shared memory. I'm not exactly 291 | * sure why GNU Radio prefers the System V usage, but I seem to recall there 292 | * was a reason. 293 | */ 294 | circular_buffer::circular_buffer (const unsigned int buf_len, 295 | const unsigned int item_size, 296 | const unsigned int overwrite) 297 | { 298 | 299 | int shm_fd; 300 | char shm_name[255]; // XXX should be NAME_MAX 301 | void *base; 302 | 303 | if (!buf_len) 304 | throw std::runtime_error ("circular_buffer: buffer len is 0"); 305 | 306 | if (!item_size) 307 | throw std::runtime_error ("circular_buffer: item size is 0"); 308 | 309 | // calculate buffer size 310 | m_item_size = item_size; 311 | m_buf_size = item_size * buf_len; 312 | 313 | m_pagesize = getpagesize (); 314 | if (m_buf_size % m_pagesize) 315 | m_buf_size = (m_buf_size + m_pagesize) & ~(m_pagesize - 1); 316 | m_buf_len = m_buf_size / item_size; 317 | 318 | // create unique-ish name 319 | snprintf (shm_name, sizeof (shm_name), "/kalibrate-%d", getpid ()); 320 | 321 | // create a Posix shared memory object 322 | if ((shm_fd = 323 | shm_open (shm_name, O_RDWR | O_CREAT | O_EXCL, 324 | S_IRUSR | S_IWUSR)) == -1) 325 | { 326 | perror ("shm_open"); 327 | throw std::runtime_error ("circular_buffer: shm_open"); 328 | } 329 | 330 | // create enough space to hold everything 331 | if (ftruncate (shm_fd, 2 * m_pagesize + 2 * m_buf_size) == -1) 332 | { 333 | perror ("ftruncate"); 334 | close (shm_fd); 335 | shm_unlink (shm_name); 336 | throw std::runtime_error ("circular_buffer: ftruncate"); 337 | } 338 | 339 | // get an address for the buffer 340 | if ((base = 341 | mmap (0, 2 * m_pagesize + 2 * m_buf_size, PROT_NONE, MAP_SHARED, 342 | shm_fd, 0)) == MAP_FAILED) 343 | { 344 | perror ("mmap"); 345 | close (shm_fd); 346 | shm_unlink (shm_name); 347 | throw std::runtime_error ("circular_buffer: mmap (base)"); 348 | } 349 | 350 | // unmap everything but the first guard page 351 | if (munmap ((char *) base + m_pagesize, m_pagesize + 2 * m_buf_size) == -1) 352 | { 353 | perror ("munmap"); 354 | close (shm_fd); 355 | shm_unlink (shm_name); 356 | throw std::runtime_error ("circular_buffer: munmap"); 357 | } 358 | 359 | // race condition 360 | 361 | // map first copy of the buffer 362 | if (mmap 363 | ((char *) base + m_pagesize, m_buf_size, PROT_READ | PROT_WRITE, 364 | MAP_SHARED | MAP_FIXED, shm_fd, m_pagesize) == MAP_FAILED) 365 | { 366 | perror ("mmap"); 367 | munmap (base, 2 * m_pagesize + 2 * m_buf_size); 368 | close (shm_fd); 369 | shm_unlink (shm_name); 370 | throw std::runtime_error ("circular_buffer: mmap (buf 1)"); 371 | } 372 | 373 | // map second copy of the buffer 374 | if (mmap 375 | ((char *) base + m_pagesize + m_buf_size, m_buf_size, 376 | PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, shm_fd, 377 | m_pagesize) == MAP_FAILED) 378 | { 379 | perror ("mmap"); 380 | munmap (base, 2 * m_pagesize + 2 * m_buf_size); 381 | close (shm_fd); 382 | shm_unlink (shm_name); 383 | throw std::runtime_error ("circular_buffer: mmap (buf 2)"); 384 | } 385 | 386 | // map second copy of the guard page 387 | if (mmap 388 | ((char *) base + m_pagesize + 2 * m_buf_size, m_pagesize, PROT_NONE, 389 | MAP_SHARED | MAP_FIXED, shm_fd, 0) == MAP_FAILED) 390 | { 391 | perror ("mmap"); 392 | munmap (base, 2 * m_pagesize + 2 * m_buf_size); 393 | close (shm_fd); 394 | shm_unlink (shm_name); 395 | throw std::runtime_error ("circular_buffer: mmap (guard)"); 396 | } 397 | 398 | // both the file and name are unnecessary now 399 | close (shm_fd); 400 | shm_unlink (shm_name); 401 | 402 | // save the base address for unmap later 403 | m_base = base; 404 | 405 | // save a pointer to the data 406 | m_buf = (char *) base + m_pagesize; 407 | 408 | m_r = m_w = 0; 409 | m_read = m_written = 0; 410 | 411 | m_item_size = item_size; 412 | 413 | m_overwrite = overwrite; 414 | 415 | pthread_mutex_init (&m_mutex, 0); 416 | } 417 | 418 | 419 | circular_buffer::~circular_buffer () 420 | { 421 | 422 | munmap (m_base, 2 * m_pagesize + 2 * m_buf_size); 423 | } 424 | #endif /* !D_HOST_OSX */ 425 | 426 | 427 | /* 428 | * The amount to read can only grow unless someone calls read after this is 429 | * called. No real good way to tie the two together. 430 | */ 431 | unsigned int 432 | circular_buffer::data_available () 433 | { 434 | 435 | unsigned int amt; 436 | 437 | pthread_mutex_lock (&m_mutex); 438 | amt = m_written - m_read; // item_size 439 | pthread_mutex_unlock (&m_mutex); 440 | 441 | return amt; 442 | } 443 | 444 | 445 | unsigned int 446 | circular_buffer::space_available () 447 | { 448 | 449 | unsigned int amt; 450 | 451 | pthread_mutex_lock (&m_mutex); 452 | amt = m_buf_len - (m_written - m_read); 453 | pthread_mutex_unlock (&m_mutex); 454 | 455 | return amt; 456 | } 457 | 458 | 459 | #ifndef MIN 460 | #define MIN(a, b) ((a)<(b)?(a):(b)) 461 | #endif /* !MIN */ 462 | 463 | /* 464 | * m_buf_size is in terms of bytes 465 | * m_r and m_w are offsets in bytes 466 | * m_buf_len is in terms of m_item_size 467 | * buf_len is in terms of m_item_size 468 | * len, m_written, and m_read are all in terms of m_item_size 469 | */ 470 | unsigned int 471 | circular_buffer::read (void *buf, const unsigned int buf_len) 472 | { 473 | 474 | unsigned int len; 475 | 476 | pthread_mutex_lock (&m_mutex); 477 | len = MIN (buf_len, m_written - m_read); 478 | memcpy (buf, (char *) m_buf + m_r, len * m_item_size); 479 | m_read += len; 480 | if (m_read == m_written) 481 | { 482 | m_r = m_w = 0; 483 | m_read = m_written = 0; 484 | } 485 | else 486 | m_r = (m_r + len * m_item_size) % m_buf_size; 487 | pthread_mutex_unlock (&m_mutex); 488 | 489 | return len; 490 | } 491 | 492 | 493 | /* 494 | * warning: 495 | * 496 | * Don't use read() while you are peek()'ing. write() should be 497 | * okay unless you have an overwrite buffer. 498 | */ 499 | void * 500 | circular_buffer::peek (unsigned int *buf_len) 501 | { 502 | 503 | unsigned int len; 504 | void *p; 505 | 506 | pthread_mutex_lock (&m_mutex); 507 | len = m_written - m_read; 508 | p = (char *) m_buf + m_r; 509 | pthread_mutex_unlock (&m_mutex); 510 | 511 | if (buf_len) 512 | *buf_len = len; 513 | 514 | return p; 515 | } 516 | 517 | 518 | void * 519 | circular_buffer::poke (unsigned int *buf_len) 520 | { 521 | 522 | unsigned int len; 523 | void *p; 524 | 525 | pthread_mutex_lock (&m_mutex); 526 | len = m_buf_len - (m_written - m_read); 527 | p = (char *) m_buf + m_w; 528 | pthread_mutex_unlock (&m_mutex); 529 | 530 | if (buf_len) 531 | *buf_len = len; 532 | 533 | return p; 534 | } 535 | 536 | 537 | unsigned int 538 | circular_buffer::purge (const unsigned int buf_len) 539 | { 540 | 541 | unsigned int len; 542 | 543 | pthread_mutex_lock (&m_mutex); 544 | len = MIN (buf_len, m_written - m_read); 545 | m_read += len; 546 | if (m_read == m_written) 547 | { 548 | m_r = m_w = 0; 549 | m_read = m_written = 0; 550 | } 551 | else 552 | m_r = (m_r + len * m_item_size) % m_buf_size; 553 | pthread_mutex_unlock (&m_mutex); 554 | 555 | return len; 556 | } 557 | 558 | 559 | unsigned int 560 | circular_buffer::write (const void *buf, const unsigned int buf_len) 561 | { 562 | 563 | unsigned int len, buf_off = 0; 564 | 565 | pthread_mutex_lock (&m_mutex); 566 | if (m_overwrite) 567 | { 568 | if (buf_len > m_buf_len) 569 | { 570 | buf_off = buf_len - m_buf_len; 571 | len = m_buf_len; 572 | } 573 | else 574 | len = buf_len; 575 | } 576 | else 577 | len = MIN (buf_len, m_buf_len - (m_written - m_read)); 578 | memcpy ((char *) m_buf + m_w, (char *) buf + buf_off * m_item_size, 579 | len * m_item_size); 580 | m_written += len; 581 | m_w = (m_w + len * m_item_size) % m_buf_size; 582 | if (m_written > m_buf_len + m_read) 583 | { 584 | m_read = m_written - m_buf_len; 585 | m_r = m_w; 586 | } 587 | pthread_mutex_unlock (&m_mutex); 588 | 589 | return len; 590 | } 591 | 592 | 593 | void 594 | circular_buffer::wrote (unsigned int len) 595 | { 596 | 597 | pthread_mutex_lock (&m_mutex); 598 | m_written += len; 599 | m_w = (m_w + len * m_item_size) % m_buf_size; 600 | pthread_mutex_unlock (&m_mutex); 601 | } 602 | 603 | 604 | void 605 | circular_buffer::flush () 606 | { 607 | 608 | pthread_mutex_lock (&m_mutex); 609 | m_read = m_written = 0; 610 | m_r = m_w = 0; 611 | pthread_mutex_unlock (&m_mutex); 612 | } 613 | 614 | 615 | void 616 | circular_buffer::flush_nolock () 617 | { 618 | 619 | m_read = m_written = 0; 620 | m_r = m_w = 0; 621 | } 622 | 623 | 624 | void 625 | circular_buffer::lock () 626 | { 627 | 628 | pthread_mutex_lock (&m_mutex); 629 | } 630 | 631 | 632 | void 633 | circular_buffer::unlock () 634 | { 635 | 636 | pthread_mutex_unlock (&m_mutex); 637 | } 638 | 639 | 640 | unsigned int 641 | circular_buffer::buf_len () 642 | { 643 | 644 | return m_buf_len; 645 | } 646 | -------------------------------------------------------------------------------- /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 | { 54 | public: 55 | circular_buffer (const unsigned int buf_len, const unsigned int item_size = 56 | 1, const unsigned int overwrite = 0); 57 | ~circular_buffer (); 58 | 59 | unsigned int read (void *buf, const unsigned int buf_len); 60 | void *peek (unsigned int *buf_len); 61 | unsigned int purge (const unsigned int buf_len); 62 | void *poke (unsigned int *buf_len); 63 | void wrote (unsigned int len); 64 | unsigned int write (const void *buf, const unsigned int buf_len); 65 | unsigned int data_available (); 66 | unsigned int space_available (); 67 | void flush (); 68 | void flush_nolock (); 69 | void lock (); 70 | void unlock (); 71 | unsigned int buf_len (); 72 | 73 | private: 74 | #ifdef _WIN32 75 | HANDLE d_handle; 76 | LPVOID d_first_copy; 77 | LPVOID d_second_copy; 78 | #endif 79 | void *m_buf; 80 | unsigned int m_buf_len, m_buf_size, m_r, m_w, m_item_size; 81 | unsigned long long m_read, m_written; 82 | 83 | unsigned int m_overwrite; 84 | 85 | void *m_base; 86 | unsigned int m_pagesize; 87 | 88 | pthread_mutex_t m_mutex; 89 | }; 90 | -------------------------------------------------------------------------------- /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 | 60 | FILE *plan_fp; 61 | char plan_name[BUFSIZ]; 62 | const char *home; 63 | 64 | 65 | m_D = D; 66 | m_p = p; 67 | m_G = G; 68 | m_e = 0.0; 69 | 70 | m_sample_rate = sample_rate; 71 | m_fcch_burst_len = (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 | { 90 | strcpy (plan_name, home); 91 | strcat (plan_name, "/"); 92 | strcat (plan_name, fftw_plan_name); 93 | if ((plan_fp = fopen (plan_name, "r"))) 94 | { 95 | fftw_import_wisdom_from_file (plan_fp); 96 | fclose (plan_fp); 97 | } 98 | m_plan = fftw_plan_dft_1d (FFT_SIZE, m_in, m_out, FFTW_FORWARD, 99 | FFTW_MEASURE); 100 | if ((plan_fp = fopen (plan_name, "w"))) 101 | { 102 | fftw_export_wisdom_to_file (plan_fp); 103 | fclose (plan_fp); 104 | } 105 | } 106 | else 107 | #endif 108 | m_plan = fftw_plan_dft_1d (FFT_SIZE, m_in, m_out, FFTW_FORWARD, 109 | FFTW_ESTIMATE); 110 | if (!m_plan) 111 | throw std::runtime_error ("fcch_detector: fftw plan failed!"); 112 | } 113 | 114 | 115 | fcch_detector::~fcch_detector () 116 | { 117 | 118 | if (m_w) 119 | { 120 | delete[]m_w; 121 | m_w = 0; 122 | } 123 | if (m_x_cb) 124 | { 125 | delete m_x_cb; 126 | m_x_cb = 0; 127 | } 128 | if (m_y_cb) 129 | { 130 | delete m_y_cb; 131 | m_y_cb = 0; 132 | } 133 | if (m_e_cb) 134 | { 135 | delete m_e_cb; 136 | m_e_cb = 0; 137 | } 138 | } 139 | 140 | 141 | enum 142 | { 143 | LOW = 0, 144 | HIGH = 1 145 | }; 146 | 147 | static unsigned int g_count = 0, g_block_s = HIGH; 148 | 149 | 150 | static inline void 151 | low_to_high_init () 152 | { 153 | 154 | g_count = 0; 155 | g_block_s = HIGH; 156 | } 157 | 158 | 159 | static inline unsigned int 160 | low_to_high (float e, float a) 161 | { 162 | 163 | unsigned int r = 0; 164 | 165 | if (e > a) 166 | { 167 | if (g_block_s == LOW) 168 | { 169 | r = g_count; 170 | g_block_s = HIGH; 171 | g_count = 0; 172 | } 173 | g_count += 1; 174 | } 175 | else 176 | { 177 | if (g_block_s == HIGH) 178 | { 179 | g_block_s = LOW; 180 | g_count = 0; 181 | } 182 | g_count += 1; 183 | } 184 | 185 | return r; 186 | } 187 | 188 | 189 | static inline int 190 | peak_valley (complex * c, unsigned int c_len, complex peak, 191 | unsigned int peak_i, unsigned int width, float *p2m) 192 | { 193 | 194 | float valley = 0.0; 195 | unsigned int i, valley_count = 0; 196 | 197 | // these constants aren't the best for all burst types 198 | for (i = 2; i < 2 + width; i++) 199 | { 200 | if (i <= peak_i) 201 | { 202 | valley += norm (c[peak_i - i]); 203 | valley_count += 1; 204 | } 205 | if (peak_i + i < c_len) 206 | { 207 | valley += norm (c[peak_i + i]); 208 | valley_count += 1; 209 | } 210 | } 211 | 212 | if (valley_count < 2) 213 | { 214 | fprintf (stderr, "error: bad valley_count\n"); 215 | return -1; 216 | } 217 | valley = sqrtf (valley / (float) valley_count) + 0.00001; 218 | 219 | if (p2m) 220 | *p2m = sqrtf (norm (peak)) / valley; 221 | 222 | return 0; 223 | } 224 | 225 | 226 | static inline float 227 | sinc (const float x) 228 | { 229 | 230 | if ((x <= -0.0001) || (0.0001 <= x)) 231 | return sinf (x) / x; 232 | return 1.0; 233 | } 234 | 235 | 236 | static inline complex 237 | interpolate_point (const complex * s, const unsigned int s_len, 238 | const float s_i) 239 | { 240 | 241 | static const unsigned int filter_len = 21; 242 | 243 | int start, end, i; 244 | unsigned int d; 245 | complex point; 246 | 247 | d = (filter_len - 1) / 2; 248 | start = (int) (floor (s_i) - d); 249 | end = (int) (floor (s_i) + d + 1); 250 | if (start < 0) 251 | start = 0; 252 | if (end > (int) (s_len - 1)) 253 | end = s_len - 1; 254 | for (point = 0.0, i = start; i <= end; i++) 255 | point += s[i] * sinc (M_PI * (i - s_i)); 256 | return point; 257 | } 258 | 259 | 260 | static inline float 261 | peak_detect (const complex * s, const unsigned int s_len, complex * peak, 262 | float *avg_power) 263 | { 264 | 265 | unsigned int i; 266 | float max = -1.0, max_i = 267 | -1.0, sample_power, sum_power, early_i, late_i, incr; 268 | complex early_p, late_p, cmax; 269 | 270 | sum_power = 0; 271 | for (i = 0; i < s_len; i++) 272 | { 273 | sample_power = norm (s[i]); 274 | sum_power += sample_power; 275 | if (sample_power > max) 276 | { 277 | max = sample_power; 278 | max_i = i; 279 | } 280 | } 281 | early_i = (1 <= max_i) ? (max_i - 1) : 0; 282 | late_i = (max_i + 1 < s_len) ? (max_i + 1) : s_len - 1; 283 | 284 | incr = 0.5; 285 | while (incr > 1.0 / 1024.0) 286 | { 287 | early_p = interpolate_point (s, s_len, early_i); 288 | late_p = interpolate_point (s, s_len, late_i); 289 | if (norm (early_p) < norm (late_p)) 290 | early_i += incr; 291 | else if (norm (early_p) > norm (late_p)) 292 | early_i -= incr; 293 | else 294 | break; 295 | incr /= 2.0; 296 | late_i = early_i + 2.0; 297 | } 298 | max_i = early_i + 1.0; 299 | cmax = interpolate_point (s, s_len, max_i); 300 | 301 | if (peak) 302 | *peak = cmax; 303 | 304 | if (avg_power) 305 | *avg_power = (sum_power - norm (cmax)) / (s_len - 1); 306 | 307 | return max_i; 308 | } 309 | 310 | 311 | static inline float 312 | itof (float index, float sample_rate, unsigned int fft_size) 313 | { 314 | 315 | double r = index * (sample_rate / (double) fft_size); 316 | 317 | /* 318 | if(index > (double)fft_size / 2.0) 319 | return r - sample_rate; 320 | else 321 | return r; 322 | */ 323 | return r; 324 | } 325 | 326 | 327 | static inline unsigned int 328 | ftoi (float frequency, float sample_rate, unsigned int fft_size) 329 | { 330 | 331 | unsigned int r = (frequency / sample_rate) * fft_size; 332 | 333 | return r; 334 | } 335 | 336 | 337 | #ifndef MIN 338 | #define MIN(a, b) (a)<(b)?(a):(b) 339 | #endif /* !MIN */ 340 | 341 | 342 | float 343 | fcch_detector::freq_detect (const complex * s, const unsigned int s_len, 344 | float *pm) 345 | { 346 | 347 | unsigned int i, len; 348 | float max_i, avg_power; 349 | complex fft[FFT_SIZE], peak; 350 | 351 | len = MIN (s_len, FFT_SIZE); 352 | for (i = 0; i < len; i++) 353 | { 354 | m_in[i][0] = s[i].real (); 355 | m_in[i][1] = s[i].imag (); 356 | } 357 | for (i = len; i < FFT_SIZE; i++) 358 | { 359 | m_in[i][0] = 0; 360 | m_in[i][1] = 0; 361 | } 362 | 363 | fftw_execute (m_plan); 364 | 365 | for (i = 0; i < FFT_SIZE; i++) 366 | { 367 | fft[i] = complex (m_out[i][0], m_out[i][1]); 368 | } 369 | 370 | max_i = peak_detect (fft, FFT_SIZE, &peak, &avg_power); 371 | if (pm) 372 | *pm = norm (peak) / avg_power; 373 | return itof (max_i, m_sample_rate, FFT_SIZE); 374 | } 375 | 376 | 377 | static inline void 378 | display_complex (const complex * s, unsigned int s_len) 379 | { 380 | 381 | for (unsigned int i = 0; i < s_len; i++) 382 | { 383 | printf ("%f\n", s[i].real ()); 384 | fprintf (stderr, "%f\n", s[i].imag ()); 385 | } 386 | } 387 | 388 | 389 | /* 390 | * scan: 391 | * 1. calculate average error 392 | * 2. find neighborhoods with low error that satisfy minimum length 393 | * 3. for each such neighborhood, take fft and calculate peak/mean 394 | * 4. if peak/mean > 50, then this is a valid finding. 395 | */ 396 | unsigned int 397 | fcch_detector::scan (const complex * s, const unsigned int s_len, 398 | float *offset, unsigned int *consumed) 399 | { 400 | 401 | static const float sps = m_sample_rate / (1625000.0 / 6.0); 402 | static const unsigned int MIN_FB_LEN = 100 * sps; 403 | static const unsigned int MIN_PM = 50; // XXX arbitrary, depends on decimation 404 | 405 | unsigned int len = 0, t, e_count, i, l_count, y_offset, y_len; 406 | float e, *a, loff = 0, pm; 407 | double sum = 0.0, avg, limit; 408 | const complex *y; 409 | 410 | // calculate the error for each sample 411 | while (len < s_len) 412 | { 413 | t = m_x_cb->write (s + len, 1); 414 | len += t; 415 | if (!next_norm_error (&e)) 416 | { 417 | m_e_cb->write (&e, 1); 418 | sum += e; 419 | } 420 | } 421 | if (consumed) 422 | *consumed = len; 423 | 424 | // calculate average error over entire buffer 425 | a = (float *) m_e_cb->peek (&e_count); 426 | avg = sum / (double) e_count; 427 | limit = 0.7 * avg; 428 | 429 | if (g_debug) 430 | { 431 | printf ("debug: error limit: %.1lf\n", limit); 432 | } 433 | 434 | // find neighborhoods where the error is smaller than the limit 435 | low_to_high_init (); 436 | for (i = 0; i < e_count; i++) 437 | { 438 | l_count = low_to_high (a[i], limit); 439 | 440 | // see if p/m indicates a pure tone 441 | pm = 0; 442 | if (l_count >= MIN_FB_LEN) 443 | { 444 | y_offset = i - l_count; 445 | y_len = (l_count < m_fcch_burst_len) ? l_count : m_fcch_burst_len; 446 | y = s + y_offset; 447 | loff = freq_detect (y, y_len, &pm); 448 | if (g_debug) 449 | printf ("debug: %.0f\t%f\t%f\n", (double) l_count / sps, pm, 450 | loff); 451 | if (pm > MIN_PM) 452 | break; 453 | } 454 | } 455 | // empty buffers for next call 456 | m_e_cb->flush (); 457 | m_x_cb->flush (); 458 | m_y_cb->flush (); 459 | 460 | if (pm <= MIN_PM) 461 | return 0; 462 | 463 | if (offset) 464 | *offset = loff; 465 | 466 | if (g_debug) 467 | { 468 | printf 469 | ("debug: fcch_detector finished -----------------------------\n"); 470 | } 471 | 472 | return 1; 473 | } 474 | 475 | 476 | unsigned int 477 | fcch_detector::update (const complex * s, const unsigned int s_len) 478 | { 479 | 480 | return m_x_cb->write (s, s_len); 481 | } 482 | 483 | 484 | unsigned int 485 | fcch_detector::get_delay () 486 | { 487 | 488 | return m_w_len - 1 + m_D; 489 | } 490 | 491 | 492 | unsigned int 493 | fcch_detector::filter_len () 494 | { 495 | 496 | return m_w_len; 497 | } 498 | 499 | 500 | static float 501 | vectornorm2 (const complex * v, const unsigned int len) 502 | { 503 | 504 | unsigned int i; 505 | float e = 0.0; 506 | 507 | for (i = 0; i < len; i++) 508 | e += norm (v[i]); 509 | 510 | return e; 511 | } 512 | 513 | 514 | /* 515 | * First y value comes out at sample x[n + m_D] = x[w_len - 1 + m_D]. 516 | * 517 | * y[0] = X(x[0], ..., x[w_len - 1 + m_D]) 518 | * 519 | * So y and e are delayed by w_len - 1 + m_D. 520 | */ 521 | int 522 | fcch_detector::next_norm_error (float *error) 523 | { 524 | 525 | unsigned int i, n, max; 526 | float E; 527 | complex *x, y, e; 528 | 529 | // n is "current" sample 530 | n = m_w_len - 1; 531 | 532 | // ensure there are enough samples in the buffer 533 | x = (complex *) m_x_cb->peek (&max); 534 | if (n + m_D >= max) 535 | return n + m_D - max + 1; 536 | 537 | // update G 538 | E = vectornorm2 (x, m_w_len); 539 | if (m_G >= 2.0 / E) 540 | m_G = 1.0 / E; 541 | 542 | // calculate filtered value 543 | y = 0.0; 544 | for (i = 0; i < m_w_len; i++) 545 | y += std::conj (m_w[i]) * x[n - i]; 546 | // m_y_cb->write(&y, 1); 547 | m_y_cb->write (x + n + m_D, 1); // XXX save filtered value? 548 | 549 | // calculate error from desired signal 550 | e = x[n + m_D] - y; 551 | 552 | // update filters with opposite gradient 553 | for (i = 0; i < m_w_len; i++) 554 | m_w[i] += m_G * std::conj (e) * x[n - i]; 555 | 556 | // update error average power 557 | E /= m_w_len; 558 | m_e = (1.0 - m_p) * m_e + m_p * norm (e); 559 | 560 | // return error ratio 561 | if (error) 562 | *error = m_e / E; 563 | 564 | // remove the processed sample from the buffer 565 | m_x_cb->purge (1); 566 | 567 | return 0; 568 | } 569 | 570 | 571 | complex * 572 | fcch_detector::dump_x (unsigned int *x_len) 573 | { 574 | 575 | return (complex *) m_x_cb->peek (x_len); 576 | } 577 | 578 | 579 | complex * 580 | fcch_detector::dump_y (unsigned int *y_len) 581 | { 582 | 583 | return (complex *) m_y_cb->peek (y_len); 584 | } 585 | 586 | 587 | unsigned int 588 | fcch_detector::y_buf_len () 589 | { 590 | 591 | return m_y_cb->buf_len (); 592 | } 593 | 594 | 595 | unsigned int 596 | fcch_detector::x_buf_len () 597 | { 598 | 599 | return m_x_cb->buf_len (); 600 | } 601 | 602 | 603 | unsigned int 604 | fcch_detector::x_purge (unsigned int len) 605 | { 606 | 607 | return m_x_cb->purge (len); 608 | } 609 | -------------------------------------------------------------------------------- /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 | 52 | public: 53 | fcch_detector (const float sample_rate, const unsigned int D = 54 | 8, const float p = 1.0 / 32.0, const float G = 1.0 / 12.5); 55 | ~fcch_detector (); 56 | unsigned int scan (const complex * s, const unsigned int s_len, 57 | float *offset, unsigned int *consumed); 58 | float freq_detect (const complex * s, const unsigned int s_len, float *pm); 59 | unsigned int update (const complex * s, unsigned int s_len); 60 | int next_norm_error (float *error); 61 | complex *dump_x (unsigned int *); 62 | complex *dump_y (unsigned int *); 63 | unsigned int filter_delay () 64 | { 65 | return m_filter_delay; 66 | }; 67 | unsigned int get_delay (); 68 | unsigned int filter_len (); 69 | unsigned int x_buf_len (); 70 | unsigned int y_buf_len (); 71 | unsigned int x_purge (unsigned int); 72 | 73 | private: 74 | #define GSM_RATE (1625000.0 / 6.0) 75 | #define FFT_SIZE 1024 76 | 77 | unsigned int m_w_len, 78 | m_D, m_check_G, m_filter_delay, m_lpf_len, m_fcch_burst_len; 79 | float m_sample_rate, m_p, m_G, m_e; 80 | complex *m_w; 81 | circular_buffer *m_x_cb, *m_y_cb, *m_e_cb; 82 | 83 | fftw_complex *m_in, *m_out; 84 | fftw_plan m_plan; 85 | }; 86 | -------------------------------------------------------------------------------- /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 77 | usage (char *prog) 78 | { 79 | 80 | printf ("kalibrate v%s-hackrf, Copyright (c) 2010, Joshua Lackey\n", 81 | kal_version_string); 82 | printf 83 | ("modified for use with hackrf devices, Copyright (c) 2014, scateu@gmail.com"); 84 | printf ("\nUsage:\n"); 85 | printf ("\tGSM Base Station Scan:\n"); 86 | printf ("\t\t%s <-s band indicator> [options]\n", basename (prog)); 87 | printf ("\n"); 88 | printf ("\tClock Offset Calculation:\n"); 89 | printf ("\t\t%s <-f frequency | -c channel> [options]\n", basename (prog)); 90 | printf ("\n"); 91 | printf ("Where options are:\n"); 92 | printf ("\t-s\tband to scan (GSM850, GSM-R, GSM900, EGSM, DCS, PCS)\n"); 93 | printf ("\t-f\tfrequency of nearby GSM base station\n"); 94 | printf ("\t-c\tchannel of nearby GSM base station\n"); 95 | printf ("\t-b\tband indicator (GSM850, GSM-R, GSM900, EGSM, DCS, PCS)\n"); 96 | printf ("\t-a\trf amplifier enable\n"); 97 | printf ("\t-g\tvga (bb) gain in dB, 0-40dB, 8dB step\n"); 98 | printf ("\t-l\tlna (if) gain in dB, 0-62dB, 2dB step\n"); 99 | printf ("\t-d\trtl-sdr device index\n"); // TODO: fuck it off. 100 | printf ("\t-e\tinitial frequency error in ppm\n"); 101 | printf ("\t-E\tmanual frequency offset in hz\n"); 102 | printf ("\t-v\tverbose\n"); 103 | printf ("\t-D\tenable debug messages\n"); 104 | printf ("\t-h\thelp\n"); 105 | exit (-1); 106 | } 107 | 108 | 109 | int 110 | main (int argc, char **argv) 111 | { 112 | 113 | char *endptr; 114 | int c, bi = BI_NOT_DEFINED, chan = -1, bts_scan = 0; 115 | int ppm_error = 0, hz_adjust = 0; 116 | unsigned int subdev = 0, decimation = 29; 117 | long int fpga_master_clock_freq = 8000000; // lowest rate supported 118 | int amp_gain = 0, lna_gain = 0, vga_gain = 0; 119 | double freq = -1.0, fd; 120 | usrp_source *u; 121 | 122 | while ((c = getopt (argc, argv, "f:c:s:b:R:ag:l:e:E:d:vDh?")) != EOF) 123 | { 124 | switch (c) 125 | { 126 | case 'f': 127 | freq = strtod (optarg, 0); 128 | break; 129 | 130 | case 'c': 131 | chan = strtoul (optarg, 0, 0); 132 | break; 133 | 134 | case 's': 135 | if ((bi = str_to_bi (optarg)) == -1) 136 | { 137 | fprintf (stderr, "error: bad band " 138 | "indicator: ``%s''\n", optarg); 139 | usage (argv[0]); 140 | } 141 | bts_scan = 1; 142 | break; 143 | 144 | case 'b': 145 | if ((bi = str_to_bi (optarg)) == -1) 146 | { 147 | fprintf (stderr, "error: bad band " 148 | "indicator: ``%s''\n", optarg); 149 | usage (argv[0]); 150 | } 151 | break; 152 | 153 | case 'R': 154 | errno = 0; 155 | subdev = strtoul (optarg, &endptr, 0); 156 | if ((!errno) && (endptr != optarg)) 157 | break; 158 | if (tolower (*optarg) == 'a') 159 | { 160 | subdev = 0; 161 | } 162 | else if (tolower (*optarg) == 'b') 163 | { 164 | subdev = 1; 165 | } 166 | else 167 | { 168 | fprintf (stderr, "error: bad side: " "``%s''\n", optarg); 169 | usage (argv[0]); 170 | } 171 | break; 172 | 173 | case 'a': 174 | amp_gain = 1; 175 | break; 176 | 177 | case 'g': 178 | vga_gain = atoi (optarg); 179 | break; 180 | 181 | case 'l': 182 | lna_gain = atoi (optarg); 183 | break; 184 | 185 | case 'F': 186 | fpga_master_clock_freq = strtol (optarg, 0, 0); 187 | if (!fpga_master_clock_freq) 188 | fpga_master_clock_freq = (long int) strtod (optarg, 0); 189 | 190 | // was answer in MHz? 191 | if (fpga_master_clock_freq < 1000) 192 | { 193 | fpga_master_clock_freq *= 1000000; 194 | } 195 | break; 196 | 197 | case 'e': 198 | ppm_error = strtol (optarg, 0, 0); 199 | break; 200 | 201 | case 'E': 202 | hz_adjust = strtol(optarg, 0, 0); 203 | break; 204 | 205 | case 'd': 206 | subdev = strtol (optarg, 0, 0); 207 | break; 208 | 209 | case 'v': 210 | g_verbosity++; 211 | break; 212 | 213 | case 'D': 214 | g_debug = 1; 215 | break; 216 | 217 | case 'h': 218 | case '?': 219 | default: 220 | usage (argv[0]); 221 | break; 222 | } 223 | 224 | } 225 | 226 | // sanity check frequency / channel 227 | if (bts_scan) 228 | { 229 | if (bi == BI_NOT_DEFINED) 230 | { 231 | fprintf (stderr, "error: scaning requires band\n"); 232 | usage (argv[0]); 233 | } 234 | } 235 | else 236 | { 237 | if (freq < 0.0) 238 | { 239 | if (chan < 0) 240 | { 241 | fprintf (stderr, "error: must enter channel or " "frequency\n"); 242 | usage (argv[0]); 243 | } 244 | if ((freq = arfcn_to_freq (chan, &bi)) < 869e6) 245 | usage (argv[0]); 246 | } 247 | if ((freq < 869e6) || (2e9 < freq)) 248 | { 249 | fprintf (stderr, "error: bad frequency: %lf\n", freq); 250 | usage (argv[0]); 251 | } 252 | chan = freq_to_arfcn (freq, &bi); 253 | } 254 | 255 | // calculate decimation -- get as close to GSM rate as we can 256 | fd = (double) fpga_master_clock_freq / GSM_RATE; 257 | decimation = (unsigned int) fd; 258 | if (g_debug) 259 | { 260 | #ifdef D_HOST_OSX 261 | printf ("debug: Mac OS X version\n"); 262 | #endif 263 | printf ("debug: FPGA Master Clock Freq:\t%li\n", 264 | fpga_master_clock_freq); 265 | printf ("debug: decimation :\t%u\n", decimation); 266 | printf ("debug: RX Subdev Spec :\t%s\n", subdev ? "B" : "A"); 267 | printf ("debug: RF Amp :\t%s\n", 268 | amp_gain ? "ON" : "OFF"); 269 | printf ("debug: LNA (IF) Gain :\t%d\n", lna_gain); 270 | printf ("debug: VGA (BB) Gain :\t%d\n", vga_gain); 271 | } 272 | 273 | u = new usrp_source (decimation, fpga_master_clock_freq); 274 | if (!u) 275 | { 276 | fprintf (stderr, "error: usrp_source\n"); 277 | return -1; 278 | } 279 | if (u->open (subdev) == -1) 280 | { 281 | fprintf (stderr, "error: usrp_source::open\n"); 282 | return -1; 283 | } 284 | if (!u->set_gain (amp_gain, lna_gain, vga_gain)) 285 | { 286 | fprintf (stderr, "error: usrp_source::set_gain\n"); 287 | return -1; 288 | } 289 | 290 | if (ppm_error != 0) 291 | { 292 | if (u->set_freq_correction (ppm_error) < 0) 293 | { 294 | fprintf (stderr, "error: usrp_source::set_freq_correction\n"); 295 | return -1; 296 | } 297 | } 298 | 299 | if (!bts_scan) 300 | { 301 | if (!u->tune (freq+hz_adjust)) 302 | { 303 | fprintf (stderr, "error: usrp_source::tune\n"); 304 | return -1; 305 | } 306 | 307 | fprintf (stderr, "%s: Calculating clock frequency offset.\n", 308 | basename (argv[0])); 309 | fprintf (stderr, "Using %s channel %d (%.1fMHz)\n", 310 | bi_to_str (bi), chan, freq / 1e6); 311 | 312 | return offset_detect (u, hz_adjust); 313 | } 314 | 315 | fprintf (stderr, "%s: Scanning for %s base stations.\n", 316 | basename (argv[0]), bi_to_str (bi)); 317 | 318 | return c0_detect (u, bi, chan); 319 | } 320 | -------------------------------------------------------------------------------- /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 34 | round (double x) 35 | { 36 | return floor (x + 0.5); 37 | } 38 | #endif 39 | 40 | static const unsigned int AVG_COUNT = 100; 41 | static const unsigned int AVG_THRESHOLD = (AVG_COUNT / 10); 42 | static const float OFFSET_MAX = 40e3; 43 | 44 | extern int g_verbosity; 45 | 46 | 47 | int 48 | offset_detect (usrp_source * u, int hz_adjust) 49 | { 50 | 51 | #define GSM_RATE (1625000.0 / 6.0) 52 | 53 | unsigned int new_overruns = 0, overruns = 0; 54 | int notfound = 0; 55 | unsigned int s_len, b_len, consumed, count; 56 | float offset = 0.0, min = 0.0, max = 0.0, avg_offset = 0.0, 57 | stddev = 0.0, sps, offsets[AVG_COUNT]; 58 | double total_ppm; 59 | complex *cbuf; 60 | fcch_detector *l; 61 | circular_buffer *cb; 62 | 63 | l = new fcch_detector (u->sample_rate ()); 64 | 65 | /* 66 | * We deliberately grab 12 frames and 1 burst. We are guaranteed to 67 | * find at least one FCCH burst in this much data. 68 | */ 69 | sps = u->sample_rate () / GSM_RATE; 70 | s_len = (unsigned int) ceil ((12 * 8 * 156.25 + 156.25) * sps); 71 | cb = u->get_buffer (); 72 | 73 | u->start (); 74 | u->flush (); 75 | count = 0; 76 | while (count < AVG_COUNT) 77 | { 78 | 79 | // ensure at least s_len contiguous samples are read from usrp 80 | do 81 | { 82 | if (u->fill (s_len, &new_overruns)) 83 | { 84 | return -1; 85 | } 86 | if (new_overruns) 87 | { 88 | overruns += new_overruns; 89 | u->flush (); 90 | } 91 | } 92 | while (new_overruns); 93 | 94 | // get a pointer to the next samples 95 | cbuf = (complex *) cb->peek (&b_len); 96 | 97 | // search the buffer for a pure tone 98 | if (l->scan (cbuf, b_len, &offset, &consumed)) 99 | { 100 | 101 | // FCH is a sine wave at GSM_RATE / 4 102 | offset = offset - GSM_RATE / 4; 103 | 104 | // sanity check offset 105 | if (fabs (offset) < OFFSET_MAX) 106 | { 107 | 108 | offsets[count] = offset; 109 | count += 1; 110 | 111 | if (g_verbosity > 0) 112 | { 113 | fprintf (stderr, "\toffset %3u/%3u: %.2f\n", count, 114 | AVG_COUNT, offset); 115 | } 116 | } 117 | } 118 | else 119 | { 120 | ++notfound; 121 | } 122 | 123 | // consume used samples 124 | cb->purge (consumed); 125 | } 126 | 127 | u->stop (); 128 | delete l; 129 | 130 | // construct stats 131 | sort (offsets, AVG_COUNT); 132 | avg_offset = 133 | avg (offsets + AVG_THRESHOLD, AVG_COUNT - 2 * AVG_THRESHOLD, &stddev); 134 | min = offsets[AVG_THRESHOLD]; 135 | max = offsets[AVG_COUNT - AVG_THRESHOLD - 1]; 136 | 137 | printf ("average\t\t[min, max]\t(range, stddev)\n"); 138 | display_freq (avg_offset); 139 | printf ("\t\t[%d, %d]\t(%d, %f)\n", (int) round (min), (int) round (max), 140 | (int) round (max - min), stddev); 141 | printf ("overruns: %u\n", overruns); 142 | printf ("not found: %u\n", notfound); 143 | 144 | total_ppm = u->m_freq_corr - ((avg_offset + hz_adjust) / u->m_center_freq) * 1000000; 145 | 146 | printf ("average absolute error: %.3f ppm\n", total_ppm); 147 | return 0; 148 | } 149 | -------------------------------------------------------------------------------- /src/offset.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Joshua Lackey 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | int offset_detect (usrp_source * u, int hz_adjust); 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 31 | std::complex < float > 32 | complex; 33 | -------------------------------------------------------------------------------- /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 47 | round (double x) 48 | { 49 | return floor (x + 0.5); 50 | } 51 | #endif 52 | 53 | usrp_source::usrp_source (float sample_rate, long int fpga_master_clock_freq) 54 | { 55 | 56 | m_fpga_master_clock_freq = fpga_master_clock_freq; 57 | m_desired_sample_rate = sample_rate; 58 | m_center_freq = 0.0; 59 | m_sample_rate = 0.0; 60 | m_decimation = 0; 61 | m_cb = new circular_buffer (CB_LEN, sizeof (complex), 0); 62 | 63 | pthread_mutex_init (&m_u_mutex, 0); 64 | } 65 | 66 | 67 | usrp_source::usrp_source (unsigned int decimation, 68 | long int fpga_master_clock_freq) 69 | { 70 | 71 | m_fpga_master_clock_freq = fpga_master_clock_freq; 72 | m_center_freq = 0.0; 73 | m_sample_rate = 0.0; 74 | m_cb = new circular_buffer (CB_LEN, sizeof (complex), 0); 75 | 76 | pthread_mutex_init (&m_u_mutex, 0); 77 | 78 | m_decimation = decimation & ~1; 79 | if (m_decimation < 4) 80 | m_decimation = 4; 81 | if (m_decimation > 256) 82 | m_decimation = 256; 83 | } 84 | 85 | 86 | usrp_source::~usrp_source () 87 | { 88 | 89 | stop (); 90 | delete m_cb; 91 | hackrf_close (dev); 92 | pthread_mutex_destroy (&m_u_mutex); 93 | } 94 | 95 | 96 | void 97 | usrp_source::stop () 98 | { 99 | 100 | pthread_mutex_lock (&m_u_mutex); 101 | 102 | int result; 103 | result = hackrf_stop_rx (dev); 104 | if (result != HACKRF_SUCCESS) 105 | { 106 | printf ("hackrf_stop_rx() failed: %s (%d)\n", 107 | hackrf_error_name ((hackrf_error) result), result); 108 | pthread_mutex_unlock (&m_u_mutex); 109 | exit (1); 110 | } 111 | pthread_mutex_unlock (&m_u_mutex); 112 | } 113 | 114 | 115 | void 116 | usrp_source::start () 117 | { 118 | 119 | pthread_mutex_lock (&m_u_mutex); 120 | 121 | int result; 122 | result = hackrf_start_rx (dev, hackrf_rx_callback, this); 123 | if (result != HACKRF_SUCCESS) 124 | { 125 | printf ("hackrf_start_rx() failed: %s (%d)\n", 126 | hackrf_error_name ((hackrf_error) result), result); 127 | pthread_mutex_unlock (&m_u_mutex); 128 | exit (1); 129 | } 130 | pthread_mutex_unlock (&m_u_mutex); 131 | } 132 | 133 | 134 | void 135 | usrp_source::calculate_decimation () 136 | { 137 | 138 | float decimation_f; 139 | 140 | // decimation_f = (float)m_u_rx->fpga_master_clock_freq() / m_desired_sample_rate; 141 | m_decimation = (unsigned int) round (decimation_f) & ~1; 142 | 143 | if (m_decimation < 4) 144 | m_decimation = 4; 145 | if (m_decimation > 256) 146 | m_decimation = 256; 147 | } 148 | 149 | 150 | float 151 | usrp_source::sample_rate () 152 | { 153 | 154 | return m_sample_rate; 155 | 156 | } 157 | 158 | 159 | int 160 | usrp_source::tune (double freq) 161 | { 162 | 163 | int r = 0; 164 | 165 | pthread_mutex_lock (&m_u_mutex); 166 | if (freq != m_center_freq) 167 | { 168 | 169 | r = hackrf_set_freq (dev, (uint64_t) freq); 170 | if (g_verbosity) 171 | printf ("hackrf_set_freq: %d\n", int (freq)); 172 | 173 | if (r < 0) 174 | fprintf (stderr, "Tuning failed!\n"); 175 | else 176 | m_center_freq = freq; 177 | } 178 | 179 | pthread_mutex_unlock (&m_u_mutex); 180 | 181 | return 1; //(r < 0) ? 0 : 1; 182 | } 183 | 184 | int 185 | usrp_source::set_freq_correction (int ppm) 186 | { 187 | m_freq_corr = ppm; 188 | //return rtlsdr_set_freq_correction(dev, ppm); 189 | return 0; // TODO: add support for ppm correction 190 | } 191 | 192 | bool 193 | usrp_source::set_antenna (int antenna) 194 | { 195 | 196 | return 0; 197 | } 198 | 199 | bool 200 | usrp_source::set_gain (int amp_gain, int lna_gain, int vga_gain) 201 | { 202 | int r = 0; 203 | 204 | lna_gain = (((lna_gain + 7) / 8) * 8); 205 | vga_gain = (((vga_gain + 1) / 2) * 2); 206 | 207 | if (lna_gain > 40) 208 | lna_gain = 40; 209 | if (vga_gain > 62) 210 | vga_gain = 62; 211 | if (g_verbosity) 212 | printf ("hackrf: set gain %d/%d/%d\n", amp_gain, vga_gain, lna_gain); 213 | 214 | if (amp_gain) 215 | r = hackrf_set_amp_enable (dev, amp_gain); 216 | if (vga_gain) 217 | r |= hackrf_set_vga_gain (dev, vga_gain); 218 | if (lna_gain) 219 | r |= hackrf_set_lna_gain (dev, lna_gain); 220 | return (r < 0) ? 0 : 1; 221 | } 222 | 223 | 224 | /* 225 | * open() should be called before multiple threads access usrp_source. 226 | */ 227 | int 228 | usrp_source::open (unsigned int subdev) 229 | { 230 | int i, r, device_count, count; 231 | uint32_t dev_index = subdev; 232 | uint32_t samp_rate; 233 | 234 | samp_rate = m_fpga_master_clock_freq; // from constructor 235 | m_sample_rate = 1000000; 236 | 237 | 238 | if (g_verbosity) 239 | printf ("hackrf_init()\n"); 240 | r = hackrf_init (); 241 | if (r != HACKRF_SUCCESS) 242 | { 243 | printf ("hackrf_init() failed."); 244 | return EXIT_FAILURE; 245 | } 246 | 247 | if (g_verbosity) 248 | printf ("hackrf_open()\n"); 249 | r = hackrf_open (&dev); 250 | if (r != HACKRF_SUCCESS) 251 | { 252 | fprintf (stderr, "Failed to open hackrf device.\n"); 253 | exit (1); 254 | } 255 | 256 | /* Set the sample rate */ 257 | r = hackrf_set_sample_rate (dev, samp_rate); 258 | if (g_verbosity) 259 | printf ("hackrf_set_sample_rate(%u)\n", samp_rate); 260 | if (r != HACKRF_SUCCESS) 261 | fprintf (stderr, "WARNING: Failed to set sample rate.\n"); 262 | 263 | r = hackrf_set_baseband_filter_bandwidth (dev, 2500000); 264 | if (r != HACKRF_SUCCESS) 265 | { 266 | printf ("hackrf_baseband_filter_bandwidth_set() failed: \n"); 267 | return EXIT_FAILURE; 268 | } 269 | return 0; 270 | } 271 | 272 | #define USB_PACKET_SIZE (2 * 16384) 273 | #define FLUSH_SIZE 512 274 | 275 | int 276 | hackrf_rx_callback (hackrf_transfer * transfer) 277 | { 278 | //printf("hackrf_rx_callback()\n"); 279 | usrp_source *u; 280 | u = (usrp_source *) (transfer->rx_ctx); 281 | 282 | size_t bytes_to_write; 283 | size_t hackrf_rx_count_new = u->hackrf_rx_count + transfer->valid_length; 284 | 285 | int count_left = USB_PACKET_SIZE - hackrf_rx_count_new; 286 | if (count_left <= 0) 287 | { 288 | bytes_to_write = transfer->valid_length + count_left; 289 | } 290 | else 291 | { 292 | bytes_to_write = transfer->valid_length; 293 | } 294 | 295 | // cout << transfer->valid_length << " " << hackrf_rx_count << " " << bytes_to_write << "\n"; 296 | if (bytes_to_write != 0) 297 | { 298 | memcpy (u->ubuf + u->hackrf_rx_count, transfer->buffer, bytes_to_write); 299 | // for (size_t i=0; ibuffer[i]; 301 | // } 302 | u->hackrf_rx_count = u->hackrf_rx_count + bytes_to_write; 303 | } 304 | // cout << transfer->valid_length << " " << hackrf_rx_count << " " << bytes_to_write << "\n"; 305 | 306 | return (0); 307 | } 308 | 309 | 310 | int 311 | usrp_source::fill (unsigned int num_samples, unsigned int *overrun_i) 312 | { 313 | 314 | unsigned int i, j, space, overruns = 0; 315 | complex *c; 316 | int n_read; 317 | int result; 318 | 319 | //printf("start fill..\n"); 320 | 321 | while ((m_cb->data_available () < num_samples) 322 | && (m_cb->space_available () > 0)) 323 | { 324 | 325 | // read one usb packet from hackrf 326 | pthread_mutex_lock (&m_u_mutex); 327 | 328 | 329 | // fill ubuf with sizeof(ubuf) sample points. n_read to indicate samples actually wrote. 330 | hackrf_rx_count = 0; // clear counter 331 | while (hackrf_is_streaming (dev) != HACKRF_TRUE) 332 | { 333 | printf ("waiting for streaming...(%d)\n", 334 | hackrf_is_streaming (dev)); 335 | } 336 | 337 | while (hackrf_is_streaming (dev) == HACKRF_TRUE) 338 | { 339 | //printf("%d\n",hackrf_rx_count ); 340 | if (hackrf_rx_count == USB_PACKET_SIZE) 341 | { 342 | break; 343 | } 344 | } 345 | 346 | n_read = hackrf_rx_count; 347 | 348 | pthread_mutex_unlock (&m_u_mutex); 349 | 350 | // write complex input to complex output 351 | c = (complex *) m_cb->poke (&space); 352 | 353 | // set space to number of complex items to copy 354 | space = n_read / 2; 355 | 356 | // write data 357 | for (i = 0, j = 0; i < space; i += 1, j += 2) 358 | //c[i] = complex((ubuf[j] - 127) * 256, (ubuf[j + 1] - 127) * 256); 359 | c[i] = complex (ubuf[j] * 256, ubuf[j + 1] * 256); 360 | 361 | // update cb 362 | m_cb->wrote (i); 363 | } 364 | 365 | // if the cb is full, we left behind data from the usb packet 366 | if (m_cb->space_available () == 0) 367 | { 368 | fprintf (stderr, "warning: local overrun\n"); 369 | overruns++; 370 | } 371 | 372 | if (overrun_i) 373 | *overrun_i = overruns; 374 | 375 | return 0; 376 | } 377 | 378 | 379 | 380 | /* 381 | * Don't hold a lock on this and use the usrp at the same time. 382 | */ 383 | circular_buffer * 384 | usrp_source::get_buffer () 385 | { 386 | 387 | return m_cb; 388 | } 389 | 390 | 391 | int 392 | usrp_source::flush (unsigned int flush_count) 393 | { 394 | 395 | m_cb->flush (); 396 | //fill(flush_count * FLUSH_SIZE, 0); 397 | m_cb->flush (); 398 | 399 | return 0; 400 | } 401 | -------------------------------------------------------------------------------- /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 "libhackrf/hackrf.h" 29 | 30 | #include "usrp_complex.h" 31 | #include "circular_buffer.h" 32 | 33 | int hackrf_rx_callback (hackrf_transfer * transfer); 34 | 35 | class usrp_source 36 | { 37 | public: 38 | usrp_source (float sample_rate, long int fpga_master_clock_freq = 52000000); 39 | usrp_source (unsigned int decimation, long int fpga_master_clock_freq = 40 | 52000000); 41 | ~usrp_source (); 42 | 43 | int open (unsigned int subdev); 44 | int fill (unsigned int num_samples, unsigned int *overrun); 45 | int tune (double freq); 46 | int set_freq_correction (int ppm); 47 | bool set_antenna (int antenna); 48 | bool set_gain (int amp_gain, int lna_gain, int vga_gain); 49 | void start (); 50 | void stop (); 51 | int flush (unsigned int flush_count = FLUSH_COUNT); 52 | circular_buffer *get_buffer (); 53 | 54 | float sample_rate (); 55 | 56 | static const unsigned int side_A = 0; 57 | static const unsigned int side_B = 1; 58 | 59 | double m_center_freq; 60 | int m_freq_corr; 61 | 62 | int hackrf_rx_count; // used for and hackrf rx callback 63 | #define USB_PACKET_SIZE (2 * 16384) 64 | int8_t ubuf[USB_PACKET_SIZE]; // used for hackrf rx callback 65 | 66 | 67 | private: 68 | void calculate_decimation (); 69 | 70 | hackrf_device *dev; 71 | 72 | float m_sample_rate; 73 | float m_desired_sample_rate; 74 | unsigned int m_decimation; 75 | 76 | long int m_fpga_master_clock_freq; 77 | 78 | circular_buffer *m_cb; 79 | 80 | /* 81 | * This mutex protects access to the USRP and daughterboards but not 82 | * necessarily to any fields in this class. 83 | */ 84 | pthread_mutex_t m_u_mutex; 85 | 86 | static const unsigned int FLUSH_COUNT = 10; 87 | static const unsigned int CB_LEN = (16 * 16384); 88 | static const int NCHAN = 1; 89 | static const int INITIAL_MUX = -1; 90 | static const int FUSB_BLOCK_SIZE = 1024; 91 | static const int FUSB_NBLOCKS = 16 * 8; 92 | static const char *FPGA_FILENAME () 93 | { 94 | return "std_2rxhb_2tx.rbf"; 95 | } 96 | }; 97 | -------------------------------------------------------------------------------- /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 35 | display_freq (float f) 36 | { 37 | 38 | if (f >= 0) 39 | { 40 | printf ("+ "); 41 | } 42 | else 43 | { 44 | printf ("- "); 45 | f = -f; 46 | } 47 | if (fabs (f) >= 1e9) 48 | { 49 | printf ("%.3fGHz", f / 1e9); 50 | return; 51 | } 52 | if (fabs (f) >= 1e6) 53 | { 54 | printf ("%.1fMHz", f / 1e6); 55 | return; 56 | } 57 | if (fabs (f) >= 1e3) 58 | { 59 | printf ("%.3fkHz", f / 1e3); 60 | return; 61 | } 62 | if (fabs (f) >= 1e2) 63 | { 64 | printf ("%.0fHz", f); 65 | return; 66 | } 67 | if (fabs (f) >= 1e1) 68 | { 69 | printf (" %.0fHz", f); 70 | return; 71 | } 72 | printf (" %.0fHz", f); 73 | } 74 | 75 | 76 | void 77 | sort (float *b, unsigned int len) 78 | { 79 | 80 | for (unsigned int i = 0; i < len; i++) 81 | { 82 | for (unsigned int j = i + 1; j < len; j++) 83 | { 84 | if (b[j] < b[i]) 85 | { 86 | float t = b[i]; 87 | b[i] = b[j]; 88 | b[j] = t; 89 | } 90 | } 91 | } 92 | } 93 | 94 | 95 | double 96 | avg (float *b, unsigned int len, float *stddev) 97 | { 98 | 99 | unsigned int i; 100 | double t = 0.0, a = 0.0, s = 0.0; 101 | 102 | for (i = 0; i < len; i++) 103 | t += b[i]; 104 | a = t / len; 105 | if (stddev) 106 | { 107 | for (i = 0; i < len; i++) 108 | s += (b[i] - a) * (b[i] - a); 109 | s /= len; 110 | s = sqrt (s); 111 | *stddev = s; 112 | } 113 | 114 | return a; 115 | } 116 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------