diff --git a/WinRelease/audio/1200.pcm b/WinRelease/audio/1200.pcm old mode 100644 new mode 100755 diff --git a/WinRelease/audio/2400.pcm b/WinRelease/audio/2400.pcm old mode 100644 new mode 100755 diff --git a/WinRelease/audio/3000.pcm b/WinRelease/audio/3000.pcm old mode 100644 new mode 100755 diff --git a/WinRelease/audio/4000.pcm b/WinRelease/audio/4000.pcm old mode 100644 new mode 100755 diff --git a/WinRelease/audio/4410.pcm b/WinRelease/audio/4410.pcm old mode 100644 new mode 100755 diff --git a/WinRelease/audio/4800.pcm b/WinRelease/audio/4800.pcm old mode 100644 new mode 100755 diff --git a/WinRelease/audio/5500.pcm b/WinRelease/audio/5500.pcm old mode 100644 new mode 100755 diff --git a/WinRelease/audio/6000.pcm b/WinRelease/audio/6000.pcm old mode 100644 new mode 100755 diff --git a/WinRelease/audio/6600.pcm b/WinRelease/audio/6600.pcm old mode 100644 new mode 100755 diff --git a/WinRelease/audio/7200.pcm b/WinRelease/audio/7200.pcm old mode 100644 new mode 100755 diff --git a/WinRelease/audio/amsat.pcm b/WinRelease/audio/amsat.pcm old mode 100644 new mode 100755 diff --git a/WinRelease/audio/bpsk.pcm b/WinRelease/audio/bpsk.pcm old mode 100644 new mode 100755 diff --git a/WinRelease/audio/kbps.pcm b/WinRelease/audio/kbps.pcm old mode 100644 new mode 100755 diff --git a/WinRelease/audio/psk8.pcm b/WinRelease/audio/psk8.pcm old mode 100644 new mode 100755 diff --git a/WinRelease/audio/qpsk.pcm b/WinRelease/audio/qpsk.pcm old mode 100644 new mode 100755 diff --git a/WinRelease/hsmodem.exe b/WinRelease/hsmodem.exe index 1fa2a45..f953f75 100755 Binary files a/WinRelease/hsmodem.exe and b/WinRelease/hsmodem.exe differ diff --git a/WinRelease/hsmodem.iobj b/WinRelease/hsmodem.iobj new file mode 100755 index 0000000..e83c1a2 Binary files /dev/null and b/WinRelease/hsmodem.iobj differ diff --git a/WinRelease/hsmodem.ipdb b/WinRelease/hsmodem.ipdb new file mode 100755 index 0000000..fccc4a2 Binary files /dev/null and b/WinRelease/hsmodem.ipdb differ diff --git a/WinRelease/hsmodem.pdb b/WinRelease/hsmodem.pdb new file mode 100755 index 0000000..33c9ce3 Binary files /dev/null and b/WinRelease/hsmodem.pdb differ diff --git a/WinRelease/oscardata.exe b/WinRelease/oscardata.exe index a83dfd7..817a6a8 100755 Binary files a/WinRelease/oscardata.exe and b/WinRelease/oscardata.exe differ diff --git a/WinRelease/portaudio_x86.dll b/WinRelease/portaudio_x86.dll new file mode 100755 index 0000000..e4f98d4 Binary files /dev/null and b/WinRelease/portaudio_x86.dll differ diff --git a/hsmodem.sln b/hsmodem.sln old mode 100755 new mode 100644 diff --git a/hsmodem.wse b/hsmodem.wse old mode 100755 new mode 100644 diff --git a/hsmodem/Makefile b/hsmodem/Makefile index a5a2525..dfdd0f5 100755 --- a/hsmodem/Makefile +++ b/hsmodem/Makefile @@ -9,7 +9,18 @@ CXXFLAGS = -Wall -O3 -std=c++0x -Wno-write-strings -Wno-narrowing LDFLAGS = -lpthread -lrt -lsndfile -lasound -lm -lopus -lfftw3 -lfftw3_threads -lliquid -lcodec2 -lsoundio -OBJ = hsmodem.o constellation.o crc16.o frame_packer.o main_helper.o scrambler.o speed.o fec.o udp.o fft.o liquid_if.o symboltracker.o voiceprocessor.o codec2.o soundio.o fifo.o announcement.o fifo_voice.o voiceio.o tuning.o rtty.o +OBJ = hsmodem.o constellation.o crc16.o frame_packer.o main_helper.o\ +scrambler.o speed.o fec.o udp.o fft.o liquid_if.o symboltracker.o\ +voiceprocessor.o codec2.o fifo.o announcement.o tuning.o rtty.o volume.o\ +libkmaudio/libkmaudio_fifo.o\ +libkmaudio/libkmaudio_getDevices.o\ +libkmaudio/libkmaudio_getDevices_Linux.o\ +libkmaudio/libkmaudio_init.o\ +libkmaudio/libkmaudio_init_linux.o\ +libkmaudio/libkmaudio_interface.o\ +libkmaudio/libkmaudio_capture_linux.o\ +libkmaudio/libkmaudio_playback_linux.o\ +libkmaudio/libkmaudio_resampler.o default: $(OBJ) mkdir -p ../hsmodemLinux diff --git a/hsmodem/SharedLibs/aarch64/libliquid.so b/hsmodem/SharedLibs/aarch64/libliquid.so old mode 100644 new mode 100755 diff --git a/hsmodem/SharedLibs/aarch64/libsoundio.so b/hsmodem/SharedLibs/aarch64/libsoundio.so old mode 100644 new mode 100755 diff --git a/hsmodem/SharedLibs/aarch64/libsoundio.so.2 b/hsmodem/SharedLibs/aarch64/libsoundio.so.2 old mode 100644 new mode 100755 diff --git a/hsmodem/SharedLibs/aarch64/libsoundio.so.2.0.0 b/hsmodem/SharedLibs/aarch64/libsoundio.so.2.0.0 old mode 100644 new mode 100755 diff --git a/hsmodem/SharedLibs/windows/portaudio_x86.dll b/hsmodem/SharedLibs/windows/portaudio_x86.dll new file mode 100755 index 0000000..e4f98d4 Binary files /dev/null and b/hsmodem/SharedLibs/windows/portaudio_x86.dll differ diff --git a/hsmodem/announcement.cpp b/hsmodem/announcement.cpp index 9669645..39694df 100755 --- a/hsmodem/announcement.cpp +++ b/hsmodem/announcement.cpp @@ -92,7 +92,8 @@ void playAudioPCM(char* fn, int destination) { int to = 4000; int res; - while ((res = io_ls_fifo_usedspace()) > 10000) + //while ((res = io_ls_fifo_usedspace()) > 10000) + while ((res = io_fifo_usedspace(voice_pbidx)) > 10000) { if (--to == 0) { @@ -102,7 +103,8 @@ void playAudioPCM(char* fn, int destination) } sleep_ms(1); } - io_ls_write_fifo(f / 32768); + float f1 = f / 32768; + kmaudio_playsamples(voice_pbidx, &f1, 1, lsvol); } if (caprate == 44100) @@ -123,7 +125,8 @@ void playAudioPCM(char* fn, int destination) if ((destination & 1) == 1) { int to = 4000; - while (io_pb_fifo_usedspace() > 10000) + //while (io_pb_fifo_usedspace() > 10000) + while (io_fifo_usedspace(io_pbidx) > 10000) { if (--to == 0) { @@ -133,7 +136,7 @@ void playAudioPCM(char* fn, int destination) } sleep_ms(1); } - io_pb_write_fifo(f); + kmaudio_playsamples(io_pbidx, &f, 1, pbvol); } } @@ -191,7 +194,9 @@ void playIntro() snprintf(fn, 499, "%s/oscardata/intro/intro.pcm", homepath); fn[499] = 0; - io_clear_voice_fifos(); + //io_clear_voice_fifos(); + io_fifo_clear(voice_pbidx); + io_fifo_clear(voice_capidx); create_a(); playAudioPCM(fn, 3); close_a(); diff --git a/hsmodem/audio/1200.pcm b/hsmodem/audio/1200.pcm old mode 100644 new mode 100755 diff --git a/hsmodem/audio/2400.pcm b/hsmodem/audio/2400.pcm old mode 100644 new mode 100755 diff --git a/hsmodem/audio/3000.pcm b/hsmodem/audio/3000.pcm old mode 100644 new mode 100755 diff --git a/hsmodem/audio/4000.pcm b/hsmodem/audio/4000.pcm old mode 100644 new mode 100755 diff --git a/hsmodem/audio/4410.pcm b/hsmodem/audio/4410.pcm old mode 100644 new mode 100755 diff --git a/hsmodem/audio/4800.pcm b/hsmodem/audio/4800.pcm old mode 100644 new mode 100755 diff --git a/hsmodem/audio/5500.pcm b/hsmodem/audio/5500.pcm old mode 100644 new mode 100755 diff --git a/hsmodem/audio/6000.pcm b/hsmodem/audio/6000.pcm old mode 100644 new mode 100755 diff --git a/hsmodem/audio/6600.pcm b/hsmodem/audio/6600.pcm old mode 100644 new mode 100755 diff --git a/hsmodem/audio/7200.pcm b/hsmodem/audio/7200.pcm old mode 100644 new mode 100755 diff --git a/hsmodem/audio/amsat.pcm b/hsmodem/audio/amsat.pcm old mode 100644 new mode 100755 diff --git a/hsmodem/audio/bpsk.pcm b/hsmodem/audio/bpsk.pcm old mode 100644 new mode 100755 diff --git a/hsmodem/audio/kbps.pcm b/hsmodem/audio/kbps.pcm old mode 100644 new mode 100755 diff --git a/hsmodem/audio/psk8.pcm b/hsmodem/audio/psk8.pcm old mode 100644 new mode 100755 diff --git a/hsmodem/audio/qpsk.pcm b/hsmodem/audio/qpsk.pcm old mode 100644 new mode 100755 diff --git a/hsmodem/audio/sound0.pcm b/hsmodem/audio/sound0.pcm old mode 100644 new mode 100755 diff --git a/hsmodem/audio/sound1.pcm b/hsmodem/audio/sound1.pcm old mode 100644 new mode 100755 diff --git a/hsmodem/audio/wav2pcm.py b/hsmodem/audio/wav2pcm.py old mode 100644 new mode 100755 diff --git a/hsmodem/codec2.cpp b/hsmodem/codec2.cpp index 834d17a..d52fbb9 100755 --- a/hsmodem/codec2.cpp +++ b/hsmodem/codec2.cpp @@ -136,7 +136,7 @@ void encode_codec2(float f) firinterp_crcf_execute(interp8_48, inp, outp); for (int x = 0; x < decfactor; x++) - io_ls_write_fifo(outp[x].real); + kmaudio_playsamples(voice_pbidx, &outp[x].real, 1, lsvol); } } } @@ -191,7 +191,8 @@ void toCodecDecoder_codec2(uint8_t* pdata, int len) firinterp_crcf_execute(interp8_48, inp, outp); for (int x = 0; x < decfactor; x++) - io_ls_write_fifo(outp[x].real); + //io_ls_write_fifo(outp[x].real); + kmaudio_playsamples(voice_pbidx, &outp[x].real, 1, lsvol); } } } diff --git a/hsmodem/endian.h b/hsmodem/endian.h old mode 100644 new mode 100755 diff --git a/hsmodem/fec.h b/hsmodem/fec.h old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_crc.hpp b/hsmodem/fec/schifra_crc.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_ecc_traits.hpp b/hsmodem/fec/schifra_ecc_traits.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_erasure_channel.hpp b/hsmodem/fec/schifra_erasure_channel.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_error_processes.hpp b/hsmodem/fec/schifra_error_processes.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_fileio.hpp b/hsmodem/fec/schifra_fileio.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_galois_field.hpp b/hsmodem/fec/schifra_galois_field.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_galois_field_element.hpp b/hsmodem/fec/schifra_galois_field_element.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_galois_field_polynomial.hpp b/hsmodem/fec/schifra_galois_field_polynomial.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_galois_utilities.hpp b/hsmodem/fec/schifra_galois_utilities.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_reed_solomon_bitio.hpp b/hsmodem/fec/schifra_reed_solomon_bitio.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_reed_solomon_block.hpp b/hsmodem/fec/schifra_reed_solomon_block.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_reed_solomon_codec_validator.hpp b/hsmodem/fec/schifra_reed_solomon_codec_validator.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_reed_solomon_encoder.hpp b/hsmodem/fec/schifra_reed_solomon_encoder.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_reed_solomon_file_decoder.hpp b/hsmodem/fec/schifra_reed_solomon_file_decoder.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_reed_solomon_file_encoder.hpp b/hsmodem/fec/schifra_reed_solomon_file_encoder.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_reed_solomon_file_interleaver.hpp b/hsmodem/fec/schifra_reed_solomon_file_interleaver.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_reed_solomon_general_codec.hpp b/hsmodem/fec/schifra_reed_solomon_general_codec.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_reed_solomon_interleaving.hpp b/hsmodem/fec/schifra_reed_solomon_interleaving.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_reed_solomon_product_code.hpp b/hsmodem/fec/schifra_reed_solomon_product_code.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_reed_solomon_speed_evaluator.hpp b/hsmodem/fec/schifra_reed_solomon_speed_evaluator.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_sequential_root_generator_polynomial_creator.hpp b/hsmodem/fec/schifra_sequential_root_generator_polynomial_creator.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fec/schifra_utilities.hpp b/hsmodem/fec/schifra_utilities.hpp old mode 100644 new mode 100755 diff --git a/hsmodem/fft.cpp b/hsmodem/fft.cpp index c10fc8c..034aaae 100755 --- a/hsmodem/fft.cpp +++ b/hsmodem/fft.cpp @@ -30,7 +30,7 @@ #include #endif -uint16_t* mean(uint16_t* f); +uint16_t* mean(uint16_t* f, int smoothX, int smoothY); #define FFT_AUDIOSAMPLERATE 800 @@ -234,20 +234,20 @@ uint16_t *make_waterfall(float fre, int *retlen) if(fftrdy == 1) { *retlen = fftcount; - return mean(fftout); + if(speedmode == 10) + return mean(fftout, 2, 2); + return mean(fftout,2,4); } return NULL; } // smooth fft output -const int smoothX = 2; // must be an even number ! -const int smoothY = 10; int yidx = 0; -uint16_t* mean(uint16_t* f) +uint16_t* mean(uint16_t* f, int smoothX, int smoothY) { - + if (smoothY > 20) smoothY = 20; static uint16_t fa[FFT_AUDIOSAMPLERATE / 2 + 1]; if (tuning) @@ -270,12 +270,12 @@ uint16_t* mean(uint16_t* f) // smooth Y values - static uint16_t yarr[smoothY][FFT_AUDIOSAMPLERATE / 2 + 1]; + static uint16_t yarr[20][FFT_AUDIOSAMPLERATE / 2 + 1]; for (int i = 0; i < fftcount; i++) yarr[yidx][i] = fa[i]; if (++yidx >= smoothY) yidx = 0; - memset(fa, 0, FFT_AUDIOSAMPLERATE / 2 + 1); + memset(fa, 0, sizeof(uint16_t) * (FFT_AUDIOSAMPLERATE / 2 + 1)); for (int i = 0; i < fftcount; i++) { for (int j = 0; j < smoothY; j++) @@ -288,6 +288,7 @@ uint16_t* mean(uint16_t* f) void _init_fft() { + printf("init FFT\n"); fftcount = FFT_AUDIOSAMPLERATE / 2 + 1; // number of output samples // the FFT outputs 400 values from 0 to 4kHz with a resolution of 10 Hz @@ -301,9 +302,8 @@ void _init_fft() // decimate 44.1k or 48k down to 8000Hz // the FFT rate is 800, but we feed it with 8000 Samplerate // this results in a new fft every 100ms with a resolution of 10 Hz - float ratio = 10.0f * (float)FFT_AUDIOSAMPLERATE / (float)physRXcaprate; + float ratio = 10.0f * (float)FFT_AUDIOSAMPLERATE / (float)caprate; fftdecim = msresamp_crcf_create(ratio, 40.0f); - } void _exit_fft() diff --git a/hsmodem/fftw_lib/fftw3.h b/hsmodem/fftw_lib/fftw3.h old mode 100644 new mode 100755 diff --git a/hsmodem/fifo.cpp b/hsmodem/fifo.cpp index 6083e63..563539a 100755 --- a/hsmodem/fifo.cpp +++ b/hsmodem/fifo.cpp @@ -27,8 +27,9 @@ #include "hsmodem.h" -void rtty_init_pipes(); + +/* #ifdef _WIN32_ CRITICAL_SECTION io_cap_crit_sec; CRITICAL_SECTION io_pb_crit_sec; @@ -198,9 +199,6 @@ int io_pb_fifo_usedspace() IO_PB_UNLOCK(); return elemInFifo; - /* - int anz = io_pb_fifo_freespace(0); - return AUDIO_PLAYBACK_BUFLEN - anz;*/ } // read num elements @@ -237,7 +235,7 @@ void io_clear_audio_fifos() io_pb_write_fifo_clear(); io_cap_write_fifo_clear(); } - +*/ // ================== RTTY FIFO =================== void clear_rtty_fifos(); @@ -271,12 +269,17 @@ void RTTY_RX_UNLOCK() { pthread_mutex_unlock(&rtty_rx_crit_sec); } void rtty_init_pipes() { #ifdef _WIN32_ - if (&rtty_tx_crit_sec != NULL) DeleteCriticalSection(&rtty_tx_crit_sec); - InitializeCriticalSection(&rtty_tx_crit_sec); + static int f = 1; - if (&rtty_rx_crit_sec != NULL) DeleteCriticalSection(&rtty_rx_crit_sec); - InitializeCriticalSection(&rtty_rx_crit_sec); + if (f) + { + f = 0; + if (&rtty_tx_crit_sec != NULL) DeleteCriticalSection(&rtty_tx_crit_sec); + InitializeCriticalSection(&rtty_tx_crit_sec); + if (&rtty_rx_crit_sec != NULL) DeleteCriticalSection(&rtty_rx_crit_sec); + InitializeCriticalSection(&rtty_rx_crit_sec); + } #endif clear_rtty_fifos(); diff --git a/hsmodem/frameformat.h b/hsmodem/frameformat.h old mode 100644 new mode 100755 diff --git a/hsmodem/hsmodem.cpp b/hsmodem/hsmodem.cpp index 2ffcf13..ad2ad17 100755 --- a/hsmodem/hsmodem.cpp +++ b/hsmodem/hsmodem.cpp @@ -24,7 +24,7 @@ */ /* -* this is a console program +* this is a console program * it can be compiled under Linux: make * and under Windows: Visual-Studio * @@ -65,11 +65,11 @@ char ownfilename[] = { "hsmodem" }; char appIP[20] = { 0 }; int fixappIP = 0; int restart_modems = 0; +int init_voice = 0; int trigger_resetmodem = 0; char homepath[1000] = { 0 }; int caprate = 44100; -int physRXcaprate = 44100; int txinterpolfactor = 20; int rxPreInterpolfactor = 5; int linespeed = 4410; @@ -79,20 +79,20 @@ char playbackDeviceName[101] = { 0 }; char micDeviceName[101] = { 0 }; char lsDeviceName[101] = { 0 }; -float softwareCAPvolume = 1; -float softwarePBvolume = 1; -float softwareMICvolume = 1; -float softwareLSvolume = 1; - int announcement = 0; int VoiceAudioMode = VOICEMODE_OFF; int codec = 1; // 0=opus, 1=codec2 int tuning = 0; int marker = 1; -int init_audio_result = 0; int init_voice_result = 0; +// number of audio device in libkmaudio +int io_capidx = 0; +int io_pbidx = 0; +int voice_capidx = 0; +int voice_pbidx = 0; + int safemode = 0; int sendIntro = 0; @@ -182,7 +182,7 @@ int main(int argc, char* argv[]) struct stat st = { 0 }; - char nd[1000]; + char nd[1100]; sprintf(nd, "%s/oscardata", homepath); if (stat(nd, &st) == -1) { @@ -194,6 +194,9 @@ int main(int argc, char* argv[]) #endif printf("user home path:<%s>\n", homepath); + init_tune(); + kmaudio_init(); + kmaudio_getDeviceList(); init_packer(); initFEC(); @@ -207,6 +210,8 @@ int main(int argc, char* argv[]) while (keeprunning) { + int wait = 1; + if (restart_modems == 1) { printf("restart modem requested\n"); @@ -214,25 +219,36 @@ int main(int argc, char* argv[]) restart_modems = 0; } + if (init_voice == 1) + { + initVoice(); + init_voice = 0; + } + //doArraySend(); if (VoiceAudioMode == VOICEMODE_INTERNALLOOP) { // loop voice mic to LS - float f; - while (io_mic_read_fifo(&f)) - { - io_ls_write_fifo(f); - } + float f[1100]; // 1.1 x need rate to have reserve for resampler + int anz = kmaudio_readsamples(voice_capidx, f, 1000, micvol, 0); + if (anz > 0) + kmaudio_playsamples(voice_pbidx, f, anz,lsvol); } if (VoiceAudioMode == VOICEMODE_RECORD) { // loop voice mic to LS, and record into PCM file - float f; - while (io_mic_read_fifo(&f)) + float f[1100]; + while (1) { - io_saveStream(f); - io_ls_write_fifo(f); + int anz = kmaudio_readsamples(voice_capidx, f, 1000, micvol,0); + if (anz > 0) + { + io_saveStream(f, anz); + kmaudio_playsamples(voice_pbidx, f, anz,lsvol); + } + else + break; } } @@ -246,7 +262,8 @@ int main(int argc, char* argv[]) { // send mic to codec float f; - while (io_mic_read_fifo(&f)) + while(kmaudio_readsamples(voice_capidx, &f, 1, micvol,0)) + //while (io_mic_read_fifo(&f)) { encode(f); } @@ -255,36 +272,34 @@ int main(int argc, char* argv[]) if (tuning != 0) { do_tuning(tuning); + wait = 0; } if (speedmode == 10) { + // nothing to do here //testall(); //fmtest(); - sleep_ms(10); // nothing to do here } else { - // demodulate incoming audio data stream - static uint64_t old_tm = 0; +#ifdef _LINUX_ + /*static uint64_t old_tm = 0; uint64_t tm = getms(); if (tm >= (old_tm + 1000)) { // read Audio device list every 1s - io_readAudioDevices(); + // runtime dectection currently works under linux only + kmaudio_getDeviceList(); old_tm = tm; - } - int dret = demodulator(); - if (dret == 0) - { - // no new data in fifo -#ifdef _LINUX_ - // not important how long to sleep, 10ms is fine - sleep_ms(10); + }*/ #endif - } + + // demodulate incoming audio data stream + int dret = demodulator(); + if (dret) wait = 0; } - + if (wait) sleep_ms(10); } printf("stopped: %d\n", keeprunning); @@ -335,7 +350,6 @@ void startModem() printf("startModem\n"); close_dsp(); close_rtty(); - io_close_audio(); speedmode = set_speedmode; bitsPerSymbol = sr[speedmode].bpsym; @@ -348,7 +362,20 @@ void startModem() opusbitrate = sr[speedmode].codecrate; // int TX audio and modulator - init_audio_result = io_init_sound(playbackDeviceName, captureDeviceName); + io_capidx = kmaudio_startCapture(captureDeviceName, caprate); + if (io_capidx == -1) + { + printf("CAP: cannot open device: %s\n", captureDeviceName); + return; + } + + io_pbidx = kmaudio_startPlayback(playbackDeviceName, caprate); + if (io_pbidx == -1) + { + printf("PB: cannot open device: %s\n", playbackDeviceName); + return; + } + _init_fft(); if (speedmode < 10) { @@ -359,6 +386,26 @@ void startModem() rtty_txoff = 1; init_rtty(); } + + init_tune(); +} + +void initVoice() +{ + // init voice audio + if (VoiceAudioMode == VOICEMODE_OFF) + { + float f = 0.0f; + io_saveStream(&f, 1); // close recording + close_voiceproc(); + } + else + { + + voice_capidx = kmaudio_startCapture(micDeviceName, VOICE_SAMPRATE); + voice_pbidx = kmaudio_startPlayback(lsDeviceName, VOICE_SAMPRATE); + init_voiceproc(); + } } // called from UDP callback ! DO NOT call any system functions @@ -527,35 +574,37 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock) // reset liquid RX modem tuning = 0; resetModem(); - io_clear_audio_fifos(); + //io_clear_audio_fifos(); + io_fifo_clear(voice_pbidx); + io_fifo_clear(voice_capidx); return; } if (type == 21) { // set playback volume (in % 0..100) - io_setVolume(0,minfo); + io_setPBvolume(minfo); return; } if (type == 22) { // set capture volume (in % 0..100) - io_setVolume(1,minfo); + io_setCAPvolume(minfo); return; } if (type == 23) { // set playback volume (in % 0..100) - setVolume_voice(0, minfo); + io_setLSvolume(minfo); return; } if (type == 24) { // set capture volume (in % 0..100) - setVolume_voice(1, minfo); + io_setMICvolume(minfo); return; } @@ -579,18 +628,7 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock) //printf("LS:<%s> MIC:<%s> Mode:%d codec:%d\n", lsDeviceName, micDeviceName, VoiceAudioMode, codec); - // init voice audio - if (VoiceAudioMode == VOICEMODE_OFF) - { - io_saveStream(0.0f); // close recording - close_voiceproc(); - io_close_voice(); - } - else - { - init_voice_result = io_init_voice(lsDeviceName, micDeviceName); - init_voiceproc(); - } + init_voice = 1; return; } @@ -681,7 +719,7 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock) printf("data from app: wrong length:%d (should be %d)\n", len - 2, PAYLOADLEN); return; } - + //if (getSending() == 1) return; // already sending (Array sending) if (minfo == 0 || minfo == 3) @@ -696,6 +734,8 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock) // one frame has 258 bytes, so we need for 6s: 6* ((caprate/txinterpolfactor) * bitsPerSymbol / 8) /258 + 1 frames toGR_sendData(pdata + 2, type, minfo,0); int numframespreamble = 6 * ((caprate / txinterpolfactor) * bitsPerSymbol / 8) / 258 + 1; + if (type == 1)// BER Test + numframespreamble = 1; for (int i = 0; i < numframespreamble; i++) toGR_sendData(pdata + 2, type, minfo,1); } @@ -743,8 +783,7 @@ void toGR_sendData(uint8_t* data, int type, int status, int repeat) //showbytestring((char *)"BERtx: ", txdata, len); - if (txdata != NULL) - sendToModulator(txdata, len); + if (txdata != NULL) sendToModulator(txdata, len); } // called by liquid demodulator for received data diff --git a/hsmodem/hsmodem.h b/hsmodem/hsmodem.h index e1232ba..2c3d0a6 100755 --- a/hsmodem/hsmodem.h +++ b/hsmodem/hsmodem.h @@ -63,8 +63,9 @@ #include "udp.h" #include "symboltracker.h" #include "codec2.h" -#include "soundio.h" +#include "libkmaudio/soundio.h" #include "baudot.h" +#include "libkmaudio/libkmaudio.h" #define jpg_tempfilename "rxdata.jpg" @@ -125,7 +126,7 @@ void measure_speed_bps(int len); void initFEC(); void GetFEC(uint8_t* txblock, int len, uint8_t* destArray); int cfec_Reconstruct(uint8_t* darr, uint8_t* destination); - +/* void io_pb_write_fifo_clear(); int io_init_sound(char* pbname, char* capname); int io_pb_fifo_freespace(int nolock); @@ -140,11 +141,28 @@ int io_pb_fifo_usedspace(); int io_cap_fifo_usedPercent(); int io_pb_read_fifo_num(float* data, int num); void io_clear_audio_fifos(); +int io_pb_fifo_usedBlocks(); +void io_voice_init_pipes(); +int io_mic_read_fifo(float* data); +void io_ls_write_fifo(float sample); +char* getDevID(char* devname, int io); +int io_init_voice(char* lsname, char* micname); +int min_int(int a, int b); +void io_close_voice(); +int io_ls_read_fifo_num(float* data, int num); +void io_mic_write_fifo(float sample); +void write_sample_s16ne(char* ptr, double sample); +int io_ls_fifo_usedspace(); +void write_sample_float32ne(char* ptr, double sample); +void io_clear_voice_fifos(); + +*/ + void io_setPBvolume(int v); void io_setCAPvolume(int v); -void io_setVolume(int pbcap, int v); +void io_setLSvolume(int v); +void io_setMICvolume(int v); -void setVolume_voice(int pbcap, int v); void sendAnnouncement(); void sleep_ms(int ms); @@ -153,7 +171,7 @@ void GRdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock); void toGR_sendData(uint8_t* data, int type, int status, int repeat); void modulator(uint8_t sym_in); -int io_pb_fifo_usedBlocks(); + void init_dsp(); int demodulator(); void sendToModulator(uint8_t* d, int len); @@ -177,20 +195,6 @@ void closeAllandTerminate(); void close_voiceproc(); void close_codec2(); -void io_voice_init_pipes(); -int io_mic_read_fifo(float* data); -void io_ls_write_fifo(float sample); -void io_setLSvolume(int v); -void io_setMICvolume(int v); -char* getDevID(char* devname, int io); -int io_init_voice(char* lsname, char* micname); -int min_int(int a, int b); -void io_close_voice(); -int io_ls_read_fifo_num(float* data, int num); -void io_mic_write_fifo(float sample); -void write_sample_s16ne(char* ptr, double sample); -int io_ls_fifo_usedspace(); -void write_sample_float32ne(char* ptr, double sample); SYMTRACK* km_symtrack_cccf_create( int _ftype, unsigned int _k, @@ -202,9 +206,8 @@ void km_symtrack_cccf_set_bandwidth(SYMTRACK* , float _bw); void km_symtrack_execute(SYMTRACK* ,liquid_float_complex _x, liquid_float_complex* _y, unsigned int* _ny, unsigned int* psym_out); void km_symtrack_cccf_destroy(SYMTRACK*); -void io_saveStream(float f); +void io_saveStream(float *fa, int anz); void playIntro(); -void io_clear_voice_fifos(); float do_tuning(int send); void init_tune(); float singleFrequency(); @@ -215,7 +218,6 @@ void rtty_sendChar(int c); void init_rtty(); int do_rtty(); void make_FFTdata(float f); -void getMax(float fv); void close_rtty(); void close_a(); void rtty_modifyRXfreq(int); @@ -226,6 +228,8 @@ void rtty_rx_write_fifo(char c); int rtty_rx_read_fifo(char* pc); void clear_rtty_txfifo(); void fmtest(); +void rtty_init_pipes(); +void initVoice(); extern int speedmode; @@ -240,8 +244,6 @@ extern int UdpDataPort_ModemToApp; extern int txinterpolfactor; extern int rxPreInterpolfactor; extern char appIP[20]; -extern float softwareCAPvolume; -extern float softwarePBvolume; extern int announcement; extern int ann_running; extern int transmissions; @@ -250,7 +252,6 @@ extern uint8_t maxLevel; extern uint8_t maxTXLevel; extern int VoiceAudioMode; extern int opusbitrate; -extern int init_audio_result; extern int init_voice_result; extern int initialLSvol; extern int initialMICvol; @@ -258,9 +259,6 @@ extern int codec; extern int trigger_resetmodem; extern int rxlevel_deteced; extern int rx_in_sync; -extern float softwareMICvolume; -extern float softwareLSvolume; -extern int physRXcaprate; extern int restart_modems; extern int safemode; extern char homepath[]; @@ -272,6 +270,14 @@ extern int rtty_txoff; extern int rtty_txidx; extern int rtty_frequency; extern int rtty_autosync; +extern int io_capidx; +extern int io_pbidx; +extern int voice_capidx; +extern int voice_pbidx; +extern float pbvol; +extern float capvol; +extern float lsvol; +extern float micvol; #ifdef _LINUX_ diff --git a/hsmodem/hsmodem.vcxproj b/hsmodem/hsmodem.vcxproj index 3007ea5..fee7d53 100755 --- a/hsmodem/hsmodem.vcxproj +++ b/hsmodem/hsmodem.vcxproj @@ -225,17 +225,18 @@ - + + + - @@ -247,20 +248,29 @@ - + + + + + + + + + + + - - + diff --git a/hsmodem/hsmodem.vcxproj.filters b/hsmodem/hsmodem.vcxproj.filters index ce43efa..9a00d2a 100755 --- a/hsmodem/hsmodem.vcxproj.filters +++ b/hsmodem/hsmodem.vcxproj.filters @@ -57,27 +57,54 @@ Source Files - - Source Files - Source Files Source Files - - Source Files - - - Source Files - Source Files Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -116,14 +143,17 @@ Header Files - - Header Files - - - Header Files - Header Files + + Header Files + + + Header Files + + + Header Files + \ No newline at end of file diff --git a/hsmodem/libkmaudio/endian.h b/hsmodem/libkmaudio/endian.h new file mode 100755 index 0000000..f69072f --- /dev/null +++ b/hsmodem/libkmaudio/endian.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of libsoundio, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef SOUNDIO_ENDIAN_H +#define SOUNDIO_ENDIAN_H + +#if defined(__BIG_ENDIAN__) +#define SOUNDIO_OS_BIG_ENDIAN +#elif defined(__ARMEB__) +#define SOUNDIO_OS_BIG_ENDIAN +#elif defined(__THUMBEB__) +#define SOUNDIO_OS_BIG_ENDIAN +#elif defined(__AARCH64EB__) +#define SOUNDIO_OS_BIG_ENDIAN +#elif defined(_MIPSEB) +#define SOUNDIO_OS_BIG_ENDIAN +#elif defined(__MIPSEB) +#define SOUNDIO_OS_BIG_ENDIAN +#elif defined(__MIPSEB__) +#define SOUNDIO_OS_BIG_ENDIAN +#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define SOUNDIO_OS_BIG_ENDIAN +#elif defined(_BIG_ENDIAN) +#define SOUNDIO_OS_BIG_ENDIAN +#elif defined(__sparc) +#define SOUNDIO_OS_BIG_ENDIAN +#elif defined(__sparc__) +#define SOUNDIO_OS_BIG_ENDIAN +#elif defined(_POWER) +#define SOUNDIO_OS_BIG_ENDIAN +#elif defined(__powerpc__) +#define SOUNDIO_OS_BIG_ENDIAN +#elif defined(__ppc__) +#define SOUNDIO_OS_BIG_ENDIAN +#elif defined(__hpux) +#define SOUNDIO_OS_BIG_ENDIAN +#elif defined(__hppa) +#define SOUNDIO_OS_BIG_ENDIAN +#elif defined(_POWER) +#define SOUNDIO_OS_BIG_ENDIAN +#elif defined(__s390__) +#define SOUNDIO_OS_BIG_ENDIAN +#elif defined(__LITTLE_ENDIAN__) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(__ARMEL__) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(__THUMBEL__) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(__AARCH64EL__) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(_MIPSEL) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(__MIPSEL) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(__MIPSEL__) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(_LITTLE_ENDIAN) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(__i386__) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(__alpha__) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(__ia64) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(__ia64__) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(_M_IX86) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(_M_IA64) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(_M_ALPHA) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(__amd64) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(__amd64__) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(_M_AMD64) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(__x86_64) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(__x86_64__) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(_M_X64) +#define SOUNDIO_OS_LITTLE_ENDIAN +#elif defined(__bfin__) +#define SOUNDIO_OS_LITTLE_ENDIAN +#else +#error unable to detect endianness +#endif + +#endif diff --git a/hsmodem/libkmaudio/libkmaudio.cpp b/hsmodem/libkmaudio/libkmaudio.cpp new file mode 100755 index 0000000..c5e689a --- /dev/null +++ b/hsmodem/libkmaudio/libkmaudio.cpp @@ -0,0 +1,170 @@ +/* +* Audio Library for Linux and Windows +* =================================== +* Author: DJ0ABR +* +* Author: Kurt Moraw, Ham radio: DJ0ABR, github: dj0abr +* License: GPL-3 +* +* compilation: +* Windows ... Visual Studio +* Linux ... make +* +* Documentation see: libkmaudio.h +* Usage Example: see main() in this file +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* libkmaudio.cpp ... main() for test purposes only +* usually this library is linked to another program, in this case +* comment-out #define LIBTEST +* +*/ + +#include "libkmaudio.h" + +int kmaudio_getDeviceList_test(); + +int keeprunning = 1; + +/* +* main() +* for testing purposes only +* for library generation comment out: LIBTEST +*/ + + +#define LIBTEST + +#ifdef LIBTEST +int main() +{ + // initialize sound system + // must be called once after program start + // if called during program run, this will reset the sound system, so better don't do it + kmaudio_init(); + + // read list of devices + // call as often as needed + // if a user pluggs-in an USB device on the fly then the running stream may + // be redirected by the OS. In this case closing/opening the stream + // may be required. + kmaudio_getDeviceList(); + + // start capture and/or playback streams + // Parameter: the device name and the sample rate (44100 or 48000 are supported) + // these function return the fifo-index, which is used to access the data in the + // coresponding fifo + + int capidx = kmaudio_startCapture((char *)"Line 2 (Virtual Audio Cable)", 48000); + int pbidx = kmaudio_startPlayback((char *)"Line 2 (Virtual Audio Cable)", 48000); + // int ucapidx = kmaudio_startCapture((char*)"Mikrofon (2- USB Advanced Audio Device)", 48000); + // int upbidx = kmaudio_startPlayback((char*)"Lautsprecher (2- USB Advanced Audio Device)", 48000); + //int capidx = kmaudio_startCapture((char*)"USB Audio CODEC: - (hw:2,0)", 48000); + // int capidx = kmaudio_startCapture((char*)"Mikrofon (1080P Webcam)", 48000); + // int pbidx = kmaudio_startPlayback((char*)"Lautsprecher (2- High Definition Audio Device)", 48000); + //int pbidx = kmaudio_startPlayback((char*)"USB Audio CODEC: - (hw:2,0)", 48000); + + //int capidx = kmaudio_startCapture((char*)"PCM2902 Audio Codec Analog Stereo", 48000); + //int pbidx = kmaudio_startPlayback((char*)"PCM2902 Audio Codec Analog Stereo", 48000); + //int ucapidx = kmaudio_startCapture((char*)"USB Advanced Audio Device Analog Stereo", 48000); + //int upbidx = kmaudio_startPlayback((char*)"USB Advanced Audio Device Analog Stereo", 48000); + + if (1) + { + + int16_t f[1100]; // 1.1 x need rate to have reserve for resampler + int16_t fv = -32768; + /*for (int i = 0; i < 48000; i++) + { + kmaudio_playsamples(pbidx, &fv, 1, 1); + if (++fv >= 32768) fv = -32768; + }*/ + while (1) + { + int av = io_fifo_elems_avail(pbidx); + if (av < 10000) + { + //printf("av: %d\n", av); + for (int i = 0; i < 10000; i++) + { + kmaudio_playsamples(pbidx, &fv, 1, 1); + if (++fv >= 32768) fv = -32768; + //if (fv >= -1010 && fv <= -1000) fv = 1000; // avoid 0, for dropout testing + } + av = io_fifo_elems_avail(pbidx); + //printf("ava: %d\n", av); + } + int anz = kmaudio_readsamples(capidx, f, 20, 1, 0); + if (anz > 0) + { + /*char s[200]; + s[0] = 0; + for(int i=0; i 0) + { + // if samples are available, send them to playback fifo + //printf("write %d samples from %d to %d\n", anz, capidx, pbidx); + kmaudio_playsamples(pbidx, f, anz,1.0f); + done = 1; + } + int uanz = kmaudio_readsamples(ucapidx, f, 1000, 1.0f, 0); + if (uanz > 0) + { + // if samples are available, send them to playback fifo + //printf("write %d samples from %d to %d\n", anz, capidx, pbidx); + kmaudio_playsamples(upbidx, f, uanz, 1.0f); + done = 1; + } + if (done == 0) + { + // if no samples are available make a short break + // this is important to prevent excessive CPU usage + sleep_ms(10); + } + }*/ + } + else + printf("device not available. Ending program\n"); + + // free resources. This will never happen in this example + // but should be done in the final program + kmaudio_close(); + return 0; +} +#endif // LIBTEST diff --git a/hsmodem/libkmaudio/libkmaudio.h b/hsmodem/libkmaudio/libkmaudio.h new file mode 100755 index 0000000..b3eadb0 --- /dev/null +++ b/hsmodem/libkmaudio/libkmaudio.h @@ -0,0 +1,252 @@ +/* +* Audio Library for Linux and Windows +* =================================== +* Author: DJ0ABR +* +* Author: Kurt Moraw, Ham radio: DJ0ABR, github: dj0abr +* License: GPL-3 +* +* compilation: +* Windows ... Visual Studio +* Linux ... make +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#pragma once + +#ifdef WIN32 +// ignore senseless warnings invented by M$ to confuse developers +#pragma warning( disable : 4091 ) +#pragma warning( disable : 4003 ) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +#include "Winsock2.h" +#include "io.h" +#include +#include +#include +#include +#include +#include +#define _USE_MATH_DEFINES +#include +#include "portaudio.h" +#include "pa_win_wasapi.h" + +#pragma comment(lib, "portaudio_x86.lib") +#pragma comment(lib, "libliquid.lib") +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "soundio.h" +#endif + +#include "liquid.h" + +/* +* Sample usage: +* init sound system +* 1. kmaudio_init(); +* 2. kmaudio_getDeviceList(); +* +* start a capture and a playback stream +* 3. kmaudio_startCapture() using a device name returned by io_getAudioDevicelist() +* 4. kmaudio_startPlayback() using a device name returned by io_getAudioDevicelist() +* +* now everything runs in the background, no more need for the user program +* to handle sound specific data +* now we can read/write sound samples as needed by our application +* as an example: get sound from microphone and send to speaker +* process in a loop: +* 5. kmaudio_readsamples() +* 6. kmaudio_playsamples() +* +* for a working example see main(), you will need to +* replace the device names with the names in your computer +*/ + +/* +* initialize the audio library, create required processes +* call only once when your application starts +* returns: 0 = OK, -1 = error +*/ +int kmaudio_init(); + +/* +* closes and frees all resources +* call only when the application exits +*/ +void kmaudio_close(); + +/* +* read a list of all available audio devices into devlist +* the list can then be read by calling io_getAudioDevicelist() +* call any time to find new plugged in/out devices +* returns: 0=OK, -1 if error +*/ +int kmaudio_getDeviceList(); + +/* +* starts a capturing stream from devname with samprate +* returns: id of the capture stream or -1 = error +*/ +int kmaudio_startCapture(char* devname, int samprate); + +/* +* starts a playback stream to devname with samprate +* returns: id of the playback stream or -1 = error +*/ + +int kmaudio_startPlayback(char* devname, int samprate); + +/* +* plays len samples from psamp to device id +* returns: 0=ok, -1=error +* id ... device id returned by kmaudio_startPlayback +* psamp ... float array of length len with the audio data (mono) +* len ... number of float values in psamp +* volume ... 0.0f..2.0f will be multiplied with the output sample +*/ +int kmaudio_playsamples(int id, float* psamp, int len, float volume); + +/* +* reads len samples from device id into psamp +* returns: 0=ok, -1=error +* id ... device id returned by kmaudio_startCapture +* psamp ... float array of length len getting the audio data (mono) +* len ... number of float values to write into psamp +* volume ... 0.0f..2.0f will be multiplied with the input sample +* wait ... 1=wait for data, 0=return if not enough data available (in this case psamp will return 0,0,0...) +*/ +int kmaudio_readsamples(int id, float* psamp, int len, float volume, int wait); + +/* +* reads the names of detected sound devices +* *len...length of the returned string +* returns: pointer to device string +* Format of the device string: +* first byte = 3 ... ID of this string, followed by pure text: +* Active status of the following device "0" or "1" +* Name of playback devices, followed by ~ +* separator ^ +* Active status of the following device "0" or "1" +* Name of capture devices, followed by ~ +* these names are used for calls to kmaudio_startCapture and kmaudio_startPlayback +* to select the device +*/ +uint8_t* io_getAudioDevicelist(int* len); + +/* +* returns the max level (within 1 second) of this stream in % (0..100) +* if the level >= 100 the signal will get clipped and distorted +*/ +uint8_t kmaudio_maxlevel(int id); + + + +int io_fifo_freespace(int pipenum); +int io_fifo_usedspace(int pipenum); +void io_fifo_clear(int pipenum); +int io_fifo_usedpercent(int pipenum); + + +// -------- functions for internal use only -------- + +#define MAXDEVICES 200 +#define MAXDEVNAMELENGTH 150 + +typedef struct _DEVLIST_ { + int index = 0; // index to this list + int active = 0; // 1=device valid, 0=possibly disconencted + char name[MAXDEVNAMELENGTH] = { 0 };// real name + int in_out = 0; // 0=capture device, 1=playback device, 2=both + int supports_44100 = 0; // 1 if supported + int supports_48000 = 0; // 1 if supported + int requested_samprate = 0; // sample rate requested by caller + int real_samprate = 0; // real sample rate of the device + int working = 0; // 0=not running, 1=initialized and working +#ifdef WIN32 // Windows using portaudio + int devnum = -1; // port audio device number + PaStreamParameters inputParameters; + PaStreamParameters outputParameters; + PaStream* capStream = NULL; + PaStream* pbStream = NULL; +#else // Linux using libsoundio + struct SoundIoDevice* io_pb_device = NULL; + struct SoundIoDevice* io_cap_device = NULL; + struct SoundIoInStream* instream = NULL; + struct SoundIoOutStream* outstream = NULL; + int stereo_mono = 2; // 1=mono, 2=stereo + char id[1000] = { 0 }; +#endif +} DEVLIST; + +int searchDevice(char* devname, int io); +void measure_speed_bps(int len); +void sleep_ms(int ms); +void io_write_fifo(int pipenum, float sample); +void io_write_fifo_short(int pipenum, int16_t sample); +void io_fifo_clear(int pipenum); +void init_pipes(); +int io_read_fifo_num(int pipenum, float* data, int num); +int io_read_fifo(int pipenum, float* data); +int getRealSamprate(int idx); +int io_fifo_elems_avail(int pipenum); +void sleep_ms(int ms); +void io_buildAudioDevString(); +void resampler_create(int devidx); +float* resample(int id, float* psamp, int len, int* pnewlen); +uint64_t getms(); +void init_maxarray(); +void kmaudio_detectDropouts(int id); +int io_read_fifo_num_short(int pipenum, int16_t* data, int num); + +extern DEVLIST devlist[MAXDEVICES]; +extern int devanz; +extern int keeprunning; + +#ifndef WIN32 // Linux +int kmaudio_init_linux(); +void kmaudio_close_linux(); +char* getDevID(char* devname, int io, int* pidx); + +extern struct SoundIo* soundio; +#endif // ndef WIN32 + diff --git a/hsmodem/libkmaudio/libkmaudio_capture.cpp b/hsmodem/libkmaudio/libkmaudio_capture.cpp new file mode 100755 index 0000000..1fb6c9a --- /dev/null +++ b/hsmodem/libkmaudio/libkmaudio_capture.cpp @@ -0,0 +1,152 @@ +/* +* Audio Library for Linux and Windows +* =================================== +* Author: DJ0ABR +* +* Author: Kurt Moraw, Ham radio: DJ0ABR, github: dj0abr +* License: GPL-3 +* +* compilation: +* Windows ... Visual Studio +* Linux ... make +* +* Documentation see: libkmaudio.h +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* libkmaudio_capture.cpp ... +* starts a portaudio capture stream and a callback routine. Writes the +* received audio samples into the fifo (Windows only) +*/ +#include "libkmaudio.h" + +#define FRAMES_PER_BUFFER 512 + +int recordCallback(const void* inputBuffer, void* outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void* userData); + +int kmaudio_startCapture(char* devname, int samprate) +{ + printf("Start request for CAP stream:%s\n", devname); + + if (devname == NULL || strlen(devname) < 3) // no devices defined yet + { + printf("no capture devices specified\n"); + return -1; + } + + int idx = searchDevice(devname, 0); + if (idx == -1) + { + printf("Device:<%s> not found\n", devname); + return -1; + } + + devlist[idx].working = 0; + + if (devlist[idx].capStream != NULL) + { + printf("Closing old CAP stream:%s [%d]\n", devname, idx); + Pa_CloseStream(devlist[idx].capStream); + devlist[idx].capStream = NULL; + } + printf("Starting CAP stream:%s [%d]\n", devname, idx); + + io_fifo_clear(idx); + + devlist[idx].requested_samprate = samprate; + if(getRealSamprate(idx) == -1) + { + printf("Samplerate %d not supported by device:<%s>\n", samprate, devname); + return -1; + } + + if (devlist[idx].requested_samprate != devlist[idx].real_samprate) + resampler_create(idx); + + struct PaWasapiStreamInfo wasapiInfo; + memset(&wasapiInfo, 0, sizeof(PaWasapiStreamInfo)); + wasapiInfo.size = sizeof(PaWasapiStreamInfo); + wasapiInfo.hostApiType = paWASAPI; + wasapiInfo.version = 1; + wasapiInfo.flags = (paWinWasapiExclusive | paWinWasapiThreadPriority); + wasapiInfo.threadPriority = eThreadPriorityProAudio; + + devlist[idx].inputParameters.hostApiSpecificStreamInfo = (&wasapiInfo); + + int e = Pa_IsFormatSupported(&devlist[idx].inputParameters, NULL, (double)devlist[idx].real_samprate); + printf("err:%d device:%d PAdev:%d samprate: %f\n", e,idx, devlist[idx].devnum,(double)devlist[idx].real_samprate); + + devlist[idx].index = idx; + + int err = Pa_OpenStream( + &devlist[idx].capStream, + &devlist[idx].inputParameters, + NULL, + (double)devlist[idx].real_samprate, + FRAMES_PER_BUFFER, + paClipOff, + recordCallback, + &(devlist[idx].index)); + + if (err != paNoError) + { + printf("cannot open capture stream for device:<%s> %d\n", devname,err); + return -1; + } + + err = Pa_StartStream(devlist[idx].capStream); + if (err != paNoError) + { + printf("cannot start capture stream for device:<%s>\n", devname); + return -1; + } + + printf("Capture started sucessfully\n"); + devlist[idx].working = 1; + return idx; +} + +int recordCallback( const void* inputBuffer, + void* outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void* userData) +{ + const int16_t* rptr = (const int16_t*)inputBuffer; + int devidx = *((int *)userData); + int chans = devlist[devidx].inputParameters.channelCount; + + //printf("%ld captured %d frames. Flag: %X\n", (long)rptr,framesPerBuffer, statusFlags); + //measure_speed_bps(framesPerBuffer); + + //printf("%d %d\n", chans, rptr[0]); + for (unsigned int i = 0; i < framesPerBuffer; i++) + io_write_fifo_short(devidx, rptr[i * chans]); + + // Prevent unused variable warnings + (void)outputBuffer; + (void)timeInfo; + (void)statusFlags; + + if(keeprunning == 1) + return paContinue; + + return paComplete; +} diff --git a/hsmodem/libkmaudio/libkmaudio_capture_linux.cpp b/hsmodem/libkmaudio/libkmaudio_capture_linux.cpp new file mode 100755 index 0000000..7005e8b --- /dev/null +++ b/hsmodem/libkmaudio/libkmaudio_capture_linux.cpp @@ -0,0 +1,212 @@ +/* +* Audio Library for Linux and Windows +* =================================== +* Author: DJ0ABR +* +* Author: Kurt Moraw, Ham radio: DJ0ABR, github: dj0abr +* License: GPL-3 +* +* compilation: +* Windows ... Visual Studio +* Linux ... make +* +* Documentation see: libkmaudio.h +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* libkmaudio_capture.cpp ... +* starts a libsoundio capture stream and a callback routine. Writes the +* received audio samples into the fifo (Linux only) +*/ + +#ifndef WIN32 + +#include "libkmaudio.h" + +char* getDevID(char* devname, int io, int *pidx) +{ + for (int i = 0; i < devanz; i++) + { + //printf("%s: %d %d\n", devlist[i].name, io, devlist[i].in_out); + if (!strcmp(devname, devlist[i].name) && io == devlist[i].in_out) + { + *pidx = i; + return devlist[i].id; + } + } + return NULL; +} + +int min_int(int a, int b) +{ + return (a < b) ? a : b; +} + +void read_callback(struct SoundIoInStream* instream, int frame_count_min, int frame_count_max) +{ + int err; + if (instream == NULL || soundio == NULL) return; + //printf("cap: %d %d\n", frame_count_min, frame_count_max); + //int chans = instream->layout.channel_count; + //printf("cap:%d\n", instream->sample_rate); + int idx = *((int *)(instream->userdata)); + + struct SoundIoChannelArea* areas; + // samples are in areas.ptr + int frames_left = frame_count_max; // take all + while (keeprunning) + { + int frame_count = frames_left; + if ((err = soundio_instream_begin_read(instream, &areas, &frame_count))) + { + fprintf(stderr, "begin read error: %s", soundio_strerror(err)); + return; + } + if (!frame_count) + break; + + //printf("write %d samples to fifo %d. Channels:%d\n", frame_count, idx, instream->layout.channel_count); + for (int frame = 0; frame < frame_count; frame += 1) + { + for (int ch = 0; ch < instream->layout.channel_count; ch += 1) + { + float frxdata; + memcpy(&frxdata, areas[ch].ptr, instream->bytes_per_sample); + areas[ch].ptr += areas[ch].step; + if (ch == 0) + { + io_write_fifo(idx, frxdata); + } + } + } + + //measure_speed_bps(frame_count); + + if ((err = soundio_instream_end_read(instream))) + { + printf("end read error: %s", soundio_strerror(err)); + return; + } + + frames_left -= frame_count; + if (frames_left <= 0) + break; + } +} + +void overflow_callback(struct SoundIoInStream* instream) +{ + static int count = 0; + printf("overflow %d\n", ++count); +} + +int kmaudio_startCapture(char* devname, int samprate) +{ + printf("Start request for CAP stream:%s\n", devname); + + if (devname == NULL || strlen(devname) < 3) // no devices defined yet + { + printf("no capture devices specified\n"); + return -1; + } + + int idx = 0; // index into devlist + char* capdevid = getDevID(devname, 0, &idx); + if (capdevid == NULL) return -1; + + // if an old stream is open, close it + if (devlist[idx].instream != NULL) + { + printf("Closing old CAP stream:%s [%d]\n", devname, idx); + soundio_instream_destroy(devlist[idx].instream); + devlist[idx].instream = NULL; + } + printf("Starting CAP stream:%s [%d]\n", devname, idx); + + io_fifo_clear(idx); + + devlist[idx].working = 0; + + // define the capture device + soundio_flush_events(soundio); + + for (int i = 0; i < soundio_input_device_count(soundio); i++) + { + devlist[idx].io_cap_device = NULL; + struct SoundIoDevice* device = soundio_get_input_device(soundio, i); + if (strcmp(device->id, capdevid) == 0) + { + devlist[idx].io_cap_device = device; + break; + } + soundio_device_unref(device); + } + if (!devlist[idx].io_cap_device) + { + printf("Invalid device id: %s\n", capdevid); + return -1; + } + + if (devlist[idx].io_cap_device->probe_error) + { + printf("Unable to probe device: %s\n", soundio_strerror(devlist[idx].io_cap_device->probe_error)); + return -1; + } + + // create capture callback + devlist[idx].instream = soundio_instream_create(devlist[idx].io_cap_device); + if (!devlist[idx].instream) { + printf("capture: out of memory\n"); + return -1; + } + + devlist[idx].requested_samprate = samprate; + if (getRealSamprate(idx) == -1) + { + printf("Samplerate %d not supported by device:<%s>\n", samprate, devname); + return -1; + } + + if (devlist[idx].requested_samprate != devlist[idx].real_samprate) + resampler_create(idx); + + devlist[idx].instream->format = SoundIoFormatFloat32NE; + devlist[idx].instream->sample_rate = devlist[idx].real_samprate; + devlist[idx].instream->software_latency = 0.1f; + devlist[idx].instream->read_callback = read_callback; + devlist[idx].instream->overflow_callback = overflow_callback; + devlist[idx].instream->userdata = &(devlist[idx].index); + + int err = 0; + if ((err = soundio_instream_open(devlist[idx].instream))) { + printf("unable to open input stream: %d: %s", err, soundio_strerror(err)); + return -1; + } + + if ((err = soundio_instream_start(devlist[idx].instream))) { + printf("unable to start input device: %s", soundio_strerror(err)); + return -1; + } + + printf("selected CAPTURE device:\nname:%s\nid :%s\n", devlist[idx].name, capdevid); + printf("physical capture rate:%d, requested capture rate:%d\n", devlist[idx].real_samprate, devlist[idx].requested_samprate); + printf("format: %s\n\n", soundio_format_string(devlist[idx].instream->format)); + + devlist[idx].working = 1; + + return idx; +} + +#endif // #ifndef WIN32 diff --git a/hsmodem/libkmaudio/libkmaudio_fifo.cpp b/hsmodem/libkmaudio/libkmaudio_fifo.cpp new file mode 100755 index 0000000..a46772d --- /dev/null +++ b/hsmodem/libkmaudio/libkmaudio_fifo.cpp @@ -0,0 +1,236 @@ +/* +* Audio Library for Linux and Windows +* =================================== +* Author: DJ0ABR +* +* Author: Kurt Moraw, Ham radio: DJ0ABR, github: dj0abr +* License: GPL-3 +* +* compilation: +* Windows ... Visual Studio +* Linux ... make +* +* Documentation see: libkmaudio.h +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* libkmaudio_fifo.cpp ... thread safe FIFOs, used to interface the +* audio callback routines to the other program +* +*/ + +#include "libkmaudio.h" + +#define NUM_OF_PIPES 20 + +#ifdef WIN32 +CRITICAL_SECTION crit_sec[NUM_OF_PIPES]; +#define LOCK(pn) EnterCriticalSection(&(crit_sec[pn])) +void UNLOCK(int pn) +{ + if (&(crit_sec[pn]) != NULL) + LeaveCriticalSection(&(crit_sec[pn])); +} +#else +pthread_mutex_t crit_sec[NUM_OF_PIPES]; +#define LOCK(pn) pthread_mutex_lock(&(crit_sec[pn])) +#define UNLOCK(pn) pthread_mutex_unlock(&(crit_sec[pn])) +#endif + +#define AUDIO_FIFOFLEN 480000 // 10s at 48k samprate (minimum is number of preambles) + +int io_wridx[NUM_OF_PIPES]; +int io_rdidx[NUM_OF_PIPES]; +int16_t io_buffer[NUM_OF_PIPES][AUDIO_FIFOFLEN]; + +void init_pipes() +{ + // init pipes only once + static int f = 1; + if (f) + { + f = 0; + for (int i = 0; i < NUM_OF_PIPES; i++) + { +#ifdef WIN32 + if (&(crit_sec[i]) != NULL) DeleteCriticalSection(&(crit_sec[i])); + InitializeCriticalSection(&(crit_sec[i])); +#else + if (&(crit_sec[i]) != NULL) pthread_mutex_destroy(&(crit_sec[i])); + pthread_mutex_init(&(crit_sec[i]), NULL); +#endif + } + } + + for (int i = 0; i < NUM_OF_PIPES; i++) + io_fifo_clear(i); +} + +// write one sample into the fifo +// ignore data if the fifo is full +void io_write_fifo(int pipenum, float sample) +{ + LOCK(pipenum); + if (((io_wridx[pipenum] + 1) % AUDIO_FIFOFLEN) == io_rdidx[pipenum]) + { + //printf("cannot float WRITE fifo %d full\n",pipenum); + UNLOCK(pipenum); + return; + } + + io_buffer[pipenum][io_wridx[pipenum]] = (int16_t)(sample * 32768.0f); + if (++io_wridx[pipenum] >= AUDIO_FIFOFLEN) io_wridx[pipenum] = 0; + + UNLOCK(pipenum); +} + +void io_write_fifo_short(int pipenum, int16_t sample) +{ + LOCK(pipenum); + if (((io_wridx[pipenum] + 1) % AUDIO_FIFOFLEN) == io_rdidx[pipenum]) + { + //printf("cannot short WRITE fifo %d full\n", pipenum); + UNLOCK(pipenum); + return; + } + + io_buffer[pipenum][io_wridx[pipenum]] = sample; + if (++io_wridx[pipenum] >= AUDIO_FIFOFLEN) io_wridx[pipenum] = 0; + + UNLOCK(pipenum); +} + +int io_read_fifo(int pipenum, float* data) +{ + LOCK(pipenum); + + if (io_rdidx[pipenum] == io_wridx[pipenum]) + { + // Fifo empty, no data available + UNLOCK(pipenum); + return 0; + } + + int16_t id = io_buffer[pipenum][io_rdidx[pipenum]]; + if (++io_rdidx[pipenum] >= AUDIO_FIFOFLEN) io_rdidx[pipenum] = 0; + UNLOCK(pipenum); + + *data = (float)id / 32768; + + return 1; +} + +// read num elements +// if num elems not avail, return all what fifo has stored +int io_read_fifo_num(int pipenum, float* data, int num) +{ + LOCK(pipenum); + + int elemInFifo = (io_wridx[pipenum] + AUDIO_FIFOFLEN - io_rdidx[pipenum]) % AUDIO_FIFOFLEN; + elemInFifo -= 1; + + /*if (num > elemInFifo) + { + // Fifo not enough data available + //printf("only %d elements available\n", elemInFifo); + UNLOCK(pipenum); + return 0; + }*/ + + if (num > elemInFifo) num = elemInFifo; + + int16_t id; + + for (int i = 0; i < num; i++) + { + id = io_buffer[pipenum][io_rdidx[pipenum]]; + *data++ = (float)id / 32768; + if (++io_rdidx[pipenum] >= AUDIO_FIFOFLEN) io_rdidx[pipenum] = 0; + } + UNLOCK(pipenum); + + return num; +} + +int io_read_fifo_num_short(int pipenum, int16_t* data, int num) +{ + LOCK(pipenum); + + int elemInFifo = (io_wridx[pipenum] + AUDIO_FIFOFLEN - io_rdidx[pipenum]) % AUDIO_FIFOFLEN; + elemInFifo -= 1; + + /*if (num > elemInFifo) + { + // Fifo not enough data available + //printf("only %d elements available\n", elemInFifo); + UNLOCK(pipenum); + return 0; + }*/ + + if (num > elemInFifo) num = elemInFifo; + + for (int i = 0; i < num; i++) + { + *data++ = io_buffer[pipenum][io_rdidx[pipenum]]; + if (++io_rdidx[pipenum] >= AUDIO_FIFOFLEN) io_rdidx[pipenum] = 0; + } + UNLOCK(pipenum); + + return num; +} + +void io_fifo_clear(int pipenum) +{ + io_wridx[pipenum] = io_rdidx[pipenum] = 0; +} + +int io_fifo_freespace(int pipenum) +{ + int freebuf = 0; + + LOCK(pipenum); + + int elemInFifo = (io_wridx[pipenum] + AUDIO_FIFOFLEN - io_rdidx[pipenum]) % AUDIO_FIFOFLEN; + freebuf = AUDIO_FIFOFLEN - elemInFifo -1; + + UNLOCK(pipenum); + return freebuf; +} + +int io_fifo_elems_avail(int pipenum) +{ + int elems = 0; + + LOCK(pipenum); + elems = (io_wridx[pipenum] + AUDIO_FIFOFLEN - io_rdidx[pipenum]) % AUDIO_FIFOFLEN; + UNLOCK(pipenum); + + elems -= 10; + return elems; +} + +int io_fifo_usedspace(int pipenum) +{ + return AUDIO_FIFOFLEN - io_fifo_freespace(pipenum); +} + +int io_fifo_usedpercent(int pipenum) +{ + int used = AUDIO_FIFOFLEN - io_fifo_freespace(pipenum); + int percent = (used * 100) / AUDIO_FIFOFLEN; + //printf("idx:%d used:%d size:%d percent:%d\n", pipenum, used, AUDIO_FIFOFLEN, percent); + return percent; +} + diff --git a/hsmodem/libkmaudio/libkmaudio_getDevices.cpp b/hsmodem/libkmaudio/libkmaudio_getDevices.cpp new file mode 100755 index 0000000..2cef52a --- /dev/null +++ b/hsmodem/libkmaudio/libkmaudio_getDevices.cpp @@ -0,0 +1,293 @@ +/* +* Audio Library for Linux and Windows +* =================================== +* Author: DJ0ABR +* +* Author: Kurt Moraw, Ham radio: DJ0ABR, github: dj0abr +* License: GPL-3 +* +* compilation: +* Windows ... Visual Studio +* Linux ... make +* +* Documentation see: libkmaudio.h +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* libkmaudio_getDevices.cpp +* read audio device list via portaudio (which is only used for Windows) +* prepare a device name string which can be read by another program +* in order to present the devices to use user for selection +* +*/ + +#include "libkmaudio.h" + +void io_buildAudioDevString(); + +// number of detected devices, updated after a call to kmaudio_getDeviceList() +int devanz = 0; + +// devlist contains all information for all detected devices +// the list is filled by a call to kmaudio_getDeviceList() +DEVLIST devlist[MAXDEVICES]; + +double latency = 0.2; // WASAPI latency in seconds + +#ifdef WIN32 + +/*static double standardSampleRates[] = { +8000.0, 9600.0, 11025.0, 12000.0, 16000.0, +22050.0, 24000.0, 32000.0, 44100.0, 48000.0, +88200.0, 96000.0, 192000.0, -1 };*/ + +static double standardSampleRates[] = {44100.0, 48000.0, -1}; + +int getDevlistIndex(const char* name, int ichans, int ochans) +{ + for (int i = 0; i < devanz; i++) + { + // check if already exists + if (!strcmp(devlist[i].name, name) && + devlist[i].inputParameters.channelCount == ichans && + devlist[i].outputParameters.channelCount == ochans) + return i; + } + + int newidx = devanz; + devanz++; + //printf("New Dev:%s Idx:%d\n", name, newidx); + return newidx; +} + +int kmaudio_getDeviceList() +{ + int numDevices = Pa_GetDeviceCount(); + if (numDevices < 0) + { + printf("ERROR: Pa_GetDeviceCount returned 0x%x\n", numDevices); + return -1; + } + + //printf("%d Devices found\n", numDevices); + + for (int i = 0; i < devanz; i++) + devlist[i].active = 0; + + int didx; + for (int i = 0; i < numDevices; i++) + { + const PaDeviceInfo* deviceInfo = Pa_GetDeviceInfo(i); + const PaHostApiInfo *ai = Pa_GetHostApiInfo(deviceInfo->hostApi); + + // Windows: use WASAPI devices only + if (strstr(ai->name, "WASAPI") != NULL) + { + didx = getDevlistIndex(deviceInfo->name, deviceInfo->maxInputChannels, deviceInfo->maxOutputChannels); + + devlist[didx].devnum = i; + snprintf(devlist[didx].name, MAXDEVNAMELENGTH - 1, "%s", deviceInfo->name); + //printf("------%s-------\n", deviceInfo->name); + + devlist[didx].inputParameters.device = i; + devlist[didx].inputParameters.channelCount = deviceInfo->maxInputChannels; + devlist[didx].inputParameters.sampleFormat = paInt16; + devlist[didx].inputParameters.suggestedLatency = latency; + devlist[didx].inputParameters.hostApiSpecificStreamInfo = NULL; + + devlist[didx].outputParameters.device = i; + devlist[didx].outputParameters.channelCount = deviceInfo->maxOutputChannels; + devlist[didx].outputParameters.sampleFormat = paInt16; + devlist[didx].outputParameters.suggestedLatency = latency; + devlist[didx].outputParameters.hostApiSpecificStreamInfo = NULL; + + if (devlist[didx].inputParameters.channelCount > 0 && devlist[devanz].outputParameters.channelCount > 0) + devlist[didx].in_out = 2; + else if (devlist[didx].inputParameters.channelCount > 0) + devlist[didx].in_out = 0; + else if (devlist[didx].outputParameters.channelCount > 0) + devlist[didx].in_out = 1; + + devlist[didx].index = didx; + devlist[didx].active = 1; + + for (int j = 0; standardSampleRates[j] > 0; j++) + { + PaError err = 0; + //if (devlist[didx].inputParameters.channelCount > 0 && devlist[didx].outputParameters.channelCount > 0) + // err = Pa_IsFormatSupported(&devlist[didx].inputParameters, &devlist[didx].outputParameters, standardSampleRates[j]); + if (devlist[didx].inputParameters.channelCount > 0) + err = Pa_IsFormatSupported(&devlist[didx].inputParameters, NULL, standardSampleRates[j]); + if (devlist[didx].outputParameters.channelCount > 0) + err = Pa_IsFormatSupported(NULL, &devlist[didx].outputParameters, standardSampleRates[j]); + + // portaudio cannot detect if a device was removed, instead it delivers errors + if (err == paInvalidDevice) + devlist[didx].active = 0; + else if (err == paFormatIsSupported) + { + if (j == 0) devlist[didx].supports_44100 = 1; + if (j == 1) devlist[didx].supports_48000 = 1; + } + } + } + } + + io_buildAudioDevString(); + + // close stream if a device does not exist any more + for (int i = 0; i < devanz; i++) + { + if (devlist[i].active == 0) + { + if (devlist[i].capStream != NULL) + { + printf("capture device %s disconnected, stop stream\n", devlist[i].name); + Pa_CloseStream(devlist[i].capStream); + devlist[i].capStream = NULL; + devlist[i].working = 0; + } + + if (devlist[i].pbStream != NULL) + { + printf("playback device %s disconnected, stop stream\n", devlist[i].name); + Pa_CloseStream(devlist[i].pbStream); + devlist[i].pbStream = NULL; + devlist[i].working = 0; + } + } + } + + static int csum = 0; + int sum = 0; + uint8_t* p = (uint8_t*)&(devlist[0].index); + for (int i = 0; i < (int)sizeof(devlist); i++) + sum += *p++; + + if (csum != sum) + { + csum = sum; + + printf("Windows Devices found:\n"); + for (int i = 0; i < devanz; i++) + { + printf("Portaudio ID: %d\n", devlist[i].index); + printf("Name: %s\n", devlist[i].name); + printf("Cap/PB: %d\n", devlist[i].in_out); + printf("Channels: i:%d o:%d\n", devlist[i].inputParameters.channelCount, devlist[i].outputParameters.channelCount); + printf("SR 44100: %d\n", devlist[i].supports_44100); + printf("SR 48000: %d\n", devlist[i].supports_48000); + printf("is active: %s\n", devlist[i].active ? "yes" : "no"); + } + } + + return 0; +} + +#endif //WIN32 + + +// find a device in devlist +// returns: list index or -1 if error +int searchDevice(char* devname, int io) +{ + for (int i = 0; i < devanz; i++) + { + if (strcmp(devname, devlist[i].name) == 0 && (devlist[i].in_out == io || devlist[i].in_out == 2)) + return i; + } + return -1; +} + +// choose physical, real sample rate for a device +// returns: 0=ok, -1=error: no sample rate supported +int getRealSamprate(int idx) +{ + if (devlist[idx].requested_samprate == 44100) + { + if (devlist[idx].supports_44100) devlist[idx].real_samprate = 44100; + else if (devlist[idx].supports_48000) devlist[idx].real_samprate = 48000; + else return -1; + } + + else if (devlist[idx].requested_samprate == 48000) + { + if (devlist[idx].supports_48000) devlist[idx].real_samprate = 48000; + else if (devlist[idx].supports_44100) devlist[idx].real_samprate = 44100; + else return -1; + } + + return 0; +} + +// build string of audio device name, to be sent to application as response to Broadcast search +// starting with PB devices, sperarator ^, capture devices +// separator between devices: ~ +// the first character is 0 or 1 and does not belong to the device name +// it shows if this device was sucessfully started and is currently running (="1") +#define MAXDEVSTRLEN (MAXDEVICES * (MAXDEVNAMELENGTH + 2) + 10) +uint8_t io_devstring[MAXDEVSTRLEN]; + +void io_buildAudioDevString() +{ + memset(io_devstring, 0, sizeof(io_devstring)); + io_devstring[0] = ' '; // placeholder for ID for this UDP message + + // playback devices + for (int i = 0; i < devanz; i++) + { + if (devlist[i].active == 0) continue; + if (strlen((char *)io_devstring) > MAXDEVSTRLEN) + { + printf("io_devstring too small:%d / %d. Serious error, abort program\n", MAXDEVSTRLEN, (int)strlen((char*)io_devstring)); + exit(0); + } + if (devlist[i].in_out == 1) + { + strcat((char*)io_devstring, devlist[i].working?"1":"0"); + strcat((char*)io_devstring, devlist[i].name); + strcat((char*)io_devstring, "~"); // audio device separator + } + } + + strcat((char*)(io_devstring + 1), "^"); // PB, CAP separator + + // capture devices + for (int i = 0; i < devanz; i++) + { + if (devlist[i].active == 0) continue; + if (strlen((char*)io_devstring) > MAXDEVSTRLEN) + { + printf("io_devstring too small:%d / %d. Serious error, abort program\n", MAXDEVSTRLEN, (int)strlen((char*)io_devstring)); + exit(0); + } + if (devlist[i].in_out == 0) + { + strcat((char*)io_devstring, devlist[i].working ? "1" : "0"); + strcat((char*)io_devstring, devlist[i].name); + strcat((char*)io_devstring, "~"); // audio device separator + } + } + + //printf("<%s>\n", (char *)io_devstring); + + io_devstring[0] = 3; // ID for this message +} + +uint8_t* io_getAudioDevicelist(int* len) +{ + *len = strlen((char*)(io_devstring + 1)) + 1; + return io_devstring; +} diff --git a/hsmodem/libkmaudio/libkmaudio_getDevices_Linux.cpp b/hsmodem/libkmaudio/libkmaudio_getDevices_Linux.cpp new file mode 100755 index 0000000..6284d67 --- /dev/null +++ b/hsmodem/libkmaudio/libkmaudio_getDevices_Linux.cpp @@ -0,0 +1,217 @@ +/* +* Audio Library for Linux and Windows +* =================================== +* Author: DJ0ABR +* +* Author: Kurt Moraw, Ham radio: DJ0ABR, github: dj0abr +* License: GPL-3 +* +* compilation: +* Windows ... Visual Studio +* Linux ... make +* +* Documentation see: libkmaudio.h +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* libkmaudio_getDevices_linux.cpp +* like libkmaudio_getDevices.cpp, but uses libsoundio under Linux +* to get the device list. Portaudio does not work under Linux because +* it does not support pulseaudio. Therefore the linux functions +* use libsoundio +* +*/ + +#ifndef WIN32 // Linux + +#include "libkmaudio.h" + +int scan_devices(); + +int kmaudio_getDeviceList() +{ + if (soundio == NULL) + { + printf("kmaudio_getDeviceList: soundio not initialized\n"); + return -1; + } + + soundio_flush_events(soundio); // to get actual data + + if (scan_devices() == -1) // read devices + { + printf("cannot read audio devices\n"); + return -1; + } + + io_buildAudioDevString(); + + // close stream if a device does not exist any more + for (int i = 0; i < devanz; i++) + { + if (devlist[i].active == 0) + { + if (devlist[i].instream != NULL) + { + printf("capture device %s disconnected, stop stream\n", devlist[i].name); + soundio_instream_destroy(devlist[i].instream); + devlist[i].instream = NULL; + devlist[i].working = 0; + } + + if(devlist[i].outstream != NULL) + { + printf("playback device %s disconnected, stop stream\n", devlist[i].name); + soundio_outstream_destroy(devlist[i].outstream); + devlist[i].outstream = NULL; + devlist[i].working = 0; + } + } + } + + static int csum = 0; + int sum = 0; + uint8_t* p = (uint8_t*)&(devlist[0].index); + for (int i = 0; i < (int)sizeof(devlist); i++) + sum += *p++; + + if (csum != sum) + { + csum = sum; + + printf("====== Linux Devices found: ======\n"); + for (int i = 0; i < devanz; i++) + { + printf("Index: %d\n", devlist[i].index); + printf("Name: %s\n", devlist[i].name); + printf("ID: %s\n", devlist[i].id); + printf("Cap/PB: %d\n", devlist[i].in_out); + printf("Channels: %d\n", devlist[i].stereo_mono); + printf("SR 44100: %d\n", devlist[i].supports_44100); + printf("SR 48000: %d\n", devlist[i].supports_48000); + printf("is active: %s\n", devlist[i].active ? "yes" : "no"); + printf("--------------------------------------\n"); + } + } + return 0; +} + +static void get_channel_layout(const struct SoundIoChannelLayout* layout) +{ + if (layout->name) + { + if (strstr(layout->name, "ereo")) + devlist[devanz].stereo_mono = 2; + if (strstr(layout->name, "ono")) + devlist[devanz].stereo_mono = 1; + } +} + +int getDeviceParameters(int idx, struct SoundIoDevice* device) +{ + strncpy(devlist[idx].id, device->id, sizeof(devlist[0].id) - 1); + devlist[idx].id[sizeof(devlist[0].id) - 1] = 0; + strncpy(devlist[idx].name, device->name, sizeof(devlist[0].name) - 1); + devlist[idx].name[sizeof(devlist[0].name) - 1] = 0; + + for (int i = 0; i < device->layout_count; i++) + get_channel_layout(&device->layouts[i]); + + int min = 999999, max = 0; + for (int i = 0; i < device->sample_rate_count; i++) + { + struct SoundIoSampleRateRange* range = &device->sample_rates[i]; + if (range->min < min) + min = range->min; + + if (range->max > max) + max = range->max; + } + if (min <= 44100) devlist[idx].supports_44100 = 1; + if (max >= 48000) devlist[idx].supports_48000 = 1; + if (devlist[idx].supports_44100 == 0 && devlist[idx].supports_48000 == 0) return 0; + return 1; +} + +int getDevlistIndex(char* name, char* id) +{ + for (int i = 0; i < devanz; i++) + { + // check if already exists + if (!strcmp(devlist[i].id, id) && !strcmp(devlist[i].name, name)) + return i; + } + + int newidx = devanz; + devanz++; + //printf("New Dev:%s Idx:%d\n", name, newidx); + return newidx; +} + +int scan_devices() +{ + for (int i = 0; i < devanz; i++) + devlist[i].active = 0; + + int didx; + for (int i = 0; i < soundio_input_device_count(soundio); i++) + { + struct SoundIoDevice* device = soundio_get_input_device(soundio, i); + if (device == NULL) continue; + if (strstr(device->name, "onitor")) continue; + if (device->probe_error) continue; + + didx = getDevlistIndex(device->name, device->id); + if (getDeviceParameters(didx, device) == 1) + { + //printf("%d %d ====CAP:\nid:<%s>\nname:<%s>\n", i,devanz,device->id, device->name); + devlist[didx].in_out = 0; + devlist[didx].index = didx; + devlist[didx].active = 1; + } + else + { + *devlist[didx].name = 0; + *devlist[didx].id = 0; + } + soundio_device_unref(device); + } + + for (int i = 0; i < soundio_output_device_count(soundio); i++) + { + struct SoundIoDevice* device = soundio_get_output_device(soundio, i); + if (device == NULL) continue; + if (strstr(device->name, "onitor")) continue; + if (device->probe_error) continue; + + didx = getDevlistIndex(device->name, device->id); + if (getDeviceParameters(didx, device) == 1) + { + //printf("====PB :\nid:<%s>\nname:<%s>\n", device->id, device->name); + devlist[didx].in_out = 1; + devlist[didx].index = didx; + devlist[didx].active = 1; + } + else + { + *devlist[didx].name = 0; + *devlist[didx].id = 0; + } + soundio_device_unref(device); + } + return 0; +} + +#endif // ifndef WIN32 \ No newline at end of file diff --git a/hsmodem/libkmaudio/libkmaudio_init.cpp b/hsmodem/libkmaudio/libkmaudio_init.cpp new file mode 100755 index 0000000..0e3ec8d --- /dev/null +++ b/hsmodem/libkmaudio/libkmaudio_init.cpp @@ -0,0 +1,171 @@ +/* +* Audio Library for Linux and Windows +* =================================== +* Author: DJ0ABR +* +* Author: Kurt Moraw, Ham radio: DJ0ABR, github: dj0abr +* License: GPL-3 +* +* compilation: +* Windows ... Visual Studio +* Linux ... make +* +* Documentation see: libkmaudio.h +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* libkmaudio_init.cpp ... initialize portaudio (Windows only) +* +*/ + +#include "libkmaudio.h" + +void kmaudio_close(); + +//int keeprunning = 1; // to stop callbacks at program end + +int kmaudio_init() +{ + kmaudio_close(); + sleep_ms(100); + + printf("libkmaudio_init\n"); + + keeprunning = 1; + init_pipes(); // init fifo + init_maxarray(); // init array for maxlevel measurement + +#ifdef WIN32 + int err = Pa_Initialize(); + if (err != paNoError) + { + printf("ERROR: Pa_Initialize returned 0x%x\n", err); + return -1; + } + + printf("PortAudio version: 0x%08X\n", Pa_GetVersion()); +#else + return kmaudio_init_linux(); +#endif + + return 0; +} + +void kmaudio_close() +{ + printf("libkmaudio_close\n"); + +#ifdef WIN32 + for (int i = 0; i < devanz; i++) + { + if (devlist[i].capStream != NULL) + { + Pa_CloseStream(devlist[i].capStream); + devlist[i].capStream = NULL; + } + if (devlist[i].pbStream != NULL) + { + Pa_CloseStream(devlist[i].pbStream); + devlist[i].pbStream = NULL; + } + } + Pa_Terminate(); + +#else + kmaudio_close_linux(); +#endif + keeprunning = 0; +} +/* +// diagonstic routines for development + +#define MAXSPDARR 10 +int spdarr[MAXSPDARR]; +int spdarrbps[MAXSPDARR]; + +#ifdef _LINUX_ +uint64_t getms() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + uint64_t at = tv.tv_sec * 1000000 + tv.tv_usec; + at = at / 1000; + return at; +} +#endif + +#ifdef WIN32 +uint64_t getms() +{ + // get time in 100ns resolution + FILETIME ft_now; + GetSystemTimeAsFileTime(&ft_now); + + // convert to full 64 bit time + uint64_t ll_now = (uint64_t)ft_now.dwLowDateTime + ((uint64_t)(ft_now.dwHighDateTime) << 32LL); + + // convert to Milliseconds + ll_now /= (10 * 1000); // still needs 64 bit integer + + return ll_now; +} +#else +uint64_t getms() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + uint64_t at = tv.tv_sec * 1000000 + tv.tv_usec; + at = at / 1000; + return at; +} +#endif + +void measure_speed_bps(int len) +{ + static uint64_t lasttim = 0; + static int elems = 0; + + uint64_t tim = getms(); + int timespan = (int)(tim - lasttim); + if (timespan < 0) + { + lasttim = tim; + return; + } + + elems += len; + if (timespan < 1000) return; + + double dspd = elems; + dspd = dspd * 1e3 / timespan; + int speed = (int)dspd; + + // here we have number of elements after 1s + printf(" ======================= %d bit/s\n", speed); + + elems = 0; + lasttim = tim; +} + +void sleep_ms(int ms) +{ +#ifdef WIN32 + Sleep(ms); +#else + usleep(ms * 1000); +#endif +} + +*/ diff --git a/hsmodem/libkmaudio/libkmaudio_init_linux.cpp b/hsmodem/libkmaudio/libkmaudio_init_linux.cpp new file mode 100755 index 0000000..cf0c513 --- /dev/null +++ b/hsmodem/libkmaudio/libkmaudio_init_linux.cpp @@ -0,0 +1,78 @@ +/* +* Audio Library for Linux and Windows +* =================================== +* Author: DJ0ABR +* +* Author: Kurt Moraw, Ham radio: DJ0ABR, github: dj0abr +* License: GPL-3 +* +* compilation: +* Windows ... Visual Studio +* Linux ... make +* +* Documentation see: libkmaudio.h +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* libkmaudio_init_linux.cpp ... initialize libsoundio (Linux only) +* +*/ +#include "libkmaudio.h" + +struct SoundIo* soundio = NULL; + +#ifndef WIN32 // Linux + +int kmaudio_init_linux() +{ + int err; + + // prepare and connect to libsoundio + soundio = soundio_create(); + if (!soundio) { + printf("soundio_create: out of memory\n"); + return -1; + } + + if ((err = soundio_connect(soundio))) { + printf("soundio_connect: %s\n", soundio_strerror(err)); + return -1; + } + + return 0; +} + +void kmaudio_close_linux() +{ + for (int i = 0; i < devanz; i++) + { + if (devlist[i].instream) soundio_instream_destroy(devlist[i].instream); + devlist[i].instream = NULL; + + if (devlist[i].outstream) soundio_outstream_destroy(devlist[i].outstream); + devlist[i].outstream = NULL; + + if (devlist[i].io_pb_device) soundio_device_unref(devlist[i].io_pb_device); + devlist[i].io_pb_device = NULL; + + if (devlist[i].io_cap_device) soundio_device_unref(devlist[i].io_cap_device); + devlist[i].io_cap_device = NULL; + } + + if (soundio) soundio_destroy(soundio); + soundio = NULL; +} + +#endif // ndef WIN32 diff --git a/hsmodem/libkmaudio/libkmaudio_interface.cpp b/hsmodem/libkmaudio/libkmaudio_interface.cpp new file mode 100755 index 0000000..d53c5a8 --- /dev/null +++ b/hsmodem/libkmaudio/libkmaudio_interface.cpp @@ -0,0 +1,155 @@ + +#include "libkmaudio.h" + +void getMax(int id, float fv); + +/* +* reads len samples from device id into psamp +* returns: number of values written to psamp , -1=error +* id ... device id returned by kmaudio_startCapture +* psamp ... float array of length len getting the audio data (mono) +* len ... number of float values to write into psamp +* volume ... 0.0f..2.0f will be multiplied with the input sample +* wait ... 1=wait for data, 0=return if not enough data available (in this case psamp will return 0,0,0...) +* +* if resampling is required the number of returned samples may differ from the number of requested samples +* it can be larger, so the buffer psamp should be larger than "len" by factor 1.1 +*/ + +int kmaudio_readsamples(int id, float* psamp, int len, float volume, int wait) +{ + int e = io_fifo_elems_avail(id); + if (e < len) return 0; + + if (devlist[id].requested_samprate == devlist[id].real_samprate) + { + // data rate is ok, take samples as is + int nl = io_read_fifo_num(id, psamp, len); + for (int i = 0; i < nl; i++) + { + psamp[i] *= volume; + getMax(id, psamp[i]); + //kmaudio_detectDropouts(id); + } + return nl; + } + + // resampling is required + int num = io_read_fifo_num(id, psamp, len); + if (num == 0) return 0; + + int newlen = 0; + float *f = resample(id, psamp, len, &newlen); + for (int i = 0; i < newlen; i++) + { + psamp[i] = f[i] * volume; + getMax(id, psamp[i]); + } + + return newlen; +} + +/* +* plays len samples from psamp to device id +* returns: 0=ok, -1=error +* id ... device id returned by kmaudio_startPlayback +* psamp ... float array of length len with the audio data (mono) +* len ... number of float values in psamp +* volume ... 0.0f..2.0f will be multiplied with the output sample +*/ + +int kmaudio_playsamples(int id, float* psamp, int len, float volume) +{ + // check if resampling is required + //printf("%d %d\n", devlist[id].requested_samprate , devlist[id].real_samprate); + if (devlist[id].requested_samprate == devlist[id].real_samprate) + { + // sampling rate is ok, just play samples + for (int i = 0; i < len; i++) + { + io_write_fifo(id, psamp[i] * volume); + getMax(id, psamp[i] * volume); + } + return 0; + } + + // resampling is required + int newlen = 0; + float *f = resample(id, psamp, len, &newlen); + for (int i = 0; i < newlen; i++) + { + io_write_fifo(id, f[i] * volume); + getMax(id, psamp[i] * volume); + } + + return 0; +} + +#define MCHECK 48000 // abt. 1s of samples +float farr[MAXDEVICES][MCHECK]; +int farridx[MAXDEVICES]; + +void init_maxarray() +{ + // initialize arrays + for (int md = 0; md < MAXDEVICES; md++) + { + farridx[md] = 0; + for (int i = 0; i < MCHECK; i++) + farr[md][i] = 0; + } +} + +void getMax(int id, float fv) +{ + // put value into array + farr[id][farridx[id]] = fv; + if (++farridx[id] == MCHECK) + farridx[id] = 0; +} + +/* +* returns the max level (within 1 second) of this stream in % (0..100) +* if the level >= 100 the signal will get clipped and distorted +*/ + +uint8_t kmaudio_maxlevel(int id) +{ + float max = 0; + for (int i = 0; i < MCHECK; i++) + if (farr[id][i] > max) max = farr[id][i]; + + return (uint8_t)(max * 100); +} + +void kmaudio_detectDropouts(int id) +{ + int dropout = 0; + int stat = 0; + int drlen = 0; + + for (int i = 0; i < MCHECK; i++) + { + switch (stat) + { + case 0: // search beginning of dropout + if (farr[id][i] == 0.0f) + stat = 1; + break; + + case 1: // count length of dropout + if (farr[id][i] == 0.0f) drlen++; + else + { + // end of dropout + if (drlen > 0) + { + printf("Dropout len:%d\n", drlen); + } + drlen = 0; + stat = 0; + } + break; + } + } +} \ No newline at end of file diff --git a/hsmodem/libkmaudio/libkmaudio_playback.cpp b/hsmodem/libkmaudio/libkmaudio_playback.cpp new file mode 100755 index 0000000..67f6319 --- /dev/null +++ b/hsmodem/libkmaudio/libkmaudio_playback.cpp @@ -0,0 +1,169 @@ +/* +* Audio Library for Linux and Windows +* =================================== +* Author: DJ0ABR +* +* Author: Kurt Moraw, Ham radio: DJ0ABR, github: dj0abr +* License: GPL-3 +* +* compilation: +* Windows ... Visual Studio +* Linux ... make +* +* Documentation see: libkmaudio.h +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* libkmaudio_playback.cpp ... +* starts a portaudio playback stream and a callback routine. Plays the +* audio samples coming via the fifo (Windows only) +*/ + +#include "libkmaudio.h" + +#define FRAMES_PER_BUFFER 512 + +int playCallback(const void* inputBuffer, + void* outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void* userData); + +int kmaudio_startPlayback(char* devname, int samprate) +{ + printf("Start request for PB stream:%s\n", devname); + + if (devname == NULL || strlen(devname) < 3) // no devices defined yet + { + printf("no PB devices specified\n"); + return -1; + } + + int idx = searchDevice(devname, 1); + if (idx == -1) + { + printf("Playback Device:<%s> not found\n", devname); + return -1; + } + + devlist[idx].working = 0; + + if (devlist[idx].pbStream != NULL) + { + printf("Closing old PB stream:%s [%d]\n", devname, idx); + Pa_CloseStream(devlist[idx].pbStream); + devlist[idx].pbStream = NULL; + } + + printf("Starting PB stream:%s [%d]\n", devname, idx); + + io_fifo_clear(idx); + + devlist[idx].requested_samprate = samprate; + if (getRealSamprate(idx) == -1) + { + printf("Samplerate %d not supported by device:<%s>\n", samprate, devname); + return -1; + } + + if (devlist[idx].requested_samprate != devlist[idx].real_samprate) + resampler_create(idx); + + struct PaWasapiStreamInfo wasapiInfo; + memset(&wasapiInfo, 0, sizeof(PaWasapiStreamInfo)); + wasapiInfo.size = sizeof(PaWasapiStreamInfo); + wasapiInfo.hostApiType = paWASAPI; + wasapiInfo.version = 1; + wasapiInfo.flags = (paWinWasapiExclusive | paWinWasapiThreadPriority); + wasapiInfo.threadPriority = eThreadPriorityProAudio; + + devlist[idx].outputParameters.hostApiSpecificStreamInfo = (&wasapiInfo); + + PaError e = Pa_IsFormatSupported(&devlist[idx].outputParameters, NULL, (double)devlist[idx].real_samprate); + printf("Playback : err:%d device:%d PAdev:%d samprate: %f chans:%d\n", e, idx, devlist[idx].devnum, (double)devlist[idx].real_samprate, devlist[idx].outputParameters.channelCount); + + devlist[idx].index = idx; + + int err = Pa_OpenStream( + &devlist[idx].pbStream, + NULL, + &devlist[idx].outputParameters, + (double)devlist[idx].real_samprate, + FRAMES_PER_BUFFER, + paClipOff, + playCallback, + &(devlist[idx].index)); + + if (err != paNoError) + { + printf("cannot open playback stream for device:<%s> %d\n", devname, err); + return -1; + } + + err = Pa_StartStream(devlist[idx].pbStream); + if (err != paNoError) + { + printf("cannot start playback stream for device:<%s>\n", devname); + return -1; + } + + printf("Playback started sucessfully\n"); + devlist[idx].working = 1; + return idx; +} + +int playCallback( const void* inputBuffer, + void* outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void* userData) +{ + int16_t* rptr = (int16_t*)outputBuffer; + int devidx = *((int*)userData); + int chans = devlist[devidx].outputParameters.channelCount; + + //measure_speed_bps(framesPerBuffer); + + int16_t f[FRAMES_PER_BUFFER]; + memset(f, 0, sizeof(int16_t) * FRAMES_PER_BUFFER); + unsigned int num = io_read_fifo_num_short(devidx, f, framesPerBuffer); + if (num < framesPerBuffer) + { + //printf("got %d from fifo, requested %d\n", num, framesPerBuffer); + } + int av = io_fifo_elems_avail(devidx); + + for (unsigned int i = 0; i < framesPerBuffer; i++) + { + if (chans == 1) rptr[i] = f[i]; + else + { + rptr[i * 2] = f[i]; + rptr[i * 2 + 1] = f[i]; + } + } + + // Prevent unused variable warnings + (void)inputBuffer; + (void)timeInfo; + (void)statusFlags; + + if (keeprunning == 1) + return paContinue; + + return paComplete; +} diff --git a/hsmodem/libkmaudio/libkmaudio_playback_linux.cpp b/hsmodem/libkmaudio/libkmaudio_playback_linux.cpp new file mode 100755 index 0000000..18f064e --- /dev/null +++ b/hsmodem/libkmaudio/libkmaudio_playback_linux.cpp @@ -0,0 +1,236 @@ +/* +* Audio Library for Linux and Windows +* =================================== +* Author: DJ0ABR +* +* Author: Kurt Moraw, Ham radio: DJ0ABR, github: dj0abr +* License: GPL-3 +* +* compilation: +* Windows ... Visual Studio +* Linux ... make +* +* Documentation see: libkmaudio.h +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* libkmaudio_playback.cpp ... +* starts a libsoundio playback stream and a callback routine. Plays the +* audio samples coming via the fifo (Linux only) +*/ +#ifndef WIN32 + +#include "libkmaudio.h" + +// #define SINEWAVETEST + +#ifdef SINEWAVETEST +static const double PI = 3.14159265358979323846264338328; +static double seconds_offset = 0.0; +#endif + +void write_sample_float32ne(char* ptr, double sample) +{ + float* buf = (float*)ptr; + *buf = (float)sample; +} + +static void write_callback(struct SoundIoOutStream* outstream, int frame_count_min, int frame_count_max) +{ + if (outstream == NULL || soundio == NULL) return; + //printf("pb: %d %d\n", frame_count_min, frame_count_max); + //printf("pb :%d\n", outstream->sample_rate); + int idx = *((int*)(outstream->userdata)); + +#ifdef SINEWAVETEST + double float_sample_rate = outstream->sample_rate; + double seconds_per_frame = 1.0 / float_sample_rate; + double pitch = 440.0; + double radians_per_second = pitch * 2.0 * PI; +#endif + struct SoundIoChannelArea* areas; + int err; + + int frames_left = 4800; + if (frame_count_max < frames_left) + frames_left = frame_count_max; + + for (;;) { + int frame_count = frames_left; + if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count))) { + fprintf(stderr, "write_callback unrecoverable soundio_outstream_begin_write error: %s\n", soundio_strerror(err)); + return; + } + + if (!frame_count) + break; + + //printf("ck: %d read from fifo:%d\n", frame_count,idx); + if (frame_count >= 10000) + { + printf("frame count >= 1000: %d\n", frame_count); + exit(0); + } + + float f[10000]; + memset(f, 0, sizeof(float) * frame_count); + if (io_fifo_elems_avail(idx) >= frame_count) + { + // if fifo does not have enough data, don't take any + // this gives the fifo a chance to fill up a bit + io_read_fifo_num(idx, f, frame_count); + } + + //measure_speed_bps(frame_count); + + const struct SoundIoChannelLayout* layout = &outstream->layout; + + for (int frame = 0; frame < frame_count; frame += 1) + { +#ifdef SINEWAVETEST + double sample = sin((seconds_offset + frame * seconds_per_frame) * radians_per_second); +#endif + for (int channel = 0; channel < layout->channel_count; channel += 1) + { + float ftx = f[frame]; + //getTXMax(f[frame]); +#ifdef SINEWAVETEST + write_sample_float32ne(areas[channel].ptr, sample); // sine wave test tone +#endif + write_sample_float32ne(areas[channel].ptr, ftx); + areas[channel].ptr += areas[channel].step; + } + } +#ifdef SINEWAVETEST + seconds_offset = fmod(seconds_offset + seconds_per_frame * frame_count, 1.0); +#endif + + if ((err = soundio_outstream_end_write(outstream))) { + if (err == SoundIoErrorUnderflow) + return; + fprintf(stderr, "unrecoverable stream error: %s\n", soundio_strerror(err)); + return; + } + + frames_left -= frame_count; + if (frames_left <= 0) + break; + } +} + +void underflow_callback(struct SoundIoOutStream* outstream) +{ + static int count = 0; + printf("underflow %d\n", count++); +} + +int kmaudio_startPlayback(char* devname, int samprate) +{ + printf("Start request for PB stream:%s\n", devname); + + if (devname == NULL || strlen(devname) < 3) // no devices defined yet + { + printf("no PB devices specified\n"); + return -1; + } + + int idx = 0; // index into devlist + char* pbdevid = getDevID(devname, 1, &idx); + if (pbdevid == NULL) return -1; + + // if an old stream is open, close it + if (devlist[idx].outstream != NULL) + { + printf("Closing old PB stream:%s [%d]\n", devname, idx); + soundio_outstream_destroy(devlist[idx].outstream); + devlist[idx].outstream = NULL; + } + + printf("Starting PB stream:%s [%d]\n", devname, idx); + + io_fifo_clear(idx); + + devlist[idx].working = 0; + + // define the capture device + soundio_flush_events(soundio); + + for (int i = 0; i < soundio_output_device_count(soundio); i++) + { + devlist[idx].io_pb_device = NULL; + struct SoundIoDevice* device = soundio_get_output_device(soundio, i); + if (strcmp(device->id, pbdevid) == 0) + { + devlist[idx].io_pb_device = device; + break; + } + soundio_device_unref(device); + } + if (!devlist[idx].io_pb_device) + { + printf("Invalid device id: %s\n", pbdevid); + return -1; + } + + if (devlist[idx].io_pb_device->probe_error) + { + printf("Unable to probe device: %s\n", soundio_strerror(devlist[idx].io_pb_device->probe_error)); + return -1; + } + + // create playback callback + devlist[idx].outstream = soundio_outstream_create(devlist[idx].io_pb_device); + if (!devlist[idx].outstream) { + printf("soundio_outstream_create: out of memory\n"); + return 0; + } + + devlist[idx].requested_samprate = samprate; + if (getRealSamprate(idx) == -1) + { + printf("Samplerate %d not supported by device:<%s>\n", samprate, devname); + return -1; + } + + if (devlist[idx].requested_samprate != devlist[idx].real_samprate) + resampler_create(idx); + + devlist[idx].outstream->format = SoundIoFormatFloat32NE; + devlist[idx].outstream->sample_rate = devlist[idx].real_samprate; + devlist[idx].outstream->software_latency = 0.1f; + devlist[idx].outstream->write_callback = write_callback; + devlist[idx].outstream->underflow_callback = underflow_callback; + devlist[idx].outstream->userdata = &(devlist[idx].index); + + int err = 0; + if ((err = soundio_outstream_open(devlist[idx].outstream))) { + printf("unable to open output stream: %s", soundio_strerror(err)); + return -1; + } + + if ((err = soundio_outstream_start(devlist[idx].outstream))) { + printf("unable to start output device: %s", soundio_strerror(err)); + return -1; + } + + printf("selected PLAYBACK device:\nname:%s\nid :%s\n", devname, pbdevid); + printf("physical playback rate:%d, requested capture rate:%d\n", devlist[idx].real_samprate, devlist[idx].requested_samprate); + printf("format: %s\n\n", soundio_format_string(devlist[idx].outstream->format)); + + devlist[idx].working = 1; + return idx; +} + +#endif // ndef WIN32 diff --git a/hsmodem/libkmaudio/libkmaudio_resampler.cpp b/hsmodem/libkmaudio/libkmaudio_resampler.cpp new file mode 100755 index 0000000..caee21e --- /dev/null +++ b/hsmodem/libkmaudio/libkmaudio_resampler.cpp @@ -0,0 +1,109 @@ +/* +* Audio Library for Linux and Windows +* =================================== +* Author: DJ0ABR +* +* Author: Kurt Moraw, Ham radio: DJ0ABR, github: dj0abr +* License: GPL-3 +* +* compilation: +* Windows ... Visual Studio +* Linux ... make +* +* Documentation see: libkmaudio.h +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* libkmaudio_resampler.cpp ... converts audio streams +* between 44100 and 48000 samples/s in both directions +* uses the libliquid library +*/ +#include "libkmaudio.h" + +#define MAXRLEN 3000 + +resamp_crcf q[MAXDEVICES]; +float fresamp[MAXDEVICES][MAXRLEN]; + +unsigned int h_len = 13; // filter semi-length (filter delay) +float r = 0.9f; // resampling rate (output/input) +float bw = 0.45f; // resampling filter bandwidth +float slsl = 60.0f; // resampling filter sidelobe suppression level +unsigned int npfb = 32; // number of filters in bank (timing resolution) + +void resampler_create(int devidx) +{ + static int f = 1; + if(f) + { + f = 0; + for (int i = 0; i < MAXDEVICES; i++) + q[i] = NULL; + } + + printf("create resampler %d real %d req %d\n", devidx, devlist[devidx].real_samprate, devlist[devidx].requested_samprate); + + if (q[devidx] != NULL) resamp_crcf_destroy(q[devidx]); + + int src_rate = 0; + int dst_rate = 0; + if (devlist[devidx].in_out == 0) + { + // capture device: + src_rate = devlist[devidx].real_samprate; + dst_rate = devlist[devidx].requested_samprate; + } + else + { + // playback device: + src_rate = devlist[devidx].requested_samprate; + dst_rate = devlist[devidx].real_samprate; + } + + r = (float)dst_rate / (float)src_rate; + + printf("%f %f %f\n", r, (float)dst_rate, (float)src_rate); + + q[devidx] = resamp_crcf_create(r, h_len, bw, slsl, npfb); +} + + + +float* resample(int id, float*psamp, int len, int *pnewlen) +{ + int didx = 0; + + for (int i = 0; i < len; i++) + { + unsigned int num_written = 0; + liquid_float_complex in; + liquid_float_complex out[2]; + in.real = psamp[i]; + in.imag = 0; + + resamp_crcf_execute(q[id], in, out, &num_written); + for (unsigned int r = 0; r < num_written; r++) + { + if (didx < MAXRLEN) + fresamp[id][didx++] = out[r].real; + else + printf("MAXRLEN too small\n"); + } + } + + *pnewlen = didx; + + return fresamp[id]; +} diff --git a/hsmodem/libkmaudio/liquid.h b/hsmodem/libkmaudio/liquid.h new file mode 100755 index 0000000..1bb3061 --- /dev/null +++ b/hsmodem/libkmaudio/liquid.h @@ -0,0 +1,8823 @@ +/* + * Copyright (c) 2007 - 2020 Joseph Gaeddert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef __LIQUID_H__ +#define __LIQUID_H__ + +#ifdef __cplusplus +extern "C" { +# define LIQUID_USE_COMPLEX_H 0 +#else +# define LIQUID_USE_COMPLEX_H 1 +#endif // __cplusplus + +// common headers +#include + +// +// Make sure the version and version number macros weren't defined by +// some prevoiusly included header file. +// +#ifdef LIQUID_VERSION +# undef LIQUID_VERSION +#endif +#ifdef LIQUID_VERSION_NUMBER +# undef LIQUID_VERSION_NUMBER +#endif + +// +// Compile-time version numbers +// +// LIQUID_VERSION = "X.Y.Z" +// LIQUID_VERSION_NUMBER = (X*1000000 + Y*1000 + Z) +// +#define LIQUID_VERSION "1.3.2" +#define LIQUID_VERSION_NUMBER 1003002 + +// +// Run-time library version numbers +// +extern const char liquid_version[]; +const char * liquid_libversion(void); +int liquid_libversion_number(void); + +// run-time library validation +#define LIQUID_VALIDATE_LIBVERSION \ + if (LIQUID_VERSION_NUMBER != liquid_libversion_number()) { \ + fprintf(stderr,"%s:%u: ", __FILE__,__LINE__); \ + fprintf(stderr,"error: invalid liquid runtime library\n"); \ + exit(1); \ + } \ + +// basic error types +#define LIQUID_NUM_ERRORS 12 +typedef enum { + // everything ok + LIQUID_OK=0, + + // internal logic error; this is a bug with liquid and should be reported immediately + LIQUID_EINT, + + // invalid object, examples: + // - destroy() method called on NULL pointer + LIQUID_EIOBJ, + + // invalid parameter, or configuration; examples: + // - setting bandwidth of a filter to a negative number + // - setting FFT size to zero + // - create a spectral periodogram object with window size greater than nfft + LIQUID_EICONFIG, + + // input out of range; examples: + // - try to take log of -1 + // - try to create an FFT plan of size zero + LIQUID_EIVAL, + + // invalid vector length or dimension; examples + // - trying to refer to the 17th element of a 2 x 2 matrix + // - trying to multiply two matrices of incompatible dimensions + LIQUID_EIRANGE, + + // invalid mode; examples: + // - try to create a modem of type 'LIQUID_MODEM_XXX' which does not exit + LIQUID_EIMODE, + + // unsupported mode (e.g. LIQUID_FEC_CONV_V27 with 'libfec' not installed) + LIQUID_EUMODE, + + // object has not been created or properly initialized + // - try to run firfilt_crcf_execute(NULL, ...) + // - try to modulate using an arbitrary modem without initializing the constellation + LIQUID_ENOINIT, + + // not enough memory allocated for operation; examples: + // - try to factor 100 = 2*2*5*5 but only give 3 spaces for factors + LIQUID_EIMEM, + + // file input/output; examples: + // - could not open a file for writing because of insufficient permissions + // - could not open a file for reading because it does not exist + // - try to read more data than a file has space for + // - could not parse line in file (improper formatting) + LIQUID_EIO, + +} liquid_error_code; + +// error descriptions +extern const char * liquid_error_str[LIQUID_NUM_ERRORS]; +const char * liquid_error_info(liquid_error_code _code); + +#define LIQUID_CONCAT(prefix, name) prefix ## name +#define LIQUID_VALIDATE_INPUT + +/* + * Compile-time complex data type definitions + * + * Default: use the C99 complex data type, otherwise + * define complex type compatible with the C++ complex standard, + * otherwise resort to defining binary compatible array. + */ +#if LIQUID_USE_COMPLEX_H==1 +# include +# define LIQUID_DEFINE_COMPLEX(R,C) typedef R _Complex C +#elif defined _GLIBCXX_COMPLEX || defined _LIBCPP_COMPLEX +# define LIQUID_DEFINE_COMPLEX(R,C) typedef std::complex C +#else +# define LIQUID_DEFINE_COMPLEX(R,C) typedef struct {R real; R imag;} C; +#endif +//# define LIQUID_DEFINE_COMPLEX(R,C) typedef R C[2] + +LIQUID_DEFINE_COMPLEX(float, liquid_float_complex); +LIQUID_DEFINE_COMPLEX(double, liquid_double_complex); + +// +// MODULE : agc (automatic gain control) +// + +// available squelch modes +typedef enum { + LIQUID_AGC_SQUELCH_UNKNOWN=0, // unknown/unavailable squelch mode + LIQUID_AGC_SQUELCH_ENABLED, // squelch enabled but signal not detected + LIQUID_AGC_SQUELCH_RISE, // signal first hit/exceeded threshold + LIQUID_AGC_SQUELCH_SIGNALHI, // signal level high (above threshold) + LIQUID_AGC_SQUELCH_FALL, // signal first dropped below threshold + LIQUID_AGC_SQUELCH_SIGNALLO, // signal level low (below threshold) + LIQUID_AGC_SQUELCH_TIMEOUT, // signal level low (below threshold for a certain time) + LIQUID_AGC_SQUELCH_DISABLED, // squelch not enabled +} agc_squelch_mode; + +#define LIQUID_AGC_MANGLE_CRCF(name) LIQUID_CONCAT(agc_crcf, name) +#define LIQUID_AGC_MANGLE_RRRF(name) LIQUID_CONCAT(agc_rrrf, name) + +// large macro +// AGC : name-mangling macro +// T : primitive data type +// TC : input/output data type +#define LIQUID_AGC_DEFINE_API(AGC,T,TC) \ + \ +/* Automatic gain control (agc) for level correction and signal */ \ +/* detection */ \ +typedef struct AGC(_s) * AGC(); \ + \ +/* Create automatic gain control object. */ \ +AGC() AGC(_create)(void); \ + \ +/* Destroy object, freeing all internally-allocated memory. */ \ +int AGC(_destroy)(AGC() _q); \ + \ +/* Print object properties to stdout, including received signal */ \ +/* strength indication (RSSI), loop bandwidth, lock status, and squelch */ \ +/* status. */ \ +int AGC(_print)(AGC() _q); \ + \ +/* Reset internal state of agc object, including gain estimate, input */ \ +/* signal level estimate, lock status, and squelch mode */ \ +/* If the squelch mode is disabled, it stays disabled, but all enabled */ \ +/* modes (e.g. LIQUID_AGC_SQUELCH_TIMEOUT) resets to just */ \ +/* LIQUID_AGC_SQUELCH_ENABLED. */ \ +int AGC(_reset)(AGC() _q); \ + \ +/* Execute automatic gain control on an single input sample */ \ +/* _q : automatic gain control object */ \ +/* _x : input sample */ \ +/* _y : output sample */ \ +int AGC(_execute)(AGC() _q, \ + TC _x, \ + TC * _y); \ + \ +/* Execute automatic gain control on block of samples pointed to by _x */ \ +/* and store the result in the array of the same length _y. */ \ +/* _q : automatic gain control object */ \ +/* _x : input data array, [size: _n x 1] */ \ +/* _n : number of input, output samples */ \ +/* _y : output data array, [size: _n x 1] */ \ +int AGC(_execute_block)(AGC() _q, \ + TC * _x, \ + unsigned int _n, \ + TC * _y); \ + \ +/* Lock agc object. When locked, the agc object still makes an estimate */ \ +/* of the signal level, but the gain setting is fixed and does not */ \ +/* change. */ \ +/* This is useful for providing coarse input signal level correction */ \ +/* and quickly detecting a packet burst but not distorting signals with */ \ +/* amplitude variation due to modulation. */ \ +int AGC(_lock)(AGC() _q); \ + \ +/* Unlock agc object, and allow amplitude correction to resume. */ \ +int AGC(_unlock)(AGC() _q); \ + \ +/* Set loop filter bandwidth: attack/release time. */ \ +/* _q : automatic gain control object */ \ +/* _bt : bandwidth-time constant, _bt > 0 */ \ +int AGC(_set_bandwidth)(AGC() _q, float _bt); \ + \ +/* Get the agc object's loop filter bandwidth. */ \ +float AGC(_get_bandwidth)(AGC() _q); \ + \ +/* Get the input signal's estimated energy level, relative to unity. */ \ +/* The result is a linear value. */ \ +float AGC(_get_signal_level)(AGC() _q); \ + \ +/* Set the agc object's estimate of the input signal by specifying an */ \ +/* explicit linear value. This is useful for initializing the agc */ \ +/* object with a preliminary estimate of the signal level to help gain */ \ +/* convergence. */ \ +/* _q : automatic gain control object */ \ +/* _x2 : signal level of input, _x2 > 0 */ \ +int AGC(_set_signal_level)(AGC() _q, \ + float _x2); \ + \ +/* Get the agc object's estimated received signal strength indication */ \ +/* (RSSI) on the input signal. */ \ +/* This is similar to getting the signal level (above), but returns the */ \ +/* result in dB rather than on a linear scale. */ \ +float AGC(_get_rssi)(AGC() _q); \ + \ +/* Set the agc object's estimated received signal strength indication */ \ +/* (RSSI) on the input signal by specifying an explicit value in dB. */ \ +/* _q : automatic gain control object */ \ +/* _rssi : signal level of input [dB] */ \ +int AGC(_set_rssi)(AGC() _q, float _rssi); \ + \ +/* Get the gain value currently being applied to the input signal */ \ +/* (linear). */ \ +float AGC(_get_gain)(AGC() _q); \ + \ +/* Set the agc object's internal gain by specifying an explicit linear */ \ +/* value. */ \ +/* _q : automatic gain control object */ \ +/* _gain : gain to apply to input signal, _gain > 0 */ \ +int AGC(_set_gain)(AGC() _q, \ + float _gain); \ + \ +/* Get the ouput scaling applied to each sample (linear). */ \ +float AGC(_get_scale)(AGC() _q); \ + \ +/* Set the agc object's output scaling (linear). Note that this does */ \ +/* affect the response of the AGC. */ \ +/* _q : automatic gain control object */ \ +/* _gain : gain to apply to input signal, _gain > 0 */ \ +int AGC(_set_scale)(AGC() _q, \ + float _scale); \ + \ +/* Estimate signal level and initialize internal gain on an input */ \ +/* array. */ \ +/* _q : automatic gain control object */ \ +/* _x : input data array, [size: _n x 1] */ \ +/* _n : number of input, output samples */ \ +int AGC(_init)(AGC() _q, \ + TC * _x, \ + unsigned int _n); \ + \ +/* Enable squelch mode. */ \ +int AGC(_squelch_enable)(AGC() _q); \ + \ +/* Disable squelch mode. */ \ +int AGC(_squelch_disable)(AGC() _q); \ + \ +/* Return flag indicating if squelch is enabled or not. */ \ +int AGC(_squelch_is_enabled)(AGC() _q); \ + \ +/* Set threshold for enabling/disabling squelch. */ \ +/* _q : automatic gain control object */ \ +/* _thresh : threshold for enabling squelch [dB] */ \ +int AGC(_squelch_set_threshold)(AGC() _q, \ + T _thresh); \ + \ +/* Get squelch threshold (value in dB) */ \ +T AGC(_squelch_get_threshold)(AGC() _q); \ + \ +/* Set timeout before enabling squelch. */ \ +/* _q : automatic gain control object */ \ +/* _timeout : timeout before enabling squelch [samples] */ \ +int AGC(_squelch_set_timeout)(AGC() _q, \ + unsigned int _timeout); \ + \ +/* Get squelch timeout (number of samples) */ \ +unsigned int AGC(_squelch_get_timeout)(AGC() _q); \ + \ +/* Get squelch status (e.g. LIQUID_AGC_SQUELCH_TIMEOUT) */ \ +int AGC(_squelch_get_status)(AGC() _q); \ + +// Define agc APIs +LIQUID_AGC_DEFINE_API(LIQUID_AGC_MANGLE_CRCF, float, liquid_float_complex) +LIQUID_AGC_DEFINE_API(LIQUID_AGC_MANGLE_RRRF, float, float) + + + +// +// MODULE : audio +// + +// CVSD: continuously variable slope delta +typedef struct cvsd_s * cvsd; + +// create cvsd object +// _num_bits : number of adjacent bits to observe (4 recommended) +// _zeta : slope adjustment multiplier (1.5 recommended) +// _alpha : pre-/post-emphasis filter coefficient (0.9 recommended) +// NOTE: _alpha must be in [0,1] +cvsd cvsd_create(unsigned int _num_bits, + float _zeta, + float _alpha); + +// destroy cvsd object +void cvsd_destroy(cvsd _q); + +// print cvsd object parameters +void cvsd_print(cvsd _q); + +// encode/decode single sample +unsigned char cvsd_encode(cvsd _q, float _audio_sample); +float cvsd_decode(cvsd _q, unsigned char _bit); + +// encode/decode 8 samples at a time +void cvsd_encode8(cvsd _q, float * _audio, unsigned char * _data); +void cvsd_decode8(cvsd _q, unsigned char _data, float * _audio); + + +// +// MODULE : buffer +// + +// circular buffer +#define LIQUID_CBUFFER_MANGLE_FLOAT(name) LIQUID_CONCAT(cbufferf, name) +#define LIQUID_CBUFFER_MANGLE_CFLOAT(name) LIQUID_CONCAT(cbuffercf, name) + +// large macro +// CBUFFER : name-mangling macro +// T : data type +#define LIQUID_CBUFFER_DEFINE_API(CBUFFER,T) \ + \ +/* Circular buffer object for storing and retrieving samples in a */ \ +/* first-in/first-out (FIFO) manner using a minimal amount of memory */ \ +typedef struct CBUFFER(_s) * CBUFFER(); \ + \ +/* Create circular buffer object of a particular maximum storage length */ \ +/* _max_size : maximum buffer size, _max_size > 0 */ \ +CBUFFER() CBUFFER(_create)(unsigned int _max_size); \ + \ +/* Create circular buffer object of a particular maximum storage size */ \ +/* and specify the maximum number of elements that can be read at any */ \ +/* any given time */ \ +/* _max_size : maximum buffer size, _max_size > 0 */ \ +/* _max_read : maximum size that will be read from buffer */ \ +CBUFFER() CBUFFER(_create_max)(unsigned int _max_size, \ + unsigned int _max_read); \ + \ +/* Destroy cbuffer object, freeing all internal memory */ \ +void CBUFFER(_destroy)(CBUFFER() _q); \ + \ +/* Print cbuffer object properties to stdout */ \ +void CBUFFER(_print)(CBUFFER() _q); \ + \ +/* Print cbuffer object properties and internal state */ \ +void CBUFFER(_debug_print)(CBUFFER() _q); \ + \ +/* Clear internal buffer */ \ +void CBUFFER(_reset)(CBUFFER() _q); \ + \ +/* Get the number of elements currently in the buffer */ \ +unsigned int CBUFFER(_size)(CBUFFER() _q); \ + \ +/* Get the maximum number of elements the buffer can hold */ \ +unsigned int CBUFFER(_max_size)(CBUFFER() _q); \ + \ +/* Get the maximum number of elements you may read at once */ \ +unsigned int CBUFFER(_max_read)(CBUFFER() _q); \ + \ +/* Get the number of available slots (max_size - size) */ \ +unsigned int CBUFFER(_space_available)(CBUFFER() _q); \ + \ +/* Return flag indicating if the buffer is full or not */ \ +int CBUFFER(_is_full)(CBUFFER() _q); \ + \ +/* Write a single sample into the buffer */ \ +/* _q : circular buffer object */ \ +/* _v : input sample */ \ +void CBUFFER(_push)(CBUFFER() _q, \ + T _v); \ + \ +/* Write a block of samples to the buffer */ \ +/* _q : circular buffer object */ \ +/* _v : array of samples to write to buffer */ \ +/* _n : number of samples to write */ \ +void CBUFFER(_write)(CBUFFER() _q, \ + T * _v, \ + unsigned int _n); \ + \ +/* Remove and return a single element from the buffer by setting the */ \ +/* value of the output sample pointed to by _v */ \ +/* _q : circular buffer object */ \ +/* _v : pointer to sample output */ \ +void CBUFFER(_pop)(CBUFFER() _q, \ + T * _v); \ + \ +/* Read buffer contents by returning a pointer to the linearized array; */ \ +/* note that the returned pointer is only valid until another operation */ \ +/* is performed on the circular buffer object */ \ +/* _q : circular buffer object */ \ +/* _num_requested : number of elements requested */ \ +/* _v : output pointer */ \ +/* _num_read : number of elements referenced by _v */ \ +void CBUFFER(_read)(CBUFFER() _q, \ + unsigned int _num_requested, \ + T ** _v, \ + unsigned int * _num_read); \ + \ +/* Release _n samples from the buffer */ \ +/* _q : circular buffer object */ \ +/* _n : number of elements to release */ \ +void CBUFFER(_release)(CBUFFER() _q, \ + unsigned int _n); \ + +// Define buffer APIs +LIQUID_CBUFFER_DEFINE_API(LIQUID_CBUFFER_MANGLE_FLOAT, float) +LIQUID_CBUFFER_DEFINE_API(LIQUID_CBUFFER_MANGLE_CFLOAT, liquid_float_complex) + + + +// Windowing functions +#define LIQUID_WINDOW_MANGLE_FLOAT(name) LIQUID_CONCAT(windowf, name) +#define LIQUID_WINDOW_MANGLE_CFLOAT(name) LIQUID_CONCAT(windowcf, name) + +// large macro +// WINDOW : name-mangling macro +// T : data type +#define LIQUID_WINDOW_DEFINE_API(WINDOW,T) \ + \ +/* Sliding window first-in/first-out buffer with a fixed size */ \ +typedef struct WINDOW(_s) * WINDOW(); \ + \ +/* Create window buffer object of a fixed length */ \ +WINDOW() WINDOW(_create)(unsigned int _n); \ + \ +/* Recreate window buffer object with new length. */ \ +/* This extends an existing window's size, similar to the standard C */ \ +/* library's realloc() to n samples. */ \ +/* If the size of the new window is larger than the old one, the newest */ \ +/* values are retained at the beginning of the buffer and the oldest */ \ +/* values are truncated. If the size of the new window is smaller than */ \ +/* the old one, the oldest values are truncated. */ \ +/* _q : old window object */ \ +/* _n : new window length */ \ +WINDOW() WINDOW(_recreate)(WINDOW() _q, unsigned int _n); \ + \ +/* Destroy window object, freeing all internally memory */ \ +int WINDOW(_destroy)(WINDOW() _q); \ + \ +/* Print window object to stdout */ \ +int WINDOW(_print)(WINDOW() _q); \ + \ +/* Print window object to stdout (with extra information) */ \ +int WINDOW(_debug_print)(WINDOW() _q); \ + \ +/* Reset window object (initialize to zeros) */ \ +int WINDOW(_reset)(WINDOW() _q); \ + \ +/* Read the contents of the window by returning a pointer to the */ \ +/* aligned internal memory array. This method guarantees that the */ \ +/* elements are linearized. This method should only be used for */ \ +/* reading; writing values to the buffer has unspecified results. */ \ +/* Note that the returned pointer is only valid until another operation */ \ +/* is performed on the window buffer object */ \ +/* _q : window object */ \ +/* _v : output pointer (set to internal array) */ \ +int WINDOW(_read)(WINDOW() _q, \ + T ** _v); \ + \ +/* Index single element in buffer at a particular index */ \ +/* This retrieves the \(i^{th}\) sample in the window, storing the */ \ +/* output value in _v. */ \ +/* This is equivalent to first invoking read() and then indexing on the */ \ +/* resulting pointer; however the result is obtained much faster. */ \ +/* Therefore setting the index to 0 returns the oldest value in the */ \ +/* window. */ \ +/* _q : window object */ \ +/* _i : index of element to read */ \ +/* _v : output value pointer */ \ +int WINDOW(_index)(WINDOW() _q, \ + unsigned int _i, \ + T * _v); \ + \ +/* Shifts a single sample into the right side of the window, pushing */ \ +/* the oldest (left-most) sample out of the end. Unlike stacks, the */ \ +/* window object has no equivalent "pop" method, as values are retained */ \ +/* in memory until they are overwritten. */ \ +/* _q : window object */ \ +/* _v : single input element */ \ +int WINDOW(_push)(WINDOW() _q, \ + T _v); \ + \ +/* Write array of elements onto window buffer */ \ +/* Effectively, this is equivalent to pushing each sample one at a */ \ +/* time, but executes much faster. */ \ +/* _q : window object */ \ +/* _v : input array of values to write */ \ +/* _n : number of input values to write */ \ +int WINDOW(_write)(WINDOW() _q, \ + T * _v, \ + unsigned int _n); \ + +// Define window APIs +LIQUID_WINDOW_DEFINE_API(LIQUID_WINDOW_MANGLE_FLOAT, float) +LIQUID_WINDOW_DEFINE_API(LIQUID_WINDOW_MANGLE_CFLOAT, liquid_float_complex) +//LIQUID_WINDOW_DEFINE_API(LIQUID_WINDOW_MANGLE_UINT, unsigned int) + + +// wdelay functions : windowed-delay +// Implements an efficient z^-k delay with minimal memory +#define LIQUID_WDELAY_MANGLE_FLOAT(name) LIQUID_CONCAT(wdelayf, name) +#define LIQUID_WDELAY_MANGLE_CFLOAT(name) LIQUID_CONCAT(wdelaycf, name) +//#define LIQUID_WDELAY_MANGLE_UINT(name) LIQUID_CONCAT(wdelayui, name) + +// large macro +// WDELAY : name-mangling macro +// T : data type +#define LIQUID_WDELAY_DEFINE_API(WDELAY,T) \ + \ +/* Efficient digital delay line using a minimal amount of memory */ \ +typedef struct WDELAY(_s) * WDELAY(); \ + \ +/* Create delay buffer object with a particular number of samples of */ \ +/* delay */ \ +/* _delay : number of samples of delay in the wdelay object */ \ +WDELAY() WDELAY(_create)(unsigned int _delay); \ + \ +/* Re-create delay buffer object, adjusting the delay size, preserving */ \ +/* the internal state of the object */ \ +/* _q : old delay buffer object */ \ +/* _delay : delay for new object */ \ +WDELAY() WDELAY(_recreate)(WDELAY() _q, \ + unsigned int _delay); \ + \ +/* Destroy delay buffer object, freeing internal memory */ \ +void WDELAY(_destroy)(WDELAY() _q); \ + \ +/* Print delay buffer object's state to stdout */ \ +void WDELAY(_print)(WDELAY() _q); \ + \ +/* Clear/reset state of object */ \ +void WDELAY(_reset)(WDELAY() _q); \ + \ +/* Read delayed sample at the head of the buffer and store it to the */ \ +/* output pointer */ \ +/* _q : delay buffer object */ \ +/* _v : value of delayed element */ \ +void WDELAY(_read)(WDELAY() _q, \ + T * _v); \ + \ +/* Push new sample into delay buffer object */ \ +/* _q : delay buffer object */ \ +/* _v : new value to be added to buffer */ \ +void WDELAY(_push)(WDELAY() _q, \ + T _v); \ + +// Define wdelay APIs +LIQUID_WDELAY_DEFINE_API(LIQUID_WDELAY_MANGLE_FLOAT, float) +LIQUID_WDELAY_DEFINE_API(LIQUID_WDELAY_MANGLE_CFLOAT, liquid_float_complex) +//LIQUID_WDELAY_DEFINE_API(LIQUID_WDELAY_MANGLE_UINT, unsigned int) + + + +// +// MODULE : channel +// + +#define LIQUID_CHANNEL_MANGLE_CCCF(name) LIQUID_CONCAT(channel_cccf,name) + +// large macro +// CHANNEL : name-mangling macro +// TO : output data type +// TC : coefficients data type +// TI : input data type +#define LIQUID_CHANNEL_DEFINE_API(CHANNEL,TO,TC,TI) \ + \ +/* Channel emulation */ \ +typedef struct CHANNEL(_s) * CHANNEL(); \ + \ +/* Create channel object with default parameters */ \ +CHANNEL() CHANNEL(_create)(void); \ + \ +/* Destroy channel object, freeing all internal memory */ \ +int CHANNEL(_destroy)(CHANNEL() _q); \ + \ +/* Print channel object internals to standard output */ \ +int CHANNEL(_print)(CHANNEL() _q); \ + \ +/* Include additive white Gausss noise impairment */ \ +/* _q : channel object */ \ +/* _N0dB : noise floor power spectral density [dB] */ \ +/* _SNRdB : signal-to-noise ratio [dB] */ \ +int CHANNEL(_add_awgn)(CHANNEL() _q, \ + float _N0dB, \ + float _SNRdB); \ + \ +/* Include carrier offset impairment */ \ +/* _q : channel object */ \ +/* _frequency : carrier frequency offset [radians/sample] */ \ +/* _phase : carrier phase offset [radians] */ \ +int CHANNEL(_add_carrier_offset)(CHANNEL() _q, \ + float _frequency, \ + float _phase); \ + \ +/* Include multi-path channel impairment */ \ +/* _q : channel object */ \ +/* _h : channel coefficients (NULL for random) */ \ +/* _h_len : number of channel coefficients */ \ +int CHANNEL(_add_multipath)(CHANNEL() _q, \ + TC * _h, \ + unsigned int _h_len); \ + \ +/* Include slowly-varying shadowing impairment */ \ +/* _q : channel object */ \ +/* _sigma : standard deviation for log-normal shadowing */ \ +/* _fd : Doppler frequency, 0 <= _fd < 0.5 */ \ +int CHANNEL(_add_shadowing)(CHANNEL() _q, \ + float _sigma, \ + float _fd); \ + \ +/* Apply channel impairments on single input sample */ \ +/* _q : channel object */ \ +/* _x : input sample */ \ +/* _y : pointer to output sample */ \ +int CHANNEL(_execute)(CHANNEL() _q, \ + TI _x, \ + TO * _y); \ + \ +/* Apply channel impairments on block of samples */ \ +/* _q : channel object */ \ +/* _x : input array, [size: _n x 1] */ \ +/* _n : input array, length */ \ +/* _y : output array, [size: _n x 1] */ \ +int CHANNEL(_execute_block)(CHANNEL() _q, \ + TI * _x, \ + unsigned int _n, \ + TO * _y); \ + +LIQUID_CHANNEL_DEFINE_API(LIQUID_CHANNEL_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + + +// +// time-varying multi-path channel +// +#define LIQUID_TVMPCH_MANGLE_CCCF(name) LIQUID_CONCAT(tvmpch_cccf,name) + +// large macro +// TVMPCH : name-mangling macro +// TO : output data type +// TC : coefficients data type +// TI : input data type +#define LIQUID_TVMPCH_DEFINE_API(TVMPCH,TO,TC,TI) \ + \ +/* Time-varying multipath channel emulation */ \ +typedef struct TVMPCH(_s) * TVMPCH(); \ + \ +/* Create time-varying multi-path channel emulator object, specifying */ \ +/* the number of coefficients, the standard deviation of coefficients, */ \ +/* and the coherence time. The larger the standard deviation, the more */ \ +/* dramatic the frequency response of the channel. The shorter the */ \ +/* coeherent time, the faster the channel effects. */ \ +/* _n : number of coefficients, _n > 0 */ \ +/* _std : standard deviation, _std >= 0 */ \ +/* _tau : normalized coherence time, 0 < _tau < 1 */ \ +TVMPCH() TVMPCH(_create)(unsigned int _n, \ + float _std, \ + float _tau); \ + \ +/* Destroy channel object, freeing all internal memory */ \ +int TVMPCH(_destroy)(TVMPCH() _q); \ + \ +/* Reset object */ \ +int TVMPCH(_reset)(TVMPCH() _q); \ + \ +/* Print channel object internals to standard output */ \ +int TVMPCH(_print)(TVMPCH() _q); \ + \ +/* Push sample into emulator */ \ +/* _q : channel object */ \ +/* _x : input sample */ \ +int TVMPCH(_push)(TVMPCH() _q, \ + TI _x); \ + \ +/* Compute output sample */ \ +/* _q : channel object */ \ +/* _y : output sample */ \ +int TVMPCH(_execute)(TVMPCH() _q, \ + TO * _y); \ + \ +/* Apply channel impairments on a block of samples */ \ +/* _q : channel object */ \ +/* _x : input array, [size: _n x 1] */ \ +/* _n : input array length */ \ +/* _y : output array, [size: _n x 1] */ \ +int TVMPCH(_execute_block)(TVMPCH() _q, \ + TI * _x, \ + unsigned int _n, \ + TO * _y); \ + +LIQUID_TVMPCH_DEFINE_API(LIQUID_TVMPCH_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + + +// +// MODULE : dotprod (vector dot product) +// + +#define LIQUID_DOTPROD_MANGLE_RRRF(name) LIQUID_CONCAT(dotprod_rrrf,name) +#define LIQUID_DOTPROD_MANGLE_CCCF(name) LIQUID_CONCAT(dotprod_cccf,name) +#define LIQUID_DOTPROD_MANGLE_CRCF(name) LIQUID_CONCAT(dotprod_crcf,name) + +// large macro +// DOTPROD : name-mangling macro +// TO : output data type +// TC : coefficients data type +// TI : input data type +#define LIQUID_DOTPROD_DEFINE_API(DOTPROD,TO,TC,TI) \ + \ +/* Vector dot product operation */ \ +typedef struct DOTPROD(_s) * DOTPROD(); \ + \ +/* Run dot product without creating object. This is less efficient than */ \ +/* creating the object as it is an unoptimized portable implementation */ \ +/* that doesn't take advantage of processor extensions. It is meant to */ \ +/* provide a baseline for performance comparison and a convenient way */ \ +/* to invoke a dot product operation when fast operation is not */ \ +/* necessary. */ \ +/* _v : coefficients array [size: _n x 1] */ \ +/* _x : input array [size: _n x 1] */ \ +/* _n : dotprod length, _n > 0 */ \ +/* _y : output sample pointer */ \ +void DOTPROD(_run)( TC * _v, \ + TI * _x, \ + unsigned int _n, \ + TO * _y); \ + \ +/* This provides the same unoptimized operation as the 'run()' method */ \ +/* above, but with the loop unrolled by a factor of 4. It is marginally */ \ +/* faster than 'run()' without unrolling the loop. */ \ +/* _v : coefficients array [size: _n x 1] */ \ +/* _x : input array [size: _n x 1] */ \ +/* _n : dotprod length, _n > 0 */ \ +/* _y : output sample pointer */ \ +void DOTPROD(_run4)( TC * _v, \ + TI * _x, \ + unsigned int _n, \ + TO * _y); \ + \ +/* Create vector dot product object */ \ +/* _v : coefficients array [size: _n x 1] */ \ +/* _n : dotprod length, _n > 0 */ \ +DOTPROD() DOTPROD(_create)(TC * _v, \ + unsigned int _n); \ + \ +/* Re-create dot product object of potentially a different length with */ \ +/* different coefficients. If the length of the dot product object does */ \ +/* not change, not memory reallocation is invoked. */ \ +/* _q : old dotprod object */ \ +/* _v : coefficients array [size: _n x 1] */ \ +/* _n : dotprod length, _n > 0 */ \ +DOTPROD() DOTPROD(_recreate)(DOTPROD() _q, \ + TC * _v, \ + unsigned int _n); \ + \ +/* Destroy dotprod object, freeing all internal memory */ \ +void DOTPROD(_destroy)(DOTPROD() _q); \ + \ +/* Print dotprod object internals to standard output */ \ +void DOTPROD(_print)(DOTPROD() _q); \ + \ +/* Execute dot product on an input array */ \ +/* _q : dotprod object */ \ +/* _x : input array [size: _n x 1] */ \ +/* _y : output sample pointer */ \ +void DOTPROD(_execute)(DOTPROD() _q, \ + TI * _x, \ + TO * _y); \ + +LIQUID_DOTPROD_DEFINE_API(LIQUID_DOTPROD_MANGLE_RRRF, + float, + float, + float) + +LIQUID_DOTPROD_DEFINE_API(LIQUID_DOTPROD_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + +LIQUID_DOTPROD_DEFINE_API(LIQUID_DOTPROD_MANGLE_CRCF, + liquid_float_complex, + float, + liquid_float_complex) + +// +// sum squared methods +// + +float liquid_sumsqf(float * _v, + unsigned int _n); + +float liquid_sumsqcf(liquid_float_complex * _v, + unsigned int _n); + + +// +// MODULE : equalization +// + +// least mean-squares (LMS) +#define LIQUID_EQLMS_MANGLE_RRRF(name) LIQUID_CONCAT(eqlms_rrrf,name) +#define LIQUID_EQLMS_MANGLE_CCCF(name) LIQUID_CONCAT(eqlms_cccf,name) + +// large macro +// EQLMS : name-mangling macro +// T : data type +#define LIQUID_EQLMS_DEFINE_API(EQLMS,T) \ + \ +/* Least mean-squares equalization object */ \ +typedef struct EQLMS(_s) * EQLMS(); \ + \ +/* Create LMS EQ initialized with external coefficients */ \ +/* _h : filter coefficients; set to NULL for {1,0,0...},[size: _n x 1] */ \ +/* _n : filter length */ \ +EQLMS() EQLMS(_create)(T * _h, \ + unsigned int _n); \ + \ +/* Create LMS EQ initialized with square-root Nyquist prototype filter */ \ +/* as initial set of coefficients. This is useful for applications */ \ +/* where the baseline matched filter is a good starting point, but */ \ +/* where equalization is needed to properly remove inter-symbol */ \ +/* interference. */ \ +/* The filter length is \(2 k m + 1\) */ \ +/* _type : filter type (e.g. LIQUID_FIRFILT_RRC) */ \ +/* _k : samples/symbol */ \ +/* _m : filter delay (symbols) */ \ +/* _beta : rolloff factor (0 < beta <= 1) */ \ +/* _dt : fractional sample delay */ \ +EQLMS() EQLMS(_create_rnyquist)(int _type, \ + unsigned int _k, \ + unsigned int _m, \ + float _beta, \ + float _dt); \ + \ +/* Create LMS EQ initialized with low-pass filter */ \ +/* _n : filter length */ \ +/* _fc : filter cut-off normalized to sample rate, 0 < _fc <= 0.5 */ \ +EQLMS() EQLMS(_create_lowpass)(unsigned int _n, \ + float _fc); \ + \ +/* Re-create EQ initialized with external coefficients */ \ +/* _q : equalizer object */ \ +/* _h : filter coefficients (NULL for {1,0,0...}), [size: _n x 1] */ \ +/* _h_len : filter length */ \ +EQLMS() EQLMS(_recreate)(EQLMS() _q, \ + T * _h, \ + unsigned int _h_len); \ + \ +/* Destroy equalizer object, freeing all internal memory */ \ +int EQLMS(_destroy)(EQLMS() _q); \ + \ +/* Reset equalizer object, clearing internal state */ \ +int EQLMS(_reset)(EQLMS() _q); \ + \ +/* Print equalizer internal state */ \ +int EQLMS(_print)(EQLMS() _q); \ + \ +/* Get equalizer learning rate */ \ +float EQLMS(_get_bw)(EQLMS() _q); \ + \ +/* Set equalizer learning rate */ \ +/* _q : equalizer object */ \ +/* _lambda : learning rate, _lambda > 0 */ \ +int EQLMS(_set_bw)(EQLMS() _q, \ + float _lambda); \ + \ +/* Push sample into equalizer internal buffer */ \ +/* _q : equalizer object */ \ +/* _x : input sample */ \ +int EQLMS(_push)(EQLMS() _q, \ + T _x); \ + \ +/* Push block of samples into internal buffer of equalizer object */ \ +/* _q : equalizer object */ \ +/* _x : input sample array, [size: _n x 1] */ \ +/* _n : input sample array length */ \ +int EQLMS(_push_block)(EQLMS() _q, \ + T * _x, \ + unsigned int _n); \ + \ +/* Execute internal dot product and return result */ \ +/* _q : equalizer object */ \ +/* _y : output sample */ \ +int EQLMS(_execute)(EQLMS() _q, \ + T * _y); \ + \ +/* Execute equalizer with block of samples using constant */ \ +/* modulus algorithm, operating on a decimation rate of _k */ \ +/* samples. */ \ +/* _q : equalizer object */ \ +/* _k : down-sampling rate */ \ +/* _x : input sample array [size: _n x 1] */ \ +/* _n : input sample array length */ \ +/* _y : output sample array [size: _n x 1] */ \ +int EQLMS(_execute_block)(EQLMS() _q, \ + unsigned int _k, \ + T * _x, \ + unsigned int _n, \ + T * _y); \ + \ +/* Step through one cycle of equalizer training */ \ +/* _q : equalizer object */ \ +/* _d : desired output */ \ +/* _d_hat : actual output */ \ +int EQLMS(_step)(EQLMS() _q, \ + T _d, \ + T _d_hat); \ + \ +/* Step through one cycle of equalizer training (blind) */ \ +/* _q : equalizer object */ \ +/* _d_hat : actual output */ \ +int EQLMS(_step_blind)(EQLMS() _q, \ + T _d_hat); \ + \ +/* Get equalizer's internal coefficients */ \ +/* _q : equalizer object */ \ +/* _w : weights, [size: _p x 1] */ \ +int EQLMS(_get_weights)(EQLMS() _q, \ + T * _w); \ + \ +/* Train equalizer object on group of samples */ \ +/* _q : equalizer object */ \ +/* _w : input/output weights, [size: _p x 1] */ \ +/* _x : received sample vector,[size: _n x 1] */ \ +/* _d : desired output vector, [size: _n x 1] */ \ +/* _n : input, output vector length */ \ +int EQLMS(_train)(EQLMS() _q, \ + T * _w, \ + T * _x, \ + T * _d, \ + unsigned int _n); \ + +LIQUID_EQLMS_DEFINE_API(LIQUID_EQLMS_MANGLE_RRRF, float) +LIQUID_EQLMS_DEFINE_API(LIQUID_EQLMS_MANGLE_CCCF, liquid_float_complex) + + +// recursive least-squares (RLS) +#define LIQUID_EQRLS_MANGLE_RRRF(name) LIQUID_CONCAT(eqrls_rrrf,name) +#define LIQUID_EQRLS_MANGLE_CCCF(name) LIQUID_CONCAT(eqrls_cccf,name) + +// large macro +// EQRLS : name-mangling macro +// T : data type +#define LIQUID_EQRLS_DEFINE_API(EQRLS,T) \ + \ +/* Recursive least mean-squares equalization object */ \ +typedef struct EQRLS(_s) * EQRLS(); \ + \ +/* Create RLS EQ initialized with external coefficients */ \ +/* _h : filter coefficients; set to NULL for {1,0,0...},[size: _n x 1] */ \ +/* _n : filter length */ \ +EQRLS() EQRLS(_create)(T * _h, \ + unsigned int _n); \ + \ +/* Re-create EQ initialized with external coefficients */ \ +/* _q : equalizer object */ \ +/* _h : filter coefficients (NULL for {1,0,0...}), [size: _n x 1] */ \ +/* _n : filter length */ \ +EQRLS() EQRLS(_recreate)(EQRLS() _q, \ + T * _h, \ + unsigned int _n); \ + \ +/* Destroy equalizer object, freeing all internal memory */ \ +int EQRLS(_destroy)(EQRLS() _q); \ + \ +/* Reset equalizer object, clearing internal state */ \ +int EQRLS(_reset)(EQRLS() _q); \ + \ +/* Print equalizer internal state */ \ +int EQRLS(_print)(EQRLS() _q); \ + \ +/* Get equalizer learning rate */ \ +float EQRLS(_get_bw)(EQRLS() _q); \ + \ +/* Set equalizer learning rate */ \ +/* _q : equalizer object */ \ +/* _mu : learning rate, _mu > 0 */ \ +int EQRLS(_set_bw)(EQRLS() _q, \ + float _mu); \ + \ +/* Push sample into equalizer internal buffer */ \ +/* _q : equalizer object */ \ +/* _x : input sample */ \ +int EQRLS(_push)(EQRLS() _q, T _x); \ + \ +/* Execute internal dot product and return result */ \ +/* _q : equalizer object */ \ +/* _y : output sample */ \ +int EQRLS(_execute)(EQRLS() _q, T * _y); \ + \ +/* Step through one cycle of equalizer training */ \ +/* _q : equalizer object */ \ +/* _d : desired output */ \ +/* _d_hat : actual output */ \ +int EQRLS(_step)(EQRLS() _q, T _d, T _d_hat); \ + \ +/* Get equalizer's internal coefficients */ \ +/* _q : equalizer object */ \ +/* _w : weights, [size: _p x 1] */ \ +int EQRLS(_get_weights)(EQRLS() _q, \ + T * _w); \ + \ +/* Train equalizer object on group of samples */ \ +/* _q : equalizer object */ \ +/* _w : input/output weights, [size: _p x 1] */ \ +/* _x : received sample vector,[size: _n x 1] */ \ +/* _d : desired output vector, [size: _n x 1] */ \ +/* _n : input, output vector length */ \ +int EQRLS(_train)(EQRLS() _q, \ + T * _w, \ + T * _x, \ + T * _d, \ + unsigned int _n); \ + +LIQUID_EQRLS_DEFINE_API(LIQUID_EQRLS_MANGLE_RRRF, float) +LIQUID_EQRLS_DEFINE_API(LIQUID_EQRLS_MANGLE_CCCF, liquid_float_complex) + + + + +// +// MODULE : fec (forward error-correction) +// + +// soft bit values +#define LIQUID_SOFTBIT_0 (0) +#define LIQUID_SOFTBIT_1 (255) +#define LIQUID_SOFTBIT_ERASURE (127) + +// available CRC schemes +#define LIQUID_CRC_NUM_SCHEMES 7 +typedef enum { + LIQUID_CRC_UNKNOWN=0, // unknown/unavailable CRC scheme + LIQUID_CRC_NONE, // no error-detection + LIQUID_CRC_CHECKSUM, // 8-bit checksum + LIQUID_CRC_8, // 8-bit CRC + LIQUID_CRC_16, // 16-bit CRC + LIQUID_CRC_24, // 24-bit CRC + LIQUID_CRC_32 // 32-bit CRC +} crc_scheme; + +// pretty names for crc schemes +extern const char * crc_scheme_str[LIQUID_CRC_NUM_SCHEMES][2]; + +// Print compact list of existing and available CRC schemes +void liquid_print_crc_schemes(); + +// returns crc_scheme based on input string +crc_scheme liquid_getopt_str2crc(const char * _str); + +// get length of CRC (bytes) +unsigned int crc_get_length(crc_scheme _scheme); + +// generate error-detection key +// _scheme : error-detection scheme +// _msg : input data message, [size: _n x 1] +// _n : input data message size +unsigned int crc_generate_key(crc_scheme _scheme, + unsigned char * _msg, + unsigned int _n); + +// generate error-detection key and append to end of message +// _scheme : error-detection scheme (resulting in 'p' bytes) +// _msg : input data message, [size: _n+p x 1] +// _n : input data message size (excluding key at end) +int crc_append_key(crc_scheme _scheme, + unsigned char * _msg, + unsigned int _n); + +// validate message using error-detection key +// _scheme : error-detection scheme +// _msg : input data message, [size: _n x 1] +// _n : input data message size +// _key : error-detection key +int crc_validate_message(crc_scheme _scheme, + unsigned char * _msg, + unsigned int _n, + unsigned int _key); + +// check message with key appended to end of array +// _scheme : error-detection scheme (resulting in 'p' bytes) +// _msg : input data message, [size: _n+p x 1] +// _n : input data message size (excluding key at end) +int crc_check_key(crc_scheme _scheme, + unsigned char * _msg, + unsigned int _n); + +// get size of key (bytes) +unsigned int crc_sizeof_key(crc_scheme _scheme); + + +// available FEC schemes +#define LIQUID_FEC_NUM_SCHEMES 28 +typedef enum { + LIQUID_FEC_UNKNOWN=0, // unknown/unsupported scheme + LIQUID_FEC_NONE, // no error-correction + LIQUID_FEC_REP3, // simple repeat code, r1/3 + LIQUID_FEC_REP5, // simple repeat code, r1/5 + LIQUID_FEC_HAMMING74, // Hamming (7,4) block code, r1/2 (really 4/7) + LIQUID_FEC_HAMMING84, // Hamming (7,4) with extra parity bit, r1/2 + LIQUID_FEC_HAMMING128, // Hamming (12,8) block code, r2/3 + + LIQUID_FEC_GOLAY2412, // Golay (24,12) block code, r1/2 + LIQUID_FEC_SECDED2216, // SEC-DED (22,16) block code, r8/11 + LIQUID_FEC_SECDED3932, // SEC-DED (39,32) block code + LIQUID_FEC_SECDED7264, // SEC-DED (72,64) block code, r8/9 + + // codecs not defined internally (see http://www.ka9q.net/code/fec/) + LIQUID_FEC_CONV_V27, // r1/2, K=7, dfree=10 + LIQUID_FEC_CONV_V29, // r1/2, K=9, dfree=12 + LIQUID_FEC_CONV_V39, // r1/3, K=9, dfree=18 + LIQUID_FEC_CONV_V615, // r1/6, K=15, dfree<=57 (Heller 1968) + + // punctured (perforated) codes + LIQUID_FEC_CONV_V27P23, // r2/3, K=7, dfree=6 + LIQUID_FEC_CONV_V27P34, // r3/4, K=7, dfree=5 + LIQUID_FEC_CONV_V27P45, // r4/5, K=7, dfree=4 + LIQUID_FEC_CONV_V27P56, // r5/6, K=7, dfree=4 + LIQUID_FEC_CONV_V27P67, // r6/7, K=7, dfree=3 + LIQUID_FEC_CONV_V27P78, // r7/8, K=7, dfree=3 + + LIQUID_FEC_CONV_V29P23, // r2/3, K=9, dfree=7 + LIQUID_FEC_CONV_V29P34, // r3/4, K=9, dfree=6 + LIQUID_FEC_CONV_V29P45, // r4/5, K=9, dfree=5 + LIQUID_FEC_CONV_V29P56, // r5/6, K=9, dfree=5 + LIQUID_FEC_CONV_V29P67, // r6/7, K=9, dfree=4 + LIQUID_FEC_CONV_V29P78, // r7/8, K=9, dfree=4 + + // Reed-Solomon codes + LIQUID_FEC_RS_M8 // m=8, n=255, k=223 +} fec_scheme; + +// pretty names for fec schemes +extern const char * fec_scheme_str[LIQUID_FEC_NUM_SCHEMES][2]; + +// Print compact list of existing and available FEC schemes +void liquid_print_fec_schemes(); + +// returns fec_scheme based on input string +fec_scheme liquid_getopt_str2fec(const char * _str); + +// fec object (pointer to fec structure) +typedef struct fec_s * fec; + +// return the encoded message length using a particular error- +// correction scheme (object-independent method) +// _scheme : forward error-correction scheme +// _msg_len : raw, uncoded message length +unsigned int fec_get_enc_msg_length(fec_scheme _scheme, + unsigned int _msg_len); + +// get the theoretical rate of a particular forward error- +// correction scheme (object-independent method) +float fec_get_rate(fec_scheme _scheme); + +// create a fec object of a particular scheme +// _scheme : error-correction scheme +// _opts : (ignored) +fec fec_create(fec_scheme _scheme, + void *_opts); + +// recreate fec object +// _q : old fec object +// _scheme : new error-correction scheme +// _opts : (ignored) +fec fec_recreate(fec _q, + fec_scheme _scheme, + void *_opts); + +// destroy fec object +int fec_destroy(fec _q); + +// print fec object internals +int fec_print(fec _q); + +// encode a block of data using a fec scheme +// _q : fec object +// _dec_msg_len : decoded message length +// _msg_dec : decoded message +// _msg_enc : encoded message +int fec_encode(fec _q, + unsigned int _dec_msg_len, + unsigned char * _msg_dec, + unsigned char * _msg_enc); + +// decode a block of data using a fec scheme +// _q : fec object +// _dec_msg_len : decoded message length +// _msg_enc : encoded message +// _msg_dec : decoded message +int fec_decode(fec _q, + unsigned int _dec_msg_len, + unsigned char * _msg_enc, + unsigned char * _msg_dec); + +// decode a block of data using a fec scheme (soft decision) +// _q : fec object +// _dec_msg_len : decoded message length +// _msg_enc : encoded message (soft bits) +// _msg_dec : decoded message +int fec_decode_soft(fec _q, + unsigned int _dec_msg_len, + unsigned char * _msg_enc, + unsigned char * _msg_dec); + +// +// Packetizer +// + +// computes the number of encoded bytes after packetizing +// +// _n : number of uncoded input bytes +// _crc : error-detecting scheme +// _fec0 : inner forward error-correction code +// _fec1 : outer forward error-correction code +unsigned int packetizer_compute_enc_msg_len(unsigned int _n, + int _crc, + int _fec0, + int _fec1); + +// computes the number of decoded bytes before packetizing +// +// _k : number of encoded bytes +// _crc : error-detecting scheme +// _fec0 : inner forward error-correction code +// _fec1 : outer forward error-correction code +unsigned int packetizer_compute_dec_msg_len(unsigned int _k, + int _crc, + int _fec0, + int _fec1); + +typedef struct packetizer_s * packetizer; + +// create packetizer object +// +// _n : number of uncoded input bytes +// _crc : error-detecting scheme +// _fec0 : inner forward error-correction code +// _fec1 : outer forward error-correction code +packetizer packetizer_create(unsigned int _dec_msg_len, + int _crc, + int _fec0, + int _fec1); + +// re-create packetizer object +// +// _p : initialz packetizer object +// _n : number of uncoded input bytes +// _crc : error-detecting scheme +// _fec0 : inner forward error-correction code +// _fec1 : outer forward error-correction code +packetizer packetizer_recreate(packetizer _p, + unsigned int _dec_msg_len, + int _crc, + int _fec0, + int _fec1); + +// destroy packetizer object +void packetizer_destroy(packetizer _p); + +// print packetizer object internals +void packetizer_print(packetizer _p); + +// access methods +unsigned int packetizer_get_dec_msg_len(packetizer _p); +unsigned int packetizer_get_enc_msg_len(packetizer _p); +crc_scheme packetizer_get_crc (packetizer _p); +fec_scheme packetizer_get_fec0 (packetizer _p); +fec_scheme packetizer_get_fec1 (packetizer _p); + + +// Execute the packetizer on an input message +// +// _p : packetizer object +// _msg : input message (uncoded bytes) +// _pkt : encoded output message +void packetizer_encode(packetizer _p, + const unsigned char * _msg, + unsigned char * _pkt); + +// Execute the packetizer to decode an input message, return validity +// check of resulting data +// +// _p : packetizer object +// _pkt : input message (coded bytes) +// _msg : decoded output message +int packetizer_decode(packetizer _p, + const unsigned char * _pkt, + unsigned char * _msg); + +// Execute the packetizer to decode an input message, return validity +// check of resulting data +// +// _p : packetizer object +// _pkt : input message (coded soft bits) +// _msg : decoded output message +int packetizer_decode_soft(packetizer _p, + const unsigned char * _pkt, + unsigned char * _msg); + + +// +// interleaver +// +typedef struct interleaver_s * interleaver; + +// create interleaver +// _n : number of bytes +interleaver interleaver_create(unsigned int _n); + +// destroy interleaver object +void interleaver_destroy(interleaver _q); + +// print interleaver object internals +void interleaver_print(interleaver _q); + +// set depth (number of internal iterations) +// _q : interleaver object +// _depth : depth +void interleaver_set_depth(interleaver _q, + unsigned int _depth); + +// execute forward interleaver (encoder) +// _q : interleaver object +// _msg_dec : decoded (un-interleaved) message +// _msg_enc : encoded (interleaved) message +void interleaver_encode(interleaver _q, + unsigned char * _msg_dec, + unsigned char * _msg_enc); + +// execute forward interleaver (encoder) on soft bits +// _q : interleaver object +// _msg_dec : decoded (un-interleaved) message +// _msg_enc : encoded (interleaved) message +void interleaver_encode_soft(interleaver _q, + unsigned char * _msg_dec, + unsigned char * _msg_enc); + +// execute reverse interleaver (decoder) +// _q : interleaver object +// _msg_enc : encoded (interleaved) message +// _msg_dec : decoded (un-interleaved) message +void interleaver_decode(interleaver _q, + unsigned char * _msg_enc, + unsigned char * _msg_dec); + +// execute reverse interleaver (decoder) on soft bits +// _q : interleaver object +// _msg_enc : encoded (interleaved) message +// _msg_dec : decoded (un-interleaved) message +void interleaver_decode_soft(interleaver _q, + unsigned char * _msg_enc, + unsigned char * _msg_dec); + + + +// +// MODULE : fft (fast Fourier transform) +// + +// type of transform +typedef enum { + LIQUID_FFT_UNKNOWN = 0, // unknown transform type + + // regular complex one-dimensional transforms + LIQUID_FFT_FORWARD = +1, // complex one-dimensional FFT + LIQUID_FFT_BACKWARD = -1, // complex one-dimensional inverse FFT + + // discrete cosine transforms + LIQUID_FFT_REDFT00 = 10, // real one-dimensional DCT-I + LIQUID_FFT_REDFT10 = 11, // real one-dimensional DCT-II + LIQUID_FFT_REDFT01 = 12, // real one-dimensional DCT-III + LIQUID_FFT_REDFT11 = 13, // real one-dimensional DCT-IV + + // discrete sine transforms + LIQUID_FFT_RODFT00 = 20, // real one-dimensional DST-I + LIQUID_FFT_RODFT10 = 21, // real one-dimensional DST-II + LIQUID_FFT_RODFT01 = 22, // real one-dimensional DST-III + LIQUID_FFT_RODFT11 = 23, // real one-dimensional DST-IV + + // modified discrete cosine transform + LIQUID_FFT_MDCT = 30, // MDCT + LIQUID_FFT_IMDCT = 31, // IMDCT +} liquid_fft_type; + +#define LIQUID_FFT_MANGLE_FLOAT(name) LIQUID_CONCAT(fft,name) + +// Macro : FFT +// FFT : name-mangling macro +// T : primitive data type +// TC : primitive data type (complex) +#define LIQUID_FFT_DEFINE_API(FFT,T,TC) \ + \ +/* Fast Fourier Transform (FFT) and inverse (plan) object */ \ +typedef struct FFT(plan_s) * FFT(plan); \ + \ +/* Create regular complex one-dimensional transform */ \ +/* _n : transform size */ \ +/* _x : pointer to input array [size: _n x 1] */ \ +/* _y : pointer to output array [size: _n x 1] */ \ +/* _dir : direction (e.g. LIQUID_FFT_FORWARD) */ \ +/* _flags : options, optimization */ \ +FFT(plan) FFT(_create_plan)(unsigned int _n, \ + TC * _x, \ + TC * _y, \ + int _dir, \ + int _flags); \ + \ +/* Create real-to-real one-dimensional transform */ \ +/* _n : transform size */ \ +/* _x : pointer to input array [size: _n x 1] */ \ +/* _y : pointer to output array [size: _n x 1] */ \ +/* _type : transform type (e.g. LIQUID_FFT_REDFT00) */ \ +/* _flags : options, optimization */ \ +FFT(plan) FFT(_create_plan_r2r_1d)(unsigned int _n, \ + T * _x, \ + T * _y, \ + int _type, \ + int _flags); \ + \ +/* Destroy transform and free all internally-allocated memory */ \ +int FFT(_destroy_plan)(FFT(plan) _p); \ + \ +/* Print transform plan and internal strategy to stdout. This includes */ \ +/* information on the strategy for computing large transforms with many */ \ +/* prime factors or with large prime factors. */ \ +int FFT(_print_plan)(FFT(plan) _p); \ + \ +/* Run the transform */ \ +int FFT(_execute)(FFT(plan) _p); \ + \ +/* Perform n-point FFT allocating plan internally */ \ +/* _nfft : fft size */ \ +/* _x : input array [size: _nfft x 1] */ \ +/* _y : output array [size: _nfft x 1] */ \ +/* _dir : fft direction: LIQUID_FFT_{FORWARD,BACKWARD} */ \ +/* _flags : fft flags */ \ +int FFT(_run)(unsigned int _n, \ + TC * _x, \ + TC * _y, \ + int _dir, \ + int _flags); \ + \ +/* Perform n-point real one-dimensional FFT allocating plan internally */ \ +/* _nfft : fft size */ \ +/* _x : input array [size: _nfft x 1] */ \ +/* _y : output array [size: _nfft x 1] */ \ +/* _type : fft type, e.g. LIQUID_FFT_REDFT10 */ \ +/* _flags : fft flags */ \ +int FFT(_r2r_1d_run)(unsigned int _n, \ + T * _x, \ + T * _y, \ + int _type, \ + int _flags); \ + \ +/* Perform _n-point fft shift */ \ +/* _x : input array [size: _n x 1] */ \ +/* _n : input array size */ \ +int FFT(_shift)(TC * _x, \ + unsigned int _n); \ + + +LIQUID_FFT_DEFINE_API(LIQUID_FFT_MANGLE_FLOAT,float,liquid_float_complex) + +// antiquated fft methods +// FFT(plan) FFT(_create_plan_mdct)(unsigned int _n, +// T * _x, +// T * _y, +// int _kind, +// int _flags); + + +// +// spectral periodogram +// + +#define LIQUID_SPGRAM_MANGLE_CFLOAT(name) LIQUID_CONCAT(spgramcf,name) +#define LIQUID_SPGRAM_MANGLE_FLOAT(name) LIQUID_CONCAT(spgramf, name) + +#define LIQUID_SPGRAM_PSD_MIN (1e-12) + +// Macro : SPGRAM +// SPGRAM : name-mangling macro +// T : primitive data type +// TC : primitive data type (complex) +// TI : primitive data type (input) +#define LIQUID_SPGRAM_DEFINE_API(SPGRAM,T,TC,TI) \ + \ +/* Spectral periodogram object for computing power spectral density */ \ +/* estimates of various signals */ \ +typedef struct SPGRAM(_s) * SPGRAM(); \ + \ +/* Create spgram object, fully defined */ \ +/* _nfft : transform (FFT) size, _nfft >= 2 */ \ +/* _wtype : window type, e.g. LIQUID_WINDOW_HAMMING */ \ +/* _window_len : window length, 1 <= _window_len <= _nfft */ \ +/* _delay : delay between transforms, _delay > 0 */ \ +SPGRAM() SPGRAM(_create)(unsigned int _nfft, \ + int _wtype, \ + unsigned int _window_len, \ + unsigned int _delay); \ + \ +/* Create default spgram object of a particular transform size using */ \ +/* the Kaiser-Bessel window (LIQUID_WINDOW_KAISER), a window length */ \ +/* equal to _nfft/2, and a delay of _nfft/4 */ \ +/* _nfft : FFT size, _nfft >= 2 */ \ +SPGRAM() SPGRAM(_create_default)(unsigned int _nfft); \ + \ +/* Destroy spgram object, freeing all internally-allocated memory */ \ +int SPGRAM(_destroy)(SPGRAM() _q); \ + \ +/* Clears the internal state of the object, but not the internal buffer */ \ +int SPGRAM(_clear)(SPGRAM() _q); \ + \ +/* Reset the object to its original state completely. This effectively */ \ +/* executes the clear() method and then resets the internal buffer */ \ +int SPGRAM(_reset)(SPGRAM() _q); \ + \ +/* Print internal state of the object to stdout */ \ +int SPGRAM(_print)(SPGRAM() _q); \ + \ +/* Set the filter bandwidth for accumulating independent transform */ \ +/* squared magnitude outputs. */ \ +/* This is used to compute a running time-average power spectral */ \ +/* density output. */ \ +/* The value of _alpha determines how the power spectral estimate is */ \ +/* accumulated across transforms and can range from 0 to 1 with a */ \ +/* special case of -1 to accumulate infinitely. */ \ +/* Setting _alpha to 0 minimizes the bandwidth and the PSD estimate */ \ +/* will never update. */ \ +/* Setting _alpha to 1 forces the object to always use the most recent */ \ +/* spectral estimate. */ \ +/* Setting _alpha to -1 is a special case to enable infinite spectral */ \ +/* accumulation. */ \ +/* _q : spectral periodogram object */ \ +/* _alpha : forgetting factor, set to -1 for infinite, 0<=_alpha<=1 */ \ +int SPGRAM(_set_alpha)(SPGRAM() _q, \ + float _alpha); \ + \ +/* Get the filter bandwidth for accumulating independent transform */ \ +/* squared magnitude outputs. */ \ +float SPGRAM(_get_alpha)(SPGRAM() _q); \ + \ +/* Set the center frequency of the received signal. */ \ +/* This is for display purposes only when generating the output image. */ \ +/* _q : spectral periodogram object */ \ +/* _freq : center frequency [Hz] */ \ +int SPGRAM(_set_freq)(SPGRAM() _q, \ + float _freq); \ + \ +/* Set the sample rate (frequency) of the received signal. */ \ +/* This is for display purposes only when generating the output image. */ \ +/* _q : spectral periodogram object */ \ +/* _rate : sample rate [Hz] */ \ +int SPGRAM(_set_rate)(SPGRAM() _q, \ + float _rate); \ + \ +/* Get transform (FFT) size */ \ +unsigned int SPGRAM(_get_nfft)(SPGRAM() _q); \ + \ +/* Get window length */ \ +unsigned int SPGRAM(_get_window_len)(SPGRAM() _q); \ + \ +/* Get delay between transforms */ \ +unsigned int SPGRAM(_get_delay)(SPGRAM() _q); \ + \ +/* Get number of samples processed since reset */ \ +unsigned long long int SPGRAM(_get_num_samples)(SPGRAM() _q); \ + \ +/* Get number of samples processed since object was created */ \ +unsigned long long int SPGRAM(_get_num_samples_total)(SPGRAM() _q); \ + \ +/* Get number of transforms processed since reset */ \ +unsigned long long int SPGRAM(_get_num_transforms)(SPGRAM() _q); \ + \ +/* Get number of transforms processed since object was created */ \ +unsigned long long int SPGRAM(_get_num_transforms_total)(SPGRAM() _q); \ + \ +/* Push a single sample into the object, executing internal transform */ \ +/* as necessary. */ \ +/* _q : spgram object */ \ +/* _x : input sample */ \ +int SPGRAM(_push)(SPGRAM() _q, \ + TI _x); \ + \ +/* Write a block of samples to the object, executing internal */ \ +/* transform as necessary. */ \ +/* _q : spgram object */ \ +/* _x : input buffer [size: _n x 1] */ \ +/* _n : input buffer length */ \ +int SPGRAM(_write)(SPGRAM() _q, \ + TI * _x, \ + unsigned int _n); \ + \ +/* Compute spectral periodogram output (fft-shifted values in dB) from */ \ +/* current buffer contents */ \ +/* _q : spgram object */ \ +/* _X : output spectrum (dB), [size: _nfft x 1] */ \ +int SPGRAM(_get_psd)(SPGRAM() _q, \ + T * _X); \ + \ +/* Export stand-alone gnuplot file for plotting output spectrum, */ \ +/* returning 0 on sucess, anything other than 0 for failure */ \ +/* _q : spgram object */ \ +/* _filename : input buffer [size: _n x 1] */ \ +int SPGRAM(_export_gnuplot)(SPGRAM() _q, \ + const char * _filename); \ + \ +/* Estimate spectrum on input signal (create temporary object for */ \ +/* convenience */ \ +/* _nfft : FFT size */ \ +/* _x : input signal [size: _n x 1] */ \ +/* _n : input signal length */ \ +/* _psd : output spectrum, [size: _nfft x 1] */ \ +int SPGRAM(_estimate_psd)(unsigned int _nfft, \ + TI * _x, \ + unsigned int _n, \ + T * _psd); \ + +LIQUID_SPGRAM_DEFINE_API(LIQUID_SPGRAM_MANGLE_CFLOAT, + float, + liquid_float_complex, + liquid_float_complex) + +LIQUID_SPGRAM_DEFINE_API(LIQUID_SPGRAM_MANGLE_FLOAT, + float, + liquid_float_complex, + float) + +// +// asgram : ascii spectral periodogram +// + +#define LIQUID_ASGRAM_MANGLE_CFLOAT(name) LIQUID_CONCAT(asgramcf,name) +#define LIQUID_ASGRAM_MANGLE_FLOAT(name) LIQUID_CONCAT(asgramf, name) + +// Macro : ASGRAM +// ASGRAM : name-mangling macro +// T : primitive data type +// TC : primitive data type (complex) +// TI : primitive data type (input) +#define LIQUID_ASGRAM_DEFINE_API(ASGRAM,T,TC,TI) \ + \ +/* ASCII spectral periodogram for computing and displaying an estimate */ \ +/* of a signal's power spectrum with ASCII characters */ \ +typedef struct ASGRAM(_s) * ASGRAM(); \ + \ +/* Create asgram object with size _nfft */ \ +/* _nfft : size of FFT taken for each transform (character width) */ \ +ASGRAM() ASGRAM(_create)(unsigned int _nfft); \ + \ +/* Destroy asgram object, freeing all internally-allocated memory */ \ +int ASGRAM(_destroy)(ASGRAM() _q); \ + \ +/* Reset the internal state of the asgram object */ \ +int ASGRAM(_reset)(ASGRAM() _q); \ + \ +/* Set the scale and offset for spectrogram in terms of dB for display */ \ +/* purposes */ \ +/* _q : asgram object */ \ +/* _ref : signal reference level [dB] */ \ +/* _div : signal division [dB] */ \ +int ASGRAM(_set_scale)(ASGRAM() _q, \ + float _ref, \ + float _div); \ + \ +/* Set the display's 10 characters for output string starting from the */ \ +/* weakest and ending with the strongest */ \ +/* _q : asgram object */ \ +/* _ascii : 10-character display, default: " .,-+*&NM#" */ \ +int ASGRAM(_set_display)(ASGRAM() _q, \ + const char * _ascii); \ + \ +/* Push a single sample into the asgram object, executing internal */ \ +/* transform as necessary. */ \ +/* _q : asgram object */ \ +/* _x : input sample */ \ +int ASGRAM(_push)(ASGRAM() _q, \ + TI _x); \ + \ +/* Write a block of samples to the asgram object, executing internal */ \ +/* transforms as necessary. */ \ +/* _q : asgram object */ \ +/* _x : input buffer [size: _n x 1] */ \ +/* _n : input buffer length */ \ +int ASGRAM(_write)(ASGRAM() _q, \ + TI * _x, \ + unsigned int _n); \ + \ +/* Compute spectral periodogram output from current buffer contents */ \ +/* and return the ascii character string to display along with the peak */ \ +/* value and its frequency location */ \ +/* _q : asgram object */ \ +/* _ascii : output ASCII string [size: _nfft x 1] */ \ +/* _peakval : peak power spectral density value [dB] */ \ +/* _peakfreq : peak power spectral density frequency */ \ +int ASGRAM(_execute)(ASGRAM() _q, \ + char * _ascii, \ + float * _peakval, \ + float * _peakfreq); \ + \ +/* Compute spectral periodogram output from current buffer contents and */ \ +/* print standard format to stdout */ \ +int ASGRAM(_print)(ASGRAM() _q); \ + +LIQUID_ASGRAM_DEFINE_API(LIQUID_ASGRAM_MANGLE_CFLOAT, + float, + liquid_float_complex, + liquid_float_complex) + +LIQUID_ASGRAM_DEFINE_API(LIQUID_ASGRAM_MANGLE_FLOAT, + float, + liquid_float_complex, + float) + +// +// spectral periodogram waterfall +// + +#define LIQUID_SPWATERFALL_MANGLE_CFLOAT(name) LIQUID_CONCAT(spwaterfallcf,name) +#define LIQUID_SPWATERFALL_MANGLE_FLOAT(name) LIQUID_CONCAT(spwaterfallf, name) + +// Macro : SPWATERFALL +// SPWATERFALL : name-mangling macro +// T : primitive data type +// TC : primitive data type (complex) +// TI : primitive data type (input) +#define LIQUID_SPWATERFALL_DEFINE_API(SPWATERFALL,T,TC,TI) \ + \ +/* Spectral periodogram waterfall object for computing time-varying */ \ +/* power spectral density estimates */ \ +typedef struct SPWATERFALL(_s) * SPWATERFALL(); \ + \ +/* Create spwaterfall object, fully defined */ \ +/* _nfft : transform (FFT) size, _nfft >= 2 */ \ +/* _wtype : window type, e.g. LIQUID_WINDOW_HAMMING */ \ +/* _window_len : window length, 1 <= _window_len <= _nfft */ \ +/* _delay : delay between transforms, _delay > 0 */ \ +/* _time : number of aggregated transforms, _time > 0 */ \ +SPWATERFALL() SPWATERFALL(_create)(unsigned int _nfft, \ + int _wtype, \ + unsigned int _window_len, \ + unsigned int _delay, \ + unsigned int _time); \ + \ +/* Create default spwatefall object (Kaiser-Bessel window) */ \ +/* _nfft : transform size, _nfft >= 2 */ \ +/* _time : delay between transforms, _delay > 0 */ \ +SPWATERFALL() SPWATERFALL(_create_default)(unsigned int _nfft, \ + unsigned int _time); \ + \ +/* Destroy spwaterfall object, freeing all internally-allocated memory */ \ +int SPWATERFALL(_destroy)(SPWATERFALL() _q); \ + \ +/* Clears the internal state of the object, but not the internal buffer */ \ +int SPWATERFALL(_clear)(SPWATERFALL() _q); \ + \ +/* Reset the object to its original state completely. This effectively */ \ +/* executes the clear() method and then resets the internal buffer */ \ +int SPWATERFALL(_reset)(SPWATERFALL() _q); \ + \ +/* Print internal state of the object to stdout */ \ +int SPWATERFALL(_print)(SPWATERFALL() _q); \ + \ +/* Get number of samples processed since object was created */ \ +uint64_t SPWATERFALL(_get_num_samples_total)(SPWATERFALL() _q); \ + \ +/* Get FFT size (columns in PSD output) */ \ +unsigned int SPWATERFALL(_get_num_freq)(SPWATERFALL() _q); \ + \ +/* Get number of accumulated FFTs (rows in PSD output) */ \ +unsigned int SPWATERFALL(_get_num_time)(SPWATERFALL() _q); \ + \ +/* Get power spectral density (PSD), size: nfft x time */ \ +const T * SPWATERFALL(_get_psd)(SPWATERFALL() _q); \ + \ +/* Set the center frequency of the received signal. */ \ +/* This is for display purposes only when generating the output image. */ \ +/* _q : spectral periodogram waterfall object */ \ +/* _freq : center frequency [Hz] */ \ +int SPWATERFALL(_set_freq)(SPWATERFALL() _q, \ + float _freq); \ + \ +/* Set the sample rate (frequency) of the received signal. */ \ +/* This is for display purposes only when generating the output image. */ \ +/* _q : spectral periodogram waterfall object */ \ +/* _rate : sample rate [Hz] */ \ +int SPWATERFALL(_set_rate)(SPWATERFALL() _q, \ + float _rate); \ + \ +/* Set the canvas size. */ \ +/* This is for display purposes only when generating the output image. */ \ +/* _q : spectral periodogram waterfall object */ \ +/* _width : image width [pixels] */ \ +/* _height : image height [pixels] */ \ +int SPWATERFALL(_set_dims)(SPWATERFALL() _q, \ + unsigned int _width, \ + unsigned int _height); \ + \ +/* Set commands for executing directly before 'plot' statement. */ \ +/* _q : spectral periodogram waterfall object */ \ +/* _commands : gnuplot commands separated by semicolons */ \ +int SPWATERFALL(_set_commands)(SPWATERFALL() _q, \ + const char * _commands); \ + \ +/* Push a single sample into the object, executing internal transform */ \ +/* as necessary. */ \ +/* _q : spwaterfall object */ \ +/* _x : input sample */ \ +int SPWATERFALL(_push)(SPWATERFALL() _q, \ + TI _x); \ + \ +/* Write a block of samples to the object, executing internal */ \ +/* transform as necessary. */ \ +/* _q : spwaterfall object */ \ +/* _x : input buffer, [size: _n x 1] */ \ +/* _n : input buffer length */ \ +int SPWATERFALL(_write)(SPWATERFALL() _q, \ + TI * _x, \ + unsigned int _n); \ + \ +/* Export set of files for plotting */ \ +/* _q : spwaterfall object */ \ +/* _base : base filename (will export .gnu, .bin, and .png files) */ \ +int SPWATERFALL(_export)(SPWATERFALL() _q, \ + const char * _base); \ + + +LIQUID_SPWATERFALL_DEFINE_API(LIQUID_SPWATERFALL_MANGLE_CFLOAT, + float, + liquid_float_complex, + liquid_float_complex) + +LIQUID_SPWATERFALL_DEFINE_API(LIQUID_SPWATERFALL_MANGLE_FLOAT, + float, + liquid_float_complex, + float) + + +// +// MODULE : filter +// + +// +// firdes: finite impulse response filter design +// + +// prototypes +#define LIQUID_FIRFILT_NUM_TYPES (16) +typedef enum { + LIQUID_FIRFILT_UNKNOWN=0, // unknown filter type + + // Nyquist filter prototypes + LIQUID_FIRFILT_KAISER, // Nyquist Kaiser filter + LIQUID_FIRFILT_PM, // Parks-McClellan filter + LIQUID_FIRFILT_RCOS, // raised-cosine filter + LIQUID_FIRFILT_FEXP, // flipped exponential + LIQUID_FIRFILT_FSECH, // flipped hyperbolic secant + LIQUID_FIRFILT_FARCSECH, // flipped arc-hyperbolic secant + + // root-Nyquist filter prototypes + LIQUID_FIRFILT_ARKAISER, // root-Nyquist Kaiser (approximate optimum) + LIQUID_FIRFILT_RKAISER, // root-Nyquist Kaiser (true optimum) + LIQUID_FIRFILT_RRC, // root raised-cosine + LIQUID_FIRFILT_hM3, // harris-Moerder-3 filter + LIQUID_FIRFILT_GMSKTX, // GMSK transmit filter + LIQUID_FIRFILT_GMSKRX, // GMSK receive filter + LIQUID_FIRFILT_RFEXP, // flipped exponential + LIQUID_FIRFILT_RFSECH, // flipped hyperbolic secant + LIQUID_FIRFILT_RFARCSECH, // flipped arc-hyperbolic secant +} liquid_firfilt_type; + +// Design (root-)Nyquist filter from prototype +// _type : filter type (e.g. LIQUID_FIRFILT_RRC) +// _k : samples/symbol, _k > 1 +// _m : symbol delay, _m > 0 +// _beta : excess bandwidth factor, _beta in [0,1) +// _dt : fractional sample delay, _dt in [-1,1] +// _h : output coefficient buffer (length: 2*_k*_m+1) +void liquid_firdes_prototype(liquid_firfilt_type _type, + unsigned int _k, + unsigned int _m, + float _beta, + float _dt, + float * _h); + +// pretty names for filter design types +extern const char * liquid_firfilt_type_str[LIQUID_FIRFILT_NUM_TYPES][2]; + +// returns filter type based on input string +int liquid_getopt_str2firfilt(const char * _str); + +// estimate required filter length given +// _df : transition bandwidth (0 < _b < 0.5) +// _As : stop-band attenuation [dB], _As > 0 +unsigned int estimate_req_filter_len(float _df, + float _As); + +// estimate filter stop-band attenuation given +// _df : transition bandwidth (0 < _b < 0.5) +// _N : filter length +float estimate_req_filter_As(float _df, + unsigned int _N); + +// estimate filter transition bandwidth given +// _As : stop-band attenuation [dB], _As > 0 +// _N : filter length +float estimate_req_filter_df(float _As, + unsigned int _N); + + +// returns the Kaiser window beta factor give the filter's target +// stop-band attenuation (As) [Vaidyanathan:1993] +// _As : target filter's stop-band attenuation [dB], _As > 0 +float kaiser_beta_As(float _As); + + +// Design FIR filter using Parks-McClellan algorithm + +// band type specifier +typedef enum { + LIQUID_FIRDESPM_BANDPASS=0, // regular band-pass filter + LIQUID_FIRDESPM_DIFFERENTIATOR, // differentiating filter + LIQUID_FIRDESPM_HILBERT // Hilbert transform +} liquid_firdespm_btype; + +// weighting type specifier +typedef enum { + LIQUID_FIRDESPM_FLATWEIGHT=0, // flat weighting + LIQUID_FIRDESPM_EXPWEIGHT, // exponential weighting + LIQUID_FIRDESPM_LINWEIGHT, // linear weighting +} liquid_firdespm_wtype; + +// run filter design (full life cycle of object) +// _h_len : length of filter (number of taps) +// _num_bands : number of frequency bands +// _bands : band edges, f in [0,0.5], [size: _num_bands x 2] +// _des : desired response [size: _num_bands x 1] +// _weights : response weighting [size: _num_bands x 1] +// _wtype : weight types (e.g. LIQUID_FIRDESPM_FLATWEIGHT) [size: _num_bands x 1] +// _btype : band type (e.g. LIQUID_FIRDESPM_BANDPASS) +// _h : output coefficients array [size: _h_len x 1] +int firdespm_run(unsigned int _h_len, + unsigned int _num_bands, + float * _bands, + float * _des, + float * _weights, + liquid_firdespm_wtype * _wtype, + liquid_firdespm_btype _btype, + float * _h); + +// run filter design for basic low-pass filter +// _n : filter length, _n > 0 +// _fc : cutoff frequency, 0 < _fc < 0.5 +// _As : stop-band attenuation [dB], _As > 0 +// _mu : fractional sample offset, -0.5 < _mu < 0.5 [ignored] +// _h : output coefficient buffer, [size: _n x 1] +int firdespm_lowpass(unsigned int _n, + float _fc, + float _As, + float _mu, + float * _h); + +// firdespm response callback function +// _frequency : normalized frequency +// _userdata : pointer to userdata +// _desired : (return) desired response +// _weight : (return) weight +typedef int (*firdespm_callback)(double _frequency, + void * _userdata, + double * _desired, + double * _weight); + +// structured object +typedef struct firdespm_s * firdespm; + +// create firdespm object +// _h_len : length of filter (number of taps) +// _num_bands : number of frequency bands +// _bands : band edges, f in [0,0.5], [size: _num_bands x 2] +// _des : desired response [size: _num_bands x 1] +// _weights : response weighting [size: _num_bands x 1] +// _wtype : weight types (e.g. LIQUID_FIRDESPM_FLATWEIGHT) [size: _num_bands x 1] +// _btype : band type (e.g. LIQUID_FIRDESPM_BANDPASS) +firdespm firdespm_create(unsigned int _h_len, + unsigned int _num_bands, + float * _bands, + float * _des, + float * _weights, + liquid_firdespm_wtype * _wtype, + liquid_firdespm_btype _btype); + +// create firdespm object with user-defined callback +// _h_len : length of filter (number of taps) +// _num_bands : number of frequency bands +// _bands : band edges, f in [0,0.5], [size: _num_bands x 2] +// _btype : band type (e.g. LIQUID_FIRDESPM_BANDPASS) +// _callback : user-defined callback for specifying desired response & weights +// _userdata : user-defined data structure for callback function +firdespm firdespm_create_callback(unsigned int _h_len, + unsigned int _num_bands, + float * _bands, + liquid_firdespm_btype _btype, + firdespm_callback _callback, + void * _userdata); + +// destroy firdespm object +int firdespm_destroy(firdespm _q); + +// print firdespm object internals +int firdespm_print(firdespm _q); + +// execute filter design, storing result in _h +int firdespm_execute(firdespm _q, float * _h); + + +// Design FIR using kaiser window +// _n : filter length, _n > 0 +// _fc : cutoff frequency, 0 < _fc < 0.5 +// _As : stop-band attenuation [dB], _As > 0 +// _mu : fractional sample offset, -0.5 < _mu < 0.5 +// _h : output coefficient buffer, [size: _n x 1] +void liquid_firdes_kaiser(unsigned int _n, + float _fc, + float _As, + float _mu, + float *_h); + +// Design finite impulse response notch filter +// _m : filter semi-length, m in [1,1000] +// _f0 : filter notch frequency (normalized), -0.5 <= _fc <= 0.5 +// _As : stop-band attenuation [dB], _As > 0 +// _h : output coefficient buffer, [size: 2*_m+1 x 1] +void liquid_firdes_notch(unsigned int _m, + float _f0, + float _As, + float * _h); + +// Design FIR doppler filter +// _n : filter length +// _fd : normalized doppler frequency (0 < _fd < 0.5) +// _K : Rice fading factor (K >= 0) +// _theta : LoS component angle of arrival +// _h : output coefficient buffer +void liquid_firdes_doppler(unsigned int _n, + float _fd, + float _K, + float _theta, + float * _h); + + +// Design Nyquist raised-cosine filter +// _k : samples/symbol +// _m : symbol delay +// _beta : rolloff factor (0 < beta <= 1) +// _dt : fractional sample delay +// _h : output coefficient buffer (length: 2*k*m+1) +void liquid_firdes_rcos(unsigned int _k, + unsigned int _m, + float _beta, + float _dt, + float * _h); + +// Design root-Nyquist raised-cosine filter +void liquid_firdes_rrcos(unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h); + +// Design root-Nyquist Kaiser filter +void liquid_firdes_rkaiser(unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h); + +// Design (approximate) root-Nyquist Kaiser filter +void liquid_firdes_arkaiser(unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h); + +// Design root-Nyquist harris-Moerder filter +void liquid_firdes_hM3(unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h); + +// Design GMSK transmit and receive filters +void liquid_firdes_gmsktx(unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h); +void liquid_firdes_gmskrx(unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h); + +// Design flipped exponential Nyquist/root-Nyquist filters +void liquid_firdes_fexp( unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h); +void liquid_firdes_rfexp(unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h); + +// Design flipped hyperbolic secand Nyquist/root-Nyquist filters +void liquid_firdes_fsech( unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h); +void liquid_firdes_rfsech(unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h); + +// Design flipped arc-hyperbolic secand Nyquist/root-Nyquist filters +void liquid_firdes_farcsech( unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h); +void liquid_firdes_rfarcsech(unsigned int _k, unsigned int _m, float _beta, float _dt, float * _h); + +// Compute group delay for an FIR filter +// _h : filter coefficients array +// _n : filter length +// _fc : frequency at which delay is evaluated (-0.5 < _fc < 0.5) +float fir_group_delay(float * _h, + unsigned int _n, + float _fc); + +// Compute group delay for an IIR filter +// _b : filter numerator coefficients +// _nb : filter numerator length +// _a : filter denominator coefficients +// _na : filter denominator length +// _fc : frequency at which delay is evaluated (-0.5 < _fc < 0.5) +float iir_group_delay(float * _b, + unsigned int _nb, + float * _a, + unsigned int _na, + float _fc); + + +// liquid_filter_autocorr() +// +// Compute auto-correlation of filter at a specific lag. +// +// _h : filter coefficients [size: _h_len x 1] +// _h_len : filter length +// _lag : auto-correlation lag (samples) +float liquid_filter_autocorr(float * _h, + unsigned int _h_len, + int _lag); + +// liquid_filter_crosscorr() +// +// Compute cross-correlation of two filters at a specific lag. +// +// _h : filter coefficients [size: _h_len] +// _h_len : filter length +// _g : filter coefficients [size: _g_len] +// _g_len : filter length +// _lag : cross-correlation lag (samples) +float liquid_filter_crosscorr(float * _h, + unsigned int _h_len, + float * _g, + unsigned int _g_len, + int _lag); + +// liquid_filter_isi() +// +// Compute inter-symbol interference (ISI)--both RMS and +// maximum--for the filter _h. +// +// _h : filter coefficients [size: 2*_k*_m+1 x 1] +// _k : filter over-sampling rate (samples/symbol) +// _m : filter delay (symbols) +// _rms : output root mean-squared ISI +// _max : maximum ISI +void liquid_filter_isi(float * _h, + unsigned int _k, + unsigned int _m, + float * _rms, + float * _max); + +// Compute relative out-of-band energy +// +// _h : filter coefficients [size: _h_len x 1] +// _h_len : filter length +// _fc : analysis cut-off frequency +// _nfft : fft size +float liquid_filter_energy(float * _h, + unsigned int _h_len, + float _fc, + unsigned int _nfft); + + +// +// IIR filter design +// + +// IIR filter design filter type +typedef enum { + LIQUID_IIRDES_BUTTER=0, + LIQUID_IIRDES_CHEBY1, + LIQUID_IIRDES_CHEBY2, + LIQUID_IIRDES_ELLIP, + LIQUID_IIRDES_BESSEL +} liquid_iirdes_filtertype; + +// IIR filter design band type +typedef enum { + LIQUID_IIRDES_LOWPASS=0, + LIQUID_IIRDES_HIGHPASS, + LIQUID_IIRDES_BANDPASS, + LIQUID_IIRDES_BANDSTOP +} liquid_iirdes_bandtype; + +// IIR filter design coefficients format +typedef enum { + LIQUID_IIRDES_SOS=0, + LIQUID_IIRDES_TF +} liquid_iirdes_format; + +// IIR filter design template +// _ftype : filter type (e.g. LIQUID_IIRDES_BUTTER) +// _btype : band type (e.g. LIQUID_IIRDES_BANDPASS) +// _format : coefficients format (e.g. LIQUID_IIRDES_SOS) +// _n : filter order +// _fc : low-pass prototype cut-off frequency +// _f0 : center frequency (band-pass, band-stop) +// _Ap : pass-band ripple in dB +// _As : stop-band ripple in dB +// _B : numerator +// _A : denominator +void liquid_iirdes(liquid_iirdes_filtertype _ftype, + liquid_iirdes_bandtype _btype, + liquid_iirdes_format _format, + unsigned int _n, + float _fc, + float _f0, + float _Ap, + float _As, + float * _B, + float * _A); + +// compute analog zeros, poles, gain for specific filter types +void butter_azpkf(unsigned int _n, + liquid_float_complex * _za, + liquid_float_complex * _pa, + liquid_float_complex * _ka); +void cheby1_azpkf(unsigned int _n, + float _ep, + liquid_float_complex * _z, + liquid_float_complex * _p, + liquid_float_complex * _k); +void cheby2_azpkf(unsigned int _n, + float _es, + liquid_float_complex * _z, + liquid_float_complex * _p, + liquid_float_complex * _k); +void ellip_azpkf(unsigned int _n, + float _ep, + float _es, + liquid_float_complex * _z, + liquid_float_complex * _p, + liquid_float_complex * _k); +void bessel_azpkf(unsigned int _n, + liquid_float_complex * _z, + liquid_float_complex * _p, + liquid_float_complex * _k); + +// compute frequency pre-warping factor +float iirdes_freqprewarp(liquid_iirdes_bandtype _btype, + float _fc, + float _f0); + +// convert analog z/p/k form to discrete z/p/k form (bilinear z-transform) +// _za : analog zeros [length: _nza] +// _nza : number of analog zeros +// _pa : analog poles [length: _npa] +// _npa : number of analog poles +// _m : frequency pre-warping factor +// _zd : output digital zeros [length: _npa] +// _pd : output digital poles [length: _npa] +// _kd : output digital gain (should actually be real-valued) +void bilinear_zpkf(liquid_float_complex * _za, + unsigned int _nza, + liquid_float_complex * _pa, + unsigned int _npa, + liquid_float_complex _ka, + float _m, + liquid_float_complex * _zd, + liquid_float_complex * _pd, + liquid_float_complex * _kd); + +// digital z/p/k low-pass to high-pass +// _zd : digital zeros (low-pass prototype), [length: _n] +// _pd : digital poles (low-pass prototype), [length: _n] +// _n : low-pass filter order +// _zdt : output digital zeros transformed [length: _n] +// _pdt : output digital poles transformed [length: _n] +void iirdes_dzpk_lp2hp(liquid_float_complex * _zd, + liquid_float_complex * _pd, + unsigned int _n, + liquid_float_complex * _zdt, + liquid_float_complex * _pdt); + +// digital z/p/k low-pass to band-pass +// _zd : digital zeros (low-pass prototype), [length: _n] +// _pd : digital poles (low-pass prototype), [length: _n] +// _n : low-pass filter order +// _f0 : center frequency +// _zdt : output digital zeros transformed [length: 2*_n] +// _pdt : output digital poles transformed [length: 2*_n] +void iirdes_dzpk_lp2bp(liquid_float_complex * _zd, + liquid_float_complex * _pd, + unsigned int _n, + float _f0, + liquid_float_complex * _zdt, + liquid_float_complex * _pdt); + +// convert discrete z/p/k form to transfer function +// _zd : digital zeros [length: _n] +// _pd : digital poles [length: _n] +// _n : filter order +// _kd : digital gain +// _b : output numerator [length: _n+1] +// _a : output denominator [length: _n+1] +void iirdes_dzpk2tff(liquid_float_complex * _zd, + liquid_float_complex * _pd, + unsigned int _n, + liquid_float_complex _kd, + float * _b, + float * _a); + +// convert discrete z/p/k form to second-order sections +// _zd : digital zeros [length: _n] +// _pd : digital poles [length: _n] +// _n : filter order +// _kd : digital gain +// _B : output numerator [size: 3 x L+r] +// _A : output denominator [size: 3 x L+r] +// where r = _n%2, L = (_n-r)/2 +void iirdes_dzpk2sosf(liquid_float_complex * _zd, + liquid_float_complex * _pd, + unsigned int _n, + liquid_float_complex _kd, + float * _B, + float * _A); + +// additional IIR filter design templates + +// design 2nd-order IIR filter (active lag) +// 1 + t2 * s +// F(s) = ------------ +// 1 + t1 * s +// +// _w : filter bandwidth +// _zeta : damping factor (1/sqrt(2) suggested) +// _K : loop gain (1000 suggested) +// _b : output feed-forward coefficients [size: 3 x 1] +// _a : output feed-back coefficients [size: 3 x 1] +void iirdes_pll_active_lag(float _w, + float _zeta, + float _K, + float * _b, + float * _a); + +// design 2nd-order IIR filter (active PI) +// 1 + t2 * s +// F(s) = ------------ +// t1 * s +// +// _w : filter bandwidth +// _zeta : damping factor (1/sqrt(2) suggested) +// _K : loop gain (1000 suggested) +// _b : output feed-forward coefficients [size: 3 x 1] +// _a : output feed-back coefficients [size: 3 x 1] +void iirdes_pll_active_PI(float _w, + float _zeta, + float _K, + float * _b, + float * _a); + +// checks stability of iir filter +// _b : feed-forward coefficients [size: _n x 1] +// _a : feed-back coefficients [size: _n x 1] +// _n : number of coefficients +int iirdes_isstable(float * _b, + float * _a, + unsigned int _n); + +// +// linear prediction +// + +// compute the linear prediction coefficients for an input signal _x +// _x : input signal [size: _n x 1] +// _n : input signal length +// _p : prediction filter order +// _a : prediction filter [size: _p+1 x 1] +// _e : prediction error variance [size: _p+1 x 1] +void liquid_lpc(float * _x, + unsigned int _n, + unsigned int _p, + float * _a, + float * _g); + +// solve the Yule-Walker equations using Levinson-Durbin recursion +// for _symmetric_ autocorrelation +// _r : autocorrelation array [size: _p+1 x 1] +// _p : filter order +// _a : output coefficients [size: _p+1 x 1] +// _e : error variance [size: _p+1 x 1] +// +// NOTES: +// By definition _a[0] = 1.0 +void liquid_levinson(float * _r, + unsigned int _p, + float * _a, + float * _e); + +// +// auto-correlator (delay cross-correlation) +// + +#define LIQUID_AUTOCORR_MANGLE_CCCF(name) LIQUID_CONCAT(autocorr_cccf,name) +#define LIQUID_AUTOCORR_MANGLE_RRRF(name) LIQUID_CONCAT(autocorr_rrrf,name) + +// Macro: +// AUTOCORR : name-mangling macro +// TO : output data type +// TC : coefficients data type +// TI : input data type +#define LIQUID_AUTOCORR_DEFINE_API(AUTOCORR,TO,TC,TI) \ + \ +/* Computes auto-correlation with a fixed lag on input signals */ \ +typedef struct AUTOCORR(_s) * AUTOCORR(); \ + \ +/* Create auto-correlator object with a particular window length and */ \ +/* delay */ \ +/* _window_size : size of the correlator window */ \ +/* _delay : correlator delay [samples] */ \ +AUTOCORR() AUTOCORR(_create)(unsigned int _window_size, \ + unsigned int _delay); \ + \ +/* Destroy auto-correlator object, freeing internal memory */ \ +void AUTOCORR(_destroy)(AUTOCORR() _q); \ + \ +/* Reset auto-correlator object's internals */ \ +void AUTOCORR(_reset)(AUTOCORR() _q); \ + \ +/* Print auto-correlator parameters to stdout */ \ +void AUTOCORR(_print)(AUTOCORR() _q); \ + \ +/* Push sample into auto-correlator object */ \ +/* _q : auto-correlator object */ \ +/* _x : single input sample */ \ +void AUTOCORR(_push)(AUTOCORR() _q, \ + TI _x); \ + \ +/* Write block of samples to auto-correlator object */ \ +/* _q : auto-correlation object */ \ +/* _x : input array [size: _n x 1] */ \ +/* _n : number of input samples */ \ +void AUTOCORR(_write)(AUTOCORR() _q, \ + TI * _x, \ + unsigned int _n); \ + \ +/* Compute single auto-correlation output */ \ +/* _q : auto-correlator object */ \ +/* _rxx : auto-correlated output */ \ +void AUTOCORR(_execute)(AUTOCORR() _q, \ + TO * _rxx); \ + \ +/* Compute auto-correlation on block of samples; the input and output */ \ +/* arrays may have the same pointer */ \ +/* _q : auto-correlation object */ \ +/* _x : input array [size: _n x 1] */ \ +/* _n : number of input, output samples */ \ +/* _rxx : input array [size: _n x 1] */ \ +void AUTOCORR(_execute_block)(AUTOCORR() _q, \ + TI * _x, \ + unsigned int _n, \ + TO * _rxx); \ + \ +/* return sum of squares of buffered samples */ \ +float AUTOCORR(_get_energy)(AUTOCORR() _q); \ + +LIQUID_AUTOCORR_DEFINE_API(LIQUID_AUTOCORR_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + +LIQUID_AUTOCORR_DEFINE_API(LIQUID_AUTOCORR_MANGLE_RRRF, + float, + float, + float) + + +// +// Finite impulse response filter +// + +#define LIQUID_FIRFILT_MANGLE_RRRF(name) LIQUID_CONCAT(firfilt_rrrf,name) +#define LIQUID_FIRFILT_MANGLE_CRCF(name) LIQUID_CONCAT(firfilt_crcf,name) +#define LIQUID_FIRFILT_MANGLE_CCCF(name) LIQUID_CONCAT(firfilt_cccf,name) + +// Macro: +// FIRFILT : name-mangling macro +// TO : output data type +// TC : coefficients data type +// TI : input data type +#define LIQUID_FIRFILT_DEFINE_API(FIRFILT,TO,TC,TI) \ + \ +/* Finite impulse response (FIR) filter */ \ +typedef struct FIRFILT(_s) * FIRFILT(); \ + \ +/* Create a finite impulse response filter (firfilt) object by directly */ \ +/* specifying the filter coefficients in an array */ \ +/* _h : filter coefficients [size: _n x 1] */ \ +/* _n : number of filter coefficients, _n > 0 */ \ +FIRFILT() FIRFILT(_create)(TC * _h, \ + unsigned int _n); \ + \ +/* Create object using Kaiser-Bessel windowed sinc method */ \ +/* _n : filter length, _n > 0 */ \ +/* _fc : filter normalized cut-off frequency, 0 < _fc < 0.5 */ \ +/* _As : filter stop-band attenuation [dB], _As > 0 */ \ +/* _mu : fractional sample offset, -0.5 < _mu < 0.5 */ \ +FIRFILT() FIRFILT(_create_kaiser)(unsigned int _n, \ + float _fc, \ + float _As, \ + float _mu); \ + \ +/* Create object from square-root Nyquist prototype. */ \ +/* The filter length will be \(2 k m + 1 \) samples long with a delay */ \ +/* of \( k m + 1 \) samples. */ \ +/* _type : filter type (e.g. LIQUID_FIRFILT_RRC) */ \ +/* _k : nominal samples per symbol, _k > 1 */ \ +/* _m : filter delay [symbols], _m > 0 */ \ +/* _beta : rolloff factor, 0 < beta <= 1 */ \ +/* _mu : fractional sample offset [samples], -0.5 < _mu < 0.5 */ \ +FIRFILT() FIRFILT(_create_rnyquist)(int _type, \ + unsigned int _k, \ + unsigned int _m, \ + float _beta, \ + float _mu); \ + \ +/* Create object from Parks-McClellan algorithm prototype */ \ +/* _h_len : filter length, _h_len > 0 */ \ +/* _fc : cutoff frequency, 0 < _fc < 0.5 */ \ +/* _As : stop-band attenuation [dB], _As > 0 */ \ +FIRFILT() FIRFILT(_create_firdespm)(unsigned int _h_len, \ + float _fc, \ + float _As); \ + \ +/* Create rectangular filter prototype; that is */ \ +/* \( \vec{h} = \{ 1, 1, 1, \ldots 1 \} \) */ \ +/* _n : length of filter [samples], 0 < _n <= 1024 */ \ +FIRFILT() FIRFILT(_create_rect)(unsigned int _n); \ + \ +/* Create DC blocking filter from prototype */ \ +/* _m : prototype filter semi-length such that filter length is 2*m+1 */ \ +/* _As : prototype filter stop-band attenuation [dB], _As > 0 */ \ +FIRFILT() FIRFILT(_create_dc_blocker)(unsigned int _m, \ + float _As); \ + \ +/* Create notch filter from prototype */ \ +/* _m : prototype filter semi-length such that filter length is 2*m+1 */ \ +/* _As : prototype filter stop-band attenuation [dB], _As > 0 */ \ +/* _f0 : center frequency for notch, _fc in [-0.5, 0.5] */ \ +FIRFILT() FIRFILT(_create_notch)(unsigned int _m, \ + float _As, \ + float _f0); \ + \ +/* Re-create filter object of potentially a different length with */ \ +/* different coefficients. If the length of the filter does not change, */ \ +/* not memory reallocation is invoked. */ \ +/* _q : original filter object */ \ +/* _h : pointer to filter coefficients, [size: _n x 1] */ \ +/* _n : filter length, _n > 0 */ \ +FIRFILT() FIRFILT(_recreate)(FIRFILT() _q, \ + TC * _h, \ + unsigned int _n); \ + \ +/* Destroy filter object and free all internal memory */ \ +void FIRFILT(_destroy)(FIRFILT() _q); \ + \ +/* Reset filter object's internal buffer */ \ +void FIRFILT(_reset)(FIRFILT() _q); \ + \ +/* Print filter object information to stdout */ \ +void FIRFILT(_print)(FIRFILT() _q); \ + \ +/* Set output scaling for filter */ \ +/* _q : filter object */ \ +/* _scale : scaling factor to apply to each output sample */ \ +void FIRFILT(_set_scale)(FIRFILT() _q, \ + TC _scale); \ + \ +/* Get output scaling for filter */ \ +/* _q : filter object */ \ +/* _scale : scaling factor applied to each output sample */ \ +void FIRFILT(_get_scale)(FIRFILT() _q, \ + TC * _scale); \ + \ +/* Push sample into filter object's internal buffer */ \ +/* _q : filter object */ \ +/* _x : single input sample */ \ +void FIRFILT(_push)(FIRFILT() _q, \ + TI _x); \ + \ +/* Write block of samples into filter object's internal buffer */ \ +/* _q : filter object */ \ +/* _x : buffer of input samples, [size: _n x 1] */ \ +/* _n : number of input samples */ \ +void FIRFILT(_write)(FIRFILT() _q, \ + TI * _x, \ + unsigned int _n); \ + \ +/* Execute vector dot product on the filter's internal buffer and */ \ +/* coefficients */ \ +/* _q : filter object */ \ +/* _y : pointer to single output sample */ \ +void FIRFILT(_execute)(FIRFILT() _q, \ + TO * _y); \ + \ +/* Execute the filter on a block of input samples; in-place operation */ \ +/* is permitted (_x and _y may point to the same place in memory) */ \ +/* _q : filter object */ \ +/* _x : pointer to input array, [size: _n x 1] */ \ +/* _n : number of input, output samples */ \ +/* _y : pointer to output array, [size: _n x 1] */ \ +void FIRFILT(_execute_block)(FIRFILT() _q, \ + TI * _x, \ + unsigned int _n, \ + TO * _y); \ + \ +/* Get length of filter object (number of internal coefficients) */ \ +unsigned int FIRFILT(_get_length)(FIRFILT() _q); \ + \ +/* Compute complex frequency response of filter object */ \ +/* _q : filter object */ \ +/* _fc : normalized frequency for evaluation */ \ +/* _H : pointer to output complex frequency response */ \ +void FIRFILT(_freqresponse)(FIRFILT() _q, \ + float _fc, \ + liquid_float_complex * _H); \ + \ +/* Compute and return group delay of filter object */ \ +/* _q : filter object */ \ +/* _fc : frequency to evaluate */ \ +float FIRFILT(_groupdelay)(FIRFILT() _q, \ + float _fc); \ + +LIQUID_FIRFILT_DEFINE_API(LIQUID_FIRFILT_MANGLE_RRRF, + float, + float, + float) + +LIQUID_FIRFILT_DEFINE_API(LIQUID_FIRFILT_MANGLE_CRCF, + liquid_float_complex, + float, + liquid_float_complex) + +LIQUID_FIRFILT_DEFINE_API(LIQUID_FIRFILT_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + +// +// FIR Hilbert transform +// 2:1 real-to-complex decimator +// 1:2 complex-to-real interpolator +// + +#define LIQUID_FIRHILB_MANGLE_FLOAT(name) LIQUID_CONCAT(firhilbf, name) +//#define LIQUID_FIRHILB_MANGLE_DOUBLE(name) LIQUID_CONCAT(firhilb, name) + +// NOTES: +// Although firhilb is a placeholder for both decimation and +// interpolation, separate objects should be used for each task. +#define LIQUID_FIRHILB_DEFINE_API(FIRHILB,T,TC) \ + \ +/* Finite impulse response (FIR) Hilbert transform */ \ +typedef struct FIRHILB(_s) * FIRHILB(); \ + \ +/* Create a firhilb object with a particular filter semi-length and */ \ +/* desired stop-band attenuation. */ \ +/* Internally the object designs a half-band filter based on applying */ \ +/* a Kaiser-Bessel window to a sinc function to guarantee zeros at all */ \ +/* off-center odd indexed samples. */ \ +/* _m : filter semi-length, delay is \( 2 m + 1 \) */ \ +/* _As : filter stop-band attenuation [dB] */ \ +FIRHILB() FIRHILB(_create)(unsigned int _m, \ + float _As); \ + \ +/* Destroy finite impulse response Hilbert transform, freeing all */ \ +/* internally-allocted memory and objects. */ \ +void FIRHILB(_destroy)(FIRHILB() _q); \ + \ +/* Print firhilb object internals to stdout */ \ +void FIRHILB(_print)(FIRHILB() _q); \ + \ +/* Reset firhilb object internal state */ \ +void FIRHILB(_reset)(FIRHILB() _q); \ + \ +/* Execute Hilbert transform (real to complex) */ \ +/* _q : Hilbert transform object */ \ +/* _x : real-valued input sample */ \ +/* _y : complex-valued output sample */ \ +void FIRHILB(_r2c_execute)(FIRHILB() _q, \ + T _x, \ + TC * _y); \ + \ +/* Execute Hilbert transform (complex to real) */ \ +/* _q : Hilbert transform object */ \ +/* _x : complex-valued input sample */ \ +/* _y0 : real-valued output sample, lower side-band retained */ \ +/* _y1 : real-valued output sample, upper side-band retained */ \ +void FIRHILB(_c2r_execute)(FIRHILB() _q, \ + TC _x, \ + T * _y0, \ + T * _y1); \ + \ +/* Execute Hilbert transform decimator (real to complex) */ \ +/* _q : Hilbert transform object */ \ +/* _x : real-valued input array, [size: 2 x 1] */ \ +/* _y : complex-valued output sample */ \ +void FIRHILB(_decim_execute)(FIRHILB() _q, \ + T * _x, \ + TC * _y); \ + \ +/* Execute Hilbert transform decimator (real to complex) on a block of */ \ +/* samples */ \ +/* _q : Hilbert transform object */ \ +/* _x : real-valued input array, [size: 2*_n x 1] */ \ +/* _n : number of output samples */ \ +/* _y : complex-valued output array, [size: _n x 1] */ \ +void FIRHILB(_decim_execute_block)(FIRHILB() _q, \ + T * _x, \ + unsigned int _n, \ + TC * _y); \ + \ +/* Execute Hilbert transform interpolator (real to complex) */ \ +/* _q : Hilbert transform object */ \ +/* _x : complex-valued input sample */ \ +/* _y : real-valued output array, [size: 2 x 1] */ \ +void FIRHILB(_interp_execute)(FIRHILB() _q, \ + TC _x, \ + T * _y); \ + \ +/* Execute Hilbert transform interpolator (complex to real) on a block */ \ +/* of samples */ \ +/* _q : Hilbert transform object */ \ +/* _x : complex-valued input array, [size: _n x 1] */ \ +/* _n : number of *input* samples */ \ +/* _y : real-valued output array, [size: 2*_n x 1] */ \ +void FIRHILB(_interp_execute_block)(FIRHILB() _q, \ + TC * _x, \ + unsigned int _n, \ + T * _y); \ + +LIQUID_FIRHILB_DEFINE_API(LIQUID_FIRHILB_MANGLE_FLOAT, float, liquid_float_complex) +//LIQUID_FIRHILB_DEFINE_API(LIQUID_FIRHILB_MANGLE_DOUBLE, double, liquid_double_complex) + + +// +// Infinite impulse response (IIR) Hilbert transform +// 2:1 real-to-complex decimator +// 1:2 complex-to-real interpolator +// + +#define LIQUID_IIRHILB_MANGLE_FLOAT(name) LIQUID_CONCAT(iirhilbf, name) +//#define LIQUID_IIRHILB_MANGLE_DOUBLE(name) LIQUID_CONCAT(iirhilb, name) + +// NOTES: +// Although iirhilb is a placeholder for both decimation and +// interpolation, separate objects should be used for each task. +#define LIQUID_IIRHILB_DEFINE_API(IIRHILB,T,TC) \ + \ +/* Infinite impulse response (IIR) Hilbert transform */ \ +typedef struct IIRHILB(_s) * IIRHILB(); \ + \ +/* Create a iirhilb object with a particular filter type, order, and */ \ +/* desired pass- and stop-band attenuation. */ \ +/* _ftype : filter type (e.g. LIQUID_IIRDES_BUTTER) */ \ +/* _n : filter order, _n > 0 */ \ +/* _Ap : pass-band ripple [dB], _Ap > 0 */ \ +/* _As : stop-band ripple [dB], _Ap > 0 */ \ +IIRHILB() IIRHILB(_create)(liquid_iirdes_filtertype _ftype, \ + unsigned int _n, \ + float _Ap, \ + float _As); \ + \ +/* Create a default iirhilb object with a particular filter order. */ \ +/* _n : filter order, _n > 0 */ \ +IIRHILB() IIRHILB(_create_default)(unsigned int _n); \ + \ +/* Destroy finite impulse response Hilbert transform, freeing all */ \ +/* internally-allocted memory and objects. */ \ +void IIRHILB(_destroy)(IIRHILB() _q); \ + \ +/* Print iirhilb object internals to stdout */ \ +void IIRHILB(_print)(IIRHILB() _q); \ + \ +/* Reset iirhilb object internal state */ \ +void IIRHILB(_reset)(IIRHILB() _q); \ + \ +/* Execute Hilbert transform (real to complex) */ \ +/* _q : Hilbert transform object */ \ +/* _x : real-valued input sample */ \ +/* _y : complex-valued output sample */ \ +void IIRHILB(_r2c_execute)(IIRHILB() _q, \ + T _x, \ + TC * _y); \ + \ +/* Execute Hilbert transform (complex to real) */ \ +/* _q : Hilbert transform object */ \ +/* _x : complex-valued input sample */ \ +/* _y : real-valued output sample */ \ +void IIRHILB(_c2r_execute)(IIRHILB() _q, \ + TC _x, \ + T * _y); \ + \ +/* Execute Hilbert transform decimator (real to complex) */ \ +/* _q : Hilbert transform object */ \ +/* _x : real-valued input array, [size: 2 x 1] */ \ +/* _y : complex-valued output sample */ \ +void IIRHILB(_decim_execute)(IIRHILB() _q, \ + T * _x, \ + TC * _y); \ + \ +/* Execute Hilbert transform decimator (real to complex) on a block of */ \ +/* samples */ \ +/* _q : Hilbert transform object */ \ +/* _x : real-valued input array, [size: 2*_n x 1] */ \ +/* _n : number of output samples */ \ +/* _y : complex-valued output array, [size: _n x 1] */ \ +void IIRHILB(_decim_execute_block)(IIRHILB() _q, \ + T * _x, \ + unsigned int _n, \ + TC * _y); \ + \ +/* Execute Hilbert transform interpolator (real to complex) */ \ +/* _q : Hilbert transform object */ \ +/* _x : complex-valued input sample */ \ +/* _y : real-valued output array, [size: 2 x 1] */ \ +void IIRHILB(_interp_execute)(IIRHILB() _q, \ + TC _x, \ + T * _y); \ + \ +/* Execute Hilbert transform interpolator (complex to real) on a block */ \ +/* of samples */ \ +/* _q : Hilbert transform object */ \ +/* _x : complex-valued input array, [size: _n x 1] */ \ +/* _n : number of *input* samples */ \ +/* _y : real-valued output array, [size: 2*_n x 1] */ \ +void IIRHILB(_interp_execute_block)(IIRHILB() _q, \ + TC * _x, \ + unsigned int _n, \ + T * _y); \ + +LIQUID_IIRHILB_DEFINE_API(LIQUID_IIRHILB_MANGLE_FLOAT, float, liquid_float_complex) +//LIQUID_IIRHILB_DEFINE_API(LIQUID_IIRHILB_MANGLE_DOUBLE, double, liquid_double_complex) + + +// +// FFT-based finite impulse response filter +// + +#define LIQUID_FFTFILT_MANGLE_RRRF(name) LIQUID_CONCAT(fftfilt_rrrf,name) +#define LIQUID_FFTFILT_MANGLE_CRCF(name) LIQUID_CONCAT(fftfilt_crcf,name) +#define LIQUID_FFTFILT_MANGLE_CCCF(name) LIQUID_CONCAT(fftfilt_cccf,name) + +// Macro: +// FFTFILT : name-mangling macro +// TO : output data type +// TC : coefficients data type +// TI : input data type +#define LIQUID_FFTFILT_DEFINE_API(FFTFILT,TO,TC,TI) \ + \ +/* Fast Fourier transform (FFT) finite impulse response filter */ \ +typedef struct FFTFILT(_s) * FFTFILT(); \ + \ +/* Create FFT-based FIR filter using external coefficients */ \ +/* _h : filter coefficients, [size: _h_len x 1] */ \ +/* _h_len : filter length, _h_len > 0 */ \ +/* _n : block size = nfft/2, _n >= _h_len-1 */ \ +FFTFILT() FFTFILT(_create)(TC * _h, \ + unsigned int _h_len, \ + unsigned int _n); \ + \ +/* Destroy filter object and free all internal memory */ \ +void FFTFILT(_destroy)(FFTFILT() _q); \ + \ +/* Reset filter object's internal buffer */ \ +void FFTFILT(_reset)(FFTFILT() _q); \ + \ +/* Print filter object information to stdout */ \ +void FFTFILT(_print)(FFTFILT() _q); \ + \ +/* Set output scaling for filter */ \ +void FFTFILT(_set_scale)(FFTFILT() _q, \ + TC _scale); \ + \ +/* Get output scaling for filter */ \ +void FFTFILT(_get_scale)(FFTFILT() _q, \ + TC * _scale); \ + \ +/* Execute the filter on internal buffer and coefficients given a block */ \ +/* of input samples; in-place operation is permitted (_x and _y may */ \ +/* point to the same place in memory) */ \ +/* _q : filter object */ \ +/* _x : pointer to input data array, [size: _n x 1] */ \ +/* _y : pointer to output data array, [size: _n x 1] */ \ +void FFTFILT(_execute)(FFTFILT() _q, \ + TI * _x, \ + TO * _y); \ + \ +/* Get length of filter object's internal coefficients */ \ +unsigned int FFTFILT(_get_length)(FFTFILT() _q); \ + +LIQUID_FFTFILT_DEFINE_API(LIQUID_FFTFILT_MANGLE_RRRF, + float, + float, + float) + +LIQUID_FFTFILT_DEFINE_API(LIQUID_FFTFILT_MANGLE_CRCF, + liquid_float_complex, + float, + liquid_float_complex) + +LIQUID_FFTFILT_DEFINE_API(LIQUID_FFTFILT_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + + +// +// Infinite impulse response filter +// + +#define LIQUID_IIRFILT_MANGLE_RRRF(name) LIQUID_CONCAT(iirfilt_rrrf,name) +#define LIQUID_IIRFILT_MANGLE_CRCF(name) LIQUID_CONCAT(iirfilt_crcf,name) +#define LIQUID_IIRFILT_MANGLE_CCCF(name) LIQUID_CONCAT(iirfilt_cccf,name) + +// Macro: +// IIRFILT : name-mangling macro +// TO : output data type +// TC : coefficients data type +// TI : input data type +#define LIQUID_IIRFILT_DEFINE_API(IIRFILT,TO,TC,TI) \ + \ +/* Infinite impulse response (IIR) filter */ \ +typedef struct IIRFILT(_s) * IIRFILT(); \ + \ +/* Create infinite impulse response filter from external coefficients. */ \ +/* Note that the number of feed-forward and feed-back coefficients do */ \ +/* not need to be equal, but they do need to be non-zero. */ \ +/* Furthermore, the first feed-back coefficient \(a_0\) cannot be */ \ +/* equal to zero, otherwise the filter will be invalid as this value is */ \ +/* factored out from all coefficients. */ \ +/* For stability reasons the number of coefficients should reasonably */ \ +/* not exceed about 8 for single-precision floating-point. */ \ +/* _b : feed-forward coefficients (numerator), [size: _nb x 1] */ \ +/* _nb : number of feed-forward coefficients, _nb > 0 */ \ +/* _a : feed-back coefficients (denominator), [size: _na x 1] */ \ +/* _na : number of feed-back coefficients, _na > 0 */ \ +IIRFILT() IIRFILT(_create)(TC * _b, \ + unsigned int _nb, \ + TC * _a, \ + unsigned int _na); \ + \ +/* Create IIR filter using 2nd-order secitons from external */ \ +/* coefficients. */ \ +/* _B : feed-forward coefficients [size: _nsos x 3] */ \ +/* _A : feed-back coefficients [size: _nsos x 3] */ \ +/* _nsos : number of second-order sections (sos), _nsos > 0 */ \ +IIRFILT() IIRFILT(_create_sos)(TC * _B, \ + TC * _A, \ + unsigned int _nsos); \ + \ +/* Create IIR filter from design template */ \ +/* _ftype : filter type (e.g. LIQUID_IIRDES_BUTTER) */ \ +/* _btype : band type (e.g. LIQUID_IIRDES_BANDPASS) */ \ +/* _format : coefficients format (e.g. LIQUID_IIRDES_SOS) */ \ +/* _order : filter order, _order > 0 */ \ +/* _fc : low-pass prototype cut-off frequency, 0 <= _fc <= 0.5 */ \ +/* _f0 : center frequency (band-pass, band-stop), 0 <= _f0 <= 0.5 */ \ +/* _Ap : pass-band ripple in dB, _Ap > 0 */ \ +/* _As : stop-band ripple in dB, _As > 0 */ \ +IIRFILT() IIRFILT(_create_prototype)( \ + liquid_iirdes_filtertype _ftype, \ + liquid_iirdes_bandtype _btype, \ + liquid_iirdes_format _format, \ + unsigned int _order, \ + float _fc, \ + float _f0, \ + float _Ap, \ + float _As); \ + \ +/* Create simplified low-pass Butterworth IIR filter */ \ +/* _order : filter order, _order > 0 */ \ +/* _fc : low-pass prototype cut-off frequency */ \ +IIRFILT() IIRFILT(_create_lowpass)(unsigned int _order, \ + float _fc); \ + \ +/* Create 8th-order integrator filter */ \ +IIRFILT() IIRFILT(_create_integrator)(void); \ + \ +/* Create 8th-order differentiator filter */ \ +IIRFILT() IIRFILT(_create_differentiator)(void); \ + \ +/* Create simple first-order DC-blocking filter with transfer function */ \ +/* \( H(z) = \frac{1 - z^{-1}}{1 - (1-\alpha)z^{-1}} \) */ \ +/* _alpha : normalized filter bandwidth, _alpha > 0 */ \ +IIRFILT() IIRFILT(_create_dc_blocker)(float _alpha); \ + \ +/* Create filter to operate as second-order integrating phase-locked */ \ +/* loop (active lag design) */ \ +/* _w : filter bandwidth, 0 < _w < 1 */ \ +/* _zeta : damping factor, \( 1/\sqrt{2} \) suggested, 0 < _zeta < 1 */ \ +/* _K : loop gain, 1000 suggested, _K > 0 */ \ +IIRFILT() IIRFILT(_create_pll)(float _w, \ + float _zeta, \ + float _K); \ + \ +/* Destroy iirfilt object, freeing all internal memory */ \ +void IIRFILT(_destroy)(IIRFILT() _q); \ + \ +/* Print iirfilt object properties to stdout */ \ +void IIRFILT(_print)(IIRFILT() _q); \ + \ +/* Reset iirfilt object internals */ \ +void IIRFILT(_reset)(IIRFILT() _q); \ + \ +/* Compute filter output given a signle input sample */ \ +/* _q : iirfilt object */ \ +/* _x : input sample */ \ +/* _y : output sample pointer */ \ +void IIRFILT(_execute)(IIRFILT() _q, \ + TI _x, \ + TO * _y); \ + \ +/* Execute the filter on a block of input samples; */ \ +/* in-place operation is permitted (the input and output buffers may be */ \ +/* the same) */ \ +/* _q : filter object */ \ +/* _x : pointer to input array, [size: _n x 1] */ \ +/* _n : number of input, output samples, _n > 0 */ \ +/* _y : pointer to output array, [size: _n x 1] */ \ +void IIRFILT(_execute_block)(IIRFILT() _q, \ + TI * _x, \ + unsigned int _n, \ + TO * _y); \ + \ +/* Return number of coefficients for iirfilt object (maximum between */ \ +/* the feed-forward and feed-back coefficients). Note that the filter */ \ +/* length = filter order + 1 */ \ +unsigned int IIRFILT(_get_length)(IIRFILT() _q); \ + \ +/* Compute complex frequency response of filter object */ \ +/* _q : filter object */ \ +/* _fc : normalized frequency for evaluation */ \ +/* _H : pointer to output complex frequency response */ \ +void IIRFILT(_freqresponse)(IIRFILT() _q, \ + float _fc, \ + liquid_float_complex * _H); \ + \ +/* Compute and return group delay of filter object */ \ +/* _q : filter object */ \ +/* _fc : frequency to evaluate */ \ +float IIRFILT(_groupdelay)(IIRFILT() _q, float _fc); \ + +LIQUID_IIRFILT_DEFINE_API(LIQUID_IIRFILT_MANGLE_RRRF, + float, + float, + float) + +LIQUID_IIRFILT_DEFINE_API(LIQUID_IIRFILT_MANGLE_CRCF, + liquid_float_complex, + float, + liquid_float_complex) + +LIQUID_IIRFILT_DEFINE_API(LIQUID_IIRFILT_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + + +// +// FIR Polyphase filter bank +// +#define LIQUID_FIRPFB_MANGLE_RRRF(name) LIQUID_CONCAT(firpfb_rrrf,name) +#define LIQUID_FIRPFB_MANGLE_CRCF(name) LIQUID_CONCAT(firpfb_crcf,name) +#define LIQUID_FIRPFB_MANGLE_CCCF(name) LIQUID_CONCAT(firpfb_cccf,name) + +// Macro: +// FIRPFB : name-mangling macro +// TO : output data type +// TC : coefficients data type +// TI : input data type +#define LIQUID_FIRPFB_DEFINE_API(FIRPFB,TO,TC,TI) \ + \ +/* Finite impulse response (FIR) polyphase filter bank (PFB) */ \ +typedef struct FIRPFB(_s) * FIRPFB(); \ + \ +/* Create firpfb object with _M sub-filter each of length _h_len/_M */ \ +/* from an external array of coefficients */ \ +/* _M : number of filters in the bank, _M > 1 */ \ +/* _h : coefficients, [size: _h_len x 1] */ \ +/* _h_len : filter length (multiple of _M), _h_len >= _M */ \ +FIRPFB() FIRPFB(_create)(unsigned int _M, \ + TC * _h, \ + unsigned int _h_len); \ + \ +/* Create firpfb object using Kaiser-Bessel windowed sinc filter design */ \ +/* method, using default values for cut-off frequency and stop-band */ \ +/* attenuation. This is equivalent to: */ \ +/* FIRPFB(_create_kaiser)(_M, _m, 0.5, 60.0) */ \ +/* which creates a Nyquist filter at the appropriate cut-off frequency. */ \ +/* _M : number of filters in the bank, _M > 0 */ \ +/* _m : filter semi-length [samples], _m > 0 */ \ +FIRPFB() FIRPFB(_create_default)(unsigned int _M, \ + unsigned int _m); \ + \ +/* Create firpfb object using Kaiser-Bessel windowed sinc filter design */ \ +/* method */ \ +/* _M : number of filters in the bank, _M > 0 */ \ +/* _m : filter semi-length [samples], _m > 0 */ \ +/* _fc : filter normalized cut-off frequency, 0 < _fc < 0.5 */ \ +/* _As : filter stop-band suppression [dB], _As > 0 */ \ +FIRPFB() FIRPFB(_create_kaiser)(unsigned int _M, \ + unsigned int _m, \ + float _fc, \ + float _As); \ + \ +/* Create firpfb from square-root Nyquist prototype */ \ +/* _type : filter type (e.g. LIQUID_FIRFILT_RRC) */ \ +/* _M : number of filters in the bank, _M > 0 */ \ +/* _k : nominal samples/symbol, _k > 1 */ \ +/* _m : filter delay [symbols], _m > 0 */ \ +/* _beta : rolloff factor, 0 < _beta <= 1 */ \ +FIRPFB() FIRPFB(_create_rnyquist)(int _type, \ + unsigned int _M, \ + unsigned int _k, \ + unsigned int _m, \ + float _beta); \ + \ +/* Create from square-root derivative Nyquist prototype */ \ +/* _type : filter type (e.g. LIQUID_FIRFILT_RRC) */ \ +/* _M : number of filters in the bank, _M > 0 */ \ +/* _k : nominal samples/symbol, _k > 1 */ \ +/* _m : filter delay [symbols], _m > 0 */ \ +/* _beta : rolloff factor, 0 < _beta <= 1 */ \ +FIRPFB() FIRPFB(_create_drnyquist)(int _type, \ + unsigned int _M, \ + unsigned int _k, \ + unsigned int _m, \ + float _beta); \ + \ +/* Re-create firpfb object of potentially a different length with */ \ +/* different coefficients. If the length of the filter does not change, */ \ +/* not memory reallocation is invoked. */ \ +/* _q : original firpfb object */ \ +/* _M : number of filters in the bank, _M > 1 */ \ +/* _h : coefficients, [size: _h_len x 1] */ \ +/* _h_len : filter length (multiple of _M), _h_len >= _M */ \ +FIRPFB() FIRPFB(_recreate)(FIRPFB() _q, \ + unsigned int _M, \ + TC * _h, \ + unsigned int _h_len); \ + \ +/* Destroy firpfb object, freeing all internal memory and destroying */ \ +/* all internal objects */ \ +void FIRPFB(_destroy)(FIRPFB() _q); \ + \ +/* Print firpfb object's parameters to stdout */ \ +void FIRPFB(_print)(FIRPFB() _q); \ + \ +/* Set output scaling for filter */ \ +/* _q : filter object */ \ +/* _scale : scaling factor to apply to each output sample */ \ +void FIRPFB(_set_scale)(FIRPFB() _q, \ + TC _scale); \ + \ +/* Get output scaling for filter */ \ +/* _q : filter object */ \ +/* _scale : scaling factor applied to each output sample */ \ +void FIRPFB(_get_scale)(FIRPFB() _q, \ + TC * _scale); \ + \ +/* Reset firpfb object's internal buffer */ \ +void FIRPFB(_reset)(FIRPFB() _q); \ + \ +/* Push sample into filter object's internal buffer */ \ +/* _q : filter object */ \ +/* _x : single input sample */ \ +void FIRPFB(_push)(FIRPFB() _q, \ + TI _x); \ + \ +/* Execute vector dot product on the filter's internal buffer and */ \ +/* coefficients using the coefficients from sub-filter at index _i */ \ +/* _q : firpfb object */ \ +/* _i : index of filter to use */ \ +/* _y : pointer to output sample */ \ +void FIRPFB(_execute)(FIRPFB() _q, \ + unsigned int _i, \ + TO * _y); \ + \ +/* Execute the filter on a block of input samples, all using index _i. */ \ +/* In-place operation is permitted (_x and _y may point to the same */ \ +/* place in memory) */ \ +/* _q : firpfb object */ \ +/* _i : index of filter to use */ \ +/* _x : pointer to input array [size: _n x 1] */ \ +/* _n : number of input, output samples */ \ +/* _y : pointer to output array [size: _n x 1] */ \ +void FIRPFB(_execute_block)(FIRPFB() _q, \ + unsigned int _i, \ + TI * _x, \ + unsigned int _n, \ + TO * _y); \ + +LIQUID_FIRPFB_DEFINE_API(LIQUID_FIRPFB_MANGLE_RRRF, + float, + float, + float) + +LIQUID_FIRPFB_DEFINE_API(LIQUID_FIRPFB_MANGLE_CRCF, + liquid_float_complex, + float, + liquid_float_complex) + +LIQUID_FIRPFB_DEFINE_API(LIQUID_FIRPFB_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + +// +// Interpolators +// + +// firinterp : finite impulse response interpolator +#define LIQUID_FIRINTERP_MANGLE_RRRF(name) LIQUID_CONCAT(firinterp_rrrf,name) +#define LIQUID_FIRINTERP_MANGLE_CRCF(name) LIQUID_CONCAT(firinterp_crcf,name) +#define LIQUID_FIRINTERP_MANGLE_CCCF(name) LIQUID_CONCAT(firinterp_cccf,name) + +#define LIQUID_FIRINTERP_DEFINE_API(FIRINTERP,TO,TC,TI) \ + \ +/* Finite impulse response (FIR) interpolator */ \ +typedef struct FIRINTERP(_s) * FIRINTERP(); \ + \ +/* Create interpolator from external coefficients. Internally the */ \ +/* interpolator creates a polyphase filter bank to efficiently realize */ \ +/* resampling of the input signal. */ \ +/* If the input filter length is not a multiple of the interpolation */ \ +/* factor, the object internally pads the coefficients with zeros to */ \ +/* compensate. */ \ +/* _M : interpolation factor, _M >= 2 */ \ +/* _h : filter coefficients, [size: _h_len x 1] */ \ +/* _h_len : filter length, _h_len >= _M */ \ +FIRINTERP() FIRINTERP(_create)(unsigned int _M, \ + TC * _h, \ + unsigned int _h_len); \ + \ +/* Create interpolator from filter prototype prototype (Kaiser-Bessel */ \ +/* windowed-sinc function) */ \ +/* _M : interpolation factor, _M >= 2 */ \ +/* _m : filter delay [symbols], _m >= 1 */ \ +/* _As : stop-band attenuation [dB], _As >= 0 */ \ +FIRINTERP() FIRINTERP(_create_kaiser)(unsigned int _M, \ + unsigned int _m, \ + float _As); \ + \ +/* Create interpolator object from filter prototype */ \ +/* _type : filter type (e.g. LIQUID_FIRFILT_RCOS) */ \ +/* _M : interpolation factor, _M > 1 */ \ +/* _m : filter delay (symbols), _m > 0 */ \ +/* _beta : excess bandwidth factor, 0 <= _beta <= 1 */ \ +/* _dt : fractional sample delay, -1 <= _dt <= 1 */ \ +FIRINTERP() FIRINTERP(_create_prototype)(int _type, \ + unsigned int _M, \ + unsigned int _m, \ + float _beta, \ + float _dt); \ + \ +/* Create linear interpolator object */ \ +/* _M : interpolation factor, _M > 1 */ \ +FIRINTERP() FIRINTERP(_create_linear)(unsigned int _M); \ + \ +/* Create window interpolator object */ \ +/* _M : interpolation factor, _M > 1 */ \ +/* _m : filter semi-length, _m > 0 */ \ +FIRINTERP() FIRINTERP(_create_window)(unsigned int _M, \ + unsigned int _m); \ + \ +/* Destroy firinterp object, freeing all internal memory */ \ +void FIRINTERP(_destroy)(FIRINTERP() _q); \ + \ +/* Print firinterp object's internal properties to stdout */ \ +void FIRINTERP(_print)(FIRINTERP() _q); \ + \ +/* Reset internal state */ \ +void FIRINTERP(_reset)(FIRINTERP() _q); \ + \ +/* Get interpolation rate */ \ +unsigned int FIRINTERP(_get_interp_rate)(FIRINTERP() _q); \ + \ +/* Set output scaling for interpolator */ \ +/* _q : interpolator object */ \ +/* _scale : scaling factor to apply to each output sample */ \ +void FIRINTERP(_set_scale)(FIRINTERP() _q, \ + TC _scale); \ + \ +/* Get output scaling for interpolator */ \ +/* _q : interpolator object */ \ +/* _scale : scaling factor to apply to each output sample */ \ +void FIRINTERP(_get_scale)(FIRINTERP() _q, \ + TC * _scale); \ + \ +/* Execute interpolation on single input sample and write \(M\) output */ \ +/* samples (\(M\) is the interpolation factor) */ \ +/* _q : firinterp object */ \ +/* _x : input sample */ \ +/* _y : output sample array, [size: _M x 1] */ \ +void FIRINTERP(_execute)(FIRINTERP() _q, \ + TI _x, \ + TO * _y); \ + \ +/* Execute interpolation on block of input samples */ \ +/* _q : firinterp object */ \ +/* _x : input array, [size: _n x 1] */ \ +/* _n : size of input array */ \ +/* _y : output sample array, [size: _M*_n x 1] */ \ +void FIRINTERP(_execute_block)(FIRINTERP() _q, \ + TI * _x, \ + unsigned int _n, \ + TO * _y); \ + +LIQUID_FIRINTERP_DEFINE_API(LIQUID_FIRINTERP_MANGLE_RRRF, + float, + float, + float) + +LIQUID_FIRINTERP_DEFINE_API(LIQUID_FIRINTERP_MANGLE_CRCF, + liquid_float_complex, + float, + liquid_float_complex) + +LIQUID_FIRINTERP_DEFINE_API(LIQUID_FIRINTERP_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + +// iirinterp : infinite impulse response interpolator +#define LIQUID_IIRINTERP_MANGLE_RRRF(name) LIQUID_CONCAT(iirinterp_rrrf,name) +#define LIQUID_IIRINTERP_MANGLE_CRCF(name) LIQUID_CONCAT(iirinterp_crcf,name) +#define LIQUID_IIRINTERP_MANGLE_CCCF(name) LIQUID_CONCAT(iirinterp_cccf,name) + +#define LIQUID_IIRINTERP_DEFINE_API(IIRINTERP,TO,TC,TI) \ + \ +/* Infinite impulse response (IIR) interpolator */ \ +typedef struct IIRINTERP(_s) * IIRINTERP(); \ + \ +/* Create infinite impulse response interpolator from external */ \ +/* coefficients. */ \ +/* Note that the number of feed-forward and feed-back coefficients do */ \ +/* not need to be equal, but they do need to be non-zero. */ \ +/* Furthermore, the first feed-back coefficient \(a_0\) cannot be */ \ +/* equal to zero, otherwise the filter will be invalid as this value is */ \ +/* factored out from all coefficients. */ \ +/* For stability reasons the number of coefficients should reasonably */ \ +/* not exceed about 8 for single-precision floating-point. */ \ +/* _M : interpolation factor, _M >= 2 */ \ +/* _b : feed-forward coefficients (numerator), [size: _nb x 1] */ \ +/* _nb : number of feed-forward coefficients, _nb > 0 */ \ +/* _a : feed-back coefficients (denominator), [size: _na x 1] */ \ +/* _na : number of feed-back coefficients, _na > 0 */ \ +IIRINTERP() IIRINTERP(_create)(unsigned int _M, \ + TC * _b, \ + unsigned int _nb, \ + TC * _a, \ + unsigned int _na); \ + \ +/* Create interpolator object with default Butterworth prototype */ \ +/* _M : interpolation factor, _M >= 2 */ \ +/* _order : filter order, _order > 0 */ \ +IIRINTERP() IIRINTERP(_create_default)(unsigned int _M, \ + unsigned int _order); \ + \ +/* Create IIR interpolator from prototype */ \ +/* _M : interpolation factor, _M >= 2 */ \ +/* _ftype : filter type (e.g. LIQUID_IIRDES_BUTTER) */ \ +/* _btype : band type (e.g. LIQUID_IIRDES_BANDPASS) */ \ +/* _format : coefficients format (e.g. LIQUID_IIRDES_SOS) */ \ +/* _order : filter order, _order > 0 */ \ +/* _fc : low-pass prototype cut-off frequency, 0 <= _fc <= 0.5 */ \ +/* _f0 : center frequency (band-pass, band-stop), 0 <= _f0 <= 0.5 */ \ +/* _Ap : pass-band ripple in dB, _Ap > 0 */ \ +/* _As : stop-band ripple in dB, _As > 0 */ \ +IIRINTERP() IIRINTERP(_create_prototype)( \ + unsigned int _M, \ + liquid_iirdes_filtertype _ftype, \ + liquid_iirdes_bandtype _btype, \ + liquid_iirdes_format _format, \ + unsigned int _order, \ + float _fc, \ + float _f0, \ + float _Ap, \ + float _As); \ + \ +/* Destroy interpolator object and free internal memory */ \ +void IIRINTERP(_destroy)(IIRINTERP() _q); \ + \ +/* Print interpolator object internals to stdout */ \ +void IIRINTERP(_print)(IIRINTERP() _q); \ + \ +/* Reset interpolator object */ \ +void IIRINTERP(_reset)(IIRINTERP() _q); \ + \ +/* Execute interpolation on single input sample and write \(M\) output */ \ +/* samples (\(M\) is the interpolation factor) */ \ +/* _q : iirinterp object */ \ +/* _x : input sample */ \ +/* _y : output sample array, [size: _M x 1] */ \ +void IIRINTERP(_execute)(IIRINTERP() _q, \ + TI _x, \ + TO * _y); \ + \ +/* Execute interpolation on block of input samples */ \ +/* _q : iirinterp object */ \ +/* _x : input array, [size: _n x 1] */ \ +/* _n : size of input array */ \ +/* _y : output sample array, [size: _M*_n x 1] */ \ +void IIRINTERP(_execute_block)(IIRINTERP() _q, \ + TI * _x, \ + unsigned int _n, \ + TO * _y); \ + \ +/* Compute and return group delay of object */ \ +/* _q : filter object */ \ +/* _fc : frequency to evaluate */ \ +float IIRINTERP(_groupdelay)(IIRINTERP() _q, \ + float _fc); \ + +LIQUID_IIRINTERP_DEFINE_API(LIQUID_IIRINTERP_MANGLE_RRRF, + float, + float, + float) + +LIQUID_IIRINTERP_DEFINE_API(LIQUID_IIRINTERP_MANGLE_CRCF, + liquid_float_complex, + float, + liquid_float_complex) + +LIQUID_IIRINTERP_DEFINE_API(LIQUID_IIRINTERP_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + +// +// Decimators +// + +// firdecim : finite impulse response decimator +#define LIQUID_FIRDECIM_MANGLE_RRRF(name) LIQUID_CONCAT(firdecim_rrrf,name) +#define LIQUID_FIRDECIM_MANGLE_CRCF(name) LIQUID_CONCAT(firdecim_crcf,name) +#define LIQUID_FIRDECIM_MANGLE_CCCF(name) LIQUID_CONCAT(firdecim_cccf,name) + +#define LIQUID_FIRDECIM_DEFINE_API(FIRDECIM,TO,TC,TI) \ + \ +/* Finite impulse response (FIR) decimator */ \ +typedef struct FIRDECIM(_s) * FIRDECIM(); \ + \ +/* Create decimator from external coefficients */ \ +/* _M : decimation factor, _M >= 2 */ \ +/* _h : filter coefficients, [size: _h_len x 1] */ \ +/* _h_len : filter length, _h_len >= _M */ \ +FIRDECIM() FIRDECIM(_create)(unsigned int _M, \ + TC * _h, \ + unsigned int _h_len); \ + \ +/* Create decimator from filter prototype prototype (Kaiser-Bessel */ \ +/* windowed-sinc function) */ \ +/* _M : decimation factor, _M >= 2 */ \ +/* _m : filter delay [symbols], _m >= 1 */ \ +/* _As : stop-band attenuation [dB], _As >= 0 */ \ +FIRDECIM() FIRDECIM(_create_kaiser)(unsigned int _M, \ + unsigned int _m, \ + float _As); \ + \ +/* Create decimator object from filter prototype */ \ +/* _type : filter type (e.g. LIQUID_FIRFILT_RCOS) */ \ +/* _M : interpolation factor, _M > 1 */ \ +/* _m : filter delay (symbols), _m > 0 */ \ +/* _beta : excess bandwidth factor, 0 <= _beta <= 1 */ \ +/* _dt : fractional sample delay, -1 <= _dt <= 1 */ \ +FIRDECIM() FIRDECIM(_create_prototype)(int _type, \ + unsigned int _M, \ + unsigned int _m, \ + float _beta, \ + float _dt); \ + \ +/* Destroy decimator object, freeing all internal memory */ \ +void FIRDECIM(_destroy)(FIRDECIM() _q); \ + \ +/* Print decimator object propreties to stdout */ \ +void FIRDECIM(_print)(FIRDECIM() _q); \ + \ +/* Reset decimator object internal state */ \ +void FIRDECIM(_reset)(FIRDECIM() _q); \ + \ +/* Get decimation rate */ \ +unsigned int FIRDECIM(_get_decim_rate)(FIRDECIM() _q); \ + \ +/* Set output scaling for decimator */ \ +/* _q : decimator object */ \ +/* _scale : scaling factor to apply to each output sample */ \ +void FIRDECIM(_set_scale)(FIRDECIM() _q, \ + TC _scale); \ + \ +/* Get output scaling for decimator */ \ +/* _q : decimator object */ \ +/* _scale : scaling factor to apply to each output sample */ \ +void FIRDECIM(_get_scale)(FIRDECIM() _q, \ + TC * _scale); \ + \ +/* Execute decimator on _M input samples */ \ +/* _q : decimator object */ \ +/* _x : input samples, [size: _M x 1] */ \ +/* _y : output sample pointer */ \ +void FIRDECIM(_execute)(FIRDECIM() _q, \ + TI * _x, \ + TO * _y); \ + \ +/* Execute decimator on block of _n*_M input samples */ \ +/* _q : decimator object */ \ +/* _x : input array, [size: _n*_M x 1] */ \ +/* _n : number of _output_ samples */ \ +/* _y : output array, [_size: _n x 1] */ \ +void FIRDECIM(_execute_block)(FIRDECIM() _q, \ + TI * _x, \ + unsigned int _n, \ + TO * _y); \ + +LIQUID_FIRDECIM_DEFINE_API(LIQUID_FIRDECIM_MANGLE_RRRF, + float, + float, + float) + +LIQUID_FIRDECIM_DEFINE_API(LIQUID_FIRDECIM_MANGLE_CRCF, + liquid_float_complex, + float, + liquid_float_complex) + +LIQUID_FIRDECIM_DEFINE_API(LIQUID_FIRDECIM_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + + +// iirdecim : infinite impulse response decimator +#define LIQUID_IIRDECIM_MANGLE_RRRF(name) LIQUID_CONCAT(iirdecim_rrrf,name) +#define LIQUID_IIRDECIM_MANGLE_CRCF(name) LIQUID_CONCAT(iirdecim_crcf,name) +#define LIQUID_IIRDECIM_MANGLE_CCCF(name) LIQUID_CONCAT(iirdecim_cccf,name) + +#define LIQUID_IIRDECIM_DEFINE_API(IIRDECIM,TO,TC,TI) \ + \ +/* Infinite impulse response (IIR) decimator */ \ +typedef struct IIRDECIM(_s) * IIRDECIM(); \ + \ +/* Create infinite impulse response decimator from external */ \ +/* coefficients. */ \ +/* Note that the number of feed-forward and feed-back coefficients do */ \ +/* not need to be equal, but they do need to be non-zero. */ \ +/* Furthermore, the first feed-back coefficient \(a_0\) cannot be */ \ +/* equal to zero, otherwise the filter will be invalid as this value is */ \ +/* factored out from all coefficients. */ \ +/* For stability reasons the number of coefficients should reasonably */ \ +/* not exceed about 8 for single-precision floating-point. */ \ +/* _M : decimation factor, _M >= 2 */ \ +/* _b : feed-forward coefficients (numerator), [size: _nb x 1] */ \ +/* _nb : number of feed-forward coefficients, _nb > 0 */ \ +/* _a : feed-back coefficients (denominator), [size: _na x 1] */ \ +/* _na : number of feed-back coefficients, _na > 0 */ \ +IIRDECIM() IIRDECIM(_create)(unsigned int _M, \ + TC * _b, \ + unsigned int _nb, \ + TC * _a, \ + unsigned int _na); \ + \ +/* Create decimator object with default Butterworth prototype */ \ +/* _M : decimation factor, _M >= 2 */ \ +/* _order : filter order, _order > 0 */ \ +IIRDECIM() IIRDECIM(_create_default)(unsigned int _M, \ + unsigned int _order); \ + \ +/* Create IIR decimator from prototype */ \ +/* _M : decimation factor, _M >= 2 */ \ +/* _ftype : filter type (e.g. LIQUID_IIRDES_BUTTER) */ \ +/* _btype : band type (e.g. LIQUID_IIRDES_BANDPASS) */ \ +/* _format : coefficients format (e.g. LIQUID_IIRDES_SOS) */ \ +/* _order : filter order, _order > 0 */ \ +/* _fc : low-pass prototype cut-off frequency, 0 <= _fc <= 0.5 */ \ +/* _f0 : center frequency (band-pass, band-stop), 0 <= _f0 <= 0.5 */ \ +/* _Ap : pass-band ripple in dB, _Ap > 0 */ \ +/* _As : stop-band ripple in dB, _As > 0 */ \ +IIRDECIM() IIRDECIM(_create_prototype)( \ + unsigned int _M, \ + liquid_iirdes_filtertype _ftype, \ + liquid_iirdes_bandtype _btype, \ + liquid_iirdes_format _format, \ + unsigned int _order, \ + float _fc, \ + float _f0, \ + float _Ap, \ + float _As); \ + \ +/* Destroy decimator object and free internal memory */ \ +void IIRDECIM(_destroy)(IIRDECIM() _q); \ + \ +/* Print decimator object internals */ \ +void IIRDECIM(_print)(IIRDECIM() _q); \ + \ +/* Reset decimator object */ \ +void IIRDECIM(_reset)(IIRDECIM() _q); \ + \ +/* Execute decimator on _M input samples */ \ +/* _q : decimator object */ \ +/* _x : input samples, [size: _M x 1] */ \ +/* _y : output sample pointer */ \ +void IIRDECIM(_execute)(IIRDECIM() _q, \ + TI * _x, \ + TO * _y); \ + \ +/* Execute decimator on block of _n*_M input samples */ \ +/* _q : decimator object */ \ +/* _x : input array, [size: _n*_M x 1] */ \ +/* _n : number of _output_ samples */ \ +/* _y : output array, [_sze: _n x 1] */ \ +void IIRDECIM(_execute_block)(IIRDECIM() _q, \ + TI * _x, \ + unsigned int _n, \ + TO * _y); \ + \ +/* Compute and return group delay of object */ \ +/* _q : filter object */ \ +/* _fc : frequency to evaluate */ \ +float IIRDECIM(_groupdelay)(IIRDECIM() _q, \ + float _fc); \ + +LIQUID_IIRDECIM_DEFINE_API(LIQUID_IIRDECIM_MANGLE_RRRF, + float, + float, + float) + +LIQUID_IIRDECIM_DEFINE_API(LIQUID_IIRDECIM_MANGLE_CRCF, + liquid_float_complex, + float, + liquid_float_complex) + +LIQUID_IIRDECIM_DEFINE_API(LIQUID_IIRDECIM_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + + + +// +// Half-band resampler +// +#define LIQUID_RESAMP2_MANGLE_RRRF(name) LIQUID_CONCAT(resamp2_rrrf,name) +#define LIQUID_RESAMP2_MANGLE_CRCF(name) LIQUID_CONCAT(resamp2_crcf,name) +#define LIQUID_RESAMP2_MANGLE_CCCF(name) LIQUID_CONCAT(resamp2_cccf,name) + +#define LIQUID_RESAMP2_DEFINE_API(RESAMP2,TO,TC,TI) \ + \ +/* Half-band resampler, implemented as a dyadic (half-band) polyphase */ \ +/* filter bank for interpolation, decimation, synthesis, and analysis. */ \ +typedef struct RESAMP2(_s) * RESAMP2(); \ + \ +/* Create half-band resampler from design prototype. */ \ +/* _m : filter semi-length (h_len = 4*m+1), _m >= 2 */ \ +/* _f0 : filter center frequency, -0.5 <= _f0 <= 0.5 */ \ +/* _As : stop-band attenuation [dB], _As > 0 */ \ +RESAMP2() RESAMP2(_create)(unsigned int _m, \ + float _f0, \ + float _As); \ + \ +/* Re-create half-band resampler with new properties */ \ +/* _q : original half-band resampler object */ \ +/* _m : filter semi-length (h_len = 4*m+1), _m >= 2 */ \ +/* _f0 : filter center frequency, -0.5 <= _f0 <= 0.5 */ \ +/* _As : stop-band attenuation [dB], _As > 0 */ \ +RESAMP2() RESAMP2(_recreate)(RESAMP2() _q, \ + unsigned int _m, \ + float _f0, \ + float _As); \ + \ +/* Destroy resampler, freeing all internally-allocated memory */ \ +void RESAMP2(_destroy)(RESAMP2() _q); \ + \ +/* print resampler object's internals to stdout */ \ +void RESAMP2(_print)(RESAMP2() _q); \ + \ +/* Reset internal buffer */ \ +void RESAMP2(_reset)(RESAMP2() _q); \ + \ +/* Get resampler filter delay (semi-length m) */ \ +unsigned int RESAMP2(_get_delay)(RESAMP2() _q); \ + \ +/* Execute resampler as half-band filter for a single input sample */ \ +/* \(x\) where \(y_0\) is the output of the effective low-pass filter, */ \ +/* and \(y_1\) is the output of the effective high-pass filter. */ \ +/* _q : resampler object */ \ +/* _x : input sample */ \ +/* _y0 : output sample pointer (low frequency) */ \ +/* _y1 : output sample pointer (high frequency) */ \ +void RESAMP2(_filter_execute)(RESAMP2() _q, \ + TI _x, \ + TO * _y0, \ + TO * _y1); \ + \ +/* Execute resampler as half-band analysis filterbank on a pair of */ \ +/* sequential time-domain input samples. */ \ +/* The decimated outputs of the low- and high-pass equivalent filters */ \ +/* are stored in \(y_0\) and \(y_1\), respectively. */ \ +/* _q : resampler object */ \ +/* _x : input array, [size: 2 x 1] */ \ +/* _y : output array, [size: 2 x 1] */ \ +void RESAMP2(_analyzer_execute)(RESAMP2() _q, \ + TI * _x, \ + TO * _y); \ + \ +/* Execute resampler as half-band synthesis filterbank on a pair of */ \ +/* input samples. The low- and high-pass input samples are provided by */ \ +/* \(x_0\) and \(x_1\), respectively. The sequential time-domain output */ \ +/* samples are stored in \(y_0\) and \(y_1\). */ \ +/* _q : resampler object */ \ +/* _x : input array [size: 2 x 1] */ \ +/* _y : output array [size: 2 x 1] */ \ +void RESAMP2(_synthesizer_execute)(RESAMP2() _q, \ + TI * _x, \ + TO * _y); \ + \ +/* Execute resampler as half-band decimator on a pair of sequential */ \ +/* time-domain input samples. */ \ +/* _q : resampler object */ \ +/* _x : input array [size: 2 x 1] */ \ +/* _y : output sample pointer */ \ +void RESAMP2(_decim_execute)(RESAMP2() _q, \ + TI * _x, \ + TO * _y); \ + \ +/* Execute resampler as half-band interpolator on a single input sample */ \ +/* _q : resampler object */ \ +/* _x : input sample */ \ +/* _y : output array [size: 2 x 1] */ \ +void RESAMP2(_interp_execute)(RESAMP2() _q, \ + TI _x, \ + TO * _y); \ + +LIQUID_RESAMP2_DEFINE_API(LIQUID_RESAMP2_MANGLE_RRRF, + float, + float, + float) + +LIQUID_RESAMP2_DEFINE_API(LIQUID_RESAMP2_MANGLE_CRCF, + liquid_float_complex, + float, + liquid_float_complex) + +LIQUID_RESAMP2_DEFINE_API(LIQUID_RESAMP2_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + + +// +// Rational resampler +// +#define LIQUID_RRESAMP_MANGLE_RRRF(name) LIQUID_CONCAT(rresamp_rrrf,name) +#define LIQUID_RRESAMP_MANGLE_CRCF(name) LIQUID_CONCAT(rresamp_crcf,name) +#define LIQUID_RRESAMP_MANGLE_CCCF(name) LIQUID_CONCAT(rresamp_cccf,name) + +#define LIQUID_RRESAMP_DEFINE_API(RRESAMP,TO,TC,TI) \ + \ +/* Rational rate resampler, implemented as a polyphase filterbank */ \ +typedef struct RRESAMP(_s) * RRESAMP(); \ + \ +/* Create rational-rate resampler object from external coeffcients to */ \ +/* resample at an exact rate P/Q. */ \ +/* Note that to preserve the input filter coefficients, the greatest */ \ +/* common divisor (gcd) is not removed internally from _P and _Q when */ \ +/* this method is called. */ \ +/* _P : interpolation factor, P > 0 */ \ +/* _Q : decimation factor, Q > 0 */ \ +/* _m : filter semi-length (delay), 0 < _m */ \ +/* _h : filter coefficients, [size: 2*_P*_m x 1] */ \ +RRESAMP() RRESAMP(_create)(unsigned int _P, \ + unsigned int _Q, \ + unsigned int _m, \ + TC * _h); \ + \ +/* Create rational-rate resampler object from filter prototype to */ \ +/* resample at an exact rate P/Q. */ \ +/* Note that because the filter coefficients are computed internally */ \ +/* here, the greatest common divisor (gcd) from _P and _Q is internally */ \ +/* removed to improve speed. */ \ +/* _P : interpolation factor, P > 0 */ \ +/* _Q : decimation factor, Q > 0 */ \ +/* _m : filter semi-length (delay), 0 < _m */ \ +/* _bw : filter bandwidth relative to sample rate, 0 < _bw <= 0.5 */ \ +/* _As : filter stop-band attenuation [dB], 0 < _As */ \ +RRESAMP() RRESAMP(_create_kaiser)(unsigned int _P, \ + unsigned int _Q, \ + unsigned int _m, \ + float _bw, \ + float _As); \ + \ +/* Create rational-rate resampler object from filter prototype to */ \ +/* resample at an exact rate P/Q. */ \ +/* Note that because the filter coefficients are computed internally */ \ +/* here, the greatest common divisor (gcd) from _P and _Q is internally */ \ +/* removed to improve speed. */ \ +RRESAMP() RRESAMP(_create_prototype)(int _type, \ + unsigned int _P, \ + unsigned int _Q, \ + unsigned int _m, \ + float _beta); \ + \ +/* Create rational resampler object with a specified resampling rate of */ \ +/* exactly P/Q with default parameters. This is a simplified method to */ \ +/* provide a basic resampler with a baseline set of parameters, */ \ +/* abstracting away some of the complexities with the filterbank */ \ +/* design. */ \ +/* The default parameters are */ \ +/* m = 12 (filter semi-length), */ \ +/* bw = 0.5 (filter bandwidth), and */ \ +/* As = 60 dB (filter stop-band attenuation) */ \ +/* _P : interpolation factor, P > 0 */ \ +/* _Q : decimation factor, Q > 0 */ \ +RRESAMP() RRESAMP(_create_default)(unsigned int _P, \ + unsigned int _Q); \ + \ +/* Destroy resampler object, freeing all internal memory */ \ +void RRESAMP(_destroy)(RRESAMP() _q); \ + \ +/* Print resampler object internals to stdout */ \ +void RRESAMP(_print)(RRESAMP() _q); \ + \ +/* Reset resampler object internals */ \ +void RRESAMP(_reset)(RRESAMP() _q); \ + \ +/* Set output scaling for filter, default: \( 2 w \sqrt{P/Q} \) */ \ +/* _q : resampler object */ \ +/* _scale : scaling factor to apply to each output sample */ \ +void RRESAMP(_set_scale)(RRESAMP() _q, \ + TC _scale); \ + \ +/* Get output scaling for filter */ \ +/* _q : resampler object */ \ +/* _scale : scaling factor to apply to each output sample */ \ +void RRESAMP(_get_scale)(RRESAMP() _q, \ + TC * _scale); \ + \ +/* Get resampler delay (filter semi-length \(m\)) */ \ +unsigned int RRESAMP(_get_delay)(RRESAMP() _q); \ + \ +/* Get original interpolation factor \(P\) when object was created */ \ +/* before removing greatest common divisor */ \ +unsigned int RRESAMP(_get_P)(RRESAMP() _q); \ + \ +/* Get internal interpolation factor of resampler, \(P\), after */ \ +/* removing greatest common divisor */ \ +unsigned int RRESAMP(_get_interp)(RRESAMP() _q); \ + \ +/* Get original decimation factor \(Q\) when object was created */ \ +/* before removing greatest common divisor */ \ +unsigned int RRESAMP(_get_Q)(RRESAMP() _q); \ + \ +/* Get internal decimation factor of resampler, \(Q\), after removing */ \ +/* greatest common divisor */ \ +unsigned int RRESAMP(_get_decim)(RRESAMP() _q); \ + \ +/* Get block length (e.g. greatest common divisor) between original P */ \ +/* and Q values */ \ +unsigned int RRESAMP(_get_block_len)(RRESAMP() _q); \ + \ +/* Get rate of resampler, \(r = P/Q\) */ \ +float RRESAMP(_get_rate)(RRESAMP() _q); \ + \ +/* Execute rational-rate resampler on a block of input samples and */ \ +/* store the resulting samples in the output array. */ \ +/* Note that the size of the input and output buffers correspond to the */ \ +/* values of P and Q passed when the object was created, even if they */ \ +/* share a common divisor. Internally the rational resampler reduces P */ \ +/* and Q by their greatest commmon denominator to reduce processing; */ \ +/* however sometimes it is convenienct to create the object based on */ \ +/* expected output/input block sizes. This expectation is preserved. So */ \ +/* if an object is created with P=80 and Q=72, the object will */ \ +/* internally set P=10 and Q=9 (with a g.c.d of 8); however when */ \ +/* "execute" is called the resampler will still expect an input buffer */ \ +/* of 72 and an output buffer of 80. */ \ +/* _q : resamp object */ \ +/* _x : input sample array, [size: Q x 1] */ \ +/* _y : output sample array [size: P x 1] */ \ +void RRESAMP(_execute)(RRESAMP() _q, \ + TI * _x, \ + TO * _y); \ + +LIQUID_RRESAMP_DEFINE_API(LIQUID_RRESAMP_MANGLE_RRRF, + float, + float, + float) + +LIQUID_RRESAMP_DEFINE_API(LIQUID_RRESAMP_MANGLE_CRCF, + liquid_float_complex, + float, + liquid_float_complex) + +LIQUID_RRESAMP_DEFINE_API(LIQUID_RRESAMP_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + + +// +// Arbitrary resampler +// +#define LIQUID_RESAMP_MANGLE_RRRF(name) LIQUID_CONCAT(resamp_rrrf,name) +#define LIQUID_RESAMP_MANGLE_CRCF(name) LIQUID_CONCAT(resamp_crcf,name) +#define LIQUID_RESAMP_MANGLE_CCCF(name) LIQUID_CONCAT(resamp_cccf,name) + +#define LIQUID_RESAMP_DEFINE_API(RESAMP,TO,TC,TI) \ + \ +/* Arbitrary rate resampler, implemented as a polyphase filterbank */ \ +typedef struct RESAMP(_s) * RESAMP(); \ + \ +/* Create arbitrary resampler object from filter prototype */ \ +/* _rate : arbitrary resampling rate, 0 < _rate */ \ +/* _m : filter semi-length (delay), 0 < _m */ \ +/* _fc : filter cutoff frequency, 0 < _fc < 0.5 */ \ +/* _As : filter stop-band attenuation [dB], 0 < _As */ \ +/* _npfb : number of filters in the bank, 0 < _npfb */ \ +RESAMP() RESAMP(_create)(float _rate, \ + unsigned int _m, \ + float _fc, \ + float _As, \ + unsigned int _npfb); \ + \ +/* Create arbitrary resampler object with a specified input resampling */ \ +/* rate and default parameters. This is a simplified method to provide */ \ +/* a basic resampler with a baseline set of parameters, abstracting */ \ +/* away some of the complexities with the filterbank design. */ \ +/* The default parameters are */ \ +/* m = 7 (filter semi-length), */ \ +/* fc = min(0.49,_rate/2) (filter cutoff frequency), */ \ +/* As = 60 dB (filter stop-band attenuation), and */ \ +/* npfb = 64 (number of filters in the bank). */ \ +/* _rate : arbitrary resampling rate, 0 < _rate */ \ +RESAMP() RESAMP(_create_default)(float _rate); \ + \ +/* Destroy arbitrary resampler object, freeing all internal memory */ \ +void RESAMP(_destroy)(RESAMP() _q); \ + \ +/* Print resamp object internals to stdout */ \ +void RESAMP(_print)(RESAMP() _q); \ + \ +/* Reset resamp object internals */ \ +void RESAMP(_reset)(RESAMP() _q); \ + \ +/* Get resampler delay (filter semi-length \(m\)) */ \ +unsigned int RESAMP(_get_delay)(RESAMP() _q); \ + \ +/* Set rate of arbitrary resampler */ \ +/* _q : resampling object */ \ +/* _rate : new sampling rate, _rate > 0 */ \ +void RESAMP(_set_rate)(RESAMP() _q, \ + float _rate); \ + \ +/* Get rate of arbitrary resampler */ \ +float RESAMP(_get_rate)(RESAMP() _q); \ + \ +/* adjust rate of arbitrary resampler */ \ +/* _q : resampling object */ \ +/* _gamma : rate adjustment factor: rate <- rate * gamma, _gamma > 0 */ \ +void RESAMP(_adjust_rate)(RESAMP() _q, \ + float _gamma); \ + \ +/* Set resampling timing phase */ \ +/* _q : resampling object */ \ +/* _tau : sample timing phase, -1 <= _tau <= 1 */ \ +void RESAMP(_set_timing_phase)(RESAMP() _q, \ + float _tau); \ + \ +/* Adjust resampling timing phase */ \ +/* _q : resampling object */ \ +/* _delta : sample timing adjustment, -1 <= _delta <= 1 */ \ +void RESAMP(_adjust_timing_phase)(RESAMP() _q, \ + float _delta); \ + \ +/* Execute arbitrary resampler on a single input sample and store the */ \ +/* resulting samples in the output array. The number of output samples */ \ +/* is depenent upon the resampling rate but will be at most */ \ +/* \( \lceil{ r \rceil} \) samples. */ \ +/* _q : resamp object */ \ +/* _x : single input sample */ \ +/* _y : output sample array (pointer) */ \ +/* _num_written : number of samples written to _y */ \ +void RESAMP(_execute)(RESAMP() _q, \ + TI _x, \ + TO * _y, \ + unsigned int * _num_written); \ + \ +/* Execute arbitrary resampler on a block of input samples and store */ \ +/* the resulting samples in the output array. The number of output */ \ +/* samples is depenent upon the resampling rate and the number of input */ \ +/* samples but will be at most \( \lceil{ r n_x \rceil} \) samples. */ \ +/* _q : resamp object */ \ +/* _x : input buffer, [size: _nx x 1] */ \ +/* _nx : input buffer */ \ +/* _y : output sample array (pointer) */ \ +/* _ny : number of samples written to _y */ \ +void RESAMP(_execute_block)(RESAMP() _q, \ + TI * _x, \ + unsigned int _nx, \ + TO * _y, \ + unsigned int * _ny); \ + +LIQUID_RESAMP_DEFINE_API(LIQUID_RESAMP_MANGLE_RRRF, + float, + float, + float) + +LIQUID_RESAMP_DEFINE_API(LIQUID_RESAMP_MANGLE_CRCF, + liquid_float_complex, + float, + liquid_float_complex) + +LIQUID_RESAMP_DEFINE_API(LIQUID_RESAMP_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + + +// +// Multi-stage half-band resampler +// + +// resampling type (interpolator/decimator) +typedef enum { + LIQUID_RESAMP_INTERP=0, // interpolator + LIQUID_RESAMP_DECIM, // decimator +} liquid_resamp_type; + +#define LIQUID_MSRESAMP2_MANGLE_RRRF(name) LIQUID_CONCAT(msresamp2_rrrf,name) +#define LIQUID_MSRESAMP2_MANGLE_CRCF(name) LIQUID_CONCAT(msresamp2_crcf,name) +#define LIQUID_MSRESAMP2_MANGLE_CCCF(name) LIQUID_CONCAT(msresamp2_cccf,name) + +#define LIQUID_MSRESAMP2_DEFINE_API(MSRESAMP2,TO,TC,TI) \ + \ +/* Multi-stage half-band resampler, implemented as cascaded dyadic */ \ +/* (half-band) polyphase filter banks for interpolation and decimation. */ \ +typedef struct MSRESAMP2(_s) * MSRESAMP2(); \ + \ +/* Create multi-stage half-band resampler as either decimator or */ \ +/* interpolator. */ \ +/* _type : resampler type (e.g. LIQUID_RESAMP_DECIM) */ \ +/* _num_stages : number of resampling stages, _num_stages <= 16 */ \ +/* _fc : filter cut-off frequency, 0 < _fc < 0.5 */ \ +/* _f0 : filter center frequency (set to zero) */ \ +/* _As : stop-band attenuation [dB], _As > 0 */ \ +MSRESAMP2() MSRESAMP2(_create)(int _type, \ + unsigned int _num_stages, \ + float _fc, \ + float _f0, \ + float _As); \ + \ +/* Destroy multi-stage half-band resampler, freeing all internal memory */ \ +void MSRESAMP2(_destroy)(MSRESAMP2() _q); \ + \ +/* Print msresamp object internals to stdout */ \ +void MSRESAMP2(_print)(MSRESAMP2() _q); \ + \ +/* Reset msresamp object internal state */ \ +void MSRESAMP2(_reset)(MSRESAMP2() _q); \ + \ +/* Get multi-stage half-band resampling rate */ \ +float MSRESAMP2(_get_rate)(MSRESAMP2() _q); \ + \ +/* Get number of half-band resampling stages in object */ \ +unsigned int MSRESAMP2(_get_num_stages)(MSRESAMP2() _q); \ + \ +/* Get resampling type (LIQUID_RESAMP_DECIM, LIQUID_RESAMP_INTERP) */ \ +int MSRESAMP2(_get_type)(MSRESAMP2() _q); \ + \ +/* Get group delay (number of output samples) */ \ +float MSRESAMP2(_get_delay)(MSRESAMP2() _q); \ + \ +/* Execute multi-stage resampler, M = 2^num_stages */ \ +/* LIQUID_RESAMP_INTERP: input: 1, output: M */ \ +/* LIQUID_RESAMP_DECIM: input: M, output: 1 */ \ +/* _q : msresamp object */ \ +/* _x : input sample array */ \ +/* _y : output sample array */ \ +void MSRESAMP2(_execute)(MSRESAMP2() _q, \ + TI * _x, \ + TO * _y); \ + +LIQUID_MSRESAMP2_DEFINE_API(LIQUID_MSRESAMP2_MANGLE_RRRF, + float, + float, + float) + +LIQUID_MSRESAMP2_DEFINE_API(LIQUID_MSRESAMP2_MANGLE_CRCF, + liquid_float_complex, + float, + liquid_float_complex) + +LIQUID_MSRESAMP2_DEFINE_API(LIQUID_MSRESAMP2_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + + +// +// Multi-stage arbitrary resampler +// +#define LIQUID_MSRESAMP_MANGLE_RRRF(name) LIQUID_CONCAT(msresamp_rrrf,name) +#define LIQUID_MSRESAMP_MANGLE_CRCF(name) LIQUID_CONCAT(msresamp_crcf,name) +#define LIQUID_MSRESAMP_MANGLE_CCCF(name) LIQUID_CONCAT(msresamp_cccf,name) + +#define LIQUID_MSRESAMP_DEFINE_API(MSRESAMP,TO,TC,TI) \ + \ +/* Multi-stage half-band resampler, implemented as cascaded dyadic */ \ +/* (half-band) polyphase filter banks followed by an arbitrary rate */ \ +/* resampler for interpolation and decimation. */ \ +typedef struct MSRESAMP(_s) * MSRESAMP(); \ + \ +/* Create multi-stage arbitrary resampler */ \ +/* _r : resampling rate (output/input), _r > 0 */ \ +/* _As : stop-band attenuation [dB], _As > 0 */ \ +MSRESAMP() MSRESAMP(_create)(float _r, \ + float _As); \ + \ +/* Destroy multi-stage arbitrary resampler */ \ +void MSRESAMP(_destroy)(MSRESAMP() _q); \ + \ +/* Print msresamp object internals to stdout */ \ +void MSRESAMP(_print)(MSRESAMP() _q); \ + \ +/* Reset msresamp object internal state */ \ +void MSRESAMP(_reset)(MSRESAMP() _q); \ + \ +/* Get filter delay (output samples) */ \ +float MSRESAMP(_get_delay)(MSRESAMP() _q); \ + \ +/* get overall resampling rate */ \ +float MSRESAMP(_get_rate)(MSRESAMP() _q); \ + \ +/* Execute multi-stage resampler on one or more input samples. */ \ +/* The number of output samples is dependent upon the resampling rate */ \ +/* and the number of input samples. In general it is good practice to */ \ +/* allocate at least \( \lceil{ 1 + 2 r n_x \rceil} \) samples in the */ \ +/* output array to avoid overflows. */ \ +/* _q : msresamp object */ \ +/* _x : input sample array, [size: _nx x 1] */ \ +/* _nx : input sample array size */ \ +/* _y : pointer to output array for storing result */ \ +/* _ny : number of samples written to _y */ \ +void MSRESAMP(_execute)(MSRESAMP() _q, \ + TI * _x, \ + unsigned int _nx, \ + TO * _y, \ + unsigned int * _ny); \ + +LIQUID_MSRESAMP_DEFINE_API(LIQUID_MSRESAMP_MANGLE_RRRF, + float, + float, + float) + +LIQUID_MSRESAMP_DEFINE_API(LIQUID_MSRESAMP_MANGLE_CRCF, + liquid_float_complex, + float, + liquid_float_complex) + +LIQUID_MSRESAMP_DEFINE_API(LIQUID_MSRESAMP_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + +// +// Direct digital [up/down] synthesizer +// + +#define DDS_MANGLE_CCCF(name) LIQUID_CONCAT(dds_cccf,name) + +#define LIQUID_DDS_DEFINE_API(DDS,TO,TC,TI) \ +typedef struct DDS(_s) * DDS(); \ + \ +/* create digital synthesizer object */ \ +DDS() DDS(_create)(unsigned int _num_stages, \ + float _fc, \ + float _bw, \ + float _As); \ + \ +/* destroy digital synthesizer object */ \ +void DDS(_destroy)(DDS() _q); \ + \ +/* print synthesizer object internals to stdout */ \ +void DDS(_print)(DDS() _q); \ + \ +/* reset synthesizer object internals */ \ +void DDS(_reset)(DDS() _q); \ + \ +void DDS(_decim_execute)(DDS() _q, \ + TI * _x, \ + TO * _y); \ +void DDS(_interp_execute)(DDS() _q, \ + TI _x, \ + TO * _y); \ + +LIQUID_DDS_DEFINE_API(DDS_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + + +// +// Symbol timing recovery (symbol synchronizer) +// +#define LIQUID_SYMSYNC_MANGLE_RRRF(name) LIQUID_CONCAT(symsync_rrrf,name) +#define LIQUID_SYMSYNC_MANGLE_CRCF(name) LIQUID_CONCAT(symsync_crcf,name) + +#define LIQUID_SYMSYNC_DEFINE_API(SYMSYNC,TO,TC,TI) \ + \ +/* Multi-rate symbol synchronizer for symbol timing recovery. */ \ +typedef struct SYMSYNC(_s) * SYMSYNC(); \ + \ +/* Create synchronizer object from external coefficients */ \ +/* _k : samples per symbol, _k >= 2 */ \ +/* _M : number of filters in the bank, _M > 0 */ \ +/* _h : matched filter coefficients, [size: _h_len x 1] */ \ +/* _h_len : length of matched filter; \( h_{len} = 2 k m + 1 \) */ \ +SYMSYNC() SYMSYNC(_create)(unsigned int _k, \ + unsigned int _M, \ + TC * _h, \ + unsigned int _h_len); \ + \ +/* Create square-root Nyquist symbol synchronizer from prototype */ \ +/* _type : filter type (e.g. LIQUID_FIRFILT_RRC) */ \ +/* _k : samples/symbol, _k >= 2 */ \ +/* _m : symbol delay, _m > 0 */ \ +/* _beta : rolloff factor, 0 <= _beta <= 1 */ \ +/* _M : number of filters in the bank, _M > 0 */ \ +SYMSYNC() SYMSYNC(_create_rnyquist)(int _type, \ + unsigned int _k, \ + unsigned int _m, \ + float _beta, \ + unsigned int _M); \ + \ +/* Create symsync using Kaiser filter interpolator. This is useful when */ \ +/* the input signal has its matched filter applied already. */ \ +/* _k : input samples/symbol, _k >= 2 */ \ +/* _m : symbol delay, _m > 0 */ \ +/* _beta : rolloff factor, 0<= _beta <= 1 */ \ +/* _M : number of filters in the bank, _M > 0 */ \ +SYMSYNC() SYMSYNC(_create_kaiser)(unsigned int _k, \ + unsigned int _m, \ + float _beta, \ + unsigned int _M); \ + \ +/* Destroy symsync object, freeing all internal memory */ \ +void SYMSYNC(_destroy)(SYMSYNC() _q); \ + \ +/* Print symsync object's parameters to stdout */ \ +void SYMSYNC(_print)(SYMSYNC() _q); \ + \ +/* Reset symsync internal state */ \ +void SYMSYNC(_reset)(SYMSYNC() _q); \ + \ +/* Lock the symbol synchronizer's loop control */ \ +void SYMSYNC(_lock)(SYMSYNC() _q); \ + \ +/* Unlock the symbol synchronizer's loop control */ \ +void SYMSYNC(_unlock)(SYMSYNC() _q); \ + \ +/* Set synchronizer output rate (samples/symbol) */ \ +/* _q : synchronizer object */ \ +/* _k_out : output samples/symbol, _k_out > 0 */ \ +void SYMSYNC(_set_output_rate)(SYMSYNC() _q, \ + unsigned int _k_out); \ + \ +/* Set loop-filter bandwidth */ \ +/* _q : synchronizer object */ \ +/* _bt : loop bandwidth, 0 <= _bt <= 1 */ \ +void SYMSYNC(_set_lf_bw)(SYMSYNC() _q, \ + float _bt); \ + \ +/* Return instantaneous fractional timing offset estimate */ \ +float SYMSYNC(_get_tau)(SYMSYNC() _q); \ + \ +/* Execute synchronizer on input data array */ \ +/* _q : synchronizer object */ \ +/* _x : input data array, [size: _nx x 1] */ \ +/* _nx : number of input samples */ \ +/* _y : output data array */ \ +/* _ny : number of samples written to output buffer */ \ +void SYMSYNC(_execute)(SYMSYNC() _q, \ + TI * _x, \ + unsigned int _nx, \ + TO * _y, \ + unsigned int * _ny); \ + +LIQUID_SYMSYNC_DEFINE_API(LIQUID_SYMSYNC_MANGLE_RRRF, + float, + float, + float) + +LIQUID_SYMSYNC_DEFINE_API(LIQUID_SYMSYNC_MANGLE_CRCF, + liquid_float_complex, + float, + liquid_float_complex) + + +// +// Finite impulse response Farrow filter +// + +#define LIQUID_FIRFARROW_MANGLE_RRRF(name) LIQUID_CONCAT(firfarrow_rrrf,name) +#define LIQUID_FIRFARROW_MANGLE_CRCF(name) LIQUID_CONCAT(firfarrow_crcf,name) +//#define LIQUID_FIRFARROW_MANGLE_CCCF(name) LIQUID_CONCAT(firfarrow_cccf,name) + +// Macro: +// FIRFARROW : name-mangling macro +// TO : output data type +// TC : coefficients data type +// TI : input data type +#define LIQUID_FIRFARROW_DEFINE_API(FIRFARROW,TO,TC,TI) \ + \ +/* Finite impulse response (FIR) Farrow filter for timing delay */ \ +typedef struct FIRFARROW(_s) * FIRFARROW(); \ + \ +/* Create firfarrow object */ \ +/* _h_len : filter length, _h_len >= 2 */ \ +/* _p : polynomial order, _p >= 1 */ \ +/* _fc : filter cutoff frequency, 0 <= _fc <= 0.5 */ \ +/* _As : stopband attenuation [dB], _As > 0 */ \ +FIRFARROW() FIRFARROW(_create)(unsigned int _h_len, \ + unsigned int _p, \ + float _fc, \ + float _As); \ + \ +/* Destroy firfarrow object, freeing all internal memory */ \ +void FIRFARROW(_destroy)(FIRFARROW() _q); \ + \ +/* Print firfarrow object's internal properties */ \ +void FIRFARROW(_print)(FIRFARROW() _q); \ + \ +/* Reset firfarrow object's internal state */ \ +void FIRFARROW(_reset)(FIRFARROW() _q); \ + \ +/* Push sample into firfarrow object */ \ +/* _q : firfarrow object */ \ +/* _x : input sample */ \ +void FIRFARROW(_push)(FIRFARROW() _q, \ + TI _x); \ + \ +/* Set fractional delay of firfarrow object */ \ +/* _q : firfarrow object */ \ +/* _mu : fractional sample delay, -1 <= _mu <= 1 */ \ +void FIRFARROW(_set_delay)(FIRFARROW() _q, \ + float _mu); \ + \ +/* Execute firfarrow internal dot product */ \ +/* _q : firfarrow object */ \ +/* _y : output sample pointer */ \ +void FIRFARROW(_execute)(FIRFARROW() _q, \ + TO * _y); \ + \ +/* Execute firfarrow filter on block of samples. */ \ +/* In-place operation is permitted (the input and output arrays may */ \ +/* share the same pointer) */ \ +/* _q : firfarrow object */ \ +/* _x : input array, [size: _n x 1] */ \ +/* _n : input, output array size */ \ +/* _y : output array, [size: _n x 1] */ \ +void FIRFARROW(_execute_block)(FIRFARROW() _q, \ + TI * _x, \ + unsigned int _n, \ + TO * _y); \ + \ +/* Get length of firfarrow object (number of filter taps) */ \ +unsigned int FIRFARROW(_get_length)(FIRFARROW() _q); \ + \ +/* Get coefficients of firfarrow object */ \ +/* _q : firfarrow object */ \ +/* _h : output coefficients pointer, [size: _h_len x 1] */ \ +void FIRFARROW(_get_coefficients)(FIRFARROW() _q, \ + float * _h); \ + \ +/* Compute complex frequency response */ \ +/* _q : filter object */ \ +/* _fc : frequency */ \ +/* _H : output frequency response */ \ +void FIRFARROW(_freqresponse)(FIRFARROW() _q, \ + float _fc, \ + liquid_float_complex * _H); \ + \ +/* Compute group delay [samples] */ \ +/* _q : filter object */ \ +/* _fc : frequency */ \ +float FIRFARROW(_groupdelay)(FIRFARROW() _q, \ + float _fc); \ + +LIQUID_FIRFARROW_DEFINE_API(LIQUID_FIRFARROW_MANGLE_RRRF, + float, + float, + float) + +LIQUID_FIRFARROW_DEFINE_API(LIQUID_FIRFARROW_MANGLE_CRCF, + liquid_float_complex, + float, + liquid_float_complex) + + +// +// Order-statistic filter +// + +#define LIQUID_ORDFILT_MANGLE_RRRF(name) LIQUID_CONCAT(ordfilt_rrrf,name) + +// Macro: +// ORDFILT : name-mangling macro +// TO : output data type +// TC : coefficients data type +// TI : input data type +#define LIQUID_ORDFILT_DEFINE_API(ORDFILT,TO,TC,TI) \ + \ +/* Finite impulse response (FIR) filter */ \ +typedef struct ORDFILT(_s) * ORDFILT(); \ + \ +/* Create a order-statistic filter (ordfilt) object by specifying */ \ +/* the buffer size and appropriate sample index of order statistic. */ \ +/* _n : buffer size, _n > 0 */ \ +/* _k : sample index for order statistic, 0 <= _k < _n */ \ +ORDFILT() ORDFILT(_create)(unsigned int _n, \ + unsigned int _k); \ + \ +/* Create a median filter by specifying buffer semi-length. */ \ +/* _m : buffer semi-length */ \ +ORDFILT() ORDFILT(_create_medfilt)(unsigned int _m); \ + \ +/* Destroy filter object and free all internal memory */ \ +void ORDFILT(_destroy)(ORDFILT() _q); \ + \ +/* Reset filter object's internal buffer */ \ +void ORDFILT(_reset)(ORDFILT() _q); \ + \ +/* Print filter object information to stdout */ \ +void ORDFILT(_print)(ORDFILT() _q); \ + \ +/* Push sample into filter object's internal buffer */ \ +/* _q : filter object */ \ +/* _x : single input sample */ \ +void ORDFILT(_push)(ORDFILT() _q, \ + TI _x); \ + \ +/* Write block of samples into object's internal buffer */ \ +/* _q : filter object */ \ +/* _x : array of input samples, [size: _n x 1] */ \ +/* _n : number of input elements */ \ +void ORDFILT(_write)(ORDFILT() _q, \ + TI * _x, \ + unsigned int _n); \ + \ +/* Execute vector dot product on the filter's internal buffer and */ \ +/* coefficients */ \ +/* _q : filter object */ \ +/* _y : pointer to single output sample */ \ +void ORDFILT(_execute)(ORDFILT() _q, \ + TO * _y); \ + \ +/* Execute the filter on a block of input samples; in-place operation */ \ +/* is permitted (_x and _y may point to the same place in memory) */ \ +/* _q : filter object */ \ +/* _x : pointer to input array, [size: _n x 1] */ \ +/* _n : number of input, output samples */ \ +/* _y : pointer to output array, [size: _n x 1] */ \ +void ORDFILT(_execute_block)(ORDFILT() _q, \ + TI * _x, \ + unsigned int _n, \ + TO * _y); \ + +LIQUID_ORDFILT_DEFINE_API(LIQUID_ORDFILT_MANGLE_RRRF, + float, + float, + float) + + +// +// MODULE : framing +// + +// framesyncstats : generic frame synchronizer statistic structure + +typedef struct { + // signal quality + float evm; // error vector magnitude [dB] + float rssi; // received signal strength indicator [dB] + float cfo; // carrier frequency offset (f/Fs) + + // demodulated frame symbols + liquid_float_complex * framesyms; // pointer to array [size: framesyms x 1] + unsigned int num_framesyms; // length of framesyms + + // modulation/coding scheme etc. + unsigned int mod_scheme; // modulation scheme + unsigned int mod_bps; // modulation depth (bits/symbol) + unsigned int check; // data validity check (crc, checksum) + unsigned int fec0; // forward error-correction (inner) + unsigned int fec1; // forward error-correction (outer) +} framesyncstats_s; + +// external framesyncstats default object +extern framesyncstats_s framesyncstats_default; + +// initialize framesyncstats object on default +int framesyncstats_init_default(framesyncstats_s * _stats); + +// print framesyncstats object +int framesyncstats_print(framesyncstats_s * _stats); + + +// framedatastats : gather frame data +typedef struct { + unsigned int num_frames_detected; + unsigned int num_headers_valid; + unsigned int num_payloads_valid; + unsigned long int num_bytes_received; +} framedatastats_s; + +// reset framedatastats object +int framedatastats_reset(framedatastats_s * _stats); + +// print framedatastats object +int framedatastats_print(framedatastats_s * _stats); + + +// Generic frame synchronizer callback function type +// _header : header data [size: 8 bytes] +// _header_valid : is header valid? (0:no, 1:yes) +// _payload : payload data [size: _payload_len] +// _payload_len : length of payload (bytes) +// _payload_valid : is payload valid? (0:no, 1:yes) +// _stats : frame statistics object +// _userdata : pointer to userdata +typedef int (*framesync_callback)(unsigned char * _header, + int _header_valid, + unsigned char * _payload, + unsigned int _payload_len, + int _payload_valid, + framesyncstats_s _stats, + void * _userdata); + +// framesync csma callback functions invoked when signal levels is high or low +// _userdata : user-defined data pointer +typedef void (*framesync_csma_callback)(void * _userdata); + +// +// packet encoder/decoder +// + +typedef struct qpacketmodem_s * qpacketmodem; + +// create packet encoder +qpacketmodem qpacketmodem_create (); +int qpacketmodem_destroy(qpacketmodem _q); +int qpacketmodem_reset (qpacketmodem _q); +int qpacketmodem_print (qpacketmodem _q); + +int qpacketmodem_configure(qpacketmodem _q, + unsigned int _payload_len, + crc_scheme _check, + fec_scheme _fec0, + fec_scheme _fec1, + int _ms); + +// get length of encoded frame in symbols +unsigned int qpacketmodem_get_frame_len(qpacketmodem _q); + +// get unencoded/decoded payload length (bytes) +unsigned int qpacketmodem_get_payload_len(qpacketmodem _q); + +// regular access methods +unsigned int qpacketmodem_get_crc (qpacketmodem _q); +unsigned int qpacketmodem_get_fec0 (qpacketmodem _q); +unsigned int qpacketmodem_get_fec1 (qpacketmodem _q); +unsigned int qpacketmodem_get_modscheme(qpacketmodem _q); + +float qpacketmodem_get_demodulator_phase_error(qpacketmodem _q); +float qpacketmodem_get_demodulator_evm(qpacketmodem _q); + +// encode packet into un-modulated frame symbol indices +// _q : qpacketmodem object +// _payload : unencoded payload bytes +// _syms : encoded but un-modulated payload symbol indices +int qpacketmodem_encode_syms(qpacketmodem _q, + const unsigned char * _payload, + unsigned char * _syms); + +// decode packet from demodulated frame symbol indices (hard-decision decoding) +// _q : qpacketmodem object +// _syms : received hard-decision symbol indices [size: frame_len x 1] +// _payload : recovered decoded payload bytes +int qpacketmodem_decode_syms(qpacketmodem _q, + unsigned char * _syms, + unsigned char * _payload); + +// decode packet from demodulated frame bits (soft-decision decoding) +// _q : qpacketmodem object +// _bits : received soft-decision bits, [size: bps*frame_len x 1] +// _payload : recovered decoded payload bytes +int qpacketmodem_decode_bits(qpacketmodem _q, + unsigned char * _bits, + unsigned char * _payload); + +// encode and modulate packet into modulated frame samples +// _q : qpacketmodem object +// _payload : unencoded payload bytes +// _frame : encoded/modulated payload symbols +int qpacketmodem_encode(qpacketmodem _q, + const unsigned char * _payload, + liquid_float_complex * _frame); + +// decode packet from modulated frame samples, returning flag if CRC passed +// NOTE: hard-decision decoding +// _q : qpacketmodem object +// _frame : encoded/modulated payload symbols +// _payload : recovered decoded payload bytes +int qpacketmodem_decode(qpacketmodem _q, + liquid_float_complex * _frame, + unsigned char * _payload); + +// decode packet from modulated frame samples, returning flag if CRC passed +// NOTE: soft-decision decoding +// _q : qpacketmodem object +// _frame : encoded/modulated payload symbols +// _payload : recovered decoded payload bytes +int qpacketmodem_decode_soft(qpacketmodem _q, + liquid_float_complex * _frame, + unsigned char * _payload); + +int qpacketmodem_decode_soft_sym(qpacketmodem _q, + liquid_float_complex _symbol); + +int qpacketmodem_decode_soft_payload(qpacketmodem _q, + unsigned char * _payload); + +// +// pilot generator/synchronizer for packet burst recovery +// + +// get number of pilots in frame +unsigned int qpilot_num_pilots(unsigned int _payload_len, + unsigned int _pilot_spacing); + +// get length of frame with a particular payload length and pilot spacing +unsigned int qpilot_frame_len(unsigned int _payload_len, + unsigned int _pilot_spacing); + +// +// pilot generator for packet burst recovery +// + +typedef struct qpilotgen_s * qpilotgen; + +// create packet encoder +qpilotgen qpilotgen_create(unsigned int _payload_len, + unsigned int _pilot_spacing); + +qpilotgen qpilotgen_recreate(qpilotgen _q, + unsigned int _payload_len, + unsigned int _pilot_spacing); + +int qpilotgen_destroy(qpilotgen _q); +int qpilotgen_reset( qpilotgen _q); +int qpilotgen_print( qpilotgen _q); + +unsigned int qpilotgen_get_frame_len(qpilotgen _q); + +// insert pilot symbols +int qpilotgen_execute(qpilotgen _q, + liquid_float_complex * _payload, + liquid_float_complex * _frame); + +// +// pilot synchronizer for packet burst recovery +// +typedef struct qpilotsync_s * qpilotsync; + +// create packet encoder +qpilotsync qpilotsync_create(unsigned int _payload_len, + unsigned int _pilot_spacing); + +qpilotsync qpilotsync_recreate(qpilotsync _q, + unsigned int _payload_len, + unsigned int _pilot_spacing); + +int qpilotsync_destroy(qpilotsync _q); +int qpilotsync_reset( qpilotsync _q); +int qpilotsync_print( qpilotsync _q); + +unsigned int qpilotsync_get_frame_len(qpilotsync _q); + +// recover frame symbols from received frame +int qpilotsync_execute(qpilotsync _q, + liquid_float_complex * _frame, + liquid_float_complex * _payload); + +// get estimates +float qpilotsync_get_dphi(qpilotsync _q); +float qpilotsync_get_phi (qpilotsync _q); +float qpilotsync_get_gain(qpilotsync _q); +float qpilotsync_get_evm (qpilotsync _q); + + +// +// Basic frame generator (64 bytes data payload) +// + +// frame length in samples +#define LIQUID_FRAME64_LEN (1440) + +typedef struct framegen64_s * framegen64; + +// create frame generator +framegen64 framegen64_create(); + +// destroy frame generator +int framegen64_destroy(framegen64 _q); + +// print frame generator internal properties +int framegen64_print(framegen64 _q); + +// generate frame +// _q : frame generator object +// _header : 8-byte header data, NULL for random +// _payload : 64-byte payload data, NULL for random +// _frame : output frame samples [size: LIQUID_FRAME64_LEN x 1] +int framegen64_execute(framegen64 _q, + unsigned char * _header, + unsigned char * _payload, + liquid_float_complex * _frame); + +typedef struct framesync64_s * framesync64; + +// create framesync64 object +// _callback : callback function +// _userdata : user data pointer passed to callback function +framesync64 framesync64_create(framesync_callback _callback, + void * _userdata); + +// destroy frame synchronizer +int framesync64_destroy(framesync64 _q); + +// print frame synchronizer internal properties +int framesync64_print(framesync64 _q); + +// reset frame synchronizer internal state +int framesync64_reset(framesync64 _q); + +// push samples through frame synchronizer +// _q : frame synchronizer object +// _x : input samples [size: _n x 1] +// _n : number of input samples +int framesync64_execute(framesync64 _q, + liquid_float_complex * _x, + unsigned int _n); + +// enable/disable debugging +int framesync64_debug_enable(framesync64 _q); +int framesync64_debug_disable(framesync64 _q); +int framesync64_debug_print(framesync64 _q, const char * _filename); + +// frame data statistics +int framesync64_reset_framedatastats(framesync64 _q); +framedatastats_s framesync64_get_framedatastats (framesync64 _q); + +#if 0 +// advanced modes +int framesync64_set_csma_callbacks(framesync64 _q, + framesync_csma_callback _csma_lock, + framesync_csma_callback _csma_unlock, + void * _csma_userdata); +#endif + +// +// Flexible frame : adjustable payload, mod scheme, etc., but bring +// your own error correction, redundancy check +// + +// frame generator +typedef struct { + unsigned int check; // data validity check + unsigned int fec0; // forward error-correction scheme (inner) + unsigned int fec1; // forward error-correction scheme (outer) + unsigned int mod_scheme; // modulation scheme +} flexframegenprops_s; + +int flexframegenprops_init_default(flexframegenprops_s * _fgprops); + +typedef struct flexframegen_s * flexframegen; + +// create flexframegen object +// _props : frame properties (modulation scheme, etc.) +flexframegen flexframegen_create(flexframegenprops_s * _props); + +// destroy flexframegen object +int flexframegen_destroy(flexframegen _q); + +// print flexframegen object internals +int flexframegen_print(flexframegen _q); + +// reset flexframegen object internals +int flexframegen_reset(flexframegen _q); + +// is frame assembled? +int flexframegen_is_assembled(flexframegen _q); + +// get frame properties +int flexframegen_getprops(flexframegen _q, flexframegenprops_s * _props); + +// set frame properties +int flexframegen_setprops(flexframegen _q, flexframegenprops_s * _props); + +// set length of user-defined portion of header +int flexframegen_set_header_len(flexframegen _q, unsigned int _len); + +// set properties for header section +int flexframegen_set_header_props(flexframegen _q, + flexframegenprops_s * _props); + +// get length of assembled frame (samples) +unsigned int flexframegen_getframelen(flexframegen _q); + +// assemble a frame from an array of data +// _q : frame generator object +// _header : frame header +// _payload : payload data [size: _payload_len x 1] +// _payload_len : payload data length +int flexframegen_assemble(flexframegen _q, + const unsigned char * _header, + const unsigned char * _payload, + unsigned int _payload_len); + +// write samples of assembled frame, two samples at a time, returning +// '1' when frame is complete, '0' otherwise. Zeros will be written +// to the buffer if the frame is not assembled +// _q : frame generator object +// _buffer : output buffer [size: _buffer_len x 1] +// _buffer_len : output buffer length +int flexframegen_write_samples(flexframegen _q, + liquid_float_complex * _buffer, + unsigned int _buffer_len); + +// frame synchronizer + +typedef struct flexframesync_s * flexframesync; + +// create flexframesync object +// _callback : callback function +// _userdata : user data pointer passed to callback function +flexframesync flexframesync_create(framesync_callback _callback, + void * _userdata); + +// destroy frame synchronizer +int flexframesync_destroy(flexframesync _q); + +// print frame synchronizer internal properties +int flexframesync_print(flexframesync _q); + +// reset frame synchronizer internal state +int flexframesync_reset(flexframesync _q); + +// has frame been detected? +int flexframesync_is_frame_open(flexframesync _q); + +// change length of user-defined region in header +int flexframesync_set_header_len(flexframesync _q, + unsigned int _len); + +// enable or disable soft decoding of header +int flexframesync_decode_header_soft(flexframesync _q, + int _soft); + +// enable or disable soft decoding of payload +int flexframesync_decode_payload_soft(flexframesync _q, + int _soft); + +// set properties for header section +int flexframesync_set_header_props(flexframesync _q, + flexframegenprops_s * _props); + +// push samples through frame synchronizer +// _q : frame synchronizer object +// _x : input samples [size: _n x 1] +// _n : number of input samples +int flexframesync_execute(flexframesync _q, + liquid_float_complex * _x, + unsigned int _n); + +// frame data statistics +int flexframesync_reset_framedatastats(flexframesync _q); +framedatastats_s flexframesync_get_framedatastats (flexframesync _q); + +// enable/disable debugging +int flexframesync_debug_enable(flexframesync _q); +int flexframesync_debug_disable(flexframesync _q); +int flexframesync_debug_print(flexframesync _q, + const char * _filename); + +// +// bpacket : binary packet suitable for data streaming +// + +// +// bpacket generator/encoder +// +typedef struct bpacketgen_s * bpacketgen; + +// create bpacketgen object +// _m : p/n sequence length (ignored) +// _dec_msg_len : decoded message length (original uncoded data) +// _crc : data validity check (e.g. cyclic redundancy check) +// _fec0 : inner forward error-correction code scheme +// _fec1 : outer forward error-correction code scheme +bpacketgen bpacketgen_create(unsigned int _m, + unsigned int _dec_msg_len, + int _crc, + int _fec0, + int _fec1); + +// re-create bpacketgen object from old object +// _q : old bpacketgen object +// _m : p/n sequence length (ignored) +// _dec_msg_len : decoded message length (original uncoded data) +// _crc : data validity check (e.g. cyclic redundancy check) +// _fec0 : inner forward error-correction code scheme +// _fec1 : outer forward error-correction code scheme +bpacketgen bpacketgen_recreate(bpacketgen _q, + unsigned int _m, + unsigned int _dec_msg_len, + int _crc, + int _fec0, + int _fec1); + +// destroy bpacketgen object, freeing all internally-allocated memory +void bpacketgen_destroy(bpacketgen _q); + +// print bpacketgen internals +void bpacketgen_print(bpacketgen _q); + +// return length of full packet +unsigned int bpacketgen_get_packet_len(bpacketgen _q); + +// encode packet +void bpacketgen_encode(bpacketgen _q, + unsigned char * _msg_dec, + unsigned char * _packet); + +// +// bpacket synchronizer/decoder +// +typedef struct bpacketsync_s * bpacketsync; +typedef int (*bpacketsync_callback)(unsigned char * _payload, + int _payload_valid, + unsigned int _payload_len, + framesyncstats_s _stats, + void * _userdata); +bpacketsync bpacketsync_create(unsigned int _m, + bpacketsync_callback _callback, + void * _userdata); +int bpacketsync_destroy(bpacketsync _q); +int bpacketsync_print(bpacketsync _q); +int bpacketsync_reset(bpacketsync _q); + +// run synchronizer on array of input bytes +// _q : bpacketsync object +// _bytes : input data array [size: _n x 1] +// _n : input array size +int bpacketsync_execute(bpacketsync _q, + unsigned char * _bytes, + unsigned int _n); + +// run synchronizer on input byte +// _q : bpacketsync object +// _byte : input byte +int bpacketsync_execute_byte(bpacketsync _q, + unsigned char _byte); + +// run synchronizer on input symbol +// _q : bpacketsync object +// _sym : input symbol with _bps significant bits +// _bps : number of bits in input symbol +int bpacketsync_execute_sym(bpacketsync _q, + unsigned char _sym, + unsigned int _bps); + +// execute one bit at a time +int bpacketsync_execute_bit(bpacketsync _q, + unsigned char _bit); + +// +// M-FSK frame generator +// + +typedef struct fskframegen_s * fskframegen; + +// create M-FSK frame generator +fskframegen fskframegen_create(); +int fskframegen_destroy (fskframegen _fg); +int fskframegen_print (fskframegen _fg); +int fskframegen_reset (fskframegen _fg); +int fskframegen_assemble(fskframegen _fg, + unsigned char * _header, + unsigned char * _payload, + unsigned int _payload_len, + crc_scheme _check, + fec_scheme _fec0, + fec_scheme _fec1); +unsigned int fskframegen_getframelen(fskframegen _q); +int fskframegen_write_samples(fskframegen _fg, + liquid_float_complex * _buf, + unsigned int _buf_len); + + +// +// M-FSK frame synchronizer +// + +typedef struct fskframesync_s * fskframesync; + +// create M-FSK frame synchronizer +// _callback : callback function +// _userdata : user data pointer passed to callback function +fskframesync fskframesync_create(framesync_callback _callback, + void * _userdata); +int fskframesync_destroy(fskframesync _q); +int fskframesync_print (fskframesync _q); +int fskframesync_reset (fskframesync _q); +int fskframesync_execute(fskframesync _q, + liquid_float_complex _x); +int fskframesync_execute_block(fskframesync _q, + liquid_float_complex * _x, + unsigned int _n); + +// debugging +int fskframesync_debug_enable (fskframesync _q); +int fskframesync_debug_disable(fskframesync _q); +int fskframesync_debug_export (fskframesync _q, const char * _filename); + + +// +// GMSK frame generator +// + +typedef struct gmskframegen_s * gmskframegen; + +// create GMSK frame generator +gmskframegen gmskframegen_create(); +int gmskframegen_destroy (gmskframegen _q); +int gmskframegen_is_assembled (gmskframegen _q); +int gmskframegen_print (gmskframegen _q); +int gmskframegen_set_header_len(gmskframegen _q, unsigned int _len); +int gmskframegen_reset (gmskframegen _q); +int gmskframegen_assemble (gmskframegen _q, + const unsigned char * _header, + const unsigned char * _payload, + unsigned int _payload_len, + crc_scheme _check, + fec_scheme _fec0, + fec_scheme _fec1); +unsigned int gmskframegen_getframelen(gmskframegen _q); +int gmskframegen_write_samples(gmskframegen _q, + liquid_float_complex * _y); + +// write samples of assembled frame +// _q : frame generator object +// _buf : output buffer [size: _buf_len x 1] +// _buf_len : output buffer length +int gmskframegen_write(gmskframegen _q, + liquid_float_complex * _buf, + unsigned int _buf_len); + + +// +// GMSK frame synchronizer +// + +typedef struct gmskframesync_s * gmskframesync; + +// create GMSK frame synchronizer +// _callback : callback function +// _userdata : user data pointer passed to callback function +gmskframesync gmskframesync_create(framesync_callback _callback, + void * _userdata); +int gmskframesync_destroy(gmskframesync _q); +int gmskframesync_print(gmskframesync _q); +int gmskframesync_set_header_len(gmskframesync _q, unsigned int _len); +int gmskframesync_reset(gmskframesync _q); +int gmskframesync_is_frame_open(gmskframesync _q); +int gmskframesync_execute(gmskframesync _q, + liquid_float_complex * _x, + unsigned int _n); + +// debugging +int gmskframesync_debug_enable(gmskframesync _q); +int gmskframesync_debug_disable(gmskframesync _q); +int gmskframesync_debug_print(gmskframesync _q, const char * _filename); + + +// +// DSSS frame generator +// + +typedef struct { + unsigned int check; + unsigned int fec0; + unsigned int fec1; +} dsssframegenprops_s; + +typedef struct dsssframegen_s * dsssframegen; + +dsssframegen dsssframegen_create(dsssframegenprops_s * _props); +int dsssframegen_destroy(dsssframegen _q); +int dsssframegen_reset(dsssframegen _q); +int dsssframegen_is_assembled(dsssframegen _q); +int dsssframegen_getprops(dsssframegen _q, dsssframegenprops_s * _props); +int dsssframegen_setprops(dsssframegen _q, dsssframegenprops_s * _props); +int dsssframegen_set_header_len(dsssframegen _q, unsigned int _len); +int dsssframegen_set_header_props(dsssframegen _q, + dsssframegenprops_s * _props); +unsigned int dsssframegen_getframelen(dsssframegen _q); + +// assemble a frame from an array of data +// _q : frame generator object +// _header : frame header +// _payload : payload data [size: _payload_len x 1] +// _payload_len : payload data length +int dsssframegen_assemble(dsssframegen _q, + const unsigned char * _header, + const unsigned char * _payload, + unsigned int _payload_len); + +int dsssframegen_write_samples(dsssframegen _q, + liquid_float_complex * _buffer, + unsigned int _buffer_len); + + +// +// DSSS frame synchronizer +// + +typedef struct dsssframesync_s * dsssframesync; + +dsssframesync dsssframesync_create(framesync_callback _callback, void * _userdata); +int dsssframesync_destroy (dsssframesync _q); +int dsssframesync_print (dsssframesync _q); +int dsssframesync_reset (dsssframesync _q); +int dsssframesync_is_frame_open (dsssframesync _q); +int dsssframesync_set_header_len (dsssframesync _q, unsigned int _len); +int dsssframesync_decode_header_soft (dsssframesync _q, int _soft); +int dsssframesync_decode_payload_soft (dsssframesync _q, int _soft); +int dsssframesync_set_header_props (dsssframesync _q, dsssframegenprops_s * _props); +int dsssframesync_execute (dsssframesync _q, liquid_float_complex * _x, unsigned int _n); +int dsssframesync_reset_framedatastats(dsssframesync _q); +int dsssframesync_debug_enable (dsssframesync _q); +int dsssframesync_debug_disable (dsssframesync _q); +int dsssframesync_debug_print (dsssframesync _q, const char * _filename); +framedatastats_s dsssframesync_get_framedatastats (dsssframesync _q); + +// +// OFDM flexframe generator +// + +// ofdm frame generator properties +typedef struct { + unsigned int check; // data validity check + unsigned int fec0; // forward error-correction scheme (inner) + unsigned int fec1; // forward error-correction scheme (outer) + unsigned int mod_scheme; // modulation scheme + //unsigned int block_size; // framing block size +} ofdmflexframegenprops_s; +int ofdmflexframegenprops_init_default(ofdmflexframegenprops_s * _props); + +typedef struct ofdmflexframegen_s * ofdmflexframegen; + +// create OFDM flexible framing generator object +// _M : number of subcarriers, >10 typical +// _cp_len : cyclic prefix length +// _taper_len : taper length (OFDM symbol overlap) +// _p : subcarrier allocation (null, pilot, data), [size: _M x 1] +// _fgprops : frame properties (modulation scheme, etc.) +ofdmflexframegen ofdmflexframegen_create(unsigned int _M, + unsigned int _cp_len, + unsigned int _taper_len, + unsigned char * _p, + ofdmflexframegenprops_s * _fgprops); + +// destroy ofdmflexframegen object +int ofdmflexframegen_destroy(ofdmflexframegen _q); + +// print parameters, properties, etc. +int ofdmflexframegen_print(ofdmflexframegen _q); + +// reset ofdmflexframegen object internals +int ofdmflexframegen_reset(ofdmflexframegen _q); + +// is frame assembled? +int ofdmflexframegen_is_assembled(ofdmflexframegen _q); + +// get properties +int ofdmflexframegen_getprops(ofdmflexframegen _q, + ofdmflexframegenprops_s * _props); + +// set properties +int ofdmflexframegen_setprops(ofdmflexframegen _q, + ofdmflexframegenprops_s * _props); + +// set user-defined header length +int ofdmflexframegen_set_header_len(ofdmflexframegen _q, + unsigned int _len); + +int ofdmflexframegen_set_header_props(ofdmflexframegen _q, + ofdmflexframegenprops_s * _props); + +// get length of frame (symbols) +// _q : OFDM frame generator object +unsigned int ofdmflexframegen_getframelen(ofdmflexframegen _q); + +// assemble a frame from an array of data (NULL pointers will use random data) +// _q : OFDM frame generator object +// _header : frame header [8 bytes] +// _payload : payload data [size: _payload_len x 1] +// _payload_len : payload data length +int ofdmflexframegen_assemble(ofdmflexframegen _q, + const unsigned char * _header, + const unsigned char * _payload, + unsigned int _payload_len); + +// write samples of assembled frame +// _q : OFDM frame generator object +// _buf : output buffer [size: _buf_len x 1] +// _buf_len : output buffer length +int ofdmflexframegen_write(ofdmflexframegen _q, + liquid_float_complex * _buf, + unsigned int _buf_len); + +// +// OFDM flex frame synchronizer +// + +typedef struct ofdmflexframesync_s * ofdmflexframesync; + +// create OFDM flexible framing synchronizer object +// _M : number of subcarriers +// _cp_len : cyclic prefix length +// _taper_len : taper length (OFDM symbol overlap) +// _p : subcarrier allocation (null, pilot, data), [size: _M x 1] +// _callback : user-defined callback function +// _userdata : user-defined data pointer +ofdmflexframesync ofdmflexframesync_create(unsigned int _M, + unsigned int _cp_len, + unsigned int _taper_len, + unsigned char * _p, + framesync_callback _callback, + void * _userdata); + +int ofdmflexframesync_destroy(ofdmflexframesync _q); +int ofdmflexframesync_print(ofdmflexframesync _q); +// set user-defined header length +int ofdmflexframesync_set_header_len(ofdmflexframesync _q, + unsigned int _len); + +int ofdmflexframesync_decode_header_soft(ofdmflexframesync _q, + int _soft); + +int ofdmflexframesync_decode_payload_soft(ofdmflexframesync _q, + int _soft); + +int ofdmflexframesync_set_header_props(ofdmflexframesync _q, + ofdmflexframegenprops_s * _props); + +int ofdmflexframesync_reset(ofdmflexframesync _q); +int ofdmflexframesync_is_frame_open(ofdmflexframesync _q); +int ofdmflexframesync_execute(ofdmflexframesync _q, + liquid_float_complex * _x, + unsigned int _n); + +// query the received signal strength indication +float ofdmflexframesync_get_rssi(ofdmflexframesync _q); + +// query the received carrier offset estimate +float ofdmflexframesync_get_cfo(ofdmflexframesync _q); + +// frame data statistics +int ofdmflexframesync_reset_framedatastats(ofdmflexframesync _q); +framedatastats_s ofdmflexframesync_get_framedatastats (ofdmflexframesync _q); + +// set the received carrier offset estimate +int ofdmflexframesync_set_cfo(ofdmflexframesync _q, float _cfo); + +// enable/disable debugging +int ofdmflexframesync_debug_enable(ofdmflexframesync _q); +int ofdmflexframesync_debug_disable(ofdmflexframesync _q); +int ofdmflexframesync_debug_print(ofdmflexframesync _q, + const char * _filename); + + + +// +// Binary P/N synchronizer +// +#define LIQUID_BSYNC_MANGLE_RRRF(name) LIQUID_CONCAT(bsync_rrrf,name) +#define LIQUID_BSYNC_MANGLE_CRCF(name) LIQUID_CONCAT(bsync_crcf,name) +#define LIQUID_BSYNC_MANGLE_CCCF(name) LIQUID_CONCAT(bsync_cccf,name) + +// Macro: +// BSYNC : name-mangling macro +// TO : output data type +// TC : coefficients data type +// TI : input data type +#define LIQUID_BSYNC_DEFINE_API(BSYNC,TO,TC,TI) \ + \ +/* Binary P/N synchronizer */ \ +typedef struct BSYNC(_s) * BSYNC(); \ + \ +/* Create bsync object */ \ +/* _n : sequence length */ \ +/* _v : correlation sequence [size: _n x 1] */ \ +BSYNC() BSYNC(_create)(unsigned int _n, \ + TC * _v); \ + \ +/* Create binary synchronizer from m-sequence */ \ +/* _g : m-sequence generator polynomial */ \ +/* _k : samples/symbol (over-sampling factor) */ \ +BSYNC() BSYNC(_create_msequence)(unsigned int _g, \ + unsigned int _k); \ + \ +/* Destroy binary synchronizer object, freeing all internal memory */ \ +/* _q : bsync object */ \ +void BSYNC(_destroy)(BSYNC() _q); \ + \ +/* Print object internals to stdout */ \ +/* _q : bsync object */ \ +void BSYNC(_print)(BSYNC() _q); \ + \ +/* Correlate input signal against internal sequence */ \ +/* _q : bsync object */ \ +/* _x : input sample */ \ +/* _y : pointer to output sample */ \ +void BSYNC(_correlate)(BSYNC() _q, \ + TI _x, \ + TO * _y); \ + +LIQUID_BSYNC_DEFINE_API(LIQUID_BSYNC_MANGLE_RRRF, + float, + float, + float) + +LIQUID_BSYNC_DEFINE_API(LIQUID_BSYNC_MANGLE_CRCF, + liquid_float_complex, + float, + liquid_float_complex) + +LIQUID_BSYNC_DEFINE_API(LIQUID_BSYNC_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + + +// +// Pre-demodulation synchronizers (binary and otherwise) +// +#define LIQUID_PRESYNC_MANGLE_CCCF(name) LIQUID_CONCAT( presync_cccf,name) +#define LIQUID_BPRESYNC_MANGLE_CCCF(name) LIQUID_CONCAT(bpresync_cccf,name) + +// Macro: +// PRESYNC : name-mangling macro +// TO : output data type +// TC : coefficients data type +// TI : input data type +#define LIQUID_PRESYNC_DEFINE_API(PRESYNC,TO,TC,TI) \ + \ +/* Pre-demodulation signal synchronizer */ \ +typedef struct PRESYNC(_s) * PRESYNC(); \ + \ +/* Create pre-demod synchronizer from external sequence */ \ +/* _v : baseband sequence, [size: _n x 1] */ \ +/* _n : baseband sequence length, _n > 0 */ \ +/* _dphi_max : maximum absolute frequency deviation for detection */ \ +/* _m : number of correlators, _m > 0 */ \ +PRESYNC() PRESYNC(_create)(TC * _v, \ + unsigned int _n, \ + float _dphi_max, \ + unsigned int _m); \ + \ +/* Destroy pre-demod synchronizer, freeing all internal memory */ \ +int PRESYNC(_destroy)(PRESYNC() _q); \ + \ +/* Print pre-demod synchronizer internal state */ \ +int PRESYNC(_print)(PRESYNC() _q); \ + \ +/* Reset pre-demod synchronizer internal state */ \ +int PRESYNC(_reset)(PRESYNC() _q); \ + \ +/* Push input sample into pre-demod synchronizer */ \ +/* _q : pre-demod synchronizer object */ \ +/* _x : input sample */ \ +int PRESYNC(_push)(PRESYNC() _q, \ + TI _x); \ + \ +/* Correlate original sequence with internal input buffer */ \ +/* _q : pre-demod synchronizer object */ \ +/* _rxy : output cross correlation */ \ +/* _dphi_hat : output frequency offset estimate */ \ +int PRESYNC(_execute)(PRESYNC() _q, \ + TO * _rxy, \ + float * _dphi_hat); \ + +// non-binary pre-demodulation synchronizer +LIQUID_PRESYNC_DEFINE_API(LIQUID_PRESYNC_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + +// binary pre-demodulation synchronizer +LIQUID_PRESYNC_DEFINE_API(LIQUID_BPRESYNC_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + +// +// Frame detector +// + +typedef struct qdetector_cccf_s * qdetector_cccf; + +// create detector with generic sequence +// _s : sample sequence +// _s_len : length of sample sequence +qdetector_cccf qdetector_cccf_create(liquid_float_complex * _s, + unsigned int _s_len); + +// create detector from sequence of symbols using internal linear interpolator +// _sequence : symbol sequence +// _sequence_len : length of symbol sequence +// _ftype : filter prototype (e.g. LIQUID_FIRFILT_RRC) +// _k : samples/symbol +// _m : filter delay +// _beta : excess bandwidth factor +qdetector_cccf qdetector_cccf_create_linear(liquid_float_complex * _sequence, + unsigned int _sequence_len, + int _ftype, + unsigned int _k, + unsigned int _m, + float _beta); + +// create detector from sequence of GMSK symbols +// _sequence : bit sequence +// _sequence_len : length of bit sequence +// _k : samples/symbol +// _m : filter delay +// _beta : excess bandwidth factor +qdetector_cccf qdetector_cccf_create_gmsk(unsigned char * _sequence, + unsigned int _sequence_len, + unsigned int _k, + unsigned int _m, + float _beta); + +// create detector from sequence of CP-FSK symbols (assuming one bit/symbol) +// _sequence : bit sequence +// _sequence_len : length of bit sequence +// _bps : bits per symbol, 0 < _bps <= 8 +// _h : modulation index, _h > 0 +// _k : samples/symbol +// _m : filter delay +// _beta : filter bandwidth parameter, _beta > 0 +// _type : filter type (e.g. LIQUID_CPFSK_SQUARE) +qdetector_cccf qdetector_cccf_create_cpfsk(unsigned char * _sequence, + unsigned int _sequence_len, + unsigned int _bps, + float _h, + unsigned int _k, + unsigned int _m, + float _beta, + int _type); + +int qdetector_cccf_destroy(qdetector_cccf _q); +int qdetector_cccf_print (qdetector_cccf _q); +int qdetector_cccf_reset (qdetector_cccf _q); + +// run detector, looking for sequence; return pointer to aligned, buffered samples +void * qdetector_cccf_execute(qdetector_cccf _q, + liquid_float_complex _x); + +// set detection threshold (should be between 0 and 1, good starting point is 0.5) +int qdetector_cccf_set_threshold(qdetector_cccf _q, + float _threshold); + +// set carrier offset search range +int qdetector_cccf_set_range(qdetector_cccf _q, + float _dphi_max); + +// access methods +unsigned int qdetector_cccf_get_seq_len (qdetector_cccf _q); // sequence length +const void * qdetector_cccf_get_sequence(qdetector_cccf _q); // pointer to sequence +unsigned int qdetector_cccf_get_buf_len (qdetector_cccf _q); // buffer length +float qdetector_cccf_get_rxy (qdetector_cccf _q); // correlator output +float qdetector_cccf_get_tau (qdetector_cccf _q); // fractional timing offset estimate +float qdetector_cccf_get_gamma (qdetector_cccf _q); // channel gain +float qdetector_cccf_get_dphi (qdetector_cccf _q); // carrier frequency offset estimate +float qdetector_cccf_get_phi (qdetector_cccf _q); // carrier phase offset estimate + +// +// Pre-demodulation detector +// + +typedef struct detector_cccf_s * detector_cccf; + +// create pre-demod detector +// _s : sequence +// _n : sequence length +// _threshold : detection threshold (default: 0.7) +// _dphi_max : maximum carrier offset +detector_cccf detector_cccf_create(liquid_float_complex * _s, + unsigned int _n, + float _threshold, + float _dphi_max); + +// destroy pre-demo detector object +void detector_cccf_destroy(detector_cccf _q); + +// print pre-demod detector internal state +void detector_cccf_print(detector_cccf _q); + +// reset pre-demod detector internal state +void detector_cccf_reset(detector_cccf _q); + +// Run sample through pre-demod detector's correlator. +// Returns '1' if signal was detected, '0' otherwise +// _q : pre-demod detector +// _x : input sample +// _tau_hat : fractional sample offset estimate (set when detected) +// _dphi_hat : carrier frequency offset estimate (set when detected) +// _gamma_hat : channel gain estimate (set when detected) +int detector_cccf_correlate(detector_cccf _q, + liquid_float_complex _x, + float * _tau_hat, + float * _dphi_hat, + float * _gamma_hat); + + +// +// symbol streaming for testing (no meaningful data, just symbols) +// +#define LIQUID_SYMSTREAM_MANGLE_CFLOAT(name) LIQUID_CONCAT(symstreamcf,name) + +#define LIQUID_SYMSTREAM_DEFINE_API(SYMSTREAM,TO) \ + \ +/* Symbol streaming generator object */ \ +typedef struct SYMSTREAM(_s) * SYMSTREAM(); \ + \ +/* Create symstream object with default parameters. */ \ +/* This is equivalent to invoking the create_linear() method */ \ +/* with _ftype=LIQUID_FIRFILT_ARKAISER, _k=2, _m=7, _beta=0.3, and */ \ +/* with _ms=LIQUID_MODEM_QPSK */ \ +SYMSTREAM() SYMSTREAM(_create)(void); \ + \ +/* Create symstream object with linear modulation */ \ +/* _ftype : filter type (e.g. LIQUID_FIRFILT_RRC) */ \ +/* _k : samples per symbol, _k >= 2 */ \ +/* _m : filter delay (symbols), _m > 0 */ \ +/* _beta : filter excess bandwidth, 0 < _beta <= 1 */ \ +/* _ms : modulation scheme, e.g. LIQUID_MODEM_QPSK */ \ +SYMSTREAM() SYMSTREAM(_create_linear)(int _ftype, \ + unsigned int _k, \ + unsigned int _m, \ + float _beta, \ + int _ms); \ + \ +/* Destroy symstream object, freeing all internal memory */ \ +int SYMSTREAM(_destroy)(SYMSTREAM() _q); \ + \ +/* Print symstream object's parameters */ \ +int SYMSTREAM(_print)(SYMSTREAM() _q); \ + \ +/* Reset symstream internal state */ \ +int SYMSTREAM(_reset)(SYMSTREAM() _q); \ + \ +/* Set internal linear modulation scheme, leaving the filter parameters */ \ +/* (interpolator) unmodified */ \ +int SYMSTREAM(_set_scheme)(SYMSTREAM() _q, \ + int _ms); \ + \ +/* Get internal linear modulation scheme */ \ +int SYMSTREAM(_get_scheme)(SYMSTREAM() _q); \ + \ +/* Set internal linear gain (before interpolation) */ \ +int SYMSTREAM(_set_gain)(SYMSTREAM() _q, \ + float _gain); \ + \ +/* Get internal linear gain (before interpolation) */ \ +float SYMSTREAM(_get_gain)(SYMSTREAM() _q); \ + \ +/* Write block of samples to output buffer */ \ +/* _q : synchronizer object */ \ +/* _buf : output buffer [size: _buf_len x 1] */ \ +/* _buf_len: output buffer size */ \ +int SYMSTREAM(_write_samples)(SYMSTREAM() _q, \ + TO * _buf, \ + unsigned int _buf_len); \ + +LIQUID_SYMSTREAM_DEFINE_API(LIQUID_SYMSTREAM_MANGLE_CFLOAT, liquid_float_complex) + + + +// +// multi-signal source for testing (no meaningful data, just signals) +// + +#define LIQUID_MSOURCE_MANGLE_CFLOAT(name) LIQUID_CONCAT(msourcecf,name) + +#define LIQUID_MSOURCE_DEFINE_API(MSOURCE,TO) \ + \ +/* Multi-signal source generator object */ \ +typedef struct MSOURCE(_s) * MSOURCE(); \ + \ +/* Create msource object by specifying channelizer parameters */ \ +/* _M : number of channels in analysis channelizer object */ \ +/* _m : prototype channelizer filter semi-length */ \ +/* _As : prototype channelizer filter stop-band suppression (dB) */ \ +MSOURCE() MSOURCE(_create)(unsigned int _M, \ + unsigned int _m, \ + float _As); \ + \ +/* Create default msource object with default parameters: */ \ +/* M = 1200, m = 4, As = 60 */ \ +MSOURCE() MSOURCE(_create_default)(void); \ + \ +/* Destroy msource object */ \ +int MSOURCE(_destroy)(MSOURCE() _q); \ + \ +/* Print msource object */ \ +int MSOURCE(_print)(MSOURCE() _q); \ + \ +/* Reset msource object */ \ +int MSOURCE(_reset)(MSOURCE() _q); \ + \ +/* user-defined callback for generating samples */ \ +typedef int (*MSOURCE(_callback))(void * _userdata, \ + TO * _v, \ + unsigned int _n); \ + \ +/* Add user-defined signal generator */ \ +int MSOURCE(_add_user)(MSOURCE() _q, \ + float _fc, \ + float _bw, \ + float _gain, \ + void * _userdata, \ + MSOURCE(_callback) _callback); \ + \ +/* Add tone to signal generator, returning id of signal */ \ +int MSOURCE(_add_tone)(MSOURCE() _q, \ + float _fc, \ + float _bw, \ + float _gain); \ + \ +/* Add chirp to signal generator, returning id of signal */ \ +/* _q : multi-signal source object */ \ +/* _duration : duration of chirp [samples] */ \ +/* _negate : negate frequency direction */ \ +/* _single : run single chirp? or repeatedly */ \ +int MSOURCE(_add_chirp)(MSOURCE() _q, \ + float _fc, \ + float _bw, \ + float _gain, \ + float _duration, \ + int _negate, \ + int _repeat); \ + \ +/* Add noise source to signal generator, returning id of signal */ \ +/* _q : multi-signal source object */ \ +/* _fc : ... */ \ +/* _bw : ... */ \ +/* _nstd : ... */ \ +int MSOURCE(_add_noise)(MSOURCE() _q, \ + float _fc, \ + float _bw, \ + float _gain); \ + \ +/* Add modem signal source, returning id of signal */ \ +/* _q : multi-signal source object */ \ +/* _ms : modulation scheme, e.g. LIQUID_MODEM_QPSK */ \ +/* _m : filter delay (symbols), _m > 0 */ \ +/* _beta : filter excess bandwidth, 0 < _beta <= 1 */ \ +int MSOURCE(_add_modem)(MSOURCE() _q, \ + float _fc, \ + float _bw, \ + float _gain, \ + int _ms, \ + unsigned int _m, \ + float _beta); \ + \ +/* Add frequency-shift keying modem signal source, returning id of */ \ +/* signal */ \ +/* _q : multi-signal source object */ \ +/* _m : bits per symbol, _bps > 0 */ \ +/* _k : samples/symbol, _k >= 2^_m */ \ +int MSOURCE(_add_fsk)(MSOURCE() _q, \ + float _fc, \ + float _bw, \ + float _gain, \ + unsigned int _m, \ + unsigned int _k); \ + \ +/* Add GMSK modem signal source, returning id of signal */ \ +/* _q : multi-signal source object */ \ +/* _m : filter delay (symbols), _m > 0 */ \ +/* _bt : filter bandwidth-time factor, 0 < _bt <= 1 */ \ +int MSOURCE(_add_gmsk)(MSOURCE() _q, \ + float _fc, \ + float _bw, \ + float _gain, \ + unsigned int _m, \ + float _bt); \ + \ +/* Remove signal with a particular id, returning 0 upon success */ \ +/* _q : multi-signal source object */ \ +/* _id : signal source id */ \ +int MSOURCE(_remove)(MSOURCE() _q, \ + int _id); \ + \ +/* Enable signal source with a particular id */ \ +int MSOURCE(_enable)(MSOURCE() _q, \ + int _id); \ + \ +/* Disable signal source with a particular id */ \ +int MSOURCE(_disable)(MSOURCE() _q, \ + int _id); \ + \ +/* Set gain in decibels on signal */ \ +/* _q : msource object */ \ +/* _id : source id */ \ +/* _gain : signal gain [dB] */ \ +int MSOURCE(_set_gain)(MSOURCE() _q, \ + int _id, \ + float _gain); \ + \ +/* Get gain in decibels on signal */ \ +/* _q : msource object */ \ +/* _id : source id */ \ +/* _gain : signal gain output [dB] */ \ +int MSOURCE(_get_gain)(MSOURCE() _q, \ + int _id, \ + float * _gain); \ + \ +/* Get number of samples generated by the object so far */ \ +/* _q : msource object */ \ +/* _return : number of time-domain samples generated */ \ +unsigned long long int MSOURCE(_get_num_samples)(MSOURCE() _q); \ + \ +/* Set carrier offset to signal */ \ +/* _q : msource object */ \ +/* _id : source id */ \ +/* _fc : normalized carrier frequency offset, -0.5 <= _fc <= 0.5 */ \ +int MSOURCE(_set_frequency)(MSOURCE() _q, \ + int _id, \ + float _dphi); \ + \ +/* Get carrier offset to signal */ \ +/* _q : msource object */ \ +/* _id : source id */ \ +/* _fc : normalized carrier frequency offset */ \ +int MSOURCE(_get_frequency)(MSOURCE() _q, \ + int _id, \ + float * _dphi); \ + \ +/* Write block of samples to output buffer */ \ +/* _q : synchronizer object */ \ +/* _buf : output buffer, [size: _buf_len x 1] */ \ +/* _buf_len: output buffer size */ \ +int MSOURCE(_write_samples)(MSOURCE() _q, \ + TO * _buf, \ + unsigned int _buf_len); \ + +LIQUID_MSOURCE_DEFINE_API(LIQUID_MSOURCE_MANGLE_CFLOAT, liquid_float_complex) + + + + +// +// Symbol tracking: AGC > symsync > EQ > carrier recovery +// +#define LIQUID_SYMTRACK_MANGLE_RRRF(name) LIQUID_CONCAT(symtrack_rrrf,name) +#define LIQUID_SYMTRACK_MANGLE_CCCF(name) LIQUID_CONCAT(symtrack_cccf,name) + +// large macro +// SYMTRACK : name-mangling macro +// T : data type, primitive +// TO : data type, output +// TC : data type, coefficients +// TI : data type, input +#define LIQUID_SYMTRACK_DEFINE_API(SYMTRACK,T,TO,TC,TI) \ + \ +/* Symbol synchronizer and tracking object */ \ +typedef struct SYMTRACK(_s) * SYMTRACK(); \ + \ +/* Create symtrack object, specifying parameters for operation */ \ +/* _ftype : filter type (e.g. LIQUID_FIRFILT_RRC) */ \ +/* _k : samples per symbol, _k >= 2 */ \ +/* _m : filter delay [symbols], _m > 0 */ \ +/* _beta : excess bandwidth factor, 0 <= _beta <= 1 */ \ +/* _ms : modulation scheme, _ms(LIQUID_MODEM_BPSK) */ \ +SYMTRACK() SYMTRACK(_create)(int _ftype, \ + unsigned int _k, \ + unsigned int _m, \ + float _beta, \ + int _ms); \ + \ +/* Create symtrack object using default parameters. */ \ +/* The default parameters are */ \ +/* ftype = LIQUID_FIRFILT_ARKAISER (filter type), */ \ +/* k = 2 (samples per symbol), */ \ +/* m = 7 (filter delay), */ \ +/* beta = 0.3 (excess bandwidth factor), and */ \ +/* ms = LIQUID_MODEM_QPSK (modulation scheme) */ \ +SYMTRACK() SYMTRACK(_create_default)(); \ + \ +/* Destroy symtrack object, freeing all internal memory */ \ +int SYMTRACK(_destroy)(SYMTRACK() _q); \ + \ +/* Print symtrack object's parameters */ \ +int SYMTRACK(_print)(SYMTRACK() _q); \ + \ +/* Reset symtrack internal state */ \ +int SYMTRACK(_reset)(SYMTRACK() _q); \ + \ +/* Set symtrack modulation scheme */ \ +/* _q : symtrack object */ \ +/* _ms : modulation scheme, _ms(LIQUID_MODEM_BPSK) */ \ +int SYMTRACK(_set_modscheme)(SYMTRACK() _q, \ + int _ms); \ + \ +/* Set symtrack internal bandwidth */ \ +/* _q : symtrack object */ \ +/* _bw : tracking bandwidth, _bw > 0 */ \ +int SYMTRACK(_set_bandwidth)(SYMTRACK() _q, \ + float _bw); \ + \ +/* Adjust internal NCO by requested phase */ \ +/* _q : symtrack object */ \ +/* _dphi : NCO phase adjustment [radians] */ \ +int SYMTRACK(_adjust_phase)(SYMTRACK() _q, \ + T _dphi); \ + \ +/* Set symtrack equalization strategy to constant modulus (default) */ \ +int SYMTRACK(_set_eq_cm)(SYMTRACK() _q); \ + \ +/* Set symtrack equalization strategy to decision directed */ \ +int SYMTRACK(_set_eq_dd)(SYMTRACK() _q); \ + \ +/* Disable symtrack equalization */ \ +int SYMTRACK(_set_eq_off)(SYMTRACK() _q); \ + \ +/* Execute synchronizer on single input sample */ \ +/* _q : synchronizer object */ \ +/* _x : input data sample */ \ +/* _y : output data array, [size: 2 x 1] */ \ +/* _ny : number of samples written to output buffer (0, 1, or 2) */ \ +int SYMTRACK(_execute)(SYMTRACK() _q, \ + TI _x, \ + TO * _y, \ + unsigned int * _ny); \ + \ +/* execute synchronizer on input data array */ \ +/* _q : synchronizer object */ \ +/* _x : input data array */ \ +/* _nx : number of input samples */ \ +/* _y : output data array, [size: 2 _nx x 1] */ \ +/* _ny : number of samples written to output buffer */ \ +int SYMTRACK(_execute_block)(SYMTRACK() _q, \ + TI * _x, \ + unsigned int _nx, \ + TO * _y, \ + unsigned int * _ny); \ + +LIQUID_SYMTRACK_DEFINE_API(LIQUID_SYMTRACK_MANGLE_RRRF, + float, + float, + float, + float) + +LIQUID_SYMTRACK_DEFINE_API(LIQUID_SYMTRACK_MANGLE_CCCF, + float, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + + + +// +// MODULE : math +// + +// ln( Gamma(z) ) +float liquid_lngammaf(float _z); + +// Gamma(z) +float liquid_gammaf(float _z); + +// ln( gamma(z,alpha) ) : lower incomplete gamma function +float liquid_lnlowergammaf(float _z, float _alpha); + +// ln( Gamma(z,alpha) ) : upper incomplete gamma function +float liquid_lnuppergammaf(float _z, float _alpha); + +// gamma(z,alpha) : lower incomplete gamma function +float liquid_lowergammaf(float _z, float _alpha); + +// Gamma(z,alpha) : upper incomplete gamma function +float liquid_uppergammaf(float _z, float _alpha); + +// n! +float liquid_factorialf(unsigned int _n); + + + +// ln(I_v(z)) : log Modified Bessel function of the first kind +float liquid_lnbesselif(float _nu, float _z); + +// I_v(z) : Modified Bessel function of the first kind +float liquid_besselif(float _nu, float _z); + +// I_0(z) : Modified Bessel function of the first kind (order zero) +float liquid_besseli0f(float _z); + +// J_v(z) : Bessel function of the first kind +float liquid_besseljf(float _nu, float _z); + +// J_0(z) : Bessel function of the first kind (order zero) +float liquid_besselj0f(float _z); + + +// Q function +float liquid_Qf(float _z); + +// Marcum Q-function +float liquid_MarcumQf(int _M, + float _alpha, + float _beta); + +// Marcum Q-function (M=1) +float liquid_MarcumQ1f(float _alpha, + float _beta); + +// sin(pi x) / (pi x) +float sincf(float _x); + +// next power of 2 : y = ceil(log2(_x)) +unsigned int liquid_nextpow2(unsigned int _x); + +// (n choose k) = n! / ( k! (n-k)! ) +float liquid_nchoosek(unsigned int _n, unsigned int _k); + +// +// Windowing functions +// + +// number of window functions available, including "unknown" type +#define LIQUID_WINDOW_NUM_FUNCTIONS (10) + +// prototypes +typedef enum { + LIQUID_WINDOW_UNKNOWN=0, // unknown/unsupported scheme + + LIQUID_WINDOW_HAMMING, // Hamming + LIQUID_WINDOW_HANN, // Hann + LIQUID_WINDOW_BLACKMANHARRIS, // Blackman-harris (4-term) + LIQUID_WINDOW_BLACKMANHARRIS7, // Blackman-harris (7-term) + LIQUID_WINDOW_KAISER, // Kaiser (beta factor unspecified) + LIQUID_WINDOW_FLATTOP, // flat top (includes negative values) + LIQUID_WINDOW_TRIANGULAR, // triangular + LIQUID_WINDOW_RCOSTAPER, // raised-cosine taper (taper size unspecified) + LIQUID_WINDOW_KBD, // Kaiser-Bessel derived window (beta factor unspecified) +} liquid_window_type; + +// pretty names for window +extern const char * liquid_window_str[LIQUID_WINDOW_NUM_FUNCTIONS][2]; + +// Print compact list of existing and available windowing functions +void liquid_print_windows(); + +// returns window type based on input string +liquid_window_type liquid_getopt_str2window(const char * _str); + +// generic window function given type +// _type : window type, e.g. LIQUID_WINDOW_KAISER +// _i : window index, _i in [0,_wlen-1] +// _wlen : length of window +// _arg : window-specific argument, if required +float liquid_windowf(liquid_window_type _type, + unsigned int _i, + unsigned int _wlen, + float _arg); + +// Kaiser window +// _i : window index, _i in [0,_wlen-1] +// _wlen : full window length +// _beta : Kaiser-Bessel window shape parameter +float liquid_kaiser(unsigned int _i, + unsigned int _wlen, + float _beta); + +// Hamming window +// _i : window index, _i in [0,_wlen-1] +// _wlen : full window length +float liquid_hamming(unsigned int _i, + unsigned int _wlen); + +// Hann window +// _i : window index, _i in [0,_wlen-1] +// _wlen : full window length +float liquid_hann(unsigned int _i, + unsigned int _wlen); + +// Blackman-harris window +// _i : window index, _i in [0,_wlen-1] +// _wlen : full window length +float liquid_blackmanharris(unsigned int _i, + unsigned int _wlen); + +// 7th order Blackman-harris window +// _i : window index, _i in [0,_wlen-1] +// _wlen : full window length +float liquid_blackmanharris7(unsigned int _i, + unsigned int _wlen); + +// Flat-top window +// _i : window index, _i in [0,_wlen-1] +// _wlen : full window length +float liquid_flattop(unsigned int _i, + unsigned int _wlen); + +// Triangular window +// _i : window index, _i in [0,_wlen-1] +// _wlen : full window length +// _L : triangle length, _L in {_wlen-1, _wlen, _wlen+1} +float liquid_triangular(unsigned int _i, + unsigned int _wlen, + unsigned int _L); + +// raised-cosine tapering window +// _i : window index +// _wlen : full window length +// _t : taper length, _t in [0,_wlen/2] +float liquid_rcostaper_window(unsigned int _i, + unsigned int _wlen, + unsigned int _t); + +// Kaiser-Bessel derived window (single sample) +// _i : window index, _i in [0,_wlen-1] +// _wlen : length of filter (must be even) +// _beta : Kaiser window parameter (_beta > 0) +float liquid_kbd(unsigned int _i, + unsigned int _wlen, + float _beta); + +// Kaiser-Bessel derived window (full window) +// _wlen : full window length (must be even) +// _beta : Kaiser window parameter (_beta > 0) +// _w : window output buffer, [size: _wlen x 1] +int liquid_kbd_window(unsigned int _wlen, + float _beta, + float * _w); + + +// polynomials + + +#define LIQUID_POLY_MANGLE_DOUBLE(name) LIQUID_CONCAT(poly, name) +#define LIQUID_POLY_MANGLE_FLOAT(name) LIQUID_CONCAT(polyf, name) + +#define LIQUID_POLY_MANGLE_CDOUBLE(name) LIQUID_CONCAT(polyc, name) +#define LIQUID_POLY_MANGLE_CFLOAT(name) LIQUID_CONCAT(polycf, name) + +// large macro +// POLY : name-mangling macro +// T : data type +// TC : data type (complex) +#define LIQUID_POLY_DEFINE_API(POLY,T,TC) \ + \ +/* Evaluate polynomial _p at value _x */ \ +/* _p : polynomial coefficients [size _k x 1] */ \ +/* _k : polynomial coefficients length, order is _k - 1 */ \ +/* _x : input to evaluate polynomial */ \ +T POLY(_val)(T * _p, \ + unsigned int _k, \ + T _x); \ + \ +/* Perform least-squares polynomial fit on data set */ \ +/* _x : x-value sample set [size: _n x 1] */ \ +/* _y : y-value sample set [size: _n x 1] */ \ +/* _n : number of samples in _x and _y */ \ +/* _p : polynomial coefficients output [size _k x 1] */ \ +/* _k : polynomial coefficients length, order is _k - 1 */ \ +int POLY(_fit)(T * _x, \ + T * _y, \ + unsigned int _n, \ + T * _p, \ + unsigned int _k); \ + \ +/* Perform Lagrange polynomial exact fit on data set */ \ +/* _x : x-value sample set, size [_n x 1] */ \ +/* _y : y-value sample set, size [_n x 1] */ \ +/* _n : number of samples in _x and _y */ \ +/* _p : polynomial coefficients output [size _n x 1] */ \ +int POLY(_fit_lagrange)(T * _x, \ + T * _y, \ + unsigned int _n, \ + T * _p); \ + \ +/* Perform Lagrange polynomial interpolation on data set without */ \ +/* computing coefficients as an intermediate step. */ \ +/* _x : x-value sample set [size: _n x 1] */ \ +/* _y : y-value sample set [size: _n x 1] */ \ +/* _n : number of samples in _x and _y */ \ +/* _x0 : x-value to evaluate and compute interpolant */ \ +T POLY(_interp_lagrange)(T * _x, \ + T * _y, \ + unsigned int _n, \ + T _x0); \ + \ +/* Compute Lagrange polynomial fit in the barycentric form. */ \ +/* _x : x-value sample set, size [_n x 1] */ \ +/* _n : number of samples in _x */ \ +/* _w : barycentric weights normalized so _w[0]=1, size [_n x 1] */ \ +int POLY(_fit_lagrange_barycentric)(T * _x, \ + unsigned int _n, \ + T * _w); \ + \ +/* Perform Lagrange polynomial interpolation using the barycentric form */ \ +/* of the weights. */ \ +/* _x : x-value sample set [size: _n x 1] */ \ +/* _y : y-value sample set [size: _n x 1] */ \ +/* _w : barycentric weights [size: _n x 1] */ \ +/* _x0 : x-value to evaluate and compute interpolant */ \ +/* _n : number of samples in _x, _y, and _w */ \ +T POLY(_val_lagrange_barycentric)(T * _x, \ + T * _y, \ + T * _w, \ + T _x0, \ + unsigned int _n); \ + \ +/* Perform binomial expansion on the polynomial */ \ +/* \( P_n(x) = (1+x)^n \) */ \ +/* as */ \ +/* \( P_n(x) = p[0] + p[1]x + p[2]x^2 + ... + p[n]x^n \) */ \ +/* NOTE: _p has order n (coefficients has length n+1) */ \ +/* _n : polynomial order */ \ +/* _p : polynomial coefficients [size: _n+1 x 1] */ \ +int POLY(_expandbinomial)(unsigned int _n, \ + T * _p); \ + \ +/* Perform positive/negative binomial expansion on the polynomial */ \ +/* \( P_n(x) = (1+x)^m (1-x)^k \) */ \ +/* as */ \ +/* \( P_n(x) = p[0] + p[1]x + p[2]x^2 + ... + p[n]x^n \) */ \ +/* NOTE: _p has order n=m+k (array is length n+1) */ \ +/* _m : number of '1+x' terms */ \ +/* _k : number of '1-x' terms */ \ +/* _p : polynomial coefficients [size: _m+_k+1 x 1] */ \ +int POLY(_expandbinomial_pm)(unsigned int _m, \ + unsigned int _k, \ + T * _p); \ + \ +/* Perform root expansion on the polynomial */ \ +/* \( P_n(x) = (x-r[0]) (x-r[1]) ... (x-r[n-1]) \) */ \ +/* as */ \ +/* \( P_n(x) = p[0] + p[1]x + ... + p[n]x^n \) */ \ +/* where \( r[0],r[1],...,r[n-1]\) are the roots of \( P_n(x) \). */ \ +/* NOTE: _p has order _n (array is length _n+1) */ \ +/* _r : roots of polynomial [size: _n x 1] */ \ +/* _n : number of roots in polynomial */ \ +/* _p : polynomial coefficients [size: _n+1 x 1] */ \ +int POLY(_expandroots)(T * _r, \ + unsigned int _n, \ + T * _p); \ + \ +/* Perform root expansion on the polynomial */ \ +/* \( P_n(x) = (xb[0]-a[0]) (xb[1]-a[1])...(xb[n-1]-a[n-1]) \) */ \ +/* as */ \ +/* \( P_n(x) = p[0] + p[1]x + ... + p[n]x^n \) */ \ +/* NOTE: _p has order _n (array is length _n+1) */ \ +/* _a : subtractant of polynomial rotos [size: _n x 1] */ \ +/* _b : multiplicant of polynomial roots [size: _n x 1] */ \ +/* _n : number of roots in polynomial */ \ +/* _p : polynomial coefficients [size: _n+1 x 1] */ \ +int POLY(_expandroots2)(T * _a, \ + T * _b, \ + unsigned int _n, \ + T * _p); \ + \ +/* Find the complex roots of a polynomial. */ \ +/* _p : polynomial coefficients [size: _n x 1] */ \ +/* _k : polynomial length */ \ +/* _roots : resulting complex roots [size: _k-1 x 1] */ \ +int POLY(_findroots)(T * _poly, \ + unsigned int _n, \ + TC * _roots); \ + \ +/* Find the complex roots of the polynomial using the Durand-Kerner */ \ +/* method */ \ +/* _p : polynomial coefficients [size: _n x 1] */ \ +/* _k : polynomial length */ \ +/* _roots : resulting complex roots [size: _k-1 x 1] */ \ +int POLY(_findroots_durandkerner)(T * _p, \ + unsigned int _k, \ + TC * _roots); \ + \ +/* Find the complex roots of the polynomial using Bairstow's method. */ \ +/* _p : polynomial coefficients [size: _n x 1] */ \ +/* _k : polynomial length */ \ +/* _roots : resulting complex roots [size: _k-1 x 1] */ \ +int POLY(_findroots_bairstow)(T * _p, \ + unsigned int _k, \ + TC * _roots); \ + \ +/* Expand the multiplication of two polynomials */ \ +/* \( ( a[0] + a[1]x + a[2]x^2 + ...) (b[0] + b[1]x + b[]x^2 + ...) \) */ \ +/* as */ \ +/* \( c[0] + c[1]x + c[2]x^2 + ... + c[n]x^n \) */ \ +/* where order(c) = order(a) + order(b) + 1 */ \ +/* and therefore length(c) = length(a) + length(b) - 1 */ \ +/* _a : 1st polynomial coefficients (length is _order_a+1) */ \ +/* _order_a : 1st polynomial order */ \ +/* _b : 2nd polynomial coefficients (length is _order_b+1) */ \ +/* _order_b : 2nd polynomial order */ \ +/* _c : output polynomial [size: _order_a+_order_b+1 x 1] */ \ +int POLY(_mul)(T * _a, \ + unsigned int _order_a, \ + T * _b, \ + unsigned int _order_b, \ + T * _c); \ + +LIQUID_POLY_DEFINE_API(LIQUID_POLY_MANGLE_DOUBLE, + double, + liquid_double_complex) + +LIQUID_POLY_DEFINE_API(LIQUID_POLY_MANGLE_FLOAT, + float, + liquid_float_complex) + +LIQUID_POLY_DEFINE_API(LIQUID_POLY_MANGLE_CDOUBLE, + liquid_double_complex, + liquid_double_complex) + +LIQUID_POLY_DEFINE_API(LIQUID_POLY_MANGLE_CFLOAT, + liquid_float_complex, + liquid_float_complex) + +#if 0 +// expands the polynomial: (1+x)^n +void poly_binomial_expand(unsigned int _n, int * _c); + +// expands the polynomial: (1+x)^k * (1-x)^(n-k) +void poly_binomial_expand_pm(unsigned int _n, + unsigned int _k, + int * _c); +#endif + +// +// modular arithmetic, etc. +// + +// maximum number of factors +#define LIQUID_MAX_FACTORS (40) + +// is number prime? +int liquid_is_prime(unsigned int _n); + +// compute number's prime factors +// _n : number to factor +// _factors : pre-allocated array of factors [size: LIQUID_MAX_FACTORS x 1] +// _num_factors: number of factors found, sorted ascending +int liquid_factor(unsigned int _n, + unsigned int * _factors, + unsigned int * _num_factors); + +// compute number's unique prime factors +// _n : number to factor +// _factors : pre-allocated array of factors [size: LIQUID_MAX_FACTORS x 1] +// _num_factors: number of unique factors found, sorted ascending +int liquid_unique_factor(unsigned int _n, + unsigned int * _factors, + unsigned int * _num_factors); + +// compute greatest common divisor between to numbers P and Q +unsigned int liquid_gcd(unsigned int _P, + unsigned int _Q); + +// compute c = base^exp (mod n) +unsigned int liquid_modpow(unsigned int _base, + unsigned int _exp, + unsigned int _n); + +// find smallest primitive root of _n +unsigned int liquid_primitive_root(unsigned int _n); + +// find smallest primitive root of _n, assuming _n is prime +unsigned int liquid_primitive_root_prime(unsigned int _n); + +// Euler's totient function +unsigned int liquid_totient(unsigned int _n); + + +// +// MODULE : matrix +// + +#define LIQUID_MATRIX_MANGLE_DOUBLE(name) LIQUID_CONCAT(matrix, name) +#define LIQUID_MATRIX_MANGLE_FLOAT(name) LIQUID_CONCAT(matrixf, name) + +#define LIQUID_MATRIX_MANGLE_CDOUBLE(name) LIQUID_CONCAT(matrixc, name) +#define LIQUID_MATRIX_MANGLE_CFLOAT(name) LIQUID_CONCAT(matrixcf, name) + +// large macro +// MATRIX : name-mangling macro +// T : data type +#define LIQUID_MATRIX_DEFINE_API(MATRIX,T) \ + \ +/* Print array as matrix to stdout */ \ +/* _x : input matrix, [size: _r x _c] */ \ +/* _r : rows in matrix */ \ +/* _c : columns in matrix */ \ +int MATRIX(_print)(T * _x, \ + unsigned int _r, \ + unsigned int _c); \ + \ +/* Perform point-wise addition between two matrices \(\vec{X}\) */ \ +/* and \(\vec{Y}\), saving the result in the output matrix \(\vec{Z}\). */ \ +/* That is, \(\vec{Z}_{i,j}=\vec{X}_{i,j}+\vec{Y}_{i,j} \), */ \ +/* \( \forall_{i \in r} \) and \( \forall_{j \in c} \) */ \ +/* _x : input matrix, [size: _r x _c] */ \ +/* _y : input matrix, [size: _r x _c] */ \ +/* _z : output matrix, [size: _r x _c] */ \ +/* _r : number of rows in each matrix */ \ +/* _c : number of columns in each matrix */ \ +int MATRIX(_add)(T * _x, \ + T * _y, \ + T * _z, \ + unsigned int _r, \ + unsigned int _c); \ + \ +/* Perform point-wise subtraction between two matrices \(\vec{X}\) */ \ +/* and \(\vec{Y}\), saving the result in the output matrix \(\vec{Z}\) */ \ +/* That is, \(\vec{Z}_{i,j}=\vec{X}_{i,j}-\vec{Y}_{i,j} \), */ \ +/* \( \forall_{i \in r} \) and \( \forall_{j \in c} \) */ \ +/* _x : input matrix, [size: _r x _c] */ \ +/* _y : input matrix, [size: _r x _c] */ \ +/* _z : output matrix, [size: _r x _c] */ \ +/* _r : number of rows in each matrix */ \ +/* _c : number of columns in each matrix */ \ +int MATRIX(_sub)(T * _x, \ + T * _y, \ + T * _z, \ + unsigned int _r, \ + unsigned int _c); \ + \ +/* Perform point-wise multiplication between two matrices \(\vec{X}\) */ \ +/* and \(\vec{Y}\), saving the result in the output matrix \(\vec{Z}\) */ \ +/* That is, \(\vec{Z}_{i,j}=\vec{X}_{i,j} \vec{Y}_{i,j} \), */ \ +/* \( \forall_{i \in r} \) and \( \forall_{j \in c} \) */ \ +/* _x : input matrix, [size: _r x _c] */ \ +/* _y : input matrix, [size: _r x _c] */ \ +/* _z : output matrix, [size: _r x _c] */ \ +/* _r : number of rows in each matrix */ \ +/* _c : number of columns in each matrix */ \ +int MATRIX(_pmul)(T * _x, \ + T * _y, \ + T * _z, \ + unsigned int _r, \ + unsigned int _c); \ + \ +/* Perform point-wise division between two matrices \(\vec{X}\) */ \ +/* and \(\vec{Y}\), saving the result in the output matrix \(\vec{Z}\) */ \ +/* That is, \(\vec{Z}_{i,j}=\vec{X}_{i,j}/\vec{Y}_{i,j} \), */ \ +/* \( \forall_{i \in r} \) and \( \forall_{j \in c} \) */ \ +/* _x : input matrix, [size: _r x _c] */ \ +/* _y : input matrix, [size: _r x _c] */ \ +/* _z : output matrix, [size: _r x _c] */ \ +/* _r : number of rows in each matrix */ \ +/* _c : number of columns in each matrix */ \ +int MATRIX(_pdiv)(T * _x, \ + T * _y, \ + T * _z, \ + unsigned int _r, \ + unsigned int _c); \ + \ +/* Multiply two matrices \(\vec{X}\) and \(\vec{Y}\), storing the */ \ +/* result in \(\vec{Z}\). */ \ +/* NOTE: _rz = _rx, _cz = _cy, and _cx = _ry */ \ +/* _x : input matrix, [size: _rx x _cx] */ \ +/* _rx : number of rows in _x */ \ +/* _cx : number of columns in _x */ \ +/* _y : input matrix, [size: _ry x _cy] */ \ +/* _ry : number of rows in _y */ \ +/* _cy : number of columns in _y */ \ +/* _z : output matrix, [size: _rz x _cz] */ \ +/* _rz : number of rows in _z */ \ +/* _cz : number of columns in _z */ \ +int MATRIX(_mul)(T * _x, unsigned int _rx, unsigned int _cx, \ + T * _y, unsigned int _ry, unsigned int _cy, \ + T * _z, unsigned int _rz, unsigned int _cz); \ + \ +/* Solve \(\vec{X} = \vec{Y} \vec{Z}\) for \(\vec{Z}\) for square */ \ +/* matrices of size \(n\) */ \ +/* _x : input matrix, [size: _n x _n] */ \ +/* _y : input matrix, [size: _n x _n] */ \ +/* _z : output matrix, [size: _n x _n] */ \ +/* _n : number of rows and columns in each matrix */ \ +int MATRIX(_div)(T * _x, \ + T * _y, \ + T * _z, \ + unsigned int _n); \ + \ +/* Compute the determinant of a square matrix \(\vec{X}\) */ \ +/* _x : input matrix, [size: _r x _c] */ \ +/* _r : rows */ \ +/* _c : columns */ \ +T MATRIX(_det)(T * _x, \ + unsigned int _r, \ + unsigned int _c); \ + \ +/* Compute the in-place transpose of the matrix \(\vec{X}\) */ \ +/* _x : input matrix, [size: _r x _c] */ \ +/* _r : rows */ \ +/* _c : columns */ \ +int MATRIX(_trans)(T * _x, \ + unsigned int _r, \ + unsigned int _c); \ + \ +/* Compute the in-place Hermitian transpose of the matrix \(\vec{X}\) */ \ +/* _x : input matrix, [size: _r x _c] */ \ +/* _r : rows */ \ +/* _c : columns */ \ +int MATRIX(_hermitian)(T * _x, \ + unsigned int _r, \ + unsigned int _c); \ + \ +/* Compute \(\vec{X}\vec{X}^T\) on a \(m \times n\) matrix. */ \ +/* The result is a \(m \times m\) matrix. */ \ +/* _x : input matrix, [size: _m x _n] */ \ +/* _m : input rows */ \ +/* _n : input columns */ \ +/* _xxT : output matrix, [size: _m x _m] */ \ +int MATRIX(_mul_transpose)(T * _x, \ + unsigned int _m, \ + unsigned int _n, \ + T * _xxT); \ + \ +/* Compute \(\vec{X}^T\vec{X}\) on a \(m \times n\) matrix. */ \ +/* The result is a \(n \times n\) matrix. */ \ +/* _x : input matrix, [size: _m x _n] */ \ +/* _m : input rows */ \ +/* _n : input columns */ \ +/* _xTx : output matrix, [size: _n x _n] */ \ +int MATRIX(_transpose_mul)(T * _x, \ + unsigned int _m, \ + unsigned int _n, \ + T * _xTx); \ + \ +/* Compute \(\vec{X}\vec{X}^H\) on a \(m \times n\) matrix. */ \ +/* The result is a \(m \times m\) matrix. */ \ +/* _x : input matrix, [size: _m x _n] */ \ +/* _m : input rows */ \ +/* _n : input columns */ \ +/* _xxH : output matrix, [size: _m x _m] */ \ +int MATRIX(_mul_hermitian)(T * _x, \ + unsigned int _m, \ + unsigned int _n, \ + T * _xxH); \ + \ +/* Compute \(\vec{X}^H\vec{X}\) on a \(m \times n\) matrix. */ \ +/* The result is a \(n \times n\) matrix. */ \ +/* _x : input matrix, [size: _m x _n] */ \ +/* _m : input rows */ \ +/* _n : input columns */ \ +/* _xHx : output matrix, [size: _n x _n] */ \ +int MATRIX(_hermitian_mul)(T * _x, \ + unsigned int _m, \ + unsigned int _n, \ + T * _xHx); \ + \ + \ +/* Augment two matrices \(\vec{X}\) and \(\vec{Y}\), storing the result */ \ +/* in \(\vec{Z}\) */ \ +/* NOTE: _rz = _rx = _ry, _rx = _ry, and _cz = _cx + _cy */ \ +/* _x : input matrix, [size: _rx x _cx] */ \ +/* _rx : number of rows in _x */ \ +/* _cx : number of columns in _x */ \ +/* _y : input matrix, [size: _ry x _cy] */ \ +/* _ry : number of rows in _y */ \ +/* _cy : number of columns in _y */ \ +/* _z : output matrix, [size: _rz x _cz] */ \ +/* _rz : number of rows in _z */ \ +/* _cz : number of columns in _z */ \ +int MATRIX(_aug)(T * _x, unsigned int _rx, unsigned int _cx, \ + T * _y, unsigned int _ry, unsigned int _cy, \ + T * _z, unsigned int _rz, unsigned int _cz); \ + \ +/* Compute the inverse of a square matrix \(\vec{X}\) */ \ +/* _x : input/output matrix, [size: _r x _c] */ \ +/* _r : rows */ \ +/* _c : columns */ \ +int MATRIX(_inv)(T * _x, \ + unsigned int _r, \ + unsigned int _c); \ + \ +/* Generate the identity square matrix of size \(n\) */ \ +/* _x : output matrix, [size: _n x _n] */ \ +/* _n : dimensions of _x */ \ +int MATRIX(_eye)(T * _x, \ + unsigned int _n); \ + \ +/* Generate the all-ones matrix of size \(n\) */ \ +/* _x : output matrix, [size: _r x _c] */ \ +/* _r : rows */ \ +/* _c : columns */ \ +int MATRIX(_ones)(T * _x, \ + unsigned int _r, \ + unsigned int _c); \ + \ +/* Generate the all-zeros matrix of size \(n\) */ \ +/* _x : output matrix, [size: _r x _c] */ \ +/* _r : rows */ \ +/* _c : columns */ \ +int MATRIX(_zeros)(T * _x, \ + unsigned int _r, \ + unsigned int _c); \ + \ +/* Perform Gauss-Jordan elimination on matrix \(\vec{X}\) */ \ +/* _x : input/output matrix, [size: _r x _c] */ \ +/* _r : rows */ \ +/* _c : columns */ \ +int MATRIX(_gjelim)(T * _x, \ + unsigned int _r, \ + unsigned int _c); \ + \ +/* Pivot on element \(\vec{X}_{i,j}\) */ \ +/* _x : output matrix, [size: _r x _c] */ \ +/* _r : rows of _x */ \ +/* _c : columns of _x */ \ +/* _i : pivot row */ \ +/* _j : pivot column */ \ +int MATRIX(_pivot)(T * _x, \ + unsigned int _r, \ + unsigned int _c, \ + unsigned int _i, \ + unsigned int _j); \ + \ +/* Swap rows _r1 and _r2 of matrix \(\vec{X}\) */ \ +/* _x : input/output matrix, [size: _r x _c] */ \ +/* _r : rows of _x */ \ +/* _c : columns of _x */ \ +/* _r1 : first row to swap */ \ +/* _r2 : second row to swap */ \ +int MATRIX(_swaprows)(T * _x, \ + unsigned int _r, \ + unsigned int _c, \ + unsigned int _r1, \ + unsigned int _r2); \ + \ +/* Solve linear system of \(n\) equations: \(\vec{A}\vec{x} = \vec{b}\) */ \ +/* _A : system matrix, [size: _n x _n] */ \ +/* _n : system size */ \ +/* _b : equality vector, [size: _n x 1] */ \ +/* _x : solution vector, [size: _n x 1] */ \ +/* _opts : options (ignored for now) */ \ +int MATRIX(_linsolve)(T * _A, \ + unsigned int _n, \ + T * _b, \ + T * _x, \ + void * _opts); \ + \ +/* Solve linear system of equations using conjugate gradient method. */ \ +/* _A : symmetric positive definite square matrix */ \ +/* _n : system dimension */ \ +/* _b : equality, [size: _n x 1] */ \ +/* _x : solution estimate, [size: _n x 1] */ \ +/* _opts : options (ignored for now) */ \ +int MATRIX(_cgsolve)(T * _A, \ + unsigned int _n, \ + T * _b, \ + T * _x, \ + void * _opts); \ + \ +/* Perform L/U/P decomposition using Crout's method */ \ +/* _x : input/output matrix, [size: _rx x _cx] */ \ +/* _rx : rows of _x */ \ +/* _cx : columns of _x */ \ +/* _L : first row to swap */ \ +/* _U : first row to swap */ \ +/* _P : first row to swap */ \ +int MATRIX(_ludecomp_crout)(T * _x, \ + unsigned int _rx, \ + unsigned int _cx, \ + T * _L, \ + T * _U, \ + T * _P); \ + \ +/* Perform L/U/P decomposition, Doolittle's method */ \ +/* _x : input/output matrix, [size: _rx x _cx] */ \ +/* _rx : rows of _x */ \ +/* _cx : columns of _x */ \ +/* _L : first row to swap */ \ +/* _U : first row to swap */ \ +/* _P : first row to swap */ \ +int MATRIX(_ludecomp_doolittle)(T * _x, \ + unsigned int _rx, \ + unsigned int _cx, \ + T * _L, \ + T * _U, \ + T * _P); \ + \ +/* Perform orthnormalization using the Gram-Schmidt algorithm */ \ +/* _A : input matrix, [size: _r x _c] */ \ +/* _r : rows */ \ +/* _c : columns */ \ +/* _v : output matrix */ \ +int MATRIX(_gramschmidt)(T * _A, \ + unsigned int _r, \ + unsigned int _c, \ + T * _v); \ + \ +/* Perform Q/R decomposition using the Gram-Schmidt algorithm such that */ \ +/* \( \vec{A} = \vec{Q} \vec{R} \) */ \ +/* and \( \vec{Q}^T \vec{Q} = \vec{I}_n \) */ \ +/* and \(\vec{R\}\) is a diagonal \(m \times m\) matrix */ \ +/* NOTE: all matrices are square */ \ +/* _A : input matrix, [size: _m x _m] */ \ +/* _m : rows */ \ +/* _n : columns (same as cols) */ \ +/* _Q : output matrix, [size: _m x _m] */ \ +/* _R : output matrix, [size: _m x _m] */ \ +int MATRIX(_qrdecomp_gramschmidt)(T * _A, \ + unsigned int _m, \ + unsigned int _n, \ + T * _Q, \ + T * _R); \ + \ +/* Compute Cholesky decomposition of a symmetric/Hermitian */ \ +/* positive-definite matrix as \( \vec{A} = \vec{L}\vec{L}^T \) */ \ +/* _A : input square matrix, [size: _n x _n] */ \ +/* _n : input matrix dimension */ \ +/* _L : output lower-triangular matrix */ \ +int MATRIX(_chol)(T * _A, \ + unsigned int _n, \ + T * _L); \ + +#define matrix_access(X,R,C,r,c) ((X)[(r)*(C)+(c)]) + +#define matrixc_access(X,R,C,r,c) matrix_access(X,R,C,r,c) +#define matrixf_access(X,R,C,r,c) matrix_access(X,R,C,r,c) +#define matrixcf_access(X,R,C,r,c) matrix_access(X,R,C,r,c) + +LIQUID_MATRIX_DEFINE_API(LIQUID_MATRIX_MANGLE_FLOAT, float) +LIQUID_MATRIX_DEFINE_API(LIQUID_MATRIX_MANGLE_DOUBLE, double) + +LIQUID_MATRIX_DEFINE_API(LIQUID_MATRIX_MANGLE_CFLOAT, liquid_float_complex) +LIQUID_MATRIX_DEFINE_API(LIQUID_MATRIX_MANGLE_CDOUBLE, liquid_double_complex) + + +#define LIQUID_SMATRIX_MANGLE_BOOL(name) LIQUID_CONCAT(smatrixb, name) +#define LIQUID_SMATRIX_MANGLE_FLOAT(name) LIQUID_CONCAT(smatrixf, name) +#define LIQUID_SMATRIX_MANGLE_INT(name) LIQUID_CONCAT(smatrixi, name) + +// sparse 'alist' matrix type (similar to MacKay, Davey Lafferty convention) +// large macro +// SMATRIX : name-mangling macro +// T : primitive data type +#define LIQUID_SMATRIX_DEFINE_API(SMATRIX,T) \ + \ +/* Sparse matrix object (similar to MacKay, Davey, Lafferty convention) */ \ +typedef struct SMATRIX(_s) * SMATRIX(); \ + \ +/* Create _M x _N sparse matrix, initialized with zeros */ \ +SMATRIX() SMATRIX(_create)(unsigned int _M, \ + unsigned int _N); \ + \ +/* Create _M x _N sparse matrix, initialized on array */ \ +/* _x : input matrix, [size: _m x _n] */ \ +/* _m : number of rows in input matrix */ \ +/* _n : number of columns in input matrix */ \ +SMATRIX() SMATRIX(_create_array)(T * _x, \ + unsigned int _m, \ + unsigned int _n); \ + \ +/* Destroy object, freeing all internal memory */ \ +int SMATRIX(_destroy)(SMATRIX() _q); \ + \ +/* Print sparse matrix in compact form to stdout */ \ +int SMATRIX(_print)(SMATRIX() _q); \ + \ +/* Print sparse matrix in expanded form to stdout */ \ +int SMATRIX(_print_expanded)(SMATRIX() _q); \ + \ +/* Get size of sparse matrix (number of rows and columns) */ \ +/* _q : sparse matrix object */ \ +/* _m : number of rows in matrix */ \ +/* _n : number of columns in matrix */ \ +int SMATRIX(_size)(SMATRIX() _q, \ + unsigned int * _m, \ + unsigned int * _n); \ + \ +/* Zero all elements and retain allocated memory */ \ +int SMATRIX(_clear)(SMATRIX() _q); \ + \ +/* Zero all elements and clear memory */ \ +int SMATRIX(_reset)(SMATRIX() _q); \ + \ +/* Determine if value has been set (allocated memory) */ \ +/* _q : sparse matrix object */ \ +/* _m : row index of value to query */ \ +/* _n : column index of value to query */ \ +int SMATRIX(_isset)(SMATRIX() _q, \ + unsigned int _m, \ + unsigned int _n); \ + \ +/* Insert an element at index, allocating memory as necessary */ \ +/* _q : sparse matrix object */ \ +/* _m : row index of value to insert */ \ +/* _n : column index of value to insert */ \ +/* _v : value to insert */ \ +int SMATRIX(_insert)(SMATRIX() _q, \ + unsigned int _m, \ + unsigned int _n, \ + T _v); \ + \ +/* Delete an element at index, freeing memory */ \ +/* _q : sparse matrix object */ \ +/* _m : row index of value to delete */ \ +/* _n : column index of value to delete */ \ +int SMATRIX(_delete)(SMATRIX() _q, \ + unsigned int _m, \ + unsigned int _n); \ + \ +/* Set the value in matrix at specified row and column, allocating */ \ +/* memory if needed */ \ +/* _q : sparse matrix object */ \ +/* _m : row index of value to set */ \ +/* _n : column index of value to set */ \ +/* _v : value to set in matrix */ \ +int SMATRIX(_set)(SMATRIX() _q, \ + unsigned int _m, \ + unsigned int _n, \ + T _v); \ + \ +/* Get the value from matrix at specified row and column */ \ +/* _q : sparse matrix object */ \ +/* _m : row index of value to get */ \ +/* _n : column index of value to get */ \ +T SMATRIX(_get)(SMATRIX() _q, \ + unsigned int _m, \ + unsigned int _n); \ + \ +/* Initialize to identity matrix; set all diagonal elements to 1, all */ \ +/* others to 0. This is done with both square and non-square matrices. */ \ +int SMATRIX(_eye)(SMATRIX() _q); \ + \ +/* Multiply two sparse matrices, \( \vec{Z} = \vec{X} \vec{Y} \) */ \ +/* _x : sparse matrix object (input) */ \ +/* _y : sparse matrix object (input) */ \ +/* _z : sparse matrix object (output) */ \ +int SMATRIX(_mul)(SMATRIX() _x, \ + SMATRIX() _y, \ + SMATRIX() _z); \ + \ +/* Multiply sparse matrix by vector */ \ +/* _q : sparse matrix */ \ +/* _x : input vector, [size: _n x 1] */ \ +/* _y : output vector, [size: _m x 1] */ \ +int SMATRIX(_vmul)(SMATRIX() _q, \ + T * _x, \ + T * _y); \ + +LIQUID_SMATRIX_DEFINE_API(LIQUID_SMATRIX_MANGLE_BOOL, unsigned char) +LIQUID_SMATRIX_DEFINE_API(LIQUID_SMATRIX_MANGLE_FLOAT, float) +LIQUID_SMATRIX_DEFINE_API(LIQUID_SMATRIX_MANGLE_INT, short int) + +// +// smatrix cross methods +// + +// multiply sparse binary matrix by floating-point matrix +// _q : sparse matrix [size: A->M x A->N] +// _x : input vector [size: mx x nx ] +// _y : output vector [size: my x ny ] +int smatrixb_mulf(smatrixb _A, + float * _x, + unsigned int _mx, + unsigned int _nx, + float * _y, + unsigned int _my, + unsigned int _ny); + +// multiply sparse binary matrix by floating-point vector +// _q : sparse matrix +// _x : input vector [size: _N x 1] +// _y : output vector [size: _M x 1] +int smatrixb_vmulf(smatrixb _q, + float * _x, + float * _y); + + +// +// MODULE : modem (modulator/demodulator) +// + +// Maximum number of allowed bits per symbol +#define MAX_MOD_BITS_PER_SYMBOL 8 + +// Modulation schemes available +#define LIQUID_MODEM_NUM_SCHEMES (52) + +typedef enum { + LIQUID_MODEM_UNKNOWN=0, // Unknown modulation scheme + + // Phase-shift keying (PSK) + LIQUID_MODEM_PSK2, LIQUID_MODEM_PSK4, + LIQUID_MODEM_PSK8, LIQUID_MODEM_PSK16, + LIQUID_MODEM_PSK32, LIQUID_MODEM_PSK64, + LIQUID_MODEM_PSK128, LIQUID_MODEM_PSK256, + + // Differential phase-shift keying (DPSK) + LIQUID_MODEM_DPSK2, LIQUID_MODEM_DPSK4, + LIQUID_MODEM_DPSK8, LIQUID_MODEM_DPSK16, + LIQUID_MODEM_DPSK32, LIQUID_MODEM_DPSK64, + LIQUID_MODEM_DPSK128, LIQUID_MODEM_DPSK256, + + // amplitude-shift keying + LIQUID_MODEM_ASK2, LIQUID_MODEM_ASK4, + LIQUID_MODEM_ASK8, LIQUID_MODEM_ASK16, + LIQUID_MODEM_ASK32, LIQUID_MODEM_ASK64, + LIQUID_MODEM_ASK128, LIQUID_MODEM_ASK256, + + // rectangular quadrature amplitude-shift keying (QAM) + LIQUID_MODEM_QAM4, + LIQUID_MODEM_QAM8, LIQUID_MODEM_QAM16, + LIQUID_MODEM_QAM32, LIQUID_MODEM_QAM64, + LIQUID_MODEM_QAM128, LIQUID_MODEM_QAM256, + + // amplitude phase-shift keying (APSK) + LIQUID_MODEM_APSK4, + LIQUID_MODEM_APSK8, LIQUID_MODEM_APSK16, + LIQUID_MODEM_APSK32, LIQUID_MODEM_APSK64, + LIQUID_MODEM_APSK128, LIQUID_MODEM_APSK256, + + // specific modem types + LIQUID_MODEM_BPSK, // Specific: binary PSK + LIQUID_MODEM_QPSK, // specific: quaternary PSK + LIQUID_MODEM_OOK, // Specific: on/off keying + LIQUID_MODEM_SQAM32, // 'square' 32-QAM + LIQUID_MODEM_SQAM128, // 'square' 128-QAM + LIQUID_MODEM_V29, // V.29 star constellation + LIQUID_MODEM_ARB16OPT, // optimal 16-QAM + LIQUID_MODEM_ARB32OPT, // optimal 32-QAM + LIQUID_MODEM_ARB64OPT, // optimal 64-QAM + LIQUID_MODEM_ARB128OPT, // optimal 128-QAM + LIQUID_MODEM_ARB256OPT, // optimal 256-QAM + LIQUID_MODEM_ARB64VT, // Virginia Tech logo + + // arbitrary modem type + LIQUID_MODEM_ARB // arbitrary QAM +} modulation_scheme; + +// structure for holding full modulation type descriptor +struct modulation_type_s { + const char * name; // short name (e.g. 'bpsk') + const char * fullname; // full name (e.g. 'binary phase-shift keying') + modulation_scheme scheme; // modulation scheme (e.g. LIQUID_MODEM_BPSK) + unsigned int bps; // modulation depth (e.g. 1) +}; + +// full modulation type descriptor +extern const struct modulation_type_s modulation_types[LIQUID_MODEM_NUM_SCHEMES]; + +// Print compact list of existing and available modulation schemes +int liquid_print_modulation_schemes(); + +// returns modulation_scheme based on input string +modulation_scheme liquid_getopt_str2mod(const char * _str); + +// query basic modulation types +int liquid_modem_is_psk(modulation_scheme _ms); +int liquid_modem_is_dpsk(modulation_scheme _ms); +int liquid_modem_is_ask(modulation_scheme _ms); +int liquid_modem_is_qam(modulation_scheme _ms); +int liquid_modem_is_apsk(modulation_scheme _ms); + +// useful functions + +// counts the number of different bits between two symbols +unsigned int count_bit_errors(unsigned int _s1, unsigned int _s2); + +// counts the number of different bits between two arrays of symbols +// _msg0 : original message [size: _n x 1] +// _msg1 : copy of original message [size: _n x 1] +// _n : message size +unsigned int count_bit_errors_array(unsigned char * _msg0, + unsigned char * _msg1, + unsigned int _n); + +// converts binary-coded decimal (BCD) to gray, ensuring successive values +// differ by exactly one bit +unsigned int gray_encode(unsigned int symbol_in); + +// converts a gray-encoded symbol to binary-coded decimal (BCD) +unsigned int gray_decode(unsigned int symbol_in); + +// pack soft bits into symbol +// _soft_bits : soft input bits [size: _bps x 1] +// _bps : bits per symbol +// _sym_out : output symbol, value in [0,2^_bps) +int liquid_pack_soft_bits(unsigned char * _soft_bits, + unsigned int _bps, + unsigned int * _sym_out); + +// unpack soft bits into symbol +// _sym_in : input symbol, value in [0,2^_bps) +// _bps : bits per symbol +// _soft_bits : soft output bits [size: _bps x 1] +int liquid_unpack_soft_bits(unsigned int _sym_in, + unsigned int _bps, + unsigned char * _soft_bits); + + +// +// Linear modem +// + +#define LIQUID_MODEM_MANGLE_FLOAT(name) LIQUID_CONCAT(modem,name) + +// Macro : MODEM +// MODEM : name-mangling macro +// T : primitive data type +// TC : primitive data type (complex) +#define LIQUID_MODEM_DEFINE_API(MODEM,T,TC) \ + \ +/* Linear modulator/demodulator (modem) object */ \ +typedef struct MODEM(_s) * MODEM(); \ + \ +/* Create digital modem object with a particular scheme */ \ +/* _scheme : linear modulation scheme (e.g. LIQUID_MODEM_QPSK) */ \ +MODEM() MODEM(_create)(modulation_scheme _scheme); \ + \ +/* Create linear digital modem object with arbitrary constellation */ \ +/* points defined by an external table of symbols. Sample points are */ \ +/* provided as complex float pairs and converted internally if needed. */ \ +/* _table : array of complex constellation points, [size: _M x 1] */ \ +/* _M : modulation order and table size, _M must be power of 2 */ \ +MODEM() MODEM(_create_arbitrary)(liquid_float_complex * _table, \ + unsigned int _M); \ + \ +/* Recreate modulation scheme, re-allocating memory as necessary */ \ +/* _q : modem object */ \ +/* _scheme : linear modulation scheme (e.g. LIQUID_MODEM_QPSK) */ \ +MODEM() MODEM(_recreate)(MODEM() _q, \ + modulation_scheme _scheme); \ + \ +/* Destroy modem object, freeing all allocated memory */ \ +int MODEM(_destroy)(MODEM() _q); \ + \ +/* Print modem status to stdout */ \ +int MODEM(_print)(MODEM() _q); \ + \ +/* Reset internal state of modem object; note that this is only */ \ +/* relevant for modulation types that retain an internal state such as */ \ +/* LIQUID_MODEM_DPSK4 as most linear modulation types are stateless */ \ +int MODEM(_reset)(MODEM() _q); \ + \ +/* Generate random symbol for modulation */ \ +unsigned int MODEM(_gen_rand_sym)(MODEM() _q); \ + \ +/* Get number of bits per symbol (bps) of modem object */ \ +unsigned int MODEM(_get_bps)(MODEM() _q); \ + \ +/* Get modulation scheme of modem object */ \ +modulation_scheme MODEM(_get_scheme)(MODEM() _q); \ + \ +/* Modulate input symbol (bits) and generate output complex sample */ \ +/* _q : modem object */ \ +/* _s : input symbol, 0 <= _s <= M-1 */ \ +/* _y : output complex sample */ \ +int MODEM(_modulate)(MODEM() _q, \ + unsigned int _s, \ + TC * _y); \ + \ +/* Demodulate input sample and provide maximum-likelihood estimate of */ \ +/* symbol that would have generated it. */ \ +/* The output is a hard decision value on the input sample. */ \ +/* This is performed efficiently by taking advantage of symmetry on */ \ +/* most modulation types. */ \ +/* For example, square and rectangular quadrature amplitude modulation */ \ +/* with gray coding can use a bisection search indepdently on its */ \ +/* in-phase and quadrature channels. */ \ +/* Arbitrary modulation schemes are relatively slow, however, for large */ \ +/* modulation types as the demodulator must compute the distance */ \ +/* between the received sample and all possible symbols to derive the */ \ +/* optimal symbol. */ \ +/* _q : modem object */ \ +/* _x : input sample */ \ +/* _s : output hard symbol, 0 <= _s <= M-1 */ \ +int MODEM(_demodulate)(MODEM() _q, \ + TC _x, \ + unsigned int * _s); \ + \ +/* Demodulate input sample and provide (approximate) log-likelihood */ \ +/* ratio (LLR, soft bits) as an output. */ \ +/* Similarly to the hard-decision demodulation method, this is computed */ \ +/* efficiently for most modulation types. */ \ +/* _q : modem object */ \ +/* _x : input sample */ \ +/* _s : output hard symbol, 0 <= _s <= M-1 */ \ +/* _soft_bits : output soft bits, [size: log2(M) x 1] */ \ +int MODEM(_demodulate_soft)(MODEM() _q, \ + TC _x, \ + unsigned int * _s, \ + unsigned char * _soft_bits); \ + \ +/* Get demodulator's estimated transmit sample */ \ +int MODEM(_get_demodulator_sample)(MODEM() _q, \ + TC * _x_hat); \ + \ +/* Get demodulator phase error */ \ +float MODEM(_get_demodulator_phase_error)(MODEM() _q); \ + \ +/* Get demodulator error vector magnitude */ \ +float MODEM(_get_demodulator_evm)(MODEM() _q); \ + +// define modem APIs +LIQUID_MODEM_DEFINE_API(LIQUID_MODEM_MANGLE_FLOAT,float,liquid_float_complex) + + +// +// continuous-phase modulation +// + +// gmskmod : GMSK modulator +typedef struct gmskmod_s * gmskmod; + +// create gmskmod object +// _k : samples/symbol +// _m : filter delay (symbols) +// _BT : excess bandwidth factor +gmskmod gmskmod_create(unsigned int _k, + unsigned int _m, + float _BT); +int gmskmod_destroy(gmskmod _q); +int gmskmod_print(gmskmod _q); +int gmskmod_reset(gmskmod _q); +int gmskmod_modulate(gmskmod _q, + unsigned int _sym, + liquid_float_complex * _y); + + +// gmskdem : GMSK demodulator +typedef struct gmskdem_s * gmskdem; + +// create gmskdem object +// _k : samples/symbol +// _m : filter delay (symbols) +// _BT : excess bandwidth factor +gmskdem gmskdem_create(unsigned int _k, + unsigned int _m, + float _BT); +int gmskdem_destroy(gmskdem _q); +int gmskdem_print(gmskdem _q); +int gmskdem_reset(gmskdem _q); +int gmskdem_set_eq_bw(gmskdem _q, float _bw); +int gmskdem_demodulate(gmskdem _q, + liquid_float_complex * _y, + unsigned int * _sym); + +// +// continuous phase frequency-shift keying (CP-FSK) modems +// + +// CP-FSK filter prototypes +typedef enum { + LIQUID_CPFSK_SQUARE=0, // square pulse + LIQUID_CPFSK_RCOS_FULL, // raised-cosine (full response) + LIQUID_CPFSK_RCOS_PARTIAL, // raised-cosine (partial response) + LIQUID_CPFSK_GMSK, // Gauss minimum-shift keying pulse +} liquid_cpfsk_filter; + +// CP-FSK modulator +typedef struct cpfskmod_s * cpfskmod; + +// create cpfskmod object (frequency modulator) +// _bps : bits per symbol, _bps > 0 +// _h : modulation index, _h > 0 +// _k : samples/symbol, _k > 1, _k even +// _m : filter delay (symbols), _m > 0 +// _beta : filter bandwidth parameter, _beta > 0 +// _type : filter type (e.g. LIQUID_CPFSK_SQUARE) +cpfskmod cpfskmod_create(unsigned int _bps, + float _h, + unsigned int _k, + unsigned int _m, + float _beta, + int _type); +//cpfskmod cpfskmod_create_msk(unsigned int _k); +//cpfskmod cpfskmod_create_gmsk(unsigned int _k, float _BT); + +// destroy cpfskmod object +int cpfskmod_destroy(cpfskmod _q); + +// print cpfskmod object internals +int cpfskmod_print(cpfskmod _q); + +// reset state +int cpfskmod_reset(cpfskmod _q); + +// get transmit delay [symbols] +unsigned int cpfskmod_get_delay(cpfskmod _q); + +// modulate sample +// _q : frequency modulator object +// _s : input symbol +// _y : output sample array [size: _k x 1] +int cpfskmod_modulate(cpfskmod _q, + unsigned int _s, + liquid_float_complex * _y); + + + +// CP-FSK demodulator +typedef struct cpfskdem_s * cpfskdem; + +// create cpfskdem object (frequency modulator) +// _bps : bits per symbol, _bps > 0 +// _h : modulation index, _h > 0 +// _k : samples/symbol, _k > 1, _k even +// _m : filter delay (symbols), _m > 0 +// _beta : filter bandwidth parameter, _beta > 0 +// _type : filter type (e.g. LIQUID_CPFSK_SQUARE) +cpfskdem cpfskdem_create(unsigned int _bps, + float _h, + unsigned int _k, + unsigned int _m, + float _beta, + int _type); +//cpfskdem cpfskdem_create_msk(unsigned int _k); +//cpfskdem cpfskdem_create_gmsk(unsigned int _k, float _BT); + +// destroy cpfskdem object +int cpfskdem_destroy(cpfskdem _q); + +// print cpfskdem object internals +int cpfskdem_print(cpfskdem _q); + +// reset state +int cpfskdem_reset(cpfskdem _q); + +// get receive delay [symbols] +unsigned int cpfskdem_get_delay(cpfskdem _q); + +#if 0 +// demodulate array of samples +// _q : continuous-phase frequency demodulator object +// _y : input sample array [size: _n x 1] +// _n : input sample array length +// _s : output symbol array +// _nw : number of output symbols written +int cpfskdem_demodulate(cpfskdem _q, + liquid_float_complex * _y, + unsigned int _n, + unsigned int * _s, + unsigned int * _nw); +#else +// demodulate array of samples, assuming perfect timing +// _q : continuous-phase frequency demodulator object +// _y : input sample array [size: _k x 1] +unsigned int cpfskdem_demodulate(cpfskdem _q, + liquid_float_complex * _y); +#endif + + + +// +// M-ary frequency-shift keying (MFSK) modems +// + +// FSK modulator +typedef struct fskmod_s * fskmod; + +// create fskmod object (frequency modulator) +// _m : bits per symbol, _bps > 0 +// _k : samples/symbol, _k >= 2^_m +// _bandwidth : total signal bandwidth, (0,0.5) +fskmod fskmod_create(unsigned int _m, + unsigned int _k, + float _bandwidth); + +// destroy fskmod object +int fskmod_destroy(fskmod _q); + +// print fskmod object internals +int fskmod_print(fskmod _q); + +// reset state +int fskmod_reset(fskmod _q); + +// modulate sample +// _q : frequency modulator object +// _s : input symbol +// _y : output sample array [size: _k x 1] +int fskmod_modulate(fskmod _q, + unsigned int _s, + liquid_float_complex * _y); + + + +// FSK demodulator +typedef struct fskdem_s * fskdem; + +// create fskdem object (frequency demodulator) +// _m : bits per symbol, _bps > 0 +// _k : samples/symbol, _k >= 2^_m +// _bandwidth : total signal bandwidth, (0,0.5) +fskdem fskdem_create(unsigned int _m, + unsigned int _k, + float _bandwidth); + +// destroy fskdem object +int fskdem_destroy(fskdem _q); + +// print fskdem object internals +int fskdem_print(fskdem _q); + +// reset state +int fskdem_reset(fskdem _q); + +// demodulate symbol, assuming perfect symbol timing +// _q : fskdem object +// _y : input sample array [size: _k x 1] +unsigned int fskdem_demodulate(fskdem _q, + liquid_float_complex * _y); + +// get demodulator frequency error +float fskdem_get_frequency_error(fskdem _q); + +// get energy for a particular symbol within a certain range +float fskdem_get_symbol_energy(fskdem _q, + unsigned int _s, + unsigned int _range); + + +// +// Analog frequency modulator +// +#define LIQUID_FREQMOD_MANGLE_FLOAT(name) LIQUID_CONCAT(freqmod,name) + +// Macro : FREQMOD (analog frequency modulator) +// FREQMOD : name-mangling macro +// T : primitive data type +// TC : primitive data type (complex) +#define LIQUID_FREQMOD_DEFINE_API(FREQMOD,T,TC) \ + \ +/* Analog frequency modulation object */ \ +typedef struct FREQMOD(_s) * FREQMOD(); \ + \ +/* Create freqmod object with a particular modulation factor */ \ +/* _kf : modulation factor */ \ +FREQMOD() FREQMOD(_create)(float _kf); \ + \ +/* Destroy freqmod object, freeing all internal memory */ \ +int FREQMOD(_destroy)(FREQMOD() _q); \ + \ +/* Print freqmod object internals to stdout */ \ +int FREQMOD(_print)(FREQMOD() _q); \ + \ +/* Reset state */ \ +int FREQMOD(_reset)(FREQMOD() _q); \ + \ +/* Modulate single sample, producing single output sample at complex */ \ +/* baseband. */ \ +/* _q : frequency modulator object */ \ +/* _m : message signal \( m(t) \) */ \ +/* _s : complex baseband signal \( s(t) \) */ \ +int FREQMOD(_modulate)(FREQMOD() _q, \ + T _m, \ + TC * _s); \ + \ +/* Modulate block of samples */ \ +/* _q : frequency modulator object */ \ +/* _m : message signal \( m(t) \), [size: _n x 1] */ \ +/* _n : number of input, output samples */ \ +/* _s : complex baseband signal \( s(t) \), [size: _n x 1] */ \ +int FREQMOD(_modulate_block)(FREQMOD() _q, \ + T * _m, \ + unsigned int _n, \ + TC * _s); \ + +// define freqmod APIs +LIQUID_FREQMOD_DEFINE_API(LIQUID_FREQMOD_MANGLE_FLOAT,float,liquid_float_complex) + +// +// Analog frequency demodulator +// + +#define LIQUID_FREQDEM_MANGLE_FLOAT(name) LIQUID_CONCAT(freqdem,name) + +// Macro : FREQDEM (analog frequency modulator) +// FREQDEM : name-mangling macro +// T : primitive data type +// TC : primitive data type (complex) +#define LIQUID_FREQDEM_DEFINE_API(FREQDEM,T,TC) \ +typedef struct FREQDEM(_s) * FREQDEM(); \ + \ +/* create freqdem object (frequency modulator) */ \ +/* _kf : modulation factor */ \ +FREQDEM() FREQDEM(_create)(float _kf); \ + \ +/* destroy freqdem object */ \ +int FREQDEM(_destroy)(FREQDEM() _q); \ + \ +/* print freqdem object internals */ \ +int FREQDEM(_print)(FREQDEM() _q); \ + \ +/* reset state */ \ +int FREQDEM(_reset)(FREQDEM() _q); \ + \ +/* demodulate sample */ \ +/* _q : frequency modulator object */ \ +/* _r : received signal r(t) */ \ +/* _m : output message signal m(t) */ \ +int FREQDEM(_demodulate)(FREQDEM() _q, \ + TC _r, \ + T * _m); \ + \ +/* demodulate block of samples */ \ +/* _q : frequency demodulator object */ \ +/* _r : received signal r(t) [size: _n x 1] */ \ +/* _n : number of input, output samples */ \ +/* _m : message signal m(t), [size: _n x 1] */ \ +int FREQDEM(_demodulate_block)(FREQDEM() _q, \ + TC * _r, \ + unsigned int _n, \ + T * _m); \ + +// define freqdem APIs +LIQUID_FREQDEM_DEFINE_API(LIQUID_FREQDEM_MANGLE_FLOAT,float,liquid_float_complex) + + + +// amplitude modulation types +typedef enum { + LIQUID_AMPMODEM_DSB=0, // double side-band + LIQUID_AMPMODEM_USB, // single side-band (upper) + LIQUID_AMPMODEM_LSB // single side-band (lower) +} liquid_ampmodem_type; + +typedef struct ampmodem_s * ampmodem; + +// create ampmodem object +// _m : modulation index +// _type : AM type (e.g. LIQUID_AMPMODEM_DSB) +// _suppressed_carrier : carrier suppression flag +ampmodem ampmodem_create(float _mod_index, + liquid_ampmodem_type _type, + int _suppressed_carrier); + +// destroy ampmodem object +int ampmodem_destroy(ampmodem _q); + +// print ampmodem object internals +int ampmodem_print(ampmodem _q); + +// reset ampmodem object state +int ampmodem_reset(ampmodem _q); + +// accessor methods +unsigned int ampmodem_get_delay_mod (ampmodem _q); +unsigned int ampmodem_get_delay_demod(ampmodem _q); + +// modulate sample +int ampmodem_modulate(ampmodem _q, + float _x, + liquid_float_complex * _y); + +int ampmodem_modulate_block(ampmodem _q, + float * _m, + unsigned int _n, + liquid_float_complex * _s); + +// demodulate sample +int ampmodem_demodulate(ampmodem _q, + liquid_float_complex _y, + float * _x); + +int ampmodem_demodulate_block(ampmodem _q, + liquid_float_complex * _r, + unsigned int _n, + float * _m); + +// +// MODULE : multichannel +// + + +#define FIRPFBCH_NYQUIST 0 +#define FIRPFBCH_ROOTNYQUIST 1 + +#define LIQUID_ANALYZER 0 +#define LIQUID_SYNTHESIZER 1 + + +// +// Finite impulse response polyphase filterbank channelizer +// + +#define LIQUID_FIRPFBCH_MANGLE_CRCF(name) LIQUID_CONCAT(firpfbch_crcf,name) +#define LIQUID_FIRPFBCH_MANGLE_CCCF(name) LIQUID_CONCAT(firpfbch_cccf,name) + +// Macro: +// FIRPFBCH : name-mangling macro +// TO : output data type +// TC : coefficients data type +// TI : input data type +#define LIQUID_FIRPFBCH_DEFINE_API(FIRPFBCH,TO,TC,TI) \ +typedef struct FIRPFBCH(_s) * FIRPFBCH(); \ + \ +/* create finite impulse response polyphase filter-bank */ \ +/* channelizer object from external coefficients */ \ +/* _type : channelizer type, e.g. LIQUID_ANALYZER */ \ +/* _M : number of channels */ \ +/* _p : number of coefficients for each channel */ \ +/* _h : coefficients [size: _M*_p x 1] */ \ +FIRPFBCH() FIRPFBCH(_create)(int _type, \ + unsigned int _M, \ + unsigned int _p, \ + TC * _h); \ + \ +/* create FIR polyphase filterbank channelizer object with */ \ +/* prototype filter based on windowed Kaiser design */ \ +/* _type : type (LIQUID_ANALYZER | LIQUID_SYNTHESIZER) */ \ +/* _M : number of channels */ \ +/* _m : filter delay (symbols) */ \ +/* _As : stop-band attentuation [dB] */ \ +FIRPFBCH() FIRPFBCH(_create_kaiser)(int _type, \ + unsigned int _M, \ + unsigned int _m, \ + float _As); \ + \ +/* create FIR polyphase filterbank channelizer object with */ \ +/* prototype root-Nyquist filter */ \ +/* _type : type (LIQUID_ANALYZER | LIQUID_SYNTHESIZER) */ \ +/* _M : number of channels */ \ +/* _m : filter delay (symbols) */ \ +/* _beta : filter excess bandwidth factor, in [0,1] */ \ +/* _ftype : filter prototype (rrcos, rkaiser, etc.) */ \ +FIRPFBCH() FIRPFBCH(_create_rnyquist)(int _type, \ + unsigned int _M, \ + unsigned int _m, \ + float _beta, \ + int _ftype); \ + \ +/* destroy firpfbch object */ \ +int FIRPFBCH(_destroy)(FIRPFBCH() _q); \ + \ +/* clear/reset firpfbch internal state */ \ +int FIRPFBCH(_reset)(FIRPFBCH() _q); \ + \ +/* print firpfbch internal parameters to stdout */ \ +int FIRPFBCH(_print)(FIRPFBCH() _q); \ + \ +/* execute filterbank as synthesizer on block of samples */ \ +/* _q : filterbank channelizer object */ \ +/* _x : channelized input, [size: num_channels x 1] */ \ +/* _y : output time series, [size: num_channels x 1] */ \ +int FIRPFBCH(_synthesizer_execute)(FIRPFBCH() _q, \ + TI * _x, \ + TO * _y); \ + \ +/* execute filterbank as analyzer on block of samples */ \ +/* _q : filterbank channelizer object */ \ +/* _x : input time series, [size: num_channels x 1] */ \ +/* _y : channelized output, [size: num_channels x 1] */ \ +int FIRPFBCH(_analyzer_execute)(FIRPFBCH() _q, \ + TI * _x, \ + TO * _y); \ + + +LIQUID_FIRPFBCH_DEFINE_API(LIQUID_FIRPFBCH_MANGLE_CRCF, + liquid_float_complex, + float, + liquid_float_complex) + +LIQUID_FIRPFBCH_DEFINE_API(LIQUID_FIRPFBCH_MANGLE_CCCF, + liquid_float_complex, + liquid_float_complex, + liquid_float_complex) + + +// +// Finite impulse response polyphase filterbank channelizer +// with output rate 2 Fs / M +// + +#define LIQUID_FIRPFBCH2_MANGLE_CRCF(name) LIQUID_CONCAT(firpfbch2_crcf,name) + +// Macro: +// FIRPFBCH2 : name-mangling macro +// TO : output data type +// TC : coefficients data type +// TI : input data type +#define LIQUID_FIRPFBCH2_DEFINE_API(FIRPFBCH2,TO,TC,TI) \ +typedef struct FIRPFBCH2(_s) * FIRPFBCH2(); \ + \ +/* create firpfbch2 object */ \ +/* _type : channelizer type (e.g. LIQUID_ANALYZER) */ \ +/* _M : number of channels (must be even) */ \ +/* _m : prototype filter semi-length, length=2*M*m */ \ +/* _h : prototype filter coefficient array */ \ +FIRPFBCH2() FIRPFBCH2(_create)(int _type, \ + unsigned int _M, \ + unsigned int _m, \ + TC * _h); \ + \ +/* create firpfbch2 object using Kaiser window prototype */ \ +/* _type : channelizer type (e.g. LIQUID_ANALYZER) */ \ +/* _M : number of channels (must be even) */ \ +/* _m : prototype filter semi-length, length=2*M*m+1 */ \ +/* _As : filter stop-band attenuation [dB] */ \ +FIRPFBCH2() FIRPFBCH2(_create_kaiser)(int _type, \ + unsigned int _M, \ + unsigned int _m, \ + float _As); \ + \ +/* destroy firpfbch2 object, freeing internal memory */ \ +int FIRPFBCH2(_destroy)(FIRPFBCH2() _q); \ + \ +/* reset firpfbch2 object internals */ \ +int FIRPFBCH2(_reset)(FIRPFBCH2() _q); \ + \ +/* print firpfbch2 object internals */ \ +int FIRPFBCH2(_print)(FIRPFBCH2() _q); \ + \ +/* execute filterbank channelizer */ \ +/* LIQUID_ANALYZER: input: M/2, output: M */ \ +/* LIQUID_SYNTHESIZER: input: M, output: M/2 */ \ +/* _x : channelizer input */ \ +/* _y : channelizer output */ \ +int FIRPFBCH2(_execute)(FIRPFBCH2() _q, \ + TI * _x, \ + TO * _y); \ + + +LIQUID_FIRPFBCH2_DEFINE_API(LIQUID_FIRPFBCH2_MANGLE_CRCF, + liquid_float_complex, + float, + liquid_float_complex) + +// +// Finite impulse response polyphase filterbank channelizer +// with output rate Fs * P / M +// + +#define LIQUID_FIRPFBCHR_MANGLE_CRCF(name) LIQUID_CONCAT(firpfbchr_crcf,name) + +#define LIQUID_FIRPFBCHR_DEFINE_API(FIRPFBCHR,TO,TC,TI) \ +typedef struct FIRPFBCHR(_s) * FIRPFBCHR(); \ + \ +/* create rational rate resampling channelizer (firpfbchr) object by */ \ +/* specifying filter coefficients directly */ \ +/* _M : number of output channels in chanelizer */ \ +/* _P : output decimation factor (output rate is 1/P the input) */ \ +/* _m : prototype filter semi-length, length=2*M*m */ \ +/* _h : prototype filter coefficient array, [size: 2*M*m x 1] */ \ +FIRPFBCHR() FIRPFBCHR(_create)(unsigned int _M, \ + unsigned int _P, \ + unsigned int _m, \ + TC * _h); \ + \ +/* create rational rate resampling channelizer (firpfbchr) object by */ \ +/* specifying filter design parameters for Kaiser prototype */ \ +/* _M : number of output channels in chanelizer */ \ +/* _P : output decimation factor (output rate is 1/P the input) */ \ +/* _m : prototype filter semi-length, length=2*M*m */ \ +/* _As : filter stop-band attenuation [dB] */ \ +FIRPFBCHR() FIRPFBCHR(_create_kaiser)(unsigned int _M, \ + unsigned int _P, \ + unsigned int _m, \ + float _As); \ + \ +/* destroy firpfbchr object, freeing internal memory */ \ +int FIRPFBCHR(_destroy)(FIRPFBCHR() _q); \ + \ +/* reset firpfbchr object internal state and buffers */ \ +int FIRPFBCHR(_reset)(FIRPFBCHR() _q); \ + \ +/* print firpfbchr object internals to stdout */ \ +int FIRPFBCHR(_print)(FIRPFBCHR() _q); \ + \ +/* get number of output channels to channelizer */ \ +unsigned int FIRPFBCHR(_get_M)(FIRPFBCHR() _q); \ + \ +/* get decimation factor for channelizer */ \ +unsigned int FIRPFBCHR(_get_P)(FIRPFBCHR() _q); \ + \ +/* get semi-length to channelizer filter prototype */ \ +unsigned int FIRPFBCHR(_get_m)(FIRPFBCHR() _q); \ + \ +/* push buffer of samples into filter bank */ \ +/* _q : channelizer object */ \ +/* _x : channelizer input [size: P x 1] */ \ +int FIRPFBCHR(_push)(FIRPFBCHR() _q, \ + TI * _x); \ + \ +/* execute filterbank channelizer, writing complex baseband samples for */ \ +/* each channel into output array */ \ +/* _q : channelizer object */ \ +/* _y : channelizer output [size: _M x 1] */ \ +int FIRPFBCHR(_execute)(FIRPFBCHR() _q, \ + TO * _y); \ + + +LIQUID_FIRPFBCHR_DEFINE_API(LIQUID_FIRPFBCHR_MANGLE_CRCF, + liquid_float_complex, + float, + liquid_float_complex) + + + +#define OFDMFRAME_SCTYPE_NULL 0 +#define OFDMFRAME_SCTYPE_PILOT 1 +#define OFDMFRAME_SCTYPE_DATA 2 + +// initialize default subcarrier allocation +// _M : number of subcarriers +// _p : output subcarrier allocation array, [size: _M x 1] +int ofdmframe_init_default_sctype(unsigned int _M, + unsigned char * _p); + +// initialize default subcarrier allocation +// _M : number of subcarriers +// _f0 : lower frequency band, _f0 in [-0.5,0.5] +// _f1 : upper frequency band, _f1 in [-0.5,0.5] +// _p : output subcarrier allocation array, [size: _M x 1] +int ofdmframe_init_sctype_range(unsigned int _M, + float _f0, + float _f1, + unsigned char * _p); + +// validate subcarrier type (count number of null, pilot, and data +// subcarriers in the allocation) +// _p : subcarrier allocation array, [size: _M x 1] +// _M : number of subcarriers +// _M_null : output number of null subcarriers +// _M_pilot : output number of pilot subcarriers +// _M_data : output number of data subcarriers +int ofdmframe_validate_sctype(unsigned char * _p, + unsigned int _M, + unsigned int * _M_null, + unsigned int * _M_pilot, + unsigned int * _M_data); + +// print subcarrier allocation to screen +// _p : output subcarrier allocation array, [size: _M x 1] +// _M : number of subcarriers +int ofdmframe_print_sctype(unsigned char * _p, + unsigned int _M); + + +// +// OFDM frame (symbol) generator +// +typedef struct ofdmframegen_s * ofdmframegen; + +// create OFDM framing generator object +// _M : number of subcarriers, >10 typical +// _cp_len : cyclic prefix length +// _taper_len : taper length (OFDM symbol overlap) +// _p : subcarrier allocation (null, pilot, data), [size: _M x 1] +ofdmframegen ofdmframegen_create(unsigned int _M, + unsigned int _cp_len, + unsigned int _taper_len, + unsigned char * _p); + +int ofdmframegen_destroy(ofdmframegen _q); + +int ofdmframegen_print(ofdmframegen _q); + +int ofdmframegen_reset(ofdmframegen _q); + +// write first S0 symbol +int ofdmframegen_write_S0a(ofdmframegen _q, + liquid_float_complex *_y); + +// write second S0 symbol +int ofdmframegen_write_S0b(ofdmframegen _q, + liquid_float_complex *_y); + +// write S1 symbol +int ofdmframegen_write_S1(ofdmframegen _q, + liquid_float_complex *_y); + +// write data symbol +int ofdmframegen_writesymbol(ofdmframegen _q, + liquid_float_complex * _x, + liquid_float_complex *_y); + +// write tail +int ofdmframegen_writetail(ofdmframegen _q, + liquid_float_complex * _x); + +// +// OFDM frame (symbol) synchronizer +// +typedef int (*ofdmframesync_callback)(liquid_float_complex * _y, + unsigned char * _p, + unsigned int _M, + void * _userdata); +typedef struct ofdmframesync_s * ofdmframesync; + +// create OFDM framing synchronizer object +// _M : number of subcarriers, >10 typical +// _cp_len : cyclic prefix length +// _taper_len : taper length (OFDM symbol overlap) +// _p : subcarrier allocation (null, pilot, data), [size: _M x 1] +// _callback : user-defined callback function +// _userdata : user-defined data pointer +ofdmframesync ofdmframesync_create(unsigned int _M, + unsigned int _cp_len, + unsigned int _taper_len, + unsigned char * _p, + ofdmframesync_callback _callback, + void * _userdata); +int ofdmframesync_destroy(ofdmframesync _q); +int ofdmframesync_print(ofdmframesync _q); +int ofdmframesync_reset(ofdmframesync _q); +int ofdmframesync_is_frame_open(ofdmframesync _q); +int ofdmframesync_execute(ofdmframesync _q, + liquid_float_complex * _x, + unsigned int _n); + +// query methods +float ofdmframesync_get_rssi(ofdmframesync _q); // received signal strength indication +float ofdmframesync_get_cfo(ofdmframesync _q); // carrier offset estimate + +// set methods +int ofdmframesync_set_cfo(ofdmframesync _q, float _cfo); // set carrier offset estimate + +// debugging +int ofdmframesync_debug_enable(ofdmframesync _q); +int ofdmframesync_debug_disable(ofdmframesync _q); +int ofdmframesync_debug_print(ofdmframesync _q, const char * _filename); + + +// +// MODULE : nco (numerically-controlled oscillator) +// + +// oscillator type +// LIQUID_NCO : numerically-controlled oscillator (fast) +// LIQUID_VCO : "voltage"-controlled oscillator (precise) +typedef enum { + LIQUID_NCO=0, + LIQUID_VCO +} liquid_ncotype; + +#define LIQUID_NCO_MANGLE_FLOAT(name) LIQUID_CONCAT(nco_crcf, name) + +// large macro +// NCO : name-mangling macro +// T : primitive data type +// TC : input/output data type +#define LIQUID_NCO_DEFINE_API(NCO,T,TC) \ + \ +/* Numerically-controlled oscillator object */ \ +typedef struct NCO(_s) * NCO(); \ + \ +/* Create nco object with either fixed-point or floating-point phase */ \ +/* _type : oscillator type, _type in {LIQUID_NCO, LIQUID_VCO} */ \ +NCO() NCO(_create)(liquid_ncotype _type); \ + \ +/* Destroy nco object, freeing all internally allocated memory */ \ +int NCO(_destroy)(NCO() _q); \ + \ +/* Print nco object internals to stdout */ \ +int NCO(_print)(NCO() _q); \ + \ +/* Set phase/frequency to zero and reset the phase-locked loop filter */ \ +/* state */ \ +int NCO(_reset)(NCO() _q); \ + \ +/* Get frequency of nco object in radians per sample */ \ +T NCO(_get_frequency)(NCO() _q); \ + \ +/* Set frequency of nco object in radians per sample */ \ +/* _q : nco object */ \ +/* _dtheta : input frequency [radians/sample] */ \ +int NCO(_set_frequency)(NCO() _q, \ + T _dtheta); \ + \ +/* Adjust frequency of nco object by a step size in radians per sample */ \ +/* _q : nco object */ \ +/* _step : input frequency step [radians/sample] */ \ +int NCO(_adjust_frequency)(NCO() _q, \ + T _step); \ + \ +/* Get phase of nco object in radians */ \ +T NCO(_get_phase)(NCO() _q); \ + \ +/* Set phase of nco object in radians */ \ +/* _q : nco object */ \ +/* _phi : input phase of nco object [radians] */ \ +int NCO(_set_phase)(NCO() _q, \ + T _phi); \ + \ +/* Adjust phase of nco object by a step of \(\Delta \phi\) radians */ \ +/* _q : nco object */ \ +/* _dphi : input nco object phase adjustment [radians] */ \ +int NCO(_adjust_phase)(NCO() _q, \ + T _dphi); \ + \ +/* Increment phase by internal phase step (frequency) */ \ +int NCO(_step)(NCO() _q); \ + \ +/* Compute sine output given internal phase */ \ +T NCO(_sin)(NCO() _q); \ + \ +/* Compute cosine output given internal phase */ \ +T NCO(_cos)(NCO() _q); \ + \ +/* Compute sine and cosine outputs given internal phase */ \ +/* _q : nco object */ \ +/* _s : output sine component of phase */ \ +/* _c : output cosine component of phase */ \ +int NCO(_sincos)(NCO() _q, \ + T * _s, \ + T * _c); \ + \ +/* Compute complex exponential output given internal phase */ \ +/* _q : nco object */ \ +/* _y : output complex exponential */ \ +int NCO(_cexpf)(NCO() _q, \ + TC * _y); \ + \ +/* Set bandwidth of internal phase-locked loop */ \ +/* _q : nco object */ \ +/* _bw : input phase-locked loop bandwidth, _bw >= 0 */ \ +int NCO(_pll_set_bandwidth)(NCO() _q, \ + T _bw); \ + \ +/* Step internal phase-locked loop given input phase error, adjusting */ \ +/* internal phase and frequency proportional to coefficients defined by */ \ +/* internal PLL bandwidth */ \ +/* _q : nco object */ \ +/* _dphi : input phase-locked loop phase error */ \ +int NCO(_pll_step)(NCO() _q, \ + T _dphi); \ + \ +/* Rotate input sample up by nco angle. */ \ +/* Note that this does not adjust the internal phase or frequency. */ \ +/* _q : nco object */ \ +/* _x : input complex sample */ \ +/* _y : pointer to output sample location */ \ +int NCO(_mix_up)(NCO() _q, \ + TC _x, \ + TC * _y); \ + \ +/* Rotate input sample down by nco angle. */ \ +/* Note that this does not adjust the internal phase or frequency. */ \ +/* _q : nco object */ \ +/* _x : input complex sample */ \ +/* _y : pointer to output sample location */ \ +int NCO(_mix_down)(NCO() _q, \ + TC _x, \ + TC * _y); \ + \ +/* Rotate input vector up by NCO angle (stepping) */ \ +/* Note that this *does* adjust the internal phase as the signal steps */ \ +/* through each input sample. */ \ +/* _q : nco object */ \ +/* _x : array of input samples, [size: _n x 1] */ \ +/* _y : array of output samples, [size: _n x 1] */ \ +/* _n : number of input (and output) samples */ \ +int NCO(_mix_block_up)(NCO() _q, \ + TC * _x, \ + TC * _y, \ + unsigned int _n); \ + \ +/* Rotate input vector down by NCO angle (stepping) */ \ +/* Note that this *does* adjust the internal phase as the signal steps */ \ +/* through each input sample. */ \ +/* _q : nco object */ \ +/* _x : array of input samples, [size: _n x 1] */ \ +/* _y : array of output samples, [size: _n x 1] */ \ +/* _n : number of input (and output) samples */ \ +int NCO(_mix_block_down)(NCO() _q, \ + TC * _x, \ + TC * _y, \ + unsigned int _n); \ + +// Define nco APIs +LIQUID_NCO_DEFINE_API(LIQUID_NCO_MANGLE_FLOAT, float, liquid_float_complex) + + +// nco utilities + +// unwrap phase of array (basic) +void liquid_unwrap_phase(float * _theta, unsigned int _n); + +// unwrap phase of array (advanced) +void liquid_unwrap_phase2(float * _theta, unsigned int _n); + +#define SYNTH_MANGLE_FLOAT(name) LIQUID_CONCAT(synth_crcf, name) + +// large macro +// SYNTH : name-mangling macro +// T : primitive data type +// TC : input/output data type +#define LIQUID_SYNTH_DEFINE_API(SYNTH,T,TC) \ +typedef struct SYNTH(_s) * SYNTH(); \ + \ +SYNTH() SYNTH(_create)(const TC *_table, unsigned int _length); \ +void SYNTH(_destroy)(SYNTH() _q); \ + \ +void SYNTH(_reset)(SYNTH() _q); \ + \ +/* get/set/adjust internal frequency/phase */ \ +T SYNTH(_get_frequency)( SYNTH() _q); \ +void SYNTH(_set_frequency)( SYNTH() _q, T _f); \ +void SYNTH(_adjust_frequency)(SYNTH() _q, T _df); \ +T SYNTH(_get_phase)( SYNTH() _q); \ +void SYNTH(_set_phase)( SYNTH() _q, T _phi); \ +void SYNTH(_adjust_phase)( SYNTH() _q, T _dphi); \ + \ +unsigned int SYNTH(_get_length)(SYNTH() _q); \ +TC SYNTH(_get_current)(SYNTH() _q); \ +TC SYNTH(_get_half_previous)(SYNTH() _q); \ +TC SYNTH(_get_half_next)(SYNTH() _q); \ + \ +void SYNTH(_step)(SYNTH() _q); \ + \ +/* pll : phase-locked loop */ \ +void SYNTH(_pll_set_bandwidth)(SYNTH() _q, T _bandwidth); \ +void SYNTH(_pll_step)(SYNTH() _q, T _dphi); \ + \ +/* Rotate input sample up by SYNTH angle (no stepping) */ \ +void SYNTH(_mix_up)(SYNTH() _q, TC _x, TC *_y); \ + \ +/* Rotate input sample down by SYNTH angle (no stepping) */ \ +void SYNTH(_mix_down)(SYNTH() _q, TC _x, TC *_y); \ + \ +/* Rotate input vector up by SYNTH angle (stepping) */ \ +void SYNTH(_mix_block_up)(SYNTH() _q, \ + TC *_x, \ + TC *_y, \ + unsigned int _N); \ + \ +/* Rotate input vector down by SYNTH angle (stepping) */ \ +void SYNTH(_mix_block_down)(SYNTH() _q, \ + TC *_x, \ + TC *_y, \ + unsigned int _N); \ + \ +void SYNTH(_spread)(SYNTH() _q, \ + TC _x, \ + TC *_y); \ + \ +void SYNTH(_despread)(SYNTH() _q, \ + TC *_x, \ + TC *_y); \ + \ +void SYNTH(_despread_triple)(SYNTH() _q, \ + TC *_x, \ + TC *_early, \ + TC *_punctual, \ + TC *_late); \ + +// Define synth APIs +LIQUID_SYNTH_DEFINE_API(SYNTH_MANGLE_FLOAT, float, liquid_float_complex) + + + +// +// MODULE : optimization +// + +// utility function pointer definition +typedef float (*utility_function)(void * _userdata, + float * _v, + unsigned int _n); + +// n-dimensional Rosenbrock utility function (minimum at _v = {1,1,1...} +// _userdata : user-defined data structure (convenience) +// _v : input vector [size: _n x 1] +// _n : input vector size +float liquid_rosenbrock(void * _userdata, + float * _v, + unsigned int _n); + +// n-dimensional inverse Gauss utility function (minimum at _v = {0,0,0...} +// _userdata : user-defined data structure (convenience) +// _v : input vector [size: _n x 1] +// _n : input vector size +float liquid_invgauss(void * _userdata, + float * _v, + unsigned int _n); + +// n-dimensional multimodal utility function (minimum at _v = {0,0,0...} +// _userdata : user-defined data structure (convenience) +// _v : input vector [size: _n x 1] +// _n : input vector size +float liquid_multimodal(void * _userdata, + float * _v, + unsigned int _n); + +// n-dimensional spiral utility function (minimum at _v = {0,0,0...} +// _userdata : user-defined data structure (convenience) +// _v : input vector [size: _n x 1] +// _n : input vector size +float liquid_spiral(void * _userdata, + float * _v, + unsigned int _n); + + +// +// Gradient search +// + +#define LIQUID_OPTIM_MINIMIZE (0) +#define LIQUID_OPTIM_MAXIMIZE (1) + +typedef struct gradsearch_s * gradsearch; + +// Create a gradient search object +// _userdata : user data object pointer +// _v : array of parameters to optimize +// _num_parameters : array length (number of parameters to optimize) +// _u : utility function pointer +// _direction : search direction (e.g. LIQUID_OPTIM_MAXIMIZE) +gradsearch gradsearch_create(void * _userdata, + float * _v, + unsigned int _num_parameters, + utility_function _utility, + int _direction); + +// Destroy a gradsearch object +void gradsearch_destroy(gradsearch _q); + +// Prints current status of search +void gradsearch_print(gradsearch _q); + +// Iterate once +float gradsearch_step(gradsearch _q); + +// Execute the search +float gradsearch_execute(gradsearch _q, + unsigned int _max_iterations, + float _target_utility); + + +// quasi-Newton search +typedef struct qnsearch_s * qnsearch; + +// Create a simple qnsearch object; parameters are specified internally +// _userdata : userdata +// _v : array of parameters to optimize +// _num_parameters : array length +// _get_utility : utility function pointer +// _direction : search direction (e.g. LIQUID_OPTIM_MAXIMIZE) +qnsearch qnsearch_create(void * _userdata, + float * _v, + unsigned int _num_parameters, + utility_function _u, + int _direction); + +// Destroy a qnsearch object +int qnsearch_destroy(qnsearch _g); + +// Prints current status of search +int qnsearch_print(qnsearch _g); + +// Resets internal state +int qnsearch_reset(qnsearch _g); + +// Iterate once +int qnsearch_step(qnsearch _g); + +// Execute the search +float qnsearch_execute(qnsearch _g, + unsigned int _max_iterations, + float _target_utility); + +// +// chromosome (for genetic algorithm search) +// +typedef struct chromosome_s * chromosome; + +// create a chromosome object, variable bits/trait +chromosome chromosome_create(unsigned int * _bits_per_trait, + unsigned int _num_traits); + +// create a chromosome object, all traits same resolution +chromosome chromosome_create_basic(unsigned int _num_traits, + unsigned int _bits_per_trait); + +// create a chromosome object, cloning a parent +chromosome chromosome_create_clone(chromosome _parent); + +// copy existing chromosomes' internal traits (all other internal +// parameters must be equal) +int chromosome_copy(chromosome _parent, chromosome _child); + +// Destroy a chromosome object +int chromosome_destroy(chromosome _c); + +// get number of traits in chromosome +unsigned int chromosome_get_num_traits(chromosome _c); + +// Print chromosome values to screen (binary representation) +int chromosome_print(chromosome _c); + +// Print chromosome values to screen (floating-point representation) +int chromosome_printf(chromosome _c); + +// clear chromosome (set traits to zero) +int chromosome_reset(chromosome _c); + +// initialize chromosome on integer values +int chromosome_init(chromosome _c, + unsigned int * _v); + +// initialize chromosome on floating-point values +int chromosome_initf(chromosome _c, float * _v); + +// Mutates chromosome _c at _index +int chromosome_mutate(chromosome _c, unsigned int _index); + +// Resulting chromosome _c is a crossover of parents _p1 and _p2 at _threshold +int chromosome_crossover(chromosome _p1, + chromosome _p2, + chromosome _c, + unsigned int _threshold); + +// Initializes chromosome to random value +int chromosome_init_random(chromosome _c); + +// Returns integer representation of chromosome +unsigned int chromosome_value(chromosome _c, + unsigned int _index); + +// Returns floating-point representation of chromosome +float chromosome_valuef(chromosome _c, + unsigned int _index); + +// +// genetic algorithm search +// +typedef struct gasearch_s * gasearch; + +typedef float (*gasearch_utility)(void * _userdata, chromosome _c); + +// Create a simple gasearch object; parameters are specified internally +// _utility : chromosome fitness utility function +// _userdata : user data, void pointer passed to _get_utility() callback +// _parent : initial population parent chromosome, governs precision, etc. +// _minmax : search direction +gasearch gasearch_create(gasearch_utility _u, + void * _userdata, + chromosome _parent, + int _minmax); + +// Create a gasearch object, specifying search parameters +// _utility : chromosome fitness utility function +// _userdata : user data, void pointer passed to _get_utility() callback +// _parent : initial population parent chromosome, governs precision, etc. +// _minmax : search direction +// _population_size : number of chromosomes in population +// _mutation_rate : probability of mutating chromosomes +gasearch gasearch_create_advanced(gasearch_utility _utility, + void * _userdata, + chromosome _parent, + int _minmax, + unsigned int _population_size, + float _mutation_rate); + + +// Destroy a gasearch object +int gasearch_destroy(gasearch _q); + +// print search parameter internals +int gasearch_print(gasearch _q); + +// set mutation rate +int gasearch_set_mutation_rate(gasearch _q, + float _mutation_rate); + +// set population/selection size +// _q : ga search object +// _population_size : new population size (number of chromosomes) +// _selection_size : selection size (number of parents for new generation) +int gasearch_set_population_size(gasearch _q, + unsigned int _population_size, + unsigned int _selection_size); + +// Execute the search +// _q : ga search object +// _max_iterations : maximum number of iterations to run before bailing +// _target_utility : target utility +float gasearch_run(gasearch _q, + unsigned int _max_iterations, + float _target_utility); + +// iterate over one evolution of the search algorithm +int gasearch_evolve(gasearch _q); + +// get optimal chromosome +// _q : ga search object +// _c : output optimal chromosome +// _utility_opt : fitness of _c +int gasearch_getopt(gasearch _q, + chromosome _c, + float * _utility_opt); + +// +// MODULE : quantization +// + +float compress_mulaw(float _x, float _mu); +float expand_mulaw(float _x, float _mu); + +int compress_cf_mulaw(liquid_float_complex _x, float _mu, liquid_float_complex * _y); +int expand_cf_mulaw(liquid_float_complex _y, float _mu, liquid_float_complex * _x); + +//float compress_alaw(float _x, float _a); +//float expand_alaw(float _x, float _a); + +// inline quantizer: 'analog' signal in [-1, 1] +unsigned int quantize_adc(float _x, unsigned int _num_bits); +float quantize_dac(unsigned int _s, unsigned int _num_bits); + +// structured quantizer + +typedef enum { + LIQUID_COMPANDER_NONE=0, + LIQUID_COMPANDER_LINEAR, + LIQUID_COMPANDER_MULAW, + LIQUID_COMPANDER_ALAW +} liquid_compander_type; + +#define LIQUID_QUANTIZER_MANGLE_FLOAT(name) LIQUID_CONCAT(quantizerf, name) +#define LIQUID_QUANTIZER_MANGLE_CFLOAT(name) LIQUID_CONCAT(quantizercf, name) + +// large macro +// QUANTIZER : name-mangling macro +// T : data type +#define LIQUID_QUANTIZER_DEFINE_API(QUANTIZER,T) \ + \ +/* Amplitude quantization object */ \ +typedef struct QUANTIZER(_s) * QUANTIZER(); \ + \ +/* Create quantizer object given compander type, input range, and the */ \ +/* number of bits to represent the output */ \ +/* _ctype : compander type (linear, mulaw, alaw) */ \ +/* _range : maximum abosolute input range (ignored for now) */ \ +/* _num_bits : number of bits per sample */ \ +QUANTIZER() QUANTIZER(_create)(liquid_compander_type _ctype, \ + float _range, \ + unsigned int _num_bits); \ + \ +/* Destroy object, freeing all internally-allocated memory. */ \ +int QUANTIZER(_destroy)(QUANTIZER() _q); \ + \ +/* Print object properties to stdout, including compander type and */ \ +/* number of bits per sample */ \ +int QUANTIZER(_print)(QUANTIZER() _q); \ + \ +/* Execute quantizer as analog-to-digital converter, accepting input */ \ +/* sample and returning digitized output bits */ \ +/* _q : quantizer object */ \ +/* _x : input sample */ \ +/* _s : output bits */ \ +int QUANTIZER(_execute_adc)(QUANTIZER() _q, \ + T _x, \ + unsigned int * _s); \ + \ +/* Execute quantizer as digital-to-analog converter, accepting input */ \ +/* bits and returning representation of original input sample */ \ +/* _q : quantizer object */ \ +/* _s : input bits */ \ +/* _x : output sample */ \ +int QUANTIZER(_execute_dac)(QUANTIZER() _q, \ + unsigned int _s, \ + T * _x); \ + +LIQUID_QUANTIZER_DEFINE_API(LIQUID_QUANTIZER_MANGLE_FLOAT, float) +LIQUID_QUANTIZER_DEFINE_API(LIQUID_QUANTIZER_MANGLE_CFLOAT, liquid_float_complex) + + +// +// MODULE : random (number generators) +// + + +// Uniform random number generator, [0,1) +float randf(); +float randf_pdf(float _x); +float randf_cdf(float _x); + +// Uniform random number generator with arbitrary bounds, [a,b) +float randuf(float _a, float _b); +float randuf_pdf(float _x, float _a, float _b); +float randuf_cdf(float _x, float _a, float _b); + +// Gauss random number generator, N(0,1) +// f(x) = 1/sqrt(2*pi*sigma^2) * exp{-(x-eta)^2/(2*sigma^2)} +// +// where +// eta = mean +// sigma = standard deviation +// +float randnf(); +void awgn(float *_x, float _nstd); +void crandnf(liquid_float_complex *_y); +void cawgn(liquid_float_complex *_x, float _nstd); +float randnf_pdf(float _x, float _eta, float _sig); +float randnf_cdf(float _x, float _eta, float _sig); + +// Exponential +// f(x) = lambda exp{ -lambda x } +// where +// lambda = spread parameter, lambda > 0 +// x >= 0 +float randexpf(float _lambda); +float randexpf_pdf(float _x, float _lambda); +float randexpf_cdf(float _x, float _lambda); + +// Weibull +// f(x) = (a/b) (x/b)^(a-1) exp{ -(x/b)^a } +// where +// a = alpha : shape parameter +// b = beta : scaling parameter +// g = gamma : location (threshold) parameter +// +float randweibf(float _alpha, float _beta, float _gamma); +float randweibf_pdf(float _x, float _a, float _b, float _g); +float randweibf_cdf(float _x, float _a, float _b, float _g); + +// Gamma +// x^(a-1) exp(-x/b) +// f(x) = ------------------- +// Gamma(a) b^a +// where +// a = alpha : shape parameter, a > 0 +// b = beta : scale parameter, b > 0 +// Gamma(z) = regular gamma function +// x >= 0 +float randgammaf(float _alpha, float _beta); +float randgammaf_pdf(float _x, float _alpha, float _beta); +float randgammaf_cdf(float _x, float _alpha, float _beta); + +// Nakagami-m +// f(x) = (2/Gamma(m)) (m/omega)^m x^(2m-1) exp{-(m/omega)x^2} +// where +// m : shape parameter, m >= 0.5 +// omega : spread parameter, omega > 0 +// Gamma(z): regular complete gamma function +// x >= 0 +float randnakmf(float _m, float _omega); +float randnakmf_pdf(float _x, float _m, float _omega); +float randnakmf_cdf(float _x, float _m, float _omega); + +// Rice-K +// f(x) = (x/sigma^2) exp{ -(x^2+s^2)/(2sigma^2) } I0( x s / sigma^2 ) +// where +// s = sqrt( omega*K/(K+1) ) +// sigma = sqrt(0.5 omega/(K+1)) +// and +// K = shape parameter +// omega = spread parameter +// I0 = modified Bessel function of the first kind +// x >= 0 +float randricekf(float _K, float _omega); +float randricekf_cdf(float _x, float _K, float _omega); +float randricekf_pdf(float _x, float _K, float _omega); + + +// Data scrambler : whiten data sequence +void scramble_data(unsigned char * _x, unsigned int _len); +void unscramble_data(unsigned char * _x, unsigned int _len); +void unscramble_data_soft(unsigned char * _x, unsigned int _len); + +// +// MODULE : sequence +// + +// Binary sequence (generic) + +typedef struct bsequence_s * bsequence; + +// Create a binary sequence of a specific length (number of bits) +bsequence bsequence_create(unsigned int num_bits); + +// Free memory in a binary sequence +int bsequence_destroy(bsequence _bs); + +// Clear binary sequence (set to 0's) +int bsequence_reset(bsequence _bs); + +// initialize sequence on external array +int bsequence_init(bsequence _bs, + unsigned char * _v); + +// Print sequence to the screen +int bsequence_print(bsequence _bs); + +// Push bit into to back of a binary sequence +int bsequence_push(bsequence _bs, + unsigned int _bit); + +// circular shift (left) +int bsequence_circshift(bsequence _bs); + +// Correlate two binary sequences together +int bsequence_correlate(bsequence _bs1, bsequence _bs2); + +// compute the binary addition of two bit sequences +int bsequence_add(bsequence _bs1, bsequence _bs2, bsequence _bs3); + +// compute the binary multiplication of two bit sequences +int bsequence_mul(bsequence _bs1, bsequence _bs2, bsequence _bs3); + +// accumulate the 1's in a binary sequence +unsigned int bsequence_accumulate(bsequence _bs); + +// accessor functions +unsigned int bsequence_get_length(bsequence _bs); +unsigned int bsequence_index(bsequence _bs, unsigned int _i); + +// Complementary codes + +// intialize two sequences to complementary codes. sequences must +// be of length at least 8 and a power of 2 (e.g. 8, 16, 32, 64,...) +// _a : sequence 'a' (bsequence object) +// _b : sequence 'b' (bsequence object) +int bsequence_create_ccodes(bsequence _a, bsequence _b); + + +// M-Sequence + +#define LIQUID_MAX_MSEQUENCE_LENGTH 32767 + +// default m-sequence generators: g (hex) m n g (oct) g (binary) +#define LIQUID_MSEQUENCE_GENPOLY_M2 0x0007 // 2 3 7 111 +#define LIQUID_MSEQUENCE_GENPOLY_M3 0x000B // 3 7 13 1011 +#define LIQUID_MSEQUENCE_GENPOLY_M4 0x0013 // 4 15 23 10011 +#define LIQUID_MSEQUENCE_GENPOLY_M5 0x0025 // 5 31 45 100101 +#define LIQUID_MSEQUENCE_GENPOLY_M6 0x0043 // 6 63 103 1000011 +#define LIQUID_MSEQUENCE_GENPOLY_M7 0x0089 // 7 127 211 10001001 +#define LIQUID_MSEQUENCE_GENPOLY_M8 0x011D // 8 255 435 100101101 +#define LIQUID_MSEQUENCE_GENPOLY_M9 0x0211 // 9 511 1021 1000010001 +#define LIQUID_MSEQUENCE_GENPOLY_M10 0x0409 // 10 1023 2011 10000001001 +#define LIQUID_MSEQUENCE_GENPOLY_M11 0x0805 // 11 2047 4005 100000000101 +#define LIQUID_MSEQUENCE_GENPOLY_M12 0x1053 // 12 4095 10123 1000001010011 +#define LIQUID_MSEQUENCE_GENPOLY_M13 0x201b // 13 8191 20033 10000000011011 +#define LIQUID_MSEQUENCE_GENPOLY_M14 0x402b // 14 16383 40053 100000000101011 +#define LIQUID_MSEQUENCE_GENPOLY_M15 0x8003 // 15 32767 100003 1000000000000011 + +typedef struct msequence_s * msequence; + +// create a maximal-length sequence (m-sequence) object with +// an internal shift register length of _m bits. +// _m : generator polynomial length, sequence length is (2^m)-1 +// _g : generator polynomial, starting with most-significant bit +// _a : initial shift register state, default: 000...001 +msequence msequence_create(unsigned int _m, + unsigned int _g, + unsigned int _a); + +// create a maximal-length sequence (m-sequence) object from a generator polynomial +msequence msequence_create_genpoly(unsigned int _g); + +// creates a default maximal-length sequence +msequence msequence_create_default(unsigned int _m); + +// destroy an msequence object, freeing all internal memory +int msequence_destroy(msequence _m); + +// prints the sequence's internal state to the screen +int msequence_print(msequence _m); + +// advance msequence on shift register, returning output bit +unsigned int msequence_advance(msequence _ms); + +// generate pseudo-random symbol from shift register by +// advancing _bps bits and returning compacted symbol +// _ms : m-sequence object +// _bps : bits per symbol of output +unsigned int msequence_generate_symbol(msequence _ms, + unsigned int _bps); + +// reset msequence shift register to original state, typically '1' +int msequence_reset(msequence _ms); + +// initialize a bsequence object on an msequence object +// _bs : bsequence object +// _ms : msequence object +int bsequence_init_msequence(bsequence _bs, + msequence _ms); + +// get the length of the sequence +unsigned int msequence_get_length(msequence _ms); + +// get the internal state of the sequence +unsigned int msequence_get_state(msequence _ms); + +// set the internal state of the sequence +int msequence_set_state(msequence _ms, + unsigned int _a); + + +// +// MODULE : utility +// + +// pack binary array with symbol(s) +// _src : source array [size: _n x 1] +// _n : input source array length +// _k : bit index to write in _src +// _b : number of bits in input symbol +// _sym_in : input symbol +int liquid_pack_array(unsigned char * _src, + unsigned int _n, + unsigned int _k, + unsigned int _b, + unsigned char _sym_in); + +// unpack symbols from binary array +// _src : source array [size: _n x 1] +// _n : input source array length +// _k : bit index to write in _src +// _b : number of bits in output symbol +// _sym_out : output symbol +int liquid_unpack_array(unsigned char * _src, + unsigned int _n, + unsigned int _k, + unsigned int _b, + unsigned char * _sym_out); + +// pack one-bit symbols into bytes (8-bit symbols) +// _sym_in : input symbols array [size: _sym_in_len x 1] +// _sym_in_len : number of input symbols +// _sym_out : output symbols +// _sym_out_len : number of bytes allocated to output symbols array +// _num_written : number of output symbols actually written +int liquid_pack_bytes(unsigned char * _sym_in, + unsigned int _sym_in_len, + unsigned char * _sym_out, + unsigned int _sym_out_len, + unsigned int * _num_written); + +// unpack 8-bit symbols (full bytes) into one-bit symbols +// _sym_in : input symbols array [size: _sym_in_len x 1] +// _sym_in_len : number of input symbols +// _sym_out : output symbols array +// _sym_out_len : number of bytes allocated to output symbols array +// _num_written : number of output symbols actually written +int liquid_unpack_bytes(unsigned char * _sym_in, + unsigned int _sym_in_len, + unsigned char * _sym_out, + unsigned int _sym_out_len, + unsigned int * _num_written); + +// repack bytes with arbitrary symbol sizes +// _sym_in : input symbols array [size: _sym_in_len x 1] +// _sym_in_bps : number of bits per input symbol +// _sym_in_len : number of input symbols +// _sym_out : output symbols array +// _sym_out_bps : number of bits per output symbol +// _sym_out_len : number of bytes allocated to output symbols array +// _num_written : number of output symbols actually written +int liquid_repack_bytes(unsigned char * _sym_in, + unsigned int _sym_in_bps, + unsigned int _sym_in_len, + unsigned char * _sym_out, + unsigned int _sym_out_bps, + unsigned int _sym_out_len, + unsigned int * _num_written); + +// shift array to the left _b bits, filling in zeros +// _src : source address [size: _n x 1] +// _n : input data array size +// _b : number of bits to shift +int liquid_lbshift(unsigned char * _src, + unsigned int _n, + unsigned int _b); + +// shift array to the right _b bits, filling in zeros +// _src : source address [size: _n x 1] +// _n : input data array size +// _b : number of bits to shift +int liquid_rbshift(unsigned char * _src, + unsigned int _n, + unsigned int _b); + +// circularly shift array to the left _b bits +// _src : source address [size: _n x 1] +// _n : input data array size +// _b : number of bits to shift +int liquid_lbcircshift(unsigned char * _src, + unsigned int _n, + unsigned int _b); + +// circularly shift array to the right _b bits +// _src : source address [size: _n x 1] +// _n : input data array size +// _b : number of bits to shift +int liquid_rbcircshift(unsigned char * _src, + unsigned int _n, + unsigned int _b); + +// shift array to the left _b bytes, filling in zeros +// _src : source address [size: _n x 1] +// _n : input data array size +// _b : number of bytes to shift +int liquid_lshift(unsigned char * _src, + unsigned int _n, + unsigned int _b); + +// shift array to the right _b bytes, filling in zeros +// _src : source address [size: _n x 1] +// _n : input data array size +// _b : number of bytes to shift +int liquid_rshift(unsigned char * _src, + unsigned int _n, + unsigned int _b); + +// circular shift array to the left _b bytes +// _src : source address [size: _n x 1] +// _n : input data array size +// _b : number of bytes to shift +int liquid_lcircshift(unsigned char * _src, + unsigned int _n, + unsigned int _b); + +// circular shift array to the right _b bytes +// _src : source address [size: _n x 1] +// _n : input data array size +// _b : number of bytes to shift +int liquid_rcircshift(unsigned char * _src, + unsigned int _n, + unsigned int _b); + +// Count the number of ones in an integer +unsigned int liquid_count_ones(unsigned int _x); + +// count number of ones in an integer, modulo 2 +unsigned int liquid_count_ones_mod2(unsigned int _x); + +// compute bindary dot-product between two integers +unsigned int liquid_bdotprod(unsigned int _x, + unsigned int _y); + +// Count leading zeros in an integer +unsigned int liquid_count_leading_zeros(unsigned int _x); + +// Most-significant bit index +unsigned int liquid_msb_index(unsigned int _x); + +// Print string of bits to stdout +int liquid_print_bitstring(unsigned int _x, unsigned int _n); + +// reverse byte, word, etc. +unsigned char liquid_reverse_byte( unsigned char _x); +unsigned int liquid_reverse_uint16(unsigned int _x); +unsigned int liquid_reverse_uint24(unsigned int _x); +unsigned int liquid_reverse_uint32(unsigned int _x); + +// get scale for constant, particularly for plotting purposes +// _val : input value (e.g. 100e6) +// _unit : output unit character (e.g. 'M') +// _scale : output scale (e.g. 1e-6) +int liquid_get_scale(float _val, + char * _unit, + float * _scale); + +// +// MODULE : vector +// + +#define LIQUID_VECTOR_MANGLE_RF(name) LIQUID_CONCAT(liquid_vectorf, name) +#define LIQUID_VECTOR_MANGLE_CF(name) LIQUID_CONCAT(liquid_vectorcf,name) + +// large macro +// VECTOR : name-mangling macro +// T : data type +// TP : data type (primitive) +#define LIQUID_VECTOR_DEFINE_API(VECTOR,T,TP) \ + \ +/* Initialize vector with scalar: x[i] = c (scalar) */ \ +void VECTOR(_init)(T _c, \ + T * _x, \ + unsigned int _n); \ + \ +/* Add each element pointwise: z[i] = x[i] + y[i] */ \ +void VECTOR(_add)(T * _x, \ + T * _y, \ + unsigned int _n, \ + T * _z); \ + \ +/* Add scalar to each element: y[i] = x[i] + c */ \ +void VECTOR(_addscalar)(T * _x, \ + unsigned int _n, \ + T _c, \ + T * _y); \ + \ +/* Multiply each element pointwise: z[i] = x[i] * y[i] */ \ +void VECTOR(_mul)(T * _x, \ + T * _y, \ + unsigned int _n, \ + T * _z); \ + \ +/* Multiply each element with scalar: y[i] = x[i] * c */ \ +void VECTOR(_mulscalar)(T * _x, \ + unsigned int _n, \ + T _c, \ + T * _y); \ + \ +/* Compute complex phase rotation: x[i] = exp{j theta[i]} */ \ +void VECTOR(_cexpj)(TP * _theta, \ + unsigned int _n, \ + T * _x); \ + \ +/* Compute angle of each element: theta[i] = arg{ x[i] } */ \ +void VECTOR(_carg)(T * _x, \ + unsigned int _n, \ + TP * _theta); \ + \ +/* Compute absolute value of each element: y[i] = |x[i]| */ \ +void VECTOR(_abs)(T * _x, \ + unsigned int _n, \ + TP * _y); \ + \ +/* Compute sum of squares: sum{ |x|^2 } */ \ +TP VECTOR(_sumsq)(T * _x, \ + unsigned int _n); \ + \ +/* Compute l-2 norm: sqrt{ sum{ |x|^2 } } */ \ +TP VECTOR(_norm)(T * _x, \ + unsigned int _n); \ + \ +/* Compute l-p norm: { sum{ |x|^p } }^(1/p) */ \ +TP VECTOR(_pnorm)(T * _x, \ + unsigned int _n, \ + TP _p); \ + \ +/* Scale vector elements by l-2 norm: y[i] = x[i]/norm(x) */ \ +void VECTOR(_normalize)(T * _x, \ + unsigned int _n, \ + T * _y); \ + +LIQUID_VECTOR_DEFINE_API(LIQUID_VECTOR_MANGLE_RF, float, float) +LIQUID_VECTOR_DEFINE_API(LIQUID_VECTOR_MANGLE_CF, liquid_float_complex, float) + +// +// mixed types +// +#if 0 +void liquid_vectorf_add(float * _a, + float * _b, + unsigned int _n, + float * _c); +#endif + +#ifdef __cplusplus +} //extern "C" +#endif // __cplusplus + +#endif // __LIQUID_H__ + diff --git a/hsmodem/libkmaudio/pa_win_wasapi.h b/hsmodem/libkmaudio/pa_win_wasapi.h new file mode 100755 index 0000000..1d86896 --- /dev/null +++ b/hsmodem/libkmaudio/pa_win_wasapi.h @@ -0,0 +1,443 @@ +#ifndef PA_WIN_WASAPI_H +#define PA_WIN_WASAPI_H +/* + * $Id: $ + * PortAudio Portable Real-Time Audio Library + * DirectSound specific extensions + * + * Copyright (c) 1999-2007 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/** @file + @ingroup public_header + @brief WASAPI-specific PortAudio API extension header file. +*/ + +#include "portaudio.h" +#include "pa_win_waveformat.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +/* Setup flags */ +typedef enum PaWasapiFlags +{ + /* puts WASAPI into exclusive mode */ + paWinWasapiExclusive = (1 << 0), + + /* allows to skip internal PA processing completely */ + paWinWasapiRedirectHostProcessor = (1 << 1), + + /* assigns custom channel mask */ + paWinWasapiUseChannelMask = (1 << 2), + + /* selects non-Event driven method of data read/write + Note: WASAPI Event driven core is capable of 2ms latency!!!, but Polling + method can only provide 15-20ms latency. */ + paWinWasapiPolling = (1 << 3), + + /* forces custom thread priority setting, must be used if PaWasapiStreamInfo::threadPriority + is set to a custom value */ + paWinWasapiThreadPriority = (1 << 4) +} +PaWasapiFlags; +#define paWinWasapiExclusive (paWinWasapiExclusive) +#define paWinWasapiRedirectHostProcessor (paWinWasapiRedirectHostProcessor) +#define paWinWasapiUseChannelMask (paWinWasapiUseChannelMask) +#define paWinWasapiPolling (paWinWasapiPolling) +#define paWinWasapiThreadPriority (paWinWasapiThreadPriority) + + +/* Host processor. Allows to skip internal PA processing completely. + You must set paWinWasapiRedirectHostProcessor flag to PaWasapiStreamInfo::flags member + in order to have host processor redirected to your callback. + Use with caution! inputFrames and outputFrames depend solely on final device setup. + To query maximal values of inputFrames/outputFrames use PaWasapi_GetFramesPerHostBuffer. +*/ +typedef void (*PaWasapiHostProcessorCallback) (void *inputBuffer, long inputFrames, + void *outputBuffer, long outputFrames, + void *userData); + +/* Device role. */ +typedef enum PaWasapiDeviceRole +{ + eRoleRemoteNetworkDevice = 0, + eRoleSpeakers, + eRoleLineLevel, + eRoleHeadphones, + eRoleMicrophone, + eRoleHeadset, + eRoleHandset, + eRoleUnknownDigitalPassthrough, + eRoleSPDIF, + eRoleHDMI, + eRoleUnknownFormFactor +} +PaWasapiDeviceRole; + + +/* Jack connection type. */ +typedef enum PaWasapiJackConnectionType +{ + eJackConnTypeUnknown, + eJackConnType3Point5mm, + eJackConnTypeQuarter, + eJackConnTypeAtapiInternal, + eJackConnTypeRCA, + eJackConnTypeOptical, + eJackConnTypeOtherDigital, + eJackConnTypeOtherAnalog, + eJackConnTypeMultichannelAnalogDIN, + eJackConnTypeXlrProfessional, + eJackConnTypeRJ11Modem, + eJackConnTypeCombination +} +PaWasapiJackConnectionType; + + +/* Jack geometric location. */ +typedef enum PaWasapiJackGeoLocation +{ + eJackGeoLocUnk = 0, + eJackGeoLocRear = 0x1, /* matches EPcxGeoLocation::eGeoLocRear */ + eJackGeoLocFront, + eJackGeoLocLeft, + eJackGeoLocRight, + eJackGeoLocTop, + eJackGeoLocBottom, + eJackGeoLocRearPanel, + eJackGeoLocRiser, + eJackGeoLocInsideMobileLid, + eJackGeoLocDrivebay, + eJackGeoLocHDMI, + eJackGeoLocOutsideMobileLid, + eJackGeoLocATAPI, + eJackGeoLocReserved5, + eJackGeoLocReserved6, +} +PaWasapiJackGeoLocation; + + +/* Jack general location. */ +typedef enum PaWasapiJackGenLocation +{ + eJackGenLocPrimaryBox = 0, + eJackGenLocInternal, + eJackGenLocSeparate, + eJackGenLocOther +} +PaWasapiJackGenLocation; + + +/* Jack's type of port. */ +typedef enum PaWasapiJackPortConnection +{ + eJackPortConnJack = 0, + eJackPortConnIntegratedDevice, + eJackPortConnBothIntegratedAndJack, + eJackPortConnUnknown +} +PaWasapiJackPortConnection; + + +/* Thread priority. */ +typedef enum PaWasapiThreadPriority +{ + eThreadPriorityNone = 0, + eThreadPriorityAudio, //!< Default for Shared mode. + eThreadPriorityCapture, + eThreadPriorityDistribution, + eThreadPriorityGames, + eThreadPriorityPlayback, + eThreadPriorityProAudio, //!< Default for Exclusive mode. + eThreadPriorityWindowManager +} +PaWasapiThreadPriority; + + +/* Stream descriptor. */ +typedef struct PaWasapiJackDescription +{ + unsigned long channelMapping; + unsigned long color; /* derived from macro: #define RGB(r,g,b) ((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16))) */ + PaWasapiJackConnectionType connectionType; + PaWasapiJackGeoLocation geoLocation; + PaWasapiJackGenLocation genLocation; + PaWasapiJackPortConnection portConnection; + unsigned int isConnected; +} +PaWasapiJackDescription; + + +/** Stream category. + Note: + - values are equal to WASAPI AUDIO_STREAM_CATEGORY enum + - supported since Windows 8.0, noop on earler versions + - values 1,2 are deprecated on Windows 10 and not included into enumeration + + @version Available as of 19.6.0 +*/ +typedef enum PaWasapiStreamCategory +{ + eAudioCategoryOther = 0, + eAudioCategoryCommunications = 3, + eAudioCategoryAlerts = 4, + eAudioCategorySoundEffects = 5, + eAudioCategoryGameEffects = 6, + eAudioCategoryGameMedia = 7, + eAudioCategoryGameChat = 8, + eAudioCategorySpeech = 9, + eAudioCategoryMovie = 10, + eAudioCategoryMedia = 11 +} +PaWasapiStreamCategory; + + +/** Stream option. + Note: + - values are equal to WASAPI AUDCLNT_STREAMOPTIONS enum + - supported since Windows 8.1, noop on earler versions + + @version Available as of 19.6.0 +*/ +typedef enum PaWasapiStreamOption +{ + eStreamOptionNone = 0, //!< default + eStreamOptionRaw = 1, //!< bypass WASAPI Audio Engine DSP effects, supported since Windows 8.1 + eStreamOptionMatchFormat = 2 //!< force WASAPI Audio Engine into a stream format, supported since Windows 10 +} +PaWasapiStreamOption; + + +/* Stream descriptor. */ +typedef struct PaWasapiStreamInfo +{ + unsigned long size; /**< sizeof(PaWasapiStreamInfo) */ + PaHostApiTypeId hostApiType; /**< paWASAPI */ + unsigned long version; /**< 1 */ + + unsigned long flags; /**< collection of PaWasapiFlags */ + + /** Support for WAVEFORMATEXTENSIBLE channel masks. If flags contains + paWinWasapiUseChannelMask this allows you to specify which speakers + to address in a multichannel stream. Constants for channelMask + are specified in pa_win_waveformat.h. Will be used only if + paWinWasapiUseChannelMask flag is specified. + */ + PaWinWaveFormatChannelMask channelMask; + + /** Delivers raw data to callback obtained from GetBuffer() methods skipping + internal PortAudio processing inventory completely. userData parameter will + be the same that was passed to Pa_OpenStream method. Will be used only if + paWinWasapiRedirectHostProcessor flag is specified. + */ + PaWasapiHostProcessorCallback hostProcessorOutput; + PaWasapiHostProcessorCallback hostProcessorInput; + + /** Specifies thread priority explicitly. Will be used only if paWinWasapiThreadPriority flag + is specified. + + Please note, if Input/Output streams are opened simultaniously (Full-Duplex mode) + you shall specify same value for threadPriority or othervise one of the values will be used + to setup thread priority. + */ + PaWasapiThreadPriority threadPriority; + + /** Stream category. + @see PaWasapiStreamCategory + @version Available as of 19.6.0 + */ + PaWasapiStreamCategory streamCategory; + + /** Stream option. + @see PaWasapiStreamOption + @version Available as of 19.6.0 + */ + PaWasapiStreamOption streamOption; +} +PaWasapiStreamInfo; + + +/** Returns default sound format for device. Format is represented by PaWinWaveFormat or + WAVEFORMATEXTENSIBLE structure. + + @param pFormat Pointer to PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure. + @param nFormatSize Size of PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure in bytes. + @param nDevice Device index. + + @return Non-negative value indicating the number of bytes copied into format decriptor + or, a PaErrorCode (which are always negative) if PortAudio is not initialized + or an error is encountered. +*/ +int PaWasapi_GetDeviceDefaultFormat( void *pFormat, unsigned int nFormatSize, PaDeviceIndex nDevice ); + + +/** Returns device role (PaWasapiDeviceRole enum). + + @param nDevice device index. + + @return Non-negative value indicating device role or, a PaErrorCode (which are always negative) + if PortAudio is not initialized or an error is encountered. +*/ +int/*PaWasapiDeviceRole*/ PaWasapi_GetDeviceRole( PaDeviceIndex nDevice ); + + +/** Boost thread priority of calling thread (MMCSS). Use it for Blocking Interface only for thread + which makes calls to Pa_WriteStream/Pa_ReadStream. + + @param hTask Handle to pointer to priority task. Must be used with PaWasapi_RevertThreadPriority + method to revert thread priority to initial state. + + @param nPriorityClass Id of thread priority of PaWasapiThreadPriority type. Specifying + eThreadPriorityNone does nothing. + + @return Error code indicating success or failure. + @see PaWasapi_RevertThreadPriority +*/ +PaError PaWasapi_ThreadPriorityBoost( void **hTask, PaWasapiThreadPriority nPriorityClass ); + + +/** Boost thread priority of calling thread (MMCSS). Use it for Blocking Interface only for thread + which makes calls to Pa_WriteStream/Pa_ReadStream. + + @param hTask Task handle obtained by PaWasapi_BoostThreadPriority method. + @return Error code indicating success or failure. + @see PaWasapi_BoostThreadPriority +*/ +PaError PaWasapi_ThreadPriorityRevert( void *hTask ); + + +/** Get number of frames per host buffer. This is maximal value of frames of WASAPI buffer which + can be locked for operations. Use this method as helper to findout maximal values of + inputFrames/outputFrames of PaWasapiHostProcessorCallback. + + @param pStream Pointer to PaStream to query. + @param nInput Pointer to variable to receive number of input frames. Can be NULL. + @param nOutput Pointer to variable to receive number of output frames. Can be NULL. + @return Error code indicating success or failure. + @see PaWasapiHostProcessorCallback +*/ +PaError PaWasapi_GetFramesPerHostBuffer( PaStream *pStream, unsigned int *nInput, unsigned int *nOutput ); + + +/** Get number of jacks associated with a WASAPI device. Use this method to determine if + there are any jacks associated with the provided WASAPI device. Not all audio devices + will support this capability. This is valid for both input and output devices. + @param nDevice device index. + @param jcount Number of jacks is returned in this variable + @return Error code indicating success or failure + @see PaWasapi_GetJackDescription + */ +PaError PaWasapi_GetJackCount(PaDeviceIndex nDevice, int *jcount); + + +/** Get the jack description associated with a WASAPI device and jack number + Before this function is called, use PaWasapi_GetJackCount to determine the + number of jacks associated with device. If jcount is greater than zero, then + each jack from 0 to jcount can be queried with this function to get the jack + description. + @param nDevice device index. + @param jindex Which jack to return information + @param KSJACK_DESCRIPTION This structure filled in on success. + @return Error code indicating success or failure + @see PaWasapi_GetJackCount + */ +PaError PaWasapi_GetJackDescription(PaDeviceIndex nDevice, int jindex, PaWasapiJackDescription *pJackDescription); + + +/* + IMPORTANT: + + WASAPI is implemented for Callback and Blocking interfaces. It supports Shared and Exclusive + share modes. + + Exclusive Mode: + + Exclusive mode allows to deliver audio data directly to hardware bypassing + software mixing. + Exclusive mode is specified by 'paWinWasapiExclusive' flag. + + Callback Interface: + + Provides best audio quality with low latency. Callback interface is implemented in + two versions: + + 1) Event-Driven: + This is the most powerful WASAPI implementation which provides glitch-free + audio at around 3ms latency in Exclusive mode. Lowest possible latency for this mode is + 3 ms for HD Audio class audio chips. For the Shared mode latency can not be + lower than 20 ms. + + 2) Poll-Driven: + Polling is another 2-nd method to operate with WASAPI. It is less efficient than Event-Driven + and provides latency at around 10-13ms. Polling must be used to overcome a system bug + under Windows Vista x64 when application is WOW64(32-bit) and Event-Driven method simply + times out (event handle is never signalled on buffer completion). Please note, such WOW64 bug + does not exist in Vista x86 or Windows 7. + Polling can be setup by speciying 'paWinWasapiPolling' flag. Our WASAPI implementation detects + WOW64 bug and sets 'paWinWasapiPolling' automatically. + + Thread priority: + + Normally thread priority is set automatically and does not require modification. Although + if user wants some tweaking thread priority can be modified by setting 'paWinWasapiThreadPriority' + flag and specifying 'PaWasapiStreamInfo::threadPriority' with value from PaWasapiThreadPriority + enum. + + Blocking Interface: + + Blocking interface is implemented but due to above described Poll-Driven method can not + deliver lowest possible latency. Specifying too low latency in Shared mode will result in + distorted audio although Exclusive mode adds stability. + + Pa_IsFormatSupported: + + To check format with correct Share Mode (Exclusive/Shared) you must supply + PaWasapiStreamInfo with flags paWinWasapiExclusive set through member of + PaStreamParameters::hostApiSpecificStreamInfo structure. + + Pa_OpenStream: + + To set desired Share Mode (Exclusive/Shared) you must supply + PaWasapiStreamInfo with flags paWinWasapiExclusive set through member of + PaStreamParameters::hostApiSpecificStreamInfo structure. +*/ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* PA_WIN_WASAPI_H */ diff --git a/hsmodem/libkmaudio/pa_win_waveformat.h b/hsmodem/libkmaudio/pa_win_waveformat.h new file mode 100755 index 0000000..2c00267 --- /dev/null +++ b/hsmodem/libkmaudio/pa_win_waveformat.h @@ -0,0 +1,199 @@ +#ifndef PA_WIN_WAVEFORMAT_H +#define PA_WIN_WAVEFORMAT_H + +/* + * PortAudio Portable Real-Time Audio Library + * Windows WAVEFORMAT* data structure utilities + * portaudio.h should be included before this file. + * + * Copyright (c) 2007 Ross Bencina + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/** @file + @ingroup public_header + @brief Windows specific PortAudio API extension and utilities header file. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + The following #defines for speaker channel masks are the same + as those in ksmedia.h, except with PAWIN_ prepended, KSAUDIO_ removed + in some cases, and casts to PaWinWaveFormatChannelMask added. +*/ + +typedef unsigned long PaWinWaveFormatChannelMask; + +/* Speaker Positions: */ +#define PAWIN_SPEAKER_FRONT_LEFT ((PaWinWaveFormatChannelMask)0x1) +#define PAWIN_SPEAKER_FRONT_RIGHT ((PaWinWaveFormatChannelMask)0x2) +#define PAWIN_SPEAKER_FRONT_CENTER ((PaWinWaveFormatChannelMask)0x4) +#define PAWIN_SPEAKER_LOW_FREQUENCY ((PaWinWaveFormatChannelMask)0x8) +#define PAWIN_SPEAKER_BACK_LEFT ((PaWinWaveFormatChannelMask)0x10) +#define PAWIN_SPEAKER_BACK_RIGHT ((PaWinWaveFormatChannelMask)0x20) +#define PAWIN_SPEAKER_FRONT_LEFT_OF_CENTER ((PaWinWaveFormatChannelMask)0x40) +#define PAWIN_SPEAKER_FRONT_RIGHT_OF_CENTER ((PaWinWaveFormatChannelMask)0x80) +#define PAWIN_SPEAKER_BACK_CENTER ((PaWinWaveFormatChannelMask)0x100) +#define PAWIN_SPEAKER_SIDE_LEFT ((PaWinWaveFormatChannelMask)0x200) +#define PAWIN_SPEAKER_SIDE_RIGHT ((PaWinWaveFormatChannelMask)0x400) +#define PAWIN_SPEAKER_TOP_CENTER ((PaWinWaveFormatChannelMask)0x800) +#define PAWIN_SPEAKER_TOP_FRONT_LEFT ((PaWinWaveFormatChannelMask)0x1000) +#define PAWIN_SPEAKER_TOP_FRONT_CENTER ((PaWinWaveFormatChannelMask)0x2000) +#define PAWIN_SPEAKER_TOP_FRONT_RIGHT ((PaWinWaveFormatChannelMask)0x4000) +#define PAWIN_SPEAKER_TOP_BACK_LEFT ((PaWinWaveFormatChannelMask)0x8000) +#define PAWIN_SPEAKER_TOP_BACK_CENTER ((PaWinWaveFormatChannelMask)0x10000) +#define PAWIN_SPEAKER_TOP_BACK_RIGHT ((PaWinWaveFormatChannelMask)0x20000) + +/* Bit mask locations reserved for future use */ +#define PAWIN_SPEAKER_RESERVED ((PaWinWaveFormatChannelMask)0x7FFC0000) + +/* Used to specify that any possible permutation of speaker configurations */ +#define PAWIN_SPEAKER_ALL ((PaWinWaveFormatChannelMask)0x80000000) + +/* DirectSound Speaker Config */ +#define PAWIN_SPEAKER_DIRECTOUT 0 +#define PAWIN_SPEAKER_MONO (PAWIN_SPEAKER_FRONT_CENTER) +#define PAWIN_SPEAKER_STEREO (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT) +#define PAWIN_SPEAKER_QUAD (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ + PAWIN_SPEAKER_BACK_LEFT | PAWIN_SPEAKER_BACK_RIGHT) +#define PAWIN_SPEAKER_SURROUND (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ + PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_BACK_CENTER) +#define PAWIN_SPEAKER_5POINT1 (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ + PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_LOW_FREQUENCY | \ + PAWIN_SPEAKER_BACK_LEFT | PAWIN_SPEAKER_BACK_RIGHT) +#define PAWIN_SPEAKER_7POINT1 (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ + PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_LOW_FREQUENCY | \ + PAWIN_SPEAKER_BACK_LEFT | PAWIN_SPEAKER_BACK_RIGHT | \ + PAWIN_SPEAKER_FRONT_LEFT_OF_CENTER | PAWIN_SPEAKER_FRONT_RIGHT_OF_CENTER) +#define PAWIN_SPEAKER_5POINT1_SURROUND (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ + PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_LOW_FREQUENCY | \ + PAWIN_SPEAKER_SIDE_LEFT | PAWIN_SPEAKER_SIDE_RIGHT) +#define PAWIN_SPEAKER_7POINT1_SURROUND (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ + PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_LOW_FREQUENCY | \ + PAWIN_SPEAKER_BACK_LEFT | PAWIN_SPEAKER_BACK_RIGHT | \ + PAWIN_SPEAKER_SIDE_LEFT | PAWIN_SPEAKER_SIDE_RIGHT) +/* + According to the Microsoft documentation: + The following are obsolete 5.1 and 7.1 settings (they lack side speakers). Note this means + that the default 5.1 and 7.1 settings (KSAUDIO_SPEAKER_5POINT1 and KSAUDIO_SPEAKER_7POINT1 are + similarly obsolete but are unchanged for compatibility reasons). +*/ +#define PAWIN_SPEAKER_5POINT1_BACK PAWIN_SPEAKER_5POINT1 +#define PAWIN_SPEAKER_7POINT1_WIDE PAWIN_SPEAKER_7POINT1 + +/* DVD Speaker Positions */ +#define PAWIN_SPEAKER_GROUND_FRONT_LEFT PAWIN_SPEAKER_FRONT_LEFT +#define PAWIN_SPEAKER_GROUND_FRONT_CENTER PAWIN_SPEAKER_FRONT_CENTER +#define PAWIN_SPEAKER_GROUND_FRONT_RIGHT PAWIN_SPEAKER_FRONT_RIGHT +#define PAWIN_SPEAKER_GROUND_REAR_LEFT PAWIN_SPEAKER_BACK_LEFT +#define PAWIN_SPEAKER_GROUND_REAR_RIGHT PAWIN_SPEAKER_BACK_RIGHT +#define PAWIN_SPEAKER_TOP_MIDDLE PAWIN_SPEAKER_TOP_CENTER +#define PAWIN_SPEAKER_SUPER_WOOFER PAWIN_SPEAKER_LOW_FREQUENCY + + +/* + PaWinWaveFormat is defined here to provide compatibility with + compilation environments which don't have headers defining + WAVEFORMATEXTENSIBLE (e.g. older versions of MSVC, Borland C++ etc. + + The fields for WAVEFORMATEX and WAVEFORMATEXTENSIBLE are declared as an + unsigned char array here to avoid clients who include this file having + a dependency on windows.h and mmsystem.h, and also to to avoid having + to write separate packing pragmas for each compiler. +*/ +#define PAWIN_SIZEOF_WAVEFORMATEX 18 +#define PAWIN_SIZEOF_WAVEFORMATEXTENSIBLE (PAWIN_SIZEOF_WAVEFORMATEX + 22) + +typedef struct{ + unsigned char fields[ PAWIN_SIZEOF_WAVEFORMATEXTENSIBLE ]; + unsigned long extraLongForAlignment; /* ensure that compiler aligns struct to DWORD */ +} PaWinWaveFormat; + +/* + WAVEFORMATEXTENSIBLE fields: + + union { + WORD wValidBitsPerSample; + WORD wSamplesPerBlock; + WORD wReserved; + } Samples; + DWORD dwChannelMask; + GUID SubFormat; +*/ + +#define PAWIN_INDEXOF_WVALIDBITSPERSAMPLE (PAWIN_SIZEOF_WAVEFORMATEX+0) +#define PAWIN_INDEXOF_DWCHANNELMASK (PAWIN_SIZEOF_WAVEFORMATEX+2) +#define PAWIN_INDEXOF_SUBFORMAT (PAWIN_SIZEOF_WAVEFORMATEX+6) + + +/* + Valid values to pass for the waveFormatTag PaWin_InitializeWaveFormatEx and + PaWin_InitializeWaveFormatExtensible functions below. These must match + the standard Windows WAVE_FORMAT_* values. +*/ +#define PAWIN_WAVE_FORMAT_PCM (1) +#define PAWIN_WAVE_FORMAT_IEEE_FLOAT (3) +#define PAWIN_WAVE_FORMAT_DOLBY_AC3_SPDIF (0x0092) +#define PAWIN_WAVE_FORMAT_WMA_SPDIF (0x0164) + + +/* + returns PAWIN_WAVE_FORMAT_PCM or PAWIN_WAVE_FORMAT_IEEE_FLOAT + depending on the sampleFormat parameter. +*/ +int PaWin_SampleFormatToLinearWaveFormatTag( PaSampleFormat sampleFormat ); + +/* + Use the following two functions to initialize the waveformat structure. +*/ + +void PaWin_InitializeWaveFormatEx( PaWinWaveFormat *waveFormat, + int numChannels, PaSampleFormat sampleFormat, int waveFormatTag, double sampleRate ); + + +void PaWin_InitializeWaveFormatExtensible( PaWinWaveFormat *waveFormat, + int numChannels, PaSampleFormat sampleFormat, int waveFormatTag, double sampleRate, + PaWinWaveFormatChannelMask channelMask ); + + +/* Map a channel count to a speaker channel mask */ +PaWinWaveFormatChannelMask PaWin_DefaultChannelMask( int numChannels ); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* PA_WIN_WAVEFORMAT_H */ \ No newline at end of file diff --git a/hsmodem/libkmaudio/portaudio.h b/hsmodem/libkmaudio/portaudio.h new file mode 100755 index 0000000..8a94aaf --- /dev/null +++ b/hsmodem/libkmaudio/portaudio.h @@ -0,0 +1,1225 @@ +#ifndef PORTAUDIO_H +#define PORTAUDIO_H +/* + * $Id$ + * PortAudio Portable Real-Time Audio Library + * PortAudio API Header File + * Latest version available at: http://www.portaudio.com/ + * + * Copyright (c) 1999-2002 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/** @file + @ingroup public_header + @brief The portable PortAudio API. +*/ + + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/** Retrieve the release number of the currently running PortAudio build. + For example, for version "19.5.1" this will return 0x00130501. + + @see paMakeVersionNumber +*/ +int Pa_GetVersion( void ); + +/** Retrieve a textual description of the current PortAudio build, + e.g. "PortAudio V19.5.0-devel, revision 1952M". + The format of the text may change in the future. Do not try to parse the + returned string. + + @deprecated As of 19.5.0, use Pa_GetVersionInfo()->versionText instead. +*/ +const char* Pa_GetVersionText( void ); + +/** + Generate a packed integer version number in the same format used + by Pa_GetVersion(). Use this to compare a specified version number with + the currently running version. For example: + + @code + if( Pa_GetVersion() < paMakeVersionNumber(19,5,1) ) {} + @endcode + + @see Pa_GetVersion, Pa_GetVersionInfo + @version Available as of 19.5.0. +*/ +#define paMakeVersionNumber(major, minor, subminor) \ + (((major)&0xFF)<<16 | ((minor)&0xFF)<<8 | ((subminor)&0xFF)) + + +/** + A structure containing PortAudio API version information. + @see Pa_GetVersionInfo, paMakeVersionNumber + @version Available as of 19.5.0. +*/ +typedef struct PaVersionInfo { + int versionMajor; + int versionMinor; + int versionSubMinor; + /** + This is currently the Git revision hash but may change in the future. + The versionControlRevision is updated by running a script before compiling the library. + If the update does not occur, this value may refer to an earlier revision. + */ + const char *versionControlRevision; + /** Version as a string, for example "PortAudio V19.5.0-devel, revision 1952M" */ + const char *versionText; +} PaVersionInfo; + +/** Retrieve version information for the currently running PortAudio build. + @return A pointer to an immutable PaVersionInfo structure. + + @note This function can be called at any time. It does not require PortAudio + to be initialized. The structure pointed to is statically allocated. Do not + attempt to free it or modify it. + + @see PaVersionInfo, paMakeVersionNumber + @version Available as of 19.5.0. +*/ +const PaVersionInfo* Pa_GetVersionInfo(); + + +/** Error codes returned by PortAudio functions. + Note that with the exception of paNoError, all PaErrorCodes are negative. +*/ + +typedef int PaError; +typedef enum PaErrorCode +{ + paNoError = 0, + + paNotInitialized = -10000, + paUnanticipatedHostError, + paInvalidChannelCount, + paInvalidSampleRate, + paInvalidDevice, + paInvalidFlag, + paSampleFormatNotSupported, + paBadIODeviceCombination, + paInsufficientMemory, + paBufferTooBig, + paBufferTooSmall, + paNullCallback, + paBadStreamPtr, + paTimedOut, + paInternalError, + paDeviceUnavailable, + paIncompatibleHostApiSpecificStreamInfo, + paStreamIsStopped, + paStreamIsNotStopped, + paInputOverflowed, + paOutputUnderflowed, + paHostApiNotFound, + paInvalidHostApi, + paCanNotReadFromACallbackStream, + paCanNotWriteToACallbackStream, + paCanNotReadFromAnOutputOnlyStream, + paCanNotWriteToAnInputOnlyStream, + paIncompatibleStreamHostApi, + paBadBufferPtr +} PaErrorCode; + + +/** Translate the supplied PortAudio error code into a human readable + message. +*/ +const char *Pa_GetErrorText( PaError errorCode ); + + +/** Library initialization function - call this before using PortAudio. + This function initializes internal data structures and prepares underlying + host APIs for use. With the exception of Pa_GetVersion(), Pa_GetVersionText(), + and Pa_GetErrorText(), this function MUST be called before using any other + PortAudio API functions. + + If Pa_Initialize() is called multiple times, each successful + call must be matched with a corresponding call to Pa_Terminate(). + Pairs of calls to Pa_Initialize()/Pa_Terminate() may overlap, and are not + required to be fully nested. + + Note that if Pa_Initialize() returns an error code, Pa_Terminate() should + NOT be called. + + @return paNoError if successful, otherwise an error code indicating the cause + of failure. + + @see Pa_Terminate +*/ +PaError Pa_Initialize( void ); + + +/** Library termination function - call this when finished using PortAudio. + This function deallocates all resources allocated by PortAudio since it was + initialized by a call to Pa_Initialize(). In cases where Pa_Initialise() has + been called multiple times, each call must be matched with a corresponding call + to Pa_Terminate(). The final matching call to Pa_Terminate() will automatically + close any PortAudio streams that are still open. + + Pa_Terminate() MUST be called before exiting a program which uses PortAudio. + Failure to do so may result in serious resource leaks, such as audio devices + not being available until the next reboot. + + @return paNoError if successful, otherwise an error code indicating the cause + of failure. + + @see Pa_Initialize +*/ +PaError Pa_Terminate( void ); + + + +/** The type used to refer to audio devices. Values of this type usually + range from 0 to (Pa_GetDeviceCount()-1), and may also take on the PaNoDevice + and paUseHostApiSpecificDeviceSpecification values. + + @see Pa_GetDeviceCount, paNoDevice, paUseHostApiSpecificDeviceSpecification +*/ +typedef int PaDeviceIndex; + + +/** A special PaDeviceIndex value indicating that no device is available, + or should be used. + + @see PaDeviceIndex +*/ +#define paNoDevice ((PaDeviceIndex)-1) + + +/** A special PaDeviceIndex value indicating that the device(s) to be used + are specified in the host api specific stream info structure. + + @see PaDeviceIndex +*/ +#define paUseHostApiSpecificDeviceSpecification ((PaDeviceIndex)-2) + + +/* Host API enumeration mechanism */ + +/** The type used to enumerate to host APIs at runtime. Values of this type + range from 0 to (Pa_GetHostApiCount()-1). + + @see Pa_GetHostApiCount +*/ +typedef int PaHostApiIndex; + + +/** Retrieve the number of available host APIs. Even if a host API is + available it may have no devices available. + + @return A non-negative value indicating the number of available host APIs + or, a PaErrorCode (which are always negative) if PortAudio is not initialized + or an error is encountered. + + @see PaHostApiIndex +*/ +PaHostApiIndex Pa_GetHostApiCount( void ); + + +/** Retrieve the index of the default host API. The default host API will be + the lowest common denominator host API on the current platform and is + unlikely to provide the best performance. + + @return A non-negative value ranging from 0 to (Pa_GetHostApiCount()-1) + indicating the default host API index or, a PaErrorCode (which are always + negative) if PortAudio is not initialized or an error is encountered. +*/ +PaHostApiIndex Pa_GetDefaultHostApi( void ); + + +/** Unchanging unique identifiers for each supported host API. This type + is used in the PaHostApiInfo structure. The values are guaranteed to be + unique and to never change, thus allowing code to be written that + conditionally uses host API specific extensions. + + New type ids will be allocated when support for a host API reaches + "public alpha" status, prior to that developers should use the + paInDevelopment type id. + + @see PaHostApiInfo +*/ +typedef enum PaHostApiTypeId +{ + paInDevelopment=0, /* use while developing support for a new host API */ + paDirectSound=1, + paMME=2, + paASIO=3, + paSoundManager=4, + paCoreAudio=5, + paOSS=7, + paALSA=8, + paAL=9, + paBeOS=10, + paWDMKS=11, + paJACK=12, + paWASAPI=13, + paAudioScienceHPI=14 +} PaHostApiTypeId; + + +/** A structure containing information about a particular host API. */ + +typedef struct PaHostApiInfo +{ + /** this is struct version 1 */ + int structVersion; + /** The well known unique identifier of this host API @see PaHostApiTypeId */ + PaHostApiTypeId type; + /** A textual description of the host API for display on user interfaces. */ + const char *name; + + /** The number of devices belonging to this host API. This field may be + used in conjunction with Pa_HostApiDeviceIndexToDeviceIndex() to enumerate + all devices for this host API. + @see Pa_HostApiDeviceIndexToDeviceIndex + */ + int deviceCount; + + /** The default input device for this host API. The value will be a + device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice + if no default input device is available. + */ + PaDeviceIndex defaultInputDevice; + + /** The default output device for this host API. The value will be a + device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice + if no default output device is available. + */ + PaDeviceIndex defaultOutputDevice; + +} PaHostApiInfo; + + +/** Retrieve a pointer to a structure containing information about a specific + host Api. + + @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1) + + @return A pointer to an immutable PaHostApiInfo structure describing + a specific host API. If the hostApi parameter is out of range or an error + is encountered, the function returns NULL. + + The returned structure is owned by the PortAudio implementation and must not + be manipulated or freed. The pointer is only guaranteed to be valid between + calls to Pa_Initialize() and Pa_Terminate(). +*/ +const PaHostApiInfo * Pa_GetHostApiInfo( PaHostApiIndex hostApi ); + + +/** Convert a static host API unique identifier, into a runtime + host API index. + + @param type A unique host API identifier belonging to the PaHostApiTypeId + enumeration. + + @return A valid PaHostApiIndex ranging from 0 to (Pa_GetHostApiCount()-1) or, + a PaErrorCode (which are always negative) if PortAudio is not initialized + or an error is encountered. + + The paHostApiNotFound error code indicates that the host API specified by the + type parameter is not available. + + @see PaHostApiTypeId +*/ +PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type ); + + +/** Convert a host-API-specific device index to standard PortAudio device index. + This function may be used in conjunction with the deviceCount field of + PaHostApiInfo to enumerate all devices for the specified host API. + + @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1) + + @param hostApiDeviceIndex A valid per-host device index in the range + 0 to (Pa_GetHostApiInfo(hostApi)->deviceCount-1) + + @return A non-negative PaDeviceIndex ranging from 0 to (Pa_GetDeviceCount()-1) + or, a PaErrorCode (which are always negative) if PortAudio is not initialized + or an error is encountered. + + A paInvalidHostApi error code indicates that the host API index specified by + the hostApi parameter is out of range. + + A paInvalidDevice error code indicates that the hostApiDeviceIndex parameter + is out of range. + + @see PaHostApiInfo +*/ +PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi, + int hostApiDeviceIndex ); + + + +/** Structure used to return information about a host error condition. +*/ +typedef struct PaHostErrorInfo{ + PaHostApiTypeId hostApiType; /**< the host API which returned the error code */ + long errorCode; /**< the error code returned */ + const char *errorText; /**< a textual description of the error if available, otherwise a zero-length string */ +}PaHostErrorInfo; + + +/** Return information about the last host error encountered. The error + information returned by Pa_GetLastHostErrorInfo() will never be modified + asynchronously by errors occurring in other PortAudio owned threads + (such as the thread that manages the stream callback.) + + This function is provided as a last resort, primarily to enhance debugging + by providing clients with access to all available error information. + + @return A pointer to an immutable structure constraining information about + the host error. The values in this structure will only be valid if a + PortAudio function has previously returned the paUnanticipatedHostError + error code. +*/ +const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void ); + + + +/* Device enumeration and capabilities */ + +/** Retrieve the number of available devices. The number of available devices + may be zero. + + @return A non-negative value indicating the number of available devices or, + a PaErrorCode (which are always negative) if PortAudio is not initialized + or an error is encountered. +*/ +PaDeviceIndex Pa_GetDeviceCount( void ); + + +/** Retrieve the index of the default input device. The result can be + used in the inputDevice parameter to Pa_OpenStream(). + + @return The default input device index for the default host API, or paNoDevice + if no default input device is available or an error was encountered. +*/ +PaDeviceIndex Pa_GetDefaultInputDevice( void ); + + +/** Retrieve the index of the default output device. The result can be + used in the outputDevice parameter to Pa_OpenStream(). + + @return The default output device index for the default host API, or paNoDevice + if no default output device is available or an error was encountered. + + @note + On the PC, the user can specify a default device by + setting an environment variable. For example, to use device #1. +
+ set PA_RECOMMENDED_OUTPUT_DEVICE=1
+
+ The user should first determine the available device ids by using + the supplied application "pa_devs". +*/ +PaDeviceIndex Pa_GetDefaultOutputDevice( void ); + + +/** The type used to represent monotonic time in seconds. PaTime is + used for the fields of the PaStreamCallbackTimeInfo argument to the + PaStreamCallback and as the result of Pa_GetStreamTime(). + + PaTime values have unspecified origin. + + @see PaStreamCallback, PaStreamCallbackTimeInfo, Pa_GetStreamTime +*/ +typedef double PaTime; + + +/** A type used to specify one or more sample formats. Each value indicates + a possible format for sound data passed to and from the stream callback, + Pa_ReadStream and Pa_WriteStream. + + The standard formats paFloat32, paInt16, paInt32, paInt24, paInt8 + and aUInt8 are usually implemented by all implementations. + + The floating point representation (paFloat32) uses +1.0 and -1.0 as the + maximum and minimum respectively. + + paUInt8 is an unsigned 8 bit format where 128 is considered "ground" + + The paNonInterleaved flag indicates that audio data is passed as an array + of pointers to separate buffers, one buffer for each channel. Usually, + when this flag is not used, audio data is passed as a single buffer with + all channels interleaved. + + @see Pa_OpenStream, Pa_OpenDefaultStream, PaDeviceInfo + @see paFloat32, paInt16, paInt32, paInt24, paInt8 + @see paUInt8, paCustomFormat, paNonInterleaved +*/ +typedef unsigned long PaSampleFormat; + + +#define paFloat32 ((PaSampleFormat) 0x00000001) /**< @see PaSampleFormat */ +#define paInt32 ((PaSampleFormat) 0x00000002) /**< @see PaSampleFormat */ +#define paInt24 ((PaSampleFormat) 0x00000004) /**< Packed 24 bit format. @see PaSampleFormat */ +#define paInt16 ((PaSampleFormat) 0x00000008) /**< @see PaSampleFormat */ +#define paInt8 ((PaSampleFormat) 0x00000010) /**< @see PaSampleFormat */ +#define paUInt8 ((PaSampleFormat) 0x00000020) /**< @see PaSampleFormat */ +#define paCustomFormat ((PaSampleFormat) 0x00010000) /**< @see PaSampleFormat */ + +#define paNonInterleaved ((PaSampleFormat) 0x80000000) /**< @see PaSampleFormat */ + +/** A structure providing information and capabilities of PortAudio devices. + Devices may support input, output or both input and output. +*/ +typedef struct PaDeviceInfo +{ + int structVersion; /* this is struct version 2 */ + const char *name; + PaHostApiIndex hostApi; /**< note this is a host API index, not a type id*/ + + int maxInputChannels; + int maxOutputChannels; + + /** Default latency values for interactive performance. */ + PaTime defaultLowInputLatency; + PaTime defaultLowOutputLatency; + /** Default latency values for robust non-interactive applications (eg. playing sound files). */ + PaTime defaultHighInputLatency; + PaTime defaultHighOutputLatency; + + double defaultSampleRate; +} PaDeviceInfo; + + +/** Retrieve a pointer to a PaDeviceInfo structure containing information + about the specified device. + @return A pointer to an immutable PaDeviceInfo structure. If the device + parameter is out of range the function returns NULL. + + @param device A valid device index in the range 0 to (Pa_GetDeviceCount()-1) + + @note PortAudio manages the memory referenced by the returned pointer, + the client must not manipulate or free the memory. The pointer is only + guaranteed to be valid between calls to Pa_Initialize() and Pa_Terminate(). + + @see PaDeviceInfo, PaDeviceIndex +*/ +const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device ); + + +/** Parameters for one direction (input or output) of a stream. +*/ +typedef struct PaStreamParameters +{ + /** A valid device index in the range 0 to (Pa_GetDeviceCount()-1) + specifying the device to be used or the special constant + paUseHostApiSpecificDeviceSpecification which indicates that the actual + device(s) to use are specified in hostApiSpecificStreamInfo. + This field must not be set to paNoDevice. + */ + PaDeviceIndex device; + + /** The number of channels of sound to be delivered to the + stream callback or accessed by Pa_ReadStream() or Pa_WriteStream(). + It can range from 1 to the value of maxInputChannels in the + PaDeviceInfo record for the device specified by the device parameter. + */ + int channelCount; + + /** The sample format of the buffer provided to the stream callback, + a_ReadStream() or Pa_WriteStream(). It may be any of the formats described + by the PaSampleFormat enumeration. + */ + PaSampleFormat sampleFormat; + + /** The desired latency in seconds. Where practical, implementations should + configure their latency based on these parameters, otherwise they may + choose the closest viable latency instead. Unless the suggested latency + is greater than the absolute upper limit for the device implementations + should round the suggestedLatency up to the next practical value - ie to + provide an equal or higher latency than suggestedLatency wherever possible. + Actual latency values for an open stream may be retrieved using the + inputLatency and outputLatency fields of the PaStreamInfo structure + returned by Pa_GetStreamInfo(). + @see default*Latency in PaDeviceInfo, *Latency in PaStreamInfo + */ + PaTime suggestedLatency; + + /** An optional pointer to a host api specific data structure + containing additional information for device setup and/or stream processing. + hostApiSpecificStreamInfo is never required for correct operation, + if not used it should be set to NULL. + */ + void *hostApiSpecificStreamInfo; + +} PaStreamParameters; + + +/** Return code for Pa_IsFormatSupported indicating success. */ +#define paFormatIsSupported (0) + +/** Determine whether it would be possible to open a stream with the specified + parameters. + + @param inputParameters A structure that describes the input parameters used to + open a stream. The suggestedLatency field is ignored. See PaStreamParameters + for a description of these parameters. inputParameters must be NULL for + output-only streams. + + @param outputParameters A structure that describes the output parameters used + to open a stream. The suggestedLatency field is ignored. See PaStreamParameters + for a description of these parameters. outputParameters must be NULL for + input-only streams. + + @param sampleRate The required sampleRate. For full-duplex streams it is the + sample rate for both input and output + + @return Returns 0 if the format is supported, and an error code indicating why + the format is not supported otherwise. The constant paFormatIsSupported is + provided to compare with the return value for success. + + @see paFormatIsSupported, PaStreamParameters +*/ +PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ); + + + +/* Streaming types and functions */ + + +/** + A single PaStream can provide multiple channels of real-time + streaming audio input and output to a client application. A stream + provides access to audio hardware represented by one or more + PaDevices. Depending on the underlying Host API, it may be possible + to open multiple streams using the same device, however this behavior + is implementation defined. Portable applications should assume that + a PaDevice may be simultaneously used by at most one PaStream. + + Pointers to PaStream objects are passed between PortAudio functions that + operate on streams. + + @see Pa_OpenStream, Pa_OpenDefaultStream, Pa_OpenDefaultStream, Pa_CloseStream, + Pa_StartStream, Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive, + Pa_GetStreamTime, Pa_GetStreamCpuLoad + +*/ +typedef void PaStream; + + +/** Can be passed as the framesPerBuffer parameter to Pa_OpenStream() + or Pa_OpenDefaultStream() to indicate that the stream callback will + accept buffers of any size. +*/ +#define paFramesPerBufferUnspecified (0) + + +/** Flags used to control the behavior of a stream. They are passed as + parameters to Pa_OpenStream or Pa_OpenDefaultStream. Multiple flags may be + ORed together. + + @see Pa_OpenStream, Pa_OpenDefaultStream + @see paNoFlag, paClipOff, paDitherOff, paNeverDropInput, + paPrimeOutputBuffersUsingStreamCallback, paPlatformSpecificFlags +*/ +typedef unsigned long PaStreamFlags; + +/** @see PaStreamFlags */ +#define paNoFlag ((PaStreamFlags) 0) + +/** Disable default clipping of out of range samples. + @see PaStreamFlags +*/ +#define paClipOff ((PaStreamFlags) 0x00000001) + +/** Disable default dithering. + @see PaStreamFlags +*/ +#define paDitherOff ((PaStreamFlags) 0x00000002) + +/** Flag requests that where possible a full duplex stream will not discard + overflowed input samples without calling the stream callback. This flag is + only valid for full duplex callback streams and only when used in combination + with the paFramesPerBufferUnspecified (0) framesPerBuffer parameter. Using + this flag incorrectly results in a paInvalidFlag error being returned from + Pa_OpenStream and Pa_OpenDefaultStream. + + @see PaStreamFlags, paFramesPerBufferUnspecified +*/ +#define paNeverDropInput ((PaStreamFlags) 0x00000004) + +/** Call the stream callback to fill initial output buffers, rather than the + default behavior of priming the buffers with zeros (silence). This flag has + no effect for input-only and blocking read/write streams. + + @see PaStreamFlags +*/ +#define paPrimeOutputBuffersUsingStreamCallback ((PaStreamFlags) 0x00000008) + +/** A mask specifying the platform specific bits. + @see PaStreamFlags +*/ +#define paPlatformSpecificFlags ((PaStreamFlags)0xFFFF0000) + +/** + Timing information for the buffers passed to the stream callback. + + Time values are expressed in seconds and are synchronised with the time base used by Pa_GetStreamTime() for the associated stream. + + @see PaStreamCallback, Pa_GetStreamTime +*/ +typedef struct PaStreamCallbackTimeInfo{ + PaTime inputBufferAdcTime; /**< The time when the first sample of the input buffer was captured at the ADC input */ + PaTime currentTime; /**< The time when the stream callback was invoked */ + PaTime outputBufferDacTime; /**< The time when the first sample of the output buffer will output the DAC */ +} PaStreamCallbackTimeInfo; + + +/** + Flag bit constants for the statusFlags to PaStreamCallback. + + @see paInputUnderflow, paInputOverflow, paOutputUnderflow, paOutputOverflow, + paPrimingOutput +*/ +typedef unsigned long PaStreamCallbackFlags; + +/** In a stream opened with paFramesPerBufferUnspecified, indicates that + input data is all silence (zeros) because no real data is available. In a + stream opened without paFramesPerBufferUnspecified, it indicates that one or + more zero samples have been inserted into the input buffer to compensate + for an input underflow. + @see PaStreamCallbackFlags +*/ +#define paInputUnderflow ((PaStreamCallbackFlags) 0x00000001) + +/** In a stream opened with paFramesPerBufferUnspecified, indicates that data + prior to the first sample of the input buffer was discarded due to an + overflow, possibly because the stream callback is using too much CPU time. + Otherwise indicates that data prior to one or more samples in the + input buffer was discarded. + @see PaStreamCallbackFlags +*/ +#define paInputOverflow ((PaStreamCallbackFlags) 0x00000002) + +/** Indicates that output data (or a gap) was inserted, possibly because the + stream callback is using too much CPU time. + @see PaStreamCallbackFlags +*/ +#define paOutputUnderflow ((PaStreamCallbackFlags) 0x00000004) + +/** Indicates that output data will be discarded because no room is available. + @see PaStreamCallbackFlags +*/ +#define paOutputOverflow ((PaStreamCallbackFlags) 0x00000008) + +/** Some of all of the output data will be used to prime the stream, input + data may be zero. + @see PaStreamCallbackFlags +*/ +#define paPrimingOutput ((PaStreamCallbackFlags) 0x00000010) + +/** + Allowable return values for the PaStreamCallback. + @see PaStreamCallback +*/ +typedef enum PaStreamCallbackResult +{ + paContinue=0, /**< Signal that the stream should continue invoking the callback and processing audio. */ + paComplete=1, /**< Signal that the stream should stop invoking the callback and finish once all output samples have played. */ + paAbort=2 /**< Signal that the stream should stop invoking the callback and finish as soon as possible. */ +} PaStreamCallbackResult; + + +/** + Functions of type PaStreamCallback are implemented by PortAudio clients. + They consume, process or generate audio in response to requests from an + active PortAudio stream. + + When a stream is running, PortAudio calls the stream callback periodically. + The callback function is responsible for processing buffers of audio samples + passed via the input and output parameters. + + The PortAudio stream callback runs at very high or real-time priority. + It is required to consistently meet its time deadlines. Do not allocate + memory, access the file system, call library functions or call other functions + from the stream callback that may block or take an unpredictable amount of + time to complete. + + In order for a stream to maintain glitch-free operation the callback + must consume and return audio data faster than it is recorded and/or + played. PortAudio anticipates that each callback invocation may execute for + a duration approaching the duration of frameCount audio frames at the stream + sample rate. It is reasonable to expect to be able to utilise 70% or more of + the available CPU time in the PortAudio callback. However, due to buffer size + adaption and other factors, not all host APIs are able to guarantee audio + stability under heavy CPU load with arbitrary fixed callback buffer sizes. + When high callback CPU utilisation is required the most robust behavior + can be achieved by using paFramesPerBufferUnspecified as the + Pa_OpenStream() framesPerBuffer parameter. + + @param input and @param output are either arrays of interleaved samples or; + if non-interleaved samples were requested using the paNonInterleaved sample + format flag, an array of buffer pointers, one non-interleaved buffer for + each channel. + + The format, packing and number of channels used by the buffers are + determined by parameters to Pa_OpenStream(). + + @param frameCount The number of sample frames to be processed by + the stream callback. + + @param timeInfo Timestamps indicating the ADC capture time of the first sample + in the input buffer, the DAC output time of the first sample in the output buffer + and the time the callback was invoked. + See PaStreamCallbackTimeInfo and Pa_GetStreamTime() + + @param statusFlags Flags indicating whether input and/or output buffers + have been inserted or will be dropped to overcome underflow or overflow + conditions. + + @param userData The value of a user supplied pointer passed to + Pa_OpenStream() intended for storing synthesis data etc. + + @return + The stream callback should return one of the values in the + ::PaStreamCallbackResult enumeration. To ensure that the callback continues + to be called, it should return paContinue (0). Either paComplete or paAbort + can be returned to finish stream processing, after either of these values is + returned the callback will not be called again. If paAbort is returned the + stream will finish as soon as possible. If paComplete is returned, the stream + will continue until all buffers generated by the callback have been played. + This may be useful in applications such as soundfile players where a specific + duration of output is required. However, it is not necessary to utilize this + mechanism as Pa_StopStream(), Pa_AbortStream() or Pa_CloseStream() can also + be used to stop the stream. The callback must always fill the entire output + buffer irrespective of its return value. + + @see Pa_OpenStream, Pa_OpenDefaultStream + + @note With the exception of Pa_GetStreamCpuLoad() it is not permissible to call + PortAudio API functions from within the stream callback. +*/ +typedef int PaStreamCallback( + const void *input, void *output, + unsigned long frameCount, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ); + + +/** Opens a stream for either input, output or both. + + @param stream The address of a PaStream pointer which will receive + a pointer to the newly opened stream. + + @param inputParameters A structure that describes the input parameters used by + the opened stream. See PaStreamParameters for a description of these parameters. + inputParameters must be NULL for output-only streams. + + @param outputParameters A structure that describes the output parameters used by + the opened stream. See PaStreamParameters for a description of these parameters. + outputParameters must be NULL for input-only streams. + + @param sampleRate The desired sampleRate. For full-duplex streams it is the + sample rate for both input and output + + @param framesPerBuffer The number of frames passed to the stream callback + function, or the preferred block granularity for a blocking read/write stream. + The special value paFramesPerBufferUnspecified (0) may be used to request that + the stream callback will receive an optimal (and possibly varying) number of + frames based on host requirements and the requested latency settings. + Note: With some host APIs, the use of non-zero framesPerBuffer for a callback + stream may introduce an additional layer of buffering which could introduce + additional latency. PortAudio guarantees that the additional latency + will be kept to the theoretical minimum however, it is strongly recommended + that a non-zero framesPerBuffer value only be used when your algorithm + requires a fixed number of frames per stream callback. + + @param streamFlags Flags which modify the behavior of the streaming process. + This parameter may contain a combination of flags ORed together. Some flags may + only be relevant to certain buffer formats. + + @param streamCallback A pointer to a client supplied function that is responsible + for processing and filling input and output buffers. If this parameter is NULL + the stream will be opened in 'blocking read/write' mode. In blocking mode, + the client can receive sample data using Pa_ReadStream and write sample data + using Pa_WriteStream, the number of samples that may be read or written + without blocking is returned by Pa_GetStreamReadAvailable and + Pa_GetStreamWriteAvailable respectively. + + @param userData A client supplied pointer which is passed to the stream callback + function. It could for example, contain a pointer to instance data necessary + for processing the audio buffers. This parameter is ignored if streamCallback + is NULL. + + @return + Upon success Pa_OpenStream() returns paNoError and places a pointer to a + valid PaStream in the stream argument. The stream is inactive (stopped). + If a call to Pa_OpenStream() fails, a non-zero error code is returned (see + PaError for possible error codes) and the value of stream is invalid. + + @see PaStreamParameters, PaStreamCallback, Pa_ReadStream, Pa_WriteStream, + Pa_GetStreamReadAvailable, Pa_GetStreamWriteAvailable +*/ +PaError Pa_OpenStream( PaStream** stream, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ); + + +/** A simplified version of Pa_OpenStream() that opens the default input + and/or output devices. + + @param stream The address of a PaStream pointer which will receive + a pointer to the newly opened stream. + + @param numInputChannels The number of channels of sound that will be supplied + to the stream callback or returned by Pa_ReadStream. It can range from 1 to + the value of maxInputChannels in the PaDeviceInfo record for the default input + device. If 0 the stream is opened as an output-only stream. + + @param numOutputChannels The number of channels of sound to be delivered to the + stream callback or passed to Pa_WriteStream. It can range from 1 to the value + of maxOutputChannels in the PaDeviceInfo record for the default output device. + If 0 the stream is opened as an output-only stream. + + @param sampleFormat The sample format of both the input and output buffers + provided to the callback or passed to and from Pa_ReadStream and Pa_WriteStream. + sampleFormat may be any of the formats described by the PaSampleFormat + enumeration. + + @param sampleRate Same as Pa_OpenStream parameter of the same name. + @param framesPerBuffer Same as Pa_OpenStream parameter of the same name. + @param streamCallback Same as Pa_OpenStream parameter of the same name. + @param userData Same as Pa_OpenStream parameter of the same name. + + @return As for Pa_OpenStream + + @see Pa_OpenStream, PaStreamCallback +*/ +PaError Pa_OpenDefaultStream( PaStream** stream, + int numInputChannels, + int numOutputChannels, + PaSampleFormat sampleFormat, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamCallback *streamCallback, + void *userData ); + + +/** Closes an audio stream. If the audio stream is active it + discards any pending buffers as if Pa_AbortStream() had been called. +*/ +PaError Pa_CloseStream( PaStream *stream ); + + +/** Functions of type PaStreamFinishedCallback are implemented by PortAudio + clients. They can be registered with a stream using the Pa_SetStreamFinishedCallback + function. Once registered they are called when the stream becomes inactive + (ie once a call to Pa_StopStream() will not block). + A stream will become inactive after the stream callback returns non-zero, + or when Pa_StopStream or Pa_AbortStream is called. For a stream providing audio + output, if the stream callback returns paComplete, or Pa_StopStream() is called, + the stream finished callback will not be called until all generated sample data + has been played. + + @param userData The userData parameter supplied to Pa_OpenStream() + + @see Pa_SetStreamFinishedCallback +*/ +typedef void PaStreamFinishedCallback( void *userData ); + + +/** Register a stream finished callback function which will be called when the + stream becomes inactive. See the description of PaStreamFinishedCallback for + further details about when the callback will be called. + + @param stream a pointer to a PaStream that is in the stopped state - if the + stream is not stopped, the stream's finished callback will remain unchanged + and an error code will be returned. + + @param streamFinishedCallback a pointer to a function with the same signature + as PaStreamFinishedCallback, that will be called when the stream becomes + inactive. Passing NULL for this parameter will un-register a previously + registered stream finished callback function. + + @return on success returns paNoError, otherwise an error code indicating the cause + of the error. + + @see PaStreamFinishedCallback +*/ +PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback ); + + +/** Commences audio processing. +*/ +PaError Pa_StartStream( PaStream *stream ); + + +/** Terminates audio processing. It waits until all pending + audio buffers have been played before it returns. +*/ +PaError Pa_StopStream( PaStream *stream ); + + +/** Terminates audio processing immediately without waiting for pending + buffers to complete. +*/ +PaError Pa_AbortStream( PaStream *stream ); + + +/** Determine whether the stream is stopped. + A stream is considered to be stopped prior to a successful call to + Pa_StartStream and after a successful call to Pa_StopStream or Pa_AbortStream. + If a stream callback returns a value other than paContinue the stream is NOT + considered to be stopped. + + @return Returns one (1) when the stream is stopped, zero (0) when + the stream is running or, a PaErrorCode (which are always negative) if + PortAudio is not initialized or an error is encountered. + + @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive +*/ +PaError Pa_IsStreamStopped( PaStream *stream ); + + +/** Determine whether the stream is active. + A stream is active after a successful call to Pa_StartStream(), until it + becomes inactive either as a result of a call to Pa_StopStream() or + Pa_AbortStream(), or as a result of a return value other than paContinue from + the stream callback. In the latter case, the stream is considered inactive + after the last buffer has finished playing. + + @return Returns one (1) when the stream is active (ie playing or recording + audio), zero (0) when not playing or, a PaErrorCode (which are always negative) + if PortAudio is not initialized or an error is encountered. + + @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamStopped +*/ +PaError Pa_IsStreamActive( PaStream *stream ); + + + +/** A structure containing unchanging information about an open stream. + @see Pa_GetStreamInfo +*/ + +typedef struct PaStreamInfo +{ + /** this is struct version 1 */ + int structVersion; + + /** The input latency of the stream in seconds. This value provides the most + accurate estimate of input latency available to the implementation. It may + differ significantly from the suggestedLatency value passed to Pa_OpenStream(). + The value of this field will be zero (0.) for output-only streams. + @see PaTime + */ + PaTime inputLatency; + + /** The output latency of the stream in seconds. This value provides the most + accurate estimate of output latency available to the implementation. It may + differ significantly from the suggestedLatency value passed to Pa_OpenStream(). + The value of this field will be zero (0.) for input-only streams. + @see PaTime + */ + PaTime outputLatency; + + /** The sample rate of the stream in Hertz (samples per second). In cases + where the hardware sample rate is inaccurate and PortAudio is aware of it, + the value of this field may be different from the sampleRate parameter + passed to Pa_OpenStream(). If information about the actual hardware sample + rate is not available, this field will have the same value as the sampleRate + parameter passed to Pa_OpenStream(). + */ + double sampleRate; + +} PaStreamInfo; + + +/** Retrieve a pointer to a PaStreamInfo structure containing information + about the specified stream. + @return A pointer to an immutable PaStreamInfo structure. If the stream + parameter is invalid, or an error is encountered, the function returns NULL. + + @param stream A pointer to an open stream previously created with Pa_OpenStream. + + @note PortAudio manages the memory referenced by the returned pointer, + the client must not manipulate or free the memory. The pointer is only + guaranteed to be valid until the specified stream is closed. + + @see PaStreamInfo +*/ +const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream ); + + +/** Returns the current time in seconds for a stream according to the same clock used + to generate callback PaStreamCallbackTimeInfo timestamps. The time values are + monotonically increasing and have unspecified origin. + + Pa_GetStreamTime returns valid time values for the entire life of the stream, + from when the stream is opened until it is closed. Starting and stopping the stream + does not affect the passage of time returned by Pa_GetStreamTime. + + This time may be used for synchronizing other events to the audio stream, for + example synchronizing audio to MIDI. + + @return The stream's current time in seconds, or 0 if an error occurred. + + @see PaTime, PaStreamCallback, PaStreamCallbackTimeInfo +*/ +PaTime Pa_GetStreamTime( PaStream *stream ); + + +/** Retrieve CPU usage information for the specified stream. + The "CPU Load" is a fraction of total CPU time consumed by a callback stream's + audio processing routines including, but not limited to the client supplied + stream callback. This function does not work with blocking read/write streams. + + This function may be called from the stream callback function or the + application. + + @return + A floating point value, typically between 0.0 and 1.0, where 1.0 indicates + that the stream callback is consuming the maximum number of CPU cycles possible + to maintain real-time operation. A value of 0.5 would imply that PortAudio and + the stream callback was consuming roughly 50% of the available CPU time. The + return value may exceed 1.0. A value of 0.0 will always be returned for a + blocking read/write stream, or if an error occurs. +*/ +double Pa_GetStreamCpuLoad( PaStream* stream ); + + +/** Read samples from an input stream. The function doesn't return until + the entire buffer has been filled - this may involve waiting for the operating + system to supply the data. + + @param stream A pointer to an open stream previously created with Pa_OpenStream. + + @param buffer A pointer to a buffer of sample frames. The buffer contains + samples in the format specified by the inputParameters->sampleFormat field + used to open the stream, and the number of channels specified by + inputParameters->numChannels. If non-interleaved samples were requested using + the paNonInterleaved sample format flag, buffer is a pointer to the first element + of an array of buffer pointers, one non-interleaved buffer for each channel. + + @param frames The number of frames to be read into buffer. This parameter + is not constrained to a specific range, however high performance applications + will want to match this parameter to the framesPerBuffer parameter used + when opening the stream. + + @return On success PaNoError will be returned, or PaInputOverflowed if input + data was discarded by PortAudio after the previous call and before this call. +*/ +PaError Pa_ReadStream( PaStream* stream, + void *buffer, + unsigned long frames ); + + +/** Write samples to an output stream. This function doesn't return until the + entire buffer has been written - this may involve waiting for the operating + system to consume the data. + + @param stream A pointer to an open stream previously created with Pa_OpenStream. + + @param buffer A pointer to a buffer of sample frames. The buffer contains + samples in the format specified by the outputParameters->sampleFormat field + used to open the stream, and the number of channels specified by + outputParameters->numChannels. If non-interleaved samples were requested using + the paNonInterleaved sample format flag, buffer is a pointer to the first element + of an array of buffer pointers, one non-interleaved buffer for each channel. + + @param frames The number of frames to be written from buffer. This parameter + is not constrained to a specific range, however high performance applications + will want to match this parameter to the framesPerBuffer parameter used + when opening the stream. + + @return On success PaNoError will be returned, or paOutputUnderflowed if + additional output data was inserted after the previous call and before this + call. +*/ +PaError Pa_WriteStream( PaStream* stream, + const void *buffer, + unsigned long frames ); + + +/** Retrieve the number of frames that can be read from the stream without + waiting. + + @return Returns a non-negative value representing the maximum number of frames + that can be read from the stream without blocking or busy waiting or, a + PaErrorCode (which are always negative) if PortAudio is not initialized or an + error is encountered. +*/ +signed long Pa_GetStreamReadAvailable( PaStream* stream ); + + +/** Retrieve the number of frames that can be written to the stream without + waiting. + + @return Returns a non-negative value representing the maximum number of frames + that can be written to the stream without blocking or busy waiting or, a + PaErrorCode (which are always negative) if PortAudio is not initialized or an + error is encountered. +*/ +signed long Pa_GetStreamWriteAvailable( PaStream* stream ); + + +/* Miscellaneous utilities */ + + +/** Retrieve the size of a given sample format in bytes. + + @return The size in bytes of a single sample in the specified format, + or paSampleFormatNotSupported if the format is not supported. +*/ +PaError Pa_GetSampleSize( PaSampleFormat format ); + + +/** Put the caller to sleep for at least 'msec' milliseconds. This function is + provided only as a convenience for authors of portable code (such as the tests + and examples in the PortAudio distribution.) + + The function may sleep longer than requested so don't rely on this for accurate + musical timing. +*/ +void Pa_Sleep( long msec ); + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PORTAUDIO_H */ diff --git a/hsmodem/libkmaudio/soundio.h b/hsmodem/libkmaudio/soundio.h new file mode 100755 index 0000000..17a7d06 --- /dev/null +++ b/hsmodem/libkmaudio/soundio.h @@ -0,0 +1,1209 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of libsoundio, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef SOUNDIO_SOUNDIO_H +#define SOUNDIO_SOUNDIO_H + +#include "endian.h" +#include + +/// \cond +#ifdef __cplusplus +#define SOUNDIO_EXTERN_C extern "C" +#else +#define SOUNDIO_EXTERN_C +#endif + +#if defined(SOUNDIO_STATIC_LIBRARY) +# define SOUNDIO_EXPORT SOUNDIO_EXTERN_C +#else +# if defined(_WIN32) +# if defined(SOUNDIO_BUILDING_LIBRARY) +# define SOUNDIO_EXPORT SOUNDIO_EXTERN_C __declspec(dllexport) +# else +# define SOUNDIO_EXPORT SOUNDIO_EXTERN_C __declspec(dllimport) +# endif +# else +# define SOUNDIO_EXPORT SOUNDIO_EXTERN_C __attribute__((visibility ("default"))) +# endif +#endif +/// \endcond + +/** \mainpage + * + * \section intro_sec Overview + * + * libsoundio is a C library for cross-platform audio input and output. It is + * suitable for real-time and consumer software. + * + * Documentation: soundio.h + */ + + +/** \example sio_list_devices.c + * List the available input and output devices on the system and their + * properties. Supports watching for changes and specifying backend to use. + */ + +/** \example sio_sine.c + * Play a sine wave over the default output device. + * Supports specifying device and backend to use. + */ + +/** \example sio_record.c + * Record audio to an output file. + * Supports specifying device and backend to use. + */ + +/** \example sio_microphone.c + * Stream the default input device over the default output device. + * Supports specifying device and backend to use. + */ + +/** \example backend_disconnect_recover.c + * Demonstrates recovering from a backend disconnecting. + */ + +/// See also ::soundio_strerror +enum SoundIoError { + SoundIoErrorNone, + /// Out of memory. + SoundIoErrorNoMem, + /// The backend does not appear to be active or running. + SoundIoErrorInitAudioBackend, + /// A system resource other than memory was not available. + SoundIoErrorSystemResources, + /// Attempted to open a device and failed. + SoundIoErrorOpeningDevice, + SoundIoErrorNoSuchDevice, + /// The programmer did not comply with the API. + SoundIoErrorInvalid, + /// libsoundio was compiled without support for that backend. + SoundIoErrorBackendUnavailable, + /// An open stream had an error that can only be recovered from by + /// destroying the stream and creating it again. + SoundIoErrorStreaming, + /// Attempted to use a device with parameters it cannot support. + SoundIoErrorIncompatibleDevice, + /// When JACK returns `JackNoSuchClient` + SoundIoErrorNoSuchClient, + /// Attempted to use parameters that the backend cannot support. + SoundIoErrorIncompatibleBackend, + /// Backend server shutdown or became inactive. + SoundIoErrorBackendDisconnected, + SoundIoErrorInterrupted, + /// Buffer underrun occurred. + SoundIoErrorUnderflow, + /// Unable to convert to or from UTF-8 to the native string format. + SoundIoErrorEncodingString, +}; + +/// Specifies where a channel is physically located. +enum SoundIoChannelId { + SoundIoChannelIdInvalid, + + SoundIoChannelIdFrontLeft, ///< First of the more commonly supported ids. + SoundIoChannelIdFrontRight, + SoundIoChannelIdFrontCenter, + SoundIoChannelIdLfe, + SoundIoChannelIdBackLeft, + SoundIoChannelIdBackRight, + SoundIoChannelIdFrontLeftCenter, + SoundIoChannelIdFrontRightCenter, + SoundIoChannelIdBackCenter, + SoundIoChannelIdSideLeft, + SoundIoChannelIdSideRight, + SoundIoChannelIdTopCenter, + SoundIoChannelIdTopFrontLeft, + SoundIoChannelIdTopFrontCenter, + SoundIoChannelIdTopFrontRight, + SoundIoChannelIdTopBackLeft, + SoundIoChannelIdTopBackCenter, + SoundIoChannelIdTopBackRight, ///< Last of the more commonly supported ids. + + SoundIoChannelIdBackLeftCenter, ///< First of the less commonly supported ids. + SoundIoChannelIdBackRightCenter, + SoundIoChannelIdFrontLeftWide, + SoundIoChannelIdFrontRightWide, + SoundIoChannelIdFrontLeftHigh, + SoundIoChannelIdFrontCenterHigh, + SoundIoChannelIdFrontRightHigh, + SoundIoChannelIdTopFrontLeftCenter, + SoundIoChannelIdTopFrontRightCenter, + SoundIoChannelIdTopSideLeft, + SoundIoChannelIdTopSideRight, + SoundIoChannelIdLeftLfe, + SoundIoChannelIdRightLfe, + SoundIoChannelIdLfe2, + SoundIoChannelIdBottomCenter, + SoundIoChannelIdBottomLeftCenter, + SoundIoChannelIdBottomRightCenter, + + /// Mid/side recording + SoundIoChannelIdMsMid, + SoundIoChannelIdMsSide, + + /// first order ambisonic channels + SoundIoChannelIdAmbisonicW, + SoundIoChannelIdAmbisonicX, + SoundIoChannelIdAmbisonicY, + SoundIoChannelIdAmbisonicZ, + + /// X-Y Recording + SoundIoChannelIdXyX, + SoundIoChannelIdXyY, + + SoundIoChannelIdHeadphonesLeft, ///< First of the "other" channel ids + SoundIoChannelIdHeadphonesRight, + SoundIoChannelIdClickTrack, + SoundIoChannelIdForeignLanguage, + SoundIoChannelIdHearingImpaired, + SoundIoChannelIdNarration, + SoundIoChannelIdHaptic, + SoundIoChannelIdDialogCentricMix, ///< Last of the "other" channel ids + + SoundIoChannelIdAux, + SoundIoChannelIdAux0, + SoundIoChannelIdAux1, + SoundIoChannelIdAux2, + SoundIoChannelIdAux3, + SoundIoChannelIdAux4, + SoundIoChannelIdAux5, + SoundIoChannelIdAux6, + SoundIoChannelIdAux7, + SoundIoChannelIdAux8, + SoundIoChannelIdAux9, + SoundIoChannelIdAux10, + SoundIoChannelIdAux11, + SoundIoChannelIdAux12, + SoundIoChannelIdAux13, + SoundIoChannelIdAux14, + SoundIoChannelIdAux15, +}; + +/// Built-in channel layouts for convenience. +enum SoundIoChannelLayoutId { + SoundIoChannelLayoutIdMono, + SoundIoChannelLayoutIdStereo, + SoundIoChannelLayoutId2Point1, + SoundIoChannelLayoutId3Point0, + SoundIoChannelLayoutId3Point0Back, + SoundIoChannelLayoutId3Point1, + SoundIoChannelLayoutId4Point0, + SoundIoChannelLayoutIdQuad, + SoundIoChannelLayoutIdQuadSide, + SoundIoChannelLayoutId4Point1, + SoundIoChannelLayoutId5Point0Back, + SoundIoChannelLayoutId5Point0Side, + SoundIoChannelLayoutId5Point1, + SoundIoChannelLayoutId5Point1Back, + SoundIoChannelLayoutId6Point0Side, + SoundIoChannelLayoutId6Point0Front, + SoundIoChannelLayoutIdHexagonal, + SoundIoChannelLayoutId6Point1, + SoundIoChannelLayoutId6Point1Back, + SoundIoChannelLayoutId6Point1Front, + SoundIoChannelLayoutId7Point0, + SoundIoChannelLayoutId7Point0Front, + SoundIoChannelLayoutId7Point1, + SoundIoChannelLayoutId7Point1Wide, + SoundIoChannelLayoutId7Point1WideBack, + SoundIoChannelLayoutIdOctagonal, +}; + +enum SoundIoBackend { + SoundIoBackendNone, + SoundIoBackendJack, + SoundIoBackendPulseAudio, + SoundIoBackendAlsa, + SoundIoBackendCoreAudio, + SoundIoBackendWasapi, + SoundIoBackendDummy, +}; + +enum SoundIoDeviceAim { + SoundIoDeviceAimInput, ///< capture / recording + SoundIoDeviceAimOutput, ///< playback +}; + +/// For your convenience, Native Endian and Foreign Endian constants are defined +/// which point to the respective SoundIoFormat values. +enum SoundIoFormat { + SoundIoFormatInvalid, + SoundIoFormatS8, ///< Signed 8 bit + SoundIoFormatU8, ///< Unsigned 8 bit + SoundIoFormatS16LE, ///< Signed 16 bit Little Endian + SoundIoFormatS16BE, ///< Signed 16 bit Big Endian + SoundIoFormatU16LE, ///< Unsigned 16 bit Little Endian + SoundIoFormatU16BE, ///< Unsigned 16 bit Big Endian + SoundIoFormatS24LE, ///< Signed 24 bit Little Endian using low three bytes in 32-bit word + SoundIoFormatS24BE, ///< Signed 24 bit Big Endian using low three bytes in 32-bit word + SoundIoFormatU24LE, ///< Unsigned 24 bit Little Endian using low three bytes in 32-bit word + SoundIoFormatU24BE, ///< Unsigned 24 bit Big Endian using low three bytes in 32-bit word + SoundIoFormatS32LE, ///< Signed 32 bit Little Endian + SoundIoFormatS32BE, ///< Signed 32 bit Big Endian + SoundIoFormatU32LE, ///< Unsigned 32 bit Little Endian + SoundIoFormatU32BE, ///< Unsigned 32 bit Big Endian + SoundIoFormatFloat32LE, ///< Float 32 bit Little Endian, Range -1.0 to 1.0 + SoundIoFormatFloat32BE, ///< Float 32 bit Big Endian, Range -1.0 to 1.0 + SoundIoFormatFloat64LE, ///< Float 64 bit Little Endian, Range -1.0 to 1.0 + SoundIoFormatFloat64BE, ///< Float 64 bit Big Endian, Range -1.0 to 1.0 +}; + +#if defined(SOUNDIO_OS_BIG_ENDIAN) +#define SoundIoFormatS16NE SoundIoFormatS16BE +#define SoundIoFormatU16NE SoundIoFormatU16BE +#define SoundIoFormatS24NE SoundIoFormatS24BE +#define SoundIoFormatU24NE SoundIoFormatU24BE +#define SoundIoFormatS32NE SoundIoFormatS32BE +#define SoundIoFormatU32NE SoundIoFormatU32BE +#define SoundIoFormatFloat32NE SoundIoFormatFloat32BE +#define SoundIoFormatFloat64NE SoundIoFormatFloat64BE + +#define SoundIoFormatS16FE SoundIoFormatS16LE +#define SoundIoFormatU16FE SoundIoFormatU16LE +#define SoundIoFormatS24FE SoundIoFormatS24LE +#define SoundIoFormatU24FE SoundIoFormatU24LE +#define SoundIoFormatS32FE SoundIoFormatS32LE +#define SoundIoFormatU32FE SoundIoFormatU32LE +#define SoundIoFormatFloat32FE SoundIoFormatFloat32LE +#define SoundIoFormatFloat64FE SoundIoFormatFloat64LE + +#elif defined(SOUNDIO_OS_LITTLE_ENDIAN) + +/// Note that we build the documentation in Little Endian mode, +/// so all the "NE" macros in the docs point to "LE" and +/// "FE" macros point to "BE". On a Big Endian system it is the +/// other way around. +#define SoundIoFormatS16NE SoundIoFormatS16LE +#define SoundIoFormatU16NE SoundIoFormatU16LE +#define SoundIoFormatS24NE SoundIoFormatS24LE +#define SoundIoFormatU24NE SoundIoFormatU24LE +#define SoundIoFormatS32NE SoundIoFormatS32LE +#define SoundIoFormatU32NE SoundIoFormatU32LE +#define SoundIoFormatFloat32NE SoundIoFormatFloat32LE +#define SoundIoFormatFloat64NE SoundIoFormatFloat64LE + +#define SoundIoFormatS16FE SoundIoFormatS16BE +#define SoundIoFormatU16FE SoundIoFormatU16BE +#define SoundIoFormatS24FE SoundIoFormatS24BE +#define SoundIoFormatU24FE SoundIoFormatU24BE +#define SoundIoFormatS32FE SoundIoFormatS32BE +#define SoundIoFormatU32FE SoundIoFormatU32BE +#define SoundIoFormatFloat32FE SoundIoFormatFloat32BE +#define SoundIoFormatFloat64FE SoundIoFormatFloat64BE + +#else +//#error "unknown byte order" +#endif + +#define SOUNDIO_MAX_CHANNELS 24 +/// The size of this struct is OK to use. +struct SoundIoChannelLayout { + const char *name; + int channel_count; + enum SoundIoChannelId channels[SOUNDIO_MAX_CHANNELS]; +}; + +/// The size of this struct is OK to use. +struct SoundIoSampleRateRange { + int min; + int max; +}; + +/// The size of this struct is OK to use. +struct SoundIoChannelArea { + /// Base address of buffer. + char *ptr; + /// How many bytes it takes to get from the beginning of one sample to + /// the beginning of the next sample. + int step; +}; + +/// The size of this struct is not part of the API or ABI. +struct SoundIo { + /// Optional. Put whatever you want here. Defaults to NULL. + void *userdata; + /// Optional callback. Called when the list of devices change. Only called + /// during a call to ::soundio_flush_events or ::soundio_wait_events. + void (*on_devices_change)(struct SoundIo *); + /// Optional callback. Called when the backend disconnects. For example, + /// when the JACK server shuts down. When this happens, listing devices + /// and opening streams will always fail with + /// SoundIoErrorBackendDisconnected. This callback is only called during a + /// call to ::soundio_flush_events or ::soundio_wait_events. + /// If you do not supply a callback, the default will crash your program + /// with an error message. This callback is also called when the thread + /// that retrieves device information runs into an unrecoverable condition + /// such as running out of memory. + /// + /// Possible errors: + /// * #SoundIoErrorBackendDisconnected + /// * #SoundIoErrorNoMem + /// * #SoundIoErrorSystemResources + /// * #SoundIoErrorOpeningDevice - unexpected problem accessing device + /// information + void (*on_backend_disconnect)(struct SoundIo *, int err); + /// Optional callback. Called from an unknown thread that you should not use + /// to call any soundio functions. You may use this to signal a condition + /// variable to wake up. Called when ::soundio_wait_events would be woken up. + void (*on_events_signal)(struct SoundIo *); + + /// Read-only. After calling ::soundio_connect or ::soundio_connect_backend, + /// this field tells which backend is currently connected. + enum SoundIoBackend current_backend; + + /// Optional: Application name. + /// PulseAudio uses this for "application name". + /// JACK uses this for `client_name`. + /// Must not contain a colon (":"). + const char *app_name; + + /// Optional: Real time priority warning. + /// This callback is fired when making thread real-time priority failed. By + /// default, it will print to stderr only the first time it is called + /// a message instructing the user how to configure their system to allow + /// real-time priority threads. This must be set to a function not NULL. + /// To silence the warning, assign this to a function that does nothing. + void (*emit_rtprio_warning)(void); + + /// Optional: JACK info callback. + /// By default, libsoundio sets this to an empty function in order to + /// silence stdio messages from JACK. You may override the behavior by + /// setting this to `NULL` or providing your own function. This is + /// registered with JACK regardless of whether ::soundio_connect_backend + /// succeeds. + void (*jack_info_callback)(const char *msg); + /// Optional: JACK error callback. + /// See SoundIo::jack_info_callback + void (*jack_error_callback)(const char *msg); +}; + +/// The size of this struct is not part of the API or ABI. +struct SoundIoDevice { + /// Read-only. Set automatically. + struct SoundIo *soundio; + + /// A string of bytes that uniquely identifies this device. + /// If the same physical device supports both input and output, that makes + /// one SoundIoDevice for the input and one SoundIoDevice for the output. + /// In this case, the id of each SoundIoDevice will be the same, and + /// SoundIoDevice::aim will be different. Additionally, if the device + /// supports raw mode, there may be up to four devices with the same id: + /// one for each value of SoundIoDevice::is_raw and one for each value of + /// SoundIoDevice::aim. + char *id; + /// User-friendly UTF-8 encoded text to describe the device. + char *name; + + /// Tells whether this device is an input device or an output device. + enum SoundIoDeviceAim aim; + + /// Channel layouts are handled similarly to SoundIoDevice::formats. + /// If this information is missing due to a SoundIoDevice::probe_error, + /// layouts will be NULL. It's OK to modify this data, for example calling + /// ::soundio_sort_channel_layouts on it. + /// Devices are guaranteed to have at least 1 channel layout. + struct SoundIoChannelLayout *layouts; + int layout_count; + /// See SoundIoDevice::current_format + struct SoundIoChannelLayout current_layout; + + /// List of formats this device supports. See also + /// SoundIoDevice::current_format. + enum SoundIoFormat *formats; + /// How many formats are available in SoundIoDevice::formats. + int format_count; + /// A device is either a raw device or it is a virtual device that is + /// provided by a software mixing service such as dmix or PulseAudio (see + /// SoundIoDevice::is_raw). If it is a raw device, + /// current_format is meaningless; + /// the device has no current format until you open it. On the other hand, + /// if it is a virtual device, current_format describes the + /// destination sample format that your audio will be converted to. Or, + /// if you're the lucky first application to open the device, you might + /// cause the current_format to change to your format. + /// Generally, you want to ignore current_format and use + /// whatever format is most convenient + /// for you which is supported by the device, because when you are the only + /// application left, the mixer might decide to switch + /// current_format to yours. You can learn the supported formats via + /// formats and SoundIoDevice::format_count. If this information is missing + /// due to a probe error, formats will be `NULL`. If current_format is + /// unavailable, it will be set to #SoundIoFormatInvalid. + /// Devices are guaranteed to have at least 1 format available. + enum SoundIoFormat current_format; + + /// Sample rate is the number of frames per second. + /// Sample rate is handled very similar to SoundIoDevice::formats. + /// If sample rate information is missing due to a probe error, the field + /// will be set to NULL. + /// Devices which have SoundIoDevice::probe_error set to #SoundIoErrorNone are + /// guaranteed to have at least 1 sample rate available. + struct SoundIoSampleRateRange *sample_rates; + /// How many sample rate ranges are available in + /// SoundIoDevice::sample_rates. 0 if sample rate information is missing + /// due to a probe error. + int sample_rate_count; + /// See SoundIoDevice::current_format + /// 0 if sample rate information is missing due to a probe error. + int sample_rate_current; + + /// Software latency minimum in seconds. If this value is unknown or + /// irrelevant, it is set to 0.0. + /// For PulseAudio and WASAPI this value is unknown until you open a + /// stream. + double software_latency_min; + /// Software latency maximum in seconds. If this value is unknown or + /// irrelevant, it is set to 0.0. + /// For PulseAudio and WASAPI this value is unknown until you open a + /// stream. + double software_latency_max; + /// Software latency in seconds. If this value is unknown or + /// irrelevant, it is set to 0.0. + /// For PulseAudio and WASAPI this value is unknown until you open a + /// stream. + /// See SoundIoDevice::current_format + double software_latency_current; + + /// Raw means that you are directly opening the hardware device and not + /// going through a proxy such as dmix, PulseAudio, or JACK. When you open a + /// raw device, other applications on the computer are not able to + /// simultaneously access the device. Raw devices do not perform automatic + /// resampling and thus tend to have fewer formats available. + bool is_raw; + + /// Devices are reference counted. See ::soundio_device_ref and + /// ::soundio_device_unref. + int ref_count; + + /// This is set to a SoundIoError representing the result of the device + /// probe. Ideally this will be SoundIoErrorNone in which case all the + /// fields of the device will be populated. If there is an error code here + /// then information about formats, sample rates, and channel layouts might + /// be missing. + /// + /// Possible errors: + /// * #SoundIoErrorOpeningDevice + /// * #SoundIoErrorNoMem + int probe_error; +}; + +/// The size of this struct is not part of the API or ABI. +struct SoundIoOutStream { + /// Populated automatically when you call ::soundio_outstream_create. + struct SoundIoDevice *device; + + /// Defaults to #SoundIoFormatFloat32NE, followed by the first one + /// supported. + enum SoundIoFormat format; + + /// Sample rate is the number of frames per second. + /// Defaults to 48000 (and then clamped into range). + int sample_rate; + + /// Defaults to Stereo, if available, followed by the first layout + /// supported. + struct SoundIoChannelLayout layout; + + /// Ignoring hardware latency, this is the number of seconds it takes for + /// the last sample in a full buffer to be played. + /// After you call ::soundio_outstream_open, this value is replaced with the + /// actual software latency, as near to this value as possible. + /// On systems that support clearing the buffer, this defaults to a large + /// latency, potentially upwards of 2 seconds, with the understanding that + /// you will call ::soundio_outstream_clear_buffer when you want to reduce + /// the latency to 0. On systems that do not support clearing the buffer, + /// this defaults to a reasonable lower latency value. + /// + /// On backends with high latencies (such as 2 seconds), `frame_count_min` + /// will be 0, meaning you don't have to fill the entire buffer. In this + /// case, the large buffer is there if you want it; you only have to fill + /// as much as you want. On backends like JACK, `frame_count_min` will be + /// equal to `frame_count_max` and if you don't fill that many frames, you + /// will get glitches. + /// + /// If the device has unknown software latency min and max values, you may + /// still set this, but you might not get the value you requested. + /// For PulseAudio, if you set this value to non-default, it sets + /// `PA_STREAM_ADJUST_LATENCY` and is the value used for `maxlength` and + /// `tlength`. + /// + /// For JACK, this value is always equal to + /// SoundIoDevice::software_latency_current of the device. + double software_latency; + /// Core Audio and WASAPI only: current output Audio Unit volume. Float, 0.0-1.0. + float volume; + /// Defaults to NULL. Put whatever you want here. + void *userdata; + /// In this callback, you call ::soundio_outstream_begin_write and + /// ::soundio_outstream_end_write as many times as necessary to write + /// at minimum `frame_count_min` frames and at maximum `frame_count_max` + /// frames. `frame_count_max` will always be greater than 0. Note that you + /// should write as many frames as you can; `frame_count_min` might be 0 and + /// you can still get a buffer underflow if you always write + /// `frame_count_min` frames. + /// + /// For Dummy, ALSA, and PulseAudio, `frame_count_min` will be 0. For JACK + /// and CoreAudio `frame_count_min` will be equal to `frame_count_max`. + /// + /// The code in the supplied function must be suitable for real-time + /// execution. That means that it cannot call functions that might block + /// for a long time. This includes all I/O functions (disk, TTY, network), + /// malloc, free, printf, pthread_mutex_lock, sleep, wait, poll, select, + /// pthread_join, pthread_cond_wait, etc. + void (*write_callback)(struct SoundIoOutStream *, + int frame_count_min, int frame_count_max); + /// This optional callback happens when the sound device runs out of + /// buffered audio data to play. After this occurs, the outstream waits + /// until the buffer is full to resume playback. + /// This is called from the SoundIoOutStream::write_callback thread context. + void (*underflow_callback)(struct SoundIoOutStream *); + /// Optional callback. `err` is always SoundIoErrorStreaming. + /// SoundIoErrorStreaming is an unrecoverable error. The stream is in an + /// invalid state and must be destroyed. + /// If you do not supply error_callback, the default callback will print + /// a message to stderr and then call `abort`. + /// This is called from the SoundIoOutStream::write_callback thread context. + void (*error_callback)(struct SoundIoOutStream *, int err); + + /// Optional: Name of the stream. Defaults to "SoundIoOutStream" + /// PulseAudio uses this for the stream name. + /// JACK uses this for the client name of the client that connects when you + /// open the stream. + /// WASAPI uses this for the session display name. + /// Must not contain a colon (":"). + const char *name; + + /// Optional: Hint that this output stream is nonterminal. This is used by + /// JACK and it means that the output stream data originates from an input + /// stream. Defaults to `false`. + bool non_terminal_hint; + + + /// computed automatically when you call ::soundio_outstream_open + int bytes_per_frame; + /// computed automatically when you call ::soundio_outstream_open + int bytes_per_sample; + + /// If setting the channel layout fails for some reason, this field is set + /// to an error code. Possible error codes are: + /// * #SoundIoErrorIncompatibleDevice + int layout_error; +}; + +/// The size of this struct is not part of the API or ABI. +struct SoundIoInStream { + /// Populated automatically when you call ::soundio_outstream_create. + struct SoundIoDevice *device; + + /// Defaults to #SoundIoFormatFloat32NE, followed by the first one + /// supported. + enum SoundIoFormat format; + + /// Sample rate is the number of frames per second. + /// Defaults to max(sample_rate_min, min(sample_rate_max, 48000)) + int sample_rate; + + /// Defaults to Stereo, if available, followed by the first layout + /// supported. + struct SoundIoChannelLayout layout; + + /// Ignoring hardware latency, this is the number of seconds it takes for a + /// captured sample to become available for reading. + /// After you call ::soundio_instream_open, this value is replaced with the + /// actual software latency, as near to this value as possible. + /// A higher value means less CPU usage. Defaults to a large value, + /// potentially upwards of 2 seconds. + /// If the device has unknown software latency min and max values, you may + /// still set this, but you might not get the value you requested. + /// For PulseAudio, if you set this value to non-default, it sets + /// `PA_STREAM_ADJUST_LATENCY` and is the value used for `fragsize`. + /// For JACK, this value is always equal to + /// SoundIoDevice::software_latency_current + double software_latency; + + /// Defaults to NULL. Put whatever you want here. + void *userdata; + + + /// In this function call ::soundio_instream_begin_read and + /// ::soundio_instream_end_read as many times as necessary to read at + /// minimum `frame_count_min` frames and at maximum `frame_count_max` + /// frames. If you return from read_callback without having read + /// `frame_count_min`, the frames will be dropped. `frame_count_max` is how + /// many frames are available to read. + /// + /// The code in the supplied function must be suitable for real-time + /// execution. That means that it cannot call functions that might block + /// for a long time. This includes all I/O functions (disk, TTY, network), + /// malloc, free, printf, pthread_mutex_lock, sleep, wait, poll, select, + /// pthread_join, pthread_cond_wait, etc. + void (*read_callback)(struct SoundIoInStream *, int frame_count_min, int frame_count_max); + /// This optional callback happens when the sound device buffer is full, + /// yet there is more captured audio to put in it. + /// This is never fired for PulseAudio. + /// This is called from the SoundIoInStream::read_callback thread context. + void (*overflow_callback)(struct SoundIoInStream *); + /// Optional callback. `err` is always SoundIoErrorStreaming. + /// SoundIoErrorStreaming is an unrecoverable error. The stream is in an + /// invalid state and must be destroyed. + /// If you do not supply `error_callback`, the default callback will print + /// a message to stderr and then abort(). + /// This is called from the SoundIoInStream::read_callback thread context. + void (*error_callback)(struct SoundIoInStream *, int err); + + /// Optional: Name of the stream. Defaults to "SoundIoInStream"; + /// PulseAudio uses this for the stream name. + /// JACK uses this for the client name of the client that connects when you + /// open the stream. + /// WASAPI uses this for the session display name. + /// Must not contain a colon (":"). + const char *name; + + /// Optional: Hint that this input stream is nonterminal. This is used by + /// JACK and it means that the data received by the stream will be + /// passed on or made available to another stream. Defaults to `false`. + bool non_terminal_hint; + + /// computed automatically when you call ::soundio_instream_open + int bytes_per_frame; + /// computed automatically when you call ::soundio_instream_open + int bytes_per_sample; + + /// If setting the channel layout fails for some reason, this field is set + /// to an error code. Possible error codes are: #SoundIoErrorIncompatibleDevice + int layout_error; +}; + +/// See also ::soundio_version_major, ::soundio_version_minor, ::soundio_version_patch +SOUNDIO_EXPORT const char *soundio_version_string(void); +/// See also ::soundio_version_string, ::soundio_version_minor, ::soundio_version_patch +SOUNDIO_EXPORT int soundio_version_major(void); +/// See also ::soundio_version_major, ::soundio_version_string, ::soundio_version_patch +SOUNDIO_EXPORT int soundio_version_minor(void); +/// See also ::soundio_version_major, ::soundio_version_minor, ::soundio_version_string +SOUNDIO_EXPORT int soundio_version_patch(void); + +/// Create a SoundIo context. You may create multiple instances of this to +/// connect to multiple backends. Sets all fields to defaults. +/// Returns `NULL` if and only if memory could not be allocated. +/// See also ::soundio_destroy +SOUNDIO_EXPORT struct SoundIo *soundio_create(void); +SOUNDIO_EXPORT void soundio_destroy(struct SoundIo *soundio); + + +/// Tries ::soundio_connect_backend on all available backends in order. +/// Possible errors: +/// * #SoundIoErrorInvalid - already connected +/// * #SoundIoErrorNoMem +/// * #SoundIoErrorSystemResources +/// * #SoundIoErrorNoSuchClient - when JACK returns `JackNoSuchClient` +/// See also ::soundio_disconnect +SOUNDIO_EXPORT int soundio_connect(struct SoundIo *soundio); +/// Instead of calling ::soundio_connect you may call this function to try a +/// specific backend. +/// Possible errors: +/// * #SoundIoErrorInvalid - already connected or invalid backend parameter +/// * #SoundIoErrorNoMem +/// * #SoundIoErrorBackendUnavailable - backend was not compiled in +/// * #SoundIoErrorSystemResources +/// * #SoundIoErrorNoSuchClient - when JACK returns `JackNoSuchClient` +/// * #SoundIoErrorInitAudioBackend - requested `backend` is not active +/// * #SoundIoErrorBackendDisconnected - backend disconnected while connecting +/// See also ::soundio_disconnect +SOUNDIO_EXPORT int soundio_connect_backend(struct SoundIo *soundio, enum SoundIoBackend backend); +SOUNDIO_EXPORT void soundio_disconnect(struct SoundIo *soundio); + +/// Get a string representation of a #SoundIoError +SOUNDIO_EXPORT const char *soundio_strerror(int error); +/// Get a string representation of a #SoundIoBackend +SOUNDIO_EXPORT const char *soundio_backend_name(enum SoundIoBackend backend); + +/// Returns the number of available backends. +SOUNDIO_EXPORT int soundio_backend_count(struct SoundIo *soundio); +/// get the available backend at the specified index +/// (0 <= index < ::soundio_backend_count) +SOUNDIO_EXPORT enum SoundIoBackend soundio_get_backend(struct SoundIo *soundio, int index); + +/// Returns whether libsoundio was compiled with backend. +SOUNDIO_EXPORT bool soundio_have_backend(enum SoundIoBackend backend); + +/// Atomically update information for all connected devices. Note that calling +/// this function merely flips a pointer; the actual work of collecting device +/// information is done elsewhere. It is performant to call this function many +/// times per second. +/// +/// When you call this, the following callbacks might be called: +/// * SoundIo::on_devices_change +/// * SoundIo::on_backend_disconnect +/// This is the only time those callbacks can be called. +/// +/// This must be called from the same thread as the thread in which you call +/// these functions: +/// * ::soundio_input_device_count +/// * ::soundio_output_device_count +/// * ::soundio_get_input_device +/// * ::soundio_get_output_device +/// * ::soundio_default_input_device_index +/// * ::soundio_default_output_device_index +/// +/// Note that if you do not care about learning about updated devices, you +/// might call this function only once ever and never call +/// ::soundio_wait_events. +SOUNDIO_EXPORT void soundio_flush_events(struct SoundIo *soundio); + +/// This function calls ::soundio_flush_events then blocks until another event +/// is ready or you call ::soundio_wakeup. Be ready for spurious wakeups. +SOUNDIO_EXPORT void soundio_wait_events(struct SoundIo *soundio); + +/// Makes ::soundio_wait_events stop blocking. +SOUNDIO_EXPORT void soundio_wakeup(struct SoundIo *soundio); + + +/// If necessary you can manually trigger a device rescan. Normally you will +/// not ever have to call this function, as libsoundio listens to system events +/// for device changes and responds to them by rescanning devices and preparing +/// the new device information for you to be atomically replaced when you call +/// ::soundio_flush_events. However you might run into cases where you want to +/// force trigger a device rescan, for example if an ALSA device has a +/// SoundIoDevice::probe_error. +/// +/// After you call this you still have to use ::soundio_flush_events or +/// ::soundio_wait_events and then wait for the +/// SoundIo::on_devices_change callback. +/// +/// This can be called from any thread context except for +/// SoundIoOutStream::write_callback and SoundIoInStream::read_callback +SOUNDIO_EXPORT void soundio_force_device_scan(struct SoundIo *soundio); + + +// Channel Layouts + +/// Returns whether the channel count field and each channel id matches in +/// the supplied channel layouts. +SOUNDIO_EXPORT bool soundio_channel_layout_equal( + const struct SoundIoChannelLayout *a, + const struct SoundIoChannelLayout *b); + +SOUNDIO_EXPORT const char *soundio_get_channel_name(enum SoundIoChannelId id); +/// Given UTF-8 encoded text which is the name of a channel such as +/// "Front Left", "FL", or "front-left", return the corresponding +/// SoundIoChannelId. Returns SoundIoChannelIdInvalid for no match. +SOUNDIO_EXPORT enum SoundIoChannelId soundio_parse_channel_id(const char *str, int str_len); + +/// Returns the number of builtin channel layouts. +SOUNDIO_EXPORT int soundio_channel_layout_builtin_count(void); +/// Returns a builtin channel layout. 0 <= `index` < ::soundio_channel_layout_builtin_count +/// +/// Although `index` is of type `int`, it should be a valid +/// #SoundIoChannelLayoutId enum value. +SOUNDIO_EXPORT const struct SoundIoChannelLayout *soundio_channel_layout_get_builtin(int index); + +/// Get the default builtin channel layout for the given number of channels. +SOUNDIO_EXPORT const struct SoundIoChannelLayout *soundio_channel_layout_get_default(int channel_count); + +/// Return the index of `channel` in `layout`, or `-1` if not found. +SOUNDIO_EXPORT int soundio_channel_layout_find_channel( + const struct SoundIoChannelLayout *layout, enum SoundIoChannelId channel); + +/// Populates the name field of layout if it matches a builtin one. +/// returns whether it found a match +SOUNDIO_EXPORT bool soundio_channel_layout_detect_builtin(struct SoundIoChannelLayout *layout); + +/// Iterates over preferred_layouts. Returns the first channel layout in +/// preferred_layouts which matches one of the channel layouts in +/// available_layouts. Returns NULL if none matches. +SOUNDIO_EXPORT const struct SoundIoChannelLayout *soundio_best_matching_channel_layout( + const struct SoundIoChannelLayout *preferred_layouts, int preferred_layout_count, + const struct SoundIoChannelLayout *available_layouts, int available_layout_count); + +/// Sorts by channel count, descending. +SOUNDIO_EXPORT void soundio_sort_channel_layouts(struct SoundIoChannelLayout *layouts, int layout_count); + + +// Sample Formats + +/// Returns -1 on invalid format. +SOUNDIO_EXPORT int soundio_get_bytes_per_sample(enum SoundIoFormat format); + +/// A frame is one sample per channel. +static inline int soundio_get_bytes_per_frame(enum SoundIoFormat format, int channel_count) { + return soundio_get_bytes_per_sample(format) * channel_count; +} + +/// Sample rate is the number of frames per second. +static inline int soundio_get_bytes_per_second(enum SoundIoFormat format, + int channel_count, int sample_rate) +{ + return soundio_get_bytes_per_frame(format, channel_count) * sample_rate; +} + +/// Returns string representation of `format`. +SOUNDIO_EXPORT const char * soundio_format_string(enum SoundIoFormat format); + + + + +// Devices + +/// When you call ::soundio_flush_events, a snapshot of all device state is +/// saved and these functions merely access the snapshot data. When you want +/// to check for new devices, call ::soundio_flush_events. Or you can call +/// ::soundio_wait_events to block until devices change. If an error occurs +/// scanning devices in a background thread, SoundIo::on_backend_disconnect is called +/// with the error code. + +/// Get the number of input devices. +/// Returns -1 if you never called ::soundio_flush_events. +SOUNDIO_EXPORT int soundio_input_device_count(struct SoundIo *soundio); +/// Get the number of output devices. +/// Returns -1 if you never called ::soundio_flush_events. +SOUNDIO_EXPORT int soundio_output_device_count(struct SoundIo *soundio); + +/// Always returns a device. Call ::soundio_device_unref when done. +/// `index` must be 0 <= index < ::soundio_input_device_count +/// Returns NULL if you never called ::soundio_flush_events or if you provide +/// invalid parameter values. +SOUNDIO_EXPORT struct SoundIoDevice *soundio_get_input_device(struct SoundIo *soundio, int index); +/// Always returns a device. Call ::soundio_device_unref when done. +/// `index` must be 0 <= index < ::soundio_output_device_count +/// Returns NULL if you never called ::soundio_flush_events or if you provide +/// invalid parameter values. +SOUNDIO_EXPORT struct SoundIoDevice *soundio_get_output_device(struct SoundIo *soundio, int index); + +/// returns the index of the default input device +/// returns -1 if there are no devices or if you never called +/// ::soundio_flush_events. +SOUNDIO_EXPORT int soundio_default_input_device_index(struct SoundIo *soundio); + +/// returns the index of the default output device +/// returns -1 if there are no devices or if you never called +/// ::soundio_flush_events. +SOUNDIO_EXPORT int soundio_default_output_device_index(struct SoundIo *soundio); + +/// Add 1 to the reference count of `device`. +SOUNDIO_EXPORT void soundio_device_ref(struct SoundIoDevice *device); +/// Remove 1 to the reference count of `device`. Clean up if it was the last +/// reference. +SOUNDIO_EXPORT void soundio_device_unref(struct SoundIoDevice *device); + +/// Return `true` if and only if the devices have the same SoundIoDevice::id, +/// SoundIoDevice::is_raw, and SoundIoDevice::aim are the same. +SOUNDIO_EXPORT bool soundio_device_equal( + const struct SoundIoDevice *a, + const struct SoundIoDevice *b); + +/// Sorts channel layouts by channel count, descending. +SOUNDIO_EXPORT void soundio_device_sort_channel_layouts(struct SoundIoDevice *device); + +/// Convenience function. Returns whether `format` is included in the device's +/// supported formats. +SOUNDIO_EXPORT bool soundio_device_supports_format(struct SoundIoDevice *device, + enum SoundIoFormat format); + +/// Convenience function. Returns whether `layout` is included in the device's +/// supported channel layouts. +SOUNDIO_EXPORT bool soundio_device_supports_layout(struct SoundIoDevice *device, + const struct SoundIoChannelLayout *layout); + +/// Convenience function. Returns whether `sample_rate` is included in the +/// device's supported sample rates. +SOUNDIO_EXPORT bool soundio_device_supports_sample_rate(struct SoundIoDevice *device, + int sample_rate); + +/// Convenience function. Returns the available sample rate nearest to +/// `sample_rate`, rounding up. +SOUNDIO_EXPORT int soundio_device_nearest_sample_rate(struct SoundIoDevice *device, + int sample_rate); + + + +// Output Streams +/// Allocates memory and sets defaults. Next you should fill out the struct fields +/// and then call ::soundio_outstream_open. Sets all fields to defaults. +/// Returns `NULL` if and only if memory could not be allocated. +/// See also ::soundio_outstream_destroy +SOUNDIO_EXPORT struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device); +/// You may not call this function from the SoundIoOutStream::write_callback thread context. +SOUNDIO_EXPORT void soundio_outstream_destroy(struct SoundIoOutStream *outstream); + +/// After you call this function, SoundIoOutStream::software_latency is set to +/// the correct value. +/// +/// The next thing to do is call ::soundio_outstream_start. +/// If this function returns an error, the outstream is in an invalid state and +/// you must call ::soundio_outstream_destroy on it. +/// +/// Possible errors: +/// * #SoundIoErrorInvalid +/// * SoundIoDevice::aim is not #SoundIoDeviceAimOutput +/// * SoundIoOutStream::format is not valid +/// * SoundIoOutStream::channel_count is greater than #SOUNDIO_MAX_CHANNELS +/// * #SoundIoErrorNoMem +/// * #SoundIoErrorOpeningDevice +/// * #SoundIoErrorBackendDisconnected +/// * #SoundIoErrorSystemResources +/// * #SoundIoErrorNoSuchClient - when JACK returns `JackNoSuchClient` +/// * #SoundIoErrorIncompatibleBackend - SoundIoOutStream::channel_count is +/// greater than the number of channels the backend can handle. +/// * #SoundIoErrorIncompatibleDevice - stream parameters requested are not +/// compatible with the chosen device. +SOUNDIO_EXPORT int soundio_outstream_open(struct SoundIoOutStream *outstream); + +/// After you call this function, SoundIoOutStream::write_callback will be called. +/// +/// This function might directly call SoundIoOutStream::write_callback. +/// +/// Possible errors: +/// * #SoundIoErrorStreaming +/// * #SoundIoErrorNoMem +/// * #SoundIoErrorSystemResources +/// * #SoundIoErrorBackendDisconnected +SOUNDIO_EXPORT int soundio_outstream_start(struct SoundIoOutStream *outstream); + +/// Call this function when you are ready to begin writing to the device buffer. +/// * `outstream` - (in) The output stream you want to write to. +/// * `areas` - (out) The memory addresses you can write data to, one per +/// channel. It is OK to modify the pointers if that helps you iterate. +/// * `frame_count` - (in/out) Provide the number of frames you want to write. +/// Returned will be the number of frames you can actually write, which is +/// also the number of frames that will be written when you call +/// ::soundio_outstream_end_write. The value returned will always be less +/// than or equal to the value provided. +/// It is your responsibility to call this function exactly as many times as +/// necessary to meet the `frame_count_min` and `frame_count_max` criteria from +/// SoundIoOutStream::write_callback. +/// You must call this function only from the SoundIoOutStream::write_callback thread context. +/// After calling this function, write data to `areas` and then call +/// ::soundio_outstream_end_write. +/// If this function returns an error, do not call ::soundio_outstream_end_write. +/// +/// Possible errors: +/// * #SoundIoErrorInvalid +/// * `*frame_count` <= 0 +/// * `*frame_count` < `frame_count_min` or `*frame_count` > `frame_count_max` +/// * function called too many times without respecting `frame_count_max` +/// * #SoundIoErrorStreaming +/// * #SoundIoErrorUnderflow - an underflow caused this call to fail. You might +/// also get a SoundIoOutStream::underflow_callback, and you might not get +/// this error code when an underflow occurs. Unlike #SoundIoErrorStreaming, +/// the outstream is still in a valid state and streaming can continue. +/// * #SoundIoErrorIncompatibleDevice - in rare cases it might just now +/// be discovered that the device uses non-byte-aligned access, in which +/// case this error code is returned. +SOUNDIO_EXPORT int soundio_outstream_begin_write(struct SoundIoOutStream *outstream, + struct SoundIoChannelArea **areas, int *frame_count); + +/// Commits the write that you began with ::soundio_outstream_begin_write. +/// You must call this function only from the SoundIoOutStream::write_callback thread context. +/// +/// Possible errors: +/// * #SoundIoErrorStreaming +/// * #SoundIoErrorUnderflow - an underflow caused this call to fail. You might +/// also get a SoundIoOutStream::underflow_callback, and you might not get +/// this error code when an underflow occurs. Unlike #SoundIoErrorStreaming, +/// the outstream is still in a valid state and streaming can continue. +SOUNDIO_EXPORT int soundio_outstream_end_write(struct SoundIoOutStream *outstream); + +/// Clears the output stream buffer. +/// This function can be called from any thread. +/// This function can be called regardless of whether the outstream is paused +/// or not. +/// Some backends do not support clearing the buffer. On these backends this +/// function will return SoundIoErrorIncompatibleBackend. +/// Some devices do not support clearing the buffer. On these devices this +/// function might return SoundIoErrorIncompatibleDevice. +/// Possible errors: +/// +/// * #SoundIoErrorStreaming +/// * #SoundIoErrorIncompatibleBackend +/// * #SoundIoErrorIncompatibleDevice +SOUNDIO_EXPORT int soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream); + +/// If the underlying backend and device support pausing, this pauses the +/// stream. SoundIoOutStream::write_callback may be called a few more times if +/// the buffer is not full. +/// Pausing might put the hardware into a low power state which is ideal if your +/// software is silent for some time. +/// This function may be called from any thread context, including +/// SoundIoOutStream::write_callback. +/// Pausing when already paused or unpausing when already unpaused has no +/// effect and returns #SoundIoErrorNone. +/// +/// Possible errors: +/// * #SoundIoErrorBackendDisconnected +/// * #SoundIoErrorStreaming +/// * #SoundIoErrorIncompatibleDevice - device does not support +/// pausing/unpausing. This error code might not be returned even if the +/// device does not support pausing/unpausing. +/// * #SoundIoErrorIncompatibleBackend - backend does not support +/// pausing/unpausing. +/// * #SoundIoErrorInvalid - outstream not opened and started +SOUNDIO_EXPORT int soundio_outstream_pause(struct SoundIoOutStream *outstream, bool pause); + +/// Obtain the total number of seconds that the next frame written after the +/// last frame written with ::soundio_outstream_end_write will take to become +/// audible. This includes both software and hardware latency. In other words, +/// if you call this function directly after calling ::soundio_outstream_end_write, +/// this gives you the number of seconds that the next frame written will take +/// to become audible. +/// +/// This function must be called only from within SoundIoOutStream::write_callback. +/// +/// Possible errors: +/// * #SoundIoErrorStreaming +SOUNDIO_EXPORT int soundio_outstream_get_latency(struct SoundIoOutStream *outstream, + double *out_latency); + +SOUNDIO_EXPORT int soundio_outstream_set_volume(struct SoundIoOutStream *outstream, + double volume); + + + +// Input Streams +/// Allocates memory and sets defaults. Next you should fill out the struct fields +/// and then call ::soundio_instream_open. Sets all fields to defaults. +/// Returns `NULL` if and only if memory could not be allocated. +/// See also ::soundio_instream_destroy +SOUNDIO_EXPORT struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device); +/// You may not call this function from SoundIoInStream::read_callback. +SOUNDIO_EXPORT void soundio_instream_destroy(struct SoundIoInStream *instream); + +/// After you call this function, SoundIoInStream::software_latency is set to the correct +/// value. +/// The next thing to do is call ::soundio_instream_start. +/// If this function returns an error, the instream is in an invalid state and +/// you must call ::soundio_instream_destroy on it. +/// +/// Possible errors: +/// * #SoundIoErrorInvalid +/// * device aim is not #SoundIoDeviceAimInput +/// * format is not valid +/// * requested layout channel count > #SOUNDIO_MAX_CHANNELS +/// * #SoundIoErrorOpeningDevice +/// * #SoundIoErrorNoMem +/// * #SoundIoErrorBackendDisconnected +/// * #SoundIoErrorSystemResources +/// * #SoundIoErrorNoSuchClient +/// * #SoundIoErrorIncompatibleBackend +/// * #SoundIoErrorIncompatibleDevice +SOUNDIO_EXPORT int soundio_instream_open(struct SoundIoInStream *instream); + +/// After you call this function, SoundIoInStream::read_callback will be called. +/// +/// Possible errors: +/// * #SoundIoErrorBackendDisconnected +/// * #SoundIoErrorStreaming +/// * #SoundIoErrorOpeningDevice +/// * #SoundIoErrorSystemResources +SOUNDIO_EXPORT int soundio_instream_start(struct SoundIoInStream *instream); + +/// Call this function when you are ready to begin reading from the device +/// buffer. +/// * `instream` - (in) The input stream you want to read from. +/// * `areas` - (out) The memory addresses you can read data from. It is OK +/// to modify the pointers if that helps you iterate. There might be a "hole" +/// in the buffer. To indicate this, `areas` will be `NULL` and `frame_count` +/// tells how big the hole is in frames. +/// * `frame_count` - (in/out) - Provide the number of frames you want to read; +/// returns the number of frames you can actually read. The returned value +/// will always be less than or equal to the provided value. If the provided +/// value is less than `frame_count_min` from SoundIoInStream::read_callback this function +/// returns with #SoundIoErrorInvalid. +/// It is your responsibility to call this function no more and no fewer than the +/// correct number of times according to the `frame_count_min` and +/// `frame_count_max` criteria from SoundIoInStream::read_callback. +/// You must call this function only from the SoundIoInStream::read_callback thread context. +/// After calling this function, read data from `areas` and then use +/// ::soundio_instream_end_read` to actually remove the data from the buffer +/// and move the read index forward. ::soundio_instream_end_read should not be +/// called if the buffer is empty (`frame_count` == 0), but it should be called +/// if there is a hole. +/// +/// Possible errors: +/// * #SoundIoErrorInvalid +/// * `*frame_count` < `frame_count_min` or `*frame_count` > `frame_count_max` +/// * #SoundIoErrorStreaming +/// * #SoundIoErrorIncompatibleDevice - in rare cases it might just now +/// be discovered that the device uses non-byte-aligned access, in which +/// case this error code is returned. +SOUNDIO_EXPORT int soundio_instream_begin_read(struct SoundIoInStream *instream, + struct SoundIoChannelArea **areas, int *frame_count); +/// This will drop all of the frames from when you called +/// ::soundio_instream_begin_read. +/// You must call this function only from the SoundIoInStream::read_callback thread context. +/// You must call this function only after a successful call to +/// ::soundio_instream_begin_read. +/// +/// Possible errors: +/// * #SoundIoErrorStreaming +SOUNDIO_EXPORT int soundio_instream_end_read(struct SoundIoInStream *instream); + +/// If the underyling device supports pausing, this pauses the stream and +/// prevents SoundIoInStream::read_callback from being called. Otherwise this returns +/// #SoundIoErrorIncompatibleDevice. +/// This function may be called from any thread. +/// Pausing when already paused or unpausing when already unpaused has no +/// effect and always returns #SoundIoErrorNone. +/// +/// Possible errors: +/// * #SoundIoErrorBackendDisconnected +/// * #SoundIoErrorStreaming +/// * #SoundIoErrorIncompatibleDevice - device does not support pausing/unpausing +SOUNDIO_EXPORT int soundio_instream_pause(struct SoundIoInStream *instream, bool pause); + +/// Obtain the number of seconds that the next frame of sound being +/// captured will take to arrive in the buffer, plus the amount of time that is +/// represented in the buffer. This includes both software and hardware latency. +/// +/// This function must be called only from within SoundIoInStream::read_callback. +/// +/// Possible errors: +/// * #SoundIoErrorStreaming +SOUNDIO_EXPORT int soundio_instream_get_latency(struct SoundIoInStream *instream, + double *out_latency); + + +struct SoundIoRingBuffer; + +/// A ring buffer is a single-reader single-writer lock-free fixed-size queue. +/// libsoundio ring buffers use memory mapping techniques to enable a +/// contiguous buffer when reading or writing across the boundary of the ring +/// buffer's capacity. +/// `requested_capacity` in bytes. +/// Returns `NULL` if and only if memory could not be allocated. +/// Use ::soundio_ring_buffer_capacity to get the actual capacity, which might +/// be greater for alignment purposes. +/// See also ::soundio_ring_buffer_destroy +SOUNDIO_EXPORT struct SoundIoRingBuffer *soundio_ring_buffer_create(struct SoundIo *soundio, int requested_capacity); +SOUNDIO_EXPORT void soundio_ring_buffer_destroy(struct SoundIoRingBuffer *ring_buffer); + +/// When you create a ring buffer, capacity might be more than the requested +/// capacity for alignment purposes. This function returns the actual capacity. +SOUNDIO_EXPORT int soundio_ring_buffer_capacity(struct SoundIoRingBuffer *ring_buffer); + +/// Do not write more than capacity. +SOUNDIO_EXPORT char *soundio_ring_buffer_write_ptr(struct SoundIoRingBuffer *ring_buffer); +/// `count` in bytes. +SOUNDIO_EXPORT void soundio_ring_buffer_advance_write_ptr(struct SoundIoRingBuffer *ring_buffer, int count); + +/// Do not read more than capacity. +SOUNDIO_EXPORT char *soundio_ring_buffer_read_ptr(struct SoundIoRingBuffer *ring_buffer); +/// `count` in bytes. +SOUNDIO_EXPORT void soundio_ring_buffer_advance_read_ptr(struct SoundIoRingBuffer *ring_buffer, int count); + +/// Returns how many bytes of the buffer is used, ready for reading. +SOUNDIO_EXPORT int soundio_ring_buffer_fill_count(struct SoundIoRingBuffer *ring_buffer); + +/// Returns how many bytes of the buffer is free, ready for writing. +SOUNDIO_EXPORT int soundio_ring_buffer_free_count(struct SoundIoRingBuffer *ring_buffer); + +/// Must be called by the writer. +SOUNDIO_EXPORT void soundio_ring_buffer_clear(struct SoundIoRingBuffer *ring_buffer); + +#endif diff --git a/hsmodem/libsoundio.a b/hsmodem/libsoundio.a old mode 100644 new mode 100755 diff --git a/hsmodem/liquid_if.cpp b/hsmodem/liquid_if.cpp index aa3f2d4..567b58b 100755 --- a/hsmodem/liquid_if.cpp +++ b/hsmodem/liquid_if.cpp @@ -36,8 +36,10 @@ void close_modulator(); void init_dsp() { + printf("init DSP\n"); init_modulator(); - io_pb_write_fifo_clear(); + io_fifo_clear(io_pbidx); + io_fifo_clear(io_capidx); init_demodulator(); } @@ -202,16 +204,18 @@ void modulator(uint8_t sym_in) int fs; while(keeprunning) { - fs = io_pb_fifo_freespace(0); + fs = io_fifo_freespace(io_pbidx); // wait until there is space in fifo - if(fs > 20000) break; + if(fs > 10) break; sleep_ms(10); } - if(marker) + if (marker) usb += singleFrequency() / 4.0f; - io_pb_write_fifo(usb * 0.2f); // reduce volume and send to soundcard + // reduce volume and send to soundcard + usb *= 0.1f; + kmaudio_playsamples(io_pbidx, &usb, 1, pbvol); } } @@ -219,17 +223,11 @@ void modulator(uint8_t sym_in) nco_crcf dnnco = NULL; firdecim_crcf decim = NULL; -msresamp_crcf adecim = NULL; -msresamp_crcf lsresamp = NULL; // decimator parameters unsigned int m_predec = 8; // filter delay float As_predec = 40.0f; // stop-band att -// adecimator parameters -float r_OutDivInRatio; // resampling rate (output/input) -float As_adecim = 60.0f; // resampling filter stop-band attenuation [dB] - // symtrack parameters int ftype_st = LIQUID_FIRFILT_RRC; unsigned int k_st = 4; // samples per symbol @@ -260,13 +258,6 @@ void init_demodulator() decim = firdecim_crcf_create_kaiser(rxPreInterpolfactor, m_predec, As_predec); firdecim_crcf_set_scale(decim, 1.0f/(float)rxPreInterpolfactor); - // create arbitrary pre decimator - // if Audio SR is 48000 but caprate is 44100 - r_OutDivInRatio = (float)((float)caprate / 48000.0); - adecim = msresamp_crcf_create(r_OutDivInRatio, As_adecim); - - lsresamp = msresamp_crcf_create((float)(48000.0/44100.0), As_adecim); - // create symbol tracking synchronizer km_symtrack = km_symtrack_cccf_create(ftype_st, k_st, m_st, beta_st, getMod()); km_symtrack_cccf_set_bandwidth(km_symtrack, bandwidth_st); @@ -276,10 +267,6 @@ void close_demodulator() { if(decim != NULL) firdecim_crcf_destroy(decim); decim = NULL; - if(adecim) msresamp_crcf_destroy(adecim); - adecim = NULL; - if (lsresamp) msresamp_crcf_destroy(lsresamp); - lsresamp = NULL; if (dnnco != NULL) nco_crcf_destroy(dnnco); dnnco = NULL; if (km_symtrack != NULL) km_symtrack_cccf_destroy(km_symtrack); @@ -312,25 +299,33 @@ void make_FFTdata(float f) txpl[bidx++] = 4; // type 4: FFT data follows int us = 0; - if(speedmode < 10) - us = io_pb_fifo_usedBlocks(); + if (speedmode < 10) + { + // Bytes in Fifo + int bus = io_fifo_usedspace(io_pbidx); + // Payloads in fifo + us = bus / (txinterpolfactor * UDPBLOCKLEN * 8 / bitsPerSymbol); + //printf("bytes:%d blocks:%d\n", bus, us); + } if (speedmode == 10) { // RTTY - us = io_pb_fifo_usedspace(); + us = io_fifo_usedspace(io_pbidx); } if (us > 255 || ann_running == 1) us = 255; txpl[bidx++] = us; // usage of TX fifo - us = io_cap_fifo_usedPercent(); + + us = io_fifo_usedspace(io_capidx); + us /= 400; if (us > 255) us = 255; - txpl[bidx++] = us; // usage of TX fifo + txpl[bidx++] = us; // usage of RX fifo txpl[bidx++] = rxlevel_deteced; // RX level present txpl[bidx++] = rx_in_sync; - txpl[bidx++] = maxLevel; // actual max level on sound capture in % - txpl[bidx++] = maxTXLevel; // actual max level on sound playback in % + txpl[bidx++] = kmaudio_maxlevel(io_capidx); + txpl[bidx++] = kmaudio_maxlevel(io_pbidx); txpl[bidx++] = rtty_frequency >> 8; // rtty qrg by autosync txpl[bidx++] = rtty_frequency & 0xff; @@ -340,39 +335,11 @@ void make_FFTdata(float f) txpl[bidx++] = fft[i] >> 8; txpl[bidx++] = fft[i] & 0xff; } + sendUDP(appIP, UdpDataPort_ModemToApp, txpl, bidx); } } -#define MCHECK 48000 -void getMax(float fv) -{ - static float farr[MCHECK]; - static int idx = 0; - static int f = 1; - - if (f) - { - f = 0; - for (int i = 0; i < MCHECK; i++) - farr[i] = 1; - } - - farr[idx] = fv; - idx++; - if (idx == MCHECK) - { - idx = 0; - float max = 0; - for (int i = 0; i < MCHECK; i++) - { - if (farr[i] > max) max = farr[i]; - } - maxLevel = (uint8_t)(max*100); - //printf("RX max: %10.6f\n", max); - } -} - #define CONSTPOINTS 400 int demodulator() { @@ -383,124 +350,91 @@ static int16_t const_im[CONSTPOINTS]; static int const_idx = 0; if(dnnco == NULL) return 0; - - // get one received sample - float f; - int ret = io_cap_read_fifo(&f); + + // get available received samples + float farr[1100]; + int ret = kmaudio_readsamples(io_capidx, farr, 1000, capvol,0); if(ret == 0) return 0; - if (VoiceAudioMode == VOICEMODE_LISTENAUDIOIN) + //measure_speed_bps(ret); + + for (int fanz = 0; fanz < ret; fanz++) { - if (physRXcaprate == 44100) + float f = farr[fanz]; + if (VoiceAudioMode == VOICEMODE_LISTENAUDIOIN) { - // Loudspeaker Audio "ls" works only with 48k - // so we have to resample 44k1 to 48k - unsigned int num_written = 0; - liquid_float_complex in; - liquid_float_complex out[100]; - in.real = f; - in.imag = 0; - msresamp_crcf_execute(lsresamp, &in, 1, out, &num_written); - if (num_written != 0) - { - for (unsigned int i = 0; i < num_written; i++) - { - float fout = out[i].real; - io_ls_write_fifo(fout); - } - } + kmaudio_playsamples(voice_pbidx, &f, 1,lsvol); } - else - io_ls_write_fifo(f); - } - // input volume - f *= softwareCAPvolume; + make_FFTdata(f * 100); - getMax(f); - make_FFTdata(f * 100); - - if (caprate == 44100 && physRXcaprate == 48000) - { - // the sound card capture for a VAC always works with 48000 because - // a VAC cannot be set to a specific cap rate in shared mode - // downsample 48k to 44.1k - unsigned int num_written = 0; - liquid_float_complex in; - liquid_float_complex out; - in.real = f; - in.imag = 0; - msresamp_crcf_execute(adecim, &in, 1, &out, &num_written); - if (num_written == 0) return 1; - f = out.real; - } - - // if user changed frequency - if (radians_per_sample != last_radians_per_sample) - { - //printf("new QRG. Rad:%f\n", radians_per_sample); - nco_crcf_set_frequency(dnnco, radians_per_sample); - last_radians_per_sample = radians_per_sample; - } - - // downconvert 1,5kHz into baseband, still at soundcard sample rate - nco_crcf_step(dnnco); - - liquid_float_complex in; - in.real = f; - in.imag = f; - liquid_float_complex c; - nco_crcf_mix_down(dnnco,in,&c); - - // c is the actual sample, converted to complex and shifted to baseband - - // this is the first decimator. We need to collect rxPreInterpolfactor number of samples - // then call execute which will give us one decimated sample - ccol[ccol_idx++] = c; - if(ccol_idx < rxPreInterpolfactor) return 1; - ccol_idx = 0; - - // we have rxPreInterpolfactor samples in ccol - liquid_float_complex y; - firdecim_crcf_execute(decim, ccol, &y); - // the output of the pre decimator is exactly one sample in y - - unsigned int num_symbols_sync; - liquid_float_complex syms; - unsigned int nsym_out; // demodulated output symbol - km_symtrack_execute(km_symtrack,y, &syms, &num_symbols_sync, &nsym_out); - - if (num_symbols_sync > 1) printf("symtrack_cccf_execute %d output symbols ???\n", num_symbols_sync); - if (num_symbols_sync != 0) - { - measure_speed_syms(1); // do NOT remove, used for speed display in GUI - - // try to extract a complete frame - uint8_t symb = nsym_out; - if (bitsPerSymbol == 2) symb ^= (symb >> 1); - GRdata_rxdata(&symb, 1, NULL); - - // send the data "as is" to app for Constellation Diagram - // collect values until a UDP frame is full - const_re[const_idx] = (int16_t)(syms.real * 15000.0f); - const_im[const_idx] = (int16_t)(syms.imag * 15000.0f); - if (++const_idx >= CONSTPOINTS) + // if user changed frequency + if (radians_per_sample != last_radians_per_sample) { - uint8_t txpl[CONSTPOINTS * sizeof(int16_t) * 2 + 1]; - int idx = 0; - txpl[idx++] = 5; // type 5: IQ data follows + //printf("new QRG. Rad:%f\n", radians_per_sample); + nco_crcf_set_frequency(dnnco, radians_per_sample); + last_radians_per_sample = radians_per_sample; + } - for (int i = 0; i < CONSTPOINTS; i++) + // downconvert 1,5kHz into baseband, still at soundcard sample rate + nco_crcf_step(dnnco); + + liquid_float_complex in; + in.real = f; + in.imag = f; + liquid_float_complex c; + nco_crcf_mix_down(dnnco, in, &c); + + // c is the actual sample, converted to complex and shifted to baseband + + // this is the first decimator. We need to collect rxPreInterpolfactor number of samples + // then call execute which will give us one decimated sample + ccol[ccol_idx++] = c; + if (ccol_idx < rxPreInterpolfactor) continue; + ccol_idx = 0; + + // we have rxPreInterpolfactor samples in ccol + liquid_float_complex y; + firdecim_crcf_execute(decim, ccol, &y); + // the output of the pre decimator is exactly one sample in y + + unsigned int num_symbols_sync; + liquid_float_complex syms; + unsigned int nsym_out; // demodulated output symbol + km_symtrack_execute(km_symtrack, y, &syms, &num_symbols_sync, &nsym_out); + + if (num_symbols_sync > 1) printf("symtrack_cccf_execute %d output symbols ???\n", num_symbols_sync); + if (num_symbols_sync != 0) + { + measure_speed_syms(1); // do NOT remove, used for speed display in GUI + + // try to unpack and extract a complete frame + uint8_t symb = nsym_out; + if (bitsPerSymbol == 2) symb ^= (symb >> 1); + GRdata_rxdata(&symb, 1, NULL); + + // send the data "as is" to app for Constellation Diagram + // collect values until an UDP frame is full + const_re[const_idx] = (int16_t)(syms.real * 15000.0f); + const_im[const_idx] = (int16_t)(syms.imag * 15000.0f); + if (++const_idx >= CONSTPOINTS) { - txpl[idx++] = (uint8_t)(const_re[i] >> 8); - txpl[idx++] = (uint8_t)(const_re[i]); - txpl[idx++] = (uint8_t)(const_im[i] >> 8); - txpl[idx++] = (uint8_t)(const_im[i]); - } - - sendUDP(appIP, UdpDataPort_ModemToApp, txpl, sizeof(txpl)); + uint8_t txpl[CONSTPOINTS * sizeof(int16_t) * 2 + 1]; + int idx = 0; + txpl[idx++] = 5; // type 5: IQ data follows - const_idx = 0; + for (int i = 0; i < CONSTPOINTS; i++) + { + txpl[idx++] = (uint8_t)(const_re[i] >> 8); + txpl[idx++] = (uint8_t)(const_re[i]); + txpl[idx++] = (uint8_t)(const_im[i] >> 8); + txpl[idx++] = (uint8_t)(const_im[i]); + } + + sendUDP(appIP, UdpDataPort_ModemToApp, txpl, sizeof(txpl)); + + const_idx = 0; + } } } diff --git a/hsmodem/main_helper.cpp b/hsmodem/main_helper.cpp index 6929c4b..666bebc 100755 --- a/hsmodem/main_helper.cpp +++ b/hsmodem/main_helper.cpp @@ -74,8 +74,7 @@ void closeAllandTerminate() // terminate all Threads keeprunning = 0; // close audio - io_close_audio(); - io_close_voice(); + kmaudio_close(); // close fft _exit_fft(); // close codec2 and opus diff --git a/hsmodem/portaudio_x86.lib b/hsmodem/portaudio_x86.lib new file mode 100755 index 0000000..df382b8 Binary files /dev/null and b/hsmodem/portaudio_x86.lib differ diff --git a/hsmodem/prepare_ubuntu b/hsmodem/prepare_ubuntu old mode 100644 new mode 100755 diff --git a/hsmodem/rtty.cpp b/hsmodem/rtty.cpp index d4b0859..bf5af6b 100755 --- a/hsmodem/rtty.cpp +++ b/hsmodem/rtty.cpp @@ -78,7 +78,7 @@ int rtty_autosync = 0; void rtty_modifyRXfreq(int f_Hz) { - printf("set:%d Hz\n", f_Hz); + //printf("set:%d Hz\n", f_Hz); rtty_frequency = f_Hz; rtty_RX_RADIANS_PER_SAMPLE = ((2.0f * (float)M_PI * (float)f_Hz) / (float)caprate); } @@ -414,6 +414,8 @@ void init_rtty() //printf("wegen FM test, kein Init RTTY\n"); //return; + rtty_init_pipes(); + close_rtty(); printf("Init RTTY\n"); @@ -438,8 +440,8 @@ void init_rtty() // RTTY RX Filter rtty_q = firfilt_crcf_create_kaiser(flt_h_len, flt_fc, flt_As, 0.0f); firfilt_crcf_set_scale(rtty_q, 2.0f * flt_fc); - - // create the rtty TX threads + + // create the rtty threads static int f = 1; if (f) { @@ -497,6 +499,7 @@ void rtty_tx_function(void* param) uint8_t bd[2]; int anz = 0; + printf("TX thread\n"); while (keeprunning) { while (run_rtty_threads == 0) @@ -514,15 +517,6 @@ void rtty_tx_function(void* param) } } - /*static int last_txoff = -2; - if (last_txoff != 0 && rtty_txoff == 0) - { - // just switched TX on - //printf("force Bu/Zi switch: %d %d\n", rtty_txoff, last_txoff); - isletters = isletters ? 0 : 1; // force Bu/Zi command - } - last_txoff = rtty_txoff;*/ - char csend; if (rtty_tx_read_fifo(&csend)) { @@ -542,7 +536,7 @@ void rtty_tx_function(void* param) if (rtty_txoff > 1) rtty_txoff--; } - + //if(bd[0] != 0x1f) printf("send chars: %02X\n",bd[0]); for (int i = 0; i < anz; i++) @@ -586,14 +580,15 @@ void rtty_tx_function(void* param) int fs; while (keeprunning && run_rtty_threads) { - fs = io_pb_fifo_usedspace(); + fs = io_fifo_usedspace(io_pbidx); //printf("%d\n", fs); // attention: if this number is too low, the audio write callback will not process it if (fs < 24000) break; - sleep_ms(1); + sleep_ms(10); } - io_pb_write_fifo(usbf * 0.05f); // reduce volume and send to soundcard + usbf *= 0.2f; + kmaudio_playsamples(io_pbidx, &usbf, 1, pbvol); } } } @@ -634,18 +629,21 @@ void rtty_rx_function(void* param) } static int bridx = 0; - float f; - int ret = io_cap_read_fifo(&f); - if (ret) + // get available received samples + float farr[1100]; + int ret = kmaudio_readsamples(io_capidx, farr, 1000, capvol,0); + if (ret == 0) { + sleep_ms(10); + continue; + } + + for (int fanz = 0; fanz < ret; fanz++) + { + float f = farr[fanz]; if (VoiceAudioMode == VOICEMODE_LISTENAUDIOIN) - io_ls_write_fifo(f); + kmaudio_playsamples(voice_pbidx, &f, 1,lsvol); - // input volume - f *= softwareCAPvolume; - - // TODO check Multithreading !!!!!!!!! - getMax(f); make_FFTdata(f * 25); static float last_rs = 0; @@ -685,8 +683,6 @@ void rtty_rx_function(void* param) } } } - else - sleep_ms(1); } #ifdef _LINUX_ diff --git a/hsmodem/speed.cpp b/hsmodem/speed.cpp index 94bd0a0..d39f0dc 100755 --- a/hsmodem/speed.cpp +++ b/hsmodem/speed.cpp @@ -170,3 +170,4 @@ void measure_speed_bps(int len) elems = 0; lasttim = tim; } + \ No newline at end of file diff --git a/hsmodem/tuning.cpp b/hsmodem/tuning.cpp index 090b1a0..a2e44e3 100755 --- a/hsmodem/tuning.cpp +++ b/hsmodem/tuning.cpp @@ -26,8 +26,8 @@ #include "hsmodem.h" -#define NUMFREQ 13 -int tunefreq[NUMFREQ] = { 150,420,690,960,1230,1500,1770,2040,2310,2580,2850,2890,100 }; +#define NUMFREQ 15 +int tunefreq[NUMFREQ] = { 100,300,500,700,900,1100,1300,1500,1700,1900,2100,2300,2500,2700,2900 }; nco_crcf tunenco[NUMFREQ]; uint32_t tuning_runtime = 0; @@ -41,6 +41,7 @@ void init_tune() f = 0; } + printf("init tune tones to samprate: %d\n", caprate); for (int i = 0; i < NUMFREQ; i++) { if (tunenco[i] != NULL) nco_crcf_destroy(tunenco[i]); @@ -57,8 +58,7 @@ void init_tune() // send= 2,3,4 ... send a single freq to soundcard float do_tuning(int send) { - if (tunenco == NULL) return 0.0f; - if (send < 0 || send > 11) return 0.0f; + if (send < 0 || send >= NUMFREQ) return 0.0f; float f = 0; if (send == 1) @@ -81,13 +81,15 @@ float do_tuning(int send) int fs; while (keeprunning) { - fs = io_pb_fifo_freespace(0); + fs = io_fifo_usedspace(io_pbidx); // wait until there is space in fifo - if (fs > 20000) break; + if (fs < 48000) break; + if (tuning == 0) return 0.0f; sleep_ms(10); } - io_pb_write_fifo(f * 0.05f); // reduce volume and send to soundcard + f *= 0.05f; + kmaudio_playsamples(io_pbidx, &f, 1, pbvol); if (++tuning_runtime >= (48000 * 5 * 60)) { @@ -100,12 +102,7 @@ float do_tuning(int send) float singleFrequency() { - int /*i = 11; // 2900 Hz - if (tunenco[i] == NULL) return 0.0f; - nco_crcf_step(tunenco[i]); - float f = nco_crcf_sin(tunenco[i]);*/ - - i = 12; // 2930 Hz + int i = 0; // 100 Hz if (tunenco[i] == NULL) return 0.0f; nco_crcf_step(tunenco[i]); float f = nco_crcf_sin(tunenco[i]); diff --git a/hsmodem/udp.h b/hsmodem/udp.h old mode 100644 new mode 100755 diff --git a/hsmodem/version.h b/hsmodem/version.h old mode 100644 new mode 100755 diff --git a/hsmodem/voiceprocessor.cpp b/hsmodem/voiceprocessor.cpp index 763df73..30428dd 100755 --- a/hsmodem/voiceprocessor.cpp +++ b/hsmodem/voiceprocessor.cpp @@ -133,8 +133,7 @@ void encode(float f) printf("opus_decode_float error: %d", r); } - for (int i = 0; i < r; i++) - io_ls_write_fifo(fresult[i]); + kmaudio_playsamples(voice_pbidx, fresult, r,lsvol); } farridx = 0; @@ -171,7 +170,8 @@ void sendCodecToModulator(uint8_t *pdata, int len) while (keeprunning) { // we have to check if the TX fifo has enough data. In case of an underrun the Q(8A)PSK signal will be distorted - int us = io_pb_fifo_usedspace(); + //int us = io_pb_fifo_usedspace(); + int us = io_fifo_usedspace(io_pbidx); if (us < 20000) { //printf("tx filler\n"); @@ -244,9 +244,53 @@ void toCodecDecoder(uint8_t *pdata, int len) else { //measure_speed_bps(r); - for (int j = 0; j < r; j++) - io_ls_write_fifo(fresult[j]); + kmaudio_playsamples(voice_pbidx, fresult, r,lsvol); } } } } + +void io_saveStream(float *fa, int anz) +{ + static FILE* fw = NULL; + static int old_VoiceAudioMode = 0; + if (VoiceAudioMode != old_VoiceAudioMode) + { + if (old_VoiceAudioMode == VOICEMODE_OFF && VoiceAudioMode == VOICEMODE_RECORD) + { + char fn[500]; + snprintf(fn, 499, "%s/oscardata/intro/intro.pcm", homepath); + fn[499] = 0; + + // audio was switched on, open file to save PCM stream + if (fw) fclose(fw); + fw = fopen(fn, "wb"); + if (!fw) printf("cannot open pcm file:%s\n", fn); + else printf("AUDIO RECORDING:%s ...\n Speak, then switch off\n", fn); + } + + if (VoiceAudioMode == VOICEMODE_OFF) + { + // audio was switched off, stop recording + if (fw) + { + fclose(fw); + printf("AUDIO RECORDING off\n"); + } + fw = NULL; + } + old_VoiceAudioMode = VoiceAudioMode; + } + + if (fw) + { + for (int n = 0; n < anz; n++) + { + float f = fa[n]; + if (f > 1) f = 1; + if (f < -1) f = -1; + int16_t sh = (int16_t)(f * 32768.0f); + fwrite(&sh, sizeof(int16_t), 1, fw); + } + } +} diff --git a/hsmodem/volume.cpp b/hsmodem/volume.cpp new file mode 100755 index 0000000..5262822 --- /dev/null +++ b/hsmodem/volume.cpp @@ -0,0 +1,52 @@ +/* +* High Speed modem to transfer data in a 2,7kHz SSB channel +* ========================================================= +* Author: DJ0ABR +* +* (c) DJ0ABR +* www.dj0abr.de +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* volume.cpp ... volume setting for captue and playback +* +*/ + +#include "hsmodem.h" + +float pbvol = 1.0f; +float capvol = 1.0f; +float lsvol = 1.0f; +float micvol = 1.0f; + +void io_setPBvolume(int v) +{ + pbvol = (float)v * 2.0f / 100.0f; +} + +void io_setCAPvolume(int v) +{ + capvol = (float)v * 2.0f / 100.0f; +} + +void io_setLSvolume(int v) +{ + lsvol = (float)v * 2.0f / 100.0f; +} + +void io_setMICvolume(int v) +{ + micvol = (float)v * 2.0f / 100.0f; +} diff --git a/hsmodemLinux/audio/1200.pcm b/hsmodemLinux/audio/1200.pcm old mode 100644 new mode 100755 diff --git a/hsmodemLinux/audio/2400.pcm b/hsmodemLinux/audio/2400.pcm old mode 100644 new mode 100755 diff --git a/hsmodemLinux/audio/3000.pcm b/hsmodemLinux/audio/3000.pcm old mode 100644 new mode 100755 diff --git a/hsmodemLinux/audio/4000.pcm b/hsmodemLinux/audio/4000.pcm old mode 100644 new mode 100755 diff --git a/hsmodemLinux/audio/4410.pcm b/hsmodemLinux/audio/4410.pcm old mode 100644 new mode 100755 diff --git a/hsmodemLinux/audio/4800.pcm b/hsmodemLinux/audio/4800.pcm old mode 100644 new mode 100755 diff --git a/hsmodemLinux/audio/5500.pcm b/hsmodemLinux/audio/5500.pcm old mode 100644 new mode 100755 diff --git a/hsmodemLinux/audio/6000.pcm b/hsmodemLinux/audio/6000.pcm old mode 100644 new mode 100755 diff --git a/hsmodemLinux/audio/6600.pcm b/hsmodemLinux/audio/6600.pcm old mode 100644 new mode 100755 diff --git a/hsmodemLinux/audio/7200.pcm b/hsmodemLinux/audio/7200.pcm old mode 100644 new mode 100755 diff --git a/hsmodemLinux/audio/amsat.pcm b/hsmodemLinux/audio/amsat.pcm old mode 100644 new mode 100755 diff --git a/hsmodemLinux/audio/announcement.pcm b/hsmodemLinux/audio/announcement.pcm new file mode 100755 index 0000000..84d6293 Binary files /dev/null and b/hsmodemLinux/audio/announcement.pcm differ diff --git a/hsmodemLinux/audio/bpsk.pcm b/hsmodemLinux/audio/bpsk.pcm old mode 100644 new mode 100755 diff --git a/hsmodemLinux/audio/kbps.pcm b/hsmodemLinux/audio/kbps.pcm old mode 100644 new mode 100755 diff --git a/hsmodemLinux/audio/psk8.pcm b/hsmodemLinux/audio/psk8.pcm old mode 100644 new mode 100755 diff --git a/hsmodemLinux/audio/qpsk.pcm b/hsmodemLinux/audio/qpsk.pcm old mode 100644 new mode 100755 diff --git a/hsmodemLinux/hsmodem b/hsmodemLinux/hsmodem index 30eddd7..8ef8553 100755 Binary files a/hsmodemLinux/hsmodem and b/hsmodemLinux/hsmodem differ diff --git a/hsmodemLinux/oscardata.exe b/hsmodemLinux/oscardata.exe index a83dfd7..817a6a8 100755 Binary files a/hsmodemLinux/oscardata.exe and b/hsmodemLinux/oscardata.exe differ diff --git a/hsmodemLinux/wav2pcm.py b/hsmodemLinux/wav2pcm.py old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Form1.Designer.cs b/oscardata/oscardata/Form1.Designer.cs index d05166b..fe6340f 100755 --- a/oscardata/oscardata/Form1.Designer.cs +++ b/oscardata/oscardata/Form1.Designer.cs @@ -41,6 +41,7 @@ this.timer_qpsk = new System.Windows.Forms.Timer(this.components); this.panel_txspectrum = new System.Windows.Forms.Panel(); this.tabPage_ber = new System.Windows.Forms.TabPage(); + this.button6 = new System.Windows.Forms.Button(); this.bt_allf = new System.Windows.Forms.Button(); this.lb_tuningqrgs = new System.Windows.Forms.Label(); this.rtb = new System.Windows.Forms.RichTextBox(); @@ -103,6 +104,8 @@ this.tb_rtty_TX = new System.Windows.Forms.TextBox(); this.tb_rtty_RX = new System.Windows.Forms.TextBox(); this.panel1 = new System.Windows.Forms.Panel(); + this.textBox6 = new System.Windows.Forms.TextBox(); + this.cb_rx_autosync = new System.Windows.Forms.CheckBox(); this.rb_rtty_real = new System.Windows.Forms.RadioButton(); this.bt_rtty_text6 = new System.Windows.Forms.Button(); this.bt_rtty_text5 = new System.Windows.Forms.Button(); @@ -152,7 +155,8 @@ this.pictureBox3 = new System.Windows.Forms.PictureBox(); this.bt_tune_plus = new System.Windows.Forms.Button(); this.bt_tune_minus = new System.Windows.Forms.Button(); - this.cb_marker = new System.Windows.Forms.CheckBox(); + this.vu_cap = new oscardata.KmProgressBar(); + this.vu_pb = new oscardata.KmProgressBar(); this.pb_audioCAPstatus = new System.Windows.Forms.PictureBox(); this.pb_audioPBstatus = new System.Windows.Forms.PictureBox(); this.label6 = new System.Windows.Forms.Label(); @@ -193,14 +197,10 @@ this.lb_rxsignal = new System.Windows.Forms.Label(); this.lb_rxsync = new System.Windows.Forms.Label(); this.pn1 = new System.Windows.Forms.Panel(); - this.pb_rxsignal = new System.Windows.Forms.PictureBox(); - this.pb_rxsync = new System.Windows.Forms.PictureBox(); - this.cb_rx_autosync = new System.Windows.Forms.CheckBox(); - this.textBox6 = new System.Windows.Forms.TextBox(); this.progressBar_fifo = new oscardata.KmProgressBar(); + this.pb_rxsignal = new System.Windows.Forms.PictureBox(); this.progressBar_capfifo = new oscardata.KmProgressBar(); - this.vu_cap = new oscardata.KmProgressBar(); - this.vu_pb = new oscardata.KmProgressBar(); + this.pb_rxsync = new System.Windows.Forms.PictureBox(); this.statusStrip1.SuspendLayout(); this.tabPage_ber.SuspendLayout(); this.tabPage_image.SuspendLayout(); @@ -314,6 +314,7 @@ // tabPage_ber // this.tabPage_ber.BackColor = System.Drawing.Color.Transparent; + this.tabPage_ber.Controls.Add(this.button6); this.tabPage_ber.Controls.Add(this.bt_allf); this.tabPage_ber.Controls.Add(this.lb_tuningqrgs); this.tabPage_ber.Controls.Add(this.rtb); @@ -327,6 +328,16 @@ this.tabPage_ber.TabIndex = 0; this.tabPage_ber.Text = "BER Test"; // + // button6 + // + this.button6.Location = new System.Drawing.Point(638, 18); + this.button6.Name = "button6"; + this.button6.Size = new System.Drawing.Size(145, 23); + this.button6.TabIndex = 26; + this.button6.Text = "100..2900 (200Hz steps)"; + this.button6.UseVisualStyleBackColor = true; + this.button6.Click += new System.EventHandler(this.button6_Click); + // // bt_allf // this.bt_allf.Location = new System.Drawing.Point(561, 18); @@ -1116,6 +1127,29 @@ this.panel1.Size = new System.Drawing.Size(564, 514); this.panel1.TabIndex = 31; // + // textBox6 + // + this.textBox6.BackColor = System.Drawing.SystemColors.Control; + this.textBox6.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.textBox6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F); + this.textBox6.Location = new System.Drawing.Point(195, 167); + this.textBox6.Multiline = true; + this.textBox6.Name = "textBox6"; + this.textBox6.ReadOnly = true; + this.textBox6.Size = new System.Drawing.Size(93, 35); + this.textBox6.TabIndex = 35; + this.textBox6.Text = "or double click in spectrum"; + // + // cb_rx_autosync + // + this.cb_rx_autosync.AutoSize = true; + this.cb_rx_autosync.Location = new System.Drawing.Point(195, 146); + this.cb_rx_autosync.Name = "cb_rx_autosync"; + this.cb_rx_autosync.Size = new System.Drawing.Size(93, 17); + this.cb_rx_autosync.TabIndex = 34; + this.cb_rx_autosync.Text = "RX Auto Sync"; + this.cb_rx_autosync.UseVisualStyleBackColor = true; + // // rb_rtty_real // this.rb_rtty_real.AutoSize = true; @@ -1650,7 +1684,6 @@ this.groupBox3.Controls.Add(this.pictureBox3); this.groupBox3.Controls.Add(this.bt_tune_plus); this.groupBox3.Controls.Add(this.bt_tune_minus); - this.groupBox3.Controls.Add(this.cb_marker); this.groupBox3.Controls.Add(this.vu_cap); this.groupBox3.Controls.Add(this.vu_pb); this.groupBox3.Controls.Add(this.pb_audioCAPstatus); @@ -1712,20 +1745,19 @@ this.bt_tune_minus.Visible = false; this.bt_tune_minus.Click += new System.EventHandler(this.bt_tune_minus_Click); // - // cb_marker + // vu_cap // - this.cb_marker.AutoSize = true; - this.cb_marker.Checked = true; - this.cb_marker.CheckState = System.Windows.Forms.CheckState.Checked; - this.cb_marker.Location = new System.Drawing.Point(605, 23); - this.cb_marker.Name = "cb_marker"; - this.cb_marker.RightToLeft = System.Windows.Forms.RightToLeft.No; - this.cb_marker.Size = new System.Drawing.Size(129, 17); - this.cb_marker.TabIndex = 27; - this.cb_marker.Text = "100Hz Tuning Marker"; - this.cb_marker.UseVisualStyleBackColor = true; - this.cb_marker.Visible = false; - this.cb_marker.CheckedChanged += new System.EventHandler(this.cb_marker_CheckedChanged); + this.vu_cap.Location = new System.Drawing.Point(479, 111); + this.vu_cap.Name = "vu_cap"; + this.vu_cap.Size = new System.Drawing.Size(100, 10); + this.vu_cap.TabIndex = 20; + // + // vu_pb + // + this.vu_pb.Location = new System.Drawing.Point(479, 47); + this.vu_pb.Name = "vu_pb"; + this.vu_pb.Size = new System.Drawing.Size(100, 10); + this.vu_pb.TabIndex = 19; // // pb_audioCAPstatus // @@ -2181,49 +2213,6 @@ this.pn1.Size = new System.Drawing.Size(538, 74); this.pn1.TabIndex = 30; // - // pb_rxsignal - // - this.pb_rxsignal.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("pb_rxsignal.BackgroundImage"))); - this.pb_rxsignal.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; - this.pb_rxsignal.Location = new System.Drawing.Point(511, 47); - this.pb_rxsignal.Name = "pb_rxsignal"; - this.pb_rxsignal.Size = new System.Drawing.Size(24, 24); - this.pb_rxsignal.TabIndex = 17; - this.pb_rxsignal.TabStop = false; - // - // pb_rxsync - // - this.pb_rxsync.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("pb_rxsync.BackgroundImage"))); - this.pb_rxsync.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; - this.pb_rxsync.Location = new System.Drawing.Point(511, 2); - this.pb_rxsync.Name = "pb_rxsync"; - this.pb_rxsync.Size = new System.Drawing.Size(24, 24); - this.pb_rxsync.TabIndex = 19; - this.pb_rxsync.TabStop = false; - // - // cb_rx_autosync - // - this.cb_rx_autosync.AutoSize = true; - this.cb_rx_autosync.Location = new System.Drawing.Point(195, 146); - this.cb_rx_autosync.Name = "cb_rx_autosync"; - this.cb_rx_autosync.Size = new System.Drawing.Size(93, 17); - this.cb_rx_autosync.TabIndex = 34; - this.cb_rx_autosync.Text = "RX Auto Sync"; - this.cb_rx_autosync.UseVisualStyleBackColor = true; - // - // textBox6 - // - this.textBox6.BackColor = System.Drawing.SystemColors.Control; - this.textBox6.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.textBox6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F); - this.textBox6.Location = new System.Drawing.Point(195, 167); - this.textBox6.Multiline = true; - this.textBox6.Name = "textBox6"; - this.textBox6.ReadOnly = true; - this.textBox6.Size = new System.Drawing.Size(93, 35); - this.textBox6.TabIndex = 35; - this.textBox6.Text = "or double click in spectrum"; - // // progressBar_fifo // this.progressBar_fifo.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(128)))), ((int)(((byte)(255)))), ((int)(((byte)(255))))); @@ -2235,6 +2224,16 @@ this.progressBar_fifo.Style = System.Windows.Forms.ProgressBarStyle.Continuous; this.progressBar_fifo.TabIndex = 13; // + // pb_rxsignal + // + this.pb_rxsignal.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("pb_rxsignal.BackgroundImage"))); + this.pb_rxsignal.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.pb_rxsignal.Location = new System.Drawing.Point(511, 47); + this.pb_rxsignal.Name = "pb_rxsignal"; + this.pb_rxsignal.Size = new System.Drawing.Size(24, 24); + this.pb_rxsignal.TabIndex = 17; + this.pb_rxsignal.TabStop = false; + // // progressBar_capfifo // this.progressBar_capfifo.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(192)))), ((int)(((byte)(128))))); @@ -2245,19 +2244,15 @@ this.progressBar_capfifo.Style = System.Windows.Forms.ProgressBarStyle.Continuous; this.progressBar_capfifo.TabIndex = 15; // - // vu_cap + // pb_rxsync // - this.vu_cap.Location = new System.Drawing.Point(479, 111); - this.vu_cap.Name = "vu_cap"; - this.vu_cap.Size = new System.Drawing.Size(100, 10); - this.vu_cap.TabIndex = 20; - // - // vu_pb - // - this.vu_pb.Location = new System.Drawing.Point(479, 47); - this.vu_pb.Name = "vu_pb"; - this.vu_pb.Size = new System.Drawing.Size(100, 10); - this.vu_pb.TabIndex = 19; + this.pb_rxsync.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("pb_rxsync.BackgroundImage"))); + this.pb_rxsync.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.pb_rxsync.Location = new System.Drawing.Point(511, 2); + this.pb_rxsync.Name = "pb_rxsync"; + this.pb_rxsync.Size = new System.Drawing.Size(24, 24); + this.pb_rxsync.TabIndex = 19; + this.pb_rxsync.TabStop = false; // // Form1 // @@ -2273,7 +2268,7 @@ this.ForeColor = System.Drawing.SystemColors.ControlText; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Name = "Form1"; - this.Text = "AMSAT-DL Multimedia HS Modem V0.64 by DJ0ABR"; + this.Text = "AMSAT-DL Multimedia HS Modem V0.71 by DJ0ABR"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing); this.statusStrip1.ResumeLayout(false); this.statusStrip1.PerformLayout(); @@ -2450,7 +2445,6 @@ private System.Windows.Forms.Label lb_rec; private System.Windows.Forms.Label lb_tuningqrgs; private System.Windows.Forms.Button bt_allf; - private System.Windows.Forms.CheckBox cb_marker; private System.Windows.Forms.PictureBox pictureBox3; private System.Windows.Forms.PictureBox pictureBox4; private System.Windows.Forms.Button bt_tune_minus; @@ -2503,6 +2497,7 @@ private System.Windows.Forms.RadioButton rb_rtty_real; private System.Windows.Forms.CheckBox cb_rx_autosync; private System.Windows.Forms.TextBox textBox6; + private System.Windows.Forms.Button button6; } } diff --git a/oscardata/oscardata/Form1.cs b/oscardata/oscardata/Form1.cs index 8ff712e..918d53d 100755 --- a/oscardata/oscardata/Form1.cs +++ b/oscardata/oscardata/Form1.cs @@ -57,8 +57,6 @@ namespace oscardata // init GUI InitializeComponent(); - //showSSTV(); - // needed for ARM mono, which cannot load a picbox directly from file var bmp = new Bitmap(Resources.hintergrundxcf); pictureBox_rximage.BackgroundImage = bmp; @@ -222,8 +220,8 @@ namespace oscardata { if (s.Length > 1) { - cb_audioPB.Items.Add(s); - cb_loudspeaker.Items.Add(s); + cb_audioPB.Items.Add(s.Substring(1)); + cb_loudspeaker.Items.Add(s.Substring(1)); } } cb_loudspeaker.EndUpdate(); @@ -240,8 +238,8 @@ namespace oscardata { if (s.Length > 1) { - cb_audioCAP.Items.Add(s); - cb_mic.Items.Add(s); + cb_audioCAP.Items.Add(s.Substring(1)); + cb_mic.Items.Add(s.Substring(1)); } } cb_mic.EndUpdate(); @@ -592,7 +590,7 @@ namespace oscardata if (statics.CAPfifousage < progressBar_capfifo.Minimum) progressBar_capfifo.Value = progressBar_capfifo.Minimum; else if (statics.CAPfifousage >= progressBar_capfifo.Maximum) progressBar_capfifo.Value = progressBar_capfifo.Maximum - 1; else progressBar_capfifo.Value = statics.CAPfifousage; - if (statics.CAPfifousage > 50) progressBar_capfifo.ForeColor = Color.Red; else progressBar_capfifo.ForeColor = Color.Green; + if (statics.CAPfifousage > 80) progressBar_capfifo.ForeColor = Color.Red; else progressBar_capfifo.ForeColor = Color.Green; // Show RX Status LEDs if (statics.RXlevelDetected == 1 || statics.RXinSync == 1) @@ -621,7 +619,7 @@ namespace oscardata if (vl < 20 || vl > 85) vu_cap.ForeColor = Color.Red; else - vu_cap.ForeColor = Color.Yellow; + vu_cap.ForeColor = Color.LightGreen; addf = 80; vl = ((double)statics.maxTXlevel / addf) + Math.Log10((double)statics.maxTXlevel + factor); @@ -633,7 +631,7 @@ namespace oscardata if(vl <20 || vl > 85) vu_pb.ForeColor = Color.Red; else - vu_pb.ForeColor = Color.Yellow; + vu_pb.ForeColor = Color.LightGreen; if (recordStatus == 1) { @@ -744,13 +742,20 @@ namespace oscardata Font fnt = new Font("Verdana", 8.0f); Font smallfnt = new Font("Verdana", 6.0f); + Bitmap lastbm = null; private void panel_txspectrum_Paint(object sender, PaintEventArgs e) { Bitmap bm = Udp.UdpFftBitmap(); if (bm != null) { + try { if (lastbm != null) lastbm.Dispose(); } catch { } e.Graphics.DrawImage(bm, 0, 0); - bm.Dispose(); + lastbm = bm; + } + else + { + if(lastbm != null) + e.Graphics.DrawImage(lastbm, 0, 0); } return; } @@ -1106,6 +1111,7 @@ namespace oscardata //txcommand = statics.noTX; // finished label_rximage.ForeColor = Color.Black; pictureBox_rximage.Image = null; + cb_loop.Checked = false; ArraySend.stopSending(); bt_resetmodem_Click(null, null); } @@ -1550,7 +1556,6 @@ namespace oscardata bt_resetmodem.Visible = true; bt_tune_minus.Visible = true; bt_tune_plus.Visible = true; - cb_marker.Visible = true; } } @@ -1983,8 +1988,7 @@ namespace oscardata lb_rxsync.Text = "RX Sync:"; cb_sendIntro.Text = "send introduction before TX"; tb_recintro.Text = "record introduction"; - lb_tuningqrgs.Text = "Send Mid-Frequency:"; - cb_marker.Text = "2.9kHz Tuning Marker"; + lb_tuningqrgs.Text = "Send Marker Frequency:"; label13.Text = "Data Security:"; textBox5.Text = "Click on Callsign or Name in RX window"; textBox2.Text = @"Special Markers: @@ -2063,8 +2067,7 @@ namespace oscardata tb_shutdown.Text = "Vor dem Ausschalten eines SBC diesen hier herunterfahren"; cb_sendIntro.Text = "sende Vorstellung vor TX"; tb_recintro.Text = "Vorstellung aufnehmen"; - lb_tuningqrgs.Text = "Sende Mittenfrequenz:"; - cb_marker.Text = "2,9kHz Tuning Markierung"; + lb_tuningqrgs.Text = "Sende Frequenzmarkierung:"; label13.Text = "Datensicherheit:"; textBox5.Text = "Klicke auf Rufzeichen und Namen im RX Fenster"; textBox2.Text = @"Spezialzeichen: @@ -2225,15 +2228,12 @@ namespace oscardata private void bt_allf_Click(object sender, EventArgs e) { - bt_tuning(5); + bt_tuning(7); } - private void cb_marker_CheckedChanged(object sender, EventArgs e) + private void button6_Click(object sender, EventArgs e) { - Byte[] txdata = new byte[2]; - txdata[0] = statics.marker; - txdata[1] = (Byte)(cb_marker.Checked ? 1 : 0); - Udp.UdpSendCtrl(txdata); + bt_tuning(1); } private void bt_tune_minus_Click(object sender, EventArgs e) diff --git a/oscardata/oscardata/Form1.resx b/oscardata/oscardata/Form1.resx index 242b4af..5bd45d7 100755 --- a/oscardata/oscardata/Form1.resx +++ b/oscardata/oscardata/Form1.resx @@ -137,7 +137,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAA+ - JQAAAk1TRnQBSQFMAgEBFwEAAYgBDAGIAQwBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + JQAAAk1TRnQBSQFMAgEBFwEAAaABDAGgAQwBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo AwABQAMAAWADAAEBAQABCAYAARgYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5 AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA diff --git a/oscardata/oscardata/Properties/Actions-go-next-view-icon.png b/oscardata/oscardata/Properties/Actions-go-next-view-icon.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/Fernschreiben für Funkamateure.pdf b/oscardata/oscardata/Properties/Fernschreiben für Funkamateure.pdf old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/Lehrstuhlseminar_BenjaminKoch.pdf b/oscardata/oscardata/Properties/Lehrstuhlseminar_BenjaminKoch.pdf old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/about.png b/oscardata/oscardata/Properties/about.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/amsat_dl_logo.png b/oscardata/oscardata/Properties/amsat_dl_logo.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/answer.png b/oscardata/oscardata/Properties/answer.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/binary.png b/oscardata/oscardata/Properties/binary.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/cancel.png b/oscardata/oscardata/Properties/cancel.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/captureicon.png b/oscardata/oscardata/Properties/captureicon.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/cdc_codecloop.png b/oscardata/oscardata/Properties/cdc_codecloop.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/cdc_dig.png b/oscardata/oscardata/Properties/cdc_dig.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/cdc_digital.png b/oscardata/oscardata/Properties/cdc_digital.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/cdc_dv.png b/oscardata/oscardata/Properties/cdc_dv.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/cdc_dvrx.png b/oscardata/oscardata/Properties/cdc_dvrx.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/cdc_intloop.png b/oscardata/oscardata/Properties/cdc_intloop.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/clearscreen.png b/oscardata/oscardata/Properties/clearscreen.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/endqso-icon.png b/oscardata/oscardata/Properties/endqso-icon.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/fail.png b/oscardata/oscardata/Properties/fail.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/foht.png b/oscardata/oscardata/Properties/foht.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/greenmarker.png b/oscardata/oscardata/Properties/greenmarker.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/hintergrundxcf.png b/oscardata/oscardata/Properties/hintergrundxcf.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/home-icon.png b/oscardata/oscardata/Properties/home-icon.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/html.png b/oscardata/oscardata/Properties/html.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/image.jpg b/oscardata/oscardata/Properties/image.jpg old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/image.png b/oscardata/oscardata/Properties/image.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/image1.jpg b/oscardata/oscardata/Properties/image1.jpg old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/meter.png b/oscardata/oscardata/Properties/meter.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/meter_big.png b/oscardata/oscardata/Properties/meter_big.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/ok.png b/oscardata/oscardata/Properties/ok.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/openfolder.png b/oscardata/oscardata/Properties/openfolder.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/osci.png b/oscardata/oscardata/Properties/osci.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/playback.png b/oscardata/oscardata/Properties/playback.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/playbackicon.png b/oscardata/oscardata/Properties/playbackicon.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/rahmen.png b/oscardata/oscardata/Properties/rahmen.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/record.png b/oscardata/oscardata/Properties/record.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/redmarker.png b/oscardata/oscardata/Properties/redmarker.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/rtty.png b/oscardata/oscardata/Properties/rtty.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/ryry-icon.png b/oscardata/oscardata/Properties/ryry-icon.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/schraube.png b/oscardata/oscardata/Properties/schraube.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/screen.png b/oscardata/oscardata/Properties/screen.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/setup.png b/oscardata/oscardata/Properties/setup.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/setup_big.png b/oscardata/oscardata/Properties/setup_big.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/start-icon.png b/oscardata/oscardata/Properties/start-icon.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/start.png b/oscardata/oscardata/Properties/start.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/stop-icon.png b/oscardata/oscardata/Properties/stop-icon.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/stop.png b/oscardata/oscardata/Properties/stop.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/stopplay.png b/oscardata/oscardata/Properties/stopplay.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/text-icon.png b/oscardata/oscardata/Properties/text-icon.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/text.png b/oscardata/oscardata/Properties/text.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/text_big.png b/oscardata/oscardata/Properties/text_big.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/textrx.png b/oscardata/oscardata/Properties/textrx.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/texttx.png b/oscardata/oscardata/Properties/texttx.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/transmit.png b/oscardata/oscardata/Properties/transmit.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/uart_design_doc.pdf b/oscardata/oscardata/Properties/uart_design_doc.pdf old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/user-icon.png b/oscardata/oscardata/Properties/user-icon.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/voice-icon.png b/oscardata/oscardata/Properties/voice-icon.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/voice.png b/oscardata/oscardata/Properties/voice.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/Properties/voice_big.png b/oscardata/oscardata/Properties/voice_big.png old mode 100644 new mode 100755 diff --git a/oscardata/oscardata/bin/Release/oscardata.exe b/oscardata/oscardata/bin/Release/oscardata.exe index a83dfd7..817a6a8 100755 Binary files a/oscardata/oscardata/bin/Release/oscardata.exe and b/oscardata/oscardata/bin/Release/oscardata.exe differ diff --git a/oscardata/oscardata/udp.cs b/oscardata/oscardata/udp.cs index 4c05374..87ac1db 100755 --- a/oscardata/oscardata/udp.cs +++ b/oscardata/oscardata/udp.cs @@ -122,11 +122,9 @@ namespace oscardata statics.ModemIP = ModIP; searchtimeout = 0; // message b contains audio devices and init status - statics.initAudioStatus = b[0]; - statics.initVoiceStatus = b[1]; - //String s = statics.ByteArrayToString(b,2); - String s = statics.ByteArrayToStringUtf8(b, 2); + //String s = statics.ByteArrayToString(b,0); + String s = statics.ByteArrayToStringUtf8(b, 0); String[] sa1 = s.Split(new char[] { '^' }); statics.AudioPBdevs = sa1[0].Split(new char[] { '~' }); statics.AudioCAPdevs = sa1[1].Split(new char[] { '~' }); @@ -329,6 +327,7 @@ namespace oscardata // bitmap for drawing the complete picture bm = new Bitmap(442, 76); + int rshift = 14; using (Graphics gr = Graphics.FromImage(bm)) { // background @@ -341,8 +340,8 @@ namespace oscardata // RTTY Markers int low = (statics.tune_frequency - 170 / 2)/10; int high = (statics.tune_frequency + 170 / 2)/10; - gr.DrawLine(pengn, low + 16, yl, low + 16, yh + 3); - gr.DrawLine(pengn, high + 16, yl, high + 16, yh + 3); + gr.DrawLine(pengn, low + rshift, yl, low + rshift, yh + 3); + gr.DrawLine(pengn, high + rshift, yl, high + rshift, yh + 3); } /* // screws at the 4 corners @@ -366,7 +365,7 @@ namespace oscardata us = (int)(fus - 5.0); if(lastus != -1 && i>0) - gr.DrawLine(penyl, i/2+15, 76-lastus, i/2+1+15, 76-us); // 15 istead of 16 to get it in exact position + gr.DrawLine(penyl, i/2+ rshift, 76-lastus, i/2+1+ rshift, 76-us); // 15 istead of 16 to get it in exact position lastus = us; } } @@ -376,6 +375,7 @@ namespace oscardata // Udp TX Loop runs in its own thread static void Udptxloop() { + DateTime dt = DateTime.UtcNow; UdpClient udpc = new UdpClient(); while (statics.running) @@ -392,11 +392,19 @@ namespace oscardata if(statics.PBfifousage < 3) { // we need to send more payload data - if (uq_tx.Count() > 0) + // but never send faster than 1000 Byte/s + // because statics.PBfifousage may be updated too slow + //DateTime dtact = DateTime.UtcNow; + //TimeSpan ts = dtact - dt; + //if (ts.TotalMilliseconds > statics.UdpBlocklen) { - Byte[] b = uq_tx.Getarr(); - udpc.Send(b, b.Length, statics.ModemIP, statics.UdpTXport); - wait = false; + if (uq_tx.Count() > 0) + { + Byte[] b = uq_tx.Getarr(); + udpc.Send(b, b.Length, statics.ModemIP, statics.UdpTXport); + wait = false; + //dt = dtact; + } } } if (wait) Thread.Sleep(1); @@ -440,25 +448,6 @@ namespace oscardata return uq_rx.Getarr(); } - public static UInt16[] UdpGetFFT() - { - if (uq_fft.Count() == 0) return null; - - Byte[] d = uq_fft.Getarr(); - UInt16[] varr = new UInt16[d.Length / 2]; - int j = 0; - for (int i = 0; i < d.Length; i += 2) - { - if ((i + 1) >= d.Length) break; - UInt16 us = d[i]; - us <<= 8; - us += d[i + 1]; - if (j >= (varr.Length)) break; - varr[j++] = us; - } - return varr; - } - public static qpskitem UdpGetIQ() { if (uq_iq.Count() == 0) return null;