diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1d4be5f --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +csdr +nmux +ddcd +*.o +*.so +tags +dumpvect.*.vect diff --git a/Makefile b/Makefile index 4bcd3f8..7db181a 100644 --- a/Makefile +++ b/Makefile @@ -26,12 +26,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - LIBSOURCES = fft_fftw.c libcsdr_wrapper.c #SOURCES = csdr.c $(LIBSOURCES) cpufeature = $(if $(findstring $(1),$(shell cat /proc/cpuinfo)),$(2)) -PARAMS_SSE = $(call cpufeature,sse,-msse) $(call cpufeature,sse2,-msse2) $(call cpufeature,sse3,-msse3) $(call cpufeature,sse4,-msse4) $(call cpufeature,sse4_1,-msse4.1) $(call cpufeature,sse4_2,-msse4.2) -mfpmath=sse +PARAMS_SSE = $(call cpufeature,sse,-msse) $(call cpufeature,sse2,-msse2) $(call cpufeature,sse3,-msse3) $(call cpufeature,sse4a,-msse4a) $(call cpufeature,sse4_1,-msse4.1) $(call cpufeature,sse4_2,-msse4.2 -msse4) -mfpmath=sse PARAMS_NEON = -mfloat-abi=hard -march=armv7-a -mtune=cortex-a8 -mfpu=neon -mvectorize-with-neon-quad -funsafe-math-optimizations -Wformat=0 -DNEON_OPTS #tnx Jan Szumiec for the Raspberry Pi support PARAMS_RASPI = -mfloat-abi=hard -mcpu=arm1176jzf-s -mfpu=vfp -funsafe-math-optimizations -Wformat=0 @@ -41,26 +39,38 @@ PARAMS_LOOPVECT = -O3 -ffast-math -fdump-tree-vect-details -dumpbase dumpvect PARAMS_LIBS = -g -lm -lrt -lfftw3f -DUSE_FFTW -DLIBCSDR_GPL -DUSE_IMA_ADPCM PARAMS_SO = -fpic PARAMS_MISC = -Wno-unused-result +#DEBUG_ON = 0 #debug is always on by now (anyway it could be compiled with `make DEBUG_ON=1`) +#PARAMS_DEBUG = $(if $(DEBUG_ON),-g,) FFTW_PACKAGE = fftw-3.3.3 -all: clean-vect +.PHONY: clean-vect clean +all: csdr nmux +libcsdr.so: fft_fftw.c fft_rpi.c libcsdr_wrapper.c libcsdr.c libcsdr_gpl.c fastddc.c fastddc.h fft_fftw.h fft_rpi.h ima_adpcm.h libcsdr_gpl.h libcsdr.h predefined.h @echo NOTE: you may have to manually edit Makefile to optimize for your CPU \(especially if you compile on ARM, please edit PARAMS_NEON\). @echo Auto-detected optimization parameters: $(PARAMS_SIMD) @echo + rm -f dumpvect*.vect gcc -std=gnu99 $(PARAMS_LOOPVECT) $(PARAMS_SIMD) $(LIBSOURCES) $(PARAMS_LIBS) $(PARAMS_MISC) -fpic -shared -o libcsdr.so -./parsevect dumpvect*.vect +csdr: csdr.c libcsdr.so gcc -std=gnu99 $(PARAMS_LOOPVECT) $(PARAMS_SIMD) csdr.c $(PARAMS_LIBS) -L. -lcsdr $(PARAMS_MISC) -o csdr +ddcd: ddcd.cpp libcsdr.so ddcd.h + g++ $(PARAMS_LOOPVECT) $(PARAMS_SIMD) ddcd.cpp $(PARAMS_LIBS) -L. -lcsdr -lpthread $(PARAMS_MISC) -o ddcd +nmux: nmux.cpp libcsdr.so nmux.h tsmpool.cpp tsmpool.h + g++ $(PARAMS_LOOPVECT) $(PARAMS_SIMD) nmux.cpp tsmpool.cpp $(PARAMS_LIBS) -L. -lcsdr -lpthread $(PARAMS_MISC) -o nmux arm-cross: clean-vect #note: this doesn't work since having added FFTW arm-linux-gnueabihf-gcc -std=gnu99 -O3 -fshort-double -ffast-math -dumpbase dumpvect-arm -fdump-tree-vect-details -mfloat-abi=softfp -march=armv7-a -mtune=cortex-a9 -mfpu=neon -mvectorize-with-neon-quad -Wno-unused-result -Wformat=0 $(SOURCES) -lm -o ./csdr clean-vect: rm -f dumpvect*.vect clean: clean-vect - rm -f libcsdr.so csdr -install: + rm -f libcsdr.so csdr ddcd nmux +install: all install -m 0755 libcsdr.so /usr/lib install -m 0755 csdr /usr/bin install -m 0755 csdr-fm /usr/bin + install -m 0755 nmux /usr/bin + #-install -m 0755 ddcd /usr/bin ldconfig uninstall: rm /usr/lib/libcsdr.so /usr/bin/csdr /usr/bin/csdr-fm @@ -82,7 +92,7 @@ emcc-get-deps: emmake make; \ emmake make install emcc: - emcc -O3 -Isdr.js/$(FFTW_PACKAGE)/api -Lsdr.js/$(FFTW_PACKAGE)/emscripten-lib -o sdr.js/sdrjs-compiled.js fft_fftw.c libcsdr_wrapper.c -DLIBCSDR_GPL -DUSE_IMA_ADPCM -DUSE_FFTW -lfftw3f -s EXPORTED_FUNCTIONS="`python sdr.js/exported_functions.py`" + emcc -O3 -Isdr.js/$(FFTW_PACKAGE)/api -Lsdr.js/$(FFTW_PACKAGE)/emscripten-lib -o sdr.js/sdrjs-compiled.js fft_fftw.c libcsdr_wrapper.c -s TOTAL_MEMORY=67108864 -DLIBCSDR_GPL -DUSE_IMA_ADPCM -DUSE_FFTW -lfftw3f -s EXPORTED_FUNCTIONS="`python sdr.js/exported_functions.py`" cat sdr.js/sdrjs-header.js sdr.js/sdrjs-compiled.js sdr.js/sdrjs-footer.js > sdr.js/sdr.js emcc-beautify: bash -c 'type js-beautify >/dev/null 2>&1; if [ $$? -eq 0 ]; then js-beautify sdr.js/sdr.js >sdr.js/sdr.js.beautiful; mv sdr.js/sdr.js.beautiful sdr.js/sdr.js; fi' diff --git a/README.md b/README.md index 5b360e9..e0f2ddc 100644 --- a/README.md +++ b/README.md @@ -205,7 +205,7 @@ Internally, a sine and cosine wave is generated to perform this function, and th shift_addition_cc -Operation is the same as with `shift_math_cc`. +Operation is the same as for `shift_math_cc`. Internally, this function uses trigonometric addition formulas to generate sine and cosine, which is a bit faster. (About 4 times on the machine I have tested it on.) @@ -219,6 +219,20 @@ Operation is the same as with `shift_math_cc`. Internally, this function uses a look-up table (LUT) to recall the values of the sine function (for the first quadrant). The higher the table size is, the smaller the phase error is. + shift_addfast_cc + +Operation is the same as for `shift_math_cc`. + +Internally, this function uses a NEON-accelerated algorithm on capable systems, so it is advised to use this one on ARM boards. + + shift_unroll_cc + +Operation is the same as for `shift_math_cc`. + +This uses a modified algoritm that first stores a vector of sine and cosine values for given phase differences. + +The loop in this function unrolls quite well if compiled on a PC. It was the fastest one on an i7 CPU during the tests. + decimating_shift_addition_cc [decimation] It shifts the input signal in the frequency domain, and also decimates it, without filtering. It will be useful as a part of the FFT channelizer implementation (to be done). @@ -501,6 +515,15 @@ To remove *sdr.js* and the compiled dependencies: make emcc-clean +## [nmux] (#nmux) + +The repo also contains a command line tool called `nmux`, which is a TCP stream multiplexer. It reads data from the standard input, and sends it to each client connected through TCP sockets. Available command line options are: +* `--port (-p), --address (-a):` TCP port and address to listen. +* `--bufsize (-b), --bufcnt (-n)`: Internal buffer size and count. +* `--help (-h)`: Show help message. + +`nmux` was originally written for use in OpenWebRX. + ## [Licensing] (#licensing) Most of the code of `libcsdr` is under BSD license. diff --git a/csdr.c b/csdr.c index 048ab06..7353b85 100644 --- a/csdr.c +++ b/csdr.c @@ -48,7 +48,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "ima_adpcm.h" #include #include +#include #include +#include "fastddc.h" char usage[]= "csdr - a simple commandline tool for Software Defined Radio receiver DSP.\n\n" @@ -61,6 +63,8 @@ char usage[]= " convert_f_s8\n" " convert_f_s16\n" " convert_s16_f\n" +" convert_f_s24 [--bigendian]\n" +" convert_s24_f [--bigendian]\n" " realpart_cf\n" " clipdetect_ff\n" " limit_ff [max_amplitude]\n" @@ -190,6 +194,22 @@ int init_fifo(int argc, char *argv[]) fcntl(fd, F_SETFL, flags | O_NONBLOCK); return fd; } + else if(!strcmp(argv[2],"--fd")) + { + //to use this: + //1. Create a pipe(pipedesc) in your process. + //2. fork() and execl() your process to run csdr, and give pipedesc[0] as parameter after --fd + // Note: when forking, the child process will get a copy of the file descriptor table! That's why this + // works at all, as file descriptor indexes are normally not transferable between processes, except for a *NIX socket way which is quite complicated... + //3. From your parent process, write into pipedesc[1]. + //This is implemented in ddcd, check there to see how to do it! + int fd; + if(sscanf(argv[3], "%d",&fd)<=0) return 0; + fprintf(stderr,"csdr: fd control mode on, fd=%d\n", fd); + int flags = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, flags | O_NONBLOCK); + return fd; + } } return 0; } @@ -493,7 +513,7 @@ int main(int argc, char *argv[]) TRY_YIELD; } } - if((!strcmp(argv[1],"convert_i16_f")) || (!strcmp(argv[1],"convert_s16_f"))) //not tested + if((!strcmp(argv[1],"convert_i16_f")) || (!strcmp(argv[1],"convert_s16_f"))) { if(!sendbufsize(initialize_buffers())) return -2; for(;;) @@ -505,6 +525,34 @@ int main(int argc, char *argv[]) TRY_YIELD; } } + if(!strcmp(argv[1],"convert_f_s24")) + { + int bigendian = (argc>2) && (!strcmp(argv[2],"--bigendian")); + unsigned char* s24buffer = (unsigned char*)malloc(sizeof(unsigned char)*the_bufsize*3); + if(!sendbufsize(initialize_buffers())) return -2; + for(;;) + { + FEOF_CHECK; + FREAD_R; + convert_f_s24(input_buffer, s24buffer, the_bufsize, bigendian); + fwrite(s24buffer, sizeof(unsigned char)*3, the_bufsize, stdout); + TRY_YIELD; + } + } + if(!strcmp(argv[1],"convert_s24_f")) + { + int bigendian = (argc>2) && (!strcmp(argv[2],"--bigendian")); + unsigned char* s24buffer = (unsigned char*)malloc(sizeof(unsigned char)*the_bufsize*3); + if(!sendbufsize(initialize_buffers())) return -2; + for(;;) + { + FEOF_CHECK; + fread(s24buffer, sizeof(unsigned char)*3, the_bufsize, stdin); + convert_s24_f(s24buffer, output_buffer, the_bufsize, bigendian); + FWRITE_R; + TRY_YIELD; + } + } if(!strcmp(argv[1],"realpart_cf")) { if(!sendbufsize(initialize_buffers())) return -2; @@ -585,6 +633,7 @@ int main(int argc, char *argv[]) { FEOF_CHECK; if(!FREAD_C) break; + starting_phase=shift_math_cc((complexf*)input_buffer, (complexf*)output_buffer, the_bufsize, rate, starting_phase); FWRITE_C; TRY_YIELD; } @@ -618,6 +667,105 @@ int main(int argc, char *argv[]) return 0; } + if(!strcmp(argv[1],"shift_addfast_cc")) + { + bigbufs=1; + + float starting_phase=0; + float rate; + + int fd; + if(fd=init_fifo(argc,argv)) + { + while(!read_fifo_ctl(fd,"%g\n",&rate)) usleep(10000); + } + else + { + if(argc<=2) return badsyntax("need required parameter (rate)"); + sscanf(argv[2],"%g",&rate); + } + + if(!sendbufsize(initialize_buffers())) return -2; + for(;;) + { + shift_addfast_data_t data=shift_addfast_init(rate); + fprintf(stderr,"shift_addfast_cc: reinitialized to %g\n",rate); + int remain, current_size; + float* ibufptr; + float* obufptr; + for(;;) + { + FEOF_CHECK; + if(!FREAD_C) break; + remain=the_bufsize; + ibufptr=input_buffer; + obufptr=output_buffer; + while(remain) + { + current_size=(remain>1024)?1024:remain; + starting_phase=shift_addfast_cc((complexf*)ibufptr, (complexf*)obufptr, current_size, &data, starting_phase); + ibufptr+=current_size*2; + obufptr+=current_size*2; + remain-=current_size; + } + FWRITE_C; + if(read_fifo_ctl(fd,"%g\n",&rate)) break; + TRY_YIELD; + } + } + return 0; + } + + + if(!strcmp(argv[1],"shift_unroll_cc")) + { + bigbufs=1; + + float starting_phase=0; + float rate; + + int fd; + if(fd=init_fifo(argc,argv)) + { + while(!read_fifo_ctl(fd,"%g\n",&rate)) usleep(10000); + } + else + { + if(argc<=2) return badsyntax("need required parameter (rate)"); + sscanf(argv[2],"%g",&rate); + } + + if(!sendbufsize(initialize_buffers())) return -2; + for(;;) + { + shift_unroll_data_t data=shift_unroll_init(rate, 1024); + fprintf(stderr,"shift_unroll_cc: reinitialized to %g\n",rate); + int remain, current_size; + float* ibufptr; + float* obufptr; + for(;;) + { + FEOF_CHECK; + if(!FREAD_C) break; + remain=the_bufsize; + ibufptr=input_buffer; + obufptr=output_buffer; + while(remain) + { + current_size=(remain>1024)?1024:remain; + starting_phase=shift_unroll_cc((complexf*)ibufptr, (complexf*)obufptr, current_size, &data, starting_phase); + ibufptr+=current_size*2; + obufptr+=current_size*2; + remain-=current_size; + } + FWRITE_C; + if(read_fifo_ctl(fd,"%g\n",&rate)) break; + TRY_YIELD; + } + } + return 0; + } + #ifdef LIBCSDR_GPL if(!strcmp(argv[1],"decimating_shift_addition_cc")) { @@ -830,7 +978,7 @@ int main(int argc, char *argv[]) { FEOF_CHECK; FREAD_R; - for(int i=0; i [transition_bw [window]] + { + + int decimation; + if(argc<=2) return badsyntax("need required parameter (decimation)"); + sscanf(argv[2],"%d",&decimation); + + float transition_bw = 0.05; + if(argc>3) sscanf(argv[3],"%g",&transition_bw); + + window_t window = WINDOW_DEFAULT; + if(argc>4) window=firdes_get_window_from_string(argv[4]); + else fprintf(stderr,"fastddc_fwd_cc: window = %s\n",firdes_get_string_from_window(window)); + + fastddc_t ddc; + if(fastddc_init(&ddc, transition_bw, decimation, 0)) { badsyntax("error in fastddc_init()"); return 1; } + fastddc_print(&ddc,"fastddc_fwd_cc"); + + if(!initialize_buffers()) return -2; + sendbufsize(ddc.fft_size); + + //make FFT plan + complexf* input = (complexf*)fft_malloc(sizeof(complexf)*ddc.fft_size); + complexf* windowed = (complexf*)fft_malloc(sizeof(complexf)*ddc.fft_size); + complexf* output = (complexf*)fft_malloc(sizeof(complexf)*ddc.fft_size); + + for(int i=0;i [transition_bw [window]] + { + float shift_rate; + int plusarg=0; + + int fd; + if(fd=init_fifo(argc,argv)) + { + while(!read_fifo_ctl(fd,"%g\n",&shift_rate)) usleep(10000); + plusarg=1; + } + else + { + if(argc<=2) return badsyntax("need required parameter (rate)"); + sscanf(argv[2],"%g",&shift_rate); + } + + int decimation; + if(argc<=3+plusarg) return badsyntax("need required parameter (decimation)"); + sscanf(argv[3+plusarg],"%d",&decimation); + //fprintf(stderr, "dec=%d %d\n", decimation); + + float transition_bw = 0.05; + if(argc>4+plusarg) sscanf(argv[4+plusarg],"%g",&transition_bw); + + window_t window = WINDOW_DEFAULT; + if(argc>5+plusarg) window=firdes_get_window_from_string(argv[5+plusarg]); + else fprintf(stderr,"fastddc_apply_cc: window = %s\n",firdes_get_string_from_window(window)); + + for(;;) + { + + fastddc_t ddc; + if(fastddc_init(&ddc, transition_bw, decimation, shift_rate)) { badsyntax("error in fastddc_init()"); return 1; } + fastddc_print(&ddc,"fastddc_inv_cc"); + + if(!initialize_buffers()) return -2; + sendbufsize(ddc.post_input_size/ddc.post_decimation); //TODO not exactly correct + + //prepare making the filter and doing FFT on it + complexf* taps=(complexf*)calloc(sizeof(complexf),ddc.fft_size); //initialize to zero + complexf* taps_fft=(complexf*)malloc(sizeof(complexf)*ddc.fft_size); + FFT_PLAN_T* plan_taps = make_fft_c2c(ddc.fft_size, taps, taps_fft, 1, 0); //forward, don't benchmark (we need this only once) + + //make the filter + float filter_half_bw = 0.5/decimation; + fprintf(stderr, "fastddc_inv_cc: preparing a bandpass filter of [%g, %g] cutoff rates. Real transition bandwidth is: %g\n", (-shift_rate)-filter_half_bw, (-shift_rate)+filter_half_bw, 4.0/ddc.taps_length); + firdes_bandpass_c(taps, ddc.taps_length, (-shift_rate)-filter_half_bw, (-shift_rate)+filter_half_bw, window); + fft_execute(plan_taps); + fft_swap_sides(taps_fft,ddc.fft_size); + + //make FFT plan + complexf* inv_input = (complexf*)fft_malloc(sizeof(complexf)*ddc.fft_inv_size); + complexf* inv_output = (complexf*)fft_malloc(sizeof(complexf)*ddc.fft_inv_size); + fprintf(stderr,"fastddc_inv_cc: benchmarking FFT..."); + FFT_PLAN_T* plan_inverse = make_fft_c2c(ddc.fft_inv_size, inv_input, inv_output, 0, 1); //inverse, do benchmark + fprintf(stderr," done\n"); + + //alloc. buffers + complexf* input = (complexf*)fft_malloc(sizeof(complexf)*ddc.fft_size); + complexf* output = (complexf*)fft_malloc(sizeof(complexf)*ddc.post_input_size); + + decimating_shift_addition_status_t shift_stat; + bzero(&shift_stat, sizeof(shift_stat)); + for(;;) + { + FEOF_CHECK; + fread(input, sizeof(complexf), ddc.fft_size, stdin); + shift_stat = fastddc_inv_cc(input, output, &ddc, plan_inverse, taps_fft, shift_stat); + fwrite(output, sizeof(complexf), shift_stat.output_size, stdout); + //fprintf(stderr, "ss os = %d\n", shift_stat.output_size); + TRY_YIELD; + if(read_fifo_ctl(fd,"%g\n",&shift_rate)) break; + } + + } + } + + if( !strcmp(argv[1], "_fft2octave") ) + { + int fft_size; + if(argc<=2) return badsyntax("need required parameter (fft_size)"); + sscanf(argv[2],"%d",&fft_size); + + complexf* fft_input=(complexf*)malloc(sizeof(complexf)*fft_size); + initialize_buffers(); + if(!sendbufsize(fft_size)) return -2; + + printf("setenv(\"GNUTERM\",\"X11 noraise\");y=zeros(1,%d);semilogy(y,\"ydatasource\",\"y\");\n",fft_size); + for(;;) + { + FEOF_CHECK; + fread(fft_input, sizeof(complexf), fft_size, stdin); + printf("fftdata=["); + //we have to swap the two parts of the array to get a valid spectrum + for(int i=fft_size/2;i +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ANDRAS RETZLER BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "ddcd.h" + + +int host_port = 0; +char host_address[100] = "127.0.0.1"; +int thread_cntr = 0; + +//CLI parameters +int decimation = 0; +float transition_bw = 0.05; +int bufsize = 1024; //! currently unused +int bufcnt = 1024; +char ddc_method_str[100] = "td"; +ddc_method_t ddc_method; + +void sig_handler(int signo) +{ + fprintf(stderr, MSG_START "signal %d caught, exiting ddcd...\n", signo); + fflush(stderr); + exit(0); +} + +int main(int argc, char* argv[]) +{ + int c; + for(;;) + { + int option_index = 0; + static struct option long_options[] = { + {"port", required_argument, 0, 'p' }, + {"address", required_argument, 0, 'a' }, + {"decimation", required_argument, 0, 'd' }, + {"bufsize", required_argument, 0, 'b' }, + {"bufcnt", required_argument, 0, 'n' }, + {"method", required_argument, 0, 'm' }, + {"transition", required_argument, 0, 't' } + }; + c = getopt_long(argc, argv, "p:a:d:b:n:m:t:", long_options, &option_index); + if(c==-1) break; + switch (c) + { + case 'a': + host_address[100-1]=0; + strncpy(host_address,optarg,100-1); + break; + case 'p': + host_port=atoi(optarg); + break; + case 'd': + decimation=atoi(optarg); + break; + case 'b': + bufsize=atoi(optarg); + break; + case 'n': + bufcnt=atoi(optarg); + break; + case 'm': + ddc_method_str[100-1]=0; + strncpy(ddc_method_str,optarg,100-1); + break; + case 't': + sscanf(optarg,"%g",&transition_bw); + break; + case 0: + case '?': + case ':': + default:; + print_exit(MSG_START "error in getopt_long()\n"); + } + } + + if(!decimation) print_exit(MSG_START "missing required command line argument, --decimation.\n"); + if(!host_port) print_exit(MSG_START "missing required command line argument, --port.\n"); + if(decimation<0) print_exit(MSG_START "invalid value for --decimation (should be >0).\n"); + if(decimation==1) fprintf(stderr, MSG_START "decimation = 1, just copying raw samples.\n"); + if(transition_bw<0||transition_bw>0.5) print_exit(MSG_START "invalid value for --transition (should be between 0 and 0.5).\n"); + if(bufsize<0) print_exit(MSG_START "invalid value for --bufsize (should be >0)\n"); + if(bufcnt<0) print_exit(MSG_START "invalid value for --bufcnt (should be >0)\n"); + if(decimation==1); //don't do anything then //!will have to take care about this later + else if(!strcmp(ddc_method_str,"td")) + { + ddc_method = M_TD; + fprintf(stderr, MSG_START "method is M_TD (default).\n"); + } + else if (!strcmp(ddc_method_str,"fastddc")) + { + ddc_method = M_FASTDDC; + fprintf(stderr, MSG_START "method is M_FASTDDC.\n"); + } + else print_exit(MSG_START "invalid parameter given to --method.\n"); + + //set signals + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sig_handler; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGKILL, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); + + struct sockaddr_in addr_host; + int listen_socket; + std::vector clients; + clients.reserve(100); + listen_socket=socket(AF_INET,SOCK_STREAM,0); + + int sockopt = 1; + if( setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&sockopt, sizeof(sockopt)) == -1 ) + error_exit(MSG_START "cannot set SO_REUSEADDR"); //the best description on SO_REUSEADDR ever: http://stackoverflow.com/a/14388707/3182453 + + memset(&addr_host,'0',sizeof(addr_host)); + addr_host.sin_family = AF_INET; + addr_host.sin_port = htons(host_port); + addr_host.sin_addr.s_addr = INADDR_ANY; + + if( (addr_host.sin_addr.s_addr=inet_addr(host_address)) == INADDR_NONE ) + error_exit(MSG_START "invalid host address"); + + if( bind(listen_socket, (struct sockaddr*) &addr_host, sizeof(addr_host)) < 0 ) + error_exit(MSG_START "cannot bind() address to the socket"); + + if( listen(listen_socket, 10) == -1 ) + error_exit(MSG_START "cannot listen() on socket"); + + fprintf(stderr,MSG_START "listening on %s:%d\n", inet_ntoa(addr_host.sin_addr), host_port); + + struct sockaddr_in addr_cli; + socklen_t addr_cli_len = sizeof(addr_cli); + int new_socket; + + int highfd = 0; + FD_ZERO(&select_fds); + FD_SET(listen_socket, &select_fds); + maxfd(&highfd, listen_socket); + FD_SET(input_fd, &select_fds); + maxfd(&highfd, input_fd); + + //Set stdin and listen_socket to non-blocking + if(set_nonblocking(input_fd) || set_nonblocking(listen_socket)) + error_exit(MSG_START "cannot set_nonblocking()"); + + //Create tsmpool + tsmpool* pool = new tsmpool(bufsize, bufcnt); + if(!pool->ok) print_exit(MSG_START "tsmpool failed to initialize\n"); + + unsigned char* current_write_buffer = pool->get_write_buffer(); + int index_in_current_write_buffer = 0; + + + for(;;) + { + //Let's wait until there is any new data to read, or any new connection! + select(highfd, &select_fds, NULL, NULL, NULL); + + //Is there a new client connection? + if( (new_socket = accept(listen_socket, (struct sockaddr*)&addr_cli, &addr_cli_len)) != -1) + { + clients_close_all_finished(); + if(pthread_create(&new_client->thread, NULL, client_thread , (void*)&new_client)<0) + { + //We're the parent + client_t* new_client = new client_t; + new_client->error = 0; + memcpy(&new_client->addr, &addr_cli, sizeof(new_client->addr)); + new_client->socket = new_socket; + new_client->status = CS_CREATED; + clients.push_back(new_client); + fprintf(stderr, MSG_START "pthread_create() done, clients now: %d\n", clients.size()); + } + else fprintf(stderr, MSG_START "pthread_create() failed.\n"); + } + + if(index_in_current_write_buffer >= bufsize) + { + current_write_buffer = pool->get_write_buffer(); + index_in_current_write_buffer = 0; + } + int retval = read(input_fd, current_write_buffer + index_in_current_write_buffer, bufsize - index_in_current_write_buffer); + if(retval>0) + { + index_in_current_write_buffer += retval; + } + else if(retval==0) + { + //!end of input stream, close clients and exit + print_exit(MSG_START "end of input, exiting.\n") + } + } +} + +#if 0 +for (int i=0; ipipefd[1], buf, retval)==-1) + { + + if(!clients[i]->error) + { + print_client(clients[i], "lost buffer, failed to write pipe."); + clients[i]->error=1; + } + //fprintf(stderr, MSG_START "errno is %d\n", errno); //usually 11 + //int wpstatus; + //int wpresult = waitpid(clients[i]->pid, &wpstatus, WNOHANG); + //fprintf(stderr, MSG_START "pid is %d\n",clients[i]->pid); + //perror("somethings wrong"); + //if(wpresult == -1) print_client(clients[i], "error while waitpid()!"); + //else if(wpresult == 0) + waitpid(clients[i]->pid, NULL, WNOHANG); + if(!proc_exists(clients[i]->pid)) + { + //Client exited! + print_client(clients[i], "closing client from main process."); + close(clients[i]->pipefd[1]); + close(clients[i]->socket); + delete clients[i]; + clients.erase(clients.begin()+i); + fprintf(stderr, MSG_START "done closing client from main process.\n"); + } + } + else { if(clients[i]->error) print_client(clients[i], "pipe okay again."); clients[i]->error=0; } +} +} +//TODO: at the end, server closes pipefd[1] for client +#endif + +void clients_close_all_finished() +{ + for(int i=0;istatus == CS_THREAD_FINISHED) clients.erase(i); + } +} + +void client_parser_push(char c) +{ //!TODO + command_t cmd; + char* commands_cstr = commands.c_str(); + int newline_index = -1; + + for(int i=0;commands_cstr[i];i++) if(commands_cstr[i]=='\n') newline_index = i; + if(newline_index == -1) + + char param_name[101]; + char param_value[101]; + for(int i=0;i<100;commands_csdr + +} + +void* client_thread (void* param) //!TODO +{ + client_t* me_the_client = (client_t*)param; + me_the_client->status = CS_THREAD_RUNNING; + char ctl_data_buffer; + int retval; + tsmpool* p1_temp; + tsmpool* p2_temp; + const int num_client_buffers = 20; + if(ddc_method == M_TD) + { + p1_temp = new tsmpool(bufsize, ) + } + + for(;;) + { + do + { + retval = recv(me_the_client->socket, &ctl_data_buffer, 1, 0); + if(client_parser_push(ctl_data_buffer)) break; + } while (retval); + + + //read control data from socket + //process control data + //run shift + //run decimation + //have an exit condition (??) + if(ddc_method == M_TD) + { + + } + } + me_the_client->status = CS_THREAD_FINISHED; + pthread_exit(NULL); + return NULL; +} + +void error_exit(const char* why) +{ + perror(why); //do we need a \n at the end of (why)? + exit(1); +} + +void print_exit(const char* why) +{ + fprintf(stderr, "%s", why); + exit(1); +} + +void maxfd(int* maxfd, int fd) +{ + if(fd>=*maxfd) *maxfd=fd+1; +} diff --git a/ddcd.h b/ddcd.h new file mode 100644 index 0000000..f1cf384 --- /dev/null +++ b/ddcd.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SOFTWARE_NAME "ddcd" +#define MSG_START SOFTWARE_NAME ": " + +typedef enum ddc_method_e +{ + M_TD, + M_FASTDDC +} ddc_method_t; + +typedef enum client_status_e +{ + CS_CREATED, + CS_THREAD_RUNNING, + CS_THREAD_FINISHED +} client_status_t; + + +typedef struct client_s +{ + struct sockaddr_in addr; + int socket; + int error; //set to non-zero on error (data transfer failed) + pthread_t thread; + client_status_t status; + +} client_t; + +typedef enum command_type_e +{ + CT_SHIFT, + CT_BYPASS +} command_type_t; + + +typedef struct command_s +{ + command_type_t type; + float float_param; +} command_t; + +void print_exit(const char* why); +void error_exit(const char* why); +void maxfd(int* maxfd, int fd); diff --git a/ddcd_old.cpp b/ddcd_old.cpp new file mode 100644 index 0000000..11a17ac --- /dev/null +++ b/ddcd_old.cpp @@ -0,0 +1,560 @@ +/* +This software is part of libcsdr, a set of simple DSP routines for +Software Defined Radio. + +Copyright (c) 2014, Andras Retzler +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ANDRAS RETZLER BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "ddcd.h" + + +#define SOFTWARE_NAME "ddcd" +#define MSG_START SOFTWARE_NAME ": " + +int host_port = 0; +char host_address[100] = "127.0.0.1"; +int decimation = 0; +float transition_bw = 0.05; +int bufsize = 1024; +int bufsizeall; +int pipe_max_size; +int in_client = 0; +char ddc_method_str[100] = "td"; +ddc_method_t ddc_method; +pid_t main_dsp_proc; + +int input_fd = STDIN_FILENO; //can be stdin, or the stdout of main_subprocess +pid_t main_subprocess_pid = 0; +pid_t main_subprocess_pgrp = 0; +pid_t client_subprocess_pid = 0; +pid_t client_subprocess_pgrp = 0; + +char* buf; + +int set_nonblocking(int fd) +{ + int flagtmp; + if((flagtmp = fcntl(fd, F_GETFL))!=-1) + if((flagtmp = fcntl(fd, F_SETFL, flagtmp|O_NONBLOCK))!=-1) + return 0; + return 1; +} + +int proc_exists(pid_t pid) +{ + if(pid==0 || pid==1) return 1; + return kill(pid, 0) != -1; +} + +void sig_handler(int signo) +{ + int tmpstat; + if(signo==SIGPIPE) + { + fprintf(stderr,MSG_START "SIGPIPE received.\n"); + return; + } + if(signo==SIGCHLD) + if( main_subprocess_pid && signo==SIGCHLD && (waitpid(main_subprocess_pid, &tmpstat, WNOHANG), 1) && !proc_exists(main_subprocess_pid) ) + { + fprintf(stderr,MSG_START "main_subprocess_pid exited! Exiting...\n"); + } + else return; + //if(pgrp!=1 && pgrp!=0) //I just want to make sure that we cannot kill init or sched + // killpg(pgrp, signo); + if( !in_client && main_subprocess_pid ) killpg2(main_subprocess_pgrp); + if( in_client && client_subprocess_pid ) killpg2(client_subprocess_pgrp); + fprintf(stderr, MSG_START "signal %d caught in %s, exiting ddcd...\n", signo, (in_client)?"client":"main"); + fflush(stderr); + exit(0); +} + +client_t* this_client; + +int main(int argc, char* argv[]) +{ + int c; + fd_set select_fds; + + for(;;) + { + int option_index = 0; + static struct option long_options[] = { + {"port", required_argument, 0, 'p' }, + {"address", required_argument, 0, 'a' }, + {"decimation", required_argument, 0, 'd' }, + {"bufsize", required_argument, 0, 'b' }, + {"method", required_argument, 0, 'm' }, + {"transition", required_argument, 0, 't' } + }; + c = getopt_long(argc, argv, "p:a:d:b:m:t:", long_options, &option_index); + if(c==-1) break; + switch (c) + { + case 'a': + host_address[100-1]=0; + strncpy(host_address,optarg,100-1); + break; + case 'p': + host_port=atoi(optarg); + break; + case 'd': + decimation=atoi(optarg); + break; + case 'b': + bufsize=atoi(optarg); + break; + case 'm': + ddc_method_str[100-1]=0; + strncpy(ddc_method_str,optarg,100-1); + break; + case 't': + sscanf(optarg,"%g",&transition_bw); + break; + case 0: + case '?': + case ':': + default:; + print_exit(MSG_START "error in getopt_long()\n"); + } + } + + if(!decimation) print_exit(MSG_START "missing required command line argument, --decimation.\n"); + if(!host_port) print_exit(MSG_START "missing required command line argument, --port.\n"); + if(decimation<0) print_exit(MSG_START "invalid value for --decimation (should be >0).\n"); + if(decimation==1) fprintf(stderr, MSG_START "decimation = 1, just copying raw samples.\n"); + if(transition_bw<0||transition_bw>0.5) print_exit(MSG_START "invalid value for --transition (should be between 0 and 0.5).\n"); + + if(decimation==1); //don't do anything then + else if(!strcmp(ddc_method_str,"td")) + { + ddc_method = M_TD; + fprintf(stderr, MSG_START "method is M_TD (default).\n"); + } + else if (!strcmp(ddc_method_str,"fastddc")) + { + ddc_method = M_FASTDDC; + fprintf(stderr, MSG_START "method is M_FASTDDC.\n"); + } + else print_exit(MSG_START "invalid parameter given to --method.\n"); + + //set signals + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sig_handler; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGKILL, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGPIPE, &sa, NULL); + prctl(PR_SET_PDEATHSIG, SIGHUP); //get a signal when parent exits + + struct sockaddr_in addr_host; + int listen_socket; + std::vector clients; + clients.reserve(100); + listen_socket=socket(AF_INET,SOCK_STREAM,0); + + int sockopt = 1; + if( setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&sockopt, sizeof(sockopt)) == -1 ) + error_exit(MSG_START "cannot set SO_REUSEADDR"); //the best description on SO_REUSEADDR ever: http://stackoverflow.com/a/14388707/3182453 + + memset(&addr_host,'0',sizeof(addr_host)); + addr_host.sin_family=AF_INET; + addr_host.sin_port=htons(host_port); + addr_host.sin_addr.s_addr = INADDR_ANY; + + if( (addr_host.sin_addr.s_addr=inet_addr(host_address)) == INADDR_NONE ) + error_exit(MSG_START "invalid host address"); + + if( bind(listen_socket, (struct sockaddr*) &addr_host, sizeof(addr_host)) < 0 ) + error_exit(MSG_START "cannot bind() address to the socket"); + + if( listen(listen_socket, 10) == -1 ) + error_exit(MSG_START "cannot listen() on socket"); + + fprintf(stderr,MSG_START "listening on %s:%d\n", inet_ntoa(addr_host.sin_addr), host_port); + + struct sockaddr_in addr_cli; + socklen_t addr_cli_len = sizeof(addr_cli); + int new_socket; + + bufsizeall = bufsize*sizeof(char); + buf = (char*)malloc(bufsizeall); + + FILE* tempfile = fopen("/proc/sys/fs/pipe-max-size","r"); + if(!tempfile) + { + perror(MSG_START "cannot read /proc/sys/fs/pipe-max-size"); + } + else + { + char pipe_max_size_str[100]; + int tfread = fread(pipe_max_size_str, 1, 100, tempfile); + pipe_max_size_str[tfread]='\0'; + pipe_max_size = atoi(pipe_max_size_str); + //fprintf(stderr, MSG_START "note: pipe_max_size = %d\n", pipe_max_size); + //if(pipe_max_size>4096 && fcntl(STDIN_FILENO, F_SETPIPE_SZ, pipe_max_size)==-1) + // perror("failed to fcntl(STDIN_FILENO, F_SETPIPE_SZ, ...)"); + } + + //We'll see if it is a good idea: + //setpgrp(); + //pgrp = getpgrp(); + //It is not, because we can't catch Ctrl+C (SIGINT), as it is sent to a process group... + + //Start DSP subprocess from the main process if required + char main_subprocess_cmd_buf[500]; + + + int pipe_m2s_ctl[2]; //main to subprocess :: control channel + int pipe_s2m[2]; //subprocess to main + + if(pipe(pipe_m2s_ctl)) error_exit(MSG_START "couldn't create pipe_m2s_ctl"); + if(pipe(pipe_s2m)) error_exit(MSG_START "couldn't create pipe_s2m"); + + if(decimation!=1) + { + switch(ddc_method) + { + case M_TD: + break; + case M_FASTDDC: + sprintf(main_subprocess_cmd_buf, subprocess_args_fastddc_1, decimation, transition_bw); + fprintf(stderr, MSG_START "starting main_subprocess_cmd: %s\n", main_subprocess_cmd_buf); + if(!(main_subprocess_pid = run_subprocess( main_subprocess_cmd_buf, 0, pipe_s2m, &main_subprocess_pgrp ))) + print_exit(MSG_START "couldn't start main_subprocess_cmd!\n"); + close(STDIN_FILENO); // redirect stdin to the stdin of the subprocess + break; + } + } + + int highfd = 0; + FD_ZERO(&select_fds); + FD_SET(listen_socket, &select_fds); + maxfd(&highfd, listen_socket); + if(main_subprocess_pid) input_fd = pipe_s2m[0]; //else STDIN_FILENO + FD_SET(input_fd, &select_fds); + maxfd(&highfd, input_fd); + + //Set stdin and listen_socket to non-blocking + if(set_nonblocking(input_fd) || set_nonblocking(listen_socket)) //don't do it before subprocess fork! + error_exit(MSG_START "cannot set_nonblocking()"); + + for(;;) + { + //Let's wait until there is any new data to read, or any new connection! + select(highfd, &select_fds, NULL, NULL, NULL); + + //Is there a new client connection? + if( (new_socket = accept(listen_socket, (struct sockaddr*)&addr_cli, &addr_cli_len)) != -1) + { + this_client = new client_t; + this_client->error = 0; + memcpy(&this_client->addr, &addr_cli, sizeof(this_client->addr)); + this_client->socket = new_socket; + if(pipe(this_client->pipefd) == -1) + { + perror(MSG_START "cannot open new pipe() for the client"); + continue; + } + if(fcntl(this_client->pipefd[1], F_SETPIPE_SZ, pipe_max_size) == -1) + perror("failed to F_SETPIPE_SZ for the client pipe"); + if(this_client->pid = fork()) + { + //We're the parent + set_nonblocking(this_client->pipefd[1]); + clients.push_back(this_client); + fprintf(stderr, MSG_START "client pid: %d\n", this_client->pid); + } + else + { + //We're the client + client(); + return 1; + } + } + + int retval = read(input_fd, buf, bufsizeall); + if(retval==0) + { + //end of input stream, close clients and exit + } + else if(retval != -1) + { + for (int i=0; ipipefd[1], buf, retval)==-1) + { + + if(!clients[i]->error) + { + print_client(clients[i], "lost buffer, failed to write pipe."); + clients[i]->error=1; + } + //fprintf(stderr, MSG_START "errno is %d\n", errno); //usually 11 + //int wpstatus; + //int wpresult = waitpid(clients[i]->pid, &wpstatus, WNOHANG); + //fprintf(stderr, MSG_START "pid is %d\n",clients[i]->pid); + //perror("somethings wrong"); + //if(wpresult == -1) print_client(clients[i], "error while waitpid()!"); + //else if(wpresult == 0) + waitpid(clients[i]->pid, NULL, WNOHANG); + if(!proc_exists(clients[i]->pid)) + { + //Client exited! + print_client(clients[i], "closing client from main process."); + close(clients[i]->pipefd[1]); + close(clients[i]->socket); + delete clients[i]; + clients.erase(clients.begin()+i); + fprintf(stderr, MSG_START "done closing client from main process.\n"); + } + } + else { if(clients[i]->error) print_client(clients[i], "pipe okay again."); clients[i]->error=0; } + } + } + //TODO: at the end, server closes pipefd[1] for client + } + + return 0; +} + +pid_t run_subprocess(char* cmd, int* pipe_in, int* pipe_out, pid_t* pgrp) +{ + /*char sem_name[101]; + snprintf(sem_name,100,"ddcd_sem_%d",getpid()); + sem_t mysem; + if(sem_init(&mysem, 1, 1)==-1) error_exit("failed to sem_init() in run_subprocess()"); + fprintf(stderr, "sem_waiting\n"); + if(sem_wait(&mysem)==-1) error_exit("the first sem_wait() failed in run_subprocess()"); + fprintf(stderr, "sem_waited\n"); + */ + int syncpipe[2]; + if(pipe(syncpipe)==-1) error_exit("failed to create pipe()"); + pid_t pid = fork(); + + if(pid < 0) return 0; //fork failed + if(pid == 0) + { + setpgrp(); + write(syncpipe[1], " ", 1); + //if(sem_post(&mysem)==-1) error_exit("failed to sem_post() in run_subprocess()"); + //We're the subprocess + //fprintf(stderr, "run_subprocess :: execl\n"); + //if(fcntl(pipe_in[1], F_SETPIPE_SZ, pipe_max_size) == -1) perror("Failed to F_SETPIPE_SZ in run_subprocess()"); + if(pipe_in) + { + close(pipe_in[1]); + dup2(pipe_in[0], STDIN_FILENO); + } + if(pipe_out) + { + close(pipe_out[0]); + dup2(pipe_out[1], STDOUT_FILENO); + } + execl("/bin/bash","bash","-c",cmd, (char*)0); + error_exit(MSG_START "run_subprocess failed to execute command"); + } + else + { + //if(sem_wait(&mysem)==-1) error_exit("the second sem_wait() failed in run_subprocess()"); + int synctemp; + read(syncpipe[0], &synctemp, 1); + *pgrp = getpgid(pid); + fprintf(stderr, MSG_START "run_subprocess pgid returned = %d\n", *pgrp); + return pid; + } +} + +void print_client(client_t* client, const char* what) +{ + fprintf(stderr,MSG_START "(client %s:%d) %s\n", inet_ntoa(client->addr.sin_addr), client->addr.sin_port, what); +} + +#define CTL_BUFSIZE 1024 + +int read_socket_ctl(int fd, char* output, int max_size) +{ + //fprintf(stderr, "doing read_socket_ctl %d\n", fd); + //if(!fd) return 0; + static char buffer[CTL_BUFSIZE]; + static int buffer_index=0; + if(buffer_index==CTL_BUFSIZE) buffer_index=0; + int bytes_read=recv(fd,buffer+buffer_index,(CTL_BUFSIZE-buffer_index)*sizeof(char), MSG_DONTWAIT); + if(bytes_read<=0) return 0; + //fprintf(stderr, "recv %d\n", bytes_read); + + int prev_newline_at=0; + int last_newline_at=0; + for(int i=0;ipipefd[0]; + int pipe_ctl[2], pipe_stdout[2]; + + prctl(PR_SET_PDEATHSIG, SIGHUP); //get a signal when parent exits + + if(decimation!=1) + { + + if(pipe(pipe_ctl)==-1) error_exit(MSG_START "cannot open new pipe() for the client subprocess"); + if(pipe(pipe_stdout)==-1) error_exit(MSG_START "cannot open new pipe() for the client subprocess"); + switch(ddc_method) + { + case M_TD: + sprintf(client_subprocess_cmd_buf, subprocess_cmd_td, pipe_ctl[0], decimation, transition_bw); + break; + case M_FASTDDC: + sprintf(client_subprocess_cmd_buf, subprocess_args_fastddc_2, pipe_ctl[0], decimation, transition_bw); + break; + } + + if(!(client_subprocess_pid = run_subprocess( client_subprocess_cmd_buf, this_client->pipefd, pipe_stdout, &client_subprocess_pgrp))) + print_exit(MSG_START "couldn't start client_subprocess_cmd!\n"); + fprintf(stderr, MSG_START "starting client_subprocess_cmd: %s\n", client_subprocess_cmd_buf); + input_fd = pipe_stdout[0]; //we don't have to set it nonblocking + fprintf(stderr, MSG_START "pipe_stdout[0] = %d\n", pipe_stdout[0]); + write(pipe_ctl[1], "0.0\n", 4); + } + char recv_cmd[CTL_BUFSIZE]; + char temps[CTL_BUFSIZE*2]; + int tempi; + float tempf; + + for(;;) + { + while(read_socket_ctl(this_client->socket, recv_cmd, CTL_BUFSIZE)) + { + sprintf(temps, "read_socket_ctl: %s", recv_cmd); + print_client(this_client, temps); + if(ctl_get_arg(recv_cmd, "bypass", "%d", &tempi)) + { + if(tempi==1 && client_subprocess_pid) + { + //print_client(this_client, "suspending client_subprocess_pgrp...\n"); + //fprintf(stderr, "client_subprocess_pgrp = %d\n", client_subprocess_pgrp); + //killpg(client_subprocess_pgrp, SIGTSTP); + //while(proc_exists(client_subprocess_pid)) usleep(10000); + //print_client(this_client, "done killing client_subprocess_pid.\n"); + input_fd=this_client->pipefd[0]; //by doing this, we don't read from pipe_stdout[0] anymore, so that csdr stops doing anything, and also doesn't read anymore from the input: we get the whole I/Q stream! + } + if(tempi==0 && client_subprocess_pid) + { + input_fd=pipe_stdout[0]; + } + + } + if(ctl_get_arg(recv_cmd, "shift", "%g", &tempf)) + { + tempi=sprintf(temps, "%g\n", tempf); + write(pipe_ctl[1], temps, tempi); + fsync(pipe_ctl[1]); + } + } + int nread = read(input_fd,buf,bufsizeall); + if(nread<=0) continue; + if(send(this_client->socket,buf,nread,0)==-1) + { + print_client(this_client, "client process is exiting.\n"); + if(client_subprocess_pid) killpg2(client_subprocess_pgrp); + exit(0); + } + } +} + +void killpg2(pid_t pgrp) +{ + //fprintf(stderr, MSG_START "killpg2: %d\n", pgrp); + if(pgrp!=1 && pgrp!=0) killpg(pgrp, SIGTERM); +} + +void error_exit(const char* why) +{ + perror(why); + exit(1); +} + +void print_exit(const char* why) +{ + fprintf(stderr, "%s", why); + exit(1); +} + +void maxfd(int* maxfd, int fd) +{ + if(fd>=*maxfd) *maxfd=fd+1; +} diff --git a/ddcd_old.h b/ddcd_old.h new file mode 100644 index 0000000..af4cfb5 --- /dev/null +++ b/ddcd_old.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct client_s +{ + struct sockaddr_in addr; + int socket; + pid_t pid; + int pipefd[2]; + int error; + pid_t dsp_proc; +} client_t; + + +void client(); +void error_exit(const char* why); +void print_exit(const char* why); +void print_client(client_t* client, const char* what); +int proc_exists(pid_t pid); +pid_t run_subprocess(char* cmd, int* pipe_in, int* pipe_out, pid_t* pgrp); +void maxfd(int* maxfd, int fd); +void sig_handler(int signo); +void killpg2(pid_t pgrp); +int ctl_get_arg(char* input, const char* cmd, const char* format, ...); + +typedef enum ddc_method_e +{ + M_TD, + M_FASTDDC +} ddc_method_t; + +const char subprocess_cmd_td[] = "csdr " +#ifdef NEON_OPTS + "shift_addfast_cc" +#else + "shift_unroll_cc" +#endif + " --fd %d | csdr fir_decimate_cc %d %g"; + +const char subprocess_args_fastddc_1[] = "csdr fastddc_fwd_cc %d %g"; +//const char subprocess_args_fastddc_1[] = "csdr through %d %g"; +const char subprocess_args_fastddc_2[] = "csdr fastddc_inv_cc --fd %d %d %g"; +//const char subprocess_args_fastddc_2[] = "csdr convert_u8_f %d %d %g"; diff --git a/fastddc.c b/fastddc.c new file mode 100644 index 0000000..7b26c0d --- /dev/null +++ b/fastddc.c @@ -0,0 +1,166 @@ +/* +This software is part of libcsdr, a set of simple DSP routines for +Software Defined Radio. + +Copyright (c) 2014, Andras Retzler +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ANDRAS RETZLER BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "fastddc.h" + +//DDC implementation based on: +//http://www.3db-labs.com/01598092_MultibandFilterbank.pdf + +inline int is_integer(float a) { return floorf(a) == a; } + +int fastddc_init(fastddc_t* ddc, float transition_bw, int decimation, float shift_rate) +{ + ddc->pre_decimation = 1; //this will be done in the frequency domain + ddc->post_decimation = decimation; //this will be done in the time domain + while( is_integer((float)ddc->post_decimation/2) && ddc->post_decimation/2 != 1) + { + ddc->post_decimation/=2; + ddc->pre_decimation*=2; + } + ddc->taps_min_length = firdes_filter_len(transition_bw); //his is the minimal number of taps to achieve the given transition_bw; we are likely to have more taps than this number. + ddc->taps_length = next_pow2(ceil(ddc->taps_min_length/(float)ddc->pre_decimation) * ddc->pre_decimation) + 1; //the number of taps must be a multiple of the decimation factor + ddc->fft_size = next_pow2(ddc->taps_length * 4); //it is a good rule of thumb for performance (based on the article), but we should do benchmarks + while (ddc->fft_sizepre_decimation) ddc->fft_size*=2; //fft_size should be a multiple of pre_decimation. + ddc->overlap_length = ddc->taps_length - 1; + ddc->input_size = ddc->fft_size - ddc->overlap_length; + ddc->fft_inv_size = ddc->fft_size / ddc->pre_decimation; + + //Shift operation in the frequency domain: we can shift by a multiple of v. + ddc->v = ddc->fft_size/ddc->overlap_length; //overlap factor | +-1 ? (or maybe ceil() this?) + int middlebin=ddc->fft_size / 2; + ddc->startbin = middlebin + middlebin * (-shift_rate) * 2; + //fprintf(stderr, "ddc->startbin=%g\n",(float)ddc->startbin); + ddc->startbin = ddc->v * round( ddc->startbin / (float)ddc->v ); + //fprintf(stderr, "ddc->startbin=%g\n",(float)ddc->startbin); + ddc->offsetbin = ddc->startbin - middlebin; + ddc->post_shift = (ddc->pre_decimation)*(shift_rate+((float)ddc->offsetbin/ddc->fft_size)); + ddc->pre_shift = ddc->offsetbin/(float)ddc->fft_size; + ddc->dsadata = decimating_shift_addition_init(ddc->post_shift, ddc->post_decimation); + + //Overlap is scrapped, not added + ddc->scrap=ddc->overlap_length/ddc->pre_decimation; //TODO this is problematic sometimes! overlap_length = 401 :: scrap = 200 + ddc->post_input_size=ddc->fft_inv_size-ddc->scrap; + + return ddc->fft_size<=2; //returns true on error +} + + +void fastddc_print(fastddc_t* ddc, char* source) +{ + fprintf(stderr, + "%s: fastddc_print_sizes(): (fft_size = %d) = (taps_length = %d) + (input_size = %d) - 1\n" + " overlap :: (overlap_length = %d) = taps_length - 1, taps_min_length = %d\n" + " decimation :: decimation = (pre_decimation = %d) * (post_decimation = %d), fft_inv_size = %d\n" + " shift :: startbin = %d, offsetbin = %d, v = %d, pre_shift = %g, post_shift = %g\n" + " o&s :: post_input_size = %d, scrap = %d\n" + , + source, ddc->fft_size, ddc->taps_length, ddc->input_size, + ddc->overlap_length, ddc->taps_min_length, + ddc->pre_decimation, ddc->post_decimation, ddc->fft_inv_size, + ddc->startbin, ddc->offsetbin, ddc->v, ddc->pre_shift, ddc->post_shift, + ddc->post_input_size, ddc->scrap ); +} + +void fft_swap_sides(complexf* io, int fft_size) +{ + int middle=fft_size/2; + complexf temp; + for(int i=0;ifft_size number of elements + + complexf* inv_input = plan_inverse->input; + complexf* inv_output = plan_inverse->output; + + //Initialize buffers for inverse FFT to zero + for(int i=0;isize;i++) + { + iof(inv_input,i)=0; + qof(inv_input,i)=0; + } + + //Alias & shift & filter at once + fft_swap_sides(input, ddc->fft_size); //TODO this is not very optimal, but now we stick with this slow solution until we got the algorithm working + //fprintf(stderr, " === fastddc_inv_cc() ===\n"); + //The problem is, we have to say that the output_index should be the _center_ of the spectrum when i is at startbin! (startbin is at the _center_ of the input to downconvert, not at its first bin!) + for(int i=0;ifft_size;i++) + { + int output_index = (ddc->fft_size+i-ddc->offsetbin+(ddc->fft_inv_size/2))%plan_inverse->size; + int tap_index = i; + //fprintf(stderr, "output_index = %d , tap_index = %d, input index = %d\n", output_index, tap_index, i); + //cmultadd(inv_input+output_index, input+i, taps_fft+tap_index); //cmultadd(output, input1, input2): complex output += complex input1 * complex input 2 + // (a+b*i)*(c+d*i) = (ac-bd)+(ad+bc)*i + // a = iof(input,i) + // b = qof(input,i) + // c = iof(taps_fft,i) + // d = qof(taps_fft,i) + iof(inv_input,output_index) += iof(input,i) * iof(taps_fft,i) - qof(input,i) * qof(taps_fft,i); + qof(inv_input,output_index) += iof(input,i) * qof(taps_fft,i) + qof(input,i) * iof(taps_fft,i); + //iof(inv_input,output_index) += iof(input,i); //no filter + //qof(inv_input,output_index) += qof(input,i); + } + + //Normalize inv fft bins (now our output level is not higher than the input... but we may optimize this into the later loop when we normalize by size) + for(int i=0;isize;i++) + { + iof(inv_input,i)/=ddc->pre_decimation; + qof(inv_input,i)/=ddc->pre_decimation; + } + + fft_swap_sides(inv_input,plan_inverse->size); + fft_execute(plan_inverse); + + //Normalize data + for(int i=0;isize;i++) //@fastddc_inv_cc: normalize by size + { + iof(inv_output,i)/=plan_inverse->size; + qof(inv_output,i)/=plan_inverse->size; + } + + //Overlap is scrapped, not added + //Shift correction + shift_stat=decimating_shift_addition_cc(inv_output+ddc->scrap, output, ddc->post_input_size, ddc->dsadata, ddc->post_decimation, shift_stat); + //shift_stat.output_size = ddc->post_input_size; //bypass shift correction + //memcpy(output, inv_output+ddc->scrap, sizeof(complexf)*ddc->post_input_size); + return shift_stat; +} diff --git a/fastddc.h b/fastddc.h new file mode 100644 index 0000000..d65ca3c --- /dev/null +++ b/fastddc.h @@ -0,0 +1,29 @@ +#include +#include "libcsdr.h" +#include "libcsdr_gpl.h" + +typedef struct fastddc_s +{ + int pre_decimation; + int post_decimation; + int taps_length; + int taps_min_length; + int overlap_length; //it is taps_length - 1 + int fft_size; + int fft_inv_size; + int input_size; + int post_input_size; + float pre_shift; + int startbin; //for pre_shift + int v; //step for pre_shift + int offsetbin; + float post_shift; + int output_scrape; + int scrap; + shift_addition_data_t dsadata; +} fastddc_t; + +int fastddc_init(fastddc_t* ddc, float transition_bw, int decimation, float shift_rate); +decimating_shift_addition_status_t fastddc_inv_cc(complexf* input, complexf* output, fastddc_t* ddc, FFT_PLAN_T* plan_inverse, complexf* taps_fft, decimating_shift_addition_status_t shift_stat); +void fastddc_print(fastddc_t* ddc, char* source); +void fft_swap_sides(complexf* io, int fft_size); diff --git a/grc_tests/test_bandpass_fir_fft.grc b/grc_tests/test_bandpass_fir_fft.grc index 0d2bc4d..a1cc5f4 100644 --- a/grc_tests/test_bandpass_fir_fft.grc +++ b/grc_tests/test_bandpass_fir_fft.grc @@ -1,49 +1,49 @@ - - + + Sat Nov 22 11:19:26 2014 options - - id - top_block - - - _enabled - True - - - title - - author - - description - - window_size 1280, 1024 - - generate_options - wx_gui - category Custom - run_options - prompt + comment + - run + description + + + + _enabled True + + _coordinate + (24, 11) + + + _rotation + 0 + + + generate_options + wx_gui + + + id + top_block + max_nouts 0 @@ -53,36 +53,91 @@ - alias + run_options + prompt + + + run + True + + + thread_safe_setters - _coordinate - (24, 11) - - - _rotation - 0 + title + - variable + variable_slider - id - samp_rate + comment + + + + converver + float_converter + + + value + 1e3 _enabled True - value - 24e3 + _coordinate + (8, 83) - alias + _rotation + 0 + + + grid_pos + + id + freq + + + label + + + + max + samp_rate/2 + + + min + -samp_rate/2 + + + notebook + + + + num_steps + 100 + + + style + wx.SL_HORIZONTAL + + + + variable + + comment + + + + _enabled + True + _coordinate (184, 11) @@ -91,16 +146,119 @@ _rotation 0 + + id + samp_rate + + + value + 24e3 + + + + analog_noise_source_x + + amp + 1 + + + alias + + + + comment + + + + affinity + + + + _enabled + 0 + + + _coordinate + (112, 219) + + + _rotation + 0 + + + id + analog_noise_source_x_0 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + noise_type + analog.GR_GAUSSIAN + + + type + complex + + + seed + 0 + analog_sig_source_x + + amp + 1 + + + alias + + + + comment + + + + affinity + + + + _enabled + 1 + + + freq + freq + + + _coordinate + (128, 83) + + + _rotation + 0 + id analog_sig_source_x_0 - _enabled - True + maxoutbuf + 0 + + + minoutbuf + 0 + + + offset + 0 type @@ -114,84 +272,24 @@ waveform analog.GR_COS_WAVE - - freq - freq - - - amp - 1 - - - offset - 0 - - - alias - - - - affinity - - - - minoutbuf - 0 - - - maxoutbuf - 0 - - - _coordinate - (128, 83) - - - _rotation - 0 - blocks_throttle - - id - blocks_throttle_0 - - - _enabled - True - - - type - complex - - - samples_per_second - samp_rate - - - vlen - 1 - - - ignoretag - True - alias + + comment + + affinity - minoutbuf - 0 - - - maxoutbuf - 0 + _enabled + True _coordinate @@ -201,541 +299,56 @@ _rotation 0 - - - variable_slider id - freq + blocks_throttle_0 - _enabled + ignoretag True - label - - - - value - 1e3 - - - min - -samp_rate/2 - - - max - samp_rate/2 - - - num_steps - 100 - - - style - wx.SL_HORIZONTAL - - - converver - float_converter - - - grid_pos - - - - notebook - - - - alias - - - - _coordinate - (8, 83) - - - _rotation + maxoutbuf 0 - - - wxgui_fftsink2 - - id - wxgui_fftsink2_0_0 - - - _enabled - True - - - type - complex - - - title - FFT Plot of original signal - - - samp_rate - samp_rate - - - baseband_freq - 0 - - - y_per_div - 10 - - - y_divs - 10 - - - ref_level - 0 - - - ref_scale - 2.0 - - - fft_size - 1024 - - - fft_rate - 15 - - - peak_hold - False - - - average - False - - - avg_alpha - 0 - - - win - None - - - win_size - - - - grid_pos - - - - notebook - nb0,1 - - - freqvar - None - - - alias - - - - affinity - - - - _coordinate - (504, 219) - - - _rotation - 0 - - - - wxgui_scopesink2 - - id - wxgui_scopesink2_0 - - - _enabled - True - - - type - complex - - - title - Scope Plot of original signal - - - samp_rate - samp_rate - - - v_scale - 0 - - - v_offset - 0 - - - t_scale - 0 - - - ac_couple - False - - - xy_mode - False - - - num_inputs - 1 - - - win_size - - - - grid_pos - - - - notebook - nb0,0 - - - trig_mode - wxgui.TRIG_MODE_AUTO - - - y_axis_label - Counts - - - alias - - - - affinity - - - - _coordinate - (504, 435) - - - _rotation - 0 - - - - wxgui_scopesink2 - - id - wxgui_scopesink2_0_0 - - - _enabled - True - - - type - complex - - - title - Scope Plot of new signal - - - samp_rate - samp_rate - - - v_scale - 0 - - - v_offset - 0 - - - t_scale - 0 - - - ac_couple - False - - - xy_mode - False - - - num_inputs - 1 - - - win_size - - - - grid_pos - - - - notebook - nb1,0 - - - trig_mode - wxgui.TRIG_MODE_AUTO - - - y_axis_label - Counts - - - alias - - - - affinity - - - - _coordinate - (784, 283) - - - _rotation - 0 - - - - wxgui_fftsink2 - - id - wxgui_fftsink2_0 - - - _enabled - True - - - type - complex - - - title - FFT Plot of new signal - - - samp_rate - samp_rate - - - baseband_freq - 0 - - - y_per_div - 10 - - - y_divs - 10 - - - ref_level - 0 - - - ref_scale - 2.0 - - - fft_size - 1024 - - - fft_rate - 15 - - - peak_hold - False - - - average - False - - - avg_alpha - 0 - - - win - None - - - win_size - - - - grid_pos - - - - notebook - nb1,1 - - - freqvar - None - - - alias - - - - affinity - - - - _coordinate - (784, 67) - - - _rotation - 0 - - - - notebook - - id - nb0 - - - _enabled - True - - - style - wx.NB_TOP - - - labels - ['scope', 'fft'] - - - grid_pos - 1,1,1,1 - - - notebook - - - - alias - - - - _coordinate - (272, 11) - - - _rotation - 0 - - - - notebook - - id - nb1 - - - _enabled - True - - - style - wx.NB_TOP - - - labels - ['scope', 'fft'] - - - grid_pos - 1,2,1,1 - - - notebook - - - - alias - - - - _coordinate - (416, 11) - - - _rotation - 0 - - - - ha5kfu_execproc_xx - - id - ha5kfu_execproc_xx_0 - - - _enabled - True - - - type - cc - - - commandline - csdr bandpass_fir_fft_cc 0.1 0.5 0.05 HAMMING - - - alias - - - - affinity - - minoutbuf 0 - maxoutbuf - 0 + samples_per_second + samp_rate + + + type + complex + + + vlen + 1 + + + + ha5kfu_execproc_xx + + alias + + + + commandline + csdr bandpass_fir_fft_cc 0.1 0.3 0.05 HAMMING + + + comment + + + + affinity + + + + _enabled + True _coordinate @@ -745,7 +358,495 @@ _rotation 0 + + id + ha5kfu_execproc_xx_0 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + type + cc + + + notebook + + alias + + + + comment + + + + _enabled + True + + + _coordinate + (272, 11) + + + _rotation + 0 + + + grid_pos + 1,1,1,1 + + + id + nb0 + + + labels + ['scope', 'fft'] + + + notebook + + + + style + wx.NB_TOP + + + + notebook + + alias + + + + comment + + + + _enabled + True + + + _coordinate + (416, 11) + + + _rotation + 0 + + + grid_pos + 1,2,1,1 + + + id + nb1 + + + labels + ['scope', 'fft'] + + + notebook + + + + style + wx.NB_TOP + + + + wxgui_fftsink2 + + avg_alpha + 0 + + + average + False + + + baseband_freq + 0 + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + fft_size + 1024 + + + freqvar + None + + + _coordinate + (784, 67) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_fftsink2_0 + + + notebook + nb1,1 + + + peak_hold + False + + + ref_level + 0 + + + ref_scale + 2.0 + + + fft_rate + 15 + + + samp_rate + samp_rate + + + title + FFT Plot of new signal + + + type + complex + + + win_size + + + + win + None + + + y_divs + 10 + + + y_per_div + 10 + + + + wxgui_fftsink2 + + avg_alpha + 0 + + + average + False + + + baseband_freq + 0 + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + fft_size + 1024 + + + freqvar + None + + + _coordinate + (504, 219) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_fftsink2_0_0 + + + notebook + nb0,1 + + + peak_hold + False + + + ref_level + 0 + + + ref_scale + 2.0 + + + fft_rate + 15 + + + samp_rate + samp_rate + + + title + FFT Plot of original signal + + + type + complex + + + win_size + + + + win + None + + + y_divs + 10 + + + y_per_div + 10 + + + + wxgui_scopesink2 + + ac_couple + False + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (504, 435) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_scopesink2_0 + + + notebook + nb0,0 + + + num_inputs + 1 + + + samp_rate + samp_rate + + + t_scale + 0 + + + title + Scope Plot of original signal + + + trig_mode + wxgui.TRIG_MODE_AUTO + + + type + complex + + + v_offset + 0 + + + v_scale + 0 + + + win_size + + + + xy_mode + False + + + y_axis_label + Counts + + + + wxgui_scopesink2 + + ac_couple + False + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (784, 283) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_scopesink2_0_0 + + + notebook + nb1,0 + + + num_inputs + 1 + + + samp_rate + samp_rate + + + t_scale + 0 + + + title + Scope Plot of new signal + + + trig_mode + wxgui.TRIG_MODE_AUTO + + + type + complex + + + v_offset + 0 + + + v_scale + 0 + + + win_size + + + + xy_mode + False + + + y_axis_label + Counts + + + + analog_noise_source_x_0 + blocks_throttle_0 + 0 + 0 + analog_sig_source_x_0 blocks_throttle_0 @@ -758,12 +859,6 @@ 0 0 - - ha5kfu_execproc_xx_0 - wxgui_fftsink2_0 - 0 - 0 - blocks_throttle_0 wxgui_fftsink2_0_0 @@ -776,6 +871,12 @@ 0 0 + + ha5kfu_execproc_xx_0 + wxgui_fftsink2_0 + 0 + 0 + ha5kfu_execproc_xx_0 wxgui_scopesink2_0_0 diff --git a/grc_tests/test_fastddc.grc b/grc_tests/test_fastddc.grc new file mode 100644 index 0000000..14acb88 --- /dev/null +++ b/grc_tests/test_fastddc.grc @@ -0,0 +1,996 @@ + + + + Sat Nov 15 20:06:19 2014 + + options + + author + + + + window_size + 1280, 1024 + + + category + Custom + + + comment + + + + description + + + + _enabled + True + + + _coordinate + (10, 10) + + + _rotation + 0 + + + generate_options + wx_gui + + + id + top_block + + + max_nouts + 0 + + + realtime_scheduling + + + + run_options + prompt + + + run + True + + + thread_safe_setters + + + + title + + + + + variable + + comment + + + + _enabled + True + + + _coordinate + (128, 179) + + + _rotation + 0 + + + id + decimation + + + value + 4 + + + + variable_slider + + comment + + + + converver + float_converter + + + value + 50 + + + _enabled + True + + + _coordinate + (16, 267) + + + _rotation + 0 + + + grid_pos + + + + id + freq + + + label + + + + max + samp_rate/2 + + + min + -samp_rate/2 + + + notebook + + + + num_steps + 100 + + + style + wx.SL_HORIZONTAL + + + + variable + + comment + + + + _enabled + True + + + _coordinate + (9, 170) + + + _rotation + 0 + + + id + samp_rate + + + value + 400000 + + + + analog_noise_source_x + + amp + 1 + + + alias + + + + comment + + + + affinity + + + + _enabled + 0 + + + _coordinate + (224, 403) + + + _rotation + 0 + + + id + analog_noise_source_x_0 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + noise_type + analog.GR_GAUSSIAN + + + type + complex + + + seed + 0 + + + + analog_pll_freqdet_cf + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (112, 675) + + + _rotation + 0 + + + id + analog_pll_freqdet_cf_0 + + + w + (3.141592654/200)/2 + + + max_freq + 3.141592654 + + + maxoutbuf + 0 + + + min_freq + -3.141592654 + + + minoutbuf + 0 + + + + analog_sig_source_x + + amp + 0.2 + + + alias + + + + comment + + + + affinity + + + + _enabled + 1 + + + freq + freq + + + _coordinate + (224, 29) + + + _rotation + 0 + + + id + analog_sig_source_x_0 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + offset + 0 + + + type + complex + + + samp_rate + samp_rate + + + waveform + analog.GR_COS_WAVE + + + + blocks_multiply_const_vxx + + alias + + + + comment + + + + const + (samp_rate/decimation)*(1/(2*3.141592654)) + + + affinity + + + + _enabled + True + + + _coordinate + (368, 691) + + + _rotation + 0 + + + id + blocks_multiply_const_vxx_0 + + + type + float + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + vlen + 1 + + + + blocks_throttle + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (424, 235) + + + _rotation + 0 + + + id + blocks_throttle_0 + + + ignoretag + True + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + samples_per_second + samp_rate + + + type + complex + + + vlen + 1 + + + + ha5kfu_execproc_xx + + alias + + + + commandline + csdr fastddc_fwd_cc %d | csdr fastddc_inv_cc 0.4 %d"%(decimation,decimation)+" + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (616, 235) + + + _rotation + 0 + + + id + ha5kfu_execproc_xx_1 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + type + cc + + + + notebook + + alias + + + + comment + + + + _enabled + True + + + _coordinate + (160, 283) + + + _rotation + 0 + + + grid_pos + + + + id + nb + + + labels + ['FFT', 'Scope'] + + + notebook + + + + style + wx.NB_TOP + + + + wxgui_fftsink2 + + avg_alpha + 0 + + + average + False + + + baseband_freq + 0 + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + fft_size + 1024 + + + freqvar + None + + + _coordinate + (952, 155) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_fftsink2_0 + + + notebook + nb, 0 + + + peak_hold + False + + + ref_level + 0 + + + ref_scale + 2.0 + + + fft_rate + 15 + + + samp_rate + samp_rate/decimation + + + title + FFT plot of csdr processed signal + + + type + complex + + + win_size + + + + win + None + + + y_divs + 10 + + + y_per_div + 10 + + + + wxgui_fftsink2 + + avg_alpha + 0 + + + average + False + + + baseband_freq + 0 + + + alias + + + + comment + + + + affinity + + + + _enabled + 1 + + + fft_size + 1024 + + + freqvar + None + + + _coordinate + (616, 291) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_fftsink2_0_0 + + + notebook + + + + peak_hold + False + + + ref_level + 0 + + + ref_scale + 2.0 + + + fft_rate + 15 + + + samp_rate + samp_rate + + + title + FFT plot of original signal + + + type + complex + + + win_size + + + + win + None + + + y_divs + 10 + + + y_per_div + 10 + + + + wxgui_numbersink2 + + avg_alpha + 0 + + + average + False + + + alias + + + + comment + + + + affinity + + + + decimal_places + 10 + + + _enabled + True + + + factor + 1.0 + + + _coordinate + (576, 619) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_numbersink2_0 + + + max_value + (samp_rate/decimation)/2 + + + min_value + (-samp_rate/decimation)/2 + + + notebook + + + + number_rate + 15 + + + peak_hold + False + + + ref_level + 0 + + + samp_rate + samp_rate + + + show_gauge + True + + + title + PLL locked at + + + type + float + + + units + Hz + + + win_size + + + + + wxgui_scopesink2 + + ac_couple + False + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (952, 35) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_scopesink2_0 + + + notebook + nb, 1 + + + num_inputs + 1 + + + samp_rate + samp_rate/decimation + + + t_scale + 0 + + + title + Scope plot of csdr processed signal + + + trig_mode + wxgui.TRIG_MODE_AUTO + + + type + complex + + + v_offset + 0 + + + v_scale + 0 + + + win_size + + + + xy_mode + False + + + y_axis_label + Counts + + + + analog_noise_source_x_0 + blocks_throttle_0 + 0 + 0 + + + analog_pll_freqdet_cf_0 + blocks_multiply_const_vxx_0 + 0 + 0 + + + analog_sig_source_x_0 + blocks_throttle_0 + 0 + 0 + + + blocks_multiply_const_vxx_0 + wxgui_numbersink2_0 + 0 + 0 + + + blocks_throttle_0 + ha5kfu_execproc_xx_1 + 0 + 0 + + + blocks_throttle_0 + wxgui_fftsink2_0_0 + 0 + 0 + + + ha5kfu_execproc_xx_1 + analog_pll_freqdet_cf_0 + 0 + 0 + + + ha5kfu_execproc_xx_1 + wxgui_fftsink2_0 + 0 + 0 + + + ha5kfu_execproc_xx_1 + wxgui_scopesink2_0 + 0 + 0 + + diff --git a/grc_tests/test_shift.grc b/grc_tests/test_shift.grc index 9fc1eb7..0b48387 100644 --- a/grc_tests/test_shift.grc +++ b/grc_tests/test_shift.grc @@ -1,49 +1,49 @@ - - + + Thu Jan 15 18:51:48 2015 options - - id - top_block - - - _enabled - True - - - title - - author - - description - - window_size 1280, 1024 - - generate_options - wx_gui - category Custom - run_options - prompt + comment + - run + description + + + + _enabled True + + _coordinate + (10, 10) + + + _rotation + 0 + + + generate_options + wx_gui + + + id + top_block + max_nouts 0 @@ -53,63 +53,32 @@ - alias + run_options + prompt + + + run + True + + + thread_safe_setters - _coordinate - (10, 10) - - - _rotation - 0 + title + variable - id - rate + comment + _enabled True - - value - -0.055 - - - alias - - - - _coordinate - (8, 195) - - - _rotation - 0 - - - - variable - - id - decimation - - - _enabled - True - - - value - 3 - - - alias - - _coordinate (16, 267) @@ -118,25 +87,111 @@ _rotation 0 - - - variable id - samp_rate + decimation + + + value + 3 + + + + variable_slider + + comment + + + + converver + float_converter + + + value + 0 _enabled True - value - 250e3 + _coordinate + (184, 219) - alias + _rotation + 0 + + + grid_pos + + id + gen_freq + + + label + + + + max + samp_rate/2 + + + min + -samp_rate/2 + + + notebook + + + + num_steps + 100 + + + style + wx.SL_HORIZONTAL + + + + variable + + comment + + + + _enabled + True + + + _coordinate + (8, 195) + + + _rotation + 0 + + + id + rate + + + value + -0.055 + + + + variable + + comment + + + + _enabled + True + _coordinate (176, 11) @@ -145,16 +200,64 @@ _rotation 0 + + id + samp_rate + + + value + 250e3 + analog_sig_source_x + + amp + 1 + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + freq + gen_freq + + + _coordinate + (8, 67) + + + _rotation + 0 + id analog_sig_source_x_0 - _enabled - True + maxoutbuf + 0 + + + minoutbuf + 0 + + + offset + 0 type @@ -166,605 +269,217 @@ waveform - analog.GR_CONST_WAVE - - - freq - 20000 - - - amp - 1 - - - offset - 0 - - - alias - - - - affinity - - - - minoutbuf - 0 - - - maxoutbuf - 0 - - - _coordinate - (8, 75) - - - _rotation - 0 - - - - ha5kfu_execproc_xx - - id - ha5kfu_execproc_xx_0 - - - _enabled - True - - - type - cc - - - commandline - "csdr shift_math_cc %g"%rate - - - alias - - - - affinity - - - - minoutbuf - 0 - - - maxoutbuf - 0 - - - _coordinate - (488, 251) - - - _rotation - 0 - - - - ha5kfu_execproc_xx - - id - ha5kfu_execproc_xx_0_0 - - - _enabled - True - - - type - cc - - - commandline - "csdr shift_addition_cc %g"%rate - - - alias - - - - affinity - - - - minoutbuf - 0 - - - maxoutbuf - 0 - - - _coordinate - (472, 411) - - - _rotation - 0 - - - - notebook - - id - nb0 - - - _enabled - True - - - style - wx.NB_TOP - - - labels - ['shift_math_cc', 'shift_addition_cc', 'shift_table_cc', 'decimating_shift_addition_cc','original'] - - - grid_pos - - - - notebook - - - - alias - - - - _coordinate - (272, 11) - - - _rotation - 0 - - - - wxgui_fftsink2 - - id - wxgui_fftsink2_0_0 - - - _enabled - True - - - type - complex - - - title - FFT Plot - - - samp_rate - samp_rate - - - baseband_freq - 0 - - - y_per_div - 10 - - - y_divs - 10 - - - ref_level - 0 - - - ref_scale - 2.0 - - - fft_size - 1024 - - - fft_rate - 15 - - - peak_hold - False - - - average - False - - - avg_alpha - 0 - - - win - None - - - win_size - - - - grid_pos - - - - notebook - nb0,4 - - - freqvar - None - - - alias - - - - affinity - - - - _coordinate - (496, 27) - - - _rotation - 0 + analog.GR_SIN_WAVE blocks_throttle - id - blocks_throttle_0 + alias + + + + comment + + + + affinity + _enabled True - type - complex + _coordinate + (224, 99) - samples_per_second - samp_rate + _rotation + 0 - vlen - 1 + id + blocks_throttle_0 ignoretag True - alias - - - - affinity - + maxoutbuf + 0 minoutbuf 0 - maxoutbuf - 0 - - - _coordinate - (224, 107) - - - _rotation - 0 - - - - wxgui_scopesink2 - - id - wxgui_scopesink2_0_0_0 - - - _enabled - True - - - type - complex - - - title - Scope Plot - - - samp_rate + samples_per_second samp_rate - - v_scale - 0 - - - v_offset - 0 - - - t_scale - 0 - - - ac_couple - False - - - xy_mode - False - - - num_inputs - 1 - - - win_size - - - - grid_pos - - - - notebook - nb0,2 - - - trig_mode - wxgui.TRIG_MODE_NORM - - - y_axis_label - Counts - - - alias - - - - affinity - - - - _coordinate - (1080, 899) - - - _rotation - 0 - - - - wxgui_fftsink2 - - id - wxgui_fftsink2_0_1_0_0 - - - _enabled - True - type complex - title - FFT Plot - - - samp_rate - samp_rate/decimation - - - baseband_freq - 0 - - - y_per_div - 10 - - - y_divs - 10 - - - ref_level - 0 - - - ref_scale - 2.0 - - - fft_size - 1024 - - - fft_rate - 15 - - - peak_hold - False - - - average - False - - - avg_alpha - 0 - - - win - None - - - win_size - - - - grid_pos - - - - notebook - nb0,3 - - - freqvar - None - - - alias - - - - affinity - - - - _coordinate - (320, 667) - - - _rotation - 0 - - - - wxgui_scopesink2 - - id - wxgui_scopesink2_0_0_0_0 - - - _enabled - True - - - type - complex - - - title - Scope Plot - - - samp_rate - samp_rate/decimation - - - v_scale - 0 - - - v_offset - 0 - - - t_scale - 0 - - - ac_couple - False - - - xy_mode - False - - - num_inputs + vlen 1 - - win_size - - - - grid_pos - - - - notebook - nb0,3 - - - trig_mode - wxgui.TRIG_MODE_NORM - - - y_axis_label - Counts - - - alias - - - - affinity - - - - _coordinate - (320, 883) - - - _rotation - 0 - ha5kfu_execproc_xx - id - ha5kfu_execproc_xx_0_0_0_0 + alias + + + + commandline + "csdr shift_math_cc %g"%rate + + + comment + + + + affinity + _enabled True + + _coordinate + (832, 99) + + + _rotation + 0 + + + id + ha5kfu_execproc_xx_0 + + + maxoutbuf + 0 + + + minoutbuf + 0 + type cc + + + ha5kfu_execproc_xx + + alias + + + + commandline + "csdr shift_addition_cc %g"%rate + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (824, 443) + + + _rotation + 0 + + + id + ha5kfu_execproc_xx_0_0 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + type + cc + + + + ha5kfu_execproc_xx + + alias + + + + commandline + "csdr shift_table_cc %g 32768"%rate + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (824, 931) + + + _rotation + 0 + + + id + ha5kfu_execproc_xx_0_0_0 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + type + cc + + + + ha5kfu_execproc_xx + + alias + + commandline "csdr decimating_shift_addition_cc %g %d"%(rate, decimation) - alias + comment @@ -772,12 +487,8 @@ - minoutbuf - 0 - - - maxoutbuf - 0 + _enabled + True _coordinate @@ -787,83 +498,221 @@ _rotation 0 - - - ha5kfu_execproc_xx id - ha5kfu_execproc_xx_0_0_0 + ha5kfu_execproc_xx_0_0_0_0 - _enabled - True + maxoutbuf + 0 + + + minoutbuf + 0 type cc - - commandline - "csdr shift_table_cc %g 32768"%rate - + + + ha5kfu_execproc_xx alias + + commandline + "csdr shift_addfast_cc %g"%rate + + + comment + + affinity - minoutbuf + _enabled + True + + + _coordinate + (608, 603) + + + _rotation 0 + + id + ha5kfu_execproc_xx_0_1 + maxoutbuf 0 - _coordinate - (824, 771) + minoutbuf + 0 - _rotation - 0 + type + cc - wxgui_fftsink2 + ha5kfu_execproc_xx - id - wxgui_fftsink2_0_1_0 + alias + + + + commandline + "csdr shift_unroll_cc %g"%rate + + + comment + + + + affinity + _enabled True + + _coordinate + (104, 563) + + + _rotation + 0 + + + id + ha5kfu_execproc_xx_0_1_0 + + + maxoutbuf + 0 + + + minoutbuf + 0 + type - complex + cc + + + + notebook + + alias + - title - FFT Plot + comment + - samp_rate - samp_rate + _enabled + True + + + _coordinate + (272, 11) + + + _rotation + 0 + + + grid_pos + + + + id + nb0 + + + labels + ['shift_math_cc', 'shift_addition_cc', 'shift_table_cc', 'decimating_shift_addition_cc','shift_addfast_cc','shift_unroll_cc','original'] + + + notebook + + + + style + wx.NB_TOP + + + + wxgui_fftsink2 + + avg_alpha + 0 + + + average + False baseband_freq 0 - y_per_div - 10 + alias + - y_divs - 10 + comment + + + + affinity + + + + _enabled + True + + + fft_size + 1024 + + + freqvar + None + + + _coordinate + (1096, 19) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_fftsink2_0 + + + notebook + nb0,0 + + + peak_hold + False ref_level @@ -873,54 +722,283 @@ ref_scale 2.0 - - fft_size - 1024 - fft_rate 15 - peak_hold - False + samp_rate + samp_rate - average - False + title + FFT Plot - avg_alpha - 0 - - - win - None + type + complex win_size - grid_pos - - - - notebook - nb0,2 - - - freqvar + win None + + y_divs + 10 + + + y_per_div + 10 + + + + wxgui_fftsink2 + + avg_alpha + 0 + + + average + False + + + baseband_freq + 0 + alias + + comment + + affinity + + _enabled + True + + + fft_size + 1024 + + + freqvar + None + + + _coordinate + (848, 187) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_fftsink2_0_0 + + + notebook + nb0,6 + + + peak_hold + False + + + ref_level + 0 + + + ref_scale + 2.0 + + + fft_rate + 15 + + + samp_rate + samp_rate + + + title + FFT Plot + + + type + complex + + + win_size + + + + win + None + + + y_divs + 10 + + + y_per_div + 10 + + + + wxgui_fftsink2 + + avg_alpha + 0 + + + average + False + + + baseband_freq + 0 + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + fft_size + 1024 + + + freqvar + None + + + _coordinate + (1096, 355) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_fftsink2_0_1 + + + notebook + nb0,1 + + + peak_hold + False + + + ref_level + 0 + + + ref_scale + 2.0 + + + fft_rate + 15 + + + samp_rate + samp_rate + + + title + FFT Plot + + + type + complex + + + win_size + + + + win + None + + + y_divs + 10 + + + y_per_div + 10 + + + + wxgui_fftsink2 + + avg_alpha + 0 + + + average + False + + + baseband_freq + 0 + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + fft_size + 1024 + + + freqvar + None + _coordinate (1080, 691) @@ -929,123 +1007,21 @@ _rotation 0 - - - wxgui_scopesink2 - - id - wxgui_scopesink2_0_0 - - - _enabled - True - - - type - complex - - - title - Scope Plot - - - samp_rate - samp_rate - - - v_scale - 0 - - - v_offset - 0 - - - t_scale - 0 - - - ac_couple - False - - - xy_mode - False - - - num_inputs - 1 - - - win_size - - grid_pos - - notebook - nb0,1 - - - trig_mode - wxgui.TRIG_MODE_NORM - - - y_axis_label - Counts - - - alias - - - - affinity - - - - _coordinate - (824, 571) - - - _rotation - 0 - - - - wxgui_fftsink2 id - wxgui_fftsink2_0_1 + wxgui_fftsink2_0_1_0 - _enabled - True + notebook + nb0,2 - type - complex - - - title - FFT Plot - - - samp_rate - samp_rate - - - baseband_freq - 0 - - - y_per_div - 10 - - - y_divs - 10 + peak_hold + False ref_level @@ -1055,244 +1031,869 @@ ref_scale 2.0 - - fft_size - 1024 - fft_rate 15 - peak_hold - False + samp_rate + samp_rate - average - False + title + FFT Plot - avg_alpha - 0 - - - win - None + type + complex win_size - grid_pos - - - - notebook - nb0,1 - - - freqvar + win None + + y_divs + 10 + + + y_per_div + 10 + + + + wxgui_fftsink2 + + avg_alpha + 0 + + + average + False + + + baseband_freq + 0 + alias + + comment + + affinity + + _enabled + True + + + fft_size + 1024 + + + freqvar + None + _coordinate - (824, 355) + (320, 667) _rotation 0 + + grid_pos + + + + id + wxgui_fftsink2_0_1_0_0 + + + notebook + nb0,3 + + + peak_hold + False + + + ref_level + 0 + + + ref_scale + 2.0 + + + fft_rate + 15 + + + samp_rate + samp_rate/decimation + + + title + FFT Plot + + + type + complex + + + win_size + + + + win + None + + + y_divs + 10 + + + y_per_div + 10 + + + + wxgui_fftsink2 + + avg_alpha + 0 + + + average + False + + + baseband_freq + 0 + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + fft_size + 1024 + + + freqvar + None + + + _coordinate + (840, 523) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_fftsink2_0_1_1 + + + notebook + nb0,4 + + + peak_hold + False + + + ref_level + 0 + + + ref_scale + 2.0 + + + fft_rate + 15 + + + samp_rate + samp_rate + + + title + FFT Plot + + + type + complex + + + win_size + + + + win + None + + + y_divs + 10 + + + y_per_div + 10 + + + + wxgui_fftsink2 + + avg_alpha + 0 + + + average + False + + + baseband_freq + 0 + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + fft_size + 1024 + + + freqvar + None + + + _coordinate + (336, 315) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_fftsink2_0_1_1_0 + + + notebook + nb0,5 + + + peak_hold + False + + + ref_level + 0 + + + ref_scale + 2.0 + + + fft_rate + 15 + + + samp_rate + samp_rate + + + title + FFT Plot + + + type + complex + + + win_size + + + + win + None + + + y_divs + 10 + + + y_per_div + 10 + wxgui_scopesink2 + + ac_couple + False + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (1096, 235) + + + _rotation + 0 + + + grid_pos + + id wxgui_scopesink2_0 - _enabled - True - - - type - complex - - - title - Scope Plot - - - samp_rate - samp_rate - - - v_scale - 0 - - - v_offset - 0 - - - t_scale - 0 - - - ac_couple - False - - - xy_mode - False + notebook + nb0,0 num_inputs 1 - win_size - + samp_rate + samp_rate - grid_pos - + t_scale + 0 - notebook - nb0,0 + title + Scope Plot trig_mode wxgui.TRIG_MODE_NORM - - y_axis_label - Counts - - - alias - - - - affinity - - - - _coordinate - (824, 235) - - - _rotation - 0 - - - - wxgui_fftsink2 - - id - wxgui_fftsink2_0 - - - _enabled - True - type complex - title - FFT Plot - - - samp_rate - samp_rate - - - baseband_freq + v_offset 0 - y_per_div - 10 - - - y_divs - 10 - - - ref_level + v_scale 0 - - ref_scale - 2.0 - - - fft_size - 1024 - - - fft_rate - 15 - - - peak_hold - False - - - average - False - - - avg_alpha - 0 - - - win - None - win_size - grid_pos - + xy_mode + False - notebook - nb0,0 + y_axis_label + Counts + + + wxgui_scopesink2 - freqvar - None + ac_couple + False alias + + comment + + affinity + + _enabled + True + _coordinate - (824, 19) + (1096, 571) _rotation 0 + + grid_pos + + + + id + wxgui_scopesink2_0_0 + + + notebook + nb0,1 + + + num_inputs + 1 + + + samp_rate + samp_rate + + + t_scale + 0 + + + title + Scope Plot + + + trig_mode + wxgui.TRIG_MODE_NORM + + + type + complex + + + v_offset + 0 + + + v_scale + 0 + + + win_size + + + + xy_mode + False + + + y_axis_label + Counts + + + + wxgui_scopesink2 + + ac_couple + False + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (1080, 899) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_scopesink2_0_0_0 + + + notebook + nb0,2 + + + num_inputs + 1 + + + samp_rate + samp_rate + + + t_scale + 0 + + + title + Scope Plot + + + trig_mode + wxgui.TRIG_MODE_NORM + + + type + complex + + + v_offset + 0 + + + v_scale + 0 + + + win_size + + + + xy_mode + False + + + y_axis_label + Counts + + + + wxgui_scopesink2 + + ac_couple + False + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (320, 883) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_scopesink2_0_0_0_0 + + + notebook + nb0,3 + + + num_inputs + 1 + + + samp_rate + samp_rate/decimation + + + t_scale + 0 + + + title + Scope Plot + + + trig_mode + wxgui.TRIG_MODE_NORM + + + type + complex + + + v_offset + 0 + + + v_scale + 0 + + + win_size + + + + xy_mode + False + + + y_axis_label + Counts + + + + wxgui_scopesink2 + + ac_couple + False + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (840, 747) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_scopesink2_0_0_1 + + + notebook + nb0,4 + + + num_inputs + 1 + + + samp_rate + samp_rate + + + t_scale + 0 + + + title + Scope Plot + + + trig_mode + wxgui.TRIG_MODE_NORM + + + type + complex + + + v_offset + 0 + + + v_scale + 0 + + + win_size + + + + xy_mode + False + + + y_axis_label + Counts + + + + wxgui_scopesink2 + + ac_couple + False + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (336, 531) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_scopesink2_0_0_1_0 + + + notebook + nb0,5 + + + num_inputs + 1 + + + samp_rate + samp_rate + + + t_scale + 0 + + + title + Scope Plot + + + trig_mode + wxgui.TRIG_MODE_NORM + + + type + complex + + + v_offset + 0 + + + v_scale + 0 + + + win_size + + + + xy_mode + False + + + y_axis_label + Counts + analog_sig_source_x_0 @@ -1300,48 +1901,18 @@ 0 0 - - blocks_throttle_0 - wxgui_fftsink2_0_0 - 0 - 0 - - - ha5kfu_execproc_xx_0 - wxgui_scopesink2_0 - 0 - 0 - - - ha5kfu_execproc_xx_0 - wxgui_fftsink2_0 - 0 - 0 - blocks_throttle_0 ha5kfu_execproc_xx_0 0 0 - - ha5kfu_execproc_xx_0_0 - wxgui_fftsink2_0_1 - 0 - 0 - blocks_throttle_0 ha5kfu_execproc_xx_0_0 0 0 - - ha5kfu_execproc_xx_0_0 - wxgui_scopesink2_0_0 - 0 - 0 - blocks_throttle_0 ha5kfu_execproc_xx_0_0_0 @@ -1355,14 +1926,44 @@ 0 - ha5kfu_execproc_xx_0_0_0_0 - wxgui_scopesink2_0_0_0_0 + blocks_throttle_0 + ha5kfu_execproc_xx_0_1 0 0 - ha5kfu_execproc_xx_0_0_0_0 - wxgui_fftsink2_0_1_0_0 + blocks_throttle_0 + ha5kfu_execproc_xx_0_1_0 + 0 + 0 + + + blocks_throttle_0 + wxgui_fftsink2_0_0 + 0 + 0 + + + ha5kfu_execproc_xx_0 + wxgui_fftsink2_0 + 0 + 0 + + + ha5kfu_execproc_xx_0 + wxgui_scopesink2_0 + 0 + 0 + + + ha5kfu_execproc_xx_0_0 + wxgui_fftsink2_0_1 + 0 + 0 + + + ha5kfu_execproc_xx_0_0 + wxgui_scopesink2_0_0 0 0 @@ -1378,4 +1979,40 @@ 0 0 + + ha5kfu_execproc_xx_0_0_0_0 + wxgui_fftsink2_0_1_0_0 + 0 + 0 + + + ha5kfu_execproc_xx_0_0_0_0 + wxgui_scopesink2_0_0_0_0 + 0 + 0 + + + ha5kfu_execproc_xx_0_1 + wxgui_fftsink2_0_1_1 + 0 + 0 + + + ha5kfu_execproc_xx_0_1 + wxgui_scopesink2_0_0_1 + 0 + 0 + + + ha5kfu_execproc_xx_0_1_0 + wxgui_fftsink2_0_1_1_0 + 0 + 0 + + + ha5kfu_execproc_xx_0_1_0 + wxgui_scopesink2_0_0_1_0 + 0 + 0 + diff --git a/grc_tests/test_shift_remote.grc b/grc_tests/test_shift_remote.grc new file mode 100644 index 0000000..59620bd --- /dev/null +++ b/grc_tests/test_shift_remote.grc @@ -0,0 +1,971 @@ + + + + Thu Jan 15 18:51:48 2015 + + options + + author + + + + window_size + 1280, 1024 + + + category + Custom + + + comment + + + + description + + + + _enabled + True + + + _coordinate + (10, 10) + + + _rotation + 0 + + + generate_options + wx_gui + + + id + top_block + + + max_nouts + 0 + + + realtime_scheduling + + + + run_options + prompt + + + run + True + + + thread_safe_setters + + + + title + + + + + variable_slider + + comment + + + + converver + float_converter + + + value + 0 + + + _enabled + True + + + _coordinate + (24, 331) + + + _rotation + 0 + + + grid_pos + + + + id + gen_freq + + + label + Frequency: + + + max + samp_rate/2 + + + min + -samp_rate/2 + + + notebook + + + + num_steps + 100 + + + style + wx.SL_HORIZONTAL + + + + variable + + comment + + + + _enabled + True + + + _coordinate + (8, 195) + + + _rotation + 0 + + + id + rate + + + value + -0.1 + + + + variable + + comment + + + + _enabled + True + + + _coordinate + (176, 11) + + + _rotation + 0 + + + id + samp_rate + + + value + 250e3 + + + + analog_sig_source_x + + amp + 1 + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + freq + gen_freq + + + _coordinate + (8, 75) + + + _rotation + 0 + + + id + analog_sig_source_x_0 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + offset + 0 + + + type + complex + + + samp_rate + samp_rate + + + waveform + analog.GR_SIN_WAVE + + + + blocks_throttle + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (224, 107) + + + _rotation + 0 + + + id + blocks_throttle_0 + + + ignoretag + True + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + samples_per_second + samp_rate + + + type + complex + + + vlen + 1 + + + + ha5kfu_execproc_xx + + alias + + + + commandline + "csdr shift_addition_cc %g"%rate + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (824, 315) + + + _rotation + 0 + + + id + ha5kfu_execproc_xx_0_0 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + type + cc + + + + ha5kfu_execproc_xx + + alias + + + + commandline + ncat -v raspberrypi.local 5321 + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (536, 443) + + + _rotation + 0 + + + id + ha5kfu_execproc_xx_0_0_0_1 + + + maxoutbuf + 0 + + + minoutbuf + 0 + + + type + cc + + + + notebook + + alias + + + + comment + + + + _enabled + True + + + _coordinate + (272, 11) + + + _rotation + 0 + + + grid_pos + + + + id + nb0 + + + labels + ['original', 'shift_addition_cc','shift_addfast_cc',] + + + notebook + + + + style + wx.NB_TOP + + + + wxgui_fftsink2 + + avg_alpha + 0 + + + average + False + + + baseband_freq + 0 + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + fft_size + 1024 + + + freqvar + None + + + _coordinate + (848, 27) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_fftsink2_0_0 + + + notebook + nb0,0 + + + peak_hold + False + + + ref_level + 0 + + + ref_scale + 2.0 + + + fft_rate + 15 + + + samp_rate + samp_rate + + + title + FFT Plot + + + type + complex + + + win_size + + + + win + None + + + y_divs + 10 + + + y_per_div + 10 + + + + wxgui_fftsink2 + + avg_alpha + 0 + + + average + False + + + baseband_freq + 0 + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + fft_size + 1024 + + + freqvar + None + + + _coordinate + (1112, 339) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_fftsink2_0_1 + + + notebook + nb0,1 + + + peak_hold + False + + + ref_level + 0 + + + ref_scale + 2.0 + + + fft_rate + 15 + + + samp_rate + samp_rate + + + title + FFT Plot + + + type + complex + + + win_size + + + + win + None + + + y_divs + 10 + + + y_per_div + 10 + + + + wxgui_fftsink2 + + avg_alpha + 0 + + + average + False + + + baseband_freq + 0 + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + fft_size + 1024 + + + freqvar + None + + + _coordinate + (808, 387) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_fftsink2_0_1_1 + + + notebook + nb0,2 + + + peak_hold + False + + + ref_level + 0 + + + ref_scale + 2.0 + + + fft_rate + 15 + + + samp_rate + samp_rate + + + title + FFT Plot + + + type + complex + + + win_size + + + + win + None + + + y_divs + 10 + + + y_per_div + 10 + + + + wxgui_scopesink2 + + ac_couple + False + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (1112, 555) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_scopesink2_0_0 + + + notebook + nb0,1 + + + num_inputs + 1 + + + samp_rate + samp_rate + + + t_scale + 0 + + + title + Scope Plot + + + trig_mode + wxgui.TRIG_MODE_NORM + + + type + complex + + + v_offset + 0 + + + v_scale + 0 + + + win_size + + + + xy_mode + False + + + y_axis_label + Counts + + + + wxgui_scopesink2 + + ac_couple + False + + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (808, 611) + + + _rotation + 0 + + + grid_pos + + + + id + wxgui_scopesink2_0_0_1 + + + notebook + nb0,2 + + + num_inputs + 1 + + + samp_rate + samp_rate + + + t_scale + 0 + + + title + Scope Plot + + + trig_mode + wxgui.TRIG_MODE_NORM + + + type + complex + + + v_offset + 0 + + + v_scale + 0 + + + win_size + + + + xy_mode + False + + + y_axis_label + Counts + + + + analog_sig_source_x_0 + blocks_throttle_0 + 0 + 0 + + + blocks_throttle_0 + ha5kfu_execproc_xx_0_0 + 0 + 0 + + + blocks_throttle_0 + ha5kfu_execproc_xx_0_0_0_1 + 0 + 0 + + + blocks_throttle_0 + wxgui_fftsink2_0_0 + 0 + 0 + + + ha5kfu_execproc_xx_0_0 + wxgui_fftsink2_0_1 + 0 + 0 + + + ha5kfu_execproc_xx_0_0 + wxgui_scopesink2_0_0 + 0 + 0 + + + ha5kfu_execproc_xx_0_0_0_1 + wxgui_fftsink2_0_1_1 + 0 + 0 + + + ha5kfu_execproc_xx_0_0_0_1 + wxgui_scopesink2_0_0_1 + 0 + 0 + + diff --git a/grc_tests/test_shift_remote.sh b/grc_tests/test_shift_remote.sh new file mode 100755 index 0000000..65c7192 --- /dev/null +++ b/grc_tests/test_shift_remote.sh @@ -0,0 +1,9 @@ +#!/bin/sh +# Run this script on a Raspberry Pi 2, while running test_shift_remote.grc on your PC. +# It allows you to debug the NEON-accelerated version of specific DSP algorithms on the target hardware. +TEMPSCRIPT="/tmp/test_shift_remote_exec.sh" +echo '#!/bin/sh\ncsdr shift_addfast_cc -0.1' > $TEMPSCRIPT +cat $TEMPSCRIPT +chmod +x $TEMPSCRIPT +ncat -vvl 5321 -e $TEMPSCRIPT +rm $TEMPSCRIPT diff --git a/libcsdr.c b/libcsdr.c index 0a908e3..5f0bdb0 100644 --- a/libcsdr.c +++ b/libcsdr.c @@ -263,8 +263,208 @@ float shift_table_cc(complexf* input, complexf* output, int input_size, float ra return phase; } + +shift_unroll_data_t shift_unroll_init(float rate, int size) +{ + shift_unroll_data_t output; + output.phase_increment=2*rate*PI; + output.size = size; + output.dsin=(float*)malloc(sizeof(float)*size); + output.dcos=(float*)malloc(sizeof(float)*size); + float myphase = 0; + for(int i=0;iPI) myphase-=2*PI; + while(myphase<-PI) myphase+=2*PI; + output.dsin[i]=sin(myphase); + output.dcos[i]=cos(myphase); + } + return output; +} + +float shift_unroll_cc(complexf *input, complexf* output, int input_size, shift_unroll_data_t* d, float starting_phase) +{ + //input_size should be multiple of 4 + //fprintf(stderr, "shift_addfast_cc: input_size = %d\n", input_size); + float cos_start=cos(starting_phase); + float sin_start=sin(starting_phase); + register float cos_val, sin_val; + for(int i=0;idcos[i] - sin_start * d->dsin[i]; + sin_val = sin_start * d->dcos[i] + cos_start * d->dsin[i]; + iof(output,i)=cos_val*iof(input,i)-sin_val*qof(input,i); + qof(output,i)=sin_val*iof(input,i)+cos_val*qof(input,i); + } + starting_phase+=input_size*d->phase_increment; + while(starting_phase>PI) starting_phase-=2*PI; + while(starting_phase<-PI) starting_phase+=2*PI; + return starting_phase; +} + +shift_addfast_data_t shift_addfast_init(float rate) +{ + shift_addfast_data_t output; + output.phase_increment=2*rate*PI; + for(int i=0;i<4;i++) + { + output.dsin[i]=sin(output.phase_increment*(i+1)); + output.dcos[i]=cos(output.phase_increment*(i+1)); + } + return output; +} + #ifdef NEON_OPTS -#pragma message "We have a faster fir_decimate_cc now." +#pragma message "Manual NEON optimizations are ON: we have a faster shift_addfast_cc now." + +float shift_addfast_cc(complexf *input, complexf* output, int input_size, shift_addfast_data_t* d, float starting_phase) +{ + //input_size should be multiple of 4 + float cos_start[4], sin_start[4]; + float cos_vals[4], sin_vals[4]; + for(int i=0;i<4;i++) + { + cos_start[i] = cos(starting_phase); + sin_start[i] = sin(starting_phase); + } + + float* pdcos = d->dcos; + float* pdsin = d->dsin; + register float* pinput = (float*)input; + register float* pinput_end = (float*)(input+input_size); + register float* poutput = (float*)output; + + //Register map: + #define RDCOS "q0" //dcos, dsin + #define RDSIN "q1" + #define RCOSST "q2" //cos_start, sin_start + #define RSINST "q3" + #define RCOSV "q4" //cos_vals, sin_vals + #define RSINV "q5" + #define ROUTI "q6" //output_i, output_q + #define ROUTQ "q7" + #define RINPI "q8" //input_i, input_q + #define RINPQ "q9" + #define R3(x,y,z) x ", " y ", " z "\n\t" + + asm volatile( //(the range of q is q0-q15) + " vld1.32 {" RDCOS "}, [%[pdcos]]\n\t" + " vld1.32 {" RDSIN "}, [%[pdsin]]\n\t" + " vld1.32 {" RCOSST "}, [%[cos_start]]\n\t" + " vld1.32 {" RSINST "}, [%[sin_start]]\n\t" + "for_addfast: vld2.32 {" RINPI "-" RINPQ "}, [%[pinput]]!\n\t" //load q0 and q1 directly from the memory address stored in pinput, with interleaving (so that we get the I samples in RINPI and the Q samples in RINPQ), also increment the memory address in pinput (hence the "!" mark) + + //C version: + //cos_vals[j] = cos_start * d->dcos[j] - sin_start * d->dsin[j]; + //sin_vals[j] = sin_start * d->dcos[j] + cos_start * d->dsin[j]; + + " vmul.f32 " R3(RCOSV, RCOSST, RDCOS) //cos_vals[i] = cos_start * d->dcos[i] + " vmls.f32 " R3(RCOSV, RSINST, RDSIN) //cos_vals[i] -= sin_start * d->dsin[i] + " vmul.f32 " R3(RSINV, RSINST, RDCOS) //sin_vals[i] = sin_start * d->dcos[i] + " vmla.f32 " R3(RSINV, RCOSST, RDSIN) //sin_vals[i] += cos_start * d->dsin[i] + + //C version: + //iof(output,4*i+j)=cos_vals[j]*iof(input,4*i+j)-sin_vals[j]*qof(input,4*i+j); + //qof(output,4*i+j)=sin_vals[j]*iof(input,4*i+j)+cos_vals[j]*qof(input,4*i+j); + " vmul.f32 " R3(ROUTI, RCOSV, RINPI) //output_i = cos_vals * input_i + " vmls.f32 " R3(ROUTI, RSINV, RINPQ) //output_i -= sin_vals * input_q + " vmul.f32 " R3(ROUTQ, RSINV, RINPI) //output_q = sin_vals * input_i + " vmla.f32 " R3(ROUTQ, RCOSV, RINPQ) //output_i += cos_vals * input_q + + " vst2.32 {" ROUTI "-" ROUTQ "}, [%[poutput]]!\n\t" //store the outputs in memory + //" add %[poutput],%[poutput],#32\n\t" + " vdup.32 " RCOSST ", d9[1]\n\t" // cos_start[0-3] = cos_vals[3] + " vdup.32 " RSINST ", d11[1]\n\t" // sin_start[0-3] = sin_vals[3] + + " cmp %[pinput], %[pinput_end]\n\t" //if(pinput != pinput_end) + " bcc for_addfast\n\t" // then goto for_addfast + : + [pinput]"+r"(pinput), [poutput]"+r"(poutput) //output operand list -> C variables that we will change from ASM + : + [pinput_end]"r"(pinput_end), [pdcos]"r"(pdcos), [pdsin]"r"(pdsin), [sin_start]"r"(sin_start), [cos_start]"r"(cos_start) //input operand list + : + "memory", "q0", "q1", "q2", "q4", "q5", "q6", "q7", "q8", "q9", "cc" //clobber list + ); + starting_phase+=input_size*d->phase_increment; + while(starting_phase>PI) starting_phase-=2*PI; + while(starting_phase<-PI) starting_phase+=2*PI; + return starting_phase; +} + +#else + + +#if 1 + +#define SADF_L1(j) cos_vals_ ## j = cos_start * dcos_ ## j - sin_start * dsin_ ## j; \ + sin_vals_ ## j = sin_start * dcos_ ## j + cos_start * dsin_ ## j; +#define SADF_L2(j) iof(output,4*i+j)=(cos_vals_ ## j)*iof(input,4*i+j)-(sin_vals_ ## j)*qof(input,4*i+j); \ + qof(output,4*i+j)=(sin_vals_ ## j)*iof(input,4*i+j)+(cos_vals_ ## j)*qof(input,4*i+j); + +float shift_addfast_cc(complexf *input, complexf* output, int input_size, shift_addfast_data_t* d, float starting_phase) +{ + //input_size should be multiple of 4 + //fprintf(stderr, "shift_addfast_cc: input_size = %d\n", input_size); + float cos_start=cos(starting_phase); + float sin_start=sin(starting_phase); + float register cos_vals_0, cos_vals_1, cos_vals_2, cos_vals_3, + sin_vals_0, sin_vals_1, sin_vals_2, sin_vals_3, + dsin_0 = d->dsin[0], dsin_1 = d->dsin[1], dsin_2 = d->dsin[2], dsin_3 = d->dsin[3], + dcos_0 = d->dcos[0], dcos_1 = d->dcos[1], dcos_2 = d->dcos[2], dcos_3 = d->dcos[3]; + + for(int i=0;iphase_increment; + while(starting_phase>PI) starting_phase-=2*PI; + while(starting_phase<-PI) starting_phase+=2*PI; + return starting_phase; +} +#else +float shift_addfast_cc(complexf *input, complexf* output, int input_size, shift_addfast_data_t* d, float starting_phase) +{ + //input_size should be multiple of 4 + //fprintf(stderr, "shift_addfast_cc: input_size = %d\n", input_size); + float cos_start=cos(starting_phase); + float sin_start=sin(starting_phase); + float cos_vals[4], sin_vals[4]; + for(int i=0;idcos[j] - sin_start * d->dsin[j]; + sin_vals[j] = sin_start * d->dcos[j] + cos_start * d->dsin[j]; + } + for(int j=0;j<4;j++) //@shift_addfast_cc + { + iof(output,4*i+j)=cos_vals[j]*iof(input,4*i+j)-sin_vals[j]*qof(input,4*i+j); + qof(output,4*i+j)=sin_vals[j]*iof(input,4*i+j)+cos_vals[j]*qof(input,4*i+j); + } + cos_start = cos_vals[3]; + sin_start = sin_vals[3]; + } + starting_phase+=input_size*d->phase_increment; + while(starting_phase>PI) starting_phase-=2*PI; + while(starting_phase<-PI) starting_phase+=2*PI; + return starting_phase; +} +#endif + +#endif + +#ifdef NEON_OPTS +#pragma message "Manual NEON optimizations are ON: we have a faster fir_decimate_cc now." //max help: http://community.arm.com/groups/android-community/blog/2015/03/27/arm-neon-programming-quick-reference @@ -303,7 +503,7 @@ q4, q5: accumulator for I branch and Q branch (will be the output) " vld1.32 {q2}, [%[ptaps]]!\n\t" " vmla.f32 q4, q0, q2\n\t" //quad_acc_i += quad_input_i * quad_taps_1 //http://stackoverflow.com/questions/3240440/how-to-use-the-multiply-and-accumulate-intrinsics-in-arm-cortex-a8 //http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489e/CIHEJBIE.html " vmla.f32 q5, q1, q2\n\t" //quad_acc_q += quad_input_q * quad_taps_1 - " cmp %[ptaps], %[ptaps_end]\n\t" //if(ptaps == ptaps_end) + " cmp %[ptaps], %[ptaps_end]\n\t" //if(ptaps != ptaps_end) " bcc for_fdccasm\n\t" // then goto for_fdcasm " vst1.32 {q4}, [%[quad_acci]]\n\t" //if the loop is finished, store the two accumulators in memory " vst1.32 {q5}, [%[quad_accq]]\n\t" @@ -930,6 +1130,29 @@ void apply_window_c(complexf* input, complexf* output, int size, window_t window } } +float *precalculate_window(int size, window_t window) +{ + float (*window_function)(float)=firdes_get_window_kernel(window); + float *windowt; + windowt = malloc(sizeof(float) * size); + for(int i=0;i>8); + unsigned char* ptemp=(unsigned char*)&temp; + output[k++]=*ptemp; + output[k++]=*(ptemp+1); + output[k++]=*(ptemp+2); + } + else for(int i=0;i>8); + unsigned char* ptemp=(unsigned char*)&temp; + output[k++]=*(ptemp+2); + output[k++]=*(ptemp+1); + output[k++]=*ptemp; + } +} + +void convert_s24_f(unsigned char* input, float* output, int input_size, int bigendian) +{ + int k=0; + if(bigendian) for(int i=0;i +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ANDRAS RETZLER BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "nmux.h" + +char help_text[]="nmux is a TCP stream multiplexer. It reads data from the standard input, and sends it to each client connected through TCP sockets. Available command line options are:\n" +"\t--port (-p), --address (-a): TCP port and address to listen.\n" +"\t--bufsize (-b), --bufcnt (-n): Internal buffer size and count.\n" +"\t--help (-h): Show this message.\n"; + +int host_port = 0; +char host_address[100] = "127.0.0.1"; +int thread_cntr = 0; + +//CLI parameters +int bufsize = 1024; +int bufcnt = 1024; + +char** global_argv; +int global_argc; +tsmpool* pool; + +pthread_cond_t wait_condition; +pthread_mutex_t wait_mutex; + +void sig_handler(int signo) +{ + fprintf(stderr, MSG_START "signal %d caught, exiting...\n", signo); + fflush(stderr); + exit(0); +} + +int main(int argc, char* argv[]) +{ + global_argv = argv; + global_argc = argc; + int c; + int no_options = 1; + for(;;) + { + int option_index = 0; + static struct option long_options[] = { + {"port", required_argument, 0, 'p' }, + {"address", required_argument, 0, 'a' }, + {"bufsize", required_argument, 0, 'b' }, + {"bufcnt", required_argument, 0, 'n' }, + {"help", no_argument, 0, 'h' }, + {0, 0, 0, 0 } + }; + c = getopt_long(argc, argv, "p:a:b:n:h", long_options, &option_index); + if(c==-1) break; + no_options = 0; + switch (c) + { + case 'a': + host_address[100-1]=0; + strncpy(host_address,optarg,100-1); + break; + case 'p': + host_port=atoi(optarg); + break; + case 'b': + bufsize=atoi(optarg); + break; + case 'n': + bufcnt=atoi(optarg); + break; + case 'h': + print_exit(help_text); + break; + case 0: + case '?': + case ':': + default: + print_exit(MSG_START "error in getopt_long()\n"); + } + } + + if(no_options) print_exit(help_text); + if(!host_port) print_exit(MSG_START "missing required command line argument, --port.\n"); + if(bufsize<=0) print_exit(MSG_START "invalid value for --bufsize (should be >0)\n"); + if(bufcnt<=0) print_exit(MSG_START "invalid value for --bufcnt (should be >0)\n"); + + //set signals + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sig_handler; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGKILL, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); + + struct sockaddr_in addr_host; + int listen_socket; + std::vector clients; + clients.reserve(100); + listen_socket=socket(AF_INET,SOCK_STREAM,0); + + int sockopt = 1; + if( setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&sockopt, sizeof(sockopt)) == -1 ) + error_exit(MSG_START "cannot set SO_REUSEADDR"); //the best description on SO_REUSEADDR ever: http://stackoverflow.com/a/14388707/3182453 + + memset(&addr_host,'0',sizeof(addr_host)); + addr_host.sin_family = AF_INET; + addr_host.sin_port = htons(host_port); + addr_host.sin_addr.s_addr = INADDR_ANY; + + if( (addr_host.sin_addr.s_addr=inet_addr(host_address)) == INADDR_NONE ) + error_exit(MSG_START "invalid host address"); + + if( bind(listen_socket, (struct sockaddr*) &addr_host, sizeof(addr_host)) < 0 ) + error_exit(MSG_START "cannot bind() address to the socket"); + + if( listen(listen_socket, 10) == -1 ) + error_exit(MSG_START "cannot listen() on socket"); + + fprintf(stderr, MSG_START "listening on %s:%d\n", inet_ntoa(addr_host.sin_addr), host_port); + + struct sockaddr_in addr_cli; + socklen_t addr_cli_len = sizeof(addr_cli); + int new_socket; + + int highfd = 0; + maxfd(&highfd, listen_socket); + maxfd(&highfd, STDIN_FILENO); + + fd_set select_fds; + + //Set stdin and listen_socket to non-blocking + if(set_nonblocking(STDIN_FILENO) || set_nonblocking(listen_socket)) + error_exit(MSG_START "cannot set_nonblocking()"); + + //Create tsmpool + pool = new tsmpool(bufsize, bufcnt); + if(!pool->is_ok()) print_exit(MSG_START "tsmpool failed to initialize\n"); + + unsigned char* current_write_buffer = (unsigned char*)pool->get_write_buffer(); + int index_in_current_write_buffer = 0; + + //Create wait condition: client threads waiting for input data from the main thread will be + // waiting on this condition. They will be woken up with pthread_cond_broadcast() if new + // data arrives. + if(pthread_cond_init(&wait_condition, NULL)) + print_exit(MSG_START "pthread_cond_init failed"); //cond_attrs is ignored by Linux + + if(pthread_mutex_init(&wait_mutex, NULL)) + print_exit(MSG_START "pthread_mutex_t failed"); //cond_attrs is ignored by Linux + + for(;;) + { + FD_ZERO(&select_fds); + FD_SET(listen_socket, &select_fds); + FD_SET(STDIN_FILENO, &select_fds); + + if(NMUX_DEBUG) fprintf(stderr, "mainfor: selecting..."); + //Let's wait until there is any new data to read, or any new connection! + int select_ret = select(highfd, &select_fds, NULL, NULL, NULL); + if(NMUX_DEBUG) fprintf(stderr, "selected.\n"); + if(select_ret == -1) error_exit("mainfor select() error"); + + //Is there a new client connection? + if( FD_ISSET(listen_socket, &select_fds) && ((new_socket = accept(listen_socket, (struct sockaddr*)&addr_cli, &addr_cli_len)) != -1) ) + { + if(NMUX_DEBUG) + { + fprintf(stderr, "\x1b[1m\x1b[33mmainfor: clients before closing: "); + for(int i=0;istatus == CS_THREAD_FINISHED) + { + if(NMUX_DEBUG) fprintf(stderr, "mainfor: client removed: %d\n", i); + //client destructor + pool->remove_thread(clients[i]->tsmthread); + clients.erase(clients.begin()+i); + i--; + } + } + if(NMUX_DEBUG) + { + fprintf(stderr, "\x1b[1m\x1b[33mmainfor: clients after closing: "); + for(int i=0;ierror = 0; + memcpy(&new_client->addr, &addr_cli, sizeof(struct sockaddr_in)); + new_client->socket = new_socket; + new_client->status = CS_CREATED; + new_client->tsmthread = pool->register_thread(); + new_client->lpool = pool; + new_client->sleeping = 0; + if(pthread_create(&new_client->thread, NULL, client_thread, (void*)new_client)==0) + { + clients.push_back(new_client); + fprintf(stderr, MSG_START "pthread_create() done, clients now: %d\n", clients.size()); + } + else + { + fprintf(stderr, MSG_START "pthread_create() failed.\n"); + pool->remove_thread(new_client->tsmthread); + delete new_client; + } + } + + if( FD_ISSET(STDIN_FILENO, &select_fds) ) + { + if(index_in_current_write_buffer >= bufsize) + { + if(NMUX_DEBUG) fprintf(stderr, "mainfor: gwbing..."); + current_write_buffer = (unsigned char*)pool->get_write_buffer(); + if(NMUX_DEBUG) fprintf(stderr, "gwbed.\nmainfor: cond broadcasting..."); + pthread_mutex_lock(&wait_mutex); + pthread_cond_broadcast(&wait_condition); + pthread_mutex_unlock(&wait_mutex); + if(NMUX_DEBUG) fprintf(stderr, "cond broadcasted.\n"); + //Shouldn't we do it after we put data in? + // No, on get_write_buffer() actually the previous buffer is getting available + // for read for threads that wait for new data (wait on global pthead mutex + // wait_condition). + index_in_current_write_buffer = 0; + } + + if(NMUX_DEBUG) fprintf(stderr, "mainfor: reading...\n"); + int read_ret = read(STDIN_FILENO, current_write_buffer + index_in_current_write_buffer, bufsize - index_in_current_write_buffer); + if(NMUX_DEBUG) fprintf(stderr, "read %d\n", read_ret); + if(read_ret>0) + { + index_in_current_write_buffer += read_ret; + } + else if(read_ret==0) + { + //End of input stream, close clients and exit + print_exit(MSG_START "(main thread/for) end input stream, exiting.\n"); + } + else if(read_ret==-1) + { + if(errno == EAGAIN) { if(NMUX_DEBUG) fprintf(stderr, "mainfor: read EAGAIN\n"); /* seems like select would block forever, so we just read again */ } + else error_exit(MSG_START "(main thread/for) error in read(), exiting.\n"); + } + } + } +} + +void* client_thread (void* param) +{ + fprintf(stderr, "client 0x%x: started!\n", (intptr_t)param); + client_t* this_client = (client_t*)param; + this_client->status = CS_THREAD_RUNNING; + int retval; + tsmpool* lpool = this_client->lpool; + if(NMUX_DEBUG) fprintf(stderr, "client 0x%x: socket = %d!\n", (intptr_t)param, this_client->socket); + + if(NMUX_DEBUG) fprintf(stderr, "client 0x%x: poll init...", (intptr_t)param); + struct pollfd pollfds[1]; + pollfds[0].fd = this_client->socket; + pollfds[0].events = POLLOUT; + pollfds[0].revents = 0; + if(NMUX_DEBUG) fprintf(stderr, "client poll inited.\n"); + + //Set this_client->socket to non-blocking + if(set_nonblocking(this_client->socket)) + error_exit(MSG_START "cannot set_nonblocking() on this_client->socket"); + + int client_buffer_index = 0; + int client_goto_source = 0; + char* pool_read_buffer = NULL; + + for(;;) + { + //Wait until there is any data to send. + // If I haven't sent all the data from my last buffer, don't wait. + // (Wait for the server process to wake me up.) + while(!pool_read_buffer || client_buffer_index >= lpool->size) + { + if(NMUX_DEBUG) fprintf(stderr, "client 0x%x: trying to grb\n", (intptr_t)param); + pool_read_buffer = (char*)lpool->get_read_buffer(this_client->tsmthread); + if(pool_read_buffer) { client_buffer_index = 0; break; } + if(NMUX_DEBUG) fprintf(stderr, "client 0x%x: cond_waiting for more data\n", (intptr_t)param); + pthread_mutex_lock(&wait_mutex); + this_client->sleeping = 1; + pthread_cond_wait(&wait_condition, &wait_mutex); + pthread_mutex_unlock(&wait_mutex); + } + + //Wait for the socket to be available for write. + if(NMUX_DEBUG) fprintf(stderr, "client 0x%x: polling for socket write...", (intptr_t)param); + int ret = poll(pollfds, 1, -1); + if(NMUX_DEBUG) fprintf(stderr, "client polled for socket write.\n"); + if(ret == 0) continue; + else if (ret == -1) { client_goto_source = 1; goto client_thread_exit; } + + //Read data from global tsmpool and write it to client socket + if(NMUX_DEBUG) fprintf(stderr, "client 0x%x: sending...", (intptr_t)param); + ret = send(this_client->socket, pool_read_buffer + client_buffer_index, lpool->size - client_buffer_index, MSG_NOSIGNAL); + if(NMUX_DEBUG) fprintf(stderr, "client sent.\n"); + if(ret == -1) + { + switch(errno) + { + case EAGAIN: break; + default: client_goto_source = 2; goto client_thread_exit; + } + } + else client_buffer_index += ret; + } + +client_thread_exit: + fprintf(stderr, "client 0x%x: CS_THREAD_FINISHED, client_goto_source = %d, errno = %d", (intptr_t)param, client_goto_source, errno); + this_client->status = CS_THREAD_FINISHED; + pthread_exit(NULL); + return NULL; +} + + +int set_nonblocking(int fd) +{ + int flagtmp; + if((flagtmp = fcntl(fd, F_GETFL))!=-1) + if((flagtmp = fcntl(fd, F_SETFL, flagtmp|O_NONBLOCK))!=-1) + return 0; + return 1; +} + +void error_exit(const char* why) +{ + perror(why); //do we need a \n at the end of (why)? + exit(1); +} + +void print_exit(const char* why) +{ + fprintf(stderr, "%s", why); + exit(1); +} + +void maxfd(int* maxfd, int fd) +{ + if(fd>=*maxfd) *maxfd=fd+1; +} diff --git a/nmux.h b/nmux.h new file mode 100644 index 0000000..038bc51 --- /dev/null +++ b/nmux.h @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tsmpool.h" + +#define MSG_START "nmux: " +#define NMUX_DEBUG 0 + +typedef enum client_status_e +{ + CS_CREATED, + CS_THREAD_RUNNING, + CS_THREAD_FINISHED +} client_status_t; + + +typedef struct client_s +{ + struct sockaddr_in addr; + int socket; + int error; //set to non-zero on error (data transfer failed) + pthread_t thread; + tsmthread_t* tsmthread; + client_status_t status; + //the following members are there to give access to some global variables inside the thread: + tsmpool* lpool; + int sleeping; +} client_t; + +void print_exit(const char* why); +void sig_handler(int signo); +void* client_thread (void* param); +void error_exit(const char* why); +void maxfd(int* maxfd, int fd); +int set_nonblocking(int fd); diff --git a/test200.c b/test200.c new file mode 100644 index 0000000..0a89708 --- /dev/null +++ b/test200.c @@ -0,0 +1,124 @@ +/* +This software is part of libcsdr, a set of simple DSP routines for +Software Defined Radio. + +Copyright (c) 2014-2015, Andras Retzler +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ANDRAS RETZLER BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "libcsdr.h" +#include "libcsdr_gpl.h" + +#define T_BUFSIZE (1024*1024/4) +#define T_N (200) +#define T_TAPS (1023) +#define T_DECFACT (200) + +int main() +{ + fprintf(stderr,"Getting a %d of random samples...\n", T_BUFSIZE); + int urand_fp = open("/dev/urandom",O_RDWR); + unsigned char* buf_u8 = (unsigned char*)malloc(sizeof(unsigned char)*T_BUFSIZE*2); + complexf* buf_c = (complexf*)malloc(sizeof(complexf)*T_BUFSIZE); + complexf* outbuf_c = (complexf*)malloc(sizeof(complexf)*T_BUFSIZE); + read(urand_fp, buf_u8, T_BUFSIZE); + close(urand_fp); + + for(int i=0;ithreads_cntr = 0; + this->ok = 1; + this->lowest_read_index = -1; + this->write_index = 0; + this->my_read_index = index_before(0); + if (pthread_mutex_init(&this->mutex, NULL) != 0) { this->ok = 0; return; } + for(int i=0; iok = 0; return; } + buffers.push_back(newptr); + } +} + +int tsmpool::is_ok() { return this->ok; } + +void* tsmpool::get_write_buffer() +{ + //if(write_index==index_before(lowest_read_index)) return NULL; + pthread_mutex_lock(&this->mutex); + void* to_return = buffers[write_index]; + write_index = index_next(write_index); + pthread_mutex_unlock(&this->mutex); + if(TSM_DEBUG) fprintf(stderr, "gwb: write_index = %d\n", write_index); + return to_return; +} + +tsmthread_t* tsmpool::register_thread() +{ + if(!ok) return NULL; + pthread_mutex_lock(&this->mutex); + tsmthread_t* thread = new tsmthread_t(); + thread->read_index = index_before(write_index); + threads.push_back(thread); + pthread_mutex_unlock(&this->mutex); + return thread; +} + +int tsmpool::remove_thread(tsmthread_t* thread) +{ + pthread_mutex_lock(&this->mutex); + for(int i=0;imutex); +} + +void* tsmpool::get_read_buffer(tsmthread_t* thread) +{ + pthread_mutex_lock(&this->mutex); + int* actual_read_index = (thread==NULL) ? &my_read_index : &thread->read_index; + if(*actual_read_index==index_before(write_index)) + { + if(TSM_DEBUG) fprintf(stderr, "grb: fail," + "read_index %d is just before write_index\n", *actual_read_index); + pthread_mutex_unlock(&this->mutex); + return NULL; + } + void* to_return = buffers[*actual_read_index]; + *actual_read_index=index_next(*actual_read_index); + pthread_mutex_unlock(&this->mutex); + if(TSM_DEBUG) fprintf(stderr, "grb: read_index = %d\n", *actual_read_index); + return to_return; +} diff --git a/tsmpool.h b/tsmpool.h new file mode 100644 index 0000000..d24cc4e --- /dev/null +++ b/tsmpool.h @@ -0,0 +1,43 @@ +//tsmpool stands for Thread-Safe Memory Pool. + +//It implements a big circular buffer that one thread writes into, and multiple threads read from. +//The reader threads have lower priority than the writer thread (they can be left behind if the don't read fast enough). + +#include +#include + +#define TSM_DEBUG 0 +#include + +using namespace std; + +typedef struct tsmthread_s +{ + int read_index; //it always points to the next buffer to be read +} tsmthread_t; + +class tsmpool +{ +private: + vector threads; + vector buffers; + int threads_cntr; + pthread_mutex_t mutex; + int ok; //tsmpool is expected to be included in C-style programs. + // If something fails in the constructor, it will be seen here instead of a try{}catch{} + int write_index; //it always points to the next buffer to be written + int lowest_read_index; //unused + int my_read_index; //it is used when tsmpool is used as a single writer - single reader circular buffer + +public: + const size_t size; + const int num; + int is_ok(); + tsmpool(size_t size, int num); + void* get_write_buffer(); + tsmthread_t* register_thread(); + int remove_thread(tsmthread_t* thread); + void* get_read_buffer(tsmthread_t* thread); + int index_next(int index) { return (index+1==num)?0:index+1; } + int index_before(int index) { return (index-1<0)?num-1:index-1; } +};