Merged origin/master into nmux

This commit is contained in:
ha7ilm 2017-01-19 17:00:16 +01:00
commit 3246e20864
6 changed files with 736 additions and 311 deletions

View file

@ -29,7 +29,7 @@
LIBSOURCES = fft_fftw.c libcsdr_wrapper.c
#SOURCES = csdr.c $(LIBSOURCES)
cpufeature = $(if $(findstring $(1),$(shell cat /proc/cpuinfo)),$(2))
PARAMS_SSE = $(call cpufeature,sse,-msse) $(call cpufeature,sse2,-msse2) $(call cpufeature,sse3,-msse3) $(call cpufeature,sse4,-msse4) $(call cpufeature,sse4_1,-msse4.1) $(call cpufeature,sse4_2,-msse4.2) -mfpmath=sse
PARAMS_SSE = $(call cpufeature,sse,-msse) $(call cpufeature,sse2,-msse2) $(call cpufeature,sse3,-msse3) $(call cpufeature,sse4a,-msse4a) $(call cpufeature,sse4_1,-msse4.1) $(call cpufeature,sse4_2,-msse4.2 -msse4) -mfpmath=sse
PARAMS_NEON = -mfloat-abi=hard -march=armv7-a -mtune=cortex-a8 -mfpu=neon -mvectorize-with-neon-quad -funsafe-math-optimizations -Wformat=0 -DNEON_OPTS
#tnx Jan Szumiec for the Raspberry Pi support
PARAMS_RASPI = -mfloat-abi=hard -mcpu=arm1176jzf-s -mfpu=vfp -funsafe-math-optimizations -Wformat=0
@ -92,7 +92,7 @@ emcc-get-deps:
emmake make; \
emmake make install
emcc:
emcc -O3 -Isdr.js/$(FFTW_PACKAGE)/api -Lsdr.js/$(FFTW_PACKAGE)/emscripten-lib -o sdr.js/sdrjs-compiled.js fft_fftw.c libcsdr_wrapper.c -DLIBCSDR_GPL -DUSE_IMA_ADPCM -DUSE_FFTW -lfftw3f -s EXPORTED_FUNCTIONS="`python sdr.js/exported_functions.py`"
emcc -O3 -Isdr.js/$(FFTW_PACKAGE)/api -Lsdr.js/$(FFTW_PACKAGE)/emscripten-lib -o sdr.js/sdrjs-compiled.js fft_fftw.c libcsdr_wrapper.c -s TOTAL_MEMORY=67108864 -DLIBCSDR_GPL -DUSE_IMA_ADPCM -DUSE_FFTW -lfftw3f -s EXPORTED_FUNCTIONS="`python sdr.js/exported_functions.py`"
cat sdr.js/sdrjs-header.js sdr.js/sdrjs-compiled.js sdr.js/sdrjs-footer.js > sdr.js/sdr.js
emcc-beautify:
bash -c 'type js-beautify >/dev/null 2>&1; if [ $$? -eq 0 ]; then js-beautify sdr.js/sdr.js >sdr.js/sdr.js.beautiful; mv sdr.js/sdr.js.beautiful sdr.js/sdr.js; fi'

118
README.md
View file

