├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── beamformclient ├── heatmap2d.cpp └── heatmap2d2.cpp ├── deps ├── electronics ├── README ├── adapter.zip ├── coupler.zip ├── coupler2.jpg ├── example_clock.png └── example_noisegen_kicad502.zip ├── examplecfg ├── URA21.cfg ├── ac.cfg └── four.cfg ├── include ├── ccoherent.h ├── cconfigfile.h ├── ccontrol.h ├── cdsp.h ├── common.h ├── console.h ├── cpacketize.h ├── cpacketizer.h ├── crefnoise.h └── csdrdevice.h ├── install_on_rpi ├── matlabclient ├── CZMQSDR.m ├── compiling.txt ├── functions │ ├── D2Dtoepos.m │ ├── DA2D.m │ ├── darray.m │ ├── pmusic.m │ └── pmusicplot.m ├── matsave.cc ├── measurement_script.m ├── noiseswitch.m ├── notes.m ├── phasecorrectionplot.m ├── seqnum_and_correlation.m ├── testchannels.m ├── testclient.m ├── zmqsdr.c ├── zmqsdr.cc └── zmqsdr.mexa64 ├── refnoisefirmware ├── CMakeLists.txt ├── Makefile ├── bootloader.c ├── enter_dfu.sh ├── fw.c ├── resetusb.sh ├── stm32-blueboot.ld ├── stm32-bluebootld.ld ├── stm32-bluepill.ld ├── stm32-bluepilldfu.ld └── usbdfu.c └── src ├── CMakeLists.txt ├── ccoherent.cc ├── ccontrol.cc ├── cdsp.cc ├── common.cc ├── console.cc ├── cpacketizer.cc ├── crtlsdr.cc ├── csdrdevice.cc └── main.cc /.gitignore: -------------------------------------------------------------------------------- 1 | #ignore build 2 | build/ 3 | *.mat 4 | *.out 5 | *.zip 6 | *.m~ 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "refnoisefirmware/libopencm3"] 2 | path = refnoisefirmware/libopencm3 3 | url = https://github.com/libopencm3/libopencm3 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | 3 | 4 | option(RASPBERRYPI "RPI" OFF) 5 | if (RASPBERRYPI) 6 | add_definitions(-DRASPBERRYPI) 7 | endif(RASPBERRYPI) 8 | 9 | #set(CMAKE_CXX_STANDARD 17) 10 | set(CMAKE_CXX_FLAGS "-O3") #-Wl,-Bdynamic 11 | 12 | include_directories(${PROJECT_SOURCE_DIR}/include) 13 | add_subdirectory(src) 14 | 15 | 16 | 17 | #SET_TARGET_PROPERTIES(coherentrtlsdr PROPERTIES LINK_FLAGS -lrtlsdr) 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # things have moved forward, KrakenSDR is now fully controllable by the new (unpublished) ThorSDR code. 2 | Licencing may change to the MIT license, but this is still under consideration. SoapySDR support is 3 | also under consideration, 4 | 5 | 6 | # project development starting again, already in june, 7 | Changes are being made and the ThorSDR code seems quite stable at the moment. 8 | Also, we are considering to add soapysdr support in the future. Note that 9 | this driver will also support other reference noise switching calibration systems. e.g. KrakenSDR 10 | 11 | 12 | ***10.8.2022 PROJECT DEVELOPEMENT HALTED, WE'LL NEED TO FIND SOMEONE TO CONTINUE THE WORK WITH OUR RECEIVER PROTO. 13 | 14 | Perhaps this should be someday converted into a GNU Radio OOT module as a signal source, but I do not have the time 15 | until I finish my PhD. 16 | *** 17 | 18 | # coherent-rtlsdr, reworked synchronization algorithm 19 | 20 | Mostly the same requirements as in my previous coherentsdr proof-of-concept. One added dependency, GNU Readline, for the shell. Some unworking features: adding and deleting receivers during runtime, application does not always exit cleanly. 21 | 22 | Matlab client included. A system object interfacing to a .c MEX implementation (matlab c++ interfacing seems to be too slow). This is not built automatically by cmake, instead it has to be compiled manually (instructions in the folder). 23 | 24 | Required for compiling: 25 | 26 | :zmq: - the zero message queue: 27 | sudo apt-get install libzmq3-dev 28 | 29 | :fftw3f: - fastest fourier transform in the west: 30 | sudo apt-get install fftw3-dev 31 | 32 | :volk: - vector optimized library of kernels: 33 | sudo apt-get install volk 34 | 35 | :librtlsdr: -Tejeez/Keenerds experimental librtlsdr fork: 36 | git://github.com/tejeez/rtl-sdr 37 | 38 | +GNU Readline 39 | 40 | Requires common 28.8 MHz clock & reference signal (noise) for synchronization. Some examples in the electronics folder, but this is still missing the coupler module PCB files. 41 | 42 | Currently may return a receive matrix where data on some channels are from a previous sample buffer. This seems to happen under heavy CPU load, at least on limited platforms (testing on RockPI 4 with 21 signal channels). This can be noticed by observing discontinuous channel sequence number (readcnt). Occasionally, all channels skip a buffer in unison, i.e. 8192 sample gap in reception. Perhaps I may need to add some buffering to the packetizer singleton. Under construction. 43 | 44 | UPDATE 2.2.2021: We have concentrated on experiments with the receiver and presented a paper at EUSIPCO2020. "Phase-coherent multichannel SDR - Sparse arraybeamforming" can be found at: https://aaltodoc.aalto.fi/handle/123456789/102361 45 | 46 | Please cite this as: Laakso , M , Rajamäki , R , Wichman , R & Koivunen , V 2020 , Phase-coherent multichannel SDR - Sparse array beamforming . in 28th European Signal Processing Conference, EUSIPCO 2020 - Proceedings . , 9287664 , European Signal Processing Conference , EURASIP , pp. 1856-1860 , European Signal Processing Conference , Amsterdam , Netherlands , 24/08/2020 . https://doi.org/10.23919/Eusipco47968.2020.9287664 47 | 48 | The next paper, in which we utilize deep neural networks on data captured with coherent-rtlsdr to do near-field localization (with surprisingly accurate results), was presented at VTC2021 Helsinki. The article is titled "Near-field localization using machine learning: An empirical study" and it can be accessed at: https://aaltodoc.aalto.fi/handle/123456789/102361 49 | 50 | Please cite as: Laakso , M & Wichman , R 2021 , Near-field localization using machine learning: An empirical study . in 2021 IEEE 93rd Vehicular Technology Conference, VTC 2021-Spring - Proceedings . , 9449002 , IEEE Vehicular Technology Conference , vol. 2021-April 51 | 52 | Added the reference noise controller firmware, shamelessly edited from libopencm3 examples. This uses the 2$ STM32F103C8T6 (a.k.a bluepill) board to control 2 GPIOS for switching operating voltage for the noise amplifiers & case fan (yes, last summer I had problems with the amplifiers overheating while taking measurements during a sunny day). DFU upgradeable with dfu-util once flashed (hack, but works). 53 | 54 | -------------------------------------------------------------------------------- /beamformclient/heatmap2d.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | coherent-rtlsdr 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | coherent-rtlsdr is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with coherent-rtlsdr. If not, see . 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "gnuplot-iostream.h" 23 | #include 24 | #include 25 | 26 | #include "Eigen/Dense" 27 | #include "Eigen/KroneckerProduct" 28 | #include 29 | #include 30 | //#include "mat.h" 31 | 32 | using namespace std; 33 | using namespace Eigen; 34 | 35 | 36 | #define NSENSOR 18 37 | 38 | typedef Matrix,Dynamic,Dynamic> MatrixType; 39 | typedef Map MapType; 40 | typedef Map MapTypeConst; 41 | typedef Matrix,NSENSOR,1> ColVecType; 42 | 43 | typedef Matrix,1,NSENSOR> RowVecType; 44 | 45 | typedef Matrix,Dynamic,1> ColVecTypeD; //try the dynamic. 46 | 47 | typedef std::pair Point; 48 | 49 | typedef std::vector Point2d; 50 | 51 | const complex j(0, 1); 52 | const float pi = std::acos(-1); 53 | 54 | //g++ mattoeigen.cpp -lmat -lmx -lutil -lboost_iostreams -lboost_system -lboost_filesystem -I ~/source/eigen-3.3.7/Eigen/ -I /usr/local/MATLAB/R2019b/extern/include/ -L /usr/local/MATLAB/R2019b/bin/glnxa64/ -I ~/source/gnuplot-iostream/ -Wl,-rpath=/usr/local/MATLAB/R2019b/bin/glnxa64/ 55 | 56 | // with the unsupported Kronecker product patched in do: 57 | 58 | //g++ mattoeigen.cpp -lzmq -lutil -lboost_iostreams -lboost_system -lboost_filesystem -I ~/source/eigen-3.3.7/ -I ~/source/tmp/eigen/unsupported/ -I ~/source/gnuplot-iostream/ 59 | 60 | //Find noise subspace from Covariance matrix Rxx, assuming K signals. Use SVD. 61 | Matrix,Dynamic,Dynamic> noisesubspace(Matrix,Dynamic,Dynamic> Rxx, int K){ 62 | int M = Rxx.cols(); 63 | BDCSVD,Dynamic,Dynamic>> B=Rxx.bdcSvd(ComputeFullU|ComputeFullV); 64 | Matrix,Dynamic,Dynamic> U = B.matrixU(); 65 | Matrix,Dynamic,Dynamic> Un = U.rightCols(M-K); 66 | Matrix,1,Dynamic> S = B.singularValues(); 67 | //cout << "singularvalues: " << S << endl; 68 | return Un; 69 | } 70 | 71 | //Noise subspace with Eigendecomp. This cannot be used as such, because the eigenvectors&vals are unordered. 72 | MatrixType noisesubspaceeig(MatrixType Rxx, int K){ 73 | SelfAdjointEigenSolver es; 74 | es.compute(Rxx); 75 | //cout << "The eigenvalues of Rxx are: " << es.eigenvalues().transpose() << endl; 76 | //cout << "The matrix of eigenvectors, V, is:" << endl << es.eigenvectors() << endl << endl; 77 | return es.eigenvectors(); 78 | } 79 | 80 | //steering vector, for direction phi(rad) 81 | ColVecType s_vec(float phi){ 82 | ColVecType A; 83 | 84 | float d = (1.225*1.24)/3.0f; //element distance, wavelengths 85 | //d = 0.5f; //exp(2j*pi*d*cos(pi)) 86 | 87 | for (int i=0;i,Dynamic,Dynamic> Un, Matrix,Dynamic,1> a) 123 | { 124 | //complex denom = ((Un.adjoint()*a).norm()); 125 | complex denom = (Un.adjoint()*a).squaredNorm(); 126 | complex res = a.squaredNorm()/denom; 127 | return (res*conj(res)).real(); 128 | //complex denom = a.adjoint()*Un*Un.adjoint()*a; 129 | //return (1.0f/denom).real(); 130 | } 131 | 132 | /* 133 | Matrix,Dynamic,Dynamic> DA(Matrix,Dynamic,Dynamic>){ 134 | Matrix<,Dynamic,Dynamic> res; 135 | res.resize(); 136 | }*/ 137 | 138 | Matrix pmusic2dvec(Matrix,Dynamic,Dynamic> Un,float d, int Mx, int My, int Cx, int Cy){ 139 | Matrix res(Cx,Cy); 140 | for(int cx=0;cx pmusicvec(Matrix,Dynamic,Dynamic> Un, int N) //N+1 points between 0 to PI 151 | { 152 | Matrix res; 153 | res.resize(N,1); 154 | 155 | for(int n=0;n(msg.data()); 177 | uint32_t gseq = *((uint32_t *) s8bit); 178 | uint32_t cols = *(((uint32_t *) s8bit)+1); 179 | uint32_t rows = *(((uint32_t *) s8bit)+2); 180 | //cols = NSENSOR; //only the first antennas. 181 | 182 | cout << "Allocating a " << to_string(rows) << " X " << to_string(cols) << " matrix" << endl; 183 | 184 | MatrixType Xall(rows,cols); //MapType Xall(matptr,rows,cols); 185 | Xall.resize(rows,cols); 186 | 187 | Gnuplot gp; 188 | 189 | //gp << "set zrange [-1:1]\n"; 190 | gp << "set hidden3d nooffset\n"; 191 | gp << "set style fill transparent solid 0.65\n"; 192 | //gp << "set view map\n"; 193 | 194 | vector data; 195 | 196 | vector data2d; 197 | 198 | int nrep = 10000; 199 | 200 | for (int nn=0; nn(msg.data()); 203 | //convert samples to complex float: 204 | //cout << "converting data\n" << endl; 205 | if (nn%1==0){ 206 | float s = 1/127.0f; 207 | for(uint32_t c=0;c(float(s8bit[c*(rows<<1) + ri++]),float(s8bit[c*(rows<<1) + ri])); 210 | } 211 | 212 | 213 | //cout << "subtracting mean\n" << endl; 214 | MatrixType X = Xall.rightCols(cols-1); //.leftCols(NSENSOR); 215 | X.rowwise()-=X.colwise().mean(); //subtract col mean 216 | 217 | //cout << "Computing noise subspace\n" << endl; 218 | MatrixType Rxx = (1/float(rows))*(X.adjoint()*X); 219 | MatrixType Un = noisesubspace(Rxx,1); 220 | 221 | //cout << "Computing music\n" << endl; 222 | 223 | //Matrix pm = pmusicvec(Un,100); 224 | 225 | float d = (1.225*1.24)/3.0f; 226 | Matrix pm = pmusic2dvec(Un,d,6,3,100, 100); 227 | 228 | //pm.col(50) = pmusicvec(Un,100); 229 | 230 | auto g = pm.maxCoeff(); 231 | //cout << g << endl; 232 | pm *= 1/g; 233 | 234 | std::vector > > pts(100); 235 | for (int nx=0;nx<100;nx++){ 236 | pts[nx].resize(100); 237 | for (int ny=0;ny<100;ny++){ 238 | pts[nx][ny].resize(3); 239 | pts[nx][ny][0] = -90 + float(nx)*1.8f; 240 | pts[nx][ny][1] = -90 + float(ny)*1.8f; 241 | pts[nx][ny][2] = 20.0f*log10(pm(nx,ny)); //2.0f; 242 | 243 | } 244 | } 245 | gp.clear(); 246 | gp << "set yrange [-90:90]\n"; 247 | gp << "set xrange [-90:90]\n"; 248 | gp << "set cbrange [-18:0]\n"; 249 | gp << "set zrange [-18:1]\n"; 250 | //gp << "set view map\n"; 251 | gp << "splot '-' with pm3d title 'Pm'\n"; 252 | gp.send2d(pts); 253 | gp.flush(); 254 | //gp << gp.binFile2d(pts, "record") << "with lines title 'vec vec vec'"; 255 | 256 | /* 257 | for(int n=0;n<101;n++){ 258 | //data.push_back(Point(180.0f/pi*float(n)/10.0f,pm(n))); 259 | data.push_back(Point(float(n),10.0f*log10(pm(n)))); 260 | } 261 | 262 | //cout << "Dim X:" << X.rows() << "," << X.cols() << endl; 263 | gp.clear(); 264 | gp << "unset border\n"; 265 | //gp << "set yrange[0:10]\n"; 266 | gp << "set yrange[-20:1]\n"; 267 | gp << "plot '-' with lines title 'Pm'\n"; 268 | gp.send1d(data); 269 | gp.flush(); 270 | 271 | data.clear();*/ 272 | } 273 | /* 274 | //WORKING MUSIC 275 | for (int phi=0;phi<1800;phi++){ 276 | ColVecType A = s_vec(pi*float(phi)/1800.0f); 277 | //cout << A.transpose() << endl; 278 | data.push_back(Point(float(phi),10*log10(pmusic(Un,A)))); 279 | } 280 | */ 281 | } 282 | 283 | } 284 | 285 | //Map X(NULL); 286 | //new (&A) Map(get_matrix_pointer(i)); 287 | //new (&X) Map,Dynamic,Dynamic>>(outptr,dims[0],dims[1]); 288 | //&XMap >(array) 289 | -------------------------------------------------------------------------------- /beamformclient/heatmap2d2.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | coherent-rtlsdr 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | coherent-rtlsdr is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with coherent-rtlsdr. If not, see . 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "gnuplot-iostream.h" 23 | #include 24 | #include 25 | 26 | #include "Eigen/Dense" 27 | #include "Eigen/KroneckerProduct" 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "../include/cdsp.h" 34 | #include 35 | 36 | #include "mat.h" 37 | 38 | using namespace std; 39 | using namespace Eigen; 40 | 41 | #define MX 7 42 | #define MY 3 43 | #define NSENSOR MX*MY 44 | 45 | 46 | typedef Matrix,Dynamic,Dynamic> MatrixType; 47 | typedef Map MapType; 48 | typedef Map MapTypeConst; 49 | typedef Matrix,NSENSOR,1> ColVecType; 50 | 51 | typedef Matrix,1,NSENSOR> RowVecType; 52 | 53 | typedef Matrix,Dynamic,1> ColVecTypeD; //try the dynamic. 54 | 55 | typedef std::pair Point; 56 | 57 | typedef std::vector Point2d; 58 | 59 | const complex j(0, 1); 60 | const float pi = std::acos(-1); 61 | 62 | //g++ mattoeigen.cpp -lmat -lmx -lutil -lboost_iostreams -lboost_system -lboost_filesystem -I ~/source/eigen-3.3.7/Eigen/ -I /usr/local/MATLAB/R2019b/extern/include/ -L /usr/local/MATLAB/R2019b/bin/glnxa64/ -I ~/source/gnuplot-iostream/ -Wl,-rpath=/usr/local/MATLAB/R2019b/bin/glnxa64/ 63 | 64 | // with the unsupported Kronecker product patched in do: 65 | 66 | //g++ heatmap2d2.cpp -lmx -lmat -lvolk -lpthread -lzmq -lutil -lboost_iostreams -lboost_system -lboost_filesystem -I ~/source/eigen-3.3.7/ -I ~/source/tmp/eigen/unsupported/ -I ~/source/gnuplot-iostream/ -I /usr/local/MATLAB/R2019b/extern/include/ -L /usr/local/MATLAB/R2019b/bin/glnxa64/ -Wl,-rpath=/usr/local/MATLAB/R2019b/bin/glnxa64/ 67 | 68 | //Find noise subspace from Covariance matrix Rxx, assuming K signals. Use SVD. 69 | Matrix,Dynamic,Dynamic> noisesubspace(Matrix,Dynamic,Dynamic> Rxx, int K){ 70 | int M = Rxx.cols(); 71 | BDCSVD,Dynamic,Dynamic>> B=Rxx.bdcSvd(ComputeFullU|ComputeFullV); 72 | Matrix,Dynamic,Dynamic> U = B.matrixU(); 73 | Matrix,Dynamic,Dynamic> Un = U.rightCols(M-K); 74 | Matrix,1,Dynamic> S = B.singularValues(); 75 | //cout << "singularvalues: " << S << endl; 76 | return Un; 77 | } 78 | 79 | //Noise subspace with Eigendecomp. This cannot be used as such, because the eigenvectors&vals are unordered. 80 | MatrixType noisesubspaceeig(MatrixType Rxx, int K){ 81 | SelfAdjointEigenSolver es; 82 | es.compute(Rxx); 83 | //cout << "The eigenvalues of Rxx are: " << es.eigenvalues().transpose() << endl; 84 | //cout << "The matrix of eigenvectors, V, is:" << endl << es.eigenvectors() << endl << endl; 85 | return es.eigenvectors(); 86 | } 87 | 88 | //steering vector, for direction phi(rad) 89 | ColVecType s_vec(float phi){ 90 | ColVecType A; 91 | 92 | float d = (1.225*1.24)/3.0f; //element distance, wavelengths 93 | //d = 0.5f; //exp(2j*pi*d*cos(pi)) 94 | 95 | for (int i=0;i,Dynamic,Dynamic> Un, Matrix,Dynamic,1> a) 117 | { 118 | //complex denom = ((Un.adjoint()*a).norm()); 119 | complex denom = (Un.adjoint()*a).squaredNorm(); 120 | complex res = a.squaredNorm()/denom; 121 | return (res*conj(res)).real(); 122 | //complex denom = a.adjoint()*Un*Un.adjoint()*a; 123 | //return (1.0f/denom).real(); 124 | } 125 | 126 | /* 127 | Matrix,Dynamic,Dynamic> DA(Matrix,Dynamic,Dynamic>){ 128 | Matrix<,Dynamic,Dynamic> res; 129 | res.resize(); 130 | }*/ 131 | 132 | Matrix pmusic2dvec(Matrix,Dynamic,Dynamic> Un,float d, int Mx, int My, int Cx, int Cy){ 133 | Matrix res(Cx,Cy); 134 | for(int cx=0;cx pmusicvec(Matrix,Dynamic,Dynamic> Un, int N) //N+1 points between 0 to PI 145 | { 146 | Matrix res; 147 | res.resize(N,1); 148 | 149 | for(int n=0;n *dbuf0; 157 | complex *dbuf1; 158 | bool exit_all; 159 | uint32_t gseq; 160 | std::mutex g_i_mutex; 161 | 162 | struct cdims{ 163 | uint32_t cols; 164 | uint32_t rows; 165 | cdims(uint32_t r_,uint32_t c_):rows(r_), cols(c_){}; 166 | }; 167 | 168 | void plotthread(cdims *dims){ 169 | 170 | uint32_t cols = dims->cols; 171 | uint32_t rows = dims->rows; 172 | 173 | Gnuplot gp; 174 | gp << "set hidden3d nooffset\n"; 175 | gp << "set style fill transparent solid 0.65\n"; 176 | 177 | //preallocate 2d vector container for gnuplotc++ interface: 178 | std::vector > > pts(100); 179 | for (int nx=0;nx<100;nx++){ 180 | pts[nx].resize(100); 181 | for (int ny=0;ny<100;ny++){ 182 | pts[nx][ny].resize(3); 183 | } 184 | } 185 | MapType Xall(dbuf0,rows,cols); 186 | MapType Xall2(dbuf1,rows,cols); 187 | 188 | while (!exit_all){ 189 | MatrixType X = Xall.rightCols(cols-1); 190 | X.rowwise()-=X.colwise().mean(); 191 | 192 | { 193 | const std::lock_guard lock(g_i_mutex); 194 | std::swap(dbuf0,dbuf1); 195 | std::swap(Xall,Xall2); 196 | } 197 | 198 | // covariance matrix estimate & noise subspace via SVD 199 | MatrixType Rxx = (1/float(rows))*(X.adjoint()*X); 200 | MatrixType Un = noisesubspace(Rxx,1); 201 | 202 | float d = (1.225*1.24)/3.0f; 203 | Matrix pm = pmusic2dvec(Un,d,MX,MY,100, 100); 204 | 205 | //normalize 206 | auto g = pm.maxCoeff(); 207 | pm *= 1/g; 208 | for (int nx=0;nx<100;nx++){ 209 | for (int ny=0;ny<100;ny++){ 210 | pts[nx][ny][0] = -90 + float(nx)*1.8f; 211 | pts[nx][ny][1] = -90 + float(ny)*1.8f; 212 | pts[nx][ny][2] = 20.0f*log10(pm(nx,ny)); //2.0f; 213 | 214 | } 215 | } 216 | gp.clear(); 217 | gp << "set yrange [-90:90]\n"; 218 | gp << "set xrange [-90:90]\n"; 219 | gp << "set cbrange [-20:0]\n"; 220 | gp << "set zrange [-20:1]\n"; 221 | //gp << "set view map\n"; 222 | gp << "splot '-' with pm3d title 'Pm " << to_string(gseq)<<"'\n"; 223 | gp.send2d(pts); 224 | gp.flush(); 225 | } 226 | 227 | } 228 | 229 | void matsave(int8_t *s8bit, uint32_t r, uint32_t c, string fname){ 230 | // get the time as string 231 | time_t t = time(NULL); 232 | struct tm *tm = localtime(&t); 233 | char s[64]; 234 | strftime(s, sizeof(s), "%c", tm); 235 | 236 | cout << "Creating file " << fname << endl; 237 | MATFile *pmat = matOpen(fname.data(),"w"); 238 | 239 | mxArray *pa1 = mxCreateNumericMatrix(r,c,mxSINGLE_CLASS,mxCOMPLEX); 240 | mxArray *pa2 = mxCreateString(s); 241 | 242 | //get the pointer to our matlab matrix and convert from i8 243 | float* outptr = (float *) mxGetComplexSingles(pa1); 244 | volk_8i_s32f_convert_32f((float *) outptr, s8bit,127.0f,2*r*c); 245 | 246 | matPutVariable(pmat, "x", pa1); 247 | matPutVariable(pmat, "time", pa2); 248 | mxDestroyArray(pa1); 249 | mxDestroyArray(pa2); 250 | 251 | matClose(pmat); 252 | } 253 | 254 | 255 | void usage(void) 256 | { 257 | fprintf(stderr, 258 | "\n URA beamform & Matlab .mat save:\n" 259 | "\t[-a receiver address, e.g. tcp://localhost:5555\n" 260 | "\t[-c frame count, 0 = inf]\n" 261 | "\t[-f filename to save, if given sets framecount = 1]\n"); 262 | exit(1); 263 | } 264 | 265 | int main(int argc, char **argv) { 266 | 267 | //set defaults & parse command line 268 | string addr = "tcp://localhost:5555"; 269 | string fname; 270 | int totalframes = 1; 271 | 272 | int opt; 273 | while ((opt = getopt(argc, argv, "a:c:f:h")) != -1) { 274 | switch (opt) { 275 | case 'a': 276 | addr = string(optarg); 277 | break; 278 | case 'c': 279 | totalframes = (int) atoi(optarg); 280 | break; 281 | case 'f': 282 | fname = string(optarg); 283 | totalframes = 1; 284 | break; 285 | case 'h': 286 | default: 287 | usage(); 288 | break; 289 | } 290 | } 291 | 292 | 293 | //set up zmq and retrieve one frame to determine dimensions 294 | zmq::context_t ctx(1); 295 | zmq::socket_t sock(ctx, ZMQ_SUB); 296 | sock.setsockopt(ZMQ_SUBSCRIBE, "", 0); 297 | sock.connect(addr.data()); 298 | 299 | zmq::message_t msg; 300 | sock.recv (&msg); 301 | 302 | int8_t *s8bit = static_cast(msg.data()); 303 | gseq = *((uint32_t *) s8bit); 304 | uint32_t cols = *(((uint32_t *) s8bit)+1); 305 | uint32_t rows = *(((uint32_t *) s8bit)+2); 306 | //cols = NSENSOR; //only the first antennas. 307 | 308 | cout << "Allocating a " << to_string(rows) << " X " << to_string(cols) << " matrix" << endl; 309 | 310 | //raw data buffer for incoming sample conversion: 311 | dbuf0 = new complex [rows*cols]; 312 | dbuf1 = new complex [rows*cols]; 313 | volk_8i_s32f_convert_32f((float *) dbuf0, s8bit,127.0f,rows*cols*2); 314 | 315 | exit_all = false; 316 | cdims dim(rows,cols); 317 | std::thread plotthrd(plotthread,&dim); 318 | 319 | 320 | //main loop simply receives data and converts to 32-bit float 321 | for (int n=0;n(msg.data()); 324 | gseq = *((uint32_t *) s8bit); 325 | s8bit +=16+sizeof(uint32_t)*cols; 326 | //convert samples to complex float, use a scoped lock in case plotthread is switching buffers. 327 | { 328 | //cdsp::convtofloat(dbuf, s8bit, rows*cols*2); //maybe someday include this in cmake such that we can use this... 329 | const std::lock_guard lock(g_i_mutex); 330 | volk_8i_s32f_convert_32f((float *) dbuf0, s8bit,127.0f,rows*cols*2); 331 | } 332 | } 333 | 334 | // save the file as matlab mat. we'll have to reconvert from int8 since 335 | // plotthrd might have swapped buffers 336 | if (!fname.empty()){ 337 | exit_all = true; 338 | matsave(s8bit,rows,cols,fname); 339 | } 340 | 341 | 342 | plotthrd.join(); 343 | 344 | delete[] dbuf0; 345 | delete[] dbuf1; 346 | } -------------------------------------------------------------------------------- /deps: -------------------------------------------------------------------------------- 1 | dep ZMQ: 2 | libzmq3-dev 3 | -------------------------------------------------------------------------------- /electronics/README: -------------------------------------------------------------------------------- 1 | Examples on some of the electronics needed for this project will be added here. 2 | All of the schematics are not available in electronic form. 3 | 4 | At least the reference noise switching circuit & firmware is still missing. 5 | In our setup, this is implemented with the 2$ STM32F103C8T6 (a.k.a "Blue Pill") µC. 6 | This was (audaciously) edited from a CDCACM-device example code in libopencm3 examples 7 | to respond to a certain character ('X', could be anything and even include more functionality 8 | - too busy to do anything more) to control a GPIO pin. That is, the USB-enabled µC maps to 9 | a modem device, e.g. /dev/ttyACM0 (add your user to the dialout group for access), which you 10 | then send characters to enable/disable a high-side power switch, which drives the noise amps. 11 | One could also include a udev rule to command your host computer to not treat it as a modem, but 12 | it works fine without it after the initial AT-command garbage stops. Our setup also includes an 13 | indicator led (+a driver circuit) to observe the reference noise state. 14 | 15 | 16 | Brief descriptions: 17 | 18 | example_noisegen: 19 | Kicad project for 3+1 output reference noise generator. Drives 3 couplers, 20 | one directionally coupled for reference dongle. Separate power connectors 21 | for the MMIC amplifier chain and Zener diode. Optional enable signal for 22 | diode current (bypassed in prototype build). Our design switches the noise 23 | by disabling the operating voltage on the MMIC amps. 24 | 25 | The output connectors should be changed to MCX, currently they are MMCX. 26 | Also, an updated PCB version for 8 outputs with 'almost proper' Wilkinson power dividers 27 | is half done, but still somewhat unfinished for being uploaded here (as if anything 28 | here is finished/mature). Currently, I'm burdened with writing articles. 29 | 30 | clock_generator: 31 | The clock generator (no complete schematic for this currently), is a buffered 32 | Pierce-type oscillator. Uses 74HC04 and 74HC14 (Schmidt trigger) inverters. See 33 | example_clock. The Schmidt trigger (for reducing noise) is not strictly necessary 34 | and could be replaced by 74HC04, or alternatively a single Schmidt trigger followed 35 | by 74HC04 buffers. 36 | 37 | 38 | coupler module for 7 receivers: 39 | The reference noise is distributed to the receivers via directional coupling. 40 | Check the .jpg for the idea: here the top PCB is a D-Link USB hub. 41 | 42 | Published with permission from Tejeez: coupler.zip & adapter.zip contain the 43 | plotted gerber files for a 7-element coupler PCB assembly. You will also need 44 | a suitable 7-port HUB, some SMD caps & resistors, inductor and MCX & angled 45 | BNC connectors. Also, pin header connectors between adapter and coupler boards. 46 | A crude BOM and component values: 47 | 48 | P1,P3,P5,P7,P9,P11,P13 : MCX STRAIGHT PLUG (Farnell 1831741) 49 | P2,P4,P6,P8,P10,P12,P14: ANGLED BNC (Tyco electronics) 50 | 51 | R1-R8: 51 Ohm, 1206? (need to verify footprint size) 52 | C1-C7: 1nF 1206 53 | L1: 220 nH (Farnell 2470315: this inductor is too small, 54 | painful to solder, get one with larger footprint) 55 | 56 | C8-C9: 22pF, 1nF 57 | 58 | C11-C18: (caps to chassis GND) are not strictly necessary. 59 | 60 | P22: 15 X 2 pin header (2.54mm pitch, straight) connects to adapter 61 | 62 | adapter: 63 | C10: 1uF, 0805 (in adapter board, power source filter) 64 | 15 X 2 pin connector (straight, 2.54mm) 65 | micro-USB B connector 66 | 67 | To improve robustness, RF-switch ICs could be used instead of directional coupling 68 | to perform this function. A strong signal from the antennas on the frequency of 69 | interest might interfere with the synchronization in the current coupling approach. 70 | 71 | -------------------------------------------------------------------------------- /electronics/adapter.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlaaks/coherent-rtlsdr/1c861ad2828f0cbcac7f56fc094f1272bc7ee787/electronics/adapter.zip -------------------------------------------------------------------------------- /electronics/coupler.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlaaks/coherent-rtlsdr/1c861ad2828f0cbcac7f56fc094f1272bc7ee787/electronics/coupler.zip -------------------------------------------------------------------------------- /electronics/coupler2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlaaks/coherent-rtlsdr/1c861ad2828f0cbcac7f56fc094f1272bc7ee787/electronics/coupler2.jpg -------------------------------------------------------------------------------- /electronics/example_clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlaaks/coherent-rtlsdr/1c861ad2828f0cbcac7f56fc094f1272bc7ee787/electronics/example_clock.png -------------------------------------------------------------------------------- /electronics/example_noisegen_kicad502.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlaaks/coherent-rtlsdr/1c861ad2828f0cbcac7f56fc094f1272bc7ee787/electronics/example_noisegen_kicad502.zip -------------------------------------------------------------------------------- /examplecfg/URA21.cfg: -------------------------------------------------------------------------------- 1 | #optional config file for receiver configuration, iterate the dongles in order specified below 2 | #format is: number of channel (R or 0 for ref) : 'serial str' 3 | #use this to change the order and number of channels in the rx-matrix 4 | #configurable gains etc. for each channel could be implemented some day. 5 | R :'M REF' 6 | 1 :'M 0' 7 | 2 :'M 1' 8 | 3 :'M 2' 9 | 4 :'M 3' 10 | 5 :'M 4' 11 | 6 :'M 5' 12 | 7 :'M 6' 13 | 8 :'M 7' 14 | 9 :'M 8' 15 | 10:'M 9' 16 | 11:'M 10' 17 | 12:'M 11' 18 | 13:'M 12' 19 | 14:'M 13' 20 | 15:'M 14' 21 | 16:'M 15' 22 | 17:'M 16' 23 | 18:'M 17' 24 | 19:'M 18' 25 | 20:'M 19' 26 | 21:'M 20' 27 | -------------------------------------------------------------------------------- /examplecfg/ac.cfg: -------------------------------------------------------------------------------- 1 | #optional config file for receiver configuration, iterate the dongles in order specified below 2 | #format is: number of channel (R or 0 for ref) : 'serial str' 3 | #use this to change the order and number of channels in the rx-matrix 4 | #configurable gains etc. for each channel could be implemented some day. 5 | R :'M REF' 6 | 1 :'M 0' 7 | 2 :'M 1' 8 | 3 :'M 2' 9 | 4 :'M 3' 10 | 5 :'M 4' 11 | 6 :'M 5' 12 | 7 :'M 6' 13 | 8 :'M 7' 14 | 9 :'M 8' 15 | 10:'M 9' 16 | 11:'M 10' 17 | 12:'M 11' 18 | 13:'M 12' 19 | 14:'M 13' 20 | 15:'M 14' 21 | 16:'M 15' 22 | 17:'M 16' 23 | 18:'M 17' 24 | -------------------------------------------------------------------------------- /examplecfg/four.cfg: -------------------------------------------------------------------------------- 1 | #optional config file for receiver configuration, iterate the dongles in order specified below 2 | #format is: number of channel (R or 0 for ref) : 'serial str' 3 | #use this to change the order and number of channels in the rx-matrix 4 | #configurable gains etc. for each channel could be implemented some day. 5 | R :'M REF' 6 | 1 :'M 1' 7 | 2 :'M 2' 8 | 3 :'M 3' 9 | -------------------------------------------------------------------------------- /include/ccoherent.h: -------------------------------------------------------------------------------- 1 | /* 2 | coherent-rtlsdr 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | coherent-rtlsdr is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with coherent-rtlsd. If not, see . 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "csdrdevice.h" 24 | #include "cpacketizer.h" 25 | #include "common.h" 26 | #include "cdsp.h" 27 | #include "crefnoise.h" 28 | 29 | using tmpcomplextype_ = std::complex; //this can be done, but is it worth it? 30 | using fftff_complex = tmpcomplextype_; 31 | 32 | 33 | class ccoherent{ 34 | private: 35 | std::thread thread; 36 | static void threadf(ccoherent *); 37 | 38 | //std::vector *devices; //CHANGED 39 | lvector *devices; 40 | crefsdr *refdev; 41 | 42 | cpacketize *packetize; 43 | 44 | fft_scheme fftscheme,ifftscheme; 45 | std::complex *sifft, *sfft; //was fftwf_complex 46 | std::complex *sfloat, *sconv; 47 | float *smagsqr; 48 | 49 | int nfft; 50 | int blocksize; 51 | 52 | int mb; // RASPBERRYPI MAILBOX for gpu. 53 | 54 | std::vector lagqueue; 55 | crefnoise *refnoise; 56 | public: 57 | 58 | std::atomic do_exit; 59 | //ccoherent(crefsdr*,std::vector *, cpacketize *); //CHANGED 60 | //ccoherent(crefsdr*,lvector *, cpacketize *); 61 | ccoherent(crefsdr*,lvector *,crefnoise*, int nfft); 62 | ~ccoherent(); 63 | void start(); 64 | void request_exit(); 65 | void join(); 66 | 67 | size_t lagqueuesize(); 68 | 69 | void clearlagqueue(); 70 | void queuelag(csdrdevice *d); 71 | void queuelag(csdrdevice *d,bool refc); 72 | void computelag(); 73 | }; 74 | -------------------------------------------------------------------------------- /include/cconfigfile.h: -------------------------------------------------------------------------------- 1 | /* 2 | coherent-rtlsdr 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | coherent-rtlsdr is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with coherent-rtlsdr. If not, see . 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | using namespace std; 23 | 24 | 25 | struct sdrdefs { 26 | uint32_t devindex; 27 | string serial; 28 | }; 29 | 30 | class cconfigfile{ 31 | public: 32 | static std::vector readconfig(string fname) { 33 | string ln; 34 | ifstream cfgfile(fname); 35 | std::vector v; 36 | 37 | while(getline(cfgfile,ln)){ 38 | sdrdefs d; 39 | if (ln[0]=='#') 40 | continue; 41 | else{ 42 | std::size_t st,end; 43 | std::string::size_type sz; 44 | string ids = ln.substr(0,2); 45 | 46 | if ((ids[0]=='R')||(ids[1]=='R')) 47 | d.devindex = 0; 48 | else 49 | d.devindex = std::stoi(ids,&sz); 50 | 51 | st = ln.find(":"); 52 | st = ln.find("'",st+1); 53 | end= ln.find("'",st+1); 54 | d.serial = ln.substr(st+1,end-st-1); 55 | v.push_back(d); 56 | } 57 | 58 | } 59 | cfgfile.close(); 60 | return v; 61 | } 62 | 63 | static string get_refname(std::vector vdefs){ 64 | string ret; 65 | for(auto n : vdefs) { 66 | if (n.devindex==0){ 67 | ret=n.serial; 68 | break; 69 | } 70 | } 71 | return ret; 72 | } 73 | 74 | }; 75 | -------------------------------------------------------------------------------- /include/ccontrol.h: -------------------------------------------------------------------------------- 1 | /* 2 | coherent-rtlsdr 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | coherent-rtlsdr is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with coherent-rtlsdr. If not, see . 16 | */ 17 | #ifndef CCONTROLH 18 | #define CCONTROLH 19 | 20 | #include 21 | #include 22 | #include "csdrdevice.h" 23 | 24 | //forward declaration, these classes have a circular dependency 25 | class ccontrol; 26 | class csdrdevice; 27 | 28 | class ccontrol{ 29 | private: 30 | std::thread thread; 31 | static void threadf(ccontrol *); 32 | 33 | csdrdevice *dev; 34 | public: 35 | std::atomic do_exit; 36 | 37 | void start(); 38 | void request_exit(); 39 | void join(); 40 | ccontrol(csdrdevice *device){ 41 | dev=device; 42 | do_exit = false; 43 | } 44 | 45 | ~ccontrol(){ 46 | if (thread.joinable()) thread.join(); 47 | } 48 | 49 | }; 50 | 51 | #endif 52 | 53 | -------------------------------------------------------------------------------- /include/cdsp.h: -------------------------------------------------------------------------------- 1 | /* 2 | coherent-rtlsdr 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | coherent-rtlsdr is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with coherent-rtlsdr. If not, see . 16 | */ 17 | 18 | #ifndef CDSPH 19 | #define CDSPH 20 | //#include "common.h" 21 | #include 22 | 23 | #ifdef RASPBERRYPI 24 | extern "C"{ 25 | #include "gpu_fft.h" 26 | #include "mailbox.h" 27 | typedef struct GPU_FFT *fft_scheme; 28 | } 29 | #else 30 | #include 31 | typedef fftwf_plan fft_scheme; 32 | #endif 33 | 34 | class cbuffer; 35 | 36 | class cdsp{ 37 | public: 38 | //static void convtosigned(const cbuffer in, const cbuffer out, int n); 39 | //static void convtosigned(const uint8_t*in, const cbuffer out, int n); 40 | static void convtosigned(const uint8_t*in, const uint8_t *out, int n); 41 | static const float* convtofloat(const float *out, const int8_t *s8bit, int n); 42 | 43 | static const std::complex* convto8bit(std::complex *out,std::complex*in, int n); 44 | 45 | static const std::complex* convtofloat(const std::complex *out, const int8_t *s8bit, int n); 46 | 47 | static const std::complex* scalarmul(const std::complex *out,const std::complex *in,const std::complex scalar_in, int n); 48 | 49 | static const std::complex conj_dotproduct(const std::complex *a,const std::complex *b, int n); 50 | //static const std::complex* convtofloat(const float *out, const int8_t *s8bit, int n); 51 | 52 | static const float rms(const float *in, int n); 53 | static const float rms(const std::complex *in,int n); 54 | 55 | static const float PAPR(const std::complex *s,const std::complex *ref, int n); 56 | 57 | static const float crestfactor(const float *in,float peak, int n); 58 | static const float crestfactor(const float *in, int n); 59 | 60 | static const float* magsquared(float *out,const std::complex *in,int n); 61 | 62 | static const std::complex* conjugatemul(std::complex*out, std::complex *in1, std::complex *in2, int n); 63 | 64 | static const std::complex* fft(std::complex*out, std::complex *in,fft_scheme *scheme); 65 | static const std::complex* fft(fft_scheme *scheme); 66 | 67 | static const uint32_t indexofmax(float *in,int n); 68 | static const uint32_t indexofmax(float *out,std::complex *in,int n); 69 | //static const std::complex* ifft(std::complex*out, std::complex *in); 70 | 71 | }; 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /include/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | coherent-rtlsdr 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | coherent-rtlsdr is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with coherent-rtlsdr. If not, see . 16 | */ 17 | 18 | #ifndef COMMONH 19 | #define COMMONH 20 | 21 | #include 22 | #include 23 | #include "cdsp.h" 24 | 25 | #include 26 | #include 27 | #include //memset 28 | #include 29 | 30 | #define USELIBRTLSDRBUFS 31 | 32 | const float sync_threshold=0.005; 33 | 34 | #define TWO_POW(n) ((double)(1ULL<<(n))) 35 | 36 | template int sgn(T val) { 37 | return (T(0) < val) - (val < T(0)); 38 | } 39 | 40 | //a circular pointer array: 41 | class cbuffer{ 42 | public: 43 | int8_t **ptr; 44 | uint32_t *readcnt; 45 | uint64_t *timestamp; 46 | uint32_t L; 47 | uint32_t N; 48 | 49 | uint32_t wp; 50 | uint32_t rp; 51 | 52 | cbuffer(uint32_t n, uint32_t l){ 53 | ptr = new int8_t* [n]; 54 | readcnt = new uint32_t [n]; 55 | timestamp = new uint64_t [n]; 56 | 57 | 58 | 59 | wp = 0; 60 | rp = 0; 61 | N=n; 62 | L=l; 63 | 64 | int alignment = volk_get_alignment(); 65 | for (int i=0;i lock{mtx}; 162 | if (--cnt == 0) { 163 | cv.notify_all(); 164 | } else { 165 | cv.wait(lock, [this] { return cnt == 0; }); 166 | } 167 | } 168 | }; 169 | 170 | /*template ::value_type>> 171 | vector(InputIterator, InputIterator, Allocator = Allocator()) 172 | -> vector::value_type, Allocator>; 173 | template struct hash>; 174 | */ 175 | 176 | 177 | //template bool operator!=(const vector& x, const vector& y 178 | 179 | //template class lvector; 180 | 181 | using namespace std; 182 | template 183 | class lvector{ 184 | private: 185 | // using vector_t = std::vector; 186 | std::vector q; 187 | //T a; 188 | //std::condition_variable cv; 189 | public: 190 | mutable std::mutex m; 191 | //std::mutex m; 192 | explicit lvector(): q(){ 193 | } 194 | lvector(T obj): q(obj){ 195 | } 196 | 197 | void push_back(const T& v){ 198 | std::lock_guard lock(m); 199 | q.push_back(v); 200 | }; 201 | void pop_back(){ 202 | std::lock_guard lock(m); 203 | q.pop_back(); 204 | }; 205 | 206 | 207 | 208 | //typedef std::iterator::iterator> iterator; 209 | using iterator = typename std::vector::iterator; 210 | // using iterator = vector_t::iterator; 211 | // using const_iterator = vector_t::const_iterator; 212 | //std::iterator> begin(){ 213 | //std::iterator > begin(){ 214 | 215 | void lock(){ 216 | m.lock(); 217 | }; 218 | void unlock(){ 219 | m.unlock(); 220 | }; 221 | void erase(iterator it){ 222 | std::lock_guard lock(m); 223 | q.erase(it); 224 | } 225 | 226 | const iterator begin(){ 227 | return q.begin(); 228 | } 229 | 230 | 231 | const iterator end(){ 232 | return q.end(); 233 | } 234 | T& back(){ 235 | return q.back(); 236 | } 237 | 238 | 239 | size_t size() const{ 240 | return q.size(); 241 | }; 242 | 243 | T& operator[](std::size_t idx) { /*std::unique_lock lock(m); */ return q[idx]; } 244 | const T& operator[](std::size_t idx) const { /*std::unique_lock lock(m); */ return q[idx]; } 245 | bool operator!=(const T& a){ return !(a == q);} 246 | }; 247 | 248 | 249 | template 250 | class lqueue{ 251 | private: 252 | std::queue q; 253 | std::condition_variable cv; 254 | std::mutex m; 255 | public: 256 | lqueue(): q(){ 257 | } 258 | lqueue(T obj): q(obj){ 259 | } 260 | 261 | void push(const T & v){ 262 | std::lock_guard lock(m); 263 | q.push(v); 264 | cv.notify_one(); 265 | }; 266 | 267 | T& front(){ 268 | // std::unique_lock lock(m); 269 | // cv.wait(lock, [this]{return (q.size() > 0);}); 270 | return q.front(); 271 | }; 272 | T pop(){ 273 | { 274 | std::unique_lock lock(m); 275 | cv.wait(lock, [this]{return (q.size()>0);}); 276 | } 277 | T v =q.front(); // was T& v, reference got bad after certain number of chars when T=std::string 278 | 279 | //std::cout << "we have: "<< v << std::endl; 280 | q.pop(); 281 | return v; 282 | }; 283 | 284 | size_t size() const{ 285 | return q.size(); 286 | }; 287 | }; 288 | 289 | bool is_zeros(uint32_t *p,int n); 290 | 291 | #endif -------------------------------------------------------------------------------- /include/console.h: -------------------------------------------------------------------------------- 1 | /* 2 | coherent-rtlsdr 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | coherent-rtlsdr is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with coherent-rtlsdr. If not, see . 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include "csdrdevice.h" 33 | #include "common.h" 34 | 35 | #include "crefnoise.h" 36 | 37 | 38 | using namespace std; 39 | 40 | enum command_code{ 41 | help, 42 | fs, 43 | add, 44 | del, 45 | status, 46 | list, 47 | nop, 48 | logs, 49 | quit, 50 | fcenter, 51 | request, 52 | phase 53 | }; 54 | 55 | //static std::map command_codes; 56 | 57 | const static std::unordered_map command_codes{ 58 | {"help",help}, 59 | {"fs",fs}, 60 | {"add",add}, 61 | {"del",del}, 62 | {"status",status}, 63 | {"list",list}, 64 | {"nop",nop}, 65 | {"log",logs}, 66 | {"quit",quit}, 67 | {"fcenter",fcenter}, 68 | {"request",request}, 69 | {"phase",phase} 70 | }; 71 | 72 | struct phistory_t{ 73 | std::chrono::high_resolution_clock::time_point t; 74 | std::complex p; 75 | //auto t = std::chrono::high_resolution_clock::now(); 76 | //auto t_ns= (std::chrono::time_point_cast(t)).time_since_epoch(); 77 | }; 78 | 79 | 80 | class cconsole{ 81 | private: 82 | barrier *startbarrier; 83 | int *stderr_pipe; 84 | crefsdr *refdev; 85 | std::thread thread; 86 | static void consolethreadf(cconsole *); 87 | 88 | std::vector phistory; 89 | 90 | crefnoise *refnoise; 91 | 92 | //zmq::context_t context; 93 | //zmq::socket_t socket; 94 | 95 | public: 96 | //std::vector *devices; //CHANGED 97 | lvector *devices; 98 | 99 | std::atomic do_exit; 100 | //cconsole(int p[2],csdrdevice *refdev_, std::vector *devvec_); // CHANGED 101 | cconsole(int p[2],crefsdr *refdev_, lvector *devvec_,crefnoise * refnoise_); 102 | ~cconsole(); 103 | void start(); 104 | 105 | void request_exit(); 106 | void join(); 107 | 108 | int parsecmd(std::string); 109 | string getoptionstr(std::string inputln); 110 | 111 | int cmdlist(std::string); 112 | int cmdretune(std::string); 113 | int cmdadd(std::string); 114 | int cmddel(std::string); 115 | int cmdrequest(std::string); 116 | int cmdfs(std::string); 117 | int cmdphase(std::string); 118 | int cmdstatus(std::string); 119 | }; -------------------------------------------------------------------------------- /include/cpacketize.h: -------------------------------------------------------------------------------- 1 | /* 2 | coherent-rtlsdr 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | coherent-rtlsdr is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with coherent-rtlsdr. If not, see . 16 | */ 17 | 18 | #ifndef PACKETIZEH 19 | #define PACKETIZEH 20 | //#include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | struct hdr0{ 28 | uint32_t globalseqn; 29 | uint32_t N; 30 | uint32_t L; 31 | uint32_t unused; 32 | }; 33 | 34 | class cpacketize{ 35 | std::atomic writecnt; 36 | std::atomic bufferfilled; 37 | 38 | std::atomic do_exit; 39 | 40 | uint32_t globalseqn; 41 | uint32_t nchannels; 42 | uint32_t blocksize; 43 | bool noheader; 44 | size_t packetlength; 45 | zmq::context_t context; 46 | zmq::socket_t socket; 47 | 48 | std::mutex bmutex; 49 | std::condition_variable cv; 50 | 51 | int8_t *packetbuf0, *packetbuf1; 52 | public: 53 | cpacketize(uint32_t N,uint32_t L, std::string address, bool no_header) : context(1), socket(context,ZMQ_PUB) { 54 | globalseqn = 0; 55 | nchannels = N; 56 | blocksize = L; 57 | noheader = no_header; 58 | socket.bind(address); 59 | writecnt = nchannels; 60 | bufferfilled= false; 61 | 62 | do_exit = false; 63 | 64 | if (noheader) 65 | packetlength = 2*nchannels*blocksize; 66 | else 67 | packetlength = (16 + 4*nchannels) + 2*nchannels*blocksize; 68 | 69 | packetbuf0 = new int8_t[packetlength]; 70 | packetbuf1 = new int8_t[packetlength]; 71 | } 72 | 73 | ~cpacketize(){ 74 | sleep(1); 75 | delete [] packetbuf0; 76 | delete [] packetbuf1; 77 | } 78 | 79 | void request_exit(){ 80 | do_exit = true; 81 | }; 82 | 83 | int send(){ 84 | if (!noheader){ 85 | //fill static header. block readcounts filled by calls to write: 86 | hdr0 *hdr = (hdr0 *) packetbuf0; 87 | hdr->globalseqn = globalseqn++; 88 | hdr->N = nchannels; 89 | hdr->L = blocksize; 90 | hdr->unused = 0; 91 | } 92 | 93 | { 94 | std::unique_lock lock(bmutex); 95 | cv.wait(lock,[this]{return ((bufferfilled.load()) || (do_exit.load()) );}); 96 | bufferfilled = false; 97 | } 98 | //printf("sending..\n"); 99 | socket.send(packetbuf0,packetlength,0); 100 | 101 | 102 | } 103 | 104 | //the idea is that each thread could call this method from it's own context, as we're writing to separate locations 105 | int write(uint32_t channeln,uint32_t readcnt,int8_t *rp){ 106 | 107 | 108 | 109 | uint32_t loc; 110 | 111 | if (!noheader){ 112 | //fill dynamic size part of header, write readcounts 113 | *(packetbuf0 + sizeof(hdr0)+channeln)=readcnt; 114 | loc = (sizeof(hdr0)+nchannels*sizeof(uint32_t)) + channeln*blocksize; 115 | } 116 | else{ 117 | loc = channeln*blocksize; 118 | } 119 | 120 | //copy data 121 | memcpy((int8_t *) (packetbuf0+loc),rp,blocksize); 122 | 123 | if(writecnt--==0) 124 | { 125 | std::unique_lock lock(bmutex); 126 | 127 | int8_t *tmp=packetbuf0; 128 | packetbuf0=packetbuf1; 129 | packetbuf1=tmp; 130 | 131 | writecnt = nchannels; 132 | 133 | bufferfilled = true; 134 | lock.unlock(); 135 | 136 | cv.notify_one(); 137 | } 138 | 139 | 140 | } 141 | 142 | 143 | }; 144 | 145 | 146 | #endif 147 | -------------------------------------------------------------------------------- /include/cpacketizer.h: -------------------------------------------------------------------------------- 1 | /* 2 | coherent-rtlsdr 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | coherent-rtlsdr is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with coherent-rtlsdr. If not, see . 16 | */ 17 | 18 | #ifndef PACKETIZEH 19 | #define PACKETIZEH 20 | //#include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | //byte-size buffers are static, thus, when the first object of class packetize is created 31 | 32 | struct hdr0{ 33 | uint32_t globalseqn; 34 | uint32_t N; 35 | uint32_t L; 36 | uint32_t unused; 37 | }; 38 | 39 | 40 | class cpacketize{ 41 | static int objcount; 42 | static zmq::context_t *context; 43 | static zmq::socket_t *socket; 44 | static std::mutex bmutex; 45 | static std::condition_variable cv; 46 | static bool noheader; 47 | static size_t packetlen; 48 | static uint32_t globalseqn; 49 | 50 | static int writecnt; //was atomic 51 | static bool bufferfilled; //atomic 52 | 53 | static bool do_exit; //atomic 54 | static uint32_t blocksize; 55 | 56 | static std::unique_ptr packetbuf0, packetbuf1; //samplebuffers, one is being sent, one written to 57 | static std::vector> pcorrection; 58 | 59 | 60 | static size_t packetlength(uint32_t N,uint32_t L); 61 | void resize_buffers(uint32_t N, uint32_t L); 62 | public: 63 | 64 | //cpacketize(uint32_t N,uint32_t L, std::string address, bool no_header) : context(1), socket(context,ZMQ_PUB) { 65 | cpacketize(); 66 | ~cpacketize(); 67 | static void init(std::string address,bool noheader_,uint32_t nchannels_,uint32_t blocksize_); 68 | static void cleanup(); 69 | 70 | void request_exit(); 71 | static int send(); 72 | int write(uint32_t channeln,uint32_t readcnt,int8_t *rp); 73 | int write(uint32_t channeln,uint32_t readcnt,std::complex *in); 74 | 75 | int writedebug(uint32_t channeln,std::complex p); 76 | 77 | int notifysend(); 78 | }; 79 | #endif 80 | -------------------------------------------------------------------------------- /include/crefnoise.h: -------------------------------------------------------------------------------- 1 | /* 2 | coherent-rtlsdr 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | coherent-rtlsdr is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with coherent-rtlsdr. If not, see . 16 | */ 17 | 18 | #ifndef CREFNOISEH 19 | #define CREFNOISEH 20 | 21 | #include 22 | #include 23 | 24 | class crefnoise{ 25 | private: 26 | ofstream fp; 27 | bool enabled; 28 | 29 | public: 30 | void set_state(bool s){ 31 | enabled = s; 32 | if (s) 33 | fp<<"x"; //enable char 34 | else 35 | fp<<"o"; //disable char (right now any other than 'x') 36 | 37 | fp.flush(); //may not send single char immediately unless we flush buffers 38 | }; 39 | 40 | bool isenabled(){ 41 | return enabled; 42 | } 43 | 44 | crefnoise(std::string dev){ 45 | fp.open(dev.data()); 46 | if (!fp.good()) 47 | cout<<"Error opening reference noise control '" << dev << endl; 48 | else{ 49 | enabled = true; 50 | fp<<"x"; 51 | fp.flush(); 52 | } 53 | 54 | }; 55 | ~crefnoise(){ 56 | fp.close(); 57 | }; 58 | }; 59 | #endif 60 | -------------------------------------------------------------------------------- /include/csdrdevice.h: -------------------------------------------------------------------------------- 1 | /* 2 | coherent-rtlsdr 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | coherent-rtlsdr is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with coherent-rtlsdr. If not, see . 16 | */ 17 | 18 | #ifndef CSDRDEVICEH 19 | #define CSDRDEVICEH 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include "ccontrol.h" 33 | #include "common.h" 34 | #include "cdsp.h" 35 | #include "cpacketizer.h" 36 | 37 | 38 | //forward declaration, these classes have a circular dependency 39 | class csdrdevice; 40 | class ccontrol; 41 | 42 | struct lagpoint{ 43 | uint64_t ts; 44 | float lag; 45 | float mag; 46 | float PAPR; 47 | lagpoint() 48 | { 49 | ts=0; 50 | lag=0; 51 | mag=0; 52 | PAPR=0; 53 | } 54 | }; 55 | 56 | class csdrdevice{ 57 | uint32_t readcnt; 58 | 59 | protected: 60 | ccontrol *controller; 61 | std::thread thread; 62 | std::mutex mtx; 63 | std::condition_variable cv; 64 | 65 | std::mutex fftmtx; 66 | std::condition_variable fftcv; 67 | 68 | std::mutex syncmtx; 69 | std::condition_variable synccv; 70 | 71 | std::atomic newdata; 72 | std::atomic do_exit; 73 | 74 | std::atomic lagrequested; 75 | std::atomic lagready; 76 | std::atomic synced; 77 | std::atomic streaming; 78 | 79 | std::complex *sfloat; //was lv_32fc_t * 80 | 81 | std::complex phasecorr; 82 | std::complex phasecorrprev; 83 | 84 | uint32_t asyncbufn; //should this still be under crtlsdr... 85 | uint32_t blocksize; 86 | uint32_t samplerate; 87 | uint32_t realfs; 88 | uint32_t fcenter; 89 | lagpoint lagp; 90 | 91 | 92 | std::string devname; 93 | 94 | //virtual void asynch_thread(csdrdevice *d) = 0; 95 | public: 96 | cpacketize packetize; 97 | 98 | virtual int open(uint32_t index) = 0;//(uint32_t ndevice,uint32_t fcenter, uint32_t samplerate,uint32_t gain,uint32_t agcmode)=0; 99 | virtual int open(std::string name) = 0; 100 | virtual int close() = 0; 101 | virtual int set_fcenter(uint32_t f) = 0; 102 | virtual int set_samplerate(uint32_t fs) = 0; 103 | 104 | virtual int set_agcmode(bool flag)=0; 105 | virtual int set_tunergain(uint32_t gain)=0; 106 | virtual int set_tunergainmode(uint32_t mode)=0; 107 | virtual int set_correction_f(float f)=0; 108 | 109 | virtual int8_t *swapbuffer(uint8_t *b) = 0; 110 | virtual int8_t *read()=0; 111 | 112 | virtual void start(barrier *b) = 0; 113 | virtual void startcontrol() = 0; 114 | virtual void stop(){}; 115 | virtual const std::complex *convtofloat() = 0; 116 | virtual const std::complex *convtofloat(const std::complex*)=0; 117 | virtual void consume() = 0; 118 | virtual uint32_t get_readcntbuf()=0; 119 | //virtual float findcorrpeak(const fftwf_complex *reffft) = 0; 120 | 121 | std::complex est_phasecorrect(const lv_32fc_t *ref); 122 | std::complex get_phasecorrect(); 123 | 124 | float est_PAPR(const lv_32fc_t *ref); 125 | 126 | std::complex *phasecorrect(); 127 | 128 | void requestfft(){lagready=false; lagrequested=true;}; 129 | void requestfftblocking(){ 130 | lagrequested=true; 131 | lagready=false; 132 | { 133 | std::unique_lock lock(fftmtx); 134 | fftcv.wait(lock,[this]{return lagready.load();}); 135 | } 136 | }; 137 | 138 | void set_lag(float lag,float mag){ 139 | lagp.lag = lag; 140 | lagp.mag = mag; 141 | lagp.ts = (std::chrono::time_point_cast 142 | (std::chrono::high_resolution_clock::now())).time_since_epoch().count(); 143 | 144 | { 145 | std::lock_guard lock(fftmtx); 146 | lagready = true; 147 | lagrequested = false; 148 | } 149 | 150 | fftcv.notify_all(); 151 | } 152 | 153 | bool is_lagrequested(){ 154 | return lagrequested; 155 | } 156 | 157 | const std::complex* get_sptr(){return sfloat;}; 158 | 159 | void request_exit(){do_exit=true;}; 160 | 161 | const lagpoint* const get_lagp(){return &lagp;}; //?? 162 | float get_samplerate(){return samplerate;}; //should we return the 'actual samplerate' reported by set_samplerate()? 163 | uint32_t get_blocksize(){return blocksize;}; 164 | uint32_t get_asyncbufn(){return asyncbufn;}; 165 | uint32_t get_fcenter(){return fcenter;}; 166 | 167 | std::string get_devname(){return devname;}; 168 | 169 | 170 | inline uint32_t inc_readcnt(){return readcnt++;}; 171 | inline uint32_t get_readcnt(){return readcnt;}; 172 | 173 | inline bool wait_synchronized(){ 174 | //return synced; 175 | { 176 | std::unique_lock lock(syncmtx); 177 | synccv.wait(lock,[this]{return !synced.load();}); 178 | } 179 | return synced; 180 | }; 181 | inline void set_synchronized(bool state){ 182 | std::lock_guard lock(syncmtx); 183 | synced = state; 184 | synccv.notify_one(); 185 | }; 186 | 187 | inline bool is_ready(){ 188 | return ((readcnt >= asyncbufn) && streaming); 189 | }; 190 | 191 | inline bool get_synchronized(){return synced;}; 192 | 193 | csdrdevice(uint32_t asyncbufn_,uint32_t blocksize_,uint32_t samplerate_, uint32_t fcenter_); 194 | virtual ~csdrdevice(); 195 | }; 196 | 197 | 198 | class crtlsdr: public csdrdevice{ 199 | rtlsdr_dev_t *dev; 200 | uint32_t devnum; //rtlsdr device params 201 | 202 | uint32_t rfgain; 203 | bool enableagc; 204 | 205 | static void asynch_callback(unsigned char *buf, uint32_t len, void *ctx); 206 | 207 | 208 | 209 | int8_t *swapbuffer(uint8_t *b); 210 | protected: 211 | cbuffer s8bit; //timestamped & seqnum, N 8-bit buffers 212 | static void asynch_threadf(crtlsdr *d); 213 | barrier *startbarrier; 214 | 215 | public: 216 | static uint32_t get_device_count(); 217 | 218 | static std::string get_device_name(uint32_t index); 219 | static int get_index_by_serial(std::string serial); 220 | static std::string get_device_serial(uint32_t index); 221 | static std::string get_usb_str_concat(uint32_t index); 222 | 223 | int open(uint32_t index); 224 | int open(std::string name); 225 | 226 | int close(); 227 | int set_fcenter(uint32_t f); 228 | int set_samplerate(uint32_t fs); 229 | 230 | int set_agcmode(bool flag); 231 | int set_tunergain(uint32_t gain); 232 | int set_tunergainmode(uint32_t mode); 233 | int set_correction_f(float f); 234 | 235 | const std::complex *convtofloat(); //needed for overloading in case of reference channel. 236 | const std::complex *convtofloat(const std::complex*); 237 | //float findcorrpeak(const fftwf_complex *reffft); 238 | 239 | //float est_phasecorrect(const lv_32fc_t *ref); 240 | 241 | 242 | void start(barrier *b); 243 | void startcontrol(); 244 | void stop(); 245 | 246 | uint32_t get_readcntbuf(){ 247 | return s8bit.get_rcnt(); 248 | }; 249 | 250 | int8_t *read(); 251 | void consume(){s8bit.consume();}; 252 | 253 | crtlsdr(uint32_t asyncbufn_, uint32_t blocksize_,uint32_t samplerate_, uint32_t fcenter_) : csdrdevice(asyncbufn_,blocksize_,samplerate_,fcenter_), s8bit(asyncbufn_,blocksize_){ 254 | rfgain = 500; 255 | //int alignment = volk_get_alignment(); 256 | 257 | //s8bit = new cbuffer[asyncbufn]; //{cbuffer(block_size)} 258 | //for (int n=0;n *convtofloat(); 280 | const std::complex *convtofloat(const std::complex*); 281 | 282 | const lv_32fc_t* get_sptr(); 283 | 284 | void start(barrier *b); 285 | crefsdr(uint32_t asyncbufn_, uint32_t blocksize_,uint32_t samplerate_, uint32_t fcenter_) : crtlsdr(asyncbufn_,blocksize_,samplerate_,fcenter_){}; 286 | //~crefsdr(){}; 287 | }; 288 | 289 | #endif 290 | -------------------------------------------------------------------------------- /install_on_rpi: -------------------------------------------------------------------------------- 1 | Initial tests on RPI 3 Model B and NanoPi A64 were unsuccessful. The program can be compiled, but synchronization fails due to insufficient CPU-power. Lowering sample rate to 250 kHz had shown limited success. This needs optimization, and there are obvious tweaks that can be done. The computation of cross-correlation has already been disabled for channels which have acquired synchronization, but this feature is untested on the PI. 2 | 3 | For the NanoPi A64: Flash armbian system onto microSD, remember to resize the root partition, otherwise, it's too small (3.1G). I had to delete the partition on the RPI, recreate a larger partition starting from exact same position, then resize it on desktop debian system (fsck is absolutely required, and won't run on mounted system, and then you cannot umount /) via first running fsck -f on the partition, and then run resize2fs. Now it's 15 Gb. 4 | 5 | With the restructured software, both RPI 3 Model B and NanoPi A64 work. The ordinary RPI 3B supports cross-correlation via GPU_FFT, but due to USB-host chipset limitations, will not allow more than 8 USB devices simultaneously. Depending on what other hardware present, expect 5-8 signal dongles to function properly. A64 does not have the Videocore GPU, thus with NanoPi A64 the correlation must be performed with FFTW. Currently, the software is unstable on the NanoPi A64. However, in tests, NanoPi A64 successfully synchronized 21 signal dongles. 6 | 7 | *installing the prequisites for coherentsdr 8 | 9 | First, cmake is needed for compiling most of the required libraries. This is easy on Armbian with apt-get 10 | sudo apt-get install cmake 11 | 12 | Git git 13 | sudo apt-get install git 14 | 15 | Build tools are likely missing 16 | sudo apt-get install build-essential 17 | 18 | 1. Experimental rtl-sdr fork 19 | Get libusb developer libraries (needs root or sudo): 20 | apt-get install libusb-1.0-0-dev 21 | Download the source: 22 | git clone git://github.com/tejeez/rtl-sdr 23 | cd rtl-sdr 24 | mkdir build 25 | cmake .. -DDETACH_KERNEL_DRIVER=ON -DINSTALL_UDEV_RULES=ON 26 | make -j 4 27 | sudo make install 28 | 29 | sudo ldconfig 30 | sudo udevadm control -R 31 | sudo udevadm trigger 32 | 33 | 2. Compile Vector Optimized Library of Kernels (VOLK) from sources 34 | install Mako templates for python 35 | apt-get install python-mako 36 | install Boost-C++ libraries. note, the available version may vary, for me 1.58 worked. to check, run: sudo apt-cache search libboost 37 | apt-get install libboost1.58-all 38 | Python compatibility library six, easiest to install with pip (if needed, following cmake will complain if this is missing) 39 | sudo apt-get install python-pip 40 | pip install six 41 | 42 | Download the source: 43 | git clone git://github.com/gnuradio/volk 44 | cd volk 45 | mkdir build 46 | cd build 47 | cmake .. 48 | make -j 4 49 | sudo make install 50 | 51 | 3. Install Fastest Fourier Transform in The West (FFTW), easy 52 | apt-get install fftw3-dev 53 | 54 | 4. Install ZMQ libraries 55 | apt-get install libzmq3-dev 56 | 57 | 5. GNU Readline developer 58 | sudo apt-get install libreadline6 libreadline6-dev 59 | On Rock Pi 4 debian, this installs the developer libs 60 | sudo apt-get install libreadline-dev 61 | 62 | 6. Coherentrtlsdr (-DRASPBERRYPI replaces FFTW with GPU_FFT) 63 | mkdir build 64 | cd build 65 | cmake .. -DRASPBERRYPI=ON 66 | make 67 | 68 | -------------------------------------------------------------------------------- /matlabclient/CZMQSDR.m: -------------------------------------------------------------------------------- 1 | %Coherent-RTL-SDR 2 | % 3 | %The CZMQSDR system object. This interfaces to the zmqsdr.c mex 4 | %implementation. 5 | 6 | classdef CZMQSDR < matlab.System 7 | properties %(Access = private) 8 | CenterFrequency = 1024e6; 9 | IPAddress ='127.0.0.1'; %'127.0.0.1', 5555 10 | Port = '5555'; 11 | ControlPort = '5556'; 12 | RefNoiseEnabled = false; 13 | setuppassed = 'uninitialized'; 14 | end 15 | 16 | methods 17 | function obj = CZMQSDR(varargin) 18 | setProperties(obj,nargin,varargin{:}); 19 | end 20 | 21 | function enablerefnoise(~,state) 22 | if (state) 23 | %disp('refnoise enable'); 24 | zmqsdr('e'); 25 | else 26 | zmqsdr('d'); 27 | %disp('refnoise disable'); 28 | end 29 | end 30 | function commit(~) 31 | 32 | end 33 | end 34 | 35 | methods(Access = protected) 36 | 37 | % function resetImpl(obj) 38 | % obj.CenterFrequency = 1024e6; 39 | % obj.IPAddress ='127.0.0.1'; %'127.0.0.1', 5555 40 | % obj.Port = '5555'; 41 | % obj.ControlPort = '5556'; 42 | % obj.RefNoiseEnabled = false; 43 | % end 44 | 45 | function validatePropertiesImpl(obj) 46 | if (obj.CenterFrequency<24e6) || (obj.CenterFrequency>1766e6) 47 | error('Property CenterFrequency out of range [24,1766] MHz.'); 48 | end 49 | end 50 | 51 | 52 | function processTunedPropertiesImpl(obj) 53 | if (obj.CenterFrequency<24e6) || (obj.CenterFrequency>1766e6) 54 | error('Property CenterFrequency out of range [24,1766] MHz.'); 55 | end 56 | 57 | if isChangedProperty(obj,'CenterFrequency') 58 | zmqsdr('t',obj.CenterFrequency); %retune (t) 59 | end 60 | 61 | if isChangedProperty(obj,'RefNoiseEnabled') 62 | if (obj.RefNoiseEnabled) 63 | disp('refnoise enable'); 64 | zmqsdr('e'); 65 | else 66 | zmqsdr('d'); 67 | disp('refnoise disable'); 68 | end 69 | end 70 | 71 | end 72 | 73 | function setupImpl(obj) 74 | ret = zmqsdr('i',obj.CenterFrequency,obj.IPAddress,... 75 | obj.Port,obj.ControlPort); % init (i) 76 | if (ret==0) 77 | obj.setuppassed = 'Setup complete'; 78 | else 79 | obj.setuppassed = 'Init error'; 80 | disp(obj.setuppassed); 81 | end 82 | end 83 | function [y,gseq,seq] =stepImpl(varargin) 84 | timeoutctr = 0; 85 | while(1) 86 | if length(varargin)<2 87 | [y,gseq,seq]=zmqsdr('r'); % receive (r) 88 | else 89 | [y,gseq,seq]=zmqsdr('r',varargin{2}); % receive (r) 90 | end 91 | if y~=-1 92 | break; 93 | %a socket timeout (250 ms) returns control here. Allows matlab to pause, 94 | % & regain control if needed. If timeouted, retry. 95 | else 96 | timeoutctr = timeoutctr+1; 97 | if(timeoutctr>10) 98 | disp('timeout, no response from radio'); 99 | timeoutctr=0; 100 | end 101 | end 102 | end 103 | end 104 | 105 | function releaseImpl(~) 106 | zmqsdr('c'); % clear (c) 107 | clear mex; 108 | end 109 | end 110 | end -------------------------------------------------------------------------------- /matlabclient/compiling.txt: -------------------------------------------------------------------------------- 1 | To compile zmqsdr.mexa64: 2 | 3 | mex zmqsdr.c -lzmq -R2018a 4 | 5 | After it's done, move zmqsdr.mexa64 and CZMQSDR.m to Matlab path to make accessible from other working dirs. If you do not want to do this, 6 | have these files in the directory where your matlab receive scripts are. 7 | 8 | The C++ interface to matlab is too slow. Therefore, do not try zmqsdr.cc... 9 | -------------------------------------------------------------------------------- /matlabclient/functions/D2Dtoepos.m: -------------------------------------------------------------------------------- 1 | function [epos] = D2Dtoepos(Du) 2 | minx = min(real(Du)); 3 | maxx = max(real(Du)); 4 | miny = min(imag(Du)); 5 | maxy = max(imag(Du)); 6 | 7 | epos = zeros(maxy-miny+1,maxx-minx+1); 8 | 9 | dx = unique(real(Du)); 10 | dy = unique(imag(Du)); 11 | 12 | epos = [repmat(dy',1,length(dx));repelem(dx',length(dy))]; 13 | end -------------------------------------------------------------------------------- /matlabclient/functions/DA2D.m: -------------------------------------------------------------------------------- 1 | function [Ra, dia,Du] = DA2D(R,epos) 2 | % 3 | % clear all; close all; 4 | % dx = (0:6)'*0.5; 5 | % dy = (0:2)'*0.5; 6 | % epos = [repelem(dy',7);repmat(dx',1,3)]; 7 | % epos = [repelem(dx',3);repmat(dy',1,7)]; 8 | % 9 | % %1st row are y coords, 2nd row are x-coords 10 | % epos = [repmat(dy',1,7);repelem(dx',3)]; 11 | % 12 | % epos = 2*epos; 13 | 14 | [D,Du] = darray(epos); 15 | 16 | dia = -flipud(Du); 17 | 18 | minx = min(real(Du)); 19 | maxx = max(real(Du)); 20 | miny = min(imag(Du)); 21 | maxy = max(imag(Du)); 22 | 23 | %Dux = unique(real(Du)); 24 | %Duy = unique(imag(Du)); 25 | 26 | Nd = length(Du); 27 | Ra = complex(zeros(Nd)); 28 | for q= 1:Nd 29 | for r = q:Nd 30 | i = Nd+q-r; 31 | Ra(q,r) = mean(R(Du(i)==D)); 32 | end 33 | end 34 | 35 | Ra = triu(Ra,1)+Ra'; 36 | end -------------------------------------------------------------------------------- /matlabclient/functions/darray.m: -------------------------------------------------------------------------------- 1 | function [diff,diffuniq] = darray(epos) 2 | %2D difference co-array from Matlab steervec compatible element- 3 | %position vector epos 4 | 5 | %outputs gaussian integer matrices diff & diffuniq 6 | % real part = x pos, imaginary part = ypos 7 | 8 | Ny = length(unique(epos(1,:))); 9 | Nx = length(unique(epos(2,:))); 10 | 11 | if(Nx*Ny ~= size(epos,2)) 12 | disp('duplicate sensor positions'); 13 | end 14 | 15 | %store differences as gaussian integers: 16 | Dc = zeros(Ny,Nx); 17 | for n=1:size(epos,2) 18 | Dc(Ny-epos(1,n),epos(2,n)+1) = floor(epos(2,n) + 1j*epos(1,n)); 19 | end 20 | 21 | Dct = Dc; 22 | 23 | diff = NaN(Ny,Nx,Ny*Nx); 24 | for ix = 1:Nx 25 | for iy = 1:Ny 26 | diff(:,:,Ny*(ix-1)+iy) = Dc(iy,ix) - Dct; 27 | end 28 | end 29 | 30 | diffuniq = unique(diff); 31 | 32 | %scatterplot(diffuniq); 33 | end -------------------------------------------------------------------------------- /matlabclient/functions/pmusic.m: -------------------------------------------------------------------------------- 1 | %Coherent-RTL-SDR 2 | % 3 | %Compute the MUSIC pseudo spectrum. 4 | % x = Ns * Nsensor input sample matrix 5 | % epos = matlab steervec() compatible element position matrix 6 | % K = number of signals 7 | % DA = 1, Do direct augmentation (co-array processing) 8 | 9 | 10 | function [P,Nx,Ny] = pmusic(x,epos,K,DA) 11 | %sample covariance mtx; 12 | Ns= size(x,1); 13 | x = x - mean(x); 14 | R = x'*x/Ns; 15 | 16 | Nx = numel(unique(epos(2,:))); 17 | Ny = numel(unique(epos(1,:))); 18 | 19 | %direct augmentation: 20 | if (DA) 21 | [~,Du] = darray(2*epos); 22 | [R,~,Du] = DA2D(R,2*epos); 23 | Nx = numel(unique(real(Du))); 24 | Ny = numel(unique(imag(Du))); 25 | end 26 | [U,~,~] = svd(R); 27 | Un = U(:,(K+1):end); 28 | 29 | alphas = -90:90; betas = -90:90; 30 | for alpha=alphas 31 | for beta = betas 32 | ang = [beta;alpha]; 33 | if(DA) 34 | a = steervec(0.5*D2Dtoepos(Du),ang); 35 | else 36 | a = steervec(epos,ang); 37 | end 38 | P(alpha+91,beta+91) = (vecnorm(a)/vecnorm(Un'*a))^2; 39 | end 40 | end 41 | P = P*(1/max(max(P))); 42 | end 43 | -------------------------------------------------------------------------------- /matlabclient/functions/pmusicplot.m: -------------------------------------------------------------------------------- 1 | %Coherent-RTL-SDR 2 | % 3 | %Plot the MUSIC pseudo spectrum. 4 | % x = Ns * Nsensor input sample matrix 5 | % epos = matlab steervec() compatible element position matrix 6 | % K = number of signals 7 | % DA = 1, Do direct augmentation (co-array processing) 8 | 9 | 10 | function pmusicplot(x,epos,K,DA) 11 | miny = -20; 12 | %sample covariance mtx; 13 | Ns= size(x,1); 14 | x = x - mean(x); 15 | R = x'*x/Ns; 16 | 17 | Nx = numel(unique(epos(2,:))); 18 | Ny = numel(unique(epos(1,:))); 19 | 20 | %direct augmentation: 21 | if (DA) 22 | [~,Du] = darray(2*epos); 23 | [R,~,Du] = DA2D(R,2*epos); 24 | Nx = numel(unique(real(Du))); 25 | Ny = numel(unique(imag(Du))); 26 | end 27 | [U,~,~] = svd(R); 28 | Un = U(:,(K+1):end); 29 | 30 | alphas = -90:90; betas = -90:90; 31 | for alpha=alphas 32 | for beta = betas 33 | ang = [beta;alpha]; 34 | if(DA) 35 | a = steervec(0.5*D2Dtoepos(Du),ang); 36 | else 37 | a = steervec(epos,ang); 38 | end 39 | P(alpha+91,beta+91) = (vecnorm(a)/vecnorm(Un'*a))^2; 40 | end 41 | end 42 | P = P*(1/max(max(P))); 43 | 44 | %find the peak: 45 | [m,idx] = max(P(:)); 46 | [idxx idxy] = ind2sub(size(P),idx); 47 | 48 | surf(alphas,betas,10*log10(P)); 49 | 50 | minn = 10*log10(min(P,[],'all')); 51 | ttl = sprintf('%d X %d array. Peak location: %d, %d. Nse floor: %d dB\n',Nx,Ny, ... 52 | round(idxx-91), round(idxy-91),round(minn)); 53 | title(ttl); 54 | 55 | %title([num2str(round(idxx-91)) ',' num2str(round(idxy -91))]); 56 | miny = min(minn,miny); 57 | zlim([miny 1]); 58 | end 59 | -------------------------------------------------------------------------------- /matlabclient/matsave.cc: -------------------------------------------------------------------------------- 1 | /* 2 | coherent-rtlsdr 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | coherent-rtlsdr is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with coherent-rtlsdr. If not, see . 16 | */ 17 | 18 | // Non-finished test-code for saving .mat files 19 | 20 | // copied from matlab: edit([matlabroot '/extern/examples/eng_mat/matcreat.c']); 21 | // set LD_LIBRARY_PATH: export LD_LIBRARY_PATH=/usr/local/MATLAB/R2018b/bin/glnxa64/ 22 | // commandline -L doesn't seem to cut it. 23 | // COMPILES WITH: gcc matsaver.cc -I /usr/local/MATLAB/R2018a/extern/include/ -L /usr/local/MATLAB/R2018b/bin/glnxa64/ -lmat -lmx -lzmq 24 | 25 | 26 | #include 27 | #include /* For strcmp() */ 28 | #include /* For EXIT_FAILURE, EXIT_SUCCESS */ 29 | #include 30 | #include "mat.h" 31 | 32 | #include 33 | #include 34 | 35 | #define BUFFER_LENGTH 1<<20 36 | 37 | int8_t *buffer; 38 | 39 | void *zmq_context; 40 | void *socketr; 41 | 42 | int initzmq(const char *addr, const char *port){ 43 | char addrstr[128]; 44 | int timeout = 500; 45 | 46 | 47 | zmq_context = zmq_init(1); 48 | socketr = zmq_socket(zmq_context,ZMQ_SUB); 49 | 50 | //sampledata socket: 51 | sprintf(addrstr,"tcp://%s:%s",addr,port); 52 | zmq_setsockopt(socketr, ZMQ_SUBSCRIBE, "", 0 ); 53 | zmq_setsockopt(socketr,ZMQ_RCVTIMEO,&timeout,sizeof(int)); 54 | zmq_connect(socketr,addrstr); 55 | 56 | buffer = (int8_t*) malloc(BUFFER_LENGTH); 57 | if (buffer==NULL) 58 | return -1; 59 | 60 | return 0; 61 | } 62 | 63 | int main(int argc, char **argv) { 64 | 65 | if (initzmq("127.0.0.1","5555")) 66 | return -1; 67 | 68 | int nreceived = zmq_recv(socketr,buffer,BUFFER_LENGTH, 0); 69 | 70 | if (nreceived > 0 ){ 71 | uint32_t gseq = *((uint32_t *) buffer); 72 | uint32_t c = *(((uint32_t *) buffer)+1); 73 | uint32_t r = *(((uint32_t *) buffer)+2); 74 | 75 | float scaler=1.0f/128.0f; 76 | int offset=16+sizeof(uint32_t)*c; 77 | 78 | MATFile *pmat; 79 | mxArray *pa1,*pa3; 80 | const char *file = "mattest.mat"; 81 | printf("Creating file %s...\n\n", file); 82 | pmat = matOpen(file, "w"); 83 | 84 | pa1 = mxCreateNumericMatrix(r,c,mxSINGLE_CLASS,mxCOMPLEX); 85 | 86 | time_t t = time(NULL); 87 | struct tm *tm = localtime(&t); 88 | char s[64]; 89 | strftime(s, sizeof(s), "%c", tm); 90 | pa3 = mxCreateString(s); 91 | 92 | 93 | float* outptr = (float *) mxGetComplexSingles(pa1); 94 | 95 | //convert sampledata to a matrix of complex float: 96 | for(int n=0; n< ((r*c)<<1); n++) 97 | outptr[n] = scaler*buffer[offset+n]; 98 | 99 | matPutVariable(pmat, "sampledata", pa1); 100 | matPutVariable(pmat, "time", pa3); 101 | mxDestroyArray(pa1); 102 | mxDestroyArray(pa3); 103 | matClose(pmat); 104 | } 105 | 106 | 107 | zmq_close(socketr); 108 | free(buffer); 109 | 110 | /* 111 | 112 | MATFile *pmat; 113 | mxArray *pa1, *pa2, *pa3; 114 | double data[9] = { 1.0, 4.0, 7.0, 2.0, 5.0, 8.0, 3.0, 6.0, 9.0 }; 115 | const char *file = "mattest.mat"; 116 | char str[BUFSIZE]; 117 | int status; 118 | 119 | printf("Creating file %s...\n\n", file); 120 | pmat = matOpen(file, "w"); 121 | if (pmat == NULL) { 122 | printf("Error creating file %s\n", file); 123 | printf("(Do you have write permission in this directory?)\n"); 124 | return(EXIT_FAILURE); 125 | } 126 | 127 | pa1 = mxCreateNumericMatrix(3,3,mxSINGLE_CLASS,mxCOMPLEX); 128 | if (pa1 == NULL) { 129 | printf("%s : Out of memory on line %d\n", __FILE__, __LINE__); 130 | printf("Unable to create mxArray.\n"); 131 | return(EXIT_FAILURE); 132 | } 133 | std::complex *p = (std::complex*) mxGetComplexSingles(pa1); //this gets a pointer correctly. 134 | for (int n=0;n<9;n++) 135 | p[n] = std::complex(n,n); 136 | //memcpy((void *)(mxGetPr(pa1)), (void *)data, sizeof(data)); 137 | 138 | time_t t = time(NULL); 139 | struct tm *tm = localtime(&t); 140 | char s[64]; 141 | strftime(s, sizeof(s), "%c", tm); 142 | pa3 = mxCreateString(s); 143 | //pa3 = mxCreateString("MATLAB: the language of technical computing"); 144 | if (pa3 == NULL) { 145 | printf("%s : Out of memory on line %d\n", __FILE__, __LINE__); 146 | printf("Unable to create string mxArray.\n"); 147 | return(EXIT_FAILURE); 148 | } 149 | 150 | status = matPutVariable(pmat, "sampledata", pa1); 151 | if (status != 0) { 152 | printf("%s : Error using matPutVariable on line %d\n", __FILE__, __LINE__); 153 | return(EXIT_FAILURE); 154 | } 155 | 156 | status = matPutVariable(pmat, "time", pa3); 157 | if (status != 0) { 158 | printf("%s : Error using matPutVariable on line %d\n", __FILE__, __LINE__); 159 | return(EXIT_FAILURE); 160 | } 161 | 162 | mxDestroyArray(pa1); 163 | 164 | mxDestroyArray(pa3); 165 | 166 | if (matClose(pmat) != 0) { 167 | printf("Error closing file %s\n",file); 168 | return(EXIT_FAILURE); 169 | } 170 | 171 | printf("Done\n"); 172 | return(EXIT_SUCCESS); 173 | */ 174 | } -------------------------------------------------------------------------------- /matlabclient/measurement_script.m: -------------------------------------------------------------------------------- 1 | %Coherent-RTL-SDR 2 | % 3 | %Matlab script for 2D beamforming measurements on a 7 X 3 element URA 4 | %868 MHz ISM band 5 | % 6 | %Receive one frame, save the data for later use, plot the 2D Music 7 | %pseudo spectrum 8 | 9 | clear all;close all; 10 | 11 | addpath('functions'); 12 | 13 | %set save filename and path prefix 14 | fpath = 'data/'; 15 | fname = 'meas'; 16 | 17 | %how many frames we save? 18 | Nframes = 3; 19 | 20 | %Matlab steervec() compatible element position matrix: 21 | dx = (0:6)'*0.5; 22 | dy = (0:2)'*0.5; 23 | epos=[repmat(dy',1,7);repelem(dx',3)]; 24 | 25 | 26 | 27 | %sdr = CZMQSDR('IPAddress','127.0.0.1'); 28 | sdr = CZMQSDR('IPAddress','130.233.136.90'); 29 | 30 | %switch on refnoise, causes phasecoefficients to recalibrate, wait and 31 | %disable 32 | %sdr(); % receive one frame, otherwise the sysobj is not initialized. 33 | %sdr.enablerefnoise(true); 34 | %pause(0.5); 35 | %sdr.enablerefnoise(false); 36 | %pause(0.5); 37 | %reinit, otherwise we get old data from zmq buffers. 38 | %release(sdr); 39 | %sdr = CZMQSDR('IPAddress','127.0.0.1'); 40 | 41 | %receive frames 42 | X=[]; 43 | for n=1:Nframes 44 | [Xc,gseq(n),seq(n,:)] = sdr(); 45 | X =[X;Xc]; 46 | end 47 | 48 | %save and close 49 | spth = spath(fpath,fname); 50 | fprintf('SAVING RECEIVED MATRIX TO FILE: %s \n',spth); 51 | save(spth,'X','gseq','seq'); 52 | release(sdr); 53 | 54 | %2d DOA plot 55 | x = X(:,2:end); 56 | x = x - mean(x); 57 | pmusicplot(x,epos,1,1); 58 | figure; 59 | pmusicplot(x,epos,1,0); 60 | 61 | % filename with a running counter, returns a filename&path where 62 | % the filename is : highest fname + 1 63 | function [o] = spath(fpath,fname) 64 | try 65 | lst = ls([fpath '*' fname '*']); 66 | k = strfind(lst,fname); 67 | nmax = 0; 68 | 69 | for nn=1:length(k) 70 | nstart = k(nn)+length(fname); 71 | k2 = strfind(lst(nstart:end),'.mat'); 72 | nend = k2(1)-2; 73 | nvec(nn) = str2num(lst(nstart:nstart + nend)); 74 | end 75 | 76 | o = [fpath fname num2str(max(nvec)+1) '.mat']; 77 | catch ME 78 | o = [fpath fname '0.mat']; 79 | end 80 | end -------------------------------------------------------------------------------- /matlabclient/noiseswitch.m: -------------------------------------------------------------------------------- 1 | %Coherent-RTL-SDR 2 | % 3 | %Validate the operation of reference noise switching from Matlab 4 | 5 | 6 | clear all;close all; 7 | names = {'ref','s0','s1'}; 8 | %ZMQ client for sampledata, a matlab system object: 9 | sdr = CZMQSDR('IPAddress','127.0.0.1'); 10 | 11 | % receive one frame and throw it away. This is currently 12 | % necessary, otherwise the system object does not initialize 13 | % properly and will result in crash, when enablerefnoise is called 14 | % before receiving. 15 | sdr(); 16 | 17 | %disable reference noise 18 | sdr.enablerefnoise(false); 19 | 20 | recsmp =[]; 21 | %loop, switch noise on for ~10 frames 22 | for n=1:100 23 | if(n==10) 24 | sdr.enablerefnoise(true); 25 | end 26 | if(n==19) 27 | sdr.enablerefnoise(false); 28 | end 29 | %receive 30 | [rec,gseq,seq]=sdr(); 31 | recsmp = [recsmp; rec]; 32 | end 33 | release(sdr); 34 | 35 | %plot pwr w.r.t. time: 36 | M = size(rec,2); 37 | for n=1:M 38 | subplot(M,1,n); 39 | plot(recsmp(:,n).*conj(recsmp(:,n))) 40 | title(['s' num2str(n-1)]); 41 | ylabel('pwr'); 42 | xlabel('n [sample index]'); 43 | xlim([0,size(recsmp,1)-1]); 44 | end 45 | -------------------------------------------------------------------------------- /matlabclient/notes.m: -------------------------------------------------------------------------------- 1 | %Just some random tests... 2 | 3 | clear all; close all; 4 | N=10000; 5 | x=1000*(randn(N,1) +j*randn(N,1)); 6 | %x= sin(2*pi*20*0:N); 7 | %y=randn(N,1) +j*randn(N,1); 8 | 9 | ford = 8; 10 | 11 | k=1; 12 | del=(0:0.01:0.5); 13 | for k=1:length(del) 14 | d = fdesign.fracdelay(del(k),'N',ford); 15 | hd = design(d,'lagrange','filterstructure','farrowfd'); 16 | 17 | y1 = filter(hd,x); 18 | y1 = y1(ford/2+1:end); 19 | x = x(1:end-ford/2-1); 20 | %y1 = x; 21 | 22 | c = xcorr(x,y1); 23 | 24 | %plot(abs(c)); 25 | cmax= c(N-1); 26 | csq = c.*conj(c); 27 | [m,idx] = max(csq); 28 | cre = real(c); 29 | %c = sqrt(real(c)); 30 | ang = angle(c(idx))/(2*pi); 31 | 32 | a= - cre(idx-1) - 2*cre(idx) + cre(idx+1); 33 | b= - 0.5*cre(idx-1) +0.5*cre(idx+1); 34 | D(k)=b/a; 35 | end 36 | %% 37 | plot(del,D*2.8669); 38 | X=[ones(length(D),1) D']; 39 | Y=del'; 40 | X\Y 41 | 42 | %fprintf('inverse interpol: %f, angle %f, %f\n',D*(1+pi/2),ang/D,del/D); 43 | %fprintf('%f\n',(real(c(idx))/imag(c(idx)))/N); 44 | %plot(real(x(1:20))) 45 | %hold on 46 | %plot(real(y1(1:20))) -------------------------------------------------------------------------------- /matlabclient/phasecorrectionplot.m: -------------------------------------------------------------------------------- 1 | %Coherent-RTL-SDR 2 | % 3 | %Matlab script for checking the phase correction. 4 | 5 | %Read output from the phase correction debug port. needs plain zmq, get: 6 | %zeromq-matlab 7 | %clone https://github.com/smcgill3/zeromq-matlab 8 | %build ( run make), if succesfuly, copy the resulting zmq.mexa64 to 9 | %your matlab path. 10 | clear all; close all; 11 | 12 | %set the time to capture data: 13 | runforminutes=30; 14 | 15 | FESR=1024000; 16 | FESR=1e6; 17 | L= 2^14; 18 | tb=L*(1/FESR); 19 | 20 | 21 | 22 | mq = zmq('subscribe', 'tcp', '127.0.0.1', 5557); 23 | loop_n=round(runforminutes*60/tb); 24 | 25 | recz=[]; 26 | for n=1:loop_n 27 | pcorr = zmq('receive', mq); 28 | re_im = reshape(typecast(pcorr, 'single'), 2, length(pcorr)/8); 29 | z = complex(re_im(1,:), re_im(2,:)); 30 | 31 | recz=[recz; z]; 32 | end 33 | 34 | %% 35 | %load('onehour.mat'); 36 | t=(0:(length(recz)-1))*tb/60; 37 | p=180/pi*angle(recz(:,1:end)); 38 | 39 | %try linear regression plots 40 | % for nn=1:size(p,2) 41 | % b(nn,:)=polyfit(t.',p(:,nn),1); 42 | % lr(:,nn) = b(nn,1).*t+b(nn,2); 43 | % end 44 | 45 | plot(t,p); 46 | hold on; 47 | %plot(t,lr,'k'); 48 | xlabel('time [min]','FontSize',24); 49 | ylabel('phasecorrection [degree]','FontSize',24); 50 | xlim([0 runforminutes]); 51 | hold off; 52 | 53 | %plot deviations 54 | % devs = std(p) %sqrt(var(p)) 55 | % figure 56 | % bar(devs) 57 | % xlabel('dongle #'); 58 | % ylabel('stdev'); 59 | % 60 | % %plot regression coeff 61 | % figure 62 | % bar(b(:,1)) 63 | % ylim([-2,2]); 64 | % xlabel('dongle #'); 65 | % ylabel('rad/min'); 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /matlabclient/seqnum_and_correlation.m: -------------------------------------------------------------------------------- 1 | %Coherent-RTL-SDR 2 | % 3 | %Matlab script to validate timing synchronization. 4 | % 5 | %Record a number of frames, plot correlations to reference noise 6 | %on channel 0 (=first column of X); 7 | clear all; close all; 8 | 9 | nframes = 10; 10 | ymax = 2000; 11 | 12 | %open the device and read samples 13 | %sdr = CZMQSDR('IPAddress','127.0.0.1'); 14 | sdr = CZMQSDR('IPAddress','130.233.136.90'); 15 | 16 | for n=1:nframes 17 | [X(:,:,n),gseq(n),seq(n,:)]=sdr(); 18 | end 19 | 20 | release(sdr); 21 | 22 | %% PLOT the cross-correlations: 23 | % when synchronized, the channel should have a noticeable peak in the 24 | % center. Top left is the reference noise ch and thus contains 25 | %no useful info. 26 | 27 | Np = size(X(:,:,1),2); 28 | Nr = round(Np/4); 29 | 30 | figure('units','normalized','outerposition',[0 0 1 1]); 31 | for n=1:nframes 32 | for k=1:Np 33 | subplot(Nr,4,k); 34 | c=xcorr(X(:,1,n),X(:,k,n)); 35 | stem(c.*conj(c)); 36 | xlim([0 (2*size(X,1)-1)]); 37 | ylim([0 ymax]); 38 | 39 | %PAPR. Crest factor could also be used. 40 | PAPR = abs(max(c))^2/rms(c)^2; 41 | title(['seq: ' num2str(seq(n,k)) ' PAPR: ' num2str(PAPR)]); 42 | end 43 | pause(0.01); 44 | end 45 | -------------------------------------------------------------------------------- /matlabclient/testchannels.m: -------------------------------------------------------------------------------- 1 | %Coherent-RTL-SDR 2 | % 3 | %Matlab script demonstrating CZMQSDR 4 | 5 | %Receive samples, examine spectrum (in realtime if possible, but zmq- 6 | %buffers can and will stretch if matlab polls the socket too slowly). 7 | 8 | clear all;close all; 9 | 10 | nframes = 10; 11 | 12 | sdr = CZMQSDR('IPAddress','127.0.0.1'); 13 | FESR = 1e6; % 2048000; 14 | scope = dsp.SpectrumAnalyzer(... 15 | 'Name', 'Spectrum',... 16 | 'Title', 'Spectrum', ... 17 | 'SpectrumType', 'Power',... 18 | 'FrequencySpan', 'Full', ... 19 | 'SampleRate', FESR, ... 20 | 'YLimits', [-50,5],... 21 | 'SpectralAverages', 50, ... 22 | 'FrequencySpan', 'Start and stop frequencies', ... 23 | 'StartFrequency', -FESR/2, ... 24 | 'StopFrequency', FESR/2); 25 | 26 | for n=1:nframes 27 | [x,gseq,seq]=sdr(); 28 | x = x - mean(x); 29 | scope(x(:,2:end)); %ch 1 is reference noise, exclude it from f.scope 30 | end 31 | 32 | release(sdr); -------------------------------------------------------------------------------- /matlabclient/testclient.m: -------------------------------------------------------------------------------- 1 | %Coherent-RTL-SDR 2 | % 3 | %Matlab script used in testing the performance of C vs. C++ MEX interfaces 4 | %The result showed: C++ interface is hideously slow, and will cripple 5 | %the receiver when number of channels grows. 6 | 7 | %Stick to C MEX when high volumes of data are moved, unless ofcourse, 8 | %there is a trick to speed things up considerably in the new matlab 9 | %C++ API. 10 | 11 | clear all;close all; 12 | 13 | sdr = CZMQSDR('IPAddress','127.0.0.1'); 14 | %sdr = CZMQSDR('IPAddress','10.10.10.10'); 15 | 16 | recs = ones(1,22); 17 | FESR = 1e6; % 2048000; 18 | scope = dsp.SpectrumAnalyzer(... 19 | 'Name', 'Spectrum',... 20 | 'Title', 'Spectrum', ... 21 | 'SpectrumType', 'Power',... 22 | 'FrequencySpan', 'Full', ... 23 | 'SampleRate', FESR, ... 24 | 'YLimits', [-50,5],... 25 | 'SpectralAverages', 50, ... 26 | 'FrequencySpan', 'Start and stop frequencies', ... 27 | 'StartFrequency', -500e3, ... 28 | 'StopFrequency', 500e3); 29 | rec=[]; 30 | 31 | tic; 32 | t1=toc; 33 | recsmp =[]; 34 | for n=1:100 35 | if(n==10) 36 | sdr.enablerefnoise(true); 37 | end 38 | if(n==20) 39 | sdr.enablerefnoise(false); 40 | end 41 | 42 | [rec,gseq,seq]=sdr(); 43 | recsmp = [recsmp; rec]; 44 | end 45 | t2=toc; 46 | fprintf('time was %f \n', t2-t1); 47 | fprintf('nominal time should be %4.2f, diff: %4.0f percent \n', 1/(FESR)*size(rec,1)*n, (t2-t1)/(1/FESR*size(rec,1)*n)*100); 48 | 49 | release(sdr); % this needs to be called. otherwise data can be relatively old. 50 | %% 51 | %mq = zmq('subscribe', 'tcp', '127.0.0.1', 5555); 52 | %pcorr = zmq('receive', mq); 53 | 54 | -------------------------------------------------------------------------------- /matlabclient/zmqsdr.c: -------------------------------------------------------------------------------- 1 | /* 2 | coherent-rtlsdr 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | coherent-rtlsdr is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with coherent-rtlsdr. If not, see . 16 | */ 17 | 18 | #include 19 | #include "mex.h" 20 | #include "matrix.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #define BUFFER_LENGTH 1<<20 27 | #define COMMANDBUFFER_LENGTH 256 28 | 29 | void *zmq_context; 30 | void *socketr, *socketc; 31 | 32 | int8_t *buffer; 33 | char *commandbuf; 34 | 35 | uint32_t rows,cols; 36 | 37 | 38 | typedef struct args_t{ 39 | char op; 40 | uint32_t centerfreq; 41 | char *ip,*port,*cport; 42 | }args_t; 43 | 44 | void parseargs(args_t *args,int nrhs ,const mxArray *prhs[]){ 45 | if (nrhs > 0){ 46 | args->op = mxArrayToString(prhs[0])[0]; 47 | } 48 | if ((nrhs>1) && (args->op=='t')){ 49 | args->centerfreq = *mxGetDoubles(prhs[1]); 50 | } 51 | if (nrhs==5){ 52 | args->ip = mxArrayToString(prhs[2]); 53 | args->port = mxArrayToString(prhs[3]); 54 | args->cport=mxArrayToString(prhs[4]); 55 | } 56 | } 57 | 58 | int initzmq(char *addr, char *port, char *cport){ 59 | char addrstr[128]; 60 | int timeout = 500; 61 | 62 | 63 | //mexPrintf("%s\n",addrstr); 64 | 65 | zmq_context = zmq_init(1); 66 | socketr = zmq_socket(zmq_context,ZMQ_SUB); 67 | 68 | //sampledata socket: 69 | sprintf(addrstr,"tcp://%s:%s",addr,port); 70 | zmq_setsockopt(socketr, ZMQ_SUBSCRIBE, "", 0 ); 71 | zmq_setsockopt(socketr,ZMQ_RCVTIMEO,&timeout,sizeof(int)); 72 | zmq_connect(socketr,addrstr); 73 | 74 | //control socket: 75 | sprintf(addrstr,"tcp://%s:%s",addr,cport); 76 | socketc = zmq_socket(zmq_context,ZMQ_DEALER); 77 | zmq_connect(socketc,addrstr); 78 | 79 | buffer = (int8_t*) malloc(BUFFER_LENGTH); 80 | if (buffer==NULL) 81 | return -1; 82 | 83 | commandbuf = (char*) malloc(COMMANDBUFFER_LENGTH); 84 | if (commandbuf==NULL) 85 | return -1; 86 | 87 | return 0; 88 | } 89 | 90 | void cleanup(void){ 91 | free(buffer); 92 | free(commandbuf); 93 | zmq_close(socketr); 94 | zmq_close(socketc); 95 | zmq_term(zmq_context); 96 | } 97 | 98 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { 99 | 100 | uint32_t r=8192; 101 | uint32_t c=22; 102 | 103 | args_t args; 104 | //mexPrintf("parsing..."); 105 | parseargs(&args,nrhs,prhs); 106 | //mexPrintf("found %c \n",args.op); 107 | 108 | switch(args.op){ 109 | case 'i': 110 | plhs[0] = mxCreateNumericMatrix(1,1,mxINT32_CLASS,mxREAL); 111 | int32_t* ret = (int32_t *) mxGetPr(plhs[0]); 112 | *ret = initzmq(args.ip,args.port,args.cport); 113 | mexPrintf("Initialized zmq, %s:%s, buffers allocated\n",args.ip,args.port); 114 | //mexAtExit(cleanup); //would cause double free in case we already free from the matlab system object 115 | break; 116 | case 'r':{ 117 | 118 | int nreceived = zmq_recv(socketr,buffer,BUFFER_LENGTH, 0); 119 | 120 | if (nreceived > 0 ){ 121 | uint32_t gseq = *((uint32_t *) buffer); 122 | uint32_t c = *(((uint32_t *) buffer)+1); 123 | uint32_t r = *(((uint32_t *) buffer)+2); 124 | 125 | float scaler=1.0f/128.0f; 126 | int offset=16+sizeof(uint32_t)*c; 127 | 128 | plhs[0] = mxCreateNumericMatrix(r,c,mxSINGLE_CLASS,mxCOMPLEX); 129 | 130 | //we take the pointer as float, even though our data is interpreted as interleaved complex 131 | float* outptr = (float *) mxGetComplexSingles(plhs[0]); 132 | 133 | //convert sampledata to a matrix of complex float: 134 | for(int n=0; n< ((r*c)<<1); n++) 135 | outptr[n] = scaler*buffer[offset+n]; 136 | 137 | //fill sequence numbers: 138 | plhs[1] = mxCreateDoubleScalar(gseq); 139 | plhs[2] = mxCreateNumericMatrix(1,c,mxUINT32_CLASS,mxREAL); 140 | uint32_t* seqptr = (uint32_t *)mxGetPr(plhs[2]); 141 | 142 | for(int n=0;n. 16 | */ 17 | 18 | //Coherent RTL-SDR mex interface for matlab. The C++ API IS SLOW, 19 | //(OR I'M TOO STUPID TO USE IT RIGHT). DO NOT USE THIS, VOUCH FOR PLAIN C. 20 | 21 | #include 22 | #include 23 | #include "mex.hpp" 24 | #include "mexAdapter.hpp" 25 | #include 26 | 27 | using matlab::mex::ArgumentList; 28 | using namespace matlab::data; 29 | using matlab::engine::convertUTF8StringToUTF16String; 30 | using matlab::engine::convertUTF16StringToUTF8String; 31 | 32 | 33 | const int timeout_ = 1000; 34 | 35 | const uint32_t CONTROLMSG_MAGIC=(('C'<<24) & ('T'<<16) & ('R'<<8) & 'L'); 36 | const uint32_t CONTROLMSG_SETFCENTER=0x01; 37 | const uint32_t CONTROLMSG_QUERYLAGS =0x02; 38 | 39 | static bool initialized=false; 40 | 41 | class MexFunction : public matlab::mex::Function { 42 | private: 43 | zmq::context_t context; 44 | zmq::socket_t socketr, socketc; 45 | zmq::message_t msgr; 46 | std::string addressr; 47 | std::string addressc; 48 | 49 | std::shared_ptr matlabPtr; 50 | std::ostringstream stream; 51 | ArrayFactory f; 52 | 53 | uint64_t rows,cols; //complaints about narrowing conv. matlab stores matrix sizes in long unsigned int... 54 | struct args{ 55 | char op; 56 | uint32_t centerfreq; 57 | std::string ip,port,cport; 58 | }args; 59 | //matlab::data::TypedArray> *out; 60 | //TypedArray> *out; 61 | 62 | void resizematrix(uint64_t r, uint64_t c){ 63 | rows = r; 64 | cols = c; 65 | //mexUnlock(); 66 | //outmatrix->release(); //clear old 67 | //*out = f.createArray>({rows,cols},{0}); 68 | //mexLock(); 69 | } 70 | 71 | public: 72 | 73 | MexFunction(): context(1),socketr(context,ZMQ_SUB),socketc(context,ZMQ_DEALER){ 74 | matlabPtr = getEngine(); 75 | rows=2; 76 | cols=2; 77 | //mexLock(); 78 | //*outmatrix = f.createArray>({rows,cols},{0}); //this will crash? lock? 79 | } 80 | ~MexFunction(){ 81 | //mexUnlock(); 82 | //outmatrix->release(); 83 | } 84 | 85 | void convoutputmtx(int8_t *src, matlab::data::TypedArray> &dest){ 86 | float scaler = (1.0f/128.0f); 87 | int n=0; 88 | 89 | //this iterator approach is slow... 90 | for(auto d : dest){ 91 | d = scaler * std::complex(((int8_t)(src[n])),((int8_t)(src[n + 1]))); 92 | n+=2; 93 | } 94 | } 95 | 96 | void convoutputmtxraw(int8_t *src, std::complex * dest, int N){ 97 | float scaler = (1.0f/128.0f); 98 | for(int n=0;n(((int8_t)(src[n])),((int8_t)(src[n + 1]))); 100 | } 101 | 102 | void operator()(ArgumentList outputs, ArgumentList inputs) { 103 | 104 | parseArguments(inputs); 105 | 106 | 107 | switch (args.op) { 108 | case 'i': 109 | initzmq(args.ip,args.port,args.cport); 110 | outputs[0] = f.createScalar(1); 111 | break; 112 | case 'r': 113 | { 114 | if (socketr.recv(&msgr)==EAGAIN){ 115 | outputs[0] = f.createScalar(-1); 116 | 117 | } 118 | else{ 119 | int8_t *data = static_cast(msgr.data()); 120 | 121 | uint32_t gseq = *((uint32_t *) data); 122 | uint32_t c = *(((uint32_t *) data)+1); 123 | uint32_t r = *(((uint32_t *) data)+2); 124 | 125 | float scaler=1.0f/128.0f; 126 | int offset=16+sizeof(uint32_t)*cols; 127 | int n=0; 128 | 129 | if (inputs.size()>1){ 130 | ArrayDimensions dimensions = inputs[1].getDimensions(); 131 | 132 | if ((dimensions[0]==r) && (dimensions[1]==c)){ 133 | matlab::data::TypedArray> out = std::move(inputs[1]); 134 | convoutputmtx(data+offset,out); 135 | //int N = out.getNumberOfElements(); 136 | //auto outptr = out.release(); 137 | //convoutputmtxraw(data+offset,outptr.get(),N); 138 | //out = f.createArrayFromBuffer[]>(dimensions,outptr); 139 | 140 | outputs[0] = std::move(out); //sampledata 141 | } 142 | else{ 143 | matlab::data::TypedArray> out = f.createArray>({r,c},{0}); //if output size does not match, allocate a new matrix. 144 | convoutputmtx(data+offset,out); 145 | outputs[0] = std::move(out); //sampledata 146 | } 147 | } 148 | else{ 149 | matlab::data::TypedArray> out = f.createArray>({r,c},{0}); //if output size does not match, allocate a new matrix. 150 | convoutputmtx(data+offset,out); 151 | outputs[0] = std::move(out); //sampledata 152 | } 153 | 154 | //create the channel readcount vector: 155 | matlab::data::TypedArray seq = f.createArray({1,c},{0}); 156 | for (int n=0;n(gseq); //global sequence num 161 | outputs[2] = std::move(seq); //channel sequences... 162 | 163 | } 164 | } 165 | break; 166 | case 't': 167 | stream << "retuning to" << std::to_string(args.centerfreq) << std::endl; 168 | displayOnMATLAB(stream); 169 | retune(args.centerfreq); 170 | break; 171 | case 'c': 172 | 173 | break; 174 | default: 175 | break; 176 | } 177 | 178 | } 179 | 180 | void retune(uint32_t fcenter){ 181 | zmq::message_t message(sizeof(uint32_t)*3); 182 | uint32_t *msg = static_cast(message.data()); 183 | 184 | msg[0]=CONTROLMSG_MAGIC; 185 | msg[1]=CONTROLMSG_SETFCENTER; 186 | msg[2]=fcenter; 187 | 188 | socketc.send(message); 189 | } 190 | 191 | void initzmq(std::string ip, std::string p, std::string cp) { //, std::string port, std::string cport){ 192 | 193 | std::string addr = "tcp://" + ip + ":" + p; 194 | std::string caddr= "tcp://" + ip + ":" + cp; 195 | 196 | stream << "receiver address:" << addr << std::endl; 197 | displayOnMATLAB(stream); 198 | stream << "control address:" << caddr << std::endl; 199 | displayOnMATLAB(stream); 200 | 201 | int timeout=timeout_; 202 | 203 | socketr.setsockopt(ZMQ_RCVTIMEO,&timeout,sizeof(int)); 204 | socketr.connect(addr.data()); 205 | 206 | socketr.setsockopt(ZMQ_SUBSCRIBE,"",0); 207 | 208 | socketc.connect(caddr.data()); 209 | } 210 | 211 | // read inputs into struct args, perform some checking against invalid input: 212 | void parseArguments(ArgumentList inputs){ 213 | 214 | int numargs = inputs.size(); 215 | 216 | if (numargs>0){ 217 | if(inputs[0].getType()!= ArrayType::CHAR) 218 | matlabPtr->feval(u"error",0, std::vector({f.createScalar("Incorrect input 0, operation char ('i','r','t','c').")})); 219 | const CharArray oper = inputs[0]; 220 | args.op = oper[0]; 221 | } 222 | if (numargs>1){ 223 | /* if(inputs[1].getType()!= ArrayType::DOUBLE) 224 | matlabPtr->feval(u"error",0, std::vector({f.createScalar("Incorrect input: Center frequency should be double type")})); 225 | TypedArray cfreq = inputs[1]; 226 | if (!cfreq.isEmpty()) 227 | args.centerfreq = cfreq[0];*/ 228 | } 229 | if (numargs==5){ 230 | if ((inputs[2].getType()!= ArrayType::CHAR)||(inputs[3].getType()!= ArrayType::CHAR)||(inputs[4].getType()!= ArrayType::CHAR)) 231 | matlabPtr->feval(u"error",0, std::vector({f.createScalar("Incorrect input: ip, port & control port should be char strings")})); 232 | 233 | args.ip = std::string(((CharArray) inputs[2]).toAscii()); 234 | args.port = std::string(((CharArray) inputs[3]).toAscii()); 235 | args.cport = std::string(((CharArray) inputs[4]).toAscii()); 236 | } 237 | 238 | } 239 | 240 | //from matlab examples: 241 | void displayOnMATLAB(std::ostringstream& stream) { 242 | // Pass stream content to MATLAB fprintf function 243 | matlabPtr->feval(u"fprintf", 0, 244 | std::vector({ f.createScalar(stream.str()) })); 245 | // Clear stream buffer 246 | stream.str(""); 247 | } 248 | }; 249 | -------------------------------------------------------------------------------- /matlabclient/zmqsdr.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlaaks/coherent-rtlsdr/1c861ad2828f0cbcac7f56fc094f1272bc7ee787/matlabclient/zmqsdr.mexa64 -------------------------------------------------------------------------------- /refnoisefirmware/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | 3 | project(REFNOISEFW C ASM) 4 | 5 | set(CMAKE_ASM_COMPILER arm-none-eabi-as) 6 | set(CMAKE_C_COMPILER arm-none-eabi-gcc) 7 | 8 | 9 | #optimization and architecture: 10 | set(OPT "-Os") 11 | set(ARCH_FLAGS "-mcpu=cortex-m3 -mthumb -msoft-float -mfix-cortex-m3-ldrd") 12 | SET(SECT_FLAGS "-fno-common -ffunction-sections -fdata-sections") 13 | 14 | 15 | add_definitions(-DSTM32F1) 16 | 17 | #linker defs: 18 | set(LIBOPENCM3_DIR ${CMAKE_SOURCE_DIR}/libopencm3) 19 | 20 | set(LINKERSCRIPT ${CMAKE_SOURCE_DIR}/stm32-bluepill.ld) 21 | set(LINKERSCRIPTB ${CMAKE_SOURCE_DIR}/stm32-bluebootld.ld) 22 | set(LINKERSCRIPTDFU ${CMAKE_SOURCE_DIR}/stm32-bluepilldfu.ld) 23 | 24 | set(LD_FLAGS "--static -nostartfiles -L${LIBOPENCM3_DIR}/lib/ -Wl,--gc-sections -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group") 25 | LINK_DIRECTORIES(${LIBOPENCM3_DIR}/lib) 26 | 27 | 28 | #includes & sources 29 | include_directories( 30 | ${LIBOPENCM3_DIR}/include 31 | ) 32 | 33 | set(FWSOURCES fw.c) 34 | set(BLSOURCES bootloader.c) 35 | set(DFUAPPSOURCES usbdfu.c) 36 | 37 | 38 | #build 39 | add_executable(REFNOISEFW ${FWSOURCES}) 40 | add_executable(BOOTLOADER ${BLSOURCES}) 41 | add_executable(DFUAPP ${DFUAPPSOURCES}) 42 | 43 | #output defs, flags, for firmware: 44 | set_target_properties(REFNOISEFW PROPERTIES OUTPUT_NAME "refnoisefw.elf") 45 | target_link_libraries(REFNOISEFW opencm3_stm32f1 "-T${LINKERSCRIPT}") 46 | set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS) #-rdynamic 47 | set(CMAKE_C_LINK_FLAGS) 48 | set(CMAKE_EXE_LINKER_FLAGS "${LD_FLAGS}") 49 | set(CMAKE_C_FLAGS "${OPT} ${ARCH_FLAGS} ${SECT_FLAGS}") 50 | 51 | #same for bootloader 52 | set_target_properties(BOOTLOADER PROPERTIES OUTPUT_NAME "bootloader.elf") 53 | target_link_libraries(BOOTLOADER opencm3_stm32f1 "-T${LINKERSCRIPTB}") 54 | 55 | #same for dfuapp 56 | set_target_properties(DFUAPP PROPERTIES OUTPUT_NAME "dfuapp.elf") 57 | target_link_libraries(DFUAPP opencm3_stm32f1 "-T${LINKERSCRIPTDFU}") 58 | 59 | #convert both to binaries, could also do ihex format... 60 | add_custom_command(TARGET REFNOISEFW 61 | POST_BUILD 62 | COMMAND arm-none-eabi-objcopy -O binary refnoisefw.elf refnoisefw.bin 63 | COMMAND arm-none-eabi-objcopy -O ihex refnoisefw.elf refnoisefw.hex 64 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/build 65 | ) 66 | 67 | add_custom_command(TARGET BOOTLOADER 68 | POST_BUILD 69 | COMMAND arm-none-eabi-objcopy -O binary bootloader.elf bootloader.bin 70 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/build 71 | ) 72 | 73 | add_custom_command(TARGET DFUAPP 74 | POST_BUILD 75 | COMMAND arm-none-eabi-objcopy -O binary dfuapp.elf dfuapp.bin 76 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/build) 77 | 78 | add_custom_target(flash 79 | COMMENT *FLASHING FW, BOOTLOADER & DFUAPP* 80 | COMMAND st-flash write refnoisefw.bin 0x08002000 81 | COMMAND st-flash write bootloader.bin 0x08000000 82 | COMMAND st-flash write dfuapp.bin 0x08000400 83 | DEPENDS REFNOISEFW 84 | ) 85 | 86 | add_custom_target(flash_bootloader 87 | COMMENT *FLASHING_BOOTLOADER & DFUAPP* 88 | COMMAND st-flash write bootloader.bin 0x08000000 89 | DEPENDS REFNOISEFW 90 | ) 91 | 92 | add_custom_target(dfu_flash 93 | COMMENT *FLASHING_FW_VIA_USB_DFU* 94 | COMMAND ../enter_dfu.sh 95 | COMMAND dfu-util -d 0483:df11 -a 0 -s 0x08002000:leave -D refnoisefw.bin 96 | ) -------------------------------------------------------------------------------- /refnoisefirmware/Makefile: -------------------------------------------------------------------------------- 1 | ## 2 | ## This file is part of the libopencm3 project. 3 | ## 4 | ## Copyright (C) 2009 Uwe Hermann 5 | ## 6 | ## This library is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU Lesser General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This library is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU Lesser General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU Lesser General Public License 17 | ## along with this library. If not, see . 18 | ## 19 | 20 | OPENCM3_DIR = /home/mikko/source/libopencm3 21 | 22 | BINARY = cdcacm 23 | 24 | LDSCRIPT = ../stm32-bluepill.ld 25 | 26 | include ../../Makefile.include 27 | 28 | -------------------------------------------------------------------------------- /refnoisefirmware/bootloader.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the libopencm3 project. 3 | * 4 | * Copyright (C) 2010 Gareth McMullin 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | #include 20 | #include 21 | 22 | #define APP_ADDRESS 0x08002000 23 | 24 | int main(void) 25 | { 26 | //rcc_periph_clock_enable(RCC_GPIOA); 27 | 28 | //Boot the application if it's valid. 29 | if ((*(volatile uint32_t *)APP_ADDRESS & 0x2FFE0000) == 0x20000000) { 30 | //Set vector table base address. 31 | SCB_VTOR = APP_ADDRESS & 0xFFFF; 32 | //Initialise master stack pointer. 33 | asm volatile("msr msp, %0"::"g" 34 | (*(volatile uint32_t *)APP_ADDRESS)); 35 | //Jump to application. 36 | (*(void (**)())(APP_ADDRESS + 4))(); 37 | } 38 | } -------------------------------------------------------------------------------- /refnoisefirmware/enter_dfu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #stty -F /dev/ttyACM0 9600 -parity cs8 -cstopb 3 | #echo -e "I_AM_A_WIZARD" > /dev/ttyACM0 4 | 5 | #problem is the above command will send multi-character packets. let's send one char at a time: 6 | echo "I" > /dev/ttyACM0 7 | echo "_" > /dev/ttyACM0 8 | echo "A" > /dev/ttyACM0 9 | echo "M" > /dev/ttyACM0 10 | echo "_" > /dev/ttyACM0 11 | echo "A" > /dev/ttyACM0 12 | echo "_" > /dev/ttyACM0 13 | echo "W" > /dev/ttyACM0 14 | echo "I" > /dev/ttyACM0 15 | echo "Z" > /dev/ttyACM0 16 | echo "A" > /dev/ttyACM0 17 | echo "R" > /dev/ttyACM0 18 | echo "D" > /dev/ttyACM0 19 | sudo ./resetusb.sh -------------------------------------------------------------------------------- /refnoisefirmware/fw.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the libopencm3 project. 3 | * 4 | * Copyright (C) 2010 Gareth McMullin 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | 20 | /* Modified by mlaaks to control GPIO 21 | * The original example code can be obtained from: 22 | * 23 | * https://github.com/libopencm3/libopencm3-examples 24 | * 25 | * file: 26 | * /examples/stm32/f1/stm32-h103/usb_cdcacm/cdcacm.c 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #define DFUAPP_ADDRESS 0x08000400 41 | #define VTOR_ADDRESS 0x08000400 42 | #define JUMP_TO_APP(ADDRESS,VECTOR_TABLE){ \ 43 | static uint32_t app_address = (uint32_t)ADDRESS; \ 44 | static uint8_t *p_app = (uint8_t *) &app_address; \ 45 | static uint32_t vect_table = (uint32_t)VECTOR_TABLE; \ 46 | SCB_VTOR = vect_table & 0xFFFF; \ 47 | asm volatile("msr msp, %0"::"g"(*(volatile uint32_t *)vect_table)); \ 48 | (*(void (**)())(app_address+4))(); \ 49 | }//should we just use asm("bl app_address") here? or modify that call such that app_address is immediate, not pointer where from the address is fetched. 50 | // it was (*(void (**)())(app_address + 4))(); 51 | //function pointer syntax: 52 | // void (*fun_ptr)(int) = &fun; 53 | /* 54 | 55 | asm volatile("ldr r3, [%0]"::"r"(*(volatile uint32_t *)app_address)); \ 56 | asm volatile("blx r3");\ 57 | */ 58 | 59 | //extern uint32_t _start; 60 | static uint32_t dfuapp_address = (uint32_t) DFUAPP_ADDRESS; 61 | 62 | 63 | //magic string. When encountered, jump to DFU bootloader: 64 | static char magicstr[] = "I_AM_A_WIZARD"; 65 | static uint32_t magicctr = 0; 66 | static size_t magichappens = sizeof(magicstr)-1; 67 | 68 | static bool exit_to_dfu = false; 69 | static int exit_to_dfu_cnt = 16; 70 | 71 | static const struct usb_device_descriptor dev = { 72 | .bLength = USB_DT_DEVICE_SIZE, 73 | .bDescriptorType = USB_DT_DEVICE, 74 | .bcdUSB = 0x0200, 75 | .bDeviceClass = USB_CLASS_CDC, 76 | .bDeviceSubClass = 0, 77 | .bDeviceProtocol = 0, 78 | .bMaxPacketSize0 = 64, 79 | .idVendor = 0x0483, 80 | .idProduct = 0x5740, 81 | .bcdDevice = 0x0200, 82 | .iManufacturer = 1, 83 | .iProduct = 2, 84 | .iSerialNumber = 3, 85 | .bNumConfigurations = 1, 86 | }; 87 | 88 | /* 89 | * This notification endpoint isn't implemented. According to CDC spec its 90 | * optional, but its absence causes a NULL pointer dereference in Linux 91 | * cdc_acm driver. 92 | */ 93 | static const struct usb_endpoint_descriptor comm_endp[] = {{ 94 | .bLength = USB_DT_ENDPOINT_SIZE, 95 | .bDescriptorType = USB_DT_ENDPOINT, 96 | .bEndpointAddress = 0x83, 97 | .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, 98 | .wMaxPacketSize = 16, 99 | .bInterval = 255, 100 | }}; 101 | 102 | static const struct usb_endpoint_descriptor data_endp[] = {{ 103 | .bLength = USB_DT_ENDPOINT_SIZE, 104 | .bDescriptorType = USB_DT_ENDPOINT, 105 | .bEndpointAddress = 0x01, 106 | .bmAttributes = USB_ENDPOINT_ATTR_BULK, 107 | .wMaxPacketSize = 64, 108 | .bInterval = 1, 109 | }, { 110 | .bLength = USB_DT_ENDPOINT_SIZE, 111 | .bDescriptorType = USB_DT_ENDPOINT, 112 | .bEndpointAddress = 0x82, 113 | .bmAttributes = USB_ENDPOINT_ATTR_BULK, 114 | .wMaxPacketSize = 64, 115 | .bInterval = 1, 116 | }}; 117 | 118 | static const struct { 119 | struct usb_cdc_header_descriptor header; 120 | struct usb_cdc_call_management_descriptor call_mgmt; 121 | struct usb_cdc_acm_descriptor acm; 122 | struct usb_cdc_union_descriptor cdc_union; 123 | } __attribute__((packed)) cdcacm_functional_descriptors = { 124 | .header = { 125 | .bFunctionLength = sizeof(struct usb_cdc_header_descriptor), 126 | .bDescriptorType = CS_INTERFACE, 127 | .bDescriptorSubtype = USB_CDC_TYPE_HEADER, 128 | .bcdCDC = 0x0110, 129 | }, 130 | .call_mgmt = { 131 | .bFunctionLength = 132 | sizeof(struct usb_cdc_call_management_descriptor), 133 | .bDescriptorType = CS_INTERFACE, 134 | .bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT, 135 | .bmCapabilities = 0, 136 | .bDataInterface = 1, 137 | }, 138 | .acm = { 139 | .bFunctionLength = sizeof(struct usb_cdc_acm_descriptor), 140 | .bDescriptorType = CS_INTERFACE, 141 | .bDescriptorSubtype = USB_CDC_TYPE_ACM, 142 | .bmCapabilities = 0, 143 | }, 144 | .cdc_union = { 145 | .bFunctionLength = sizeof(struct usb_cdc_union_descriptor), 146 | .bDescriptorType = CS_INTERFACE, 147 | .bDescriptorSubtype = USB_CDC_TYPE_UNION, 148 | .bControlInterface = 0, 149 | .bSubordinateInterface0 = 1, 150 | }, 151 | }; 152 | 153 | static const struct usb_interface_descriptor comm_iface[] = {{ 154 | .bLength = USB_DT_INTERFACE_SIZE, 155 | .bDescriptorType = USB_DT_INTERFACE, 156 | .bInterfaceNumber = 0, 157 | .bAlternateSetting = 0, 158 | .bNumEndpoints = 1, 159 | .bInterfaceClass = USB_CLASS_CDC, 160 | .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, 161 | .bInterfaceProtocol = USB_CDC_PROTOCOL_AT, 162 | .iInterface = 0, 163 | 164 | .endpoint = comm_endp, 165 | 166 | .extra = &cdcacm_functional_descriptors, 167 | .extralen = sizeof(cdcacm_functional_descriptors), 168 | }}; 169 | 170 | static const struct usb_interface_descriptor data_iface[] = {{ 171 | .bLength = USB_DT_INTERFACE_SIZE, 172 | .bDescriptorType = USB_DT_INTERFACE, 173 | .bInterfaceNumber = 1, 174 | .bAlternateSetting = 0, 175 | .bNumEndpoints = 2, 176 | .bInterfaceClass = USB_CLASS_DATA, 177 | .bInterfaceSubClass = 0, 178 | .bInterfaceProtocol = 0, 179 | .iInterface = 0, 180 | 181 | .endpoint = data_endp, 182 | }}; 183 | 184 | static const struct usb_interface ifaces[] = {{ 185 | .num_altsetting = 1, 186 | .altsetting = comm_iface, 187 | }, { 188 | .num_altsetting = 1, 189 | .altsetting = data_iface, 190 | }}; 191 | 192 | static const struct usb_config_descriptor config = { 193 | .bLength = USB_DT_CONFIGURATION_SIZE, 194 | .bDescriptorType = USB_DT_CONFIGURATION, 195 | .wTotalLength = 0, 196 | .bNumInterfaces = 2, 197 | .bConfigurationValue = 1, 198 | .iConfiguration = 0, 199 | .bmAttributes = 0x80, 200 | .bMaxPower = 0x32, 201 | 202 | .interface = ifaces, 203 | }; 204 | 205 | static const char *usb_strings[] = { 206 | "COHERENT RTLSDR", 207 | "Reference noise controller:", 208 | "https://github.com/mlaaks/coherent-rtlsdr", 209 | }; 210 | 211 | /* Buffer to be used for control requests. */ 212 | uint8_t usbd_control_buffer[128]; 213 | 214 | static enum usbd_request_return_codes cdcacm_control_request(usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, 215 | uint16_t *len, void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req)) 216 | { 217 | (void)complete; 218 | (void)buf; 219 | (void)usbd_dev; 220 | if (!exit_to_dfu){ 221 | switch (req->bRequest) { 222 | case USB_CDC_REQ_SET_CONTROL_LINE_STATE: { 223 | /* 224 | * This Linux cdc_acm driver requires this to be implemented 225 | * even though it's optional in the CDC spec, and we don't 226 | * advertise it in the ACM functional descriptor. 227 | */ 228 | char local_buf[10]; 229 | struct usb_cdc_notification *notif = (void *)local_buf; 230 | 231 | /* We echo signals back to host as notification. */ 232 | notif->bmRequestType = 0xA1; 233 | notif->bNotification = USB_CDC_NOTIFY_SERIAL_STATE; 234 | notif->wValue = 0; 235 | notif->wIndex = 0; 236 | notif->wLength = 2; 237 | local_buf[8] = req->wValue & 3; 238 | local_buf[9] = 0; 239 | // usbd_ep_write_packet(0x83, buf, 10); 240 | return USBD_REQ_HANDLED; 241 | } 242 | case USB_CDC_REQ_SET_LINE_CODING: 243 | if (*len < sizeof(struct usb_cdc_line_coding)) 244 | return USBD_REQ_NOTSUPP; 245 | return USBD_REQ_HANDLED; 246 | } 247 | } 248 | return USBD_REQ_NOTSUPP; 249 | } 250 | 251 | static void noisestate(bool); 252 | static void fanstate(bool); 253 | static bool areyouawizard(uint8_t c); 254 | static void cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep) 255 | { 256 | (void)ep; 257 | (void)usbd_dev; 258 | 259 | char buf[64]; 260 | int len = usbd_ep_read_packet(usbd_dev, 0x01, buf, 64); 261 | if (!exit_to_dfu){ 262 | if (len) { 263 | switch (buf[0]) 264 | { 265 | case 'X': //enable referencenoise amps 266 | noisestate(true); 267 | break; 268 | case 'x': //disable amps 269 | noisestate(false); 270 | break; 271 | case 'F': //enable fan 272 | fanstate(true); 273 | break; 274 | case 'f': //disable fan 275 | fanstate(false); 276 | break; 277 | default: 278 | break; 279 | 280 | } 281 | if (areyouawizard(buf[0])) 282 | { 283 | uint8_t msg[]="JUMPING TO DFU-MODE"; 284 | usbd_ep_write_packet(usbd_dev, 0x82, msg, sizeof(msg)); 285 | exit_to_dfu = true; 286 | } 287 | else{ 288 | usbd_ep_write_packet(usbd_dev, 0x82, buf, len); 289 | buf[len] = 0; 290 | } 291 | } 292 | } 293 | } 294 | 295 | static void cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue) 296 | { 297 | (void)wValue; 298 | (void)usbd_dev; 299 | 300 | usbd_ep_setup(usbd_dev, 0x01, USB_ENDPOINT_ATTR_BULK, 64, cdcacm_data_rx_cb); 301 | usbd_ep_setup(usbd_dev, 0x82, USB_ENDPOINT_ATTR_BULK, 64, NULL); 302 | usbd_ep_setup(usbd_dev, 0x83, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL); 303 | 304 | usbd_register_control_callback( 305 | usbd_dev, 306 | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, 307 | USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, 308 | cdcacm_control_request); 309 | } 310 | 311 | static void noisestate(bool state){ 312 | if (state) 313 | { 314 | gpio_set(GPIOB, GPIO14); 315 | gpio_clear(GPIOC,GPIO13); 316 | } 317 | else 318 | { 319 | gpio_clear(GPIOB,GPIO14); 320 | gpio_set(GPIOC,GPIO13); 321 | } 322 | } 323 | 324 | static void fanstate(bool state){ 325 | if (state) 326 | { 327 | gpio_set(GPIOB, GPIO12); 328 | } 329 | else 330 | { 331 | gpio_clear(GPIOB,GPIO12); 332 | } 333 | } 334 | 335 | static void gpioinit() 336 | { 337 | rcc_periph_clock_enable(RCC_GPIOA); 338 | rcc_periph_clock_enable(RCC_GPIOB); 339 | rcc_periph_clock_enable(RCC_GPIOC); 340 | 341 | 342 | gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_2_MHZ, 343 | GPIO_CNF_OUTPUT_PUSHPULL, GPIO13); 344 | 345 | gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ, 346 | GPIO_CNF_OUTPUT_PUSHPULL, GPIO12); 347 | 348 | gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ, 349 | GPIO_CNF_OUTPUT_PUSHPULL, GPIO14); 350 | 351 | gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, 352 | GPIO_CNF_OUTPUT_PUSHPULL, GPIO11); 353 | gpio_set(GPIOC, GPIO11); 354 | 355 | 356 | } 357 | 358 | static void gpiouninit() 359 | { 360 | gpio_set_mode(GPIOC,GPIO_MODE_INPUT,GPIO_CNF_INPUT_FLOAT,GPIO13); 361 | gpio_set_mode(GPIOB,GPIO_MODE_INPUT,GPIO_CNF_INPUT_FLOAT,GPIO12); 362 | gpio_set_mode(GPIOB,GPIO_MODE_INPUT,GPIO_CNF_INPUT_FLOAT,GPIO14); 363 | gpio_set_mode(GPIOC,GPIO_MODE_INPUT,GPIO_CNF_INPUT_FLOAT,GPIO11); 364 | rcc_periph_clock_disable(RCC_GPIOC); 365 | rcc_periph_clock_disable(RCC_GPIOB); 366 | rcc_periph_clock_disable(RCC_GPIOA); 367 | } 368 | 369 | static bool areyouawizard(uint8_t c){ 370 | 371 | magicctr = (c == magicstr[magicctr]) ? magicctr + 1 : 0; 372 | 373 | if (magicctr >= magichappens) 374 | { 375 | return true; 376 | } 377 | else 378 | { 379 | return false; 380 | } 381 | } 382 | 383 | static void delay(){ 384 | int i; 385 | for (i = 0; i < 0x800000; i++) 386 | __asm__("nop"); 387 | } 388 | 389 | static void disableallints(){ 390 | nvic_disable_irq(NVIC_OTG_FS_IRQ); 391 | nvic_disable_irq(NVIC_USB_WAKEUP_IRQ); 392 | systick_counter_disable(); 393 | systick_interrupt_disable(); 394 | cm_disable_interrupts(); 395 | } 396 | 397 | static void usbd_soft_disconnect(){ 398 | delay(); 399 | delay(); 400 | delay(); 401 | gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, 402 | GPIO_CNF_OUTPUT_PUSHPULL, GPIO12); 403 | gpio_clear(GPIOA, GPIO12); 404 | delay(); 405 | delay(); 406 | delay(); 407 | } 408 | 409 | static void zeromem(uint8_t *p, size_t len){ 410 | int cnt; 411 | for (cnt=0;cnt0)) 444 | { 445 | usbd_poll(usbd_dev); 446 | exit_to_dfu_cnt -= (exit_to_dfu) ? 1 : 0; 447 | } 448 | 449 | usbd_disconnect(usbd_dev,true); 450 | zeromem((uint8_t *) usbd_dev,sizeof(usbd_dev)); 451 | gpiouninit(); 452 | disableallints(); 453 | 454 | 455 | delay(); 456 | 457 | //revert to HSI clock: 458 | //rcc_clock_setup_in_hsi_out_48mhz(); 459 | 460 | //the great jump 461 | JUMP_TO_APP(DFUAPP_ADDRESS,VTOR_ADDRESS); 462 | } 463 | -------------------------------------------------------------------------------- /refnoisefirmware/resetusb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ $EUID != 0 ]] ; then 4 | echo This must be run as root! 5 | exit 1 6 | fi 7 | 8 | for xhci in /sys/bus/pci/drivers/?hci_hcd ; do 9 | 10 | if ! cd $xhci ; then 11 | echo Weird error. Failed to change directory to $xhci 12 | exit 1 13 | fi 14 | 15 | echo Resetting devices from $xhci... 16 | 17 | for i in ????:??:??.? ; do 18 | echo -n "$i" > unbind 19 | echo -n "$i" > bind 20 | done 21 | done -------------------------------------------------------------------------------- /refnoisefirmware/stm32-blueboot.ld: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the libopencm3 project. 3 | * 4 | * Copyright (C) 2009 Uwe Hermann 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | 20 | /* Linker script for Olimex STM32-H103 (STM32F103RBT6, 128K flash, 20K RAM). */ 21 | 22 | /* Define memory regions. */ 23 | MEMORY 24 | { 25 | bootloader (rx): ORIGIN = 0x08000000, LENGTH = 6400 26 | dfuapp (rx) : ORIGIN = 0x08001900, LENGTH = 1K 27 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K 28 | } 29 | 30 | /* Include the common ld script. */ 31 | 32 | /* IDEA: DEFINE OUR SECTIONS HERE AND HAVE SEPARATE SHIT FOR MAIN1 AND 2 TO JUMP INTO*/ 33 | /* INCLUDE cortex-m-generic.ld */ 34 | 35 | /* Enforce emmition of the vector table. */ 36 | EXTERN (vector_table) 37 | 38 | /* Enforce emmittion of our own reset function, without this it will be optimized away since 39 | * there's no call to it */ 40 | EXTERN (reset_to_dfu) 41 | 42 | /* Define the entry point of the output file. */ 43 | ENTRY(reset_handler) 44 | 45 | /* Define sections. */ 46 | SECTIONS 47 | { 48 | .text : { 49 | _vector_table_start = .; 50 | *(.vectors) /*Vector table */ 51 | *(.text*) /* Program code */ 52 | . = ALIGN(4); 53 | *(.rodata*) /* Read-only data */ 54 | . = ALIGN(4); 55 | } >bootloader 56 | 57 | .dfu : { 58 | _dfuapp_start = .; 59 | *(.reset_to_dfu) 60 | *(.dfu*) /* Program code */ 61 | . = ALIGN(4); 62 | *(.rodata*) /* Read-only data */ 63 | . = ALIGN(4); 64 | } >dfuapp 65 | 66 | /* C++ Static constructors/destructors, also used for __attribute__ 67 | * ((constructor)) and the likes */ 68 | .preinit_array : { 69 | . = ALIGN(4); 70 | __preinit_array_start = .; 71 | KEEP (*(.preinit_array)) 72 | __preinit_array_end = .; 73 | } >bootloader 74 | .init_array : { 75 | . = ALIGN(4); 76 | __init_array_start = .; 77 | KEEP (*(SORT(.init_array.*))) 78 | KEEP (*(.init_array)) 79 | __init_array_end = .; 80 | } >bootloader 81 | .fini_array : { 82 | . = ALIGN(4); 83 | __fini_array_start = .; 84 | KEEP (*(.fini_array)) 85 | KEEP (*(SORT(.fini_array.*))) 86 | __fini_array_end = .; 87 | } >bootloader 88 | 89 | /* 90 | * Another section used by C++ stuff, appears when using newlib with 91 | * 64bit (long long) printf support 92 | */ 93 | .ARM.extab : { 94 | *(.ARM.extab*) 95 | } >bootloader 96 | .ARM.exidx : { 97 | __exidx_start = .; 98 | *(.ARM.exidx*) 99 | __exidx_end = .; 100 | } >bootloader 101 | 102 | . = ALIGN(4); 103 | _etext = .; 104 | 105 | .data : { 106 | _data = .; 107 | *(.data*) /* Read-write initialized data */ 108 | . = ALIGN(4); 109 | _edata = .; 110 | } >ram AT >bootloader 111 | _data_loadaddr = LOADADDR(.data); 112 | 113 | .bss : { 114 | *(.bss*) /* Read-write zero initialized data */ 115 | *(COMMON) 116 | . = ALIGN(4); 117 | _ebss = .; 118 | } >ram 119 | 120 | /* 121 | * The .eh_frame section appears to be used for C++ exception handling. 122 | * You may need to fix this if you're using C++. 123 | */ 124 | /DISCARD/ : { *(.eh_frame) } 125 | 126 | . = ALIGN(4); 127 | end = .; 128 | } 129 | 130 | PROVIDE(_stack = ORIGIN(ram) + LENGTH(ram)); 131 | 132 | -------------------------------------------------------------------------------- /refnoisefirmware/stm32-bluebootld.ld: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the libopencm3 project. 3 | * 4 | * Copyright (C) 2009 Uwe Hermann 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | 20 | /* Linker script for Olimex STM32-H103 (STM32F103RBT6, 128K flash, 20K RAM). */ 21 | 22 | /* Define memory regions. */ 23 | MEMORY 24 | { 25 | rom (rx) : ORIGIN = 0x08000000, LENGTH = 1K 26 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K 27 | } 28 | 29 | /* Include the common ld script. */ 30 | INCLUDE cortex-m-generic.ld 31 | 32 | -------------------------------------------------------------------------------- /refnoisefirmware/stm32-bluepill.ld: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the libopencm3 project. 3 | * 4 | * Copyright (C) 2009 Uwe Hermann 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | 20 | /* Linker script for Olimex STM32-H103 (STM32F103RBT6, 128K flash, 20K RAM). */ 21 | 22 | /* Define memory regions. */ 23 | MEMORY 24 | { 25 | rom (rx) : ORIGIN = 0x08002000, LENGTH = 120K 26 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K 27 | } 28 | 29 | /* Include the common ld script. */ 30 | INCLUDE cortex-m-generic.ld 31 | 32 | -------------------------------------------------------------------------------- /refnoisefirmware/stm32-bluepilldfu.ld: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the libopencm3 project. 3 | * 4 | * Copyright (C) 2009 Uwe Hermann 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | 20 | /* Linker script for Olimex STM32-H103 (STM32F103RBT6, 128K flash, 20K RAM). */ 21 | 22 | /* Define memory regions. */ 23 | MEMORY 24 | { 25 | rom (rx) : ORIGIN = 0x08000400, LENGTH = 127K 26 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K 27 | } 28 | 29 | /* Include the common ld script. */ 30 | INCLUDE cortex-m-generic.ld 31 | 32 | -------------------------------------------------------------------------------- /refnoisefirmware/usbdfu.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the libopencm3 project. 3 | * 4 | * Copyright (C) 2010 Gareth McMullin 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #define APP_ADDRESS 0x08002000 30 | 31 | /* Commands sent with wBlockNum == 0 as per ST implementation. */ 32 | #define CMD_SETADDR 0x21 33 | #define CMD_ERASE 0x41 34 | 35 | /* We need a special large control buffer for this device: */ 36 | uint8_t usbd_control_buffer[1024]; 37 | 38 | static enum dfu_state usbdfu_state = STATE_DFU_IDLE; 39 | 40 | static struct { 41 | uint8_t buf[sizeof(usbd_control_buffer)]; 42 | uint16_t len; 43 | uint32_t addr; 44 | uint16_t blocknum; 45 | } prog; 46 | 47 | const struct usb_device_descriptor dev = { 48 | .bLength = USB_DT_DEVICE_SIZE, 49 | .bDescriptorType = USB_DT_DEVICE, 50 | .bcdUSB = 0x0200, 51 | .bDeviceClass = 0, 52 | .bDeviceSubClass = 0, 53 | .bDeviceProtocol = 0, 54 | .bMaxPacketSize0 = 64, 55 | .idVendor = 0x0483, 56 | .idProduct = 0xDF11, 57 | .bcdDevice = 0x0200, 58 | .iManufacturer = 1, 59 | .iProduct = 2, 60 | .iSerialNumber = 3, 61 | .bNumConfigurations = 1, 62 | }; 63 | 64 | const struct usb_dfu_descriptor dfu_function = { 65 | .bLength = sizeof(struct usb_dfu_descriptor), 66 | .bDescriptorType = DFU_FUNCTIONAL, 67 | .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH, 68 | .wDetachTimeout = 255, 69 | .wTransferSize = 1024, 70 | .bcdDFUVersion = 0x011A, 71 | }; 72 | 73 | const struct usb_interface_descriptor iface = { 74 | .bLength = USB_DT_INTERFACE_SIZE, 75 | .bDescriptorType = USB_DT_INTERFACE, 76 | .bInterfaceNumber = 0, 77 | .bAlternateSetting = 0, 78 | .bNumEndpoints = 0, 79 | .bInterfaceClass = 0xFE, /* Device Firmware Upgrade */ 80 | .bInterfaceSubClass = 1, 81 | .bInterfaceProtocol = 2, 82 | 83 | /* The ST Microelectronics DfuSe application needs this string. 84 | * The format isn't documented... */ 85 | .iInterface = 4, 86 | 87 | .extra = &dfu_function, 88 | .extralen = sizeof(dfu_function), 89 | }; 90 | 91 | const struct usb_interface ifaces[] = {{ 92 | .num_altsetting = 1, 93 | .altsetting = &iface, 94 | }}; 95 | 96 | const struct usb_config_descriptor config = { 97 | .bLength = USB_DT_CONFIGURATION_SIZE, 98 | .bDescriptorType = USB_DT_CONFIGURATION, 99 | .wTotalLength = 0, 100 | .bNumInterfaces = 1, 101 | .bConfigurationValue = 1, 102 | .iConfiguration = 0, 103 | .bmAttributes = 0xC0, 104 | .bMaxPower = 0x32, 105 | 106 | .interface = ifaces, 107 | }; 108 | 109 | static const char *usb_strings[] = { 110 | "COHERENT RTLSDR - DFU", 111 | "Reference noise controller firmware update", 112 | "I_AM_A_WIZARD", 113 | /* This string is used by ST Microelectronics' DfuSe utility. */ 114 | "@Internal Flash /0x08000000/8*001Ka,56*001Kg", 115 | }; 116 | 117 | static uint8_t usbdfu_getstatus(usbd_device *usbd_dev, uint32_t *bwPollTimeout) 118 | { 119 | (void)usbd_dev; 120 | 121 | switch (usbdfu_state) { 122 | case STATE_DFU_DNLOAD_SYNC: 123 | usbdfu_state = STATE_DFU_DNBUSY; 124 | *bwPollTimeout = 100; 125 | return DFU_STATUS_OK; 126 | case STATE_DFU_MANIFEST_SYNC: 127 | /* Device will reset when read is complete. */ 128 | usbdfu_state = STATE_DFU_MANIFEST; 129 | return DFU_STATUS_OK; 130 | default: 131 | return DFU_STATUS_OK; 132 | } 133 | } 134 | 135 | static void usbdfu_getstatus_complete(usbd_device *usbd_dev, struct usb_setup_data *req) 136 | { 137 | int i; 138 | (void)req; 139 | (void)usbd_dev; 140 | 141 | switch (usbdfu_state) { 142 | case STATE_DFU_DNBUSY: 143 | flash_unlock(); 144 | //flash_unlock_upper(); 145 | if (prog.blocknum == 0) { 146 | switch (prog.buf[0]) { 147 | case CMD_ERASE: 148 | { 149 | uint32_t *dat = (uint32_t *)(prog.buf + 1); 150 | flash_erase_page(*dat); 151 | } 152 | case CMD_SETADDR: 153 | { 154 | uint32_t *dat = (uint32_t *)(prog.buf + 1); 155 | prog.addr = *dat; 156 | } 157 | } 158 | } else { 159 | uint32_t baseaddr = prog.addr + ((prog.blocknum - 2) * 160 | dfu_function.wTransferSize); 161 | for (i = 0; i < prog.len; i += 2) { 162 | uint16_t *dat = (uint16_t *)(prog.buf + i); 163 | flash_program_half_word(baseaddr + i, 164 | *dat); 165 | } 166 | } 167 | //flash_lock_upper(); 168 | flash_lock(); 169 | 170 | /* Jump straight to dfuDNLOAD-IDLE, skipping dfuDNLOAD-SYNC. */ 171 | usbdfu_state = STATE_DFU_DNLOAD_IDLE; 172 | return; 173 | case STATE_DFU_MANIFEST: 174 | /* USB device must detach, we just reset... */ 175 | scb_reset_system(); 176 | return; /* Will never return. */ 177 | default: 178 | return; 179 | } 180 | } 181 | 182 | static enum usbd_request_return_codes usbdfu_control_request(usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, 183 | uint16_t *len, void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req)) 184 | { 185 | if ((req->bmRequestType & 0x7F) != 0x21) 186 | return USBD_REQ_NOTSUPP; /* Only accept class request. */ 187 | switch (req->bRequest) { 188 | case DFU_DNLOAD: 189 | if ((len == NULL) || (*len == 0)) { 190 | usbdfu_state = STATE_DFU_MANIFEST_SYNC; 191 | } else { 192 | /* Copy download data for use on GET_STATUS. */ 193 | prog.blocknum = req->wValue; 194 | prog.len = *len; 195 | memcpy(prog.buf, *buf, *len); 196 | usbdfu_state = STATE_DFU_DNLOAD_SYNC; 197 | } 198 | return USBD_REQ_HANDLED; 199 | case DFU_CLRSTATUS: 200 | /* Clear error and return to dfuIDLE. */ 201 | if (usbdfu_state == STATE_DFU_ERROR) 202 | usbdfu_state = STATE_DFU_IDLE; 203 | return USBD_REQ_HANDLED; 204 | case DFU_ABORT: 205 | /* Abort returns to dfuIDLE state. */ 206 | usbdfu_state = STATE_DFU_IDLE; 207 | return USBD_REQ_HANDLED; 208 | case DFU_UPLOAD: 209 | /* Upload not supported for now. */ 210 | return USBD_REQ_NOTSUPP; 211 | case DFU_GETSTATUS: { 212 | uint32_t bwPollTimeout = 0; /* 24-bit integer in DFU class spec */ 213 | (*buf)[0] = usbdfu_getstatus(usbd_dev, &bwPollTimeout); 214 | (*buf)[1] = bwPollTimeout & 0xFF; 215 | (*buf)[2] = (bwPollTimeout >> 8) & 0xFF; 216 | (*buf)[3] = (bwPollTimeout >> 16) & 0xFF; 217 | (*buf)[4] = usbdfu_state; 218 | (*buf)[5] = 0; /* iString not used here */ 219 | *len = 6; 220 | *complete = usbdfu_getstatus_complete; 221 | return USBD_REQ_HANDLED; 222 | } 223 | case DFU_GETSTATE: 224 | /* Return state with no state transision. */ 225 | *buf[0] = usbdfu_state; 226 | *len = 1; 227 | return USBD_REQ_HANDLED; 228 | } 229 | 230 | return USBD_REQ_NOTSUPP; 231 | } 232 | 233 | static void usbdfu_set_config(usbd_device *usbd_dev, uint16_t wValue) 234 | { 235 | (void)wValue; 236 | 237 | usbd_register_control_callback( 238 | usbd_dev, 239 | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, 240 | USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, 241 | usbdfu_control_request); 242 | } 243 | 244 | int main(void) 245 | { 246 | usbd_device *usbd_dev; 247 | 248 | rcc_clock_setup_in_hse_8mhz_out_72mhz(); 249 | 250 | rcc_periph_clock_enable(RCC_GPIOC); 251 | 252 | gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, 253 | GPIO_CNF_OUTPUT_PUSHPULL, GPIO11); 254 | gpio_set(GPIOC, GPIO11); 255 | 256 | usbd_dev = usbd_init(&st_usbfs_v1_usb_driver, &dev, &config, usb_strings, 4, usbd_control_buffer, sizeof(usbd_control_buffer)); 257 | usbd_register_set_config_callback(usbd_dev, usbdfu_set_config); 258 | 259 | gpio_clear(GPIOC, GPIO11); 260 | 261 | while (1) 262 | usbd_poll(usbd_dev); 263 | } 264 | 265 | 266 | //UNNEEDED. MY INITIAL ATTEMPTS TO COMPILE THIS AS A WHOLE AND HAVE DFU-APP IN A NAMED SECTION. DID NOT ACCOMPLISH THIS... 267 | //NEEDED TWO VECTOR TABLES. MAYBE SOME DAY... what is now main was dfu_main() and bootloader.c code was main here. 268 | 269 | /* 270 | extern uint32_t _vector_table_start; 271 | 272 | typedef void (*funcp_t) (void); 273 | extern funcp_t __preinit_array_start, __preinit_array_end; 274 | extern funcp_t __init_array_start, __init_array_end; 275 | extern funcp_t __fini_array_start, __fini_array_end; 276 | */ 277 | 278 | /* 279 | int dfu_main(void); 280 | 281 | int main(void) 282 | { 283 | 284 | 285 | rcc_periph_clock_enable(RCC_GPIOA); 286 | 287 | //Boot the application if it's valid. 288 | if ((*(volatile uint32_t *)APP_ADDRESS & 0x2FFE0000) == 0x20000000) { 289 | //Set vector table base address. 290 | SCB_VTOR = APP_ADDRESS & 0xFFFF; 291 | //Initialise master stack pointer. 292 | asm volatile("msr msp, %0"::"g" 293 | (*(volatile uint32_t *)APP_ADDRESS)); 294 | //Jump to application. 295 | (*(void (**)())(APP_ADDRESS + 4))(); 296 | } 297 | 298 | dfu_main(); 299 | }*/ 300 | /* 301 | 302 | 303 | //Modified from libopencm3 reset_handler(). We place this initializer function in our named segment, call it when we need jump to DFU. 304 | void __attribute__ ((section(".dfu"))) reset_to_dfu(void) 305 | { 306 | volatile unsigned *src, *dest; 307 | funcp_t *fp; 308 | 309 | for (src = &_data_loadaddr, dest = &_data; 310 | dest < &_edata; 311 | src++, dest++) { 312 | *dest = *src; 313 | } 314 | 315 | while (dest < &_ebss) { 316 | *dest++ = 0; 317 | } 318 | 319 | SCB_CCR |= SCB_CCR_STKALIGN; 320 | 321 | for (fp = &__preinit_array_start; fp < &__preinit_array_end; fp++) { 322 | (*fp)(); 323 | } 324 | for (fp = &__init_array_start; fp < &__init_array_end; fp++) { 325 | (*fp)(); 326 | } 327 | 328 | (void)dfu_main(); 329 | 330 | for (fp = &__fini_array_start; fp < &__fini_array_end; fp++) { 331 | (*fp)(); 332 | } 333 | 334 | } 335 | */ -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (coherentrtlsdr) 2 | 3 | #include_directories(${PROJECT_SOURCE_DIR}/include) 4 | 5 | set(SOURCES main.cc csdrdevice.cc crtlsdr.cc console.cc ccoherent.cc cdsp.cc ccontrol.cc cpacketizer.cc common.cc) 6 | 7 | set(GPUFFT_DIR /opt/vc/src/hello_pi/hello_fft) 8 | 9 | set(GPUFFT_SOURCES ${GPUFFT_DIR}/mailbox.c 10 | ${GPUFFT_DIR}/gpu_fft.c 11 | ${GPUFFT_DIR}/gpu_fft_base.c 12 | ${GPUFFT_DIR}/gpu_fft_twiddles.c 13 | ${GPUFFT_DIR}/gpu_fft_shaders.c) 14 | 15 | if (RASPBERRYPI) 16 | include_directories(${GPUFFT_DIR}) 17 | include_directories(${GPUFFT_DIR}/hex) 18 | 19 | add_executable(coherentrtlsdr ${SOURCES} ${GPUFFT_SOURCES}) 20 | target_link_libraries(coherentrtlsdr zmq pthread fftw3f volk rtlsdr readline stdc++ dl rt) 21 | else (RASPBERRYPI) 22 | add_executable(coherentrtlsdr ${SOURCES}) 23 | target_link_libraries(coherentrtlsdr zmq pthread fftw3f volk rtlsdr readline stdc++) 24 | endif (RASPBERRYPI) 25 | 26 | -------------------------------------------------------------------------------- /src/ccoherent.cc: -------------------------------------------------------------------------------- 1 | /* 2 | coherent-rtlsdr 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | coherent-rtlsdr is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with coherent-rtlsdr. If not, see . 16 | */ 17 | 18 | #include "ccoherent.h" 19 | 20 | #include "unistd.h" 21 | #include 22 | #include 23 | 24 | #ifdef RASPBERRYPI 25 | int uint32log2(uint32_t k){ 26 | int i=0; 27 | for (i=-1;k>=1;k>>=1,++i); 28 | return i; 29 | } 30 | #endif 31 | 32 | ccoherent::ccoherent(crefsdr* refdev_,lvector *devvec_,crefnoise* refnoise_, int nfft_){ 33 | refdev =refdev_; 34 | devices=devvec_; 35 | refnoise=refnoise_; 36 | do_exit=false; 37 | nfft = nfft_; 38 | 39 | //TODO: get blocksize from refdev...! 40 | 41 | int alignment = volk_get_alignment(); 42 | 43 | blocksize = refdev->get_blocksize(); 44 | uint32_t bufferlen = blocksize*nfft; 45 | 46 | smagsqr = (float *) volk_malloc(sizeof(float)*bufferlen,alignment); 47 | std::memset(smagsqr,0,sizeof(float)*bufferlen); 48 | 49 | // create plans for nfft transforms 50 | #ifdef RASPBERRYPI 51 | int ret; 52 | int log2_N = uint32log2(blocksize); 53 | mb = mbox_open(); 54 | ret = gpu_fft_prepare(mb,log2_N,GPU_FFT_FWD,nfft,&fftscheme); 55 | if (ret){ 56 | cout << "Failed to create "<< to_string(nfft) << "X gpu_fft_forward for log2N: " << to_string(log2_N) << endl; 57 | } 58 | ret = gpu_fft_prepare(mb,log2_N,GPU_FFT_REV,nfft,&ifftscheme); //perhaps check return values some day... 59 | if (ret){ 60 | cout << "Failed to create "<< to_string(nfft) << "X gpu_fft_inverse for log2N: " << to_string(log2_N) << endl; 61 | } 62 | 63 | #else 64 | 65 | //alloc buffers for dsp. note, fftwf_complex is bin. compat with lv_32fc_t and std::complex. 66 | sfft = (std::complex *) fftwf_alloc_complex(bufferlen); 67 | sifft = (std::complex *) fftwf_alloc_complex(bufferlen); 68 | sfloat = (lv_32fc_t *) volk_malloc(sizeof(lv_32fc_t)*bufferlen,alignment); 69 | sconv = (lv_32fc_t *) volk_malloc(sizeof(lv_32fc_t)*bufferlen,alignment); 70 | 71 | //zero all buffers. important especially for circ. convolution. 72 | std::memset(sfft,0,sizeof(fftwf_complex)*bufferlen); 73 | std::memset(sifft,0,sizeof(fftwf_complex)*bufferlen); 74 | std::memset(sfloat,0,sizeof(lv_32fc_t)*bufferlen); 75 | std::memset(sconv,0,sizeof(lv_32fc_t)*bufferlen); 76 | 77 | 78 | int rank =1; 79 | int n[] = {blocksize}; 80 | int *inembed = n; //apparently passing NULL is equivalent to passing n. 81 | int *onembed= n; 82 | int idist = blocksize; 83 | int odist = blocksize; 84 | int istride=1; 85 | int ostride=1; 86 | 87 | fftscheme = fftwf_plan_many_dft(rank, n, nfft, (fftwf_complex *) sfloat, 88 | inembed,istride,idist,(fftwf_complex *) sfft, 89 | onembed,ostride,odist,FFTW_FORWARD,0); 90 | 91 | ifftscheme = fftwf_plan_many_dft(rank, n, nfft, (fftwf_complex *) sconv, 92 | inembed,istride,idist,(fftwf_complex *) sifft, 93 | onembed,ostride,odist,FFTW_BACKWARD,0); 94 | #endif 95 | } 96 | 97 | ccoherent::~ccoherent(){ 98 | if (thread.joinable()) thread.join(); 99 | 100 | volk_free(smagsqr); 101 | #ifdef RASPBERRYPI 102 | gpu_fft_release(fftscheme); 103 | gpu_fft_release(ifftscheme); 104 | #else 105 | volk_free(sfloat); 106 | volk_free(sconv); 107 | fftwf_free(sfft); 108 | fftwf_free(sifft); 109 | fftwf_destroy_plan(fftscheme); 110 | fftwf_destroy_plan(ifftscheme); 111 | #endif 112 | 113 | } 114 | 115 | void ccoherent::clearlagqueue(){ 116 | lagqueue.clear(); 117 | } 118 | 119 | size_t ccoherent::lagqueuesize(){ 120 | return lagqueue.size(); 121 | } 122 | 123 | void ccoherent::queuelag(csdrdevice *d){ 124 | if (lagqueue.size()get_sptr()); 127 | printf(" copy to: %p\n",(sfloat+lagqueue.size()*blocksize)); 128 | printf("nbytes : %d\n",sizeof(std::complex)*(blocksize)); 129 | printf("blocksize: %d\n",(blocksize));*/ 130 | 131 | //if (is_zeros((uint32_t*)sfloat,blocksize)) 132 | // cout << "ref is zeros" < *inptr = (std::complex *) (fftscheme->in + lagqueue.size()*fftscheme->step); 135 | std::memcpy(inptr,d->get_sptr(),sizeof(std::complex)*(blocksize)); 136 | #else 137 | std::memcpy((sfloat+lagqueue.size()*blocksize), d->get_sptr(),sizeof(std::complex)*(blocksize)); //fix me, half of the copy is zeros. 138 | #endif 139 | lagqueue.push_back(d); 140 | 141 | } 142 | } 143 | void ccoherent::queuelag(csdrdevice *d,bool refc){ 144 | if (lagqueue.size()get_sptr(),sizeof(std::complex)*(blocksize)); //fix me, half of the copy is zeros. 146 | if (refc) 147 | d->convtofloat(sfloat+lagqueue.size()*blocksize); 148 | else 149 | d->convtofloat(sfloat+lagqueue.size()*blocksize); 150 | lagqueue.push_back(d); 151 | } 152 | } 153 | 154 | void ccoherent::computelag() 155 | { 156 | 157 | #ifdef RASPBERRYPI 158 | 159 | cdsp::fft(&fftscheme); 160 | std::complex *refptr = (std::complex *) fftscheme->out; 161 | for (int k=1;k *outptr = (std::complex *) (fftscheme->out + k*fftscheme->step); 163 | std::complex *inptr = (std::complex *) (ifftscheme->in + k*ifftscheme->step); 164 | cdsp::conjugatemul(inptr,outptr,refptr,blocksize); 165 | } 166 | cdsp::fft(&ifftscheme); 167 | 168 | for (int k=1;k *outptr = (std::complex *) (ifftscheme->out + k*ifftscheme->step); 170 | cdsp::magsquared(smagsqr+k*blocksize,outptr,blocksize); 171 | } 172 | //fft->in + j*fft->step; 173 | #else 174 | cdsp::fft(sfft,sfloat,&fftscheme); //nfft fft:s 175 | 176 | //multiply each with ref 177 | for(int k=1;k corr = sifft[k*blocksize +idx]; 197 | //float argh = std::arg(corr); 198 | //cout << to_string(corr.real()) <<"," << to_string(corr.imag()) << endl; 199 | //end edited 200 | 201 | //OLD CODE STARTS HERE 202 | 203 | float *ptr = (smagsqr + k*blocksize + idx); // this should be taken from sifft() real part. 204 | float mag = sqrt( *(ptr) / float(blocksize>>1) ); // not quite sure about the scaling. fftw is unnormalized, so ifft(fft(x)) = N*x 205 | 206 | if ((idx>1) && (idx<(blocksize-1))){ //check that n-1 and n+1 won't go off bounds 207 | a=-*(ptr-1)-2*(*ptr)+*(ptr+1); 208 | b=-0.5f*(*(ptr-1))+0.5f*(*(ptr+1)); 209 | 210 | //handle possible divide by zero by setting the fractional to zero: 211 | if (a!=0.0f) 212 | D=-b/a; 213 | else{ 214 | D=0.0f; 215 | cout << "frac zeroed at 1" < *ptrc = (sifft + (k+1)*blocksize/2 + idx); 225 | mag = (ptrc[0]*conj(ptrc[0])).real(); 226 | a = -ptrc[-1].real() - 2.0f*ptrc[0].real() + ptrc[1].real(); 227 | b = -0.5f*ptrc[-1].real() + 0.5f*ptrc[1].real(); 228 | D = -(b/a)*(1.0f+M_PI/2); 229 | lag = D + idx; 230 | */ 231 | 232 | lag-=(blocksize >> 1); 233 | lagqueue[k]->set_lag(lag,mag); 234 | } 235 | lagqueue.clear(); 236 | 237 | //if (is_zeros((uint32_t*)sfloat,sizeof(std::complex)*(blocksize>>1))) 238 | // cout << "ref is zeros" <do_exit){ 248 | //read reference channel and perform fft: 249 | ctx->clearlagqueue(); 250 | int8_t *refsptr = ctx->refdev->read(); 251 | ctx->refdev->convtofloat(); //working 252 | ctx->queuelag(ctx->refdev); 253 | ctx->refdev->packetize.write(0,ctx->refdev->get_readcntbuf(),refsptr); 254 | ctx->refdev->consume(); 255 | //ctx->queuelag(ctx->refdev,true); 256 | 257 | int c=1; 258 | { 259 | //std::unique_lock lock(ctx->devices->m); 260 | //std::scoped_lock(ctx->devices->m); 261 | //ctx->devices->lock(); 262 | for(auto *d: *ctx->devices){ 263 | int8_t *ptr = d->read(); // this is the culprit for the hang... 264 | if(d->is_ready()){ 265 | std::complex *sfloat = (std::complex *) d->convtofloat(); 266 | if (d->is_lagrequested()){ 267 | ctx->queuelag(d); 268 | //ctx->queuelag(d,false); 269 | } 270 | 271 | if (ctx->refnoise->isenabled()){ 272 | std::complex p = d->est_phasecorrect(ctx->refdev->get_sptr()+(ctx->refdev->get_blocksize()>>1)); 273 | } 274 | 275 | d->phasecorrect(); 276 | 277 | //d->packetize.write(c,d->get_readcnt(),ptr); 278 | d->packetize.write(c,d->get_readcntbuf(),sfloat); 279 | d->packetize.writedebug(c,d->get_phasecorrect()); 280 | c++; 281 | d->consume(); 282 | } 283 | } 284 | if (ctx->lagqueuesize()>1){ 285 | ctx->computelag(); 286 | //cout << "firing lag computation!" << endl; 287 | } 288 | ctx->refdev->packetize.notifysend(); 289 | //ctx->devices->unlock(); 290 | } 291 | } 292 | 293 | //ctx->packetize->request_exit(); 294 | } 295 | 296 | void ccoherent::start(){ 297 | thread = std::thread(&ccoherent::threadf,this); 298 | } 299 | 300 | void ccoherent::request_exit(){ 301 | do_exit=true; 302 | } 303 | -------------------------------------------------------------------------------- /src/ccontrol.cc: -------------------------------------------------------------------------------- 1 | /* 2 | coherent-rtlsdr 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | coherent-rtlsdr is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with coherent-rtlsdr. If not, see . 16 | */ 17 | 18 | #include "ccontrol.h" 19 | #include "common.h" 20 | #include //usleep remove... 21 | #include 22 | #include 23 | #include 24 | 25 | const uint32_t rtl_xtal = 28800000; 26 | 27 | const double maxppm = TWO_POW(13)/TWO_POW(24); //TWO_POW(-11) 28 | const float scale = 100.0f; //WAS 75. This works well for 1e6 sample rate 29 | const float frac_t = 0.90f; 30 | 31 | void hndlr(int in){ 32 | 33 | } 34 | 35 | double realfs(uint32_t requestedfs){ 36 | //from rtlsdr source 37 | //#define APPLY_PPM_CORR(val,ppm) (((val) * (1.0 + (ppm) / 1e6))) 38 | 39 | uint32_t fsratio = (uint32_t)((rtl_xtal * TWO_POW(22)) / requestedfs); 40 | fsratio &= 0x0ffffffc; 41 | 42 | uint32_t real_fsratio = fsratio | ((fsratio & 0x08000000) << 1); 43 | return (rtl_xtal * TWO_POW(22)) / real_fsratio; 44 | } 45 | 46 | void ccontrol::start(){ 47 | do_exit= false; 48 | 49 | thread = std::thread(&ccontrol::threadf, this); 50 | } 51 | 52 | void ccontrol::request_exit(){ 53 | do_exit=true; 54 | } 55 | 56 | void ccontrol::join(){ 57 | thread.join(); 58 | } 59 | 60 | void fillts(struct timespec* t,double ts){ 61 | long int tnsec = ts*1e9; 62 | if (ts>1.0){ 63 | t->tv_sec = floor(ts); 64 | t->tv_nsec= (ts - floor(ts))*1e9; 65 | } 66 | else 67 | { 68 | t->tv_sec=0; 69 | t->tv_nsec = tnsec; 70 | } 71 | } 72 | 73 | float descent(float lag){ 74 | //return fabs(maxppm*tanh(lag/scale)); 75 | return maxppm*tanh(lag/scale); //EDIT 76 | } 77 | 78 | void ccontrol::threadf(ccontrol *ctx){ 79 | 80 | struct timespec tsp = {1,0L}; 81 | long int tns = 0L; 82 | time_t ts = 0; 83 | 84 | //std::cout << "entering control thread with do exit " << std::to_string(ctx->do_exit) << std::endl; 85 | usleep(16*32800); //UGLY! //this should be resolved: if this wait is removed, segfault happens... 86 | //ctx->dev->wait_synchronized(); // check if wait before the loop resolves...nope. 87 | 88 | while(!ctx->do_exit){ 89 | if(!ctx->dev->wait_synchronized()){ 90 | 91 | ctx->dev->requestfftblocking(); //ask for a new fft. 92 | 93 | float lag = ctx->dev->get_lagp()->lag; 94 | uint32_t L = ctx->dev->get_blocksize(); 95 | float fs = realfs(ctx->dev->get_samplerate()); 96 | float fcorr; 97 | 98 | //std::cout << "lag is" << std::to_string(lag) << std::endl; 99 | if (fabs(lag)>sync_threshold){ //should we check when the estimate is calculated ? 100 | 101 | float p = descent(lag); 102 | double t = frac_t*fabs(lag/(p*fs)); //time to spend at altered samplerate 103 | 104 | fillts(&tsp,t); 105 | 106 | //set the resampler frequency: 107 | //fcorr = (float) sgn(lag)*p; 108 | fcorr = p; //EDIT 109 | 110 | ctx->dev->set_correction_f(fcorr); 111 | 112 | nanosleep(&tsp,NULL); //the second argument is a timepec *rem (aining) 113 | //if (errno==EINTR) 114 | // cout << "we've been interrupted" << endl; 115 | ctx->dev->set_correction_f(0.0f); 116 | } 117 | else{ 118 | ctx->dev->set_correction_f(0.0f); 119 | ctx->dev->set_synchronized(true); 120 | } 121 | } 122 | } 123 | } 124 | 125 | 126 | -------------------------------------------------------------------------------- /src/cdsp.cc: -------------------------------------------------------------------------------- 1 | /* 2 | coherent-rtlsdr 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | coherent-rtlsdr is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with coherent-rtlsdr. If not, see . 16 | */ 17 | 18 | #include "cdsp.h" 19 | // currently wraps to volk kernels. In future, these could be mapped to custom code. 20 | 21 | void cdsp::convtosigned(const uint8_t *in, const uint8_t *out, int n){ 22 | //now the compiler may or may not optimize this to use 32 or 64 -bit pointers, xorring multiple samples (4, or 8) simultaneously 23 | //should check, if not, then write the code manually 24 | //for(int i=0;i> 3; 31 | 32 | for(int i=0;i* cdsp::convtofloat(const std::complex *out, const int8_t *s8bit,int n){ 42 | volk_8i_s32f_convert_32f((float *) out, s8bit,127.0f,n); 43 | return out; 44 | } 45 | 46 | const std::complex* cdsp::scalarmul(const std::complex *out,const std::complex *in,const std::complex scalar_in, int n){ 47 | volk_32fc_s32fc_multiply_32fc((lv_32fc_t *) out,(lv_32fc_t*) in,scalar_in,n); 48 | return out; 49 | } 50 | 51 | const std::complex* cdsp::convto8bit(std::complex *out,std::complex*in, int n){ 52 | volk_32f_s32f_convert_8i((int8_t *) out,(float *) in,127.0f,(n<<1)); 53 | return out; 54 | } 55 | /* 56 | const std::complex* cdsp::convtofloat(const std::complex *out, const int8_t *s8bit,int n){ 57 | volk_8i_s32f_convert_32f((float *) out, s8bit,127.0f,n); 58 | return out; 59 | }*/ 60 | 61 | const std::complex cdsp::conj_dotproduct(const std::complex *a,const std::complex *b, int n){ 62 | 63 | std::complex c = 0.0f; 64 | volk_32fc_x2_conjugate_dot_prod_32fc((lv_32fc_t *) &c,(lv_32fc_t *) a,(lv_32fc_t *) b,n); 65 | return c; 66 | } 67 | 68 | const float cdsp::rms(const float *in, int n){ 69 | float res; 70 | volk_32f_x2_dot_prod_32f(&res, in, in,n); 71 | return sqrt(res/n); 72 | //volk_32f_x2_dot_prod_32f(float* result, const float* input, const float* taps, unsigned int num_points) 73 | } 74 | 75 | const float cdsp::rms(const std::complex *s, int n){ 76 | std::complex res = conj_dotproduct(s,s,n); 77 | return sqrt(res.real()/n); 78 | } 79 | 80 | const float cdsp::crestfactor(const float *in,float peak, int n){ 81 | float r = rms(in,n); 82 | return(peak/r); 83 | } 84 | 85 | const float cdsp::PAPR(const std::complex *s,const std::complex *ref, int n){ 86 | //float r = rms(in,n 87 | return 0; //FIXXXX (peak/r)*(peak/r); 88 | } 89 | 90 | const float cdsp::crestfactor(const float *in, int n){ 91 | uint32_t idx; 92 | 93 | volk_32f_index_max_32u(&idx,in,n); 94 | float m = in[idx]; 95 | float r = rms(in,n); 96 | 97 | return (m/r); 98 | } 99 | 100 | const float* cdsp::magsquared(float *out,const std::complex *in,int n){ 101 | volk_32fc_magnitude_squared_32f(out,in,n); 102 | return out; 103 | } 104 | 105 | const std::complex* cdsp::conjugatemul(std::complex*out, std::complex *in1, std::complex *in2, int n){ 106 | volk_32fc_x2_multiply_conjugate_32fc(out,(const lv_32fc_t *) in1, (const lv_32fc_t *) in2,n); //result is in1 .* conj(in2) 107 | return out; 108 | } 109 | 110 | const std::complex* cdsp::fft(std::complex*out, std::complex *in,fft_scheme *scheme){ 111 | 112 | #ifdef RASPBERRYPI 113 | gpu_fft_execute(*scheme); 114 | #else 115 | fftwf_execute_dft(*scheme,(fftwf_complex*) in,(fftwf_complex *) out); 116 | //fftwf_execute(*scheme); 117 | #endif 118 | 119 | return out; 120 | } 121 | 122 | const std::complex* cdsp::fft(fft_scheme *scheme){ 123 | 124 | std::complex *out; 125 | #ifdef RASPBERRYPI 126 | gpu_fft_execute(*scheme); 127 | out = NULL; 128 | #else 129 | fftwf_execute(*scheme); 130 | out = NULL; 131 | #endif 132 | return out; 133 | } 134 | 135 | const uint32_t cdsp::indexofmax(float *in,int n){ 136 | uint32_t idx; 137 | volk_32f_index_max_32u(&idx,in,n); 138 | return idx; 139 | } 140 | 141 | const uint32_t cdsp::indexofmax(float *out,std::complex *in,int n){ 142 | uint32_t idx; 143 | volk_32fc_magnitude_squared_32f(out,in,n); 144 | volk_32f_index_max_32u(&idx,out,n); 145 | return idx; 146 | } 147 | -------------------------------------------------------------------------------- /src/common.cc: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | bool is_zeros(uint32_t *p,int n){ 3 | bool res=true; 4 | for(int k=0;k. 16 | */ 17 | 18 | #include "console.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | //#include 25 | 26 | //extern std::atomic exit_all; 27 | 28 | std::atomic exit_console; 29 | 30 | std::atomic command_processed; 31 | 32 | //extern crefnoise refnoise; 33 | zmq::context_t ccontext(1); 34 | zmq::socket_t csocket(ccontext,ZMQ_ROUTER); 35 | 36 | lqueue *commandqueue; 37 | 38 | void localc(){ 39 | 40 | rl_bind_key('\t', rl_complete); 41 | //rl_getc_function = getc; // CAUTION. 42 | char *input; 43 | 44 | while(!exit_console){ 45 | 46 | if (command_processed){ 47 | input = readline(":"); 48 | add_history(input); 49 | commandqueue->push(std::string(input)); 50 | command_processed=false; 51 | } 52 | //usleep(1000); //the thread needs to halt here until command is processed and output stops, otherwise prompt is messy... 53 | //cout << "pushing input "<< input << endl; 54 | } 55 | cout << "Local console exiting "<< endl; 56 | free(input); 57 | } 58 | 59 | void remotec(std::string address){ 60 | zmq::message_t message; 61 | int timeout = 250; 62 | 63 | csocket.setsockopt(ZMQ_RCVTIMEO,&timeout,sizeof(int)); 64 | csocket.bind(address); 65 | 66 | char *msg; 67 | size_t len; 68 | while(!exit_console){ 69 | int nreceived = csocket.recv(&message); //socket.recv(&message,ZMQ_NOBLOCK); 70 | if (nreceived > 0) { 71 | msg = static_cast(message.data()); 72 | len = message.size()/sizeof(char); 73 | cout << "remote command: "<< std::string(msg) << endl; 74 | commandqueue->push(std::string(msg)); 75 | } 76 | } 77 | //csocket.unbind(address); 78 | csocket.close(); 79 | //ccontext.close(); 80 | cout << "Remote console exiting "<< endl; 81 | } 82 | 83 | void monitorc(std::string address){ 84 | //socket.monitor("inproc://monitor.rep", ZMQ_EVENT_ALL) 85 | zmq_socket_monitor((void*)csocket, "inproc://monitor-client", ZMQ_EVENT_ALL); 86 | 87 | zmq_event_t *event; 88 | char *addr; 89 | zmq::message_t message; 90 | zmq::message_t addrmsg; 91 | int64_t more; 92 | size_t moresize; 93 | 94 | // zmq::socket_t msocket(context,ZMQ_PAIR); 95 | csocket.connect("inproc://monitor-client"); 96 | 97 | while(!exit_console){ 98 | csocket.recv(&message); 99 | //socket.getsockopt(ZMQ_RCVMORE,&more,&moresize); //get more? these messages should arrive in pairs. 100 | 101 | if(more){ //second part ist the address.. 102 | csocket.recv(&message); 103 | addr = static_cast(message.data()); 104 | } 105 | 106 | event = static_cast(message.data()); 107 | 108 | switch(event->event){ 109 | case ZMQ_EVENT_LISTENING: 110 | cout << "Control socket listening at: " << std::string(addr) < *devvec_,crefnoise * refnoise_): thread(){ //, context(1), socket(context,ZMQ_ROUTER){ //CHANGED 129 | refdev =refdev_; 130 | devices=devvec_; 131 | stderr_pipe = pipe; 132 | refnoise = refnoise_; 133 | do_exit=false; 134 | exit_console=false; 135 | } 136 | 137 | // context(1), socket(context,ZMQ_PUB) 138 | 139 | cconsole::~cconsole(){ 140 | if (thread.joinable()) thread.join(); 141 | } 142 | 143 | void cconsole::join(){ 144 | thread.join(); 145 | } 146 | 147 | void cconsole::start(){ 148 | thread = std::thread(cconsole::consolethreadf,this); 149 | } 150 | 151 | void cconsole::request_exit(){ 152 | do_exit=true; 153 | } 154 | 155 | 156 | int cconsole::cmdfs(std::string opt){ 157 | 158 | if (opt.compare("fs")==0) 159 | cout << fmtfloat("%4.6f",refdev->get_samplerate()/1e6) << " MHz" << endl; 160 | else 161 | try{ 162 | float fs_ = stof(opt); 163 | uint32_t fs= uint32_t(fs_); 164 | 165 | refdev->set_samplerate(fs); 166 | for (auto *d : *devices){ 167 | d->set_samplerate(fs); 168 | d->set_synchronized(false); 169 | } 170 | } 171 | catch (const std::exception& e){ 172 | cout << "invalid argument: " << e.what() << "("<< opt <<")" << endl; 173 | } 174 | return 0; 175 | } 176 | int cconsole::cmdretune(std::string opt){ 177 | 178 | //cout << "options string is " << opt << endl; //REMOVE 179 | 180 | if (opt.compare("fcenter")==0) 181 | cout << fmtfloat("%4.3f",refdev->get_fcenter()/1e6) << " MHz"<=1000000) && (fc<1800000000)){ 190 | cout << "retuning to " << fmtfloat("%4.3f",fcenter/1e6) << " MHz" << endl; 191 | refdev->set_fcenter(fc); 192 | for (auto *d : *devices){ 193 | d->set_fcenter(fc); 194 | } 195 | } 196 | } 197 | catch (const std::invalid_argument& ia){ 198 | cout << "invalid argument: " << ia.what() <<"("<< opt <<")"<< endl; 199 | } 200 | return 0; 201 | } 202 | 203 | int cconsole::cmdlist(std::string opt){ 204 | bool all = (opt.compare("all")==0); 205 | 206 | for (uint32_t n=0; n < crtlsdr::get_device_count();++n){ 207 | string devs = crtlsdr::get_usb_str_concat(n); 208 | string name = crtlsdr::get_device_serial(n); 209 | string capt =""; 210 | 211 | for(lvector::iterator d = devices->begin(); d != devices->end(); ++d) 212 | { 213 | if (name.compare((*d)->get_devname())==0){ 214 | capt = string("\t * capturing, signal channel #"+ to_string(std::distance(devices->begin(),d))); 215 | if(!all) 216 | cout << devs << capt << endl; 217 | } 218 | } 219 | if (all) 220 | cout << devs << capt << endl; 221 | } 222 | return 0; 223 | } 224 | 225 | int cconsole::cmddel(std::string opt){ 226 | for(lvector::iterator d = devices->begin(); d != devices->end(); ++d){ 227 | if (opt.compare((*d)->get_devname()) == 0){ 228 | 229 | 230 | //devices->lock(); //this is too slow, interrupts streaming, but currently only way to avoid the deadlock situation.... 231 | cout << "stopping " << opt << endl; 232 | (*d)->stop(); 233 | cout << "exiting " << opt << endl; 234 | (*d)->request_exit(); 235 | cout << "closing " << opt << endl; 236 | (*d)->close(); 237 | cout << "deleting pointer " << opt << endl; 238 | csdrdevice *tmp = (*d); 239 | devices->erase(d); 240 | delete tmp; // delete after removal, since controller may be using the device until it's removed from container. 241 | return 0; 242 | } 243 | } 244 | cout << "'"<get_devname())==0){ 252 | cout <<"'" << opt << "' already capturing" << endl; 253 | return -1; 254 | } 255 | 256 | devices->push_back(new crtlsdr(refdev->get_asyncbufn(),refdev->get_blocksize(),refdev->get_samplerate(),refdev->get_fcenter())); 257 | 258 | if (devices->back()->open(opt)){ 259 | delete devices->back(); 260 | devices->pop_back(); 261 | cout<<"could not open '" << opt <<"'" << endl; 262 | } 263 | startbarrier = new barrier(2); 264 | devices->back()->start(startbarrier); 265 | startbarrier->wait(); 266 | 267 | delete startbarrier; 268 | 269 | return 0; 270 | } 271 | int cconsole::cmdrequest(std::string opt){ 272 | 273 | if (opt.compare("re")==0){ 274 | cout << "enable refnoise"<set_state(true); 276 | } 277 | else if (opt.compare("rd")==0){ 278 | refnoise->set_state(false); 279 | cout << "disable refnoise"<requestfftblocking(); 284 | } 285 | else if (opt.compare("sync")==0){ 286 | for (auto *d : *devices){ 287 | d->set_synchronized(false); 288 | } 289 | } 290 | 291 | return 0; 292 | } 293 | 294 | int cconsole::cmdphase(std::string opt){ 295 | for(auto *d : *devices){ 296 | d->convtofloat(); 297 | std::complex p = d->est_phasecorrect(refdev->get_sptr()+(refdev->get_blocksize()>>1)); 298 | cout<< to_string(int(180.0f/M_PI*atan2f(p.imag(),p.real()))) << "\t"; 299 | //cout<< to_string(atan2f(p.imag(),p.real())) << "\t"; 300 | 301 | phistory_t ph; 302 | ph.t = std::chrono::high_resolution_clock::now(); 303 | ph.p = p; 304 | phistory.push_back(ph); 305 | 306 | 307 | } 308 | cout << endl; 309 | return 0; 310 | } 311 | 312 | int cconsole::cmdstatus(std::string opt){ 313 | int numsynch=0; 314 | for (auto *d : *devices){ 315 | if(d->get_synchronized()) numsynch++; 316 | } 317 | cout << to_string(numsynch) <<" / " << to_string(devices->size()) << " synchronized" << endl; 318 | if (refnoise->isenabled()) 319 | cout << "Reference noise ENABLED." << endl; 320 | else 321 | cout << "Reference noise DISABLED." << endl; 322 | 323 | int cc=0; 324 | for (auto *d : *devices){ 325 | cout << d->get_devname() << ":" << fmtfloat("%+4.3f",d->get_lagp()->lag) << ":" << fmtfloat("%4.0f",d->get_lagp()->mag) << "\t"; 326 | if ((++cc % 6)==0) //6 devs / line 327 | cout << endl; 328 | } 329 | cout << endl; 330 | //cout << endl; 331 | return 0; 332 | } 333 | 334 | int cconsole::parsecmd(std::string inputln){ 335 | int cmd; 336 | size_t wspace = inputln.find(" "); 337 | try{ 338 | cmd=command_codes.at(inputln.substr(0,wspace)); 339 | } catch (const std::out_of_range& err){ 340 | cmd = nop; 341 | //if (!ctx->do_exit) 342 | // cout << "invalid command: " << inputln << ". Try: help" << endl; // this catches end of lines also... 343 | } 344 | 345 | //cout << "got cmd: "<; 372 | std::thread local(localc); 373 | 374 | std::thread remote(remotec,"tcp://*:5556"); 375 | //std::thread monitor(monitorc,"null"); 376 | command_processed=true; 377 | 378 | while(!ctx->do_exit){ 379 | cmd=nop; 380 | //cin >> inputln; 381 | //input = readline(": "); 382 | //add_history(input); 383 | //inputln = std::string(input); 384 | inputln = commandqueue->pop(); 385 | 386 | //cout << "got command: '"<< inputln <<"'" << endl; 387 | 388 | cmd = ctx->parsecmd(inputln); 389 | options = ctx->getoptionstr(inputln); 390 | if (!ctx->do_exit) //reorder the loop to get rid of this check... 391 | switch (cmd){ 392 | case help: 393 | cout <<"help"<cmdfs(options); 397 | break; 398 | case add: 399 | ctx->cmdadd(options); 400 | break; 401 | case del: 402 | ctx->cmddel(options); 403 | break; 404 | case status: 405 | ctx->cmdstatus(options); 406 | /*{ 407 | int numsynch=0; 408 | for (auto *d : *ctx->devices){ 409 | if(d->get_synchronized()) numsynch++; 410 | } 411 | cout << to_string(numsynch) <<" / " << to_string(ctx->devices->size()) << " synchronized" << endl; 412 | for (lvector::iterator it = ctx->devices->begin(); it != ctx->devices->end(); ++it) 413 | { 414 | cout<< to_string((*it)->get_lagp()->lag)<< "\t"; 415 | } 416 | cout << endl; 417 | }*/ 418 | break; 419 | case list: 420 | ctx->cmdlist(options); 421 | break; 422 | case logs: 423 | { 424 | if (read(ctx->stderr_pipe[0], buf, sizeof(buf))>0) //this works! 425 | cout << std::string(buf) << endl; 426 | } 427 | break; 428 | case nop: 429 | break; 430 | case fcenter: 431 | ctx->cmdretune(options); 432 | break; 433 | case request: 434 | ctx->cmdrequest(options); 435 | break; 436 | case phase: 437 | ctx->cmdphase(options); 438 | break; 439 | case quit: 440 | ctx->request_exit(); 441 | exit_console = true; 442 | break; 443 | default: 444 | break; 445 | 446 | } 447 | command_processed=true; 448 | } 449 | //free(input); 450 | 451 | if (local.joinable()) local.join(); 452 | if (remote.joinable()) remote.join(); 453 | 454 | //monitor.join(); 455 | delete commandqueue; 456 | 457 | cout << "console thread exiting"<. 16 | */ 17 | 18 | #include "cpacketizer.h" 19 | #include 20 | #include 21 | #include "cdsp.h" 22 | 23 | #include 24 | 25 | int cpacketize::objcount=0; 26 | uint32_t cpacketize::globalseqn=0; 27 | std::condition_variable cpacketize::cv; 28 | zmq::socket_t *cpacketize::socket; 29 | zmq::context_t *cpacketize::context; 30 | std::mutex cpacketize::bmutex; 31 | std::unique_ptr cpacketize::packetbuf0; 32 | std::unique_ptr cpacketize::packetbuf1; 33 | bool cpacketize::noheader; 34 | size_t cpacketize::packetlen=0; 35 | 36 | int cpacketize::writecnt=0; 37 | bool cpacketize::bufferfilled=false; 38 | uint32_t cpacketize::blocksize=0; 39 | bool cpacketize::do_exit=false; 40 | std::vector> cpacketize::pcorrection; 41 | 42 | 43 | zmq::socket_t *debugsocket; 44 | 45 | cpacketize::cpacketize(){ 46 | 47 | objcount++; //this is a stupid approach. should just alloc a large buffer beforehand...realloc sparsely if need be. 48 | if (packetlength(objcount,blocksize)>packetlen){ 49 | resize_buffers(objcount,blocksize); 50 | } 51 | } 52 | cpacketize::~cpacketize(){ 53 | objcount--; 54 | resize_buffers(objcount,blocksize); 55 | } 56 | 57 | 58 | void cpacketize::init(std::string address,bool noheader_,uint32_t nchannels_,uint32_t blocksize_){ 59 | context = new zmq::context_t(1); 60 | socket = new zmq::socket_t(*context,ZMQ_PUB); 61 | socket->bind(address.data()); 62 | noheader = noheader_; 63 | blocksize= blocksize_; 64 | 65 | debugsocket = new zmq::socket_t(*context,ZMQ_PUB); 66 | debugsocket->bind("tcp://*:5557"); 67 | 68 | packetbuf0 = std::make_unique(cpacketize::packetlength(nchannels_,blocksize_)); 69 | packetbuf1 = std::make_unique(cpacketize::packetlength(nchannels_,blocksize_)); 70 | 71 | packetlen = cpacketize::packetlength(nchannels_,blocksize_); 72 | 73 | pcorrection.resize(nchannels_,std::complex(0.0f,0.0f)); 74 | } 75 | 76 | void cpacketize::cleanup(){ 77 | socket->close(); 78 | debugsocket->close(); 79 | delete context; 80 | delete socket; 81 | 82 | delete debugsocket; 83 | } 84 | 85 | 86 | 87 | void cpacketize::request_exit(){ 88 | do_exit = true; 89 | } 90 | 91 | size_t cpacketize::packetlength(uint32_t N,uint32_t L){ 92 | if (noheader) 93 | return (2*N*L); 94 | else 95 | return (16 + 4*N) + 2*N*L; 96 | } 97 | 98 | void cpacketize::resize_buffers(uint32_t N, uint32_t L){ 99 | size_t plen = packetlength(N,L); 100 | {//lock, switch pointers, delete, release lock 101 | std::lock_guard lock(bmutex); 102 | packetbuf0 = std::make_unique(cpacketize::packetlength(N,L)); 103 | packetbuf1 = std::make_unique(cpacketize::packetlength(N,L)); 104 | 105 | pcorrection.resize(N,std::complex(0.0f,0.0f)); 106 | } 107 | } 108 | 109 | int cpacketize::send(){ 110 | if (!noheader){ 111 | //fill static header. block readcounts filled by calls to write: 112 | hdr0 *hdr = (hdr0 *) packetbuf0.get(); 113 | hdr->globalseqn = globalseqn++; 114 | hdr->N = objcount; //nchannels; 115 | hdr->L = blocksize >> 1; 116 | hdr->unused = 0; 117 | //std::cout << "sending packet: "<N) <<"x" << std::to_string(hdr->L) << "header " << std::to_string(noheader) << std::endl; 118 | } 119 | 120 | { 121 | std::unique_lock lock(bmutex); 122 | cv.wait(lock,[]{return ((bufferfilled) || (do_exit));}); 123 | bufferfilled = false; 124 | } 125 | socket->send(packetbuf1.get(),packetlen,0); 126 | //printf("sending %d phase factors\n",objcount); 127 | debugsocket->send(pcorrection.data(),objcount*sizeof(std::complex),0); 128 | return 0; 129 | } 130 | 131 | int cpacketize::writedebug(uint32_t channeln,std::complex p){ 132 | pcorrection[channeln] = p; 133 | return 0; 134 | } 135 | 136 | //the idea is that each thread could call this method from it's own context, as we're writing to separate locations 137 | int cpacketize::write(uint32_t channeln,uint32_t readcnt,int8_t *rp){ 138 | uint32_t loc; 139 | 140 | if (!noheader){ 141 | //fill dynamic size part of header, write readcounts 142 | *(((uint32_t *)packetbuf0.get()) + sizeof(hdr0)/sizeof(uint32_t)+channeln)=readcnt; 143 | loc = (sizeof(hdr0)+objcount*sizeof(uint32_t)) + channeln*blocksize; 144 | } 145 | else{ 146 | loc = channeln*blocksize; 147 | } 148 | 149 | //printf("writing to %p, %d bytes, %d reserved size...",(int8_t *) (packetbuf0.get() +loc),blocksize, packetlength(objcount,blocksize)); 150 | //copy data 151 | std::memcpy((int8_t *) (packetbuf0.get() +loc),rp,blocksize); 152 | //printf("written\n"); 153 | 154 | //if(writecnt--==0) 155 | return 0; 156 | } 157 | 158 | int cpacketize::write(uint32_t channeln,uint32_t readcnt,std::complex *in){ 159 | uint32_t loc; 160 | 161 | if (!noheader){ 162 | //fill dynamic size part of header, write readcounts 163 | *(((uint32_t *)packetbuf0.get()) + sizeof(hdr0)/sizeof(uint32_t)+channeln)=readcnt; 164 | loc = (sizeof(hdr0)+objcount*sizeof(uint32_t)) + channeln*blocksize; 165 | } 166 | else{ 167 | loc = channeln*blocksize; 168 | } 169 | cdsp::convto8bit((std::complex *) (packetbuf0.get()+loc),in, (blocksize>>1)); 170 | //std::memcpy((int8_t *) (packetbuf0.get() +loc),rp,blocksize); 171 | return 0; 172 | } 173 | 174 | int cpacketize::notifysend(){ 175 | std::unique_lock lock(bmutex); 176 | 177 | packetbuf0.swap(packetbuf1); 178 | writecnt = objcount; 179 | 180 | bufferfilled = true; 181 | lock.unlock(); 182 | 183 | cv.notify_one(); 184 | return 0; 185 | } 186 | 187 | 188 | -------------------------------------------------------------------------------- /src/crtlsdr.cc: -------------------------------------------------------------------------------- 1 | /* 2 | coherent-rtlsdr 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | coherent-rtlsdr is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with coherent-rtlsdr. If not, see . 16 | */ 17 | 18 | #include "csdrdevice.h" 19 | #include 20 | #include 21 | //extern barrier* bar; 22 | //barrier *crtlsdr::startbarrier = NULL; 23 | 24 | void crtlsdr::start(barrier *b){ 25 | startbarrier = b; 26 | streaming = true; 27 | thread = std::thread(crtlsdr::asynch_threadf,this); 28 | controller->start(); 29 | 30 | } 31 | 32 | void crtlsdr::startcontrol(){ 33 | //controller->start(); 34 | } 35 | 36 | void crtlsdr::stop(){ 37 | rtlsdr_cancel_async(dev); 38 | streaming = false; 39 | cv.notify_all(); 40 | controller->request_exit(); 41 | std::raise(SIGUSR1); 42 | } 43 | 44 | void crtlsdr::asynch_threadf(crtlsdr *d){ //a static func 45 | int ret; 46 | //std::cout << "starting #" << std::to_string(d->devnum) << std::endl; 47 | 48 | d->startbarrier->wait(); 49 | 50 | ret = rtlsdr_reset_buffer(d->dev); 51 | if (ret<0) 52 | std::cerr << "rtlsdr_reset_buffer failed for#" << std::to_string(d->devnum) << std::endl; 53 | 54 | ret = rtlsdr_read_async(d->dev,crtlsdr::asynch_callback, (void *)d, d->asyncbufn, d->blocksize); 55 | if (ret<0) 56 | std::cerr << "rtlsdr_read_async failed for #" << std::to_string(d->devnum) << std::endl; 57 | else 58 | std::cerr << "asynch_thread #" << std::to_string(d->devnum) << "exited" << std::endl; 59 | } 60 | 61 | void crtlsdr::asynch_callback(unsigned char *buf, uint32_t len, void *ctx) 62 | { 63 | if (ctx) { 64 | ((csdrdevice *)ctx) -> swapbuffer(buf); 65 | //csdrdevice *d = (csdrdevice *)ctx; 66 | //d->swapbuffer(buf); 67 | } 68 | } 69 | 70 | uint32_t crtlsdr::get_device_count(){ 71 | return rtlsdr_get_device_count(); 72 | } 73 | 74 | std::string crtlsdr::get_device_name(uint32_t index){ 75 | return rtlsdr_get_device_name(index); 76 | } 77 | 78 | int crtlsdr::get_index_by_serial(std::string serial){ 79 | return rtlsdr_get_index_by_serial(serial.data()); //(const char *serial); 80 | } 81 | 82 | std::string crtlsdr::get_device_serial(uint32_t index){ 83 | char manufact[256]; 84 | char product[256]; 85 | char serial[256]; 86 | int ret = rtlsdr_get_device_usb_strings(index,manufact,product,serial); 87 | if (ret<0){ 88 | std::cerr << "get_device_serial() failed for #"<< std::to_string(index) << std::endl; 89 | return ""; 90 | } 91 | 92 | return serial; 93 | } 94 | 95 | std::string crtlsdr::get_usb_str_concat(uint32_t index){ 96 | char manufact[256]; 97 | char product[256]; 98 | char serial[256]; 99 | int ret = rtlsdr_get_device_usb_strings(index,manufact,product,serial); 100 | if (ret<0){ 101 | std::cerr << "get_device_serial() failed for #"<< std::to_string(index) << std::endl; 102 | return ""; 103 | } 104 | 105 | return std::string(serial) +" : " + std::string(product) + " : " + std::string(manufact); 106 | } 107 | 108 | int crtlsdr::open(std::string name){ 109 | return open(crtlsdr::get_index_by_serial(name.data())); 110 | } 111 | 112 | int crtlsdr::open(uint32_t index){ 113 | 114 | int ret; 115 | devnum = index; 116 | //std::cout << "opening #" << std::to_string(index) << std::endl; 117 | ret = rtlsdr_open(&dev,index); 118 | if (ret!=0) return ret; 119 | ret = set_samplerate(samplerate); 120 | if (ret!=0) return ret; 121 | ret = rtlsdr_set_dithering(dev, 0); //THIS MUST PRECEDE THE TUNING FREQ CALL, OTHERWISE IT WILL FAIL! 122 | if (ret!=0) return ret; 123 | ret = set_fcenter(fcenter); 124 | if (ret!=0) return ret; 125 | ret = set_agcmode(enableagc); 126 | if (ret!=0) return ret; 127 | ret = set_tunergainmode(1); 128 | if (ret!=0) return ret; 129 | ret = set_tunergain(rfgain); 130 | if (ret!=0) return ret; 131 | ret = set_correction_f(0.0f); //this must be zeroed. value is retained after close... 132 | 133 | devname = crtlsdr::get_device_serial(devnum); 134 | return ret; 135 | } 136 | 137 | int crtlsdr::close(){ 138 | return rtlsdr_close(dev); 139 | } 140 | 141 | 142 | int crtlsdr::set_fcenter(uint32_t f){ 143 | fcenter=f; 144 | rtlsdr_set_dithering(dev, 0); 145 | return rtlsdr_set_center_freq(dev,fcenter); 146 | } 147 | 148 | int crtlsdr::set_samplerate(uint32_t fs){ 149 | samplerate = fs; 150 | return rtlsdr_set_sample_rate(dev,fs); 151 | } 152 | 153 | int crtlsdr::set_agcmode(bool flag){ 154 | uint32_t agc = (flag) ? 1 : 0; 155 | return rtlsdr_set_agc_mode(dev, agc); 156 | } 157 | 158 | int crtlsdr::set_tunergain(uint32_t gain){ 159 | rfgain = gain; 160 | return rtlsdr_set_tuner_gain(dev, gain); 161 | } 162 | 163 | int crtlsdr::set_tunergainmode(uint32_t mode){ 164 | return rtlsdr_set_tuner_gain_mode(dev, mode); 165 | } 166 | 167 | int crtlsdr::set_correction_f(float f){ 168 | //std::cout << "set correction on dev " << std::to_string(devnum) << ":" << std::to_string((long int )dev) << std::endl; 169 | return ((dev!=NULL) && (!do_exit)) ? rtlsdr_set_sample_freq_correction_f(dev,f) : -1; 170 | } 171 | 172 | 173 | int8_t* crtlsdr::swapbuffer(uint8_t *b){ 174 | //auto t = std::chrono::high_resolution_clock::now(); 175 | //auto t_ns= (std::chrono::time_point_cast(t)).time_since_epoch(); 176 | 177 | //if (streaming){ 178 | { 179 | std::unique_lock lock(mtx); 180 | s8bit.setbufferptr(b,get_readcnt()); //postincremented 181 | inc_readcnt(); //was included in mutex scope. 182 | 183 | 184 | 185 | //cdsp::convtosigned(b, s8bit, blocksize); 186 | //s8bit.readcnt = inc_readcnt(); 187 | //s8bit.timestamp= t_ns.count(); 188 | newdata++; //newdata=true; 189 | } 190 | //} 191 | cv.notify_all(); 192 | return s8bit.getbufferptr(); 193 | } 194 | 195 | int8_t* crtlsdr::read(){ 196 | if (streaming){ 197 | std::unique_lock lock(mtx); 198 | cv.wait(lock, [this]{return (newdata.load()>0);}); 199 | newdata--; //false; 200 | return s8bit.getbufferptr(); //this has to be inside mutex scope, otherwise swapbuffer may modify reaccnt and we return wrong buffer occasionally. 201 | } 202 | return s8bit.getbufferptr(); // double return statements, in case streaming is false. 203 | } 204 | 205 | const std::complex *crtlsdr::convtofloat(){ 206 | return cdsp::convtofloat(sfloat,s8bit.getbufferptr(),blocksize); 207 | } 208 | 209 | 210 | const std::complex *crtlsdr::convtofloat(const std::complex *p){ 211 | return cdsp::convtofloat(p,s8bit.getbufferptr(),blocksize); 212 | } 213 | 214 | 215 | const std::complex *crefsdr::convtofloat(){ 216 | cdsp::convtofloat(sfloat+(blocksize>>1),s8bit.getbufferptr(),blocksize); 217 | return sfloat + (blocksize>>1); 218 | } 219 | 220 | const std::complex *crefsdr::convtofloat(const std::complex *p){ 221 | cdsp::convtofloat(p+(blocksize>>1),s8bit.getbufferptr(),blocksize); 222 | return p + (blocksize >> 1); 223 | } 224 | 225 | void crefsdr::start(barrier *b){ 226 | startbarrier = b; 227 | thread = std::thread(crtlsdr::asynch_threadf,this); 228 | streaming = true; 229 | //controller->start(); 230 | } 231 | 232 | const std::complex* crefsdr::get_sptr(){ 233 | return sfloat;// + (blocksize >> 1); 234 | } -------------------------------------------------------------------------------- /src/csdrdevice.cc: -------------------------------------------------------------------------------- 1 | /* 2 | coherent-rtlsdr 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | coherent-rtlsdr is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with coherent-rtlsdr. If not, see . 16 | */ 17 | 18 | #include "csdrdevice.h" 19 | 20 | csdrdevice::csdrdevice(uint32_t asyncbufn_, uint32_t blocksize_, uint32_t samplerate_, uint32_t fcenter_) : thread(){ 21 | asyncbufn = asyncbufn_; 22 | blocksize = blocksize_; 23 | samplerate= samplerate_; 24 | fcenter = fcenter_; 25 | 26 | realfs = 0; 27 | readcnt = 0; 28 | do_exit = false; 29 | lagrequested = true; 30 | lagready =false; 31 | synced = false; 32 | newdata = 0; //false; 33 | streaming=false; 34 | devname = ""; 35 | 36 | lagp.lag = 0; 37 | lagp.ts = 0; 38 | 39 | phasecorr = std::complex(1.0f,0.0f); 40 | phasecorrprev = std::complex(1.0f,0.0f); 41 | 42 | int alignment = volk_get_alignment(); 43 | sfloat = (lv_32fc_t *) volk_malloc(sizeof(lv_32fc_t)*blocksize,alignment); 44 | 45 | std::memset(sfloat,0,sizeof(lv_32fc_t)*blocksize); 46 | 47 | controller = new ccontrol(this); 48 | } 49 | 50 | csdrdevice::~csdrdevice(){ 51 | if (thread.joinable()) thread.join(); 52 | 53 | volk_free(sfloat); 54 | 55 | delete controller; 56 | } 57 | 58 | std::complex csdrdevice::est_phasecorrect(const lv_32fc_t *ref){ 59 | 60 | float alpha = 0.5f; 61 | std::complex correlation=0.0f; 62 | volk_32fc_x2_conjugate_dot_prod_32fc((lv_32fc_t *) &correlation,sfloat,ref,(blocksize >> 1)); 63 | phasecorr = std::conj(correlation) * (1.0f /std::abs(correlation)); 64 | 65 | 66 | phasecorr = alpha*phasecorr + (1-alpha)*phasecorrprev; 67 | phasecorrprev = phasecorr; 68 | return phasecorr; 69 | } 70 | 71 | float csdrdevice::est_PAPR(const lv_32fc_t *ref){ 72 | 73 | 74 | } 75 | 76 | std::complex csdrdevice::get_phasecorrect(){ 77 | return phasecorr; 78 | } 79 | 80 | std::complex *csdrdevice::phasecorrect(){ 81 | 82 | cdsp::scalarmul(sfloat,sfloat,phasecorr,(blocksize>>1)); 83 | return sfloat; 84 | } 85 | 86 | //float est_phasecorrect(const lv_32fc_t *ref); 87 | // float get_phasecorrect(); 88 | -------------------------------------------------------------------------------- /src/main.cc: -------------------------------------------------------------------------------- 1 | /* 2 | coherent-rtlsdr 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | coherent-rtlsdr is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with coherent-rtlsdr. If not, see . 16 | */ 17 | 18 | //builds with: 19 | // g++ main.cc ccoherent.cc crtlsdr.cc cdsp.cc console.cc -lm -lpthread -lrtlsdr -lvolk -lfftw3f 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "console.h" 32 | #include "csdrdevice.h" 33 | #include "ccoherent.h" 34 | #include "cpacketizer.h" 35 | #include "common.h" 36 | #include "crefnoise.h" 37 | #include "cconfigfile.h" 38 | 39 | using namespace std; 40 | int stderr_pipe[2]; //for redirecting stderr, where librtlsdr outputs 41 | FILE *stderr_f; 42 | 43 | std::atomic exit_all; 44 | //std::sig_atomic_t sig; 45 | //barrier *bar; 46 | 47 | void signal_handler(int signal) 48 | { 49 | if (signal==SIGINT){ 50 | //cout << "caught SIGINT" << std::to_string(signal) << endl; 51 | cout << endl << "caught CTRL+C (SIGINT), exiting after CTRL+D..." <fs=(uint32_t)atof(optarg); 115 | break; 116 | case 'b': 117 | ops->blocksize = (uint32_t)atoi(optarg); 118 | break; 119 | case 'f': 120 | ops->fc=(uint32_t) atof(optarg); 121 | break; 122 | case 'h': 123 | usage(); 124 | break; 125 | case 'n': 126 | if ((uint32_t)atoi(optarg)<=ops->ndev) 127 | ops->ndev =(uint32_t)atoi(optarg); 128 | else 129 | cout << "Requested device count higher than devices connected to system " 130 | << to_string(ops->ndev) << ", setting ndev=" << to_string(ops->ndev) << endl; 131 | break; 132 | case 'g': 133 | ops->gain = (uint32_t) atoi(optarg)*10; 134 | break; 135 | case 'r': 136 | ops->refgain = (uint32_t) atoi(optarg)*10; 137 | break; 138 | case 'I': 139 | ops->refname=std::string(optarg); 140 | break; 141 | case 'C': 142 | ops->config_fname = std::string(optarg); 143 | ops->use_cfg = true; 144 | break; 145 | case 'A': 146 | ops->agc = true; 147 | break; 148 | case 'R': 149 | ops->no_header = true; 150 | break; 151 | case 'q': 152 | ops->quiet = true; 153 | break; 154 | default: 155 | usage(); 156 | break; 157 | } 158 | } 159 | return 0; 160 | }; 161 | 162 | int main(int argc, char **argv) 163 | { 164 | 165 | int nfft = 8; 166 | 167 | cl_ops ops = {"M REF",false,2048000,uint32_t(1024e6),8,1<<14,4,500,500,false,"",false,false}; 168 | ops.ndev = crtlsdr::get_device_count(); 169 | cout << to_string(ops.ndev) << " devices found." << endl; 170 | parsecommandline(&ops,argc,argv); 171 | 172 | cout << "ops parsed\n"<< endl; 173 | if (ops.no_header) 174 | cout << "streaming in raw mode" << endl; 175 | 176 | if (crtlsdr::get_index_by_serial(ops.refname)<0){ 177 | cout << "reference '"<< ops.refname <<"' not found, exiting" << endl; 178 | exit(1); 179 | } 180 | 181 | { 182 | barrier *startbarrier; 183 | crefnoise * refnoise = new crefnoise("/dev/ttyACM0"); 184 | 185 | //redirect stderr stream 186 | std::stringstream buffer; 187 | std::streambuf *old; 188 | 189 | exit_all=false; 190 | 191 | if (ops.quiet){ 192 | old = std::cerr.rdbuf(buffer.rdbuf()); 193 | redir_stderr(true); 194 | } 195 | 196 | std::signal(SIGINT,signal_handler); 197 | std::signal(SIGUSR1,int_handler); 198 | 199 | //std::vector v_devices; //CHANGED 200 | lvector v_devices; 201 | 202 | //read device order from config file, if not given, build order by device index: 203 | std::vector vdefs; 204 | if (ops.use_cfg){ 205 | cout << "Reading config " << ops.config_fname << endl; 206 | vdefs = cconfigfile::readconfig(ops.config_fname); 207 | ops.ndev = vdefs.size(); 208 | ops.refname = cconfigfile::get_refname(vdefs); 209 | } 210 | else{ 211 | uint32_t refindex = crtlsdr::get_index_by_serial(ops.refname); 212 | for(uint32_t n=0;nopen(ops.refname)){ 226 | cout <<"could not open reference, serial number:'" << ops.refname <<"'"<open(n.serial)){ 235 | delete v_devices.back(); 236 | v_devices.pop_back(); 237 | cout<<"!"; 238 | } 239 | else 240 | { 241 | cout<<"*"; 242 | v_devices.back()->set_agcmode(ops.agc); 243 | } 244 | cout.flush(); 245 | /*if (exit_all){ 246 | break; 247 | }*/ 248 | } 249 | cout << endl; 250 | 251 | cout << "creating barrier" <start(startbarrier); 256 | cout << "starting capture on the signal devices" <start(startbarrier); 259 | 260 | //cpacketize* packetize = new cpacketize(v_devices.size(),ops.blocksize, "tcp://*:5555", false); 261 | cpacketize::init("tcp://*:5555",ops.no_header,v_devices.size()+1,ops.blocksize); 262 | 263 | cconsole console(stderr_pipe,ref_dev, &v_devices,refnoise); 264 | 265 | console.start(); 266 | 267 | //sleep(1); 268 | 269 | cout << "starting coherence" <stop(); ref_dev->request_exit(); ref_dev->close(); 287 | 288 | for (auto d : v_devices){ 289 | d->stop(); 290 | d->request_exit(); 291 | } 292 | 293 | cout << "stopping console" <close(); 301 | 302 | console.join(); 303 | 304 | if (ops.quiet){ 305 | std::cerr.rdbuf( old ); 306 | close(stderr_pipe[0]); 307 | } 308 | 309 | delete startbarrier; 310 | cout << "closing reference" <