diff --git a/Makefile b/Makefile index 6b77fed..1752fc9 100644 --- a/Makefile +++ b/Makefile @@ -38,9 +38,10 @@ PARAMS_RASPI = -mfloat-abi=hard -mcpu=arm1176jzf-s -mfpu=vfp -funsafe-math-optim PARAMS_ARM = $(if $(call cpufeature,BCM2708,dummy-text),$(PARAMS_RASPI),$(PARAMS_NEON)) PARAMS_SIMD = $(if $(call cpufeature,sse,dummy-text),$(PARAMS_SSE),$(PARAMS_ARM)) PARAMS_LOOPVECT = -O3 -ffast-math -fdump-tree-vect-details -dumpbase dumpvect -PARAMS_LIBS = -g -lm -lrt -lfftw3f -DUSE_FFTW -DLIBCSDR_GPL +PARAMS_LIBS = -g -lm -lrt -lfftw3f -DUSE_FFTW -DLIBCSDR_GPL -DUSE_IMA_ADPCM PARAMS_SO = -fpic PARAMS_MISC = -Wno-unused-result +FFTW_PACKAGE = fftw-3.3.3 all: clean-vect @echo NOTE: you may have to manually edit Makefile to optimize for your CPU \(especially if you compile on ARM, please edit PARAMS_NEON\). @@ -64,3 +65,22 @@ install: uninstall: rm /usr/lib/libcsdr.so /usr/bin/csdr /usr/bin/csdr-fm ldconfig +emcc-clean: + -rm sdr.js/sdr.js + -rm sdr.js/sdrjs-compiled.js + -rm -rf sdr.js/$(FFTW_PACKAGE) +emcc-get-deps: + echo "getting and compiling fftw3 with emscripten..." + cd sdr.js; \ + wget http://fftw.org/$(FFTW_PACKAGE).tar.gz; \ + tar -xvf $(FFTW_PACKAGE).tar.gz; \ + rm $(FFTW_PACKAGE).tar.gz; \ + cd $(FFTW_PACKAGE); \ + emconfigure ./configure --enable-float --disable-fortran --prefix=`pwd`/emscripten-install --libdir=`pwd`/emscripten-lib; \ + emmake make; \ + emmake make install +emcc: + emcc -O3 -Isdr.js/$(FFTW_PACKAGE)/api -Lsdr.js/$(FFTW_PACKAGE)/emscripten-lib -o sdr.js/sdrjs-compiled.js fft_fftw.c libcsdr_wrapper.c -DLIBCSDR_GPL -DUSE_IMA_ADPCM -DUSE_FFTW -lfftw3f -s EXPORTED_FUNCTIONS="`python sdr.js/exported_functions.py`" + cat sdr.js/sdrjs-header.js sdr.js/sdrjs-compiled.js sdr.js/sdrjs-footer.js > sdr.js/sdr.js +emcc-beautify: + bash -c 'type js-beautify >/dev/null 2>&1; if [ $$? -eq 0 ]; then js-beautify sdr.js/sdr.js >sdr.js/sdr.js.beautiful; mv sdr.js/sdr.js.beautiful sdr.js/sdr.js; fi' diff --git a/README.md b/README.md index c306814..b0c9d79 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ Most of the code is available under the permissive BSD license, with some option - The code of *libcsdr* was intended to be easy to follow. - *libcsdr* was designed to use auto-vectorization available in *gcc*. It means that it can achieve some speedup by taking advantage of SIMD command sets available in today's CPUs (e.g. SSE on x86 and NEON on ARM). +Moreover, *libcsdr* serves as the base for the new, experimental sdr.js, which takes Software Defined Radio DSP to today's web browsers that provide JavScript JIT compilation. + How to compile -------------- The project was only tested on Linux. It has the following dependencies: `libfftw3-dev` @@ -155,15 +157,33 @@ It multiplies all samples by `gain`. It copies the input to the output. + none + +The `csdr` process just exits with 0. + yes_f [buf_times] It outputs continously the `to_repeat` float number. If `buf_times` is not given, it never stops. Else, after outputing `buf_times` number of buffers (the size of which is stated in the `BUFSIZE` macro), it exits. + detect_nan_ff + +Along with copying its input samples to the output, it prints a warning message to *stderr* if it finds any IEEE floating point NaN values among the samples. + + floatdump_f + +It prints any floating point input samples. +The format string used is `"%g "`. + + flowcontrol + +It limits the data rate of a stream to a given `data_rate` number of bytes per second. +It copies `data_rate / reads_per_second` bytes from the input to the output, doing it `reads_per_second` times every second. + shift_math_cc -It shifts the complex spectrum by `rate`. +It shifts the signal in the frequency domain by `rate`. `rate` is a floating point number between -0.5 and 0.5. `rate` is relative to the sampling rate. @@ -179,6 +199,17 @@ Internally, this function uses trigonometric addition formulas to generate sine This function was used to test the accuracy of the method above. + shift_table_cc [table_size] + +Operation is the same as with `shift_math_cc`. +Internally, this function uses a look-up table (LUT) to recall the values of the sine function (for the first quadrant). +The higher the table size is, the smaller the phase error is. + + decimating_shift_addition_cc [decimation] + +It shifts the input signal in the frequency domain, and also decimates it, without filtering. It will be useful as a part of the FFT channelizer implementation (to be done). +It cannot be used as a channelizer by itself, use `fir_decimate_cc` instead. + dcblock_ff This is a DC blocking IIR filter. @@ -214,7 +245,7 @@ It uses fixed filters so it works only on predefined sample rates, for the actua It is an AM demodulator that uses `sqrt`. On some architectures `sqrt` can be directly calculated by dedicated CPU instructions, but on others it may be slower. -amdemod_estimator_cf + amdemod_estimator_cf It is an AM demodulator that uses an estimation method that is faster but less accurate than `amdemod_cf`. @@ -296,10 +327,28 @@ FFTW can be faster if we let it optimalize a while before starting the first tra It measures the time taken to process `fft_cycles` transforms of `fft_size`. It lets FFTW optimalize if used with the `--benchmark` switch. - lowpower_cf [add_db] + logpower_cf [add_db] Calculates `10*log10(i^2+q^2)+add_db` for the input complex samples. It is useful for drawing power spectrum graphs. + encode_ima_adpcm_i16_u8 + +Encodes the audio stream to IMA ADPCM, which decreases the size to 25% of the original. + + decode_ima_adpcm_u8_i16 + +Decodes the audio stream from IMA ADPCM. + + compress_fft_adpcm_f_u8 + +Encodes the FFT output vectors of `fft_size`. It should be used on the data output from `logpower_cf`. +It resets the ADPCM encoder at the beginning of every vector, and to compensate it, `COMPRESS_FFT_PAD_N` samples are added at beginning (these equal to the first relevant sample). +The actual number of padding samples can be determined by running `cat csdr.c | grep "define COMPRESS_FFT_PAD_N"`. + + fft_exchange_sides_ff + +It exchanges the first and second part of the FFT vector, to prepare it for the waterfall/spectrum display. It should operate on the data output from `logpower_cf`. + #### Control via pipes Some parameters can be changed while the `csdr` process is running. To achieve this, some `csdr` functions have special parameters. You have to supply a fifo previously created by the `mkfifo` command. Processing will only start after the first control command has been received by `csdr` over the FIFO. @@ -326,6 +375,28 @@ E.g. you can send `-0.05 0.02\n` `csdr` was tested with GNU Radio Companion flowgraphs. These flowgraphs are available under the directory `grc_tests`, and they require the gr-ha5kfu set of blocks for GNU Radio. +## [sdr.js] (#sdr.js) + +*sdr.js* is *libcsdr* compiled to JavaScript code with *Emscripten*. Nowadays JavaScript runs quite fast in browsers, as all major browser vendors included JavaScript JIT machines into their product. You can find a great introductory slideshow here about *Emscripten*. + +The purpose of *sdr.js* is to make SDR DSP processing available in the web browser. However, it is not easy to use in production yet. By now, only those functions have wrappers that the front-end of OpenWebRX uses. + +To compile *sdr.js*, you will need emscripten. (It turns out that *emscripten* is already included in Ubuntu repositories.) + +To install and build dependencies (for now, only FFTW3): + + make emcc-get-deps + +To compile *sdr.js* (which will be created in the `sdr.js` subdirectory): + + make emcc + +You can test *sdr.js* by opening *sdr.html*. It contains a test for *firdes_lowpass_f* for this time. + +To remove *sdr.js* and the compiled dependencies: + + make emcc-clean + ## [Licensing] (#licensing) Most of the code of `libcsdr` is under BSD license. diff --git a/csdr.c b/csdr.c index 6aea2a8..905a879 100644 --- a/csdr.c +++ b/csdr.c @@ -44,6 +44,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include "libcsdr.h" #include "libcsdr_gpl.h" +#include "ima_adpcm.h" +#include +#include char usage[]= "csdr - a simple commandline tool for Software Defined Radio receiver DSP.\n\n" @@ -59,10 +62,16 @@ char usage[]= " limit_ff [max_amplitude]\n" " gain_ff \n" " clone\n" +" none\n" " yes_f [buf_times]\n" +" detect_nan_ff\n" +" floatdump_f\n" +" flowcontrol \n" " shift_math_cc \n" " shift_addition_cc \n" " shift_addition_cc_test\n" +" shift_table_cc [table_size]\n" +" decimating_shift_addition_cc [decimation]\n" " dcblock_ff\n" " fastdcblock_ff\n" " fmdemod_atan_cf\n" @@ -83,12 +92,22 @@ char usage[]= " logpower_cf [add_db]\n" " fft_benchmark [--benchmark]\n" " bandpass_fir_fft_cc [window]\n" +" encode_ima_adpcm_i16_u8\n" +" decode_ima_adpcm_u8_i16\n" +" compress_fft_adpcm_f_u8 \n" +" fft_exchange_sides_ff \n" +" \n" ; -#define BUFSIZE (1024*8) +#define BUFSIZE (1024) +#define BIG_BUFSIZE (1024*16) //should be multiple of 16! (size of double complex) //also, keep in mind that shift_addition_cc works better the smaller this buffer is. +#define YIELD_EVERY_N_TIMES 3 +#define TRY_YIELD if(++yield_counter%YIELD_EVERY_N_TIMES==0) sched_yield() +unsigned yield_counter=0; + int badsyntax(char* why) { if(why==0) fprintf(stderr, "%s", usage); @@ -113,6 +132,7 @@ int clone() { fread(clone_buffer, sizeof(unsigned char), BUFSIZE, stdin); fwrite(clone_buffer, sizeof(unsigned char), BUFSIZE, stdout); + TRY_YIELD; } } @@ -121,6 +141,8 @@ int clone() #define FWRITE_R fwrite(output_buffer, sizeof(float), BUFSIZE, stdout) #define FWRITE_C fwrite(output_buffer, sizeof(float)*2, BUFSIZE, stdout) #define FEOF_CHECK if(feof(stdin)) return 0 +#define BIG_FREAD_C fread(input_buffer, sizeof(float)*2, BIG_BUFSIZE, stdin) +#define BIG_FWRITE_C fwrite(output_buffer, sizeof(float)*2, BIG_BUFSIZE, stdout) int init_fifo(int argc, char *argv[]) { @@ -179,12 +201,12 @@ int read_fifo_ctl(int fd, char* format, ...) } int main(int argc, char *argv[]) -{ - static float input_buffer[BUFSIZE*2]; - static unsigned char buffer_u8[BUFSIZE*2]; - static float output_buffer[BUFSIZE*2]; - static short buffer_i16[BUFSIZE*2]; - static float temp_f[BUFSIZE*4]; +{ + static float input_buffer[BIG_BUFSIZE*2]; + static unsigned char buffer_u8[BIG_BUFSIZE*2]; + static float output_buffer[BIG_BUFSIZE*2]; + static short buffer_i16[BIG_BUFSIZE*2]; + static float temp_f[BIG_BUFSIZE*4]; if(argc<=1) return badsyntax(0); if(!strcmp(argv[1],"--help")) return badsyntax(0); if(!strcmp(argv[1],"convert_u8_f")) @@ -195,6 +217,7 @@ int main(int argc, char *argv[]) fread(buffer_u8, sizeof(unsigned char), BUFSIZE, stdin); convert_u8_f(buffer_u8, output_buffer, BUFSIZE); FWRITE_R; + TRY_YIELD; } } if(!strcmp(argv[1],"convert_f_u8")) //not tested @@ -205,6 +228,7 @@ int main(int argc, char *argv[]) FREAD_R; convert_f_u8(input_buffer, buffer_u8, BUFSIZE); fwrite(buffer_u8, sizeof(unsigned char), BUFSIZE, stdout); + TRY_YIELD; } } if(!strcmp(argv[1],"convert_f_i16")) @@ -215,6 +239,7 @@ int main(int argc, char *argv[]) FREAD_R; convert_f_i16(input_buffer, buffer_i16, BUFSIZE); fwrite(buffer_i16, sizeof(short), BUFSIZE, stdout); + TRY_YIELD; } } if(!strcmp(argv[1],"convert_i16_f")) //not tested @@ -225,6 +250,7 @@ int main(int argc, char *argv[]) fread(buffer_i16, sizeof(short), BUFSIZE, stdin); convert_i16_f(buffer_i16, output_buffer, BUFSIZE); FWRITE_R; + TRY_YIELD; } } if(!strcmp(argv[1],"realpart_cf")) @@ -235,6 +261,7 @@ int main(int argc, char *argv[]) FREAD_C; for(int i=0;i=4) sscanf(argv[3],"%d",&buf_times); for(int i=0;i/dev/null //csdr yes_f 1 1000000 | time csdr shift_addition_cc 0.2 >/dev/null + //csdr yes_f 1 1000000 | time csdr shift_table_cc 0.2 >/dev/null + + if(!strcmp(argv[1],"shift_table_cc")) + { + if(argc<=2) return badsyntax("need required parameter (rate)"); + float starting_phase=0; + float rate; + int table_size=65536; + sscanf(argv[2],"%g",&rate); + if(argc>3) sscanf(argv[3],"%d",&table_size); + shift_table_data_t table_data=shift_table_init(table_size); + fprintf(stderr,"shift_table_cc: LUT initialized\n"); + for(;;) + { + FEOF_CHECK; + if(!BIG_FREAD_C) break; + starting_phase=shift_table_cc((complexf*)input_buffer, (complexf*)output_buffer, BIG_BUFSIZE, rate, table_data, starting_phase); + BIG_FWRITE_C; + TRY_YIELD; + } + return 0; + } + #ifdef LIBCSDR_GPL + if(!strcmp(argv[1],"decimating_shift_addition_cc")) + { + if(argc<=2) return badsyntax("need required parameter (rate)"); + float starting_phase=0; + float rate; + int decimation=1; + sscanf(argv[2],"%g",&rate); + if(argc>3) sscanf(argv[3],"%d",&decimation); + shift_addition_data_t d=decimating_shift_addition_init(rate, decimation); + decimating_shift_addition_status_t s; + s.decimation_remain=0; + s.starting_phase=0; + for(;;) + { + FEOF_CHECK; + if(!BIG_FREAD_C) break; + s=decimating_shift_addition_cc((complexf*)input_buffer, (complexf*)output_buffer, BIG_BUFSIZE, d, decimation, s); + fwrite(output_buffer, sizeof(float)*2, s.output_size, stdout); + TRY_YIELD; + } + return 0; + } + if(!strcmp(argv[1],"shift_addition_cc")) { float starting_phase=0; @@ -329,10 +410,11 @@ int main(int argc, char *argv[]) for(;;) { FEOF_CHECK; - if(!FREAD_C) break; - starting_phase=shift_addition_cc((complexf*)input_buffer, (complexf*)output_buffer, BUFSIZE, data, starting_phase); - FWRITE_C; + if(!BIG_FREAD_C) break; + starting_phase=shift_addition_cc((complexf*)input_buffer, (complexf*)output_buffer, BIG_BUFSIZE, data, starting_phase); + BIG_FWRITE_C; if(read_fifo_ctl(fd,"%g\n",&rate)) break; + TRY_YIELD; } } return 0; @@ -357,6 +439,7 @@ int main(int argc, char *argv[]) FREAD_R; dcp=dcblock_ff(input_buffer, output_buffer, BUFSIZE, 0, dcp); FWRITE_R; + TRY_YIELD; } } @@ -372,6 +455,7 @@ int main(int argc, char *argv[]) fread(dcblock_buffer, sizeof(float), dcblock_bufsize, stdin); last_dc_level=fastdcblock_ff(dcblock_buffer, dcblock_buffer, dcblock_bufsize, last_dc_level); fwrite(dcblock_buffer, sizeof(float), dcblock_bufsize, stdout); + TRY_YIELD; } } @@ -385,6 +469,7 @@ int main(int argc, char *argv[]) if(feof(stdin)) return 0; last_phase=fmdemod_atan_cf((complexf*)input_buffer, output_buffer, BUFSIZE, last_phase); FWRITE_R; + TRY_YIELD; } } if(!strcmp(argv[1],"fmdemod_quadri_cf")) @@ -398,6 +483,7 @@ int main(int argc, char *argv[]) FREAD_C; last_sample=fmdemod_quadri_cf((complexf*)input_buffer, output_buffer, BUFSIZE, temp_f, last_sample); FWRITE_R; + TRY_YIELD; } } if(!strcmp(argv[1],"fmdemod_quadri_novect_cf")) @@ -411,6 +497,7 @@ int main(int argc, char *argv[]) FREAD_C; last_sample=fmdemod_quadri_novect_cf((complexf*)input_buffer, output_buffer, BUFSIZE, last_sample); FWRITE_R; + TRY_YIELD; } } if(!strcmp(argv[1],"deemphasis_wfm_ff")) @@ -421,7 +508,6 @@ int main(int argc, char *argv[]) float tau; sscanf(argv[3],"%g",&tau); fprintf(stderr,"deemphasis_wfm_ff: tau = %g, sample_rate = %d\n",tau,sample_rate); - float last_output=0; for(;;) { @@ -429,8 +515,42 @@ int main(int argc, char *argv[]) FREAD_R; last_output=deemphasis_wfm_ff(input_buffer, output_buffer, BUFSIZE, tau, sample_rate, last_output); FWRITE_R; + TRY_YIELD; } } + + if(!strcmp(argv[1],"detect_nan_ff")) + { + for(;;) + { + FEOF_CHECK; + FREAD_R; + int nan_detect=0; + for(int i=0; ioutput, sizeof(complexf), input_size, stdout); if(read_fifo_ctl(fd,"%g %g\n",&low_cut,&high_cut)) break; + TRY_YIELD; } } } +#ifdef USE_IMA_ADPCM +#define IMA_ADPCM_BUFSIZE BUFSIZE + + if(!strcmp(argv[1],"encode_ima_adpcm_i16_u8")) + { + ima_adpcm_state_t d; + d.index=d.previousValue=0; + for(;;) + { + FEOF_CHECK; + fread(buffer_i16, sizeof(short), IMA_ADPCM_BUFSIZE, stdin); + d=encode_ima_adpcm_i16_u8(buffer_i16, buffer_u8, IMA_ADPCM_BUFSIZE, d); + fwrite(buffer_u8, sizeof(unsigned char), IMA_ADPCM_BUFSIZE/2, stdout); + TRY_YIELD; + } + } + + if(!strcmp(argv[1],"decode_ima_adpcm_u8_i16")) + { + ima_adpcm_state_t d; + d.index=d.previousValue=0; + for(;;) + { + FEOF_CHECK; + fread(buffer_u8, sizeof(unsigned char), IMA_ADPCM_BUFSIZE/2, stdin); + d=decode_ima_adpcm_u8_i16(buffer_u8, buffer_i16, IMA_ADPCM_BUFSIZE/2, d); + fwrite(buffer_i16, sizeof(short), IMA_ADPCM_BUFSIZE, stdout); + TRY_YIELD; + } + } +#endif + + if(!strcmp(argv[1],"flowcontrol")) + { + if(argc<=3) return badsyntax("need required parameters (data_rate, reads_per_seconds)"); + int data_rate; + sscanf(argv[2],"%d",&data_rate); + int reads_per_second; + sscanf(argv[3],"%d",&reads_per_second); + int flowcontrol_bufsize=ceil(1.*(double)data_rate/reads_per_second); + unsigned char* flowcontrol_buffer = (unsigned char*)malloc(sizeof(unsigned char)*flowcontrol_bufsize); + int flowcontrol_sleep=floor(1000000./reads_per_second); + fprintf(stderr, "flowcontrol: flowcontrol_bufsize = %d, flowcontrol_sleep = %d\n", flowcontrol_bufsize, flowcontrol_sleep); + for(;;) + { + FEOF_CHECK; + fread(flowcontrol_buffer, sizeof(unsigned char), flowcontrol_bufsize, stdin); + fwrite(flowcontrol_buffer, sizeof(unsigned char), flowcontrol_bufsize, stdout); + usleep(flowcontrol_sleep); + TRY_YIELD; + } + } + if(!strcmp(argv[1],"none")) { return 0; diff --git a/grc_tests/test_ima_adpcm.grc b/grc_tests/test_ima_adpcm.grc new file mode 100644 index 0000000..c0b77f8 --- /dev/null +++ b/grc_tests/test_ima_adpcm.grc @@ -0,0 +1,844 @@ + + + + Sun Jan 25 13:25:30 2015 + + options + + id + top_block + + + _enabled + True + + + title + + + + author + + + + description + + + + window_size + 1280, 1024 + + + generate_options + wx_gui + + + category + Custom + + + run_options + prompt + + + run + True + + + max_nouts + 0 + + + realtime_scheduling + + + + alias + + + + _coordinate + (10, 10) + + + _rotation + 0 + + + + variable + + id + samp_rate + + + _enabled + True + + + value + 32000 + + + alias + + + + _coordinate + (176, 11) + + + _rotation + 0 + + + + variable_slider + + id + amp + + + _enabled + True + + + label + + + + value + 0.5 + + + min + 0 + + + max + 1 + + + num_steps + 100 + + + style + wx.SL_HORIZONTAL + + + converver + float_converter + + + grid_pos + + + + notebook + + + + alias + + + + _coordinate + (544, 11) + + + _rotation + 0 + + + + analog_sig_source_x + + id + analog_sig_source_x_0 + + + _enabled + True + + + type + float + + + samp_rate + samp_rate + + + waveform + analog.GR_COS_WAVE + + + freq + freq + + + amp + amp + + + offset + 0 + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (8, 163) + + + _rotation + 0 + + + + blocks_throttle + + id + blocks_throttle_0 + + + _enabled + True + + + type + float + + + samples_per_second + samp_rate + + + vlen + 1 + + + ignoretag + True + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (176, 195) + + + _rotation + 0 + + + + wxgui_scopesink2 + + id + wxgui_scopesink2_0 + + + _enabled + True + + + type + float + + + title + Scope Plot of Original Signal + + + samp_rate + samp_rate + + + v_scale + 0 + + + v_offset + 0 + + + t_scale + 0 + + + ac_couple + False + + + xy_mode + False + + + num_inputs + 1 + + + win_size + + + + grid_pos + + + + notebook + nb0,0 + + + trig_mode + wxgui.TRIG_MODE_AUTO + + + y_axis_label + Counts + + + alias + + + + affinity + + + + _coordinate + (352, 331) + + + _rotation + 0 + + + + notebook + + id + nb0 + + + _enabled + True + + + style + wx.NB_TOP + + + labels + ['Scope', 'FFT'] + + + grid_pos + + + + notebook + + + + alias + + + + _coordinate + (272, 11) + + + _rotation + 0 + + + + notebook + + id + nb1 + + + _enabled + True + + + style + wx.NB_TOP + + + labels + ['Scope', 'FFT'] + + + grid_pos + + + + notebook + + + + alias + + + + _coordinate + (272, 99) + + + _rotation + 0 + + + + wxgui_fftsink2 + + id + wxgui_fftsink2_0 + + + _enabled + True + + + type + float + + + title + FFT Plot of Original Signal + + + samp_rate + samp_rate + + + baseband_freq + 0 + + + y_per_div + 10 + + + y_divs + 10 + + + ref_level + 0 + + + ref_scale + 2.0 + + + fft_size + 1024 + + + fft_rate + 15 + + + peak_hold + False + + + average + False + + + avg_alpha + 0 + + + win + None + + + win_size + + + + grid_pos + + + + notebook + nb0,1 + + + freqvar + None + + + alias + + + + affinity + + + + _coordinate + (352, 459) + + + _rotation + 0 + + + + wxgui_scopesink2 + + id + wxgui_scopesink2_0_0 + + + _enabled + True + + + type + float + + + title + Scope Plot of Processed Signal (csdr) + + + samp_rate + samp_rate + + + v_scale + 0 + + + v_offset + 0 + + + t_scale + 0 + + + ac_couple + False + + + xy_mode + False + + + num_inputs + 1 + + + win_size + + + + grid_pos + + + + notebook + nb1,0 + + + trig_mode + wxgui.TRIG_MODE_AUTO + + + y_axis_label + Counts + + + alias + + + + affinity + + + + _coordinate + (760, 171) + + + _rotation + 0 + + + + wxgui_fftsink2 + + id + wxgui_fftsink2_0_0 + + + _enabled + True + + + type + float + + + title + FFT Plot of Processed Signal (csdr) + + + samp_rate + samp_rate + + + baseband_freq + 0 + + + y_per_div + 10 + + + y_divs + 10 + + + ref_level + 0 + + + ref_scale + 2.0 + + + fft_size + 1024 + + + fft_rate + 15 + + + peak_hold + False + + + average + False + + + avg_alpha + 0 + + + win + None + + + win_size + + + + grid_pos + + + + notebook + nb1,1 + + + freqvar + None + + + alias + + + + affinity + + + + _coordinate + (760, 299) + + + _rotation + 0 + + + + ha5kfu_execproc_xx + + id + ha5kfu_execproc_xx_0 + + + _enabled + True + + + type + ff + + + commandline + csdr convert_f_i16 | csdr encode_ima_adpcm_i16_u8 | csdr decode_ima_adpcm_u8_i16 | csdr convert_i16_f + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (448, 195) + + + _rotation + 0 + + + + variable_slider + + id + freq + + + _enabled + True + + + label + + + + value + 1000 + + + min + 0 + + + max + samp_rate/2 + + + num_steps + 100 + + + style + wx.SL_HORIZONTAL + + + converver + float_converter + + + grid_pos + + + + notebook + + + + alias + + + + _coordinate + (424, 11) + + + _rotation + 0 + + + + analog_sig_source_x_0 + blocks_throttle_0 + 0 + 0 + + + blocks_throttle_0 + ha5kfu_execproc_xx_0 + 0 + 0 + + + blocks_throttle_0 + wxgui_scopesink2_0 + 0 + 0 + + + blocks_throttle_0 + wxgui_fftsink2_0 + 0 + 0 + + + ha5kfu_execproc_xx_0 + wxgui_scopesink2_0_0 + 0 + 0 + + + ha5kfu_execproc_xx_0 + wxgui_fftsink2_0_0 + 0 + 0 + + diff --git a/grc_tests/test_rational_resampler.grc b/grc_tests/test_rational_resampler.grc index 7bb2e71..fe8e838 100644 --- a/grc_tests/test_rational_resampler.grc +++ b/grc_tests/test_rational_resampler.grc @@ -1,7 +1,7 @@ - Tue Nov 25 18:15:25 2014 + Tue Mar 17 19:40:27 2015 options @@ -77,7 +77,7 @@ value - 2 + 1 alias @@ -104,7 +104,7 @@ value - 5 + 4 alias @@ -939,49 +939,6 @@ 0 - - ha5kfu_execproc_xx - - id - ha5kfu_execproc_xx_0 - - - _enabled - True - - - type - ff - - - commandline - "csdr rational_resampler_ff %d %d 0.05"%(interpolation,decimation) - - - alias - - - - affinity - - - - minoutbuf - 0 - - - maxoutbuf - 0 - - - _coordinate - (752, 195) - - - _rotation - 0 - - notebook @@ -1076,6 +1033,49 @@ 0 + + ha5kfu_execproc_xx + + id + ha5kfu_execproc_xx_0 + + + _enabled + True + + + type + ff + + + commandline + "csdr rational_resampler_ff %d %d 0.05"%(interpolation,decimation) + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (752, 195) + + + _rotation + 0 + + blocks_throttle_0 ha5kfu_execproc_xx_0 diff --git a/grc_tests/test_shift.grc b/grc_tests/test_shift.grc index 5f5a9ab..9fc1eb7 100644 --- a/grc_tests/test_shift.grc +++ b/grc_tests/test_shift.grc @@ -1,7 +1,7 @@ - Sat Nov 15 22:24:14 2014 + Thu Jan 15 18:51:48 2015 options @@ -65,6 +65,60 @@ 0 + + variable + + id + rate + + + _enabled + True + + + value + -0.055 + + + alias + + + + _coordinate + (8, 195) + + + _rotation + 0 + + + + variable + + id + decimation + + + _enabled + True + + + value + 3 + + + alias + + + + _coordinate + (16, 267) + + + _rotation + 0 + + variable @@ -85,7 +139,7 @@ _coordinate - (10, 170) + (176, 11) _rotation @@ -144,7 +198,132 @@ _coordinate - (56, 243) + (8, 75) + + + _rotation + 0 + + + + ha5kfu_execproc_xx + + id + ha5kfu_execproc_xx_0 + + + _enabled + True + + + type + cc + + + commandline + "csdr shift_math_cc %g"%rate + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (488, 251) + + + _rotation + 0 + + + + ha5kfu_execproc_xx + + id + ha5kfu_execproc_xx_0_0 + + + _enabled + True + + + type + cc + + + commandline + "csdr shift_addition_cc %g"%rate + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (472, 411) + + + _rotation + 0 + + + + notebook + + id + nb0 + + + _enabled + True + + + style + wx.NB_TOP + + + labels + ['shift_math_cc', 'shift_addition_cc', 'shift_table_cc', 'decimating_shift_addition_cc','original'] + + + grid_pos + + + + notebook + + + + alias + + + + _coordinate + (272, 11) _rotation @@ -155,7 +334,7 @@ wxgui_fftsink2 id - wxgui_fftsink2_0 + wxgui_fftsink2_0_0 _enabled @@ -227,7 +406,7 @@ notebook - nb0,0 + nb0,4 freqvar @@ -243,7 +422,7 @@ _coordinate - (952, 275) + (496, 27) _rotation @@ -251,22 +430,105 @@ - notebook + blocks_throttle id - nb0 + blocks_throttle_0 _enabled True - style - wx.NB_TOP + type + complex - labels - ['shift_math_ff', 'shift_addition_ff', 'original'] + samples_per_second + samp_rate + + + vlen + 1 + + + ignoretag + True + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (224, 107) + + + _rotation + 0 + + + + wxgui_scopesink2 + + id + wxgui_scopesink2_0_0_0 + + + _enabled + True + + + type + complex + + + title + Scope Plot + + + samp_rate + samp_rate + + + v_scale + 0 + + + v_offset + 0 + + + t_scale + 0 + + + ac_couple + False + + + xy_mode + False + + + num_inputs + 1 + + + win_size + grid_pos @@ -274,15 +536,27 @@ notebook - + nb0,2 + + + trig_mode + wxgui.TRIG_MODE_NORM + + + y_axis_label + Counts alias + + affinity + + _coordinate - (256, 27) + (1080, 899) _rotation @@ -293,7 +567,275 @@ wxgui_fftsink2 id - wxgui_fftsink2_0_0 + wxgui_fftsink2_0_1_0_0 + + + _enabled + True + + + type + complex + + + title + FFT Plot + + + samp_rate + samp_rate/decimation + + + baseband_freq + 0 + + + y_per_div + 10 + + + y_divs + 10 + + + ref_level + 0 + + + ref_scale + 2.0 + + + fft_size + 1024 + + + fft_rate + 15 + + + peak_hold + False + + + average + False + + + avg_alpha + 0 + + + win + None + + + win_size + + + + grid_pos + + + + notebook + nb0,3 + + + freqvar + None + + + alias + + + + affinity + + + + _coordinate + (320, 667) + + + _rotation + 0 + + + + wxgui_scopesink2 + + id + wxgui_scopesink2_0_0_0_0 + + + _enabled + True + + + type + complex + + + title + Scope Plot + + + samp_rate + samp_rate/decimation + + + v_scale + 0 + + + v_offset + 0 + + + t_scale + 0 + + + ac_couple + False + + + xy_mode + False + + + num_inputs + 1 + + + win_size + + + + grid_pos + + + + notebook + nb0,3 + + + trig_mode + wxgui.TRIG_MODE_NORM + + + y_axis_label + Counts + + + alias + + + + affinity + + + + _coordinate + (320, 883) + + + _rotation + 0 + + + + ha5kfu_execproc_xx + + id + ha5kfu_execproc_xx_0_0_0_0 + + + _enabled + True + + + type + cc + + + commandline + "csdr decimating_shift_addition_cc %g %d"%(rate, decimation) + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (80, 835) + + + _rotation + 0 + + + + ha5kfu_execproc_xx + + id + ha5kfu_execproc_xx_0_0_0 + + + _enabled + True + + + type + cc + + + commandline + "csdr shift_table_cc %g 32768"%rate + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (824, 771) + + + _rotation + 0 + + + + wxgui_fftsink2 + + id + wxgui_fftsink2_0_1_0 _enabled @@ -381,7 +923,90 @@ _coordinate - (952, 59) + (1080, 691) + + + _rotation + 0 + + + + wxgui_scopesink2 + + id + wxgui_scopesink2_0_0 + + + _enabled + True + + + type + complex + + + title + Scope Plot + + + samp_rate + samp_rate + + + v_scale + 0 + + + v_offset + 0 + + + t_scale + 0 + + + ac_couple + False + + + xy_mode + False + + + num_inputs + 1 + + + win_size + + + + grid_pos + + + + notebook + nb0,1 + + + trig_mode + wxgui.TRIG_MODE_NORM + + + y_axis_label + Counts + + + alias + + + + affinity + + + + _coordinate + (824, 571) _rotation @@ -480,58 +1105,7 @@ _coordinate - (952, 635) - - - _rotation - 0 - - - - blocks_throttle - - id - blocks_throttle_0 - - - _enabled - True - - - type - complex - - - samples_per_second - samp_rate - - - vlen - 1 - - - ignoretag - True - - - alias - - - - affinity - - - - minoutbuf - 0 - - - maxoutbuf - 0 - - - _coordinate - (232, 275) + (824, 355) _rotation @@ -614,7 +1188,7 @@ _coordinate - (952, 491) + (824, 235) _rotation @@ -622,10 +1196,10 @@ - wxgui_scopesink2 + wxgui_fftsink2 id - wxgui_scopesink2_0_0 + wxgui_fftsink2_0 _enabled @@ -637,35 +1211,55 @@ title - Scope Plot + FFT Plot samp_rate samp_rate - v_scale + baseband_freq 0 - v_offset + y_per_div + 10 + + + y_divs + 10 + + + ref_level 0 - t_scale - 0 + ref_scale + 2.0 - ac_couple + fft_size + 1024 + + + fft_rate + 15 + + + peak_hold False - xy_mode + average False - num_inputs - 1 + avg_alpha + 0 + + + win + None win_size @@ -677,15 +1271,11 @@ notebook - nb0,1 + nb0,0 - trig_mode - wxgui.TRIG_MODE_NORM - - - y_axis_label - Counts + freqvar + None alias @@ -697,93 +1287,7 @@ _coordinate - (952, 875) - - - _rotation - 0 - - - - ha5kfu_execproc_xx - - id - ha5kfu_execproc_xx_0 - - - _enabled - True - - - type - cc - - - commandline - csdr shift_math_cc -0.25 - - - alias - - - - affinity - - - - minoutbuf - 0 - - - maxoutbuf - 0 - - - _coordinate - (496, 483) - - - _rotation - 0 - - - - ha5kfu_execproc_xx - - id - ha5kfu_execproc_xx_0_0 - - - _enabled - True - - - type - cc - - - commandline - csdr shift_addition_cc -0.25 - - - alias - - - - affinity - - - - minoutbuf - 0 - - - maxoutbuf - 0 - - - _coordinate - (504, 635) + (824, 19) _rotation @@ -838,4 +1342,40 @@ 0 0 + + blocks_throttle_0 + ha5kfu_execproc_xx_0_0_0 + 0 + 0 + + + blocks_throttle_0 + ha5kfu_execproc_xx_0_0_0_0 + 0 + 0 + + + ha5kfu_execproc_xx_0_0_0_0 + wxgui_scopesink2_0_0_0_0 + 0 + 0 + + + ha5kfu_execproc_xx_0_0_0_0 + wxgui_fftsink2_0_1_0_0 + 0 + 0 + + + ha5kfu_execproc_xx_0_0_0 + wxgui_fftsink2_0_1_0 + 0 + 0 + + + ha5kfu_execproc_xx_0_0_0 + wxgui_scopesink2_0_0_0 + 0 + 0 + diff --git a/ima_adpcm.c b/ima_adpcm.c new file mode 100644 index 0000000..03a9b7c --- /dev/null +++ b/ima_adpcm.c @@ -0,0 +1,176 @@ +/* + +This software is part of libcsdr, a set of simple DSP routines for +Software Defined Radio. + +Copyright (c) 2015, Andras Retzler +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ANDRAS RETZLER BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Copyright 1997 Tim Kientzle. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. 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. +3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + This product includes software developed by Tim Kientzle + and published in ``The Programmer's Guide to Sound.'' +4. Neither the names of Tim Kientzle nor Addison-Wesley + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``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 TIM KIENTZLE OR ADDISON-WESLEY 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. +*/ + +/*********************************************************** +Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The +Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +#ifdef USE_IMA_ADPCM + +#include "ima_adpcm.h" + +const int indexAdjustTable[16] = { + -1, -1, -1, -1, // +0 - +3, decrease the step size + 2, 4, 6, 8, // +4 - +7, increase the step size + -1, -1, -1, -1, // -0 - -3, decrease the step size + 2, 4, 6, 8, // -4 - -7, increase the step size +}; + +const int _stepSizeTable[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, + 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, + 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, + 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, + 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, + 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, + 27086, 29794, 32767 +}; + +static inline short ImaAdpcmDecode(unsigned char deltaCode, ima_adpcm_state_t* state) { + // Get the current step size + int step = _stepSizeTable[state->index]; + + // Construct the difference by scaling the current step size + // This is approximately: difference = (deltaCode+.5)*step/4 + int difference = step>>3; + if ( deltaCode & 1 ) difference += step>>2; + if ( deltaCode & 2 ) difference += step>>1; + if ( deltaCode & 4 ) difference += step; + if ( deltaCode & 8 ) difference = -difference; + + // Build the new sample + state->previousValue += difference; + if (state->previousValue > 32767) state->previousValue = 32767; + else if (state->previousValue < -32768) state->previousValue = -32768; + + // Update the step for the next sample + state->index += indexAdjustTable[deltaCode]; + if (state->index < 0) state->index = 0; + else if (state->index > 88) state->index = 88; + + return state->previousValue; +} + +static inline unsigned char ImaAdpcmEncode(short sample, ima_adpcm_state_t* state) { + int diff = sample - state->previousValue; + int step = _stepSizeTable[state->index]; + int deltaCode = 0; + + // Set sign bit + if (diff < 0) { deltaCode = 8; diff = -diff; } + + // This is essentially deltaCode = (diff<<2)/step, + // except the roundoff is handled differently. + if ( diff >= step ) { deltaCode |= 4; diff -= step; } + step >>= 1; + if ( diff >= step ) { deltaCode |= 2; diff -= step; } + step >>= 1; + if ( diff >= step ) { deltaCode |= 1; diff -= step; } + + ImaAdpcmDecode(deltaCode,state); // update state + return deltaCode; +} + +ima_adpcm_state_t encode_ima_adpcm_i16_u8(short* input, unsigned char* output, int input_length, ima_adpcm_state_t state) +{ + int k=0; + for(int i=0;i>4)&0xf,&state); + } + return state; +} + +#endif diff --git a/ima_adpcm.h b/ima_adpcm.h new file mode 100644 index 0000000..7e7bd9d --- /dev/null +++ b/ima_adpcm.h @@ -0,0 +1,13 @@ +#pragma once + +#ifdef USE_IMA_ADPCM + +typedef struct ImaState { + int index; // Index into step size table + int previousValue; // Most recent sample value +} ima_adpcm_state_t; + +ima_adpcm_state_t encode_ima_adpcm_i16_u8(short* input, unsigned char* output, int input_length, ima_adpcm_state_t state); +ima_adpcm_state_t decode_ima_adpcm_u8_i16(unsigned char* input, short* output, int input_length, ima_adpcm_state_t state); + +#endif diff --git a/libcsdr.c b/libcsdr.c index 7d6e449..2cf5e9b 100644 --- a/libcsdr.c +++ b/libcsdr.c @@ -205,6 +205,64 @@ float shift_math_cc(complexf *input, complexf* output, int input_size, float rat return phase; } + + +shift_table_data_t shift_table_init(int table_size) +{ + //RTODO + shift_table_data_t output; + output.table=(float*)malloc(sizeof(float)*table_size); + output.table_size=table_size; + for(int i=0;i1)?-1:1; //in quadrant 2 and 3 + cos_sign=(quadrant&&quadrant<3)?-1:1; //in quadrant 1 and 2 + sinval=sin_sign*table_data.table[sin_index]; + cosval=cos_sign*table_data.table[cos_index]; + //we multiply two complex numbers. + //how? enter this to maxima (software) for explanation: + // (a+b*%i)*(c+d*%i), rectform; + iof(output,i)=cosval*iof(input,i)-sinval*qof(input,i); + qof(output,i)=sinval*iof(input,i)+cosval*qof(input,i); + phase+=phase_increment; + while(phase>2*PI) phase-=2*PI; //@shift_math_cc: normalize phase + while(phase<0) phase+=2*PI; + } + return phase; +} + int fir_decimate_cc(complexf *input, complexf *output, int input_size, int decimation, float *taps, int taps_length) { //Theory: http://www.dspguru.com/dsp/faqs/multirate/decimation @@ -339,6 +397,8 @@ fractional_decimator_ff_t fractional_decimator_ff(float* input, float* output, i void apply_fir_fft_cc(FFT_PLAN_T* plan, FFT_PLAN_T* plan_inverse, complexf* taps_fft, complexf* last_overlap, int overlap_size) { + //use the overlap & add method for filtering + //calculate FFT on input buffer fft_execute(plan); @@ -593,6 +653,14 @@ complexf fmdemod_quadri_cf(complexf* input, float* output, int input_size, float return input[input_size-1]; } +inline int is_nan(float f) +{ + //http://stackoverflow.com/questions/570669/checking-if-a-double-or-float-is-nan-in-c + unsigned u = *(unsigned*)&f; + return (u&0x7F800000) == 0x7F800000 && (u&0x7FFFFF); // Both NaN and qNan. +} + + float deemphasis_wfm_ff (float* input, float* output, int input_size, float tau, int sample_rate, float last_output) { /* @@ -604,6 +672,7 @@ float deemphasis_wfm_ff (float* input, float* output, int input_size, float tau, */ float dt = 1.0/sample_rate; float alpha = dt/(tau+dt); + if(is_nan(last_output)) last_output=0.0; //if last_output is NaN output[0]=alpha*input[0]+(1-alpha)*last_output; for (int i=1;iPI) s.starting_phase-=2*PI; //@shift_addition_cc: normalize starting_phase + while(s.starting_phase<-PI) s.starting_phase+=2*PI; + return s; +} + + float agc_ff(float* input, float* output, int input_size, float reference, float attack_rate, float decay_rate, float max_gain, short hang_time, short attack_wait_time, float gain_filter_alpha, float last_gain) { /* diff --git a/libcsdr_gpl.h b/libcsdr_gpl.h index 98c0feb..a4bfd23 100644 --- a/libcsdr_gpl.h +++ b/libcsdr_gpl.h @@ -35,5 +35,14 @@ void shift_addition_cc_test(shift_addition_data_t d); float agc_ff(float* input, float* output, int input_size, float reference, float attack_rate, float decay_rate, float max_gain, short hang_time, short attack_wait_time, float gain_filter_alpha, float last_gain); +typedef struct decimating_shift_addition_status_s +{ + int decimation_remain; + float starting_phase; + int output_size; +} decimating_shift_addition_status_t; +decimating_shift_addition_status_t decimating_shift_addition_cc(complexf *input, complexf* output, int input_size, shift_addition_data_t d, int decimation, decimating_shift_addition_status_t s); +shift_addition_data_t decimating_shift_addition_init(float rate, int decimation); + #endif diff --git a/libcsdr_wrapper.c b/libcsdr_wrapper.c index 4ab53e3..3e6ab87 100644 --- a/libcsdr_wrapper.c +++ b/libcsdr_wrapper.c @@ -1,3 +1,4 @@ #include "libcsdr.c" #include "libcsdr_gpl.c" +#include "ima_adpcm.c" //this wrapper helps parsevect.py to generate better output diff --git a/predefined.h b/predefined.h index e0c74bb..a00607f 100644 --- a/predefined.h +++ b/predefined.h @@ -50,11 +50,9 @@ function mkdeemph(sr,tapnum,norm_freq) printf("%g, ",coeffs); printf("\n") end - -mkdeemph(48000,199,500) - */ +//mkdeemph(48000,199,500) float deemphasis_nfm_predefined_fir_48000[] = { 0.00172568, 0.00179665, 0.00191952, 0.00205318, 0.00215178, 0.00217534, 0.00209924, 0.00192026, 0.00165789, 0.0013502, 0.00104545, 0.000790927, 0.000621911, 0.000553077, 0.000574554, 0.000653624, 0.000741816, 0.000785877, 0.000740151, 0.000577506, 0.000296217, -7.89273e-05, -0.0005017, -0.000914683, -0.00126243, -0.00150456, -0.00162564, -0.0016396, -0.00158725, -0.00152751, -0.00152401, -0.00163025, -0.00187658, -0.00226223, -0.00275443, -0.003295, -0.0038132, -0.00424193, -0.00453375, -0.00467274, -0.00467943, -0.00460728, -0.00453119, -0.00453056, -0.00467051, -0.00498574, -0.00547096, -0.00608027, -0.00673627, -0.00734698, -0.00782705, -0.00811841, -0.00820539, -0.00812057, -0.00793936, -0.00776415, -0.00770111, -0.00783479, -0.00820643, -0.00880131, -0.00954878, -0.0103356, -0.0110303, -0.011514, -0.0117094, -0.0116029, -0.0112526, -0.0107795, -0.010343, -0.0101053, -0.0101917, -0.0106561, -0.0114608, -0.0124761, -0.0135018, -0.0143081, -0.0146885, -0.0145126, -0.0137683, -0.0125796, -0.0111959, -0.00994914, -0.00918404, -0.00917447, -0.0100402, -0.0116822, -0.0137533, -0.0156723, -0.0166881, -0.0159848, -0.0128153, -0.00664117, 0.00274383, 0.0151313, 0.0298729, 0.0459219, 0.0619393, 0.076451, 0.0880348, 0.0955087, 0.098091, 0.0955087, 0.0880348, 0.076451, 0.0619393, 0.0459219, 0.0298729, 0.0151313, 0.00274383, -0.00664117, -0.0128153, -0.0159848, -0.0166881, -0.0156723, -0.0137533, -0.0116822, -0.0100402, -0.00917447, -0.00918404, -0.00994914, -0.0111959, -0.0125796, -0.0137683, -0.0145126, -0.0146885, -0.0143081, -0.0135018, -0.0124761, -0.0114608, -0.0106561, -0.0101917, -0.0101053, -0.010343, -0.0107795, -0.0112526, -0.0116029, -0.0117094, -0.011514, -0.0110303, -0.0103356, -0.00954878, -0.00880131, -0.00820643, -0.00783479, -0.00770111, -0.00776415, -0.00793936, -0.00812057, -0.00820539, -0.00811841, -0.00782705, -0.00734698, -0.00673627, -0.00608027, -0.00547096, -0.00498574, -0.00467051, -0.00453056, -0.00453119, -0.00460728, -0.00467943, -0.00467274, -0.00453375, -0.00424193, -0.0038132, -0.003295, -0.00275443, -0.00226223, -0.00187658, -0.00163025, -0.00152401, -0.00152751, -0.00158725, -0.0016396, -0.00162564, -0.00150456, -0.00126243, -0.000914683, -0.0005017, -7.89273e-05, 0.000296217, 0.000577506, 0.000740151, 0.000785877, 0.000741816, 0.000653624, 0.000574554, 0.000553077, 0.000621911, 0.000790927, 0.00104545, 0.0013502, 0.00165789, 0.00192026, 0.00209924, 0.00217534, 0.00215178, 0.00205318, 0.00191952, 0.00179665, 0.00172568 }; @@ -64,3 +62,7 @@ float deemphasis_nfm_predefined_fir_8000[] = float deemphasis_nfm_predefined_fir_44100[] = { 0.0025158, 0.00308564, 0.00365507, 0.00413598, 0.00446279, 0.00461162, 0.00460866, 0.00452474, 0.00445739, 0.00450444, 0.00473648, 0.0051757, 0.0057872, 0.00648603, 0.00715856, 0.00769296, 0.00801081, 0.00809096, 0.00797853, 0.00777577, 0.00761627, 0.00762871, 0.00789987, 0.00844699, 0.00920814, 0.0100543, 0.0108212, 0.0113537, 0.011551, 0.0113994, 0.0109834, 0.0104698, 0.0100665, 0.00996618, 0.0102884, 0.0110369, 0.0120856, 0.0131998, 0.0140907, 0.0144924, 0.0142417, 0.0133401, 0.0119771, 0.0105043, 0.00935909, 0.00895022, 0.00952985, 0.0110812, 0.0132522, 0.015359, 0.0164664, 0.0155409, 0.0116496, 0.00416925, -0.00703664, -0.021514, -0.0382135, -0.0555955, -0.0718318, -0.0850729, -0.0937334, -0.0967458, -0.0937334, -0.0850729, -0.0718318, -0.0555955, -0.0382135, -0.021514, -0.00703664, 0.00416925, 0.0116496, 0.0155409, 0.0164664, 0.015359, 0.0132522, 0.0110812, 0.00952985, 0.00895022, 0.00935909, 0.0105043, 0.0119771, 0.0133401, 0.0142417, 0.0144924, 0.0140907, 0.0131998, 0.0120856, 0.0110369, 0.0102884, 0.00996618, 0.0100665, 0.0104698, 0.0109834, 0.0113994, 0.011551, 0.0113537, 0.0108212, 0.0100543, 0.00920814, 0.00844699, 0.00789987, 0.00762871, 0.00761627, 0.00777577, 0.00797853, 0.00809096, 0.00801081, 0.00769296, 0.00715856, 0.00648603, 0.0057872, 0.0051757, 0.00473648, 0.00450444, 0.00445739, 0.00452474, 0.00460866, 0.00461162, 0.00446279, 0.00413598, 0.00365507, 0.00308564, 0.0025158 }; + +//mkdeemph(11025,79,500) +float deemphasis_nfm_predefined_fir_11025[] = +{ 0.00113162, 0.000911207, 0.00173815, -0.000341385, -0.000849373, -0.00033066, -0.00290692, -0.00357326, -0.0031917, -0.00607078, -0.00659201, -0.00601551, -0.00886603, -0.00880243, -0.00759841, -0.0100344, -0.0088993, -0.00664423, -0.00835258, -0.00572919, -0.00214109, -0.00302443, 0.00132902, 0.00627003, 0.00596494, 0.0120731, 0.0180437, 0.0176243, 0.0253776, 0.0316572, 0.0298485, 0.0393389, 0.0446019, 0.0389943, 0.0516463, 0.0521951, 0.0350192, 0.0600945, 0.0163128, -0.217526, -0.378533, -0.217526, 0.0163128, 0.0600945, 0.0350192, 0.0521951, 0.0516463, 0.0389943, 0.0446019, 0.0393389, 0.0298485, 0.0316572, 0.0253776, 0.0176243, 0.0180437, 0.0120731, 0.00596494, 0.00627003, 0.00132902, -0.00302443, -0.00214109, -0.00572919, -0.00835258, -0.00664423, -0.0088993, -0.0100344, -0.00759841, -0.00880243, -0.00886603, -0.00601551, -0.00659201, -0.00607078, -0.0031917, -0.00357326, -0.00290692, -0.00033066, -0.000849373, -0.000341385, 0.00173815, 0.000911207, 0.00113162 }; diff --git a/sdr.js/exported_functions.py b/sdr.js/exported_functions.py new file mode 100644 index 0000000..28b0021 --- /dev/null +++ b/sdr.js/exported_functions.py @@ -0,0 +1,81 @@ +""" +This software is part of libcsdr, a set of simple DSP routines for +Software Defined Radio. + +Copyright (c) 2014, Andras Retzler +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ANDRAS RETZLER BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""" + + +exported_functions= \ +"""firdes_lowpass_f +firdes_bandpass_c +firdes_wkernel_blackman +firdes_wkernel_hamming +firdes_wkernel_boxcar +firdes_get_window_from_string +firdes_get_string_from_window +firdes_filter_len +fmdemod_quadri_cf +fmdemod_quadri_novect_cf +fmdemod_atan_cf +amdemod_cf +amdemod_estimator_cf +limit_ff +fir_decimate_cc +deemphasis_nfm_ff +deemphasis_wfm_ff +shift_math_cc +dcblock_ff +fastdcblock_ff +fastagc_ff +rational_resampler_ff +rational_resampler_get_lowpass_f +apply_window_c +apply_window_f +logpower_cf +fractional_decimator_ff +shift_table_deinit +shift_table_init +shift_table_cc +log2n +next_pow2 +apply_fir_fft_cc +gain_ff +convert_u8_f +convert_f_u8 +convert_f_i16 +convert_i16_f +shift_addition_init +shift_addition_cc +shift_addition_cc_test +agc_ff +decimating_shift_addition_cc +decimating_shift_addition_init +encode_ima_adpcm_i16_u8 +decode_ima_adpcm_u8_i16""" + +exported_functions_quoted=map(lambda x:"'_"+x+"'",exported_functions.split("\n")) +print "["+(", ".join(exported_functions_quoted))+"]" diff --git a/sdr.js/sdrjs-footer.js b/sdr.js/sdrjs-footer.js new file mode 100644 index 0000000..21ea992 --- /dev/null +++ b/sdr.js/sdrjs-footer.js @@ -0,0 +1,296 @@ +// ========================================================== +// ========= / THE CODE COMPILED BY EMCC ENDS HERE ========== +// ========================================================== + +asm$ = +{ + malloc: function(type, size) + { + real_size=size*type.BYTES_PER_ELEMENT; + pointer = Module._malloc(real_size); + heap = new Uint8Array(Module.HEAPU8.buffer, pointer, real_size); + return { + asm$: true, + ptr: heap.byteOffset, + free: function() { Module._free(this.ptr); }, + arr: new type(heap.buffer, heap.byteOffset, size), + size: size + }; + }, + cpy: function(dst, dst_offset, src, src_offset, size) + { + if(typeof dst.asm$!='undefined') dst=dst.arr; + if(typeof src.asm$!='undefined') src=src.arr; + for(var i=0;i rwithin"); + for(var i=0;i this.buffer_size) + { + return new Float32Array(0); console.log("sdrjs.RationalResamplerFF: critical audio buffering error"); //This should not happen... + /* console.log("RationalResamplerFF: splitting..."); //TODO: this branch has not been checked + output_buffers=Array(); + new_buffer_size=this.buffer_size/2; + i=0; + //process the input in chunks of new_buffer_size, and add the output product Float32Array-s to output_buffers. + while((i++)*new_buffer_size<=input.length) + { + output_buffers.push(this._process_noheapcheck(input.subarray(i*new_buffer_size,(i+1)*new_buffer_size))); + } + //add up the sizes of the output_buffer-s. + total_output_length=0; + output_buffers.forEach(function(a){total_output_length+=a.length;}); + //create one big buffer from concatenating the output_buffer-s + output=new Float32Array(total_output_length); + output_pos=0; + output_buffers.forEach(function(a){ + asm$.cpy(output,output_pos,a,0,a.length); + output_pos+=a.length; + }); + return output;*/ + } + else return this._process_noheapcheck(input); + }; + this._process_noheapcheck=function(input) //if we are sure we have enough space in the buffers + { + asm$.cpy(this.input_buffer.arr,0,this.input_buffer.arr,this.remain_offset,this.remain); + asm$.cpy(this.input_buffer.arr, this.remain, input, 0, input.length); + var total_input_size=input.length+this.remain; + d=rational_resampler_ff(this.input_buffer.ptr, this.output_buffer.ptr, total_input_size, this.interpolation, this.decimation, this.taps.ptr, this.taps_length, this.last_taps_delay); + this.last_taps_delay=d.last_taps_delay; + this.remain=total_input_size-d.input_processed; + this.remain_offset=d.input_processed; + var output_copy_arr=new Float32Array(d.output_size); + asm$.cpy(output_copy_arr,0,this.output_buffer.arr,0,d.output_size); + return output_copy_arr; + }; +}; + + +_sdrjs_logb=function(what) { document.body.innerHTML+=what+"
"; } + + +function test_firdes_lowpass_f_original() +{ + //Original method explained over here: + //http://kapadia.github.io/emscripten/2013/09/13/emscripten-pointers-and-pointers.html + _sdrjs_logb("test_firdes_lowpass_f_original():"); + _sdrjs_logb("Now designing FIR filter with firdes_lowpass_f in sdr.js..."); + _sdrjs_logb("output should be the same as: csdr firdes_lowpass_f 0.1 101 HAMMING"); + + var outputSize = 101*4; + var outputPtr = Module._malloc(outputSize); + var outputHeap = new Uint8Array(Module.HEAPU8.buffer, outputPtr, outputSize); + firdes_lowpass_f(outputHeap.byteOffset,101,0.1,2); + var output = new Float32Array(outputHeap.buffer, outputHeap.byteOffset, 101); + outputStr=String(); + for(i=0;icsdr firdes_lowpass_f 0.1 101 HAMMING"); + + output=asm$.malloc(Float32Array,101); + firdes_lowpass_f(output.ptr,101,0.1,2); + outputStr=String(); + for(i=0;i