@ -12,14 +12,22 @@ Most of the code is available under the permissive BSD license, with some option
How to compile
--------------
The project was only tested on Linux. It has the following dependencies: `libfftw3-dev`
make
sudo make install
The project was only tested on Linux. It has the following dependencies: `libfftw3-dev`
If you compile on ARM, please edit the Makefile and tailor `PARAMS_NEON` for your CPU.
To run the examples, you will also need <a href="http://sdr.osmocom.org/trac/wiki/rtl-sdr">rtl_sdr</a> from Osmocom, and the following packages (at least on Debian): `mplayer octave gnuplot gnuplot-x11`
If you compile *fftw3* from sources for use with *libcsdr*, you need to configure it with 32-bit float support enabled:
./configure --enable-float
(This is for *fftw3*, not *libcsdr*. You do not need to run the configure script before compiling *libcsdr*.)
Credits
-------
The library was written by Andras Retzler, HA7ILM &lt;<randras@sdr.hu>&gt;.
@ -31,7 +39,7 @@ Usage by example
### Demodulate WFM
rtl_sdr -s 240000 -f 89500000 -g 20 - | csdr convert_u8_f | csdr fmdemod_quadri_cf | csdr fractional_decimator_ff 5 | csdr deemphasis_wfm_ff 48000 50e-6 | csdr convert_f_i16 | mplayer -cache 1024 -quiet -rawaudio samplesize=2:channels=1:rate=48000 -demuxer rawaudio -
rtl_sdr -s 240000 -f 89500000 -g 20 - | csdr convert_u8_f | csdr fmdemod_quadri_cf | csdr fractional_decimator_ff 5 | csdr deemphasis_wfm_ff 48000 50e-6 | csdr convert_f_s16 | mplayer -cache 1024 -quiet -rawaudio samplesize=2:channels=1:rate=48000 -demuxer rawaudio -
- Baseband I/Q signal is coming from an RTL-SDR USB dongle, with a center frequency of `-f 104300000` Hz, a sampling rate of `-s 240000` samples per second.
- The `rtl_sdr` tool outputs an unsigned 8-bit I/Q signal (one byte of I sample and one byte of Q coming after each other), but `libcsdr` DSP routines internally use floating point data type, so we convert the data stream of `unsigned char` to `float` by `csdr convert_u8_f`.
@ -43,7 +51,7 @@ Usage by example
### Demodulate WFM: advanced
rtl_sdr -s 2400000 -f 89300000 -g 20 - | csdr convert_u8_f | csdr shift_addition_cc -0.085 | csdr fir_decimate_cc 10 0.05 HAMMING | csdr fmdemod_quadri_cf | csdr fractional_decimator_ff 5 | csdr deemphasis_wfm_ff 48000 50e-6 | csdr convert_f_i16 | mplayer -cache 1024 -quiet -rawaudio samplesize=2:channels=1:rate=48000 -demuxer rawaudio -
rtl_sdr -s 2400000 -f 89300000 -g 20 - | csdr convert_u8_f | csdr shift_addition_cc -0.085 | csdr fir_decimate_cc 10 0.05 HAMMING | csdr fmdemod_quadri_cf | csdr fractional_decimator_ff 5 | csdr deemphasis_wfm_ff 48000 50e-6 | csdr convert_f_s16 | mplayer -cache 1024 -quiet -rawaudio samplesize=2:channels=1:rate=48000 -demuxer rawaudio -
- We want to listen to one radio station, but input signal contains multiple stations, and its bandwidth is too large for sending it directly to the FM demodulator.
- We shift the signal to the center frequency of the station we want to receive: `-0.085*2400000 = -204000`, so basically we will listen to the radio station centered at 89504000 Hz.
@ -64,7 +72,7 @@ The first parameter is the frequency in MHz, and the second optional parameter i
### Demodulate NFM
rtl_sdr -s 2400000 -f 145000000 -g 20 - | csdr convert_u8_f | csdr shift_addition_cc `python -c "print float(145000000-145350000)/2400000"` | csdr fir_decimate_cc 50 0.005 HAMMING | csdr fmdemod_quadri_cf | csdr limit_ff | csdr deemphasis_nfm_ff 48000 | csdr fastagc_ff | csdr convert_f_i16 | mplayer -cache 1024 -quiet -rawaudio samplesize=2:channels=1:rate=48000 -demuxer rawaudio -
rtl_sdr -s 2400000 -f 145000000 -g 20 - | csdr convert_u8_f | csdr shift_addition_cc `python -c "print float(145000000-145350000)/2400000"` | csdr fir_decimate_cc 50 0.005 HAMMING | csdr fmdemod_quadri_cf | csdr limit_ff | csdr deemphasis_nfm_ff 48000 | csdr fastagc_ff | csdr convert_f_s16 | mplayer -cache 1024 -quiet -rawaudio samplesize=2:channels=1:rate=48000 -demuxer rawaudio -
- Note that the decimation factor is higher (we want to select a ~25 kHz channel).
- Also there is a python hack to calculate the relative shift offset. The real receiver frequency is `145350000` Hz.
@ -72,7 +80,7 @@ The first parameter is the frequency in MHz, and the second optional parameter i
### Demodulate AM
rtl_sdr -s 2400000 -f 145000000 -g 20 - | csdr convert_u8_f | csdr shift_addition_cc `python -c "print float(145000000-144400000)/2400000"` | csdr fir_decimate_cc 50 0.005 HAMMING | csdr amdemod_cf | csdr fastdcblock_ff | csdr agc_ff | csdr limit_ff | csdr convert_f_i16 | mplayer -cache 1024 -quiet -rawaudio samplesize=2:channels=1:rate=48000 -demuxer rawaudio -
rtl_sdr -s 2400000 -f 145000000 -g 20 - | csdr convert_u8_f | csdr shift_addition_cc `python -c "print float(145000000-144400000)/2400000"` | csdr fir_decimate_cc 50 0.005 HAMMING | csdr amdemod_cf | csdr fastdcblock_ff | csdr agc_ff | csdr limit_ff | csdr convert_f_s16 | mplayer -cache 1024 -quiet -rawaudio samplesize=2:channels=1:rate=48000 -demuxer rawaudio -
- `amdemod_cf` is used as demodulator.
- `agc_ff` should be used for AM and SSB.
@ -87,7 +95,7 @@ The first parameter is the frequency in MHz, and the second optional parameter i
### Demodulate SSB
rtl_sdr -s 2400000 -f 145000000 -g 20 - | csdr convert_u8_f | csdr shift_addition_cc `python -c "print float(145000000-144400000)/2400000"` | csdr fir_decimate_cc 50 0.005 HAMMING | csdr bandpass_fir_fft_cc 0 0.1 0.05 | csdr realpart_cf | csdr agc_ff | csdr limit_ff | csdr convert_f_i16 | mplayer -cache 1024 -quiet -rawaudio samplesize=2:channels=1:rate=48000 -demuxer rawaudio -
rtl_sdr -s 2400000 -f 145000000 -g 20 - | csdr convert_u8_f | csdr shift_addition_cc `python -c "print float(145000000-144400000)/2400000"` | csdr fir_decimate_cc 50 0.005 HAMMING | csdr bandpass_fir_fft_cc 0 0.1 0.05 | csdr realpart_cf | csdr agc_ff | csdr limit_ff | csdr convert_f_s16 | mplayer -cache 1024 -quiet -rawaudio samplesize=2:channels=1:rate=48000 -demuxer rawaudio -
- It is a modified Weaver-demodulator. The complex FIR filter removes the lower sideband and lets only the upper pass (USB). If you want to demodulate LSB, change `bandpass_fir_fft_cc 0 0.05` to `bandpass_fir_fft_cc -0.05 0`.
@ -109,7 +117,7 @@ Data types are noted as it follows:
- `f` is `float` (single percision)
- `c` is `complexf` (two single precision floating point values in a struct)
- `u8` is `unsigned char` of 1 byte/8 bits (e. g. the output of `rtl_sdr` is of `u8`)
- `i16` is `signed short` of 2 bytes/16 bits (e. g. sound card input is usually `i16`)
- `s16` is `signed short` of 2 bytes/16 bits (e. g. sound card input is usually `s16`)
Functions usually end as:
@ -124,12 +132,14 @@ The following commands are available:
- `csdr convert_f_u8`
- `csdr convert_s8_f`
- `csdr convert_f_s8`
- `csdr convert_i16_f`
- `csdr convert_f_i16`
- `csdr convert_s16_f`
- `csdr convert_f_s16`
How to interpret: `csdr convert_<src>_<dst>`
You can use these commands on complex streams, too, as they are only interleaved values (I,Q,I,Q,I,Q... coming after each other).
> Note: The the functions with `i16` in their names have been renamed, but still work (e.g. `csdr convert_f_i16`).
#### csdr commands
`csdr` should be considered as a reference implementation on using `libcsdr`. For additional details on how to use the library, check `csdr.c` and `libcsdr.c`.
@ -157,6 +167,10 @@ It multiplies all samples by `gain`.
It copies the input to the output.
through
It copies the input to the output, while also displaying the speed of the data going through it.
none
The `csdr` process just exits with 0.
@ -363,11 +377,45 @@ The actual number of padding samples can be determined by running `cat csdr.c |
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`.
dsb_fc [q_value]
It converts a real signal to a double sideband complex signal centered around DC.
It does so by generating a complex signal:
* the real part of which is the input real signal,
* the imaginary part of which is `q_value` (0 by default).
With `q_value = 0` it is an AM-DSB/SC modulator. If you want to get an AM-DSB signal, you will have to add a carrier to it.
add_dcoffset_cc
It adds a DC offset to the complex signal: `i_output = 0.5 + i_input / 2, q_output = q_input / 2`
convert_f_samplerf <wait_for_this_sample>
It converts a real signal to the `-mRF` input format of [https://github.com/F5OEO/rpitx](rpitx), so it allows you to generate frequency modulation. The input signal will be the modulating signal. The `<wait_for_this_sample>` parameter is the value for `rpitx` indicating the time to wait between samples. For a sampling rate of 48 ksps, this is 20833.
fmmod_fc
It generates a complex FM modulated output from a real input signal.
fixed_amplitude_cc <new_amplitude>
It changes the amplitude of every complex input sample to a fixed value. It does not change the phase information of the samples.
mono2stereo_s16
It doubles every input sample.
setbuf <buffer_size>
If the environment variable `CSDR_DYNAMIC_BUFSIZE_ON` is set to 1, then you can use this command to set the input buffer size for the next `csdr` process in the chain.
See the [buffer sizes](#buffer_sizes) section.
squelch_and_smeter_cc --fifo <squelch_fifo> --outfifo <smeter_fifo> <use_every_nth> <report_every_nth>
This is a controllable squelch, which reads the squelch level input from `<squelch_fifo>` and writes the power level output to `<smeter_fifo>`. Both input and output are in the format of `%g\n`. While calculating the power level, it takes only every `<use_every_nth>` sample into consideration. It writes the S-meter value for every `<report_every_nth>` buffer to `<smeter_fifo>`. If the squelch level is set to 0, it it forces the squelch to be open. If the squelch is closed, it fills the output with zero.
fifo <buffer_size> <number_of_buffers>
It is similar to `clone`, but internally it uses a circular buffer. It reads as much as possible from the input. It discards input samples if the input buffer is full.
#### Control via pipes
@ -391,6 +439,56 @@ By writing to the given FIFO file with the syntax below, you can control the shi
E.g. you can send `-0.05 0.02\n`
#### Buffer sizes
*csdr* has three modes of determining the buffer sizes, which can be chosen by the appropriate environment variables:
* *default:* 16k or 1k buffer is chosen based on function,
* *dynamic buffer size determination:* input buffer size is recommended by the previous process, output buffer size is determined by the process,
* *fixed buffer sizes*.
*csdr* can choose from two different buffer sizes by **default**.
* For operations handling the full-bandwidth I/Q data from the receiver, a buffer size of 16384 samples is used (see `env_csdr_fixed_big_bufsize` in the code).
* For operations handling only a selected channel, a buffer size of 1024 samples is used (see `env_csdr_fixed_bufsize` in the code).
*csdr* now has an experimental feature called **dynamic buffer size determination**, which is switched on by issuing `export CSDR_DYNAMIC_BUFSIZE_ON=1` in the shell before running `csdr`. If it is enabled:
* All `csdr` processes in a DSP chain acquire their recommended input buffer size from the previous `csdr` process. This information is in the first 8 bytes of the input stream.
* Each process can decide whether to use this or choose another input buffer size (if that's more practical).
* Every process sends out its output buffer size to the next process. Then it startss processing data.
* The DSP chain should start with a `csdr setbuf <buffer_size>` process, which only copies data from the input to the output, but also sends out the given buffer size information to the next process.
* The 8 bytes of information included in the beginning of the stream is:
* a preamble of the bytes 'c','s','d','r' (4 bytes),
* the buffer size stored as `int` (4 bytes).
* This size always counts as samples, as we expect that the user takes care of connecting the functions with right data types to each other.
> I added this feature while researching how to decrease the latency of a DSP chain consisting of several multirate algorithms.<br />
> For example, a `csdr fir_decimate_cc 10` would use an input buffer of 10240, and an output buffer of 1024. The next process in the chain, `csdr bandpass_fir_fft_cc` would automatically adjust to it, using a buffer of 1024 for both input and output.<br />
> In contrast to original expectations, using dynamic buffer sizes didn't decrease the latency much.
If dynamic buffer size determination is disabled, you can still set a **fixed buffer size** with `export CSDR_FIXED_BUFSIZE=<buffer_size>`.
For debug purposes, buffer sizes of all processes can be printed using `export CSDR_PRINT_BUFSIZES=1`.
If you add your own functions to `csdr`, you have to initialize the buffers before doing the processing. Buffer size will be stored in the global variable `the_bufsize`.
Example of initialization if the process generates N output samples for N input samples:
if(!sendbufsize(initialize_buffers())) return -2;
Example of initalization if the process generates N/D output samples for N input samples:
if(!initialize_buffers()) return -2;
sendbufsize(the_bufsize/D);
Example of initialization if the process allocates memory for itself, and it doesn't want to use the global buffers:
getbufsize(); //dummy
sendbufsize(my_own_bufsize);
Example of initialization if the process always works with a fixed output size, regardless of the input:
if(!initialize_buffers()) return -2;
sendbufsize(fft_size);
#### Testbench
`csdr` was tested with GNU Radio Companion flowgraphs. These flowgraphs are available under the directory `grc_tests`, and they require the <a href="https://github.com/simonyiszk/gr-ha5kfu">gr-ha5kfu</a> set of blocks for GNU Radio.

276
csdr.c
View file

@ -49,6 +49,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sched.h>
#include <math.h>
#include <strings.h>
#include <errno.h>
#include "fastddc.h"
char usage[]=
@ -60,8 +61,10 @@ char usage[]=
" convert_f_u8\n"
" convert_s8_f\n"
" convert_f_s8\n"
" convert_f_i16\n"
" convert_i16_f\n"
" convert_f_s16\n"
" convert_s16_f\n"
" convert_f_s24 [--bigendian]\n"
" convert_s24_f [--bigendian]\n"
" realpart_cf\n"
" clipdetect_ff\n"
" limit_ff [max_amplitude]\n"
@ -73,7 +76,9 @@ char usage[]=
" floatdump_f\n"
" flowcontrol <data_rate> <reads_per_second> [prebuffer_sec] [thrust]\n"
" shift_math_cc <rate>\n"
" shift_math_cc --fifo <fifo_path>\n"
" shift_addition_cc <rate>\n"
" shift_addition_cc --fifo <fifo_path>\n"
" shift_addition_cc_test\n"
" shift_table_cc <rate> [table_size]\n"
" decimating_shift_addition_cc <rate> [decimation]\n"
@ -97,10 +102,21 @@ char usage[]=
" logpower_cf [add_db]\n"
" fft_benchmark <fft_size> <fft_cycles> [--benchmark]\n"
" bandpass_fir_fft_cc <low_cut> <high_cut> <transition_bw> [window]\n"
" encode_ima_adpcm_i16_u8\n"
" decode_ima_adpcm_u8_i16\n"
" bandpass_fir_fft_cc --fifo <fifo_path> <transition_bw> [window]\n"
" encode_ima_adpcm_s16_u8\n"
" decode_ima_adpcm_u8_s16\n"
" compress_fft_adpcm_f_u8 <fft_size>\n"
" flowcontrol <data_rate> <reads_per_second>\n"
" through\n"
" dsb_fc [q_value]\n"
" convert_f_samperf <wait_for_this_sample> \n"
" fmmod_fc\n"
" fixed_amplitude_cc <new_amplitude>\n"
" monos2stereo_s16\n"
" setbuf <buffer_size>\n"
" fft_exchange_sides_ff <fft_size>\n"
" squelch_and_smeter_cc --fifo <squelch_fifo> --outfifo <smeter_fifo> <use_every_nth> <report_every_nth>\n"
" fifo <buffer_size> <number_of_buffers>\n"
" \n"
;
@ -306,7 +322,7 @@ int parse_env()
envtmp=getenv("CSDR_FIXED_BUFSIZE");
if(envtmp)
{
env_csdr_fixed_bufsize = atoi(envtmp);
env_csdr_fixed_big_bufsize = env_csdr_fixed_bufsize = atoi(envtmp);
}
}
envtmp=getenv("CSDR_PRINT_BUFSIZES");
@ -341,6 +357,94 @@ int main(int argc, char *argv[])
if(!sendbufsize(initialize_buffers())) return -2;
clone_(the_bufsize);
}
#define SET_NONBLOCK(fd) fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK)
if(!strcmp(argv[1],"fifo"))
{
if(!sendbufsize(initialize_buffers())) return -2;
int fifo_buffer_size;
if(argc<=2) return badsyntax("need required parameter (buffer_size)");
sscanf(argv[2],"%d",&fifo_buffer_size);
int fifo_num_buffers;
if(argc<=3) return badsyntax("need required parameter (number of buffers)");
sscanf(argv[3],"%d",&fifo_num_buffers);
char** fifo_buffers = (char**)malloc(sizeof(char*)*fifo_num_buffers);
for(int i=0;i<fifo_num_buffers;i++) fifo_buffers[i]=(char*)malloc(sizeof(char)*fifo_buffer_size);
SET_NONBLOCK(STDIN_FILENO);
SET_NONBLOCK(STDOUT_FILENO);
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(STDIN_FILENO, &read_fds);
fd_set write_fds;
FD_ZERO(&write_fds);
FD_SET(STDOUT_FILENO, &write_fds);
int highfd = ((STDOUT_FILENO > STDIN_FILENO) ? STDOUT_FILENO : STDIN_FILENO) + 1;
int fifo_actual_buffer_wr = fifo_num_buffers - 1;
int fifo_actual_buffer_rd = 0;
int fifo_actual_buffer_wr_pos = 0;
int fifo_actual_buffer_rd_pos = 0;
int fifo_error = 0;
int fifo_overrun_shown = 0;
for(;;)
{
select(highfd, &read_fds, NULL, NULL, NULL);
//try to read until buffer is full
if(FD_ISSET(STDIN_FILENO, &read_fds)) for(;;)
{
int read_bytes=read(STDIN_FILENO, fifo_buffers[fifo_actual_buffer_rd]+fifo_actual_buffer_rd_pos, fifo_buffer_size-fifo_actual_buffer_rd_pos);
//fprintf(stderr, "r %d %d | %d %d\n", read_bytes, fifo_buffer_size-fifo_actual_buffer_rd_pos, fifo_actual_buffer_rd, fifo_actual_buffer_rd_pos);
if(!read_bytes || ((read_bytes<0)&&(fifo_error=read_bytes)) ) break;
fifo_actual_buffer_rd_pos+=read_bytes;
if(!((fifo_actual_buffer_rd==fifo_actual_buffer_wr-1)||(fifo_actual_buffer_wr==0&&fifo_actual_buffer_rd==fifo_num_buffers-1)))
{
if(fifo_actual_buffer_rd_pos==fifo_buffer_size)
{
fifo_overrun_shown = 0;
fifo_actual_buffer_rd++;
fifo_actual_buffer_rd_pos = 0;
if(fifo_actual_buffer_rd>=fifo_num_buffers) fifo_actual_buffer_rd=0;
}
}
else
{
if(fifo_actual_buffer_rd_pos==fifo_buffer_size)
{
fifo_actual_buffer_rd_pos = 0; //rewrite same buffer
if(!fifo_overrun_shown) { fifo_overrun_shown=1; fprintf(stderr, "fifo: circular buffer full, dropping samples\n"); }
}
}
}
//try to write until buffer is empty
if(FD_ISSET(STDOUT_FILENO, &write_fds)) for(;;)
{
if(fifo_actual_buffer_wr == fifo_actual_buffer_rd) break;
int written_bytes=write(STDOUT_FILENO, fifo_buffers[fifo_actual_buffer_wr]+fifo_actual_buffer_wr_pos, fifo_buffer_size-fifo_actual_buffer_wr_pos);
//fprintf(stderr, "w %d %d | %d %d\n", written_bytes, fifo_buffer_size-fifo_actual_buffer_wr_pos, fifo_actual_buffer_wr, fifo_actual_buffer_wr_pos);
if(!written_bytes || ((written_bytes<0)&&(fifo_error=written_bytes)) ) break;
fifo_actual_buffer_wr_pos+=written_bytes;
if(fifo_actual_buffer_wr_pos==fifo_buffer_size)
{
fifo_actual_buffer_wr++;
fifo_actual_buffer_wr_pos = 0;
if(fifo_actual_buffer_wr>=fifo_num_buffers) fifo_actual_buffer_wr=0;
}
}
if(fifo_error&&errno!=11) { fprintf(stderr,"fifo: fifo_error (%d)", errno); return -1; }
}
return -1;
}
if(!strcmp(argv[1],"convert_u8_f"))
{
@ -368,27 +472,29 @@ int main(int argc, char *argv[])
}
if(!strcmp(argv[1],"convert_s8_f"))
{
if(!sendbufsize(initialize_buffers())) return -2;
for(;;)
{
FEOF_CHECK;
fread(buffer_s8, sizeof(signed char), BUFSIZE, stdin);
convert_s8_f(buffer_s8, output_buffer, BUFSIZE);
fread((signed char*)buffer_u8, sizeof(signed char), the_bufsize, stdin);
convert_s8_f((signed char*)buffer_u8, output_buffer, the_bufsize);
FWRITE_R;
TRY_YIELD;
}
}
if(!strcmp(argv[1],"convert_f_s8")) //not tested
{
if(!sendbufsize(initialize_buffers())) return -2;
for(;;)
{
FEOF_CHECK;
FREAD_R;
convert_f_s8(input_buffer, buffer_s8, BUFSIZE);
fwrite(buffer_s8, sizeof(signed char), BUFSIZE, stdout);
convert_f_s8(input_buffer, (signed char*)buffer_u8, the_bufsize);
fwrite((signed char*)buffer_u8, sizeof(signed char), the_bufsize, stdout);
TRY_YIELD;
}
}
if(!strcmp(argv[1],"convert_f_i16"))
if((!strcmp(argv[1],"convert_f_i16")) || (!strcmp(argv[1],"convert_f_s16")))
{
if(!sendbufsize(initialize_buffers())) return -2;
for(;;)
@ -400,7 +506,7 @@ int main(int argc, char *argv[])
TRY_YIELD;
}
}
if(!strcmp(argv[1],"convert_i16_f")) //not tested
if((!strcmp(argv[1],"convert_i16_f")) || (!strcmp(argv[1],"convert_s16_f")))
{
if(!sendbufsize(initialize_buffers())) return -2;
for(;;)
@ -412,6 +518,34 @@ int main(int argc, char *argv[])
TRY_YIELD;
}
}
if(!strcmp(argv[1],"convert_f_s24"))
{
int bigendian = (argc>2) && (!strcmp(argv[2],"--bigendian"));
unsigned char* s24buffer = (unsigned char*)malloc(sizeof(unsigned char)*the_bufsize*3);
if(!sendbufsize(initialize_buffers())) return -2;
for(;;)
{
FEOF_CHECK;
FREAD_R;
convert_f_s24(input_buffer, s24buffer, the_bufsize, bigendian);
fwrite(s24buffer, sizeof(unsigned char)*3, the_bufsize, stdout);
TRY_YIELD;
}
}
if(!strcmp(argv[1],"convert_s24_f"))
{
int bigendian = (argc>2) && (!strcmp(argv[2],"--bigendian"));
unsigned char* s24buffer = (unsigned char*)malloc(sizeof(unsigned char)*the_bufsize*3);
if(!sendbufsize(initialize_buffers())) return -2;
for(;;)
{
FEOF_CHECK;
fread(s24buffer, sizeof(unsigned char)*3, the_bufsize, stdin);
convert_s24_f(s24buffer, output_buffer, the_bufsize, bigendian);
FWRITE_R;
TRY_YIELD;
}
}
if(!strcmp(argv[1],"realpart_cf"))
{
if(!sendbufsize(initialize_buffers())) return -2;
@ -906,11 +1040,15 @@ int main(int argc, char *argv[])
}
else fprintf(stderr,"fir_decimate_cc: window = %s\n",firdes_get_string_from_window(window));
int taps_length=firdes_filter_len(transition_bw);
fprintf(stderr,"fir_decimate_cc: taps_length = %d\n",taps_length);
while (env_csdr_fixed_big_bufsize < taps_length*2) env_csdr_fixed_big_bufsize*=2; //temporary fix for buffer size if [transition_bw] is low
//fprintf(stderr, "env_csdr_fixed_big_bufsize = %d\n", env_csdr_fixed_big_bufsize);
if(!initialize_buffers()) return -2;
sendbufsize(the_bufsize/factor);
int taps_length=firdes_filter_len(transition_bw);
fprintf(stderr,"fir_decimate_cc: taps_length = %d\n",taps_length);
int padded_taps_length = taps_length;
float *taps;
@ -1255,6 +1393,8 @@ int main(int argc, char *argv[])
FFT_PLAN_T* plan=make_fft_c2c(fft_size, windowed, output, 1, benchmark);
if(benchmark) fprintf(stderr," done\n");
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(;;)
{
FEOF_CHECK;
@ -1273,7 +1413,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];
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);
if(octave)
{
@ -1309,6 +1450,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(argc<=2) return badsyntax("need required parameters (fft_size)");
@ -1409,7 +1584,6 @@ int main(int argc, char *argv[])
float high_cut;
float transition_bw;
window_t window = WINDOW_DEFAULT;
int fd;
if(fd=init_fifo(argc,argv))
{
@ -1485,7 +1659,7 @@ int main(int argc, char *argv[])
#ifdef USE_IMA_ADPCM
#define IMA_ADPCM_BUFSIZE BUFSIZE
if(!strcmp(argv[1],"encode_ima_adpcm_i16_u8"))
if( (!strcmp(argv[1],"encode_ima_adpcm_i16_u8"))||(!strcmp(argv[1],"encode_ima_adpcm_s16_u8")) )
{
if(!sendbufsize(initialize_buffers()/2)) return -2;
ima_adpcm_state_t d;
@ -1500,7 +1674,7 @@ int main(int argc, char *argv[])
}
}
if(!strcmp(argv[1],"decode_ima_adpcm_u8_i16"))
if( (!strcmp(argv[1],"decode_ima_adpcm_u8_i16"))||(!strcmp(argv[1],"decode_ima_adpcm_u8_s16")) )
{
ima_adpcm_state_t d;
d.index=d.previousValue=0;
@ -1515,7 +1689,7 @@ int main(int argc, char *argv[])
}
}
#endif
/*
if(!strcmp(argv[1],"flowcontrol"))
{
if(argc<=3) return badsyntax("need required parameters (data_rate, reads_per_seconds)");
@ -1537,9 +1711,9 @@ int main(int argc, char *argv[])
usleep(flowcontrol_sleep);
TRY_YIELD;
}
}*/
}
#if 0
if(!strcmp(argv[1],"flowcontrol"))
{
if(argc<=3) return badsyntax("need required parameters (data_rate, reads_per_seconds)");
@ -1638,6 +1812,7 @@ int main(int argc, char *argv[])
TRY_YIELD;
}
}
#endif
if(!strcmp(argv[1],"through"))
{
@ -1767,7 +1942,7 @@ int main(int argc, char *argv[])
}
}
if(!strcmp(argv[1],"mono2stereo_i16"))
if((!strcmp(argv[1],"mono2stereo_i16"))||(!strcmp(argv[1],"mono2stereo_s16")))
{
if(!sendbufsize(initialize_buffers())) return -2;
float last_phase = 0;
@ -1785,6 +1960,59 @@ int main(int argc, char *argv[])
}
}
if(!strcmp(argv[1],"squelch_and_smeter_cc"))
{
if(!sendbufsize(initialize_buffers())) return -2;
float power;
float squelch_level;
int decimation;
int report_every_nth;
int fd;
char power_value_buf[101];
int power_value_buf_size;
int report_cntr=0;
complexf* zerobuf = (complexf*)malloc(sizeof(complexf)*the_bufsize);
for(int i=0;i<the_bufsize*2;i++) *(((float*)zerobuf)+i)=0;
if(fd=init_fifo(argc,argv)) while(!read_fifo_ctl(fd,"%g\n",&squelch_level)) usleep(10000);
else return badsyntax("need required parameter (--fifo <fifo>)");
fprintf(stderr, "squelch_and_power_cc: initial squelch level is %g\n", squelch_level);
if((argc<=5)||((argc>5)&&(strcmp(argv[4],"--outfifo")))) return badsyntax("need required parameter (--outfifo <fifo>)");
int fd2 = open(argv[5], O_WRONLY);
if(fd2==-1) return badsyntax("error while opening --outfifo");
int flags = fcntl(fd2, F_GETFL, 0);
fcntl(fd2, F_SETFL, flags | O_NONBLOCK);
if(argc<=6) return badsyntax("need required parameter (use_every_nth)");
sscanf(argv[6],"%d",&decimation);
if(decimation<=0) return badsyntax("use_every_nth <= 0 is invalid");
if(argc<=7) return badsyntax("need required parameter (report_every_nth)");
sscanf(argv[7],"%d",&report_every_nth);
if(report_every_nth<=0) return badsyntax("report_every_nth <= 0 is invalid");
for(;;)
{
FEOF_CHECK;
FREAD_C; //read input data
power = get_power_c((complexf*)input_buffer, the_bufsize, decimation);
if(report_cntr++>report_every_nth)
{
report_cntr=0;
power_value_buf_size=snprintf(power_value_buf,100,"%g\n",power);
write(fd2,power_value_buf,power_value_buf_size*sizeof(char));
}
if(squelch_level==0||power>=squelch_level)
{
//fprintf(stderr,"P");
fwrite(input_buffer, sizeof(complexf), the_bufsize, stdout);
}
else
{
//fprintf(stderr,"S");
fwrite(zerobuf, sizeof(complexf), the_bufsize, stdout);
}
if(read_fifo_ctl(fd,"%g\n",&squelch_level)) fprintf(stderr, "squelch_and_power_cc: new squelch level is %g\n", squelch_level);
TRY_YIELD;
}
}
if( !strcmp(argv[1],"fastddc_fwd_cc") ) //<decimation> [transition_bw [window]]
{
@ -1942,9 +2170,5 @@ int main(int argc, char *argv[])
return 0;
}
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;
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;
}

