Merged master to feature/digitalmods
This commit is contained in:
commit
e524dc2af1
25 changed files with 6908 additions and 1621 deletions
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
csdr
|
||||||
|
nmux
|
||||||
|
ddcd
|
||||||
|
*.o
|
||||||
|
*.so
|
||||||
|
tags
|
||||||
|
dumpvect.*.vect
|
24
Makefile
24
Makefile
|
@ -26,12 +26,10 @@
|
||||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
LIBSOURCES = fft_fftw.c libcsdr_wrapper.c
|
LIBSOURCES = fft_fftw.c libcsdr_wrapper.c
|
||||||
#SOURCES = csdr.c $(LIBSOURCES)
|
#SOURCES = csdr.c $(LIBSOURCES)
|
||||||
cpufeature = $(if $(findstring $(1),$(shell cat /proc/cpuinfo)),$(2))
|
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
|
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
|
#tnx Jan Szumiec for the Raspberry Pi support
|
||||||
PARAMS_RASPI = -mfloat-abi=hard -mcpu=arm1176jzf-s -mfpu=vfp -funsafe-math-optimizations -Wformat=0
|
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_LIBS = -g -lm -lrt -lfftw3f -DUSE_FFTW -DLIBCSDR_GPL -DUSE_IMA_ADPCM
|
||||||
PARAMS_SO = -fpic
|
PARAMS_SO = -fpic
|
||||||
PARAMS_MISC = -Wno-unused-result
|
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
|
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 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 Auto-detected optimization parameters: $(PARAMS_SIMD)
|
||||||
@echo
|
@echo
|
||||||
|
rm -f dumpvect*.vect
|
||||||
gcc -std=gnu99 $(PARAMS_LOOPVECT) $(PARAMS_SIMD) $(LIBSOURCES) $(PARAMS_LIBS) $(PARAMS_MISC) -fpic -shared -o libcsdr.so
|
gcc -std=gnu99 $(PARAMS_LOOPVECT) $(PARAMS_SIMD) $(LIBSOURCES) $(PARAMS_LIBS) $(PARAMS_MISC) -fpic -shared -o libcsdr.so
|
||||||
-./parsevect dumpvect*.vect
|
-./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
|
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
|
arm-cross: clean-vect
|
||||||
#note: this doesn't work since having added FFTW
|
#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
|
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:
|
clean-vect:
|
||||||
rm -f dumpvect*.vect
|
rm -f dumpvect*.vect
|
||||||
clean: clean-vect
|
clean: clean-vect
|
||||||
rm -f libcsdr.so csdr
|
rm -f libcsdr.so csdr ddcd nmux
|
||||||
install:
|
install: all
|
||||||
install -m 0755 libcsdr.so /usr/lib
|
install -m 0755 libcsdr.so /usr/lib
|
||||||
install -m 0755 csdr /usr/bin
|
install -m 0755 csdr /usr/bin
|
||||||
install -m 0755 csdr-fm /usr/bin
|
install -m 0755 csdr-fm /usr/bin
|
||||||
|
install -m 0755 nmux /usr/bin
|
||||||
|
#-install -m 0755 ddcd /usr/bin
|
||||||
ldconfig
|
ldconfig
|
||||||
uninstall:
|
uninstall:
|
||||||
rm /usr/lib/libcsdr.so /usr/bin/csdr /usr/bin/csdr-fm
|
rm /usr/lib/libcsdr.so /usr/bin/csdr /usr/bin/csdr-fm
|
||||||
|
@ -82,7 +92,7 @@ emcc-get-deps:
|
||||||
emmake make; \
|
emmake make; \
|
||||||
emmake make install
|
emmake make install
|
||||||
emcc:
|
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
|
cat sdr.js/sdrjs-header.js sdr.js/sdrjs-compiled.js sdr.js/sdrjs-footer.js > sdr.js/sdr.js
|
||||||
emcc-beautify:
|
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'
|
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'
|
||||||
|
|
25
README.md
25
README.md
|
@ -205,7 +205,7 @@ Internally, a sine and cosine wave is generated to perform this function, and th
|
||||||
|
|
||||||
shift_addition_cc <rate>
|
shift_addition_cc <rate>
|
||||||
|
|
||||||
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.)
|
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).
|
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.
|
The higher the table size is, the smaller the phase error is.
|
||||||
|
|
||||||
|
shift_addfast_cc <rate>
|
||||||
|
|
||||||
|
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 <rate>
|
||||||
|
|
||||||
|
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 <rate> [decimation]
|
decimating_shift_addition_cc <rate> [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).
|
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
|
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)
|
## [Licensing] (#licensing)
|
||||||
|
|
||||||
Most of the code of `libcsdr` is under BSD license.
|
Most of the code of `libcsdr` is under BSD license.
|
||||||
|
|
371
csdr.c
371
csdr.c
|
@ -48,7 +48,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include "ima_adpcm.h"
|
#include "ima_adpcm.h"
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <strings.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include "fastddc.h"
|
||||||
|
|
||||||
char usage[]=
|
char usage[]=
|
||||||
"csdr - a simple commandline tool for Software Defined Radio receiver DSP.\n\n"
|
"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_s8\n"
|
||||||
" convert_f_s16\n"
|
" convert_f_s16\n"
|
||||||
" convert_s16_f\n"
|
" convert_s16_f\n"
|
||||||
|
" convert_f_s24 [--bigendian]\n"
|
||||||
|
" convert_s24_f [--bigendian]\n"
|
||||||
" realpart_cf\n"
|
" realpart_cf\n"
|
||||||
" clipdetect_ff\n"
|
" clipdetect_ff\n"
|
||||||
" limit_ff [max_amplitude]\n"
|
" limit_ff [max_amplitude]\n"
|
||||||
|
@ -190,6 +194,22 @@ int init_fifo(int argc, char *argv[])
|
||||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||||
return fd;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -493,7 +513,7 @@ int main(int argc, char *argv[])
|
||||||
TRY_YIELD;
|
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;
|
if(!sendbufsize(initialize_buffers())) return -2;
|
||||||
for(;;)
|
for(;;)
|
||||||
|
@ -505,6 +525,34 @@ int main(int argc, char *argv[])
|
||||||
TRY_YIELD;
|
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(!strcmp(argv[1],"realpart_cf"))
|
||||||
{
|
{
|
||||||
if(!sendbufsize(initialize_buffers())) return -2;
|
if(!sendbufsize(initialize_buffers())) return -2;
|
||||||
|
@ -585,6 +633,7 @@ int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
FEOF_CHECK;
|
FEOF_CHECK;
|
||||||
if(!FREAD_C) break;
|
if(!FREAD_C) break;
|
||||||
|
starting_phase=shift_math_cc((complexf*)input_buffer, (complexf*)output_buffer, the_bufsize, rate, starting_phase);
|
||||||
FWRITE_C;
|
FWRITE_C;
|
||||||
TRY_YIELD;
|
TRY_YIELD;
|
||||||
}
|
}
|
||||||
|
@ -618,6 +667,105 @@ int main(int argc, char *argv[])
|
||||||
return 0;
|
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
|
#ifdef LIBCSDR_GPL
|
||||||
if(!strcmp(argv[1],"decimating_shift_addition_cc"))
|
if(!strcmp(argv[1],"decimating_shift_addition_cc"))
|
||||||
{
|
{
|
||||||
|
@ -830,7 +978,7 @@ int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
FEOF_CHECK;
|
FEOF_CHECK;
|
||||||
FREAD_R;
|
FREAD_R;
|
||||||
for(int i=0; i<the_bufsize;i++) fprintf(stderr, "%g ",input_buffer[i]);
|
for(int i=0; i<the_bufsize;i++) printf("%g ",input_buffer[i]);
|
||||||
TRY_YIELD;
|
TRY_YIELD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1252,6 +1400,8 @@ int main(int argc, char *argv[])
|
||||||
FFT_PLAN_T* plan=make_fft_c2c(fft_size, windowed, output, 1, benchmark);
|
FFT_PLAN_T* plan=make_fft_c2c(fft_size, windowed, output, 1, benchmark);
|
||||||
if(benchmark) fprintf(stderr," done\n");
|
if(benchmark) fprintf(stderr," done\n");
|
||||||
if(octave) printf("setenv(\"GNUTERM\",\"X11 noraise\");y=zeros(1,%d);semilogy(y,\"ydatasource\",\"y\");\n",fft_size);
|
if(octave) printf("setenv(\"GNUTERM\",\"X11 noraise\");y=zeros(1,%d);semilogy(y,\"ydatasource\",\"y\");\n",fft_size);
|
||||||
|
float *windowt;
|
||||||
|
windowt = precalculate_window(fft_size, window);
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
FEOF_CHECK;
|
FEOF_CHECK;
|
||||||
|
@ -1270,7 +1420,8 @@ int main(int argc, char *argv[])
|
||||||
for(int i=0;i<fft_size-every_n_samples;i++) input[i]=input[i+every_n_samples];
|
for(int i=0;i<fft_size-every_n_samples;i++) input[i]=input[i+every_n_samples];
|
||||||
fread(input+fft_size-every_n_samples, sizeof(complexf), every_n_samples, stdin);
|
fread(input+fft_size-every_n_samples, sizeof(complexf), every_n_samples, stdin);
|
||||||
}
|
}
|
||||||
apply_window_c(input,windowed,fft_size,window);
|
//apply_window_c(input,windowed,fft_size,window);
|
||||||
|
apply_precalculated_window_c(input,windowed,fft_size,windowt);
|
||||||
fft_execute(plan);
|
fft_execute(plan);
|
||||||
if(octave)
|
if(octave)
|
||||||
{
|
{
|
||||||
|
@ -1306,6 +1457,40 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!strcmp(argv[1],"logaveragepower_cf"))
|
||||||
|
{
|
||||||
|
bigbufs=1;
|
||||||
|
if(argc<=4) return badsyntax("need required parameters (add_db, table_size, avgnumber)");
|
||||||
|
float add_db=0;
|
||||||
|
int avgnumber=0;
|
||||||
|
int fft_size=0;
|
||||||
|
|
||||||
|
sscanf(argv[2],"%g",&add_db);
|
||||||
|
sscanf(argv[3],"%d",&fft_size);
|
||||||
|
sscanf(argv[4],"%d",&avgnumber);
|
||||||
|
|
||||||
|
float *input = malloc(sizeof(float)*2 * fft_size);
|
||||||
|
float *output = malloc(sizeof(float) * fft_size);
|
||||||
|
|
||||||
|
add_db -= 10.0*log10(avgnumber);
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
int i,n;
|
||||||
|
for(i = 0; i < fft_size; i++) {
|
||||||
|
output[i] = 0;
|
||||||
|
}
|
||||||
|
FEOF_CHECK;
|
||||||
|
for(n = 0; n < avgnumber; n++) {
|
||||||
|
fread (input, sizeof(float)*2, fft_size, stdin);
|
||||||
|
accumulate_power_cf((complexf*)input, output, fft_size);
|
||||||
|
}
|
||||||
|
log_ff(output, output, fft_size, add_db);
|
||||||
|
fwrite (output, sizeof(float), fft_size, stdout);
|
||||||
|
TRY_YIELD;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if(!strcmp(argv[1],"fft_exchange_sides_ff"))
|
if(!strcmp(argv[1],"fft_exchange_sides_ff"))
|
||||||
{
|
{
|
||||||
if(argc<=2) return badsyntax("need required parameters (fft_size)");
|
if(argc<=2) return badsyntax("need required parameters (fft_size)");
|
||||||
|
@ -1361,8 +1546,6 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define TIME_TAKEN(start,end) ((end.tv_sec-start.tv_sec)+(end.tv_nsec-start.tv_nsec)/1e9)
|
|
||||||
|
|
||||||
if(!strcmp(argv[1],"fft_benchmark"))
|
if(!strcmp(argv[1],"fft_benchmark"))
|
||||||
{
|
{
|
||||||
if(argc<=3) return badsyntax("need required parameters (fft_size, fft_cycles)");
|
if(argc<=3) return badsyntax("need required parameters (fft_size, fft_cycles)");
|
||||||
|
@ -1408,8 +1591,6 @@ int main(int argc, char *argv[])
|
||||||
float high_cut;
|
float high_cut;
|
||||||
float transition_bw;
|
float transition_bw;
|
||||||
window_t window = WINDOW_DEFAULT;
|
window_t window = WINDOW_DEFAULT;
|
||||||
char window_string[256]; //TODO: nice buffer overflow opportunity
|
|
||||||
|
|
||||||
int fd;
|
int fd;
|
||||||
if(fd=init_fifo(argc,argv))
|
if(fd=init_fifo(argc,argv))
|
||||||
{
|
{
|
||||||
|
@ -1839,6 +2020,179 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
______ _ _____ _____ _____
|
||||||
|
| ____| | | | __ \| __ \ / ____|
|
||||||
|
| |__ __ _ ___| |_| | | | | | | |
|
||||||
|
| __/ _` / __| __| | | | | | | |
|
||||||
|
| | | (_| \__ \ |_| |__| | |__| | |____
|
||||||
|
|_| \__,_|___/\__|_____/|_____/ \_____|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
if( !strcmp(argv[1],"fastddc_fwd_cc") ) //<decimation> [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<ddc.fft_size;i++) iof(input,i)=qof(input,i)=0; //null the input buffer
|
||||||
|
|
||||||
|
int benchmark = 1;
|
||||||
|
if(benchmark) fprintf(stderr,"fastddc_fwd_cc: benchmarking FFT...");
|
||||||
|
FFT_PLAN_T* plan=make_fft_c2c(ddc.fft_size, windowed, output, 1, benchmark);
|
||||||
|
if(benchmark) fprintf(stderr," done\n");
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
FEOF_CHECK;
|
||||||
|
//overlapped FFT
|
||||||
|
for(int i=0;i<ddc.overlap_length;i++) input[i]=input[i+ddc.input_size];
|
||||||
|
fread(input+ddc.overlap_length, sizeof(complexf), ddc.input_size, stdin);
|
||||||
|
//apply_window_c(input,windowed,ddc.fft_size,window);
|
||||||
|
memcpy(windowed, input, ddc.fft_size*sizeof(complexf)); //we can switch off windows; TODO: it is likely that we shouldn't apply a window to both the FFT and the filter.
|
||||||
|
fft_execute(plan);
|
||||||
|
fwrite(output, sizeof(complexf), ddc.fft_size, stdout);
|
||||||
|
TRY_YIELD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !strcmp(argv[1],"fastddc_inv_cc") ) //<shift_rate> <decimation> [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<fft_size;i++) printf("(%g)+(%g)*i ",iof(fft_input,i),qof(fft_input,i));
|
||||||
|
for(int i=0;i<fft_size/2;i++) printf("(%g)+(%g)*i ",iof(fft_input,i),qof(fft_input,i));
|
||||||
|
printf(
|
||||||
|
"];\n"
|
||||||
|
"y=abs(fftdata);\n"
|
||||||
|
"refreshdata;\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
_____ _ _ _ _ _
|
||||||
|
| __ \(_) (_) | | | | |
|
||||||
|
| | | |_ __ _ _| |_ __ _| | _ __ ___ ___ __| | ___ ___
|
||||||
|
| | | | |/ _` | | __/ _` | | | '_ ` _ \ / _ \ / _` |/ _ \/ __|
|
||||||
|
| |__| | | (_| | | || (_| | | | | | | | | (_) | (_| | __/\__ \
|
||||||
|
|_____/|_|\__, |_|\__\__,_|_| |_| |_| |_|\___/ \__,_|\___||___/
|
||||||
|
__/ |
|
||||||
|
|___/
|
||||||
|
*/
|
||||||
|
|
||||||
if(!strcmp(argv[1],"bpsk31_varicode2ascii_sy_u8"))
|
if(!strcmp(argv[1],"bpsk31_varicode2ascii_sy_u8"))
|
||||||
{
|
{
|
||||||
unsigned long long status_shr = 0;
|
unsigned long long status_shr = 0;
|
||||||
|
@ -2002,6 +2356,5 @@ int main(int argc, char *argv[])
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "csdr: function name \"%s\" given in argument 1 does not exist. Possible causes:\n- You mistyped the commandline.\n- You need to update csdr to a newer version (if available).", argv[1]);
|
fprintf(stderr,"csdr: function name given in argument 1 (%s) does not exist. Possible causes:\n- You mistyped the commandline.\n- You need to update csdr to a newer version (if available).", argv[1]); return -1;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
335
ddcd.cpp
Normal file
335
ddcd.cpp
Normal file
|
@ -0,0 +1,335 @@
|
||||||
|
/*
|
||||||
|
This software is part of libcsdr, a set of simple DSP routines for
|
||||||
|
Software Defined Radio.
|
||||||
|
|
||||||
|
Copyright (c) 2014, Andras Retzler <randras@sdr.hu>
|
||||||
|
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<client_t*> 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; i<clients.size(); i++)
|
||||||
|
{
|
||||||
|
if(write(clients[i]->pipefd[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;i<clients.size();i++)
|
||||||
|
{
|
||||||
|
if(clients[i]->status == 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;
|
||||||
|
}
|
57
ddcd.h
Normal file
57
ddcd.h
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#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);
|
560
ddcd_old.cpp
Normal file
560
ddcd_old.cpp
Normal file
|
@ -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 <randras@sdr.hu>
|
||||||
|
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<client_t*> 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; i<clients.size(); i++)
|
||||||
|
{
|
||||||
|
if(write(clients[i]->pipefd[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;i<buffer_index+bytes_read;i++)
|
||||||
|
{
|
||||||
|
if(buffer[i]=='\n')
|
||||||
|
{
|
||||||
|
prev_newline_at=last_newline_at;
|
||||||
|
last_newline_at=i+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(last_newline_at)
|
||||||
|
{
|
||||||
|
int oi=0;
|
||||||
|
for(int i=prev_newline_at;buffer[i]!='\n'&&oi<max_size;i++) output[oi++]=buffer[i]; //copy to output buffer
|
||||||
|
output[oi++]='\0';
|
||||||
|
memmove(buffer,buffer+last_newline_at,buffer_index+bytes_read-last_newline_at);
|
||||||
|
buffer_index=bytes_read-last_newline_at;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer_index+=bytes_read;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ctl_get_arg(char* input, const char* cmd, const char* format, ...)
|
||||||
|
{
|
||||||
|
int retval=0;
|
||||||
|
int cmdlen=strlen(cmd);
|
||||||
|
if(input[cmdlen]=='=')
|
||||||
|
{
|
||||||
|
//fprintf(stderr, "cga found=\n");
|
||||||
|
if(input[cmdlen]=0, !strcmp(input,cmd))
|
||||||
|
{
|
||||||
|
//fprintf(stderr, "cga foundokay\n");
|
||||||
|
va_list vl;
|
||||||
|
va_start(vl,format);
|
||||||
|
retval=vsscanf(input+cmdlen+1,format,vl);
|
||||||
|
va_end(vl);
|
||||||
|
}
|
||||||
|
input[cmdlen]='=';
|
||||||
|
}
|
||||||
|
//fprintf(stderr, "cga retval %d\n", retval);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void client()
|
||||||
|
{
|
||||||
|
in_client=1;
|
||||||
|
print_client(this_client, "client process forked.");
|
||||||
|
|
||||||
|
char client_subprocess_cmd_buf[500];
|
||||||
|
int input_fd = this_client->pipefd[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;
|
||||||
|
}
|
62
ddcd_old.h
Normal file
62
ddcd_old.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
|
||||||
|
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";
|
166
fastddc.c
Normal file
166
fastddc.c
Normal file
|
@ -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 <randras@sdr.hu>
|
||||||
|
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_size<ddc->pre_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;i<middle;i++)
|
||||||
|
{
|
||||||
|
iof(&temp,0)=iof(io,i);
|
||||||
|
qof(&temp,0)=qof(io,i);
|
||||||
|
iof(io,i)=iof(io,i+middle);
|
||||||
|
qof(io,i)=qof(io,i+middle);
|
||||||
|
iof(io,i+middle)=iof(&temp,0);
|
||||||
|
qof(io,i+middle)=qof(&temp,0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
//implements DDC by using the overlap & scrap method
|
||||||
|
//TODO: +/-1s on overlap_size et al
|
||||||
|
//input shoud have ddc->fft_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;i<plan_inverse->size;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;i<ddc->fft_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;i<plan_inverse->size;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;i<plan_inverse->size;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;
|
||||||
|
}
|
29
fastddc.h
Normal file
29
fastddc.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#include <math.h>
|
||||||
|
#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);
|
File diff suppressed because it is too large
Load diff
996
grc_tests/test_fastddc.grc
Normal file
996
grc_tests/test_fastddc.grc
Normal file
|
@ -0,0 +1,996 @@
|
||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<?grc format='1' created='3.7.8'?>
|
||||||
|
<flow_graph>
|
||||||
|
<timestamp>Sat Nov 15 20:06:19 2014</timestamp>
|
||||||
|
<block>
|
||||||
|
<key>options</key>
|
||||||
|
<param>
|
||||||
|
<key>author</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>window_size</key>
|
||||||
|
<value>1280, 1024</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>category</key>
|
||||||
|
<value>Custom</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>description</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(10, 10)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>generate_options</key>
|
||||||
|
<value>wx_gui</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>top_block</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>max_nouts</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>realtime_scheduling</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>run_options</key>
|
||||||
|
<value>prompt</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>run</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>thread_safe_setters</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>title</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>variable</key>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(128, 179)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>decimation</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>value</key>
|
||||||
|
<value>4</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>variable_slider</key>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>converver</key>
|
||||||
|
<value>float_converter</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>value</key>
|
||||||
|
<value>50</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(16, 267)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>grid_pos</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>freq</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>label</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>max</key>
|
||||||
|
<value>samp_rate/2</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>min</key>
|
||||||
|
<value>-samp_rate/2</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>notebook</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>num_steps</key>
|
||||||
|
<value>100</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>style</key>
|
||||||
|
<value>wx.SL_HORIZONTAL</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>variable</key>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(9, 170)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>samp_rate</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>value</key>
|
||||||
|
<value>400000</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>analog_noise_source_x</key>
|
||||||
|
<param>
|
||||||
|
<key>amp</key>
|
||||||
|
<value>1</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>affinity</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(224, 403)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>analog_noise_source_x_0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>maxoutbuf</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>minoutbuf</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>noise_type</key>
|
||||||
|
<value>analog.GR_GAUSSIAN</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>type</key>
|
||||||
|
<value>complex</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>seed</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>analog_pll_freqdet_cf</key>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>affinity</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(112, 675)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>analog_pll_freqdet_cf_0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>w</key>
|
||||||
|
<value>(3.141592654/200)/2</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>max_freq</key>
|
||||||
|
<value>3.141592654</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>maxoutbuf</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>min_freq</key>
|
||||||
|
<value>-3.141592654</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>minoutbuf</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>analog_sig_source_x</key>
|
||||||
|
<param>
|
||||||
|
<key>amp</key>
|
||||||
|
<value>0.2</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>affinity</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>1</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>freq</key>
|
||||||
|
<value>freq</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(224, 29)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>analog_sig_source_x_0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>maxoutbuf</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>minoutbuf</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>offset</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>type</key>
|
||||||
|
<value>complex</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>samp_rate</key>
|
||||||
|
<value>samp_rate</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>waveform</key>
|
||||||
|
<value>analog.GR_COS_WAVE</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>blocks_multiply_const_vxx</key>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>const</key>
|
||||||
|
<value>(samp_rate/decimation)*(1/(2*3.141592654))</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>affinity</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(368, 691)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>blocks_multiply_const_vxx_0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>type</key>
|
||||||
|
<value>float</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>maxoutbuf</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>minoutbuf</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>vlen</key>
|
||||||
|
<value>1</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>blocks_throttle</key>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>affinity</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(424, 235)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>blocks_throttle_0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>ignoretag</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>maxoutbuf</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>minoutbuf</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>samples_per_second</key>
|
||||||
|
<value>samp_rate</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>type</key>
|
||||||
|
<value>complex</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>vlen</key>
|
||||||
|
<value>1</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>ha5kfu_execproc_xx</key>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>commandline</key>
|
||||||
|
<value>csdr fastddc_fwd_cc %d | csdr fastddc_inv_cc 0.4 %d"%(decimation,decimation)+"</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>affinity</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(616, 235)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>ha5kfu_execproc_xx_1</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>maxoutbuf</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>minoutbuf</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>type</key>
|
||||||
|
<value>cc</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>notebook</key>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(160, 283)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>grid_pos</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>nb</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>labels</key>
|
||||||
|
<value>['FFT', 'Scope']</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>notebook</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>style</key>
|
||||||
|
<value>wx.NB_TOP</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>wxgui_fftsink2</key>
|
||||||
|
<param>
|
||||||
|
<key>avg_alpha</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>average</key>
|
||||||
|
<value>False</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>baseband_freq</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>affinity</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>fft_size</key>
|
||||||
|
<value>1024</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>freqvar</key>
|
||||||
|
<value>None</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(952, 155)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>grid_pos</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>wxgui_fftsink2_0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>notebook</key>
|
||||||
|
<value>nb, 0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>peak_hold</key>
|
||||||
|
<value>False</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>ref_level</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>ref_scale</key>
|
||||||
|
<value>2.0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>fft_rate</key>
|
||||||
|
<value>15</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>samp_rate</key>
|
||||||
|
<value>samp_rate/decimation</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>title</key>
|
||||||
|
<value>FFT plot of csdr processed signal</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>type</key>
|
||||||
|
<value>complex</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>win_size</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>win</key>
|
||||||
|
<value>None</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>y_divs</key>
|
||||||
|
<value>10</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>y_per_div</key>
|
||||||
|
<value>10</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>wxgui_fftsink2</key>
|
||||||
|
<param>
|
||||||
|
<key>avg_alpha</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>average</key>
|
||||||
|
<value>False</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>baseband_freq</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>affinity</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>1</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>fft_size</key>
|
||||||
|
<value>1024</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>freqvar</key>
|
||||||
|
<value>None</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(616, 291)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>grid_pos</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>wxgui_fftsink2_0_0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>notebook</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>peak_hold</key>
|
||||||
|
<value>False</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>ref_level</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>ref_scale</key>
|
||||||
|
<value>2.0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>fft_rate</key>
|
||||||
|
<value>15</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>samp_rate</key>
|
||||||
|
<value>samp_rate</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>title</key>
|
||||||
|
<value>FFT plot of original signal</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>type</key>
|
||||||
|
<value>complex</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>win_size</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>win</key>
|
||||||
|
<value>None</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>y_divs</key>
|
||||||
|
<value>10</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>y_per_div</key>
|
||||||
|
<value>10</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>wxgui_numbersink2</key>
|
||||||
|
<param>
|
||||||
|
<key>avg_alpha</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>average</key>
|
||||||
|
<value>False</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>affinity</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>decimal_places</key>
|
||||||
|
<value>10</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>factor</key>
|
||||||
|
<value>1.0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(576, 619)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>grid_pos</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>wxgui_numbersink2_0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>max_value</key>
|
||||||
|
<value>(samp_rate/decimation)/2</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>min_value</key>
|
||||||
|
<value>(-samp_rate/decimation)/2</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>notebook</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>number_rate</key>
|
||||||
|
<value>15</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>peak_hold</key>
|
||||||
|
<value>False</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>ref_level</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>samp_rate</key>
|
||||||
|
<value>samp_rate</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>show_gauge</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>title</key>
|
||||||
|
<value>PLL locked at</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>type</key>
|
||||||
|
<value>float</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>units</key>
|
||||||
|
<value>Hz</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>win_size</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>wxgui_scopesink2</key>
|
||||||
|
<param>
|
||||||
|
<key>ac_couple</key>
|
||||||
|
<value>False</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>affinity</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(952, 35)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>grid_pos</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>wxgui_scopesink2_0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>notebook</key>
|
||||||
|
<value>nb, 1</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>num_inputs</key>
|
||||||
|
<value>1</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>samp_rate</key>
|
||||||
|
<value>samp_rate/decimation</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>t_scale</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>title</key>
|
||||||
|
<value>Scope plot of csdr processed signal</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>trig_mode</key>
|
||||||
|
<value>wxgui.TRIG_MODE_AUTO</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>type</key>
|
||||||
|
<value>complex</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>v_offset</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>v_scale</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>win_size</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>xy_mode</key>
|
||||||
|
<value>False</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>y_axis_label</key>
|
||||||
|
<value>Counts</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<connection>
|
||||||
|
<source_block_id>analog_noise_source_x_0</source_block_id>
|
||||||
|
<sink_block_id>blocks_throttle_0</sink_block_id>
|
||||||
|
<source_key>0</source_key>
|
||||||
|
<sink_key>0</sink_key>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<source_block_id>analog_pll_freqdet_cf_0</source_block_id>
|
||||||
|
<sink_block_id>blocks_multiply_const_vxx_0</sink_block_id>
|
||||||
|
<source_key>0</source_key>
|
||||||
|
<sink_key>0</sink_key>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<source_block_id>analog_sig_source_x_0</source_block_id>
|
||||||
|
<sink_block_id>blocks_throttle_0</sink_block_id>
|
||||||
|
<source_key>0</source_key>
|
||||||
|
<sink_key>0</sink_key>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<source_block_id>blocks_multiply_const_vxx_0</source_block_id>
|
||||||
|
<sink_block_id>wxgui_numbersink2_0</sink_block_id>
|
||||||
|
<source_key>0</source_key>
|
||||||
|
<sink_key>0</sink_key>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<source_block_id>blocks_throttle_0</source_block_id>
|
||||||
|
<sink_block_id>ha5kfu_execproc_xx_1</sink_block_id>
|
||||||
|
<source_key>0</source_key>
|
||||||
|
<sink_key>0</sink_key>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<source_block_id>blocks_throttle_0</source_block_id>
|
||||||
|
<sink_block_id>wxgui_fftsink2_0_0</sink_block_id>
|
||||||
|
<source_key>0</source_key>
|
||||||
|
<sink_key>0</sink_key>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<source_block_id>ha5kfu_execproc_xx_1</source_block_id>
|
||||||
|
<sink_block_id>analog_pll_freqdet_cf_0</sink_block_id>
|
||||||
|
<source_key>0</source_key>
|
||||||
|
<sink_key>0</sink_key>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<source_block_id>ha5kfu_execproc_xx_1</source_block_id>
|
||||||
|
<sink_block_id>wxgui_fftsink2_0</sink_block_id>
|
||||||
|
<source_key>0</source_key>
|
||||||
|
<sink_key>0</sink_key>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<source_block_id>ha5kfu_execproc_xx_1</source_block_id>
|
||||||
|
<sink_block_id>wxgui_scopesink2_0</sink_block_id>
|
||||||
|
<source_key>0</source_key>
|
||||||
|
<sink_key>0</sink_key>
|
||||||
|
</connection>
|
||||||
|
</flow_graph>
|
File diff suppressed because it is too large
Load diff
971
grc_tests/test_shift_remote.grc
Normal file
971
grc_tests/test_shift_remote.grc
Normal file
|
@ -0,0 +1,971 @@
|
||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<?grc format='1' created='3.7.8'?>
|
||||||
|
<flow_graph>
|
||||||
|
<timestamp>Thu Jan 15 18:51:48 2015</timestamp>
|
||||||
|
<block>
|
||||||
|
<key>options</key>
|
||||||
|
<param>
|
||||||
|
<key>author</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>window_size</key>
|
||||||
|
<value>1280, 1024</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>category</key>
|
||||||
|
<value>Custom</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>description</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(10, 10)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>generate_options</key>
|
||||||
|
<value>wx_gui</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>top_block</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>max_nouts</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>realtime_scheduling</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>run_options</key>
|
||||||
|
<value>prompt</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>run</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>thread_safe_setters</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>title</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>variable_slider</key>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>converver</key>
|
||||||
|
<value>float_converter</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>value</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(24, 331)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>grid_pos</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>gen_freq</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>label</key>
|
||||||
|
<value>Frequency:</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>max</key>
|
||||||
|
<value>samp_rate/2</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>min</key>
|
||||||
|
<value>-samp_rate/2</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>notebook</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>num_steps</key>
|
||||||
|
<value>100</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>style</key>
|
||||||
|
<value>wx.SL_HORIZONTAL</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>variable</key>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(8, 195)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>rate</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>value</key>
|
||||||
|
<value>-0.1</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>variable</key>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(176, 11)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>samp_rate</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>value</key>
|
||||||
|
<value>250e3</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>analog_sig_source_x</key>
|
||||||
|
<param>
|
||||||
|
<key>amp</key>
|
||||||
|
<value>1</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>affinity</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>freq</key>
|
||||||
|
<value>gen_freq</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(8, 75)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>analog_sig_source_x_0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>maxoutbuf</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>minoutbuf</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>offset</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>type</key>
|
||||||
|
<value>complex</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>samp_rate</key>
|
||||||
|
<value>samp_rate</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>waveform</key>
|
||||||
|
<value>analog.GR_SIN_WAVE</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>blocks_throttle</key>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>affinity</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(224, 107)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>blocks_throttle_0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>ignoretag</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>maxoutbuf</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>minoutbuf</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>samples_per_second</key>
|
||||||
|
<value>samp_rate</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>type</key>
|
||||||
|
<value>complex</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>vlen</key>
|
||||||
|
<value>1</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>ha5kfu_execproc_xx</key>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>commandline</key>
|
||||||
|
<value>"csdr shift_addition_cc %g"%rate</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>affinity</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(824, 315)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>ha5kfu_execproc_xx_0_0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>maxoutbuf</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>minoutbuf</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>type</key>
|
||||||
|
<value>cc</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>ha5kfu_execproc_xx</key>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>commandline</key>
|
||||||
|
<value>ncat -v raspberrypi.local 5321</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>affinity</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(536, 443)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>ha5kfu_execproc_xx_0_0_0_1</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>maxoutbuf</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>minoutbuf</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>type</key>
|
||||||
|
<value>cc</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>notebook</key>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(272, 11)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>grid_pos</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>nb0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>labels</key>
|
||||||
|
<value>['original', 'shift_addition_cc','shift_addfast_cc',]</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>notebook</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>style</key>
|
||||||
|
<value>wx.NB_TOP</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>wxgui_fftsink2</key>
|
||||||
|
<param>
|
||||||
|
<key>avg_alpha</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>average</key>
|
||||||
|
<value>False</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>baseband_freq</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>affinity</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>fft_size</key>
|
||||||
|
<value>1024</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>freqvar</key>
|
||||||
|
<value>None</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(848, 27)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>grid_pos</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>wxgui_fftsink2_0_0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>notebook</key>
|
||||||
|
<value>nb0,0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>peak_hold</key>
|
||||||
|
<value>False</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>ref_level</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>ref_scale</key>
|
||||||
|
<value>2.0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>fft_rate</key>
|
||||||
|
<value>15</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>samp_rate</key>
|
||||||
|
<value>samp_rate</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>title</key>
|
||||||
|
<value>FFT Plot</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>type</key>
|
||||||
|
<value>complex</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>win_size</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>win</key>
|
||||||
|
<value>None</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>y_divs</key>
|
||||||
|
<value>10</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>y_per_div</key>
|
||||||
|
<value>10</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>wxgui_fftsink2</key>
|
||||||
|
<param>
|
||||||
|
<key>avg_alpha</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>average</key>
|
||||||
|
<value>False</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>baseband_freq</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>affinity</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>fft_size</key>
|
||||||
|
<value>1024</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>freqvar</key>
|
||||||
|
<value>None</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(1112, 339)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>grid_pos</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>wxgui_fftsink2_0_1</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>notebook</key>
|
||||||
|
<value>nb0,1</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>peak_hold</key>
|
||||||
|
<value>False</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>ref_level</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>ref_scale</key>
|
||||||
|
<value>2.0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>fft_rate</key>
|
||||||
|
<value>15</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>samp_rate</key>
|
||||||
|
<value>samp_rate</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>title</key>
|
||||||
|
<value>FFT Plot</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>type</key>
|
||||||
|
<value>complex</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>win_size</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>win</key>
|
||||||
|
<value>None</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>y_divs</key>
|
||||||
|
<value>10</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>y_per_div</key>
|
||||||
|
<value>10</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>wxgui_fftsink2</key>
|
||||||
|
<param>
|
||||||
|
<key>avg_alpha</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>average</key>
|
||||||
|
<value>False</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>baseband_freq</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>affinity</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>fft_size</key>
|
||||||
|
<value>1024</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>freqvar</key>
|
||||||
|
<value>None</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(808, 387)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>grid_pos</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>wxgui_fftsink2_0_1_1</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>notebook</key>
|
||||||
|
<value>nb0,2</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>peak_hold</key>
|
||||||
|
<value>False</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>ref_level</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>ref_scale</key>
|
||||||
|
<value>2.0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>fft_rate</key>
|
||||||
|
<value>15</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>samp_rate</key>
|
||||||
|
<value>samp_rate</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>title</key>
|
||||||
|
<value>FFT Plot</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>type</key>
|
||||||
|
<value>complex</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>win_size</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>win</key>
|
||||||
|
<value>None</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>y_divs</key>
|
||||||
|
<value>10</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>y_per_div</key>
|
||||||
|
<value>10</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>wxgui_scopesink2</key>
|
||||||
|
<param>
|
||||||
|
<key>ac_couple</key>
|
||||||
|
<value>False</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>affinity</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(1112, 555)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>grid_pos</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>wxgui_scopesink2_0_0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>notebook</key>
|
||||||
|
<value>nb0,1</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>num_inputs</key>
|
||||||
|
<value>1</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>samp_rate</key>
|
||||||
|
<value>samp_rate</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>t_scale</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>title</key>
|
||||||
|
<value>Scope Plot</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>trig_mode</key>
|
||||||
|
<value>wxgui.TRIG_MODE_NORM</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>type</key>
|
||||||
|
<value>complex</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>v_offset</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>v_scale</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>win_size</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>xy_mode</key>
|
||||||
|
<value>False</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>y_axis_label</key>
|
||||||
|
<value>Counts</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<block>
|
||||||
|
<key>wxgui_scopesink2</key>
|
||||||
|
<param>
|
||||||
|
<key>ac_couple</key>
|
||||||
|
<value>False</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>alias</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>comment</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>affinity</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_enabled</key>
|
||||||
|
<value>True</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_coordinate</key>
|
||||||
|
<value>(808, 611)</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>_rotation</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>grid_pos</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>id</key>
|
||||||
|
<value>wxgui_scopesink2_0_0_1</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>notebook</key>
|
||||||
|
<value>nb0,2</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>num_inputs</key>
|
||||||
|
<value>1</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>samp_rate</key>
|
||||||
|
<value>samp_rate</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>t_scale</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>title</key>
|
||||||
|
<value>Scope Plot</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>trig_mode</key>
|
||||||
|
<value>wxgui.TRIG_MODE_NORM</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>type</key>
|
||||||
|
<value>complex</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>v_offset</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>v_scale</key>
|
||||||
|
<value>0</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>win_size</key>
|
||||||
|
<value></value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>xy_mode</key>
|
||||||
|
<value>False</value>
|
||||||
|
</param>
|
||||||
|
<param>
|
||||||
|
<key>y_axis_label</key>
|
||||||
|
<value>Counts</value>
|
||||||
|
</param>
|
||||||
|
</block>
|
||||||
|
<connection>
|
||||||
|
<source_block_id>analog_sig_source_x_0</source_block_id>
|
||||||
|
<sink_block_id>blocks_throttle_0</sink_block_id>
|
||||||
|
<source_key>0</source_key>
|
||||||
|
<sink_key>0</sink_key>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<source_block_id>blocks_throttle_0</source_block_id>
|
||||||
|
<sink_block_id>ha5kfu_execproc_xx_0_0</sink_block_id>
|
||||||
|
<source_key>0</source_key>
|
||||||
|
<sink_key>0</sink_key>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<source_block_id>blocks_throttle_0</source_block_id>
|
||||||
|
<sink_block_id>ha5kfu_execproc_xx_0_0_0_1</sink_block_id>
|
||||||
|
<source_key>0</source_key>
|
||||||
|
<sink_key>0</sink_key>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<source_block_id>blocks_throttle_0</source_block_id>
|
||||||
|
<sink_block_id>wxgui_fftsink2_0_0</sink_block_id>
|
||||||
|
<source_key>0</source_key>
|
||||||
|
<sink_key>0</sink_key>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<source_block_id>ha5kfu_execproc_xx_0_0</source_block_id>
|
||||||
|
<sink_block_id>wxgui_fftsink2_0_1</sink_block_id>
|
||||||
|
<source_key>0</source_key>
|
||||||
|
<sink_key>0</sink_key>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<source_block_id>ha5kfu_execproc_xx_0_0</source_block_id>
|
||||||
|
<sink_block_id>wxgui_scopesink2_0_0</sink_block_id>
|
||||||
|
<source_key>0</source_key>
|
||||||
|
<sink_key>0</sink_key>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<source_block_id>ha5kfu_execproc_xx_0_0_0_1</source_block_id>
|
||||||
|
<sink_block_id>wxgui_fftsink2_0_1_1</sink_block_id>
|
||||||
|
<source_key>0</source_key>
|
||||||
|
<sink_key>0</sink_key>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<source_block_id>ha5kfu_execproc_xx_0_0_0_1</source_block_id>
|
||||||
|
<sink_block_id>wxgui_scopesink2_0_0_1</sink_block_id>
|
||||||
|
<source_key>0</source_key>
|
||||||
|
<sink_key>0</sink_key>
|
||||||
|
</connection>
|
||||||
|
</flow_graph>
|
9
grc_tests/test_shift_remote.sh
Executable file
9
grc_tests/test_shift_remote.sh
Executable file
|
@ -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
|
277
libcsdr.c
277
libcsdr.c
|
@ -263,8 +263,208 @@ float shift_table_cc(complexf* input, complexf* output, int input_size, float ra
|
||||||
return phase;
|
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;i<size;i++)
|
||||||
|
{
|
||||||
|
myphase += output.phase_increment;
|
||||||
|
while(myphase>PI) 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;i<input_size; i++) //@shift_unroll_cc
|
||||||
|
{
|
||||||
|
cos_val = cos_start * d->dcos[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
|
#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;i<input_size/4; i++) //@shift_addfast_cc
|
||||||
|
{
|
||||||
|
SADF_L1(0)
|
||||||
|
SADF_L1(1)
|
||||||
|
SADF_L1(2)
|
||||||
|
SADF_L1(3)
|
||||||
|
SADF_L2(0)
|
||||||
|
SADF_L2(1)
|
||||||
|
SADF_L2(2)
|
||||||
|
SADF_L2(3)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
#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;i<input_size/4; i++) //@shift_addfast_cc
|
||||||
|
{
|
||||||
|
for(int j=0;j<4;j++) //@shift_addfast_cc
|
||||||
|
{
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
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
|
//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"
|
" 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 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
|
" 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
|
" 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 {q4}, [%[quad_acci]]\n\t" //if the loop is finished, store the two accumulators in memory
|
||||||
" vst1.32 {q5}, [%[quad_accq]]\n\t"
|
" 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<size;i++) //@precalculate_window
|
||||||
|
{
|
||||||
|
float rate=(float)i/(size-1);
|
||||||
|
windowt[i] = window_function(2.0*rate+1.0);
|
||||||
|
}
|
||||||
|
return windowt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply_precalculated_window_c(complexf* input, complexf* output, int size, float *windowt)
|
||||||
|
{
|
||||||
|
for(int i=0;i<size;i++) //@apply_precalculated_window_c
|
||||||
|
{
|
||||||
|
iof(output,i)=iof(input,i)*windowt[i];
|
||||||
|
qof(output,i)=qof(input,i)*windowt[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void apply_window_f(float* input, float* output, int size, window_t window)
|
void apply_window_f(float* input, float* output, int size, window_t window)
|
||||||
{
|
{
|
||||||
float (*window_function)(float)=firdes_get_window_kernel(window);
|
float (*window_function)(float)=firdes_get_window_kernel(window);
|
||||||
|
@ -949,6 +1172,19 @@ void logpower_cf(complexf* input, float* output, int size, float add_db)
|
||||||
for(int i=0;i<size;i++) output[i]=10*output[i]+add_db; //@logpower_cf: pass 3
|
for(int i=0;i<size;i++) output[i]=10*output[i]+add_db; //@logpower_cf: pass 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void accumulate_power_cf(complexf* input, float* output, int size)
|
||||||
|
{
|
||||||
|
for(int i=0;i<size;i++) output[i] += iof(input,i)*iof(input,i) + qof(input,i)*qof(input,i); //@logpower_cf: pass 1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_ff(float* input, float* output, int size, float add_db) {
|
||||||
|
for(int i=0;i<size;i++) output[i]=log10(input[i]); //@logpower_cf: pass 2
|
||||||
|
|
||||||
|
for(int i=0;i<size;i++) output[i]=10*output[i]+add_db; //@logpower_cf: pass 3
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
_____ _ _ _ _ _ _
|
_____ _ _ _ _ _ _
|
||||||
| __ \(_) (_) | | | | | | |
|
| __ \(_) (_) | | | | | | |
|
||||||
|
@ -1447,6 +1683,43 @@ void convert_f_s16(float* input, short* output, int input_size)
|
||||||
void convert_i16_f(short* input, float* output, int input_size) { convert_s16_f(input, output, input_size); }
|
void convert_i16_f(short* input, float* output, int input_size) { convert_s16_f(input, output, input_size); }
|
||||||
void convert_f_i16(float* input, short* output, int input_size) { convert_f_s16(input, output, input_size); }
|
void convert_f_i16(float* input, short* output, int input_size) { convert_f_s16(input, output, input_size); }
|
||||||
|
|
||||||
|
void convert_f_s24(float* input, unsigned char* output, int input_size, int bigendian)
|
||||||
|
{
|
||||||
|
int k=0;
|
||||||
|
if(bigendian) for(int i=0;i<input_size;i++)
|
||||||
|
{
|
||||||
|
int temp=input[i]*(INT_MAX>>8);
|
||||||
|
unsigned char* ptemp=(unsigned char*)&temp;
|
||||||
|
output[k++]=*ptemp;
|
||||||
|
output[k++]=*(ptemp+1);
|
||||||
|
output[k++]=*(ptemp+2);
|
||||||
|
}
|
||||||
|
else for(int i=0;i<input_size;i++)
|
||||||
|
{
|
||||||
|
int temp=input[i]*(INT_MAX>>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<input_size*3;i+=3)
|
||||||
|
{
|
||||||
|
int temp=(input[i+2]<<24)|(input[i+1]<<16)|(input[i]<<8);
|
||||||
|
output[k++]=temp/(float)(INT_MAX-256);
|
||||||
|
}
|
||||||
|
else for(int i=0;i<input_size*3;i+=3)
|
||||||
|
{
|
||||||
|
int temp=(input[i+2]<<8)|(input[i+1]<<16)|(input[i]<<24);
|
||||||
|
output[k++]=temp/(float)(INT_MAX-256);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int trivial_vectorize()
|
int trivial_vectorize()
|
||||||
{
|
{
|
||||||
//this function is trivial to vectorize and should pass on both NEON and SSE
|
//this function is trivial to vectorize and should pass on both NEON and SSE
|
||||||
|
|
28
libcsdr.h
28
libcsdr.h
|
@ -64,6 +64,8 @@ typedef struct complexf_s { float i; float q; } complexf;
|
||||||
//they dropped M_PI in C99, so we define it:
|
//they dropped M_PI in C99, so we define it:
|
||||||
#define PI ((float)3.14159265358979323846)
|
#define PI ((float)3.14159265358979323846)
|
||||||
|
|
||||||
|
#define TIME_TAKEN(start,end) ((end.tv_sec-start.tv_sec)+(end.tv_nsec-start.tv_nsec)/1e9)
|
||||||
|
|
||||||
//window
|
//window
|
||||||
typedef enum window_s
|
typedef enum window_s
|
||||||
{
|
{
|
||||||
|
@ -136,9 +138,13 @@ typedef struct rational_resampler_ff_s
|
||||||
rational_resampler_ff_t rational_resampler_ff(float *input, float *output, int input_size, int interpolation, int decimation, float *taps, int taps_length, int last_taps_delay);
|
rational_resampler_ff_t rational_resampler_ff(float *input, float *output, int input_size, int interpolation, int decimation, float *taps, int taps_length, int last_taps_delay);
|
||||||
void rational_resampler_get_lowpass_f(float* output, int output_size, int interpolation, int decimation, window_t window);
|
void rational_resampler_get_lowpass_f(float* output, int output_size, int interpolation, int decimation, window_t window);
|
||||||
|
|
||||||
|
float *precalculate_window(int size, window_t window);
|
||||||
void apply_window_c(complexf* input, complexf* output, int size, window_t window);
|
void apply_window_c(complexf* input, complexf* output, int size, window_t window);
|
||||||
|
void apply_precalculated_window_c(complexf* input, complexf* output, int size, float *windowt);
|
||||||
void apply_window_f(float* input, float* output, int size, window_t window);
|
void apply_window_f(float* input, float* output, int size, window_t window);
|
||||||
void logpower_cf(complexf* input, float* output, int size, float add_db);
|
void logpower_cf(complexf* input, float* output, int size, float add_db);
|
||||||
|
void accumulate_power_cf(complexf* input, float* output, int size);
|
||||||
|
void log_ff(float* input, float* output, int size, float add_db);
|
||||||
|
|
||||||
typedef struct fractional_decimator_ff_s
|
typedef struct fractional_decimator_ff_s
|
||||||
{
|
{
|
||||||
|
@ -157,6 +163,25 @@ void shift_table_deinit(shift_table_data_t table_data);
|
||||||
shift_table_data_t shift_table_init(int table_size);
|
shift_table_data_t shift_table_init(int table_size);
|
||||||
float shift_table_cc(complexf* input, complexf* output, int input_size, float rate, shift_table_data_t table_data, float starting_phase);
|
float shift_table_cc(complexf* input, complexf* output, int input_size, float rate, shift_table_data_t table_data, float starting_phase);
|
||||||
|
|
||||||
|
typedef struct shift_addfast_data_s
|
||||||
|
{
|
||||||
|
float dsin[4];
|
||||||
|
float dcos[4];
|
||||||
|
float phase_increment;
|
||||||
|
} shift_addfast_data_t;
|
||||||
|
shift_addfast_data_t shift_addfast_init(float rate);
|
||||||
|
shift_addfast_data_t shift_addfast_init(float rate);
|
||||||
|
float shift_addfast_cc(complexf *input, complexf* output, int input_size, shift_addfast_data_t* d, float starting_phase);
|
||||||
|
|
||||||
|
typedef struct shift_unroll_data_s
|
||||||
|
{
|
||||||
|
float* dsin;
|
||||||
|
float* dcos;
|
||||||
|
float phase_increment;
|
||||||
|
int size;
|
||||||
|
} shift_unroll_data_t;
|
||||||
|
float shift_unroll_cc(complexf *input, complexf* output, int input_size, shift_unroll_data_t* d, float starting_phase);
|
||||||
|
shift_unroll_data_t shift_unroll_init(float rate, int size);
|
||||||
|
|
||||||
int log2n(int x);
|
int log2n(int x);
|
||||||
int next_pow2(int x);
|
int next_pow2(int x);
|
||||||
|
@ -177,6 +202,9 @@ void convert_f_s16(float* input, short* output, int input_size);
|
||||||
void convert_s16_f(short* input, float* output, int input_size);
|
void convert_s16_f(short* input, float* output, int input_size);
|
||||||
void convert_f_i16(float* input, short* output, int input_size);
|
void convert_f_i16(float* input, short* output, int input_size);
|
||||||
void convert_i16_f(short* input, float* output, int input_size);
|
void convert_i16_f(short* input, float* output, int input_size);
|
||||||
|
void convert_f_s24(float* input, unsigned char* output, int input_size, int bigendian);
|
||||||
|
void convert_s24_f(unsigned char* input, float* output, int input_size, int bigendian);
|
||||||
|
|
||||||
|
|
||||||
int is_nan(float f);
|
int is_nan(float f);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "libcsdr.c"
|
#include "libcsdr.c"
|
||||||
#include "libcsdr_gpl.c"
|
#include "libcsdr_gpl.c"
|
||||||
#include "ima_adpcm.c"
|
#include "ima_adpcm.c"
|
||||||
|
#include "fastddc.c"
|
||||||
//this wrapper helps parsevect.py to generate better output
|
//this wrapper helps parsevect.py to generate better output
|
||||||
|
|
2
make_test200
Executable file
2
make_test200
Executable file
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/bash
|
||||||
|
gcc test200.c --std=gnu99 -o test200 -DUSE_FFTW -DLIBCSDR_GPL -lcsdr
|
7
nmux-todo.md
Normal file
7
nmux-todo.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Remove nmux repo, it will rather be part of csdr
|
||||||
|
Try in OpenWebRX
|
||||||
|
Add UDP support
|
||||||
|
Evaluate performance against ncat
|
||||||
|
Remove debug messages
|
||||||
|
Document README.md
|
||||||
|
Test with a limited number of people
|
374
nmux.cpp
Normal file
374
nmux.cpp
Normal file
|
@ -0,0 +1,374 @@
|
||||||
|
/*
|
||||||
|
This software is part of libcsdr, a set of simple DSP routines for
|
||||||
|
Software Defined Radio.
|
||||||
|
|
||||||
|
Copyright (c) 2014, Andras Retzler <randras@sdr.hu>
|
||||||
|
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<client_t*> 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;i<clients.size();i++) fprintf(stderr, "0x%x ", (intptr_t)clients[i]);
|
||||||
|
fprintf(stderr, "\x1b[0m\n");
|
||||||
|
}
|
||||||
|
if(NMUX_DEBUG) fprintf(stderr, "mainfor: accepted (socket = %d).\n", new_socket);
|
||||||
|
//Close all finished clients
|
||||||
|
for(int i=0;i<clients.size();i++)
|
||||||
|
{
|
||||||
|
if(clients[i]->status == 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;i<clients.size();i++) fprintf(stderr, "0x%x ", (intptr_t)clients[i]);
|
||||||
|
fprintf(stderr, "\x1b[0m\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
//We're the parent, let's create a new client and initialize it
|
||||||
|
client_t* new_client = new client_t;
|
||||||
|
new_client->error = 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;
|
||||||
|
}
|
45
nmux.h
Normal file
45
nmux.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#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);
|
124
test200.c
Normal file
124
test200.c
Normal file
|
@ -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 <randras@sdr.hu>
|
||||||
|
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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#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;i<T_BUFSIZE;i++)
|
||||||
|
{
|
||||||
|
iof(buf_c,i)=buf_u8[2*i]/128.0;
|
||||||
|
qof(buf_c,i)=buf_u8[2*i+1]/128.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float* taps_f = (float*)malloc(sizeof(float)*T_TAPS);
|
||||||
|
firdes_lowpass_f(taps_f, T_TAPS, 1.0f/T_DECFACT, WINDOW_DEFAULT);
|
||||||
|
|
||||||
|
struct timespec start_time, end_time;
|
||||||
|
|
||||||
|
fprintf(stderr,"Starting tests of processing %d samples...\n", T_BUFSIZE*T_N);
|
||||||
|
|
||||||
|
//fir_decimate_cc
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_RAW, &start_time);
|
||||||
|
for(int i=0;i<T_N;i++) fir_decimate_cc(buf_c, outbuf_c, T_BUFSIZE, 10, taps_f, T_TAPS);
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_RAW, &end_time);
|
||||||
|
fprintf(stderr,"fir_decimate_cc done in %g seconds.\n",TIME_TAKEN(start_time,end_time));
|
||||||
|
|
||||||
|
|
||||||
|
//shift_math_cc
|
||||||
|
float starting_phase = 0;
|
||||||
|
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_RAW, &start_time);
|
||||||
|
for(int i=0;i<T_N;i++) starting_phase = shift_math_cc(buf_c, outbuf_c, T_BUFSIZE, 0.1, starting_phase);
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_RAW, &end_time);
|
||||||
|
fprintf(stderr,"shift_math_cc done in %g seconds.\n",TIME_TAKEN(start_time,end_time));
|
||||||
|
|
||||||
|
//shift_table_cc
|
||||||
|
shift_table_data_t shift_table_data=shift_table_init(65536);
|
||||||
|
starting_phase = 0;
|
||||||
|
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_RAW, &start_time);
|
||||||
|
for(int i=0;i<T_N;i++) starting_phase = starting_phase=shift_table_cc(buf_c, outbuf_c, T_BUFSIZE, 0.1, shift_table_data, starting_phase);;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_RAW, &end_time);
|
||||||
|
fprintf(stderr,"shift_table_cc (table size = %d) done in %g seconds.\n",65536,TIME_TAKEN(start_time,end_time));
|
||||||
|
|
||||||
|
|
||||||
|
//shift_addition_cc
|
||||||
|
shift_addition_data_t data_addition = shift_addition_init(0.1);
|
||||||
|
starting_phase = 0;
|
||||||
|
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_RAW, &start_time);
|
||||||
|
for(int i=0;i<T_N;i++) starting_phase = shift_addition_cc(buf_c, outbuf_c, T_BUFSIZE, data_addition, starting_phase);
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_RAW, &end_time);
|
||||||
|
fprintf(stderr,"shift_addition_cc done in %g seconds.\n",TIME_TAKEN(start_time,end_time));
|
||||||
|
|
||||||
|
//shift_addfast_cc
|
||||||
|
shift_addfast_data_t data_addfast = shift_addfast_init(0.1);
|
||||||
|
starting_phase = 0;
|
||||||
|
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_RAW, &start_time);
|
||||||
|
for(int i=0;i<T_N;i++) starting_phase = shift_addfast_cc(buf_c, outbuf_c, T_BUFSIZE, &data_addfast, starting_phase);
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_RAW, &end_time);
|
||||||
|
fprintf(stderr,"shift_addfast_cc done in %g seconds.\n",TIME_TAKEN(start_time,end_time));
|
||||||
|
|
||||||
|
//shift_unroll_cc
|
||||||
|
shift_unroll_data_t data_unroll = shift_unroll_init(0.1, T_BUFSIZE);
|
||||||
|
starting_phase = 0;
|
||||||
|
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_RAW, &start_time);
|
||||||
|
for(int i=0;i<T_N;i++) starting_phase = shift_unroll_cc(buf_c, outbuf_c, T_BUFSIZE, &data_unroll, starting_phase);
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_RAW, &end_time);
|
||||||
|
fprintf(stderr,"shift_unroll_cc done in %g seconds.\n",TIME_TAKEN(start_time,end_time));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
74
tsmpool.cpp
Normal file
74
tsmpool.cpp
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#include "tsmpool.h"
|
||||||
|
|
||||||
|
tsmpool::tsmpool(size_t size, int num) :
|
||||||
|
size(size),
|
||||||
|
num(num) //number of buffers of (size) to alloc
|
||||||
|
{
|
||||||
|
this->threads_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; i<num; i++)
|
||||||
|
{
|
||||||
|
void* newptr = (void*)new char[size];
|
||||||
|
if(!newptr) { this->ok = 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;i<threads.size();i++)
|
||||||
|
if(threads[i] == thread)
|
||||||
|
{
|
||||||
|
delete threads[i];
|
||||||
|
threads.erase(threads.begin()+i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&this->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
43
tsmpool.h
Normal file
43
tsmpool.h
Normal file
|
@ -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 <vector>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#define TSM_DEBUG 0
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
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<tsmthread_t*> threads;
|
||||||
|
vector<void*> 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; }
|
||||||
|
};
|
Loading…
Reference in a new issue