diff --git a/.gitignore b/.gitignore
index 73f4345..3aad0c2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,5 +5,5 @@ ddcd
*.so
tags
dumpvect.*.vect
-grc_tests/top_block.py
*.swp
+grc_tests/top_block.py
diff --git a/Makefile b/Makefile
index 047630c..4db95f1 100644
--- a/Makefile
+++ b/Makefile
@@ -44,9 +44,10 @@ PARAMS_MISC = -Wno-unused-result
FFTW_PACKAGE = fftw-3.3.3
PREFIX ?= /usr
SOVERSION = 0.15
+PARSEVECT ?= yes
-.PHONY: clean-vect clean
-all: csdr nmux
+.PHONY: clean-vect clean codequality checkdocs v
+all: codequality csdr nmux
libcsdr.so: fft_fftw.c fft_rpi.c libcsdr_wrapper.c libcsdr.c libcsdr_gpl.c fastddc.c fastddc.h fft_fftw.h fft_rpi.h ima_adpcm.h libcsdr_gpl.h libcsdr.h predefined.h
@echo NOTE: you may have to manually edit Makefile to optimize for your CPU \(especially if you compile on ARM, please edit PARAMS_NEON\).
@echo Auto-detected optimization parameters: $(PARAMS_SIMD)
@@ -54,7 +55,9 @@ libcsdr.so: fft_fftw.c fft_rpi.c libcsdr_wrapper.c libcsdr.c libcsdr_gpl.c fastd
rm -f dumpvect*.vect
gcc -std=gnu99 $(PARAMS_LOOPVECT) $(PARAMS_SIMD) $(LIBSOURCES) $(PARAMS_LIBS) $(PARAMS_MISC) -fpic -shared -Wl,-soname,libcsdr.so.$(SOVERSION) -o libcsdr.so.$(SOVERSION)
@ln -fs libcsdr.so.$(SOVERSION) libcsdr.so
+ifeq ($(PARSEVECT),yes)
-./parsevect dumpvect*.vect
+endif
csdr: csdr.c libcsdr.so
gcc -std=gnu99 $(PARAMS_LOOPVECT) $(PARAMS_SIMD) csdr.c $(PARAMS_LIBS) -L. -lcsdr $(PARAMS_MISC) -o csdr
ddcd: ddcd.cpp libcsdr.so ddcd.h
@@ -99,3 +102,12 @@ emcc:
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'
+codequality:
+ @bash -c 'if [ `cat csdr.c | grep badsyntax | grep -v return | wc -l` -ne 1 ]; then echo "error at code quality check: badsyntax() used in csdr.c without return."; exit 1; else exit 0; fi'
+checkdocs:
+ @cat csdr.c | grep strcmp | egrep 'argv\[1\]' | awk -F'"' '$$0=$$2' > /tmp/csdr-list-of-functions
+ @cat /tmp/csdr-list-of-functions | xargs -I{} bash -c 'if ! cat csdr.c | grep \"\ \ \ \ {} >/dev/null ; then echo "warning: \"{}\" is in csdr.c code, but not in usage string"; fi'
+ @cat /tmp/csdr-list-of-functions | xargs -I{} bash -c 'if ! cat README.md | grep {} >/dev/null ; then echo "warning: \"{}\" is in csdr.c code, but not in README.md"; fi'
+ @rm /tmp/csdr-list-of-functions
+v:
+ vim csdr.c libcsdr.c
diff --git a/README.md b/README.md
old mode 100644
new mode 100755
index dbc3f45..3ffb1c6
--- a/README.md
+++ b/README.md
@@ -1,20 +1,32 @@
-libcsdr
-=======
+CSDR
+====
+
+`csdr` is a command line tool to carry out DSP tasks for Software Defined Radio.
+
+It can be used to build simple signal processing flow graphs, right from the command line.
+
+The included `libcsdr` library contains the DSP functions that `csdr` makes use of. It was designed to use auto-vectorization available in `gcc`, and also has some functions optimized with inline assembly for ARM NEON to achieve some speedup by taking advantage of SIMD command sets available in today's CPUs.
-*libcsdr* is a set of simple DSP routines for Software Defined Radio.
-It is mostly useful for AM/FM/SSB demodulation and spectrum display.
Feel free to use it in your projects.
Most of the code is available under the permissive BSD license, with some optional parts under GPL. For additional details, see licensing.
-- The package comes with a command-line tool `csdr`, which lets you build DSP processing chains by shell pipes.
-- 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).
+`csdr` has already been used to build:
+
+- AM, FM, SSB, CW and BPSK31 demodulators and waterfall display in [OpenWebRX](https://github.com/simonyiszk/openwebrx),
+- AM, FM, SSB modulators in [qtcsdr](https://github.com/ha7ilm/qtcsdr) that can also be used standalone with [rpitx](https://github.com/ha7ilm/rpitx-app-note),
+- a demodulator for FSK transmissions sent with the CC1111 wireless MCU, and also a standalone RTTY demodulator.
+
+This animation shows the Gardner timing recovery algorithm in `csdr` locking on a baseband BPSK signal:
+
+![Gardner](https://raw.githubusercontent.com/wiki/simonyiszk/csdr/gardner.gif)
+
+(The symbol is sampled at the left red dot. The algorithm moves the middle dot as close to the symbol transition center, as possible.)
How to compile
--------------
- make
- sudo make install
+ make
+ sudo make install
The project was only tested on Linux. It has the following dependencies: `libfftw3-dev`
@@ -22,11 +34,11 @@ If you compile on ARM, please edit the Makefile and tailor `PARAMS_NEON` for you
To run the examples, you will also need rtl_sdr 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:
+If you compile `fftw3` from sources for use with `libcsdr`, you need to configure it with 32-bit float support enabled:
- ./configure --enable-float
+ ./configure --enable-float
-(This is for *fftw3*, not *libcsdr*. You do not need to run the configure script before compiling *libcsdr*.)
+(This is for `fftw3`, not `libcsdr`. You do not need to run the configure script before compiling `libcsdr`.)
Credits
-------
@@ -39,7 +51,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_s16 | 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`.
@@ -51,7 +63,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_s16 | 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.
@@ -60,19 +72,19 @@ Usage by example
Sample rates look like this:
- 2.4 Msps 240 ksps 48 ksps
- I/Q source ------------> FIR decimation ------------> FM demod -> frac. decimation ---------> deemphasis -> sound card
+ 2.4 Msps 240 ksps 48 ksps
+ I/Q source ------------> FIR decimation ------------> FM demod -> frac. decimation ---------> deemphasis -> sound card
*Note:* there is an example shell script that does this for you (without the unnecessary shift operation). If you just want to listen to FM radio, type:
- csdr-fm 89.5 20
+ csdr-fm 89.5 20
The first parameter is the frequency in MHz, and the second optional parameter is the RTL-SDR tuner gain in dB.
### 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_s16 | 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.
@@ -80,14 +92,14 @@ 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_s16 | 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.
### Design FIR band-pass filter (with complex taps)
- csdr firdes_bandpass_c 0 0.5 59 HAMMING --octave | octave -i
+ csdr firdes_bandpass_c 0 0.5 59 HAMMING --octave | octave -i
- ...and then plot its frequency response with octave. (You can close octave window by issuing Ctrl-C in the terminal window.)
- It will design a filter that lets only the positive frequencies pass (low cut is 0, high cut is 0.5 - these are relative to the sampling rate).
@@ -95,13 +107,13 @@ 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_s16 | 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`.
### Draw FFT
- rtl_sdr -s 2400000 -f 104300000 -g 20 - | csdr convert_u8_f | csdr fft_cc 1024 1200000 HAMMING --octave | octave -i > /dev/null
+ rtl_sdr -s 2400000 -f 104300000 -g 20 - | csdr convert_u8_f | csdr fft_cc 1024 1200000 HAMMING --octave | octave -i > /dev/null
- We calculate the Fast Fourier Transform by `csdr fft_cc` on the first 1024 samples of every block of 1200000 complex samples coming after each other. (We calculate FFT from 1024 samples and then skip 1200000-1024=1198976 samples. This way we will calculate FFT two times every second.)
- The window used for FFT is the Hamming window, and the output consists of commands that can be directly interpreted by GNU Octave which plots us the spectrum.
@@ -134,98 +146,229 @@ The following commands are available:
- `csdr convert_f_s8`
- `csdr convert_s16_f`
- `csdr convert_f_s16`
+- `csdr convert_s24_f [--bigendian]`
+- `csdr convert_f_s24 [--bigendian]`
How to interpret: `csdr convert__`
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 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`.
Regarding `csdr`, the first command-line parameter is the name of a function, others are the parameters for the given function. Compulsory parameters are noted as ``, optional parameters are noted as `[parameter]`.
Optional parameters have safe defaults, for more info look at the code.
- realpart_cf
+----
+
+### [realpart_cf](#realpart_cf)
+
+Syntax:
+
+ csdr realpart_cf
It takes the real part of the complex signal, and throws away the imaginary part.
- clipdetect_ff
+----
+
+### [clipdetect_ff](#clipdetect_ff)
+
+Syntax:
+
+ csdr clipdetect_ff
It clones the signal (the input and the output is the same), but it prints a warning on `stderr` if any sample value is out of the -1.0 ... 1.0 range.
- limit_ff [max_amplitude]
+----
+
+### [limit_ff](#limit_ff)
+
+Syntax:
+
+ csdr limit_ff [max_amplitude]
The input signal amplitude will not be let out of the `-max_amplitude ... max_amplitude` range.
- gain_ff
+----
+
+### [gain_ff](#gain_ff)
+
+Syntax:
+
+ csdr gain_ff
It multiplies all samples by `gain`.
- clone
+----
+
+### [clone](#clone)
+
+Syntax:
+
+ csdr clone
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.
+### [through](#through)
- none
+Syntax:
+
+ csdr through
+
+It copies the input to the output, while also displaying the data rate going through it.
+
+----
+
+### [none](#none)
+
+Syntax:
+
+ csdr none
The `csdr` process just exits with 0.
- yes_f [buf_times]
+----
+
+### [yes_f](#yes_f)
+
+Syntax:
+
+ csdr 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
+----
+
+### [detect_nan_ff](#detect_nan_ff)
+
+Syntax:
+
+ csdr 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 "`.
+### [dump_f](#dump_f)
- flowcontrol
+Syntax:
+
+ csdr dump_f
+
+It prints all floating point input samples as text.
+
+The C format string used is `"%g "`.
+
+You can also use it to print complex float values, then you will see the I and Q samples interleaved, like: `I Q I Q I Q ...`
+
+Alternatively, you can use the `od` command (built into most Linux distributions). To display a list of floating point values with their addresses as well, you can use: `od -vf`
+
+Aliases for this function: `floatdump_f`
+
+----
+
+### [dump_u8](#dump_u8)
+
+Syntax:
+
+ csdr dump_u8
+
+It prints all input bytes as text, in hexadecimal form.
+
+Alternatively, you can use the `xxd` command (built into most Linux distributions). To display a hexadecimal dump of the standard input (with addresses at the beginning of rows), you can use: `xxd -g1`
+
+----
+
+### [flowcontrol](#flowcontrol)
+
+Syntax:
+
+ csdr 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
+----
+
+### [shift_math_cc](#shift_math_cc)
+
+Syntax:
+
+ csdr shift_math_cc
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.
-Internally, a sine and cosine wave is generated to perform this function, and this function uses `math.h` for this purpose, which is quite accurate, but not always very fast.
+Internally, a sine and cosine wave is generated, and this function uses `math.h` for this purpose, which is quite accurate, but not always very fast.
- shift_addition_cc
+----
+
+### [shift_addition_cc](#shift_addition_cc)
+
+Syntax:
+
+ csdr shift_addition_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.)
- shift_addition_cc_test
+----
+
+### [shift_addition_cc_test](#shift_addition_cc_test)
+
+Syntax:
+
+ csdr shift_addition_cc_test
This function was used to test the accuracy of the method above.
- shift_table_cc [table_size]
+----
+
+### [shift_table_cc](#shift_table_cc)
+
+Syntax:
+
+ csdr 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.
- shift_addfast_cc
+----
+
+### [shift_addfast_cc](#shift_addfast_cc)
+
+Syntax:
+
+ csdr shift_addfast_cc
Operation is the same as for `shift_math_cc`.
Internally, this function uses a NEON-accelerated algorithm on capable systems, so it is advised to use this one on ARM boards.
- shift_unroll_cc
+----
+
+### [shift_unroll_cc](#shift_unroll_cc)
+
+Syntax:
+
+ csdr shift_unroll_cc
Operation is the same as for `shift_math_cc`.
@@ -233,86 +376,210 @@ This uses a modified algoritm that first stores a vector of sine and cosine valu
The loop in this function unrolls quite well if compiled on a PC. It was the fastest one on an i7 CPU during the tests.
- decimating_shift_addition_cc [decimation]
+----
+
+### [decimating_shift_addition_cc](#decimating_shift_addition_cc)
+
+Syntax:
+
+ csdr 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
+----
+
+### [shift_addition_fc](#shift_addition_fc)
+
+Syntax:
+
+ csdr shift_addition_fc
+
+It converts the real input signal to complex, and then shifts it in the frequency domain by `rate`.
+
+----
+
+### [dcblock_ff](#dcblock_ff)
+
+Syntax:
+
+ csdr dcblock_ff
This is a DC blocking IIR filter.
- fastdcblock_ff
+----
+
+### [fastdcblock_ff](#fastdcblock_ff)
+
+Syntax:
+
+ csdr fastdcblock_ff
This is a DC blocker that works based on the average of the buffer.
- fmdemod_atan_cf
+----
+
+### [fmdemod_atan_cf](#fmdemod_atan_cf)
+
+Syntax:
+
+ csdr fmdemod_atan_cf
It is an FM demodulator that internally uses the `atan` function in `math.h`, so it is not so fast.
- fmdemod_quadri_cf
+----
+
+### [fmdemod_quadri_cf](#fmdemod_quadri_cf)
+
+Syntax:
+
+ csdr fmdemod_quadri_cf
It is an FM demodulator that is based on the quadri-correlator method, and it can be effectively auto-vectorized, so it should be faster.
- fmdemod_quadri_novect_cf
+----
+
+### [fmdemod_quadri_novect_cf](#fmdemod_quadri_novect_cf)
+
+Syntax:
+
+ csdr fmdemod_quadri_novect_cf
It has more easily understandable code than the previous one, but can't be auto-vectorized.
- deemphasis_wfm_ff
+----
+
+### [deemphasis_wfm_ff](#deemphasis_wfm_ff)
+
+Syntax:
+
+ csdr deemphasis_wfm_ff
It does de-emphasis with the given RC time constant `tau`.
+
Different parts of the world use different pre-emphasis filters for FM broadcasting.
+
In Europe, `tau` should be chosen as `50e-6`, and in the USA, `tau` should be `75e-6`.
- deemphasis_nfm_ff
+----
+
+### [deemphasis_nfm_ff](#deemphasis_nfm_ff)
+
+Syntax:
+
+ csdr deemphasis_nfm_ff
It does de-emphasis on narrow-band FM for communication equipment (e.g. two-way radios).
-It uses fixed filters so it works only on predefined sample rates, for the actual list of them run: `cat libcsdr.c | grep DNFMFF_ADD_ARRAY`
- amdemod_cf
+It uses fixed filters so it works only on predefined sample rates, for the actual list of them run:
+
+ cat libcsdr.c | grep DNFMFF_ADD_ARRAY
+
+----
+
+### [amdemod_cf](#amdemod_cf)
+
+Syntax:
+
+ csdr amdemod_cf
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](#amdemod_estimator_cf)
+
+Syntax:
+
+ csdr amdemod_estimator_cf
It is an AM demodulator that uses an estimation method that is faster but less accurate than `amdemod_cf`.
- firdes_lowpass_f [window [--octave]]
+----
+
+### [firdes_lowpass_f](#firdes_lowpass_f)
+
+Syntax:
+
+ csdr firdes_lowpass_f [window [--octave]]
Low-pass FIR filter design function to output real taps, with a `cutoff_rate` proportional to the sampling frequency, using the windowed sinc filter design method.
+
`cutoff_rate` can be between 0 and 0.5.
`length` is the number of filter taps to output, and should be odd.
+
The longer the filter kernel is, the shorter the transition bandwidth is, but the more CPU time it takes to process the filter.
+
The transition bandwidth (proportional to the sampling rate) can be calculated as: `transition_bw = 4 / length`.
+
Some functions (below) require the `transition_bw` to be given instead of filter `length`. Try to find the best compromise between speed and accuracy by changing this parameter.
`window` is the window function used to compensate finite filter length. Its typical values are: `HAMMING`, `BLACKMAN`, `BOXCAR`. For the actual list of values, run: `cpp libcsdr.c | grep window\ ==`
The `--octave` parameter lets you directly view the filter response in `octave`. For more information, look at the [Usage by example] section.
- firdes_bandpass_c [window [--octave]]
+----
+
+### [firdes_bandpass_c](#firdes_bandpass_c)
+
+Syntax:
+
+ csdr firdes_bandpass_c [window [--octave]]
Band-pass FIR filter design function to output complex taps.
+
`low_cut` and ` high_cut` both may be between -0.5 and 0.5, and are also proportional to the sampling frequency.
Other parameters were explained above at `firdes_lowpass_f`.
- fir_decimate_cc [transition_bw [window]]
+----
+
+### [fir_decimate_cc](#fir_decimate_cc)
+
+Syntax:
+
+ csdr fir_decimate_cc [transition_bw [window]]
It is a decimator that keeps one sample out of `decimation_factor` samples.
-To avoid aliasing, it runs a filter on the signal and removes spectral components above `0.5 × nyquist_frequency × decimation_factor`.
+
+To avoid aliasing, it runs a filter on the signal and removes spectral components above `0.5 × nyquist_frequency × decimation_factor` from the input signal.
+
+----
+
+### [fir_interpolate_cc](#fir_interpolate_cc)
+
+Syntax:
+
+ csdr fir_interpolate_cc [transition_bw [window]]
+
+It is an interpolator that generates `interpolation_factor` number of output samples from one input sample.
+
+To avoid aliasing, it runs a filter on the signal and removes spectral components above `0.5 × nyquist_frequency / interpolation_factor` from the output signal.
`transition_bw` and `window` are the parameters of the filter.
- rational_resampler_ff [transition_bw [window]]
+----
+
+### [rational_resampler_ff](#rational_resampler_ff)
+
+Syntax:
+
+ csdr rational_resampler_ff [transition_bw [window]]
It is a resampler that takes integer values of `interpolation` and `decimation`.
The output sample rate will be `interpolation / decimation × input_sample_rate`.
`transition_bw` and `window` are the parameters of the filter.
- fractional_decimator_ff [num_poly_points ( [transition_bw [window]] | --prefilter )]
+----
+
+### [fractional_decimator_ff](#fractional_decimator_ff)
+
+Syntax:
+
+ csdr fractional_decimator_ff [num_poly_points ( [transition_bw [window]] | --prefilter )]
It can decimate by a floating point ratio.
@@ -323,17 +590,37 @@ It can filter the signal with an anti-aliasing FIR filter before applying the La
* passing only the `transition_bw`, or both the `transition_bw` and the `window` parameters of the filter,
* using the `--prefilter` switch after `num_poly_points` to switch this filter on with the default parameters.
- bandpass_fir_fft_cc [window]
+----
+
+### [old_fractional_decimator_ff](#old_fractional_decimator_ff)
+
+Syntax:
+
+ csdr old_fractional_decimator_ff [transition_bw [window]]
+
+This is the deprecated, old algorithm to decimate by a floating point ratio, superseded by `fractional_decimator_ff`.
+
+(It uses linear interpolation, and its filter cuts at 59% of the passband.)
+
+----
+
+### [bandpass_fir_fft_cc](#bandpass_fir_fft_cc)
+
+Syntax:
+
+ csdr bandpass_fir_fft_cc [window]
It performs a bandpass FIR filter on complex samples, using FFT and the overlap-add method.
Parameters are described under `firdes_bandpass_c` and `firdes_lowpass_f`.
- old_fractional_decimator_ff [num_poly_points [transition_bw [window]]]
+----
-This is the deprecated, old version of `fractional_decimator_ff` (only uses linear interpolation, its filter cuts at 59% of the passband).
+### [agc_ff](#agc_ff)
- agc_ff [hang_time [reference [attack_rate [decay_rate [max_gain [attack_wait [filter_alpha]]]]]]]
+Syntax:
+
+ csdr agc_ff [hang_time [reference [attack_rate [decay_rate [max_gain [attack_wait [filter_alpha]]]]]]]
It is an automatic gain control function.
@@ -347,11 +634,23 @@ It is an automatic gain control function.
Its default parameters work best for an audio signal sampled at 48000 Hz.
- fastagc_ff [block_size [reference]]
+----
+
+### [fastagc_ff](#fastagc_ff)
+
+Syntax:
+
+ csdr fastagc_ff [block_size [reference]]
It is a faster AGC that linearly changes the gain, taking the highest amplitude peak in the buffer into consideration. Its output will never exceed `-reference ... reference`.
- fft_cc [window [--octave] [--benchmark]]
+----
+
+### [fft_cc](#fft_cc)
+
+Syntax:
+
+ csdr fft_cc [window [--octave] [--benchmark]]
It performs an FFT on the first `fft_size` samples out of `out_of_every_n_samples`, thus skipping `out_of_every_n_samples - fft_size` samples in the input.
@@ -359,92 +658,749 @@ It can draw the spectrum by using `--octave`, for more information, look at the
FFTW can be faster if we let it optimalize a while before starting the first transform, hence the `--benchmark` switch.
- fft_benchmark [--benchmark]
+----
+
+### [fft_fc](#fft_fc)
+
+Syntax:
+
+ csdr fft_fc [--benchmark]
+
+It works similarly to fft_cc, but on real input samples.
+
+For real FFT, the `fft_out_size` parameter is the number of output complex bins instead of the actual FFT size.
+
+Number of input samples used for each FFT is `2 × fft_out_size`. This makes it easier to replace `fft_cc` by `fft_fc` in some applications.
+
+----
+
+### [fft_benchmark](#fft_benchmark)
+
+Syntax:
+
+ csdr fft_benchmark [--benchmark]
It measures the time taken to process `fft_cycles` transforms of `fft_size`.
It lets FFTW optimalize if used with the `--benchmark` switch.
- logpower_cf [add_db]
+----
+
+### [logpower_cf](#logpower_cf)
+
+Syntax:
+
+ csdr 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
+----
+
+### [encode_ima_adpcm_i16_u8](#encode_ima_adpcm_i16_u8)
+
+Syntax:
+
+ csdr csdr 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
+----
+
+### [decode_ima_adpcm_u8_i16](#decode_ima_adpcm_u8_i16)
+
+Syntax:
+
+ csdr decode_ima_adpcm_u8_i16
Decodes the audio stream from IMA ADPCM.
- compress_fft_adpcm_f_u8
+----
+
+### [compress_fft_adpcm_f_u8](#compress_fft_adpcm_f_u8)
+
+Syntax:
+
+ csdr 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 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](#fft_exchange_sides_ff)
+
+Syntax:
+
+ csdr 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`.
- dsb_fc [q_value]
+----
+
+### [dsb_fc](#dsb_fc)
+
+Syntax:
+
+ csdr 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
+----
+
+### [add_dcoffset_cc](#add_dcoffset_cc)
+
+Syntax:
+
+ csdr 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
+----
+
+### [convert_f_samplerf](#convert_f_samplerf)
+
+Syntax:
+
+ csdr convert_f_samplerf
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 `` 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
+----
+
+### [fmmod_fc](#fmmod_fc)
+
+Syntax:
+
+ csdr fmmod_fc
It generates a complex FM modulated output from a real input signal.
- fixed_amplitude_cc
+----
+
+### [fixed_amplitude_cc](#fixed_amplitude_cc)
+
+Syntax:
+
+ csdr fixed_amplitude_cc
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.
+### [mono2stereo_s16](#mono2stereo_s16)
- setbuf
+Syntax:
+
+ csdr mono2stereo_s16
+
+It doubles every input sample.
+
+Example: if the input samples are 16 bit signed integers:
+
+ 23 -452 3112
+
+The output will be:
+
+ 23 23 -452 -452 3112 3112
+
+----
+
+### [setbuf](#setbuf)
+
+Syntax:
+
+ csdr setbuf
See the [buffer sizes](#buffer_sizes) section.
- squelch_and_smeter_cc --fifo --outfifo
+ squelch_and_smeter_cc --fifo --outfifo
This is a controllable squelch, which reads the squelch level input from `` and writes the power level output to ``. Both input and output are in the format of `%g\n`. While calculating the power level, it takes only every `` sample into consideration. It writes the S-meter value for every `` buffer to ``. 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
+----
+
+### [fifo](#fifo)
+
+Syntax:
+
+ csdr fifo
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.
+----
+
+### [psk31_varicode_encoder_u8_u8](#psk31_varicode_encoder_u8_u8)
+
+Syntax:
+
+ csdr psk31_varicode_encoder_u8_u8
+
+It encodes ASCII characters into varicode for PSK31 transmission. It puts a `00` sequence between the varicode characters (which acts as a separator).
+
+For the Varicode character set, see: http://www.arrl.org/psk31-spec
+
+For example, `aaa` means the bit sequence `101100101100101100`.
+
+For this input, the output of `psk31_varicode_encoder_u8_u8` will be the following bytes (in hexadecimal):
+
+```
+01 00 01 01 00 00 01 00
+01 01 00 00 01 00 01 01
+00 00
+```
+
+----
+
+### [repeat_u8](#repeat_u8)
+
+Syntax:
+
+ csdr repeat_u8
+
+It repeatedly outputs a set of data bytes (given with decimal numbers).
+
+For example, `csdr repeat_u8 1 1 0 0` will output:
+
+```
+01 01 00 00 01 01 00 00
+01 01 00 00 01 01 00 00
+```
+
+----
+
+### [uniform_noise_f](#uniform_noise_f)
+
+Syntax:
+
+ csdr uniform_noise_f
+
+It outputs uniform white noise. All samples are within the range [-1.0, 1.0].
+
+----
+
+### [gaussian_noise_c](#gaussian_noise_c)
+
+Syntax:
+
+ csdr gaussian_noise_c
+
+It outputs Gaussian white noise. All samples are within the unit circle.
+
+----
+
+### [pack_bits_8to1_u8_u8](#pack_bits_8to1_u8_u8)
+
+Syntax:
+
+ csdr pack_bits_8to1_u8_u8
+
+TODO
+
+----
+
+### [pack_bits_1to8_u8_u8](#pack_bits_1to8_u8_u8)
+
+Syntax:
+
+ csdr pack_bits_1to8_u8_u8
+
+It serializes the bytes on the input: it outputs each bit of the input byte as a single byte valued 0x00 or 0x01, starting from the lowest bit and going to the highest bit.
+
+The output is 8 times as large in size as the input.
+
+For example, the input byte 0x43 will result in eight bytes at the output:
+
+```
+01 01 00 00 00 00 01 00
+```
+
+For consequtive 0x02, 0x03, 0xff bytes on the input, the output will be:
+
+```
+00 01 00 00 00 00 00 00
+01 01 00 00 00 00 00 00
+01 01 01 01 01 01 01 01
+```
+
+----
+
+### [awgn_cc](#awgn_cc)
+
+Syntax:
+
+ csdr awgn_cc [--snrshow]
+
+It adds white noise with the given SNR to a signal assumed to be of 0 dB power.
+
+If the `--snrshow` switch is given, it also shows the actual SNR based on the calculated power of signal and noise components.
+
+----
+
+### [add_n_zero_samples_at_beginning_f](#add_n_zero_samples_at_beginning_f)
+
+Syntax:
+
+ csdr add_n_zero_samples_at_beginning_f
+
+When the function is executed, it furst writes `` 32-bit floating point zeros at the output, after that it just clones the input at the output.
+
+----
+
+### [fft_one_side_ff](#fft_one_side_ff)
+
+Syntax:
+
+ csdr fft_one_side_ff
+
+If the frequency domain signal spans between frequencies -fs/2 to fs/2, this function removes the part from -fs/2 to DC. This can be useful if the FFT of a real signal has been taken (so that the spectrum is mirrored to DC).
+
+----
+
+### [logaveragepower_cf](#logaveragepower_cf)
+
+Syntax:
+
+ csdr logaveragepower_cf
+
+It works like logpower_cf , but it calculates the average of every `avgnumber` FFTs.
+
+----
+
+### [mono2stereo_s16](#mono2stereo_s16)
+
+Syntax:
+
+ csdr mono2stereo_s16
+
+It duplicates each 16-bit integer input sample.
+
+----
+
+### [psk31_varicode_decoder_u8_u8](#psk31_varicode_decoder_u8_u8)
+
+Syntax:
+
+ csdr psk31_varicode_decoder_u8_u8
+
+It expects symbols encoded as 0x00 and 0x01 bytes on the input, and extracts Varicode characters from them.
+
+----
+
+### [_fft2octave](#_fft2octave)
+
+Syntax:
+
+ csdr _fft2octave
+
+It is used for plotting FFT data with a GNU Octave session, piping its output to `octave -i`.
+
+----
+
+### [invert_u8_u8](#invert_u8_u8)
+
+Syntax:
+
+ csdr invert_u8_u8
+
+It maps
+
+* each 0x00 to 0x01,
+* each 0x01 to 0x00.
+
+----
+
+### [rtty_baudot2ascii_u8_u8](#rtty_baudot2ascii_u8_u8)
+
+Syntax:
+
+ csdr rtty_baudot2ascii_u8_u8
+
+This function awaits baudot code characters on its input (ranging from 0b00000000 to 0b00011111), and converts them into ASCII characters. It has an internal state to switch between letters and figures.
+
+----
+
+### [binary_slicer_f_u8](#binary_slicer_f_u8)
+
+Syntax:
+
+ csdr binary_slicer_f_u8
+
+* If the input sample is below or equals to 0.0, it outputs a 0x00.
+* If the input sample is above 0.0, it outputs a 0x01.
+
+----
+
+### [serial_line_decoder_f_u8](#serial_line_decoder_f_u8)
+
+Syntax:
+
+ csdr serial_line_decoder_f_u8 [databits [stopbits]]
+
+It decodes bits from a sampled serial line. It does so by finding the appropriate start and stop bits, and extracts the data bits in between.
+
+----
+
+### [pll_cc](#pll_cc)
+
+Syntax:
+
+ csdr pll_cc (1 [alpha] |2 [bandwidth [damping_factor [ko [kd]]]])
+
+It implements a PLL that can lock onto a sinusoidal input signal.
+
+The first parameter corresponds to the order of the PLL loop filter (first or second order), others are parameters of the loop filter.
+
+----
+
+### [timing_recovery_cc](#timing_recovery_cc)
+
+Syntax:
+
+ csdr timing_recovery_cc [mu [max_error [--add_q [--output_error | --output_indexes | --octave | --octave_save ]]]]
+
+It implements non-data aided timing recovery (Gardner and early-late gate algorithms).
+
+[More information](http://openwebrx.org/msc-thesis.pdf#page=34) (section 4.4 from page 34)
+
+----
+
+### [octave_complex_c](#octave_complex_c)
+
+Syntax:
+
+ csdr octave_complex_c [--2d]
+
+It generates octave commands to plot a complex time domain signal. Its output can be piped into `octave -i`. It plots every `samples_to_plot` samples `out_of_n_samples`.
+
+----
+
+### [psk_modulator_u8_c](#psk_modulator_u8_c)
+
+Syntax:
+
+ csdr psk_modulator_u8_c
+
+It generates an N-PSK modulated signal from the input symbols.
+
+As an example, for `n_psk`=4, it will translate:
+
+* any 0x00 byte on the input into 1+0j on the output,
+* any 0x01 byte on the input into 0+1j on the output,
+* any 0x02 byte on the input into -1+0j on the output,
+* any 0x03 byte on the input into 0-1j on the output.
+
+----
+
+### [duplicate_samples_ntimes_u8_u8](#duplicate_samples_ntimes_u8_u8)
+
+Syntax:
+
+ csdr duplicate_samples_ntimes_u8_u8
+
+It duplicates each sample of `sample_size_bytes` the given `ntimes` times.
+
+----
+
+### [psk31_interpolate_sine_cc](#psk31_interpolate_sine_cc)
+
+Syntax:
+
+ csdr psk31_interpolate_sine_cc
+
+The input to this function is one complex sample per symbol, the output is `interpolation` samples per symbol, interpolated using a cosine envelope (which is used for PSK31).
+
+----
+
+### [differential_encoder_u8_u8](#differential_encoder_u8_u8)
+
+Syntax:
+
+ csdr differential_encoder_u8_u8
+
+It can be used while generating e.g. differential BPSK modulation.
+
+* If the input is 0x01, the output remains the same as the last output.
+* If the input is 0x00, the output changes from 0x00 to 0x01, or 0x01 to 0x00.
+
+----
+
+### [differential_decoder_u8_u8](#differential_decoder_u8_u8)
+
+Syntax:
+
+ csdr differential_decoder_u8_u8
+
+It can be used while demodulating e.g. differential BPSK modulation. The following table show the logic function it performs:
+
+| Last input | Current input | Output |
+| ---------- | ------------- | ------ |
+| 0x00 | 0x00 | 0x01 |
+| 0x00 | 0x01 | 0x00 |
+| 0x01 | 0x00 | 0x00 |
+| 0x01 | 0x01 | 0x01 |
+
+----
+
+### [bpsk_costas_loop_cc](#bpsk_costas_loop_cc)
+
+Syntax:
+
+ csdr bpsk_costas_loop_cc [--dd | --decision_directed] [--output_error | --output_dphase | --output_nco | --output_combined ]
+
+It implements a Costas loop for BPSK signals.
+
+[More information](http://openwebrx.org/msc-thesis.pdf#page=55) (section 5.4 from page 55)
+
+----
+
+### [simple_agc_cc](#simple_agc_cc)
+
+Syntax:
+
+ csdr simple_agc_cc [reference [max_gain]]
+
+It is an automatic gain control function with a single pole IIR loop filter.
+
+- `reference` is the reference level for the AGC. It tries to keep the amplitude of the output signal close to that.
+- AGC won't increase the gain over `max_gain`.
+- `rate` is the parameter of the loop filter.
+
+The block diagram of this function follows:
+
+![simple_agc_cc block diagram](https://raw.githubusercontent.com/wiki/simonyiszk/csdr/simple-agc-dataflow.png)
+
+----
+
+### [peaks_fir_cc](#peaks_fir_cc)
+
+Syntax:
+
+ csdr peaks_fir_cc
+
+It applies a peak filter to the input signal. The peak filter is a very narrow bandpass filter, the opposite of a notch filter. The higher the `taps_length` is, the sharper the filter frequency transfer function is.
+
+`peak_rate` is the center of the passband, in proportion to the sampling rate.
+
+----
+
+### [firdes_peak_c](#firdes_peak_c)
+
+Syntax:
+
+ csdr firdes_peak_c [window [--octave]]
+
+It designs a FIR peak filter, and writes the taps to the output. More about this filter at peaks_fir_cc.
+
+This command also supports GNU Octave-friendly output that can be piped into the Octave interpreter `octave -i`.
+
+----
+
+### [normalized_timing_variance_u32_f](#normalized_timing_variance_u32_f)
+
+Syntax:
+
+ csdr normalized_timing_variance_u32_f [--debug]
+
+It calculates the normalized timing variance. It works on the sample indexes output from the `timing_recovery_cc` function.
+
+----
+
+### [pulse_shaping_filter_cc](#pulse_shaping_filter_cc)
+
+Syntax:
+
+ csdr pulse_shaping_filter_cc (RRC | COSINE )
+
+It runs a pulse shaping FIR filter on the signal.
+
+* `RRC` stands for Root-Raised-Cosine filter, a design parameter of which is `beta`.
+* The `COSINE` filter is the one used for BPSK31.
+* `samples_per_symbol` is the number of input samples per symbol.
+* `num_taps` is the filter length.
+
+----
+
+### [firdes_pulse_shaping_filter_f](#firdes_pulse_shaping_filter_f)
+
+Syntax:
+
+ csdr firdes_pulse_shaping_filter_f (RRC | COSINE )
+
+It designs a pulse shaping filter, and outputs the taps. It has the same parameters as `pulse_shaping_filter_cc`.
+
+----
+
+### [generic_slicer_f_u8](#generic_slicer_f_u8)
+
+Syntax:
+
+ csdr generic_slicer_f_u8
+
+It decides which symbol the sample corresponds to, where the highest symbol corresponds to 1.0, and the lowest symbol corresponds to -1.0.
+
+As an example, if N=3, the 3 symbols to choose from are: -1, 0, 1. The algorithm will output:
+
+* 0x00 for any input sample between -infinity and -0.5.
+* 0x01 for any input sample between -0.5 and 0.5.
+* 0x02 for any input sample between 0.5 and infinity.
+
+----
+
+### [plain_interpolate_cc](#plain_interpolate_cc)
+
+Syntax:
+
+ csdr plain_interpolate_cc
+
+It interpolates the signal by writing `interpolation - 1` zero samples between each input sample. You need to run an anti-aliasing filter on its output.
+
+----
+
+### [dbpsk_decoder_c_u8](#dbpsk_decoder_c_u8)
+
+Syntax:
+
+ csdr dbpsk_decoder_c_u8
+
+It implements a differential BPSK demodulator, with the following data flow:
+
+![DBPSK dataflow](https://raw.githubusercontent.com/wiki/simonyiszk/csdr/dbpsk-dataflow.png)
+
+The output is 0x00 or 0x01.
+
+----
+
+### [bfsk_demod_cf](#bfsk_demod_cf)
+
+Syntax:
+
+ csdr bfsk_demod_cf
+
+It implements a 2-FSK demodulator, with the following data flow:
+
+![BFSK dataflow](https://raw.githubusercontent.com/wiki/simonyiszk/csdr/bfsk-dataflow.png)
+
+You can calculate the expected frequencies of the two tones on the input by the following formulas: `+(spacing/sampling_rate)` and `-(spacing/sampling_rate)`.
+
+Filter length is the length of the peak filters (FIR) applied to the input för each tone.
+
+----
+
+### [add_const_cc](#add_const_cc)
+
+Syntax:
+
+ csdr add_const_cc
+
+It adds a constant value of `i+q*j` to each input sample.
+
+----
+
+### [pattern_search_u8_u8](#pattern_search_u8_u8)
+
+Syntax:
+
+ csdr pattern_search_u8_u8
+
+It can be used for preamble search. It looks for a given sequence of N bytes (``) in the input data, and if the sequence is found, it reads the following `` bytes and outputs them. The `` parameter is read as unsigned integers.
+
+----
+
+### [tee](#tee)
+
+Syntax:
+
+ csdr tee [buffers]
+
+Similarly to the `tee` command, it reads data from the standard input, and writes it to both a file and the standard output.
+
+Unlike `tee`, if it fails to flush the data to the file, it still flushes it to the standard output. This allows us to have less glitches / better response time if we use this as a way to put branches in the data flow. Example:
+
+ mkfifo /tmp/csdr_fifo
+ rtl_sdr - | csdr tee /tmp/csdr_fifo | csdr dump_u8
+ cat /tmp/csdr_fifo | csdr convert_u8_f | csdr dump_f
+
+How the data flow looks like:
+
+ rtl_sdr --> tee --> dump_u8
+ |
+ \/
+ convert_u8_f --> dump_f
+
+
+### [?](#search_the_function_list)
+
+Syntax:
+
+ csdr ?
+
+You can search the functions available in `csdr` just as if you typed: `csdr 2>&1 | grep `
+
+### [=](#evaluate-python-expression)
+
+Syntax:
+
+ csdr =
+
+When running complicated `csdr` commands, we usually run into using `python` to calculate certain parameters.
+
+This function can eliminate some typing and make our command clearer.
+
+Instead of having to write:
+
+ csdr shift_addition_cc $(python -c "print 1200/2400000.")
+
+...we can type:
+
+ csdr shift_addition_cc $(csdr =1200/2400000.)
+
+If using parenthesis inside the expression, it needs to be escaped (as `bash` would want to parse it):
+
+ csdr shift_addition_cc $(csdr =\(1200+300\)/2400000)
+
+Another solution is using single quotes to wrap the expression:
+
+ csdr shift_addition_cc $(csdr '=(1200+300)/2400000.')
+
+Current version of `csdr` executes the following python script for this function:
+
+```python
+import os, sys
+from math import *
+print
+```
+
+This means that one can also call math functions like `sqrt()`.
+
#### 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.
- shift_addition_cc --fifo
+ shift_addition_cc --fifo
By writing to the given FIFO file with the syntax below, you can control the shift rate:
- \n
+ \n
E.g. you can send `-0.3\n`
Processing will only start after the first control command has been received by `csdr` over the FIFO.
- bandpass_fir_fft_cc --fifo [window]
+ bandpass_fir_fft_cc --fifo [window]
By writing to the given FIFO file with the syntax below, you can control the shift rate:
- \n
+ \n
E.g. you can send `-0.05 0.02\n`
@@ -502,7 +1458,7 @@ Example of initialization if the process always works with a fixed output size,
`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] (#sdrjs)
+## [sdr.js](#sdrjs)
*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 on the concept behind *Emscripten* and *asm.js*.
@@ -512,19 +1468,19 @@ To compile *sdr.js*, first get Emscripten.
To install and build dependencies (for now, only FFTW3):
- make emcc-get-deps
+ make emcc-get-deps
To compile *sdr.js* (which will be created in the `sdr.js` subdirectory):
- make emcc
+ 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
+ make emcc-clean
-## [nmux] (#nmux)
+## [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.
@@ -533,7 +1489,7 @@ The repo also contains a command line tool called `nmux`, which is a TCP stream
`nmux` was originally written for use in OpenWebRX.
-## [Licensing] (#licensing)
+## [Licensing](#licensing)
Most of the code of `libcsdr` is under BSD license.
However, before the implementation of some algoritms, GPL-licensed code from other applications have been reviewed.
diff --git a/csdr.c b/csdr.c
old mode 100644
new mode 100755
index aea07b0..27dbf6d
--- a/csdr.c
+++ b/csdr.c
@@ -27,7 +27,7 @@ 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.
*/
-
+
#define _POSIX_C_SOURCE 199309L
#define _BSD_SOURCE
#define _GNU_SOURCE
@@ -51,6 +51,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include
#include
#include "fastddc.h"
+#include
char usage[]=
"csdr - a simple commandline tool for Software Defined Radio receiver DSP.\n\n"
@@ -73,8 +74,7 @@ char usage[]=
" none\n"
" yes_f [buf_times]\n"
" detect_nan_ff\n"
-" floatdump_f\n"
-" flowcontrol [prebuffer_sec] [thrust]\n"
+" dump_f\n"
" shift_math_cc \n"
" shift_math_cc --fifo \n"
" shift_addition_cc \n"
@@ -94,6 +94,7 @@ char usage[]=
" amdemod_cf\n"
" amdemod_estimator_cf\n"
" fir_decimate_cc [transition_bw [window]]\n"
+" fir_interpolate_cc [transition_bw [window]]\n"
" firdes_lowpass_f [window [--octave]]\n"
" firdes_bandpass_c [window [--octave]]\n"
" agc_ff [hang_time [reference [attack_rate [decay_rate [max_gain [attack_wait [filter_alpha]]]]]]]\n"
@@ -116,11 +117,66 @@ char usage[]=
" convert_f_samperf \n"
" fmmod_fc\n"
" fixed_amplitude_cc \n"
-" monos2stereo_s16\n"
+" mono2stereo_s16\n"
" setbuf \n"
" fft_exchange_sides_ff \n"
" squelch_and_smeter_cc --fifo --outfifo \n"
" fifo \n"
+" invert_u8_u8\n"
+" rtty_line_decoder_u8_u8\n"
+" rtty_baudot2ascii_u8_u8\n"
+" serial_line_decoder_f_u8 [databits [stopbits]]\n"
+" octave_complex_c [--2d]\n"
+" timing_recovery_cc [mu [max_error [--add_q [--output_error | --output_indexes | --octave | --octave_save ]]]] \n"
+" psk31_varicode_encoder_u8_u8\n"
+" psk31_varicode_decoder_u8_u8\n"
+" differential_encoder_u8_u8\n"
+" differential_decoder_u8_u8\n"
+" dump_u8\n"
+" psk_modulator_u8_c \n"
+" psk31_interpolate_sine_cc \n"
+" duplicate_samples_ntimes_u8_u8 \n"
+" bpsk_costas_loop_cc [--dd | --decision_directed] [--output_error | --output_dphase | --output_nco | --output_combined ]\n"
+" binary_slicer_f_u8\n"
+" simple_agc_cc [reference [max_gain]]\n"
+" firdes_peak_c [window [--octave]]\n"
+" peaks_fir_cc [peak_rate × N]\n"
+" repeat_u8 \n"
+" uniform_noise_f\n"
+" gaussian_noise_c\n"
+" awgn_cc [--awgnfile ] [--snrshow]\n"
+" pack_bits_8to1_u8_u8\n"
+" pack_bits_1to8_u8_u8\n"
+" firdes_pulse_shaping_filter_f (RRC | COSINE )\n"
+" pulse_shaping_filter_cc (RRC | COSINE )\n"
+" add_n_zero_samples_at_beginning_f \n"
+" generic_slicer_f_u8 \n"
+" plain_interpolate_cc \n"
+" add_const_cc \n"
+" tee [buffers]\n"
+" pll_cc (1 [alpha] |2 [bandwidth [damping_factor [ko [kd]]]])\n"
+" pattern_search_u8_u8 \n"
+" dbpsk_decoder_c_u8\n"
+" bfsk_demod_cf \n"
+" normalized_timing_variance_u32_f [--debug]\n"
+" ?\n"
+" ??\n"
+" =\n"
+" shift_addfast_cc #only if system supports NEON \n"
+" shift_unroll_cc \n"
+" logaveragepower_cf \n"
+" fft_one_side_ff \n"
+" convert_f_samplerf \n"
+" add_dcoffset_cc\n"
+" fastddc_fwd_cc [transition_bw [window]]\n"
+" fastddc_inv_cc [transition_bw [window]]\n"
+" _fft2octave \n"
+" convert_f_i16 #deprecated, use instead: convert_f_s16\n"
+" convert_i16_f #deprecated, use instead: convert_s16_f\n"
+" floatdump_f #deprecated, use instead: dump_f\n"
+" mono2stereo_i16 #deprecated, use instead: mono2stereo_s16\n"
+" decode_ima_adpcm_u8_i16 #deprecated, use instead: decode_ima_adpcm_u8_s16\n"
+" encode_ima_adpcm_i16_u8 #deprecated, use instead: encode_ima_adpcm_i16_u8\n"
" \n"
;
@@ -139,76 +195,91 @@ int bigbufs = 0;
//change on on 2015-08-29: we don't yield at all. fread() will do it if it blocks
#define YIELD_EVERY_N_TIMES 3
//#define TRY_YIELD if(++yield_counter%YIELD_EVERY_N_TIMES==0) sched_yield()
-#define TRY_YIELD fflush(stdout); sched_yield()
+#define TRY_YIELD fflush(stdout);sched_yield()
//unsigned yield_counter=0;
+char **argv_global;
+int argc_global;
+
+int errhead()
+{
+ fprintf(stderr, "%s%s%s: ", argv_global[0], ((argc_global>=2)?" ":""), ((argc_global>=2)?argv_global[1]:""));
+}
+
int badsyntax(char* why)
{
- if(why==0) fprintf(stderr, "%s", usage);
- else fprintf(stderr, "csdr: %s\n\n", why);
- return -1;
+ if(why==0) fprintf(stderr, "%s", usage);
+ else
+ {
+ errhead();
+ fprintf(stderr, "%s\n", why);
+ }
+ return -1;
}
int clipdetect_ff(float* input, int input_size)
{
- for(int i=0;i1.0) { fprintf(stderr, "csdr clipdetect_ff: Signal value above 1.0!\n"); return 1; }
- }
- return 0;
+ for(int i=0;i1.0) { errhead(); fprintf(stderr, "Signal value above 1.0!\n"); return 1; }
+ }
+ return 0;
}
int clone_(int bufsize_param)
{
- unsigned char* clone_buffer;
- clone_buffer = (unsigned char*)malloc(bufsize_param*sizeof(unsigned char));
- for(;;)
- {
- fread(clone_buffer, sizeof(unsigned char), bufsize_param, stdin);
- fwrite(clone_buffer, sizeof(unsigned char), bufsize_param, stdout);
- TRY_YIELD;
- }
+ unsigned char* clone_buffer;
+ clone_buffer = (unsigned char*)malloc(bufsize_param*sizeof(unsigned char));
+ for(;;)
+ {
+ fread(clone_buffer, sizeof(unsigned char), bufsize_param, stdin);
+ fwrite(clone_buffer, sizeof(unsigned char), bufsize_param, stdout);
+ TRY_YIELD;
+ }
}
-#define FREAD_R fread (input_buffer, sizeof(float), the_bufsize, stdin)
-#define FREAD_C fread (input_buffer, sizeof(float)*2, the_bufsize, stdin)
-#define FWRITE_R fwrite (output_buffer, sizeof(float), the_bufsize, stdout)
-#define FWRITE_C fwrite (output_buffer, sizeof(float)*2, the_bufsize, stdout)
-#define FEOF_CHECK if(feof(stdin)) return 0
+#define FREAD_U8 fread (input_buffer, sizeof(unsigned char), the_bufsize, stdin)
+#define FWRITE_U8 fwrite (output_buffer, sizeof(unsigned char), the_bufsize, stdout)
+#define FREAD_R fread (input_buffer, sizeof(float), the_bufsize, stdin)
+#define FREAD_C fread (input_buffer, sizeof(float)*2, the_bufsize, stdin)
+#define FWRITE_R fwrite (output_buffer, sizeof(float), the_bufsize, stdout)
+#define FWRITE_C fwrite (output_buffer, sizeof(float)*2, the_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[])
{
- if(argc>=4)
- {
- if(!strcmp(argv[2],"--fifo"))
- {
- fprintf(stderr,"csdr: fifo control mode on\n");
- int fd = open(argv[3], O_RDONLY);
- int flags = fcntl(fd, F_GETFL, 0);
- fcntl(fd, F_SETFL, flags | O_NONBLOCK);
- return fd;
- }
- else if(!strcmp(argv[2],"--fd"))
- {
- //to use this:
- //1. Create a pipe(pipedesc) in your process.
- //2. fork() and execl() your process to run csdr, and give pipedesc[0] as parameter after --fd
- // Note: when forking, the child process will get a copy of the file descriptor table! That's why this
- // works at all, as file descriptor indexes are normally not transferable between processes, except for a *NIX socket way which is quite complicated...
- //3. From your parent process, write into pipedesc[1].
- //This is implemented in ddcd, check there to see how to do it!
- int fd;
- if(sscanf(argv[3], "%d",&fd)<=0) return 0;
- fprintf(stderr,"csdr: fd control mode on, fd=%d\n", fd);
- int flags = fcntl(fd, F_GETFL, 0);
- fcntl(fd, F_SETFL, flags | O_NONBLOCK);
- return fd;
- }
- }
- return 0;
+ if(argc>=4)
+ {
+ if(!strcmp(argv[2],"--fifo"))
+ {
+ errhead(); fprintf(stderr,"fifo control mode on\n");
+ int fd = open(argv[3], O_RDONLY);
+ int flags = fcntl(fd, F_GETFL, 0);
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ return fd;
+ }
+ else if(!strcmp(argv[2],"--fd"))
+ {
+ //to use this:
+ //1. Create a pipe(pipedesc) in your process.
+ //2. fork() and execl() your process to run csdr, and give pipedesc[0] as parameter after --fd
+ // Note: when forking, the child process will get a copy of the file descriptor table! That's why this
+ // works at all, as file descriptor indexes are normally not transferable between processes, except for a *NIX socket way which is quite complicated...
+ //3. From your parent process, write into pipedesc[1].
+ //This is implemented in ddcd, check there to see how to do it!
+ int fd;
+ if(sscanf(argv[3], "%d",&fd)<=0) return 0;
+ errhead();
+ fprintf(stderr,"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;
}
@@ -217,38 +288,38 @@ int init_fifo(int argc, char *argv[])
int read_fifo_ctl(int fd, char* format, ...)
{
- if(!fd) return 0;
- static char buffer[RFCTL_BUFSIZE];
- static int buffer_index=0;
- int bytes_read=read(fd,buffer+buffer_index,(RFCTL_BUFSIZE-buffer_index)*sizeof(char));
- if(bytes_read<=0) return 0;
+ if(!fd) return 0;
+ static char buffer[RFCTL_BUFSIZE];
+ static int buffer_index=0;
+ int bytes_read=read(fd,buffer+buffer_index,(RFCTL_BUFSIZE-buffer_index)*sizeof(char));
+ if(bytes_read<=0) return 0;
- int prev_newline_at=0;
- int last_newline_at=0;
- for(int i=0;i\" at the beginning of the chain! Falling back to default buffer size: " STRINGIFY_VALUE(SETBUF_DEFAULT_BUFSIZE)); return SETBUF_DEFAULT_BUFSIZE; }
- if(recv_first[1]<=0) { badsyntax("warning! Invalid buffer size." ); return 0; }
- return recv_first[1];
+ if(!env_csdr_dynamic_bufsize_on) return (bigbufs) ? env_csdr_fixed_big_bufsize : env_csdr_fixed_bufsize;
+ int recv_first[2];
+ fread(recv_first, sizeof(int), 2, stdin);
+ if(memcmp(recv_first, SETBUF_PREAMBLE, sizeof(char)*4)!=0)
+ { badsyntax("warning! Did not match preamble on the beginning of the stream. You should put \"csdr setbuf \" at the beginning of the chain! Falling back to default buffer size: " STRINGIFY_VALUE(SETBUF_DEFAULT_BUFSIZE)); return SETBUF_DEFAULT_BUFSIZE; }
+ if(recv_first[1]<=0) { badsyntax("warning! Invalid buffer size." ); return 0; }
+ return recv_first[1];
}
@@ -274,1438 +345,1393 @@ float *output_buffer;
short *buffer_i16;
float *temp_f;
int the_bufsize = 0;
-char **argv_global;
-#define UNITROUND_UNIT 128
+
+#define UNITROUND_UNIT 4
int unitround(int what)
{
- if(what<=0) return UNITROUND_UNIT;
- return ((what-1)&~(UNITROUND_UNIT-1))+UNITROUND_UNIT;
+ if(what<=0) return UNITROUND_UNIT;
+ return ((what-1)&~(UNITROUND_UNIT-1))+UNITROUND_UNIT;
}
int initialize_buffers()
{
- if(!(the_bufsize=getbufsize())) return 0;
- the_bufsize=unitround(the_bufsize);
- if(env_csdr_print_bufsizes) fprintf(stderr,"%s %s: buffer size set to %d\n",argv_global[0], argv_global[1], the_bufsize);
- input_buffer = (float*) malloc(the_bufsize*sizeof(float) * 2); //need the 2× because we might also put complex floats into it
- output_buffer = (float*) malloc(the_bufsize*sizeof(float) * 2);
- buffer_u8 = (unsigned char*)malloc(the_bufsize*sizeof(unsigned char));
- buffer_i16 = (short*) malloc(the_bufsize*sizeof(short));
- temp_f = (float*) malloc(the_bufsize*sizeof(float) * 4);
- return the_bufsize;
+ if(!(the_bufsize=getbufsize())) return 0;
+ the_bufsize=unitround(the_bufsize);
+ if(env_csdr_print_bufsizes) { errhead(); fprintf(stderr,"buffer size set to %d\n", the_bufsize); }
+ input_buffer = (float*) malloc(the_bufsize*sizeof(float) * 2); //need the 2× because we might also put complex floats into it
+ output_buffer = (float*) malloc(the_bufsize*sizeof(float) * 2);
+ buffer_u8 = (unsigned char*)malloc(the_bufsize*sizeof(unsigned char));
+ buffer_i16 = (short*) malloc(the_bufsize*sizeof(short));
+ temp_f = (float*) malloc(the_bufsize*sizeof(float) * 4);
+ if(the_bufsize<=4096) //this is hacky, should be done correctly
+ {
+ fcntl(STDIN_FILENO, F_SETPIPE_SZ, 4096);
+ fcntl(STDOUT_FILENO, F_SETPIPE_SZ, 4096);
+ }
+ return the_bufsize;
}
int sendbufsize(int size)
{
- //The first word is a preamble, "csdr".
- //If the next csdr process detects it, sets the buffer size according to the second word
- if(!env_csdr_dynamic_bufsize_on) return env_csdr_fixed_bufsize;
- if(env_csdr_print_bufsizes) fprintf(stderr,"%s %s: next process proposed input buffer size is %d\n",argv_global[0], argv_global[1], size);
- int send_first[2];
- memcpy((char*)send_first, SETBUF_PREAMBLE, 4*sizeof(char));
- send_first[1] = size;
- fwrite(send_first, sizeof(int), 2, stdout);
- return size;
+ if(size<=4096)
+ {
+ fcntl(STDOUT_FILENO, F_SETPIPE_SZ, 4096);
+ }
+ //The first word is a preamble, "csdr".
+ //If the next csdr process detects it, sets the buffer size according to the second word
+ if(!env_csdr_dynamic_bufsize_on) return env_csdr_fixed_bufsize;
+ if(env_csdr_print_bufsizes) { errhead(); fprintf(stderr,"next process proposed input buffer size is %d\n", size); }
+ int send_first[2];
+ memcpy((char*)send_first, SETBUF_PREAMBLE, 4*sizeof(char));
+ send_first[1] = size;
+ fwrite(send_first, sizeof(int), 2, stdout);
+ return size;
}
int parse_env()
{
- char* envtmp;
- envtmp=getenv("CSDR_DYNAMIC_BUFSIZE_ON");
- //fprintf(stderr, "envtmp: %s\n",envtmp);
- if(envtmp)
- {
- env_csdr_dynamic_bufsize_on = !!atoi(envtmp);
- env_csdr_fixed_bufsize = 0;
- }
- else
- {
- envtmp=getenv("CSDR_FIXED_BUFSIZE");
- if(envtmp)
- {
- env_csdr_fixed_big_bufsize = env_csdr_fixed_bufsize = atoi(envtmp);
- }
- }
- envtmp=getenv("CSDR_PRINT_BUFSIZES");
- if(envtmp)
- {
- env_csdr_print_bufsizes = atoi(envtmp);
- }
+ char* envtmp;
+ envtmp=getenv("CSDR_DYNAMIC_BUFSIZE_ON");
+ //fprintf(stderr, "envtmp: %s\n",envtmp);
+ if(envtmp)
+ {
+ env_csdr_dynamic_bufsize_on = !!atoi(envtmp);
+ env_csdr_fixed_bufsize = 0;
+ }
+ else
+ {
+ envtmp=getenv("CSDR_FIXED_BUFSIZE");
+ if(envtmp)
+ {
+ env_csdr_fixed_big_bufsize = env_csdr_fixed_bufsize = atoi(envtmp);
+ }
+ }
+ envtmp=getenv("CSDR_PRINT_BUFSIZES");
+ if(envtmp)
+ {
+ env_csdr_print_bufsizes = atoi(envtmp);
+ }
}
int main(int argc, char *argv[])
{
- parse_env();
- argv_global=argv;
- if(argc<=1) return badsyntax(0);
- if(!strcmp(argv[1],"--help")) return badsyntax(0);
+ parse_env();
+ argv_global=argv;
+ argc_global=argc;
+ if(argc<=1) return badsyntax(0);
+ if(!strcmp(argv[1],"--help")) return badsyntax(0);
- fcntl(STDIN_FILENO, F_SETPIPE_SZ, 65536*32);
- fcntl(STDOUT_FILENO, F_SETPIPE_SZ, 65536*32);
- //fprintf(stderr, "csdr: F_SETPIPE_SZ\n");
+ fcntl(STDIN_FILENO, F_SETPIPE_SZ, 65536*32);
+ fcntl(STDOUT_FILENO, F_SETPIPE_SZ, 65536*32);
+ //fprintf(stderr, "csdr: F_SETPIPE_SZ\n");
- if(!strcmp(argv[1],"setbuf"))
- {
- if(argc<=2) return badsyntax("need required parameter (buffer size)");
- sscanf(argv[2],"%d",&the_bufsize);
- if(the_bufsize<=0) return badsyntax("buffer size <= 0 is invalid");
- sendbufsize(the_bufsize);
- clone_(the_bufsize); //After sending the buffer size out, just copy stdin to stdout
- }
+ if(!strcmp(argv[1],"setbuf"))
+ {
+ if(argc<=2) return badsyntax("need required parameter (buffer size)");
+ sscanf(argv[2],"%d",&the_bufsize);
+ if(the_bufsize<=0) return badsyntax("buffer size <= 0 is invalid");
+ sendbufsize(the_bufsize);
+ clone_(the_bufsize); //After sending the buffer size out, just copy stdin to stdout
+ }
- if(!strcmp(argv[1],"clone"))
- {
- if(!sendbufsize(initialize_buffers())) return -2;
- clone_(the_bufsize);
- }
+ if(!strcmp(argv[1],"clone") || !strcmp(argv[1],"REM"))
+ {
+ 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;
+ 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);
+ 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 STDIN_FILENO) ? STDOUT_FILENO : STDIN_FILENO) + 1;
+ 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;
+ 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);
+ 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;
- }
+ //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; errhead(); fprintf(stderr, "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; }
- }
+ }
+ if(fifo_error&&errno!=11) { errhead(); fprintf(stderr,"fifo_error (%d)", errno); return -1; }
+ }
- return -1;
+ return -1;
- }
+ }
- if(!strcmp(argv[1],"convert_u8_f"))
- {
- if(!sendbufsize(initialize_buffers())) return -2;
- for(;;)
- {
- FEOF_CHECK;
- fread(buffer_u8, sizeof(unsigned char), the_bufsize, stdin);
- convert_u8_f(buffer_u8, output_buffer, the_bufsize);
- FWRITE_R;
- TRY_YIELD;
- }
- }
- if(!strcmp(argv[1],"convert_f_u8")) //not tested
- {
- if(!sendbufsize(initialize_buffers())) return -2;
- for(;;)
- {
- FEOF_CHECK;
- FREAD_R;
- convert_f_u8(input_buffer, buffer_u8, the_bufsize);
- fwrite(buffer_u8, sizeof(unsigned char), the_bufsize, stdout);
- TRY_YIELD;
- }
- }
- if(!strcmp(argv[1],"convert_s8_f"))
- {
- if(!sendbufsize(initialize_buffers())) return -2;
- for(;;)
- {
- FEOF_CHECK;
- 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, (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")) || (!strcmp(argv[1],"convert_f_s16")))
- {
- if(!sendbufsize(initialize_buffers())) return -2;
- for(;;)
- {
- FEOF_CHECK;
- FREAD_R;
- convert_f_i16(input_buffer, buffer_i16, the_bufsize);
- fwrite(buffer_i16, sizeof(short), the_bufsize, stdout);
- TRY_YIELD;
- }
- }
- if((!strcmp(argv[1],"convert_i16_f")) || (!strcmp(argv[1],"convert_s16_f")))
- {
- if(!sendbufsize(initialize_buffers())) return -2;
- for(;;)
- {
- FEOF_CHECK;
- fread(buffer_i16, sizeof(short), the_bufsize, stdin);
- convert_i16_f(buffer_i16, output_buffer, the_bufsize);
- FWRITE_R;
- 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;
- for(;;)
- {
- FEOF_CHECK;
- FREAD_C;
- for(int i=0;i=3) sscanf(argv[2],"%g",&max_amplitude);
- if(!sendbufsize(initialize_buffers())) return -2;
- for(;;)
- {
- FEOF_CHECK;
- FREAD_R;
- limit_ff(input_buffer, output_buffer, the_bufsize, max_amplitude);
- FWRITE_R;
- TRY_YIELD;
- }
- }
- if(!strcmp(argv[1],"yes_f"))
- {
- if(argc<=2) return badsyntax("need required parameter (to_repeat)");
- float to_repeat;
- sscanf(argv[2],"%g",&to_repeat);
- int buf_times = 0;
- if(argc>=4) sscanf(argv[3],"%d",&buf_times);
- if(!sendbufsize(initialize_buffers())) return -2;
- for(int i=0;i