108
libcsdr.c
View file

@ -941,7 +941,7 @@ complexf fmdemod_quadri_cf(complexf* input, float* output, int input_size, float
}
for (int i=0; i<input_size; i++) //@fmdemod_quadri_cf: output division
{
output[i]=fmdemod_quadri_K*output[i]/temp[i];
output[i]=(temp[i])?fmdemod_quadri_K*output[i]/temp[i]:0;
}
return input[input_size-1];
@ -1018,6 +1018,26 @@ void gain_ff(float* input, float* output, int input_size, float gain)
for(int i=0;i<input_size;i++) output[i]=gain*input[i]; //@gain_ff
}
float get_power_f(float* input, int input_size, int decimation)
{
float acc = 0;
for(int i=0;i<input_size;i+=decimation)
{
acc += (input[i]*input[i])/input_size;
}
return acc;
}
float get_power_c(complexf* input, int input_size, int decimation)
{
float acc = 0;
for(int i=0;i<input_size;i+=decimation)
{
acc += (iof(input,i)*iof(input,i)+qof(input,i)*qof(input,i))/input_size;
}
return acc;
}
/*
__ __ _ _ _
| \/ | | | | | | |
@ -1110,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)
{
float (*window_function)(float)=firdes_get_window_kernel(window);
@ -1129,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
}
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
}
/*
_____ _ _
| __ \ | | (_)
@ -1149,9 +1205,9 @@ void convert_s8_f(signed char* input, float* output, int input_size)
for(int i=0;i<input_size;i++) output[i]=((float)input[i])/SCHAR_MAX; //@convert_s8_f
}
void convert_i16_f(short* input, float* output, int input_size)
void convert_s16_f(short* input, float* output, int input_size)
{
for(int i=0;i<input_size;i++) output[i]=(float)input[i]/SHRT_MAX; //@convert_i16_f
for(int i=0;i<input_size;i++) output[i]=(float)input[i]/SHRT_MAX; //@convert_s16_f
}
void convert_f_u8(float* input, unsigned char* output, int input_size)
@ -1166,16 +1222,56 @@ void convert_f_s8(float* input, signed char* output, int input_size)
for(int i=0;i<input_size;i++) output[i]=input[i]*SCHAR_MAX; //@convert_f_s8
}
void convert_f_i16(float* input, short* output, int input_size)
void convert_f_s16(float* input, short* output, int input_size)
{
/*for(int i=0;i<input_size;i++)
{
if(input[i]>1.0) input[i]=1.0;
if(input[i]<-1.0) input[i]=-1.0;
}*/
for(int i=0;i<input_size;i++) output[i]=input[i]*SHRT_MAX; //@convert_f_i16
for(int i=0;i<input_size;i++) output[i]=input[i]*SHRT_MAX; //@convert_f_s16
}
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_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()
{
//this function is trivial to vectorize and should pass on both NEON and SSE
@ -1186,5 +1282,3 @@ int trivial_vectorize()
}
return c[0];
}

View file

@ -137,9 +137,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);
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_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 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
{
@ -182,6 +186,8 @@ int log2n(int x);
int next_pow2(int x);
void apply_fir_fft_cc(FFT_PLAN_T* plan, FFT_PLAN_T* plan_inverse, complexf* taps_fft, complexf* last_overlap, int overlap_size);
void gain_ff(float* input, float* output, int input_size, float gain);
float get_power_f(float* input, int input_size, int decimation);
float get_power_c(complexf* input, int input_size, int decimation);
void add_dcoffset_cc(complexf* input, complexf* output, int input_size);
float fmmod_fc(float* input, complexf* output, int input_size, float last_phase);
@ -191,7 +197,12 @@ void convert_u8_f(unsigned char* input, float* output, int input_size);
void convert_f_u8(float* input, unsigned char* output, int input_size);
void convert_s8_f(signed char* input, float* output, int input_size);
void convert_f_s8(float* input, signed char* output, int input_size);
void convert_f_s16(float* input, short* 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_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);

View file

@ -219,10 +219,9 @@ float agc_ff(float* input, float* output, int input_size, float reference, float
}
gain=gain+dgain;
//fprintf(stderr,"g=%f dg=%f\n",gain,dgain);
if(gain>max_gain) gain=max_gain; //We also have to limit our gain, it can't be infinity.
if(gain<0) gain=0;
}
if(gain>max_gain) gain=max_gain; //We also have to limit our gain, it can't be infinity.
if(gain<0) gain=0;
//output[i]=gain*input[i]; //Here we do the actual scaling of the samples.
//Here we do the actual scaling of the samples, but we run an IIR filter on the gain values:
output[i]=(gain=gain+last_gain-gain_filter_alpha*last_gain)*input[i]; //dc-pass-filter: freqz([1 -1],[1 -0.99]) y[i]=x[i]+y[i-1]-alpha*x[i-1]
@ -234,4 +233,3 @@ float agc_ff(float* input, float* output, int input_size, float reference, float
}
#endif