diff --git a/.travis.yml b/.travis.yml index d81eecc..3bc5da6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: cpp -dist: trusty +dist: bionic sudo: false compiler: @@ -14,6 +14,7 @@ addons: - libmbe-dev - libsndfile1-dev - libitpp-dev + - librtlsdr-dev - portaudio19-dev before_script: diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c1663c..cdf3e4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,9 +10,12 @@ find_package(LibSndFile REQUIRED) find_package(MBE REQUIRED) find_package(ITPP REQUIRED) find_package(LibPortAudio) +find_package(RTLSDR) +#more fucking around +find_package(Curses REQUIRED) -include_directories(SYSTEM ${LIBSNDFILE_INCLUDE_DIR} ${MBE_INCLUDE_DIR} ${ITPP_INCLUDE_DIR}) -set(LIBS ${MBE_LIBRARY} ${LIBSNDFILE_LIBRARY} ${ITPP_LIBRARY}) +include_directories(SYSTEM ${LIBSNDFILE_INCLUDE_DIR} ${MBE_INCLUDE_DIR} ${ITPP_INCLUDE_DIR} ${CURSES_INCLUDE_DIR}) +set(LIBS ${MBE_LIBRARY} ${LIBSNDFILE_LIBRARY} ${ITPP_LIBRARY} ${CURSES_LIBRARY}) if(PORTAUDIO_FOUND) include_directories(SYSTEM ${PORTAUDIO_INCLUDE_DIRS}) @@ -20,9 +23,20 @@ if(PORTAUDIO_FOUND) add_definitions(-DUSE_PORTAUDIO) endif(PORTAUDIO_FOUND) +if(RTLSDR_FOUND) + find_package(Threads) + include_directories(SYSTEM ${RTLSDR_INCLUDE_DIRS}) + list(APPEND LIBS ${RTLSDR_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + add_definitions(-DUSE_RTLSDR) +endif(RTLSDR_FOUND) + FILE(GLOB SRCS src/*.c src/*.cpp) FILE(GLOB HEADERS include/*.h include/*.hpp) +if(NOT RTLSDR_FOUND) + list(REMOVE_ITEM SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/rtl_sdr_fm.cpp) +endif(NOT RTLSDR_FOUND) + configure_file("src/git_ver.c.in" "${CMAKE_CURRENT_BINARY_DIR}/git_ver.c" @ONLY) list(APPEND SRCS "${CMAKE_CURRENT_BINARY_DIR}/git_ver.c") diff --git a/README.md b/README.md index ea0d4e9..2b224d5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,13 @@ [![Build Status](https://travis-ci.org/szechyjs/dsd.svg?branch=master)](https://travis-ci.org/szechyjs/dsd) -# Digital Speech Decoder 1.7.0-dev +# Digital Speech Decoder - Florida Man Edition +This version of DSD shouldn't be used by anybody wanting a stable DSD experience, please see the original version for that. +Also, please don't submit bug reports to the links on this page, those will direct them to the original version. Links will be updated at a later date. + +The purpose of this is to experiment with the RTL code and other things, and is purely for academic research into the inner workings of DSD. Basically, its for me to poke around with and see what I can manage to break. + + +## DSD is able to decode [several digital voice formats](https://github.com/szechyjs/dsd/wiki/Supported-formats) from discriminator tap audio and synthesize the decoded speech. Speech synthesis requires mbelib, which is a separate package. diff --git a/cmake/FindCURSES.cmake b/cmake/FindCURSES.cmake new file mode 100644 index 0000000..29818ba --- /dev/null +++ b/cmake/FindCURSES.cmake @@ -0,0 +1,16 @@ +# - Try to find NCURSES +# Once done this will define +# +# ITPP_FOUND - System has ITPP +# ITPP_INCLUDE_DIR - The ITPP include directory +# ITPP_LIBRARY - The library needed to use ITPP +# +set(CURSES_LIBRARY "/opt/lib/libncurses.so") +set(CURSES_INCLUDE_PATH "/opt/include") +find_path(CURSES_INCLUDE_DIR /usr/ncurses.h) + +set(CURSES_NAMES ${CURSES_NAMES} curses.h curses curses5w curses6w curses6 curses5 libncursesw) +FIND_LIBRARY(CURSES_LIBRARY NAMES ${CURSES_NAMES}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(CURSES DEFAULT_MSG NCURSES_LIBRARY CURSES_INCLUDE_DIR) diff --git a/cmake/FindLibSndFile.cmake b/cmake/FindLibSndFile.cmake index b12b358..d976921 100644 --- a/cmake/FindLibSndFile.cmake +++ b/cmake/FindLibSndFile.cmake @@ -12,5 +12,5 @@ SET(LIBSNDFILE_NAMES ${LIBSNDFILE_NAMES} sndfile libsndfile) FIND_LIBRARY(LIBSNDFILE_LIBRARY NAMES ${LIBSNDFILE_NAMES}) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(LIBSNDFILE DEFAULT_MSG LIBSNDFILE_LIBRARY - LIBSNDFILE_INCLUDE_DIR) \ No newline at end of file +find_package_handle_standard_args(LibSndFile DEFAULT_MSG LIBSNDFILE_LIBRARY + LIBSNDFILE_INCLUDE_DIR) diff --git a/cmake/FindRTLSDR.cmake b/cmake/FindRTLSDR.cmake new file mode 100644 index 0000000..8c18b49 --- /dev/null +++ b/cmake/FindRTLSDR.cmake @@ -0,0 +1,29 @@ +# - Try to find rtlsdr - the hardware driver for the realtek chip in the dvb receivers +# Once done this will define +# RTLSDR_FOUND - System has rtlsdr +# RTLSDR_LIBRARIES - The rtlsdr libraries +# RTLSDR_INCLUDE_DIRS - The rtlsdr include directories +# RTLSDR_LIB_DIRS - The rtlsdr library directories + +if (NOT RTLSDR_FOUND) + + find_package(PkgConfig) + pkg_check_modules(RTLSDR_PKG librtlsdr) + set(RTLSDR_DEFINITIONS ${PC_RTLSDR_CFLAGS_OTHER}) + + find_path(RTLSDR_INCLUDE_DIR + NAMES rtl-sdr.h + HINTS ${RTLSDR_PKG_INCLUDE_DIRS}) + + find_library(RTLSDR_LIBRARY + NAMES rtlsdr + HINTS ${RTLSDR_PKG_LIBRARY_DIRS}) + + set(RTLSDR_LIBRARIES ${RTLSDR_LIBRARY}) + set(RTLSDR_INCLUDE_DIRS ${RTLSDR_INCLUDE_DIR}) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(RTLSDR DEFAULT_MSG RTLSDR_LIBRARY RTLSDR_INCLUDE_DIR) + mark_as_advanced(RTLSDR_INCLUDE_DIR RTLSDR_LIBRARY) + +endif (NOT RTLSDR_FOUND) diff --git a/debian/control b/debian/control index 58239bc..0479cf4 100644 --- a/debian/control +++ b/debian/control @@ -2,8 +2,8 @@ Source: dsd Section: hamradio Priority: optional Maintainer: Jared Szechy -Build-Depends: debhelper (>= 9), libmbe-dev, libitpp-dev, libsndfile1-dev, - portaudio19-dev, cmake, git, help2man +Build-Depends: debhelper (>= 9), libmbe-dev, libitpp-dev, librtlsdr-dev + libsndfile1-dev, portaudio19-dev, cmake, git, help2man Standards-Version: 4.1.4 Homepage: https://github.com/szechyjs/dsd Vcs-Git: https://github.com/szechyjs/dsd.git @@ -17,4 +17,4 @@ Pre-Depends: ${misc:Pre-Depends} Depends: ${shlibs:Depends}, ${misc:Depends} Description: Digital Speech Decoder DSD is able to decode several digital voice formats from discriminator tap - audio and synthesize the decoded speech. \ No newline at end of file + audio and synthesize the decoded speech. diff --git a/debian/copyright b/debian/copyright index 898cf45..46dadab 100644 --- a/debian/copyright +++ b/debian/copyright @@ -20,6 +20,14 @@ Files: src/dstar_header.c include/dstar_header.h include/fcs.h Copyright: Copyright (C) 2010 by Kristoff Bonne, ON1ARF License: GPL-2 +Files: src/rtl_sdr_fm.cpp +Copyright: Copyright (C) 2012 by Steve Markgraf + Copyright (C) 2012 by Hoernchen + Copyright (C) 2012 by Kyle Keen + Copyright (C) 2013 by Elias Oenal + Copyright (C) 2014 by Kyle Keen +License: GPL-2 + Files: include/descramble.h Copyright: Copyright (C) 2011 by Jonathan Naylor, G4KLX License: GPL-2 @@ -103,4 +111,4 @@ License: BSL-1.0 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. \ No newline at end of file + DEALINGS IN THE SOFTWARE. diff --git a/include/dsd.h b/include/dsd.h index 353b8de..bac0193 100644 --- a/include/dsd.h +++ b/include/dsd.h @@ -58,10 +58,16 @@ #define PA_LATENCY_OUT 0.100 #endif +#ifdef USE_RTLSDR +#include +#endif + /* * global variables */ -extern int exitflag; + +static volatile int exitflag; +//volatile int exitflag; //didn't work, couldn't build, multiple definitions of exitflag typedef struct @@ -85,7 +91,9 @@ typedef struct #ifdef USE_PORTAUDIO PaStream* audio_in_pa_stream; #endif - int audio_in_type; // 0 for device, 1 for file, 2 for portaudio + uint32_t rtlsdr_center_freq; + int rtlsdr_ppm_error; + int audio_in_type; // 0 for device, 1 for file, 2 for portaudio, 3 for rtlsdr char audio_out_dev[1024]; int audio_out_fd; SNDFILE *audio_out_file; @@ -129,6 +137,8 @@ typedef struct int delay; int use_cosine_filter; int unmute_encrypted_p25; + int rtl_dev_index; + int rtl_gain_value; } dsd_opts; typedef struct @@ -317,4 +327,15 @@ void processDSTAR_HD (dsd_opts * opts, dsd_state * state); short dmr_filter(short sample); short nxdn_filter(short sample); +#ifdef __cplusplus +extern "C" { +#endif +void open_rtlsdr_stream(dsd_opts *opts); +void cleanup_rtlsdr_stream(); +void get_rtlsdr_sample(int16_t *sample); +void rtlsdr_sighandler(); +#ifdef __cplusplus +} +#endif + #endif // DSD_H diff --git a/src/dsd_audio.c b/src/dsd_audio.c index 993add2..6c7ab9c 100644 --- a/src/dsd_audio.c +++ b/src/dsd_audio.c @@ -17,7 +17,6 @@ #include "dsd.h" - void processAudio (dsd_opts * opts, dsd_state * state) { @@ -164,7 +163,7 @@ writeSynthesizedVoice (dsd_opts * opts, dsd_state * state) // for(n=0; n<160; n++) // printf("%d ", ((short*)(state->audio_out_temp_buf))[n]); // printf("\n"); - + aout_buf_p = aout_buf; state->audio_out_temp_buf_p = state->audio_out_temp_buf; @@ -214,6 +213,84 @@ writeSynthesizedVoice (dsd_opts * opts, dsd_state * state) */ } +//Test playing raw audio HERE +//get_rtlsdr_sample(&sample); +void +playRawAudio (dsd_opts * opts, dsd_state * state) +{ + ssize_t result; + + //if (state->audio_out_idx > opts->delay) + if (opts->audio_in_type == 3) //if set to rtl_fm, maybe it shouldn't matter but for now, using this + { + // output synthesized speech to sound card + if(opts->audio_out_type == 2) + { +#ifdef USE_PORTAUDIO + PaError err = paNoError; + do + { + long available = Pa_GetStreamWriteAvailable( opts->audio_out_pa_stream ); + if(available < 0) + err = available; + //printf("Frames available: %d\n", available); + if( err != paNoError ) + break; + if(available > SAMPLE_RATE_OUT * PA_LATENCY_OUT) + { + //It looks like this might not be needed for very small latencies. However, it's definitely needed for a bit larger ones. + //When PA_LATENCY_OUT == 0.500 I get output buffer underruns if I don't use this. With PA_LATENCY_OUT <= 0.100 I don't see those happen. + //But with PA_LATENCY_OUT < 0.100 I run the risk of choppy audio and stream errors. + printf("\nSyncing voice output stream\n"); + err = Pa_StopStream( opts->audio_out_pa_stream ); + if( err != paNoError ) + break; + } + + err = Pa_IsStreamActive( opts->audio_out_pa_stream ); + if(err == 0) + { + printf("Start voice output stream\n"); + err = Pa_StartStream( opts->audio_out_pa_stream ); + } + else if(err == 1) + { + err = paNoError; + } + if( err != paNoError ) + break; + + //printf("write stream %d\n", state->audio_out_idx); + err = Pa_WriteStream( opts->audio_out_pa_stream, (state->audio_out_buf_p - state->audio_out_idx), state->audio_out_idx ); + if( err != paNoError ) + break; + } while(0); + + if( err != paNoError ) + { + fprintf( stderr, "An error occured while using the portaudio output stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + } + +#endif + } + else + result = write (opts->audio_out_fd, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2)); + state->audio_out_idx = 0; + } + + if (state->audio_out_idx2 >= 800000) + { + state->audio_out_float_buf_p = state->audio_out_float_buf + 100; + state->audio_out_buf_p = state->audio_out_buf + 100; + memset (state->audio_out_float_buf, 0, 100 * sizeof (float)); + memset (state->audio_out_buf, 0, 100 * sizeof (short)); + state->audio_out_idx2 = 0; + } +} + +//end test raw audio void playSynthesizedVoice (dsd_opts * opts, dsd_state * state) @@ -309,7 +386,7 @@ int getPADevice(char* dev, int input, PaStream** stream) printf( "ERROR: Requested device %d is larger than number of devices.\n", devnum ); return(1); } - + const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo( devnum ); /* print device name */ @@ -500,6 +577,10 @@ openAudioInDevice (dsd_opts * opts) exit(1); #endif } + else if(strncmp(opts->audio_in_dev, "rtl", 3) == 0) + { + opts->audio_in_type = 3; + } else { struct stat stat_buf; @@ -529,9 +610,9 @@ openAudioInDevice (dsd_opts * opts) #ifdef SOLARIS sample_info_t aset, aget; int rgain; - + rgain = 64; - + if (opts->split == 1) { opts->audio_in_fd = open (opts->audio_in_dev, O_RDONLY); @@ -545,10 +626,10 @@ openAudioInDevice (dsd_opts * opts) printf ("Error, couldn't open %s\n", opts->audio_in_dev); exit(1); } - + // get current ioctl (opts->audio_in_fd, AUDIO_GETINFO, &aset); - + aset.record.sample_rate = SAMPLE_RATE_IN; aset.play.sample_rate = SAMPLE_RATE_IN; aset.record.channels = 1; @@ -559,7 +640,7 @@ openAudioInDevice (dsd_opts * opts) aset.play.encoding = AUDIO_ENCODING_LINEAR; aset.record.port = AUDIO_LINE_IN; aset.record.gain = rgain; - + if (ioctl (opts->audio_in_fd, AUDIO_SETINFO, &aset) == -1) { printf ("Error setting sample device parameters\n"); @@ -569,7 +650,7 @@ openAudioInDevice (dsd_opts * opts) #if defined(BSD) && !defined(__APPLE__) int fmt; - + if (opts->split == 1) { opts->audio_in_fd = open (opts->audio_in_dev, O_RDONLY); @@ -578,13 +659,13 @@ openAudioInDevice (dsd_opts * opts) { opts->audio_in_fd = open (opts->audio_in_dev, O_RDWR); } - + if (opts->audio_in_fd == -1) { printf ("Error, couldn't open %s\n", opts->audio_in_dev); opts->audio_out = 0; } - + fmt = 0; if (ioctl (opts->audio_in_fd, SNDCTL_DSP_RESET) < 0) { diff --git a/src/dsd_file.c b/src/dsd_file.c index c805885..36f7dee 100644 --- a/src/dsd_file.c +++ b/src/dsd_file.c @@ -211,7 +211,8 @@ closeMbeOutFile (dsd_opts * opts, dsd_state * state) fclose (opts->mbe_out_f); opts->mbe_out_f = NULL; strftime (datestr, 31, "%Y-%m-%d-%H%M%S", &timep); - sprintf (newfilename, "nac%X-%s-tg%i%s", state->nac, datestr, talkgroup, ext); + //sprintf (newfilename, "nac%X-%s-tg%i%s", state->nac, datestr, talkgroup, ext); + sprintf (newfilename, "%s%s", datestr, ext); //this one sprintf (new_path, "%s%s", opts->mbe_out_dir, newfilename); #ifdef _WIN32 sprintf (shell, "move %s %s", opts->mbe_out_path, new_path); diff --git a/src/dsd_frame.c b/src/dsd_frame.c index 539c391..8234ff1 100644 --- a/src/dsd_frame.c +++ b/src/dsd_frame.c @@ -31,7 +31,7 @@ printFrameInfo (dsd_opts * opts, dsd_state * state) level = (int) state->max / 164; if (opts->verbose > 0) { - printf ("inlvl: %2i%% ", level); + //printf ("inlvl: %2i%% ", level); } if (state->nac != 0) { @@ -89,7 +89,7 @@ processFrame (dsd_opts * opts, dsd_state * state) if (opts->verbose > 0) { level = (int) state->max / 164; - printf ("inlvl: %2i%% ", level); + //printf ("inlvl: %2i%% ", level); } } state->nac = 0; @@ -112,7 +112,7 @@ processFrame (dsd_opts * opts, dsd_state * state) if (opts->verbose > 0) { level = (int) state->max / 164; - printf ("inlvl: %2i%% ", level); + //printf ("inlvl: %2i%% ", level); } } state->nac = 0; @@ -134,7 +134,7 @@ processFrame (dsd_opts * opts, dsd_state * state) if (opts->verbose > 0) { level = (int) state->max / 164; - printf ("inlvl: %2i%% ", level); + //printf ("inlvl: %2i%% ", level); } } state->nac = 0; @@ -156,7 +156,7 @@ processFrame (dsd_opts * opts, dsd_state * state) if (opts->verbose > 0) { level = (int) state->max / 164; - printf ("inlvl: %2i%% ", level); + //printf ("inlvl: %2i%% ", level); } } state->nac = 0; @@ -179,7 +179,7 @@ processFrame (dsd_opts * opts, dsd_state * state) if (opts->verbose > 0) { level = (int) state->max / 164; - printf ("inlvl: %2i%% ", level); + //printf ("inlvl: %2i%% ", level); } } if ((state->synctype == 11) || (state->synctype == 12)) @@ -233,7 +233,7 @@ processFrame (dsd_opts * opts, dsd_state * state) if (opts->verbose > 0) { level = (int) state->max / 164; - printf ("inlvl: %2i%% ", level); + //printf ("inlvl: %2i%% ", level); } } if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) diff --git a/src/dsd_frame_sync.c b/src/dsd_frame_sync.c index 1993c23..90ceb76 100644 --- a/src/dsd_frame_sync.c +++ b/src/dsd_frame_sync.c @@ -16,27 +16,73 @@ */ #include "dsd.h" +#include +#include + +time_t now; +char * getTime(void) //get pretty hh:mm:ss timestamp +{ + time_t t = time(NULL); + + char * curr; + char * stamp = asctime(localtime( & t)); + + curr = strtok(stamp, " "); + curr = strtok(NULL, " "); + curr = strtok(NULL, " "); + curr = strtok(NULL, " "); + + return curr; +} + +char * getDate(void) { + char datename[32]; + char * curr2; + struct tm * to; + time_t t; + t = time(NULL); + to = localtime( & t); + strftime(datename, sizeof(datename), "%Y-%m-%d", to); + curr2 = strtok(datename, " "); + return curr2; +} + void printFrameSync (dsd_opts * opts, dsd_state * state, char *frametype, int offset, char *modulation) { + //char datestr[32]; + //struct tm timep; + //erase(); + //attron(COLOR_PAIR(4)); + //for (short int i = 0; i < 7; i++) { + //printw("%s \n", FM_banner2[i]);} + //attroff(COLOR_PAIR(4)); if (opts->verbose > 0) { - printf ("Sync: %s ", frametype); + //strftime (datestr, 31, "%Y-%m-%d-%H%M%S", &timep); + //printf ("Sync: %s ", frametype); + //printf ("%s %s Sync: %s ", getDate(), getTime(), frametype); + printf ("%s Sync: %s ", getTime(), frametype); + printw("%s Sync: %s ", getTime(), frametype); } if (opts->verbose > 2) { printf ("o: %4i ", offset); + printw("o: %4i ", offset); } if (opts->verbose > 1) { printf ("mod: %s ", modulation); + printw("mod: %s ", modulation); } if (opts->verbose > 2) { printf ("g: %f ", state->aout_gain); + printw("g: %f ", state->aout_gain); } + refresh(); } int @@ -100,6 +146,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) if ((opts->symboltiming == 1) && (state->carrier == 1)) { printf ("\nSymbol Timing:\n"); + //printw("\nSymbol Timing:\n"); } while (sync == 0) { @@ -489,8 +536,10 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->min = ((state->min) + lmin) / 2; sprintf (state->ftype, " ProVoice "); if (opts->errorbars == 1) + //if (opts->errorbars == 1 && (time(NULL) - now) > 2 ) { printFrameSync (opts, state, " -ProVoice ", synctest_pos + 1, modulation); + now = time(NULL); } state->lastsynctype = 14; return (14); @@ -503,8 +552,10 @@ getFrameSync (dsd_opts * opts, dsd_state * state) state->min = ((state->min) + lmin) / 2; sprintf (state->ftype, " ProVoice "); if (opts->errorbars == 1) + //if (opts->errorbars == 1 && (time(NULL) - now) > 2 ) { printFrameSync (opts, state, " -ProVoice ", synctest_pos + 1, modulation); + now = time(NULL); } state->lastsynctype = 15; return (15); @@ -819,6 +870,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) if ((opts->errorbars == 1) && (opts->verbose > 1) && (state->carrier == 1)) { printf ("Sync: no sync\n"); + printf("Press CTRL + C to close.\n"); //Kindly remind user to double tap CTRL + C } noCarrier (opts, state); return (-1); diff --git a/src/dsd_main.c b/src/dsd_main.c index 02decec..05c8ce8 100644 --- a/src/dsd_main.c +++ b/src/dsd_main.c @@ -28,7 +28,18 @@ #include "p25p1_heuristics.h" #include "pa_devs.h" -int exitflag = 0; +#include +#include +char * FM_banner[9] = { + " CTRL + C twice to exit", + "██████╗ ██████╗██████╗    ███████╗███╗ ███╗███████╗", + "██╔══██╗██╔════╝██╔══██╗   ██╔════╝████╗ ████║██╔════╝", + "██║ ██║╚█████╗ ██║ ██║   █████╗ ██╔████╔██║█████╗ ", + "██║ ██║ ╚═══██╗██║ ██║   ██╔══╝ ██║╚██╔╝██║██╔══╝ ", + "██████╔╝██████╔╝██████╔╝   ██║ ██║ ╚═╝ ██║███████╗", + "╚═════╝ ╚═════╝ ╚═════╝    ╚═╝ ╚═╝ ╚═╝╚══════╝", +}; + int comp (const void *a, const void *b) @@ -80,6 +91,11 @@ noCarrier (dsd_opts * opts, dsd_state * state) sprintf (state->algid, "________"); sprintf (state->keyid, "________________"); mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced); + //test below + //uint16_t sample; + //get_rtlsdr_sample(&sample); + //Pa_StartStream(sample); + //end test } void @@ -142,6 +158,8 @@ initOpts (dsd_opts * opts) opts->delay = 0; opts->use_cosine_filter = 1; opts->unmute_encrypted_p25 = 0; + opts->rtl_dev_index = 0; //choose which device we want by index number + opts->rtl_gain_value = 0; //set actual gain and not automatic gain } void @@ -267,15 +285,21 @@ usage () printf (" -z Frame rate for datascope\n"); printf ("\n"); printf ("Input/Output options:\n"); - printf (" -i Audio input device (default is /dev/audio, - for piped stdin)\n"); + printf (" -i Audio input device (default is /dev/audio, - for piped stdin, rtl for rtl device)\n"); printf (" -o Audio output device (default is /dev/audio)\n"); printf (" -d Create mbe data files, use this directory\n"); - printf (" -r Read/Play saved mbe data from file(s)\n"); + printf (" -r Read/Play saved mbe data from file(s)\n"); printf (" -g Audio output gain (default = 0 = auto, disable = -1)\n"); printf (" -n Do not send synthesized speech to audio output device\n"); - printf (" -w Output synthesized speech to a .wav file\n"); + printf (" -w Output synthesized speech to a .wav file\n"); printf (" -a Display port audio devices\n"); printf ("\n"); + printf ("RTL-SDR options:\n"); + printf (" -c RTL-SDR tune to frequency\n"); + printf (" -P RTL-SDR ppm error (default = 0)\n"); + printf (" -D RTL-SDR Device Index Number\n"); + printf (" -G RTL-SDR Device Gain (0-49) (default = 0 Auto Gain)\n"); + printf ("\n"); printf ("Scanner control options:\n"); printf (" -B Serial port baud rate (default=115200)\n"); printf (" -C Serial port for scanner control (default=/dev/ttyUSB0)\n"); @@ -285,7 +309,7 @@ usage () printf (" -fa Auto-detect frame type (default)\n"); printf (" -f1 Decode only P25 Phase 1\n"); printf (" -fd Decode only D-STAR\n"); - printf (" -fi Decode only NXDN48* (6.25 kHz) / IDAS*\n"); + printf (" -fi Decode only NXDN48* (6.25 kHz) / IDAS*\n"); printf (" -fn Decode only NXDN96 (12.5 kHz)\n"); printf (" -fp Decode only ProVoice*\n"); printf (" -fr Decode only DMR/MOTOTRBO\n"); @@ -322,12 +346,18 @@ liveScanner (dsd_opts * opts, dsd_state * state) PaError err = Pa_StartStream( opts->audio_in_pa_stream ); if( err != paNoError ) { - fprintf( stderr, "An error occured while starting the portaudio input stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + //fprintf( stderr, "An error occured while starting the portaudio input stream\n" ); + //fprintf( stderr, "Error number: %d\n", err ); + //fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); return; } } +#endif +#ifdef USE_RTLSDR + if(opts->audio_in_type == 3) + { + open_rtlsdr_stream(opts); + } #endif while (1) { @@ -378,9 +408,9 @@ cleanupAndExit (dsd_opts * opts, dsd_state * state) err = Pa_CloseStream( opts->audio_in_pa_stream ); if( err != paNoError ) { - fprintf( stderr, "An error occured while closing the portaudio input stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + //fprintf( stderr, "An error occured while closing the portaudio input stream\n" ); + //fprintf( stderr, "Error number: %d\n", err ); + //fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); } } if(opts->audio_out_pa_stream != NULL) @@ -390,28 +420,36 @@ cleanupAndExit (dsd_opts * opts, dsd_state * state) err = Pa_StopStream( opts->audio_out_pa_stream ); if( err != paNoError ) { - fprintf( stderr, "An error occured while closing the portaudio output stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + //fprintf( stderr, "An error occured while closing the portaudio output stream\n" ); + //fprintf( stderr, "Error number: %d\n", err ); + //fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); } err = Pa_CloseStream( opts->audio_out_pa_stream ); if( err != paNoError ) { - fprintf( stderr, "An error occured while closing the portaudio output stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + //fprintf( stderr, "An error occured while closing the portaudio output stream\n" ); + //fprintf( stderr, "Error number: %d\n", err ); + //fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); } } err = Pa_Terminate(); if( err != paNoError ) { - fprintf( stderr, "An error occured while terminating portaudio\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + //fprintf( stderr, "An error occured while terminating portaudio\n" ); + //fprintf( stderr, "Error number: %d\n", err ); + //fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); } } #endif +#ifdef USE_RTLSDR + if(opts->audio_in_type == 3) + { + // TODO: cleanup demod threads + cleanup_rtlsdr_stream(); + } +#endif + printf("\n"); printf("Total audio errors: %i\n", state->debug_audio_errors); printf("Total header errors: %i\n", state->debug_header_errors); @@ -448,6 +486,61 @@ sigfun (int sig) { exitflag = 1; signal (SIGINT, SIG_DFL); +#ifdef USE_RTLSDR + rtlsdr_sighandler(); +#endif +} + +//LEH version of sigfun +/* +void sigfun (int sig) +{ + UNUSED_VARIABLE(sig); //what is this and where is it defined? + exitflag = 1; + signal (SIGINT, SIG_DFL); +} + +//another LEH thing to free allocated memory +void freeAllocatedMemory(dsd_opts * opts, dsd_state * state) +{ + UNUSED_VARIABLE(opts); + + // Free allocated memory // + free(state->prev_mp_enhanced); + free(state->prev_mp); + free(state->cur_mp); + free(state->audio_out_float_buf); + free(state->audio_out_buf); + free(state->dibit_buf); +} + +*/ +//end LEH version of sigfun + +double atofs(char *s) +{ + char last; + int len; + double suff = 1.0; + len = strlen(s); + last = s[len-1]; + s[len-1] = '\0'; + switch (last) { + case 'g': + case 'G': + suff *= 1e3; + case 'm': + case 'M': + suff *= 1e3; + case 'k': + case 'K': + suff *= 1e3; + suff *= atof(s); + s[len-1] = last; + return suff; + } + s[len-1] = last; + return atof(s); } int @@ -461,7 +554,12 @@ main (int argc, char **argv) char versionstr[25]; mbe_printVersion (versionstr); - printf ("Digital Speech Decoder 1.7.0-dev (build:%s)\n", GIT_TAG); + + for (short int i = 0; i < 7; i++) { + printf("%s \n", FM_banner[i]); + } + //printf ("Digital Speech Decoder 1.7.0-dev (build:%s)\n", GIT_TAG); + printf ("Digital Speech Decoder: Florida Man Edition\n"); printf ("mbelib version %s\n", versionstr); initOpts (&opts); @@ -470,7 +568,7 @@ main (int argc, char **argv) exitflag = 0; signal (SIGINT, sigfun); - while ((c = getopt (argc, argv, "haep:qstv:z:i:o:d:g:nw:B:C:R:f:m:u:x:A:S:M:rl")) != -1) + while ((c = getopt (argc, argv, "haep:P:qstv:z:i:o:d:c:g:nw:B:C:R:f:m:u:x:A:S:M:G:D:rl")) != -1) { opterr = 0; switch (c) @@ -507,6 +605,9 @@ main (int argc, char **argv) opts.unmute_encrypted_p25 = 1; } break; + case 'P': + sscanf (optarg, "%i", &opts.rtlsdr_ppm_error); + break; case 'q': opts.errorbars = 0; opts.verbose = 0; @@ -528,6 +629,18 @@ main (int argc, char **argv) case 'v': sscanf (optarg, "%d", &opts.verbose); break; + + case 'G': //Set rtl device gain + sscanf (optarg, "%d", &opts.rtl_gain_value); //multiple value by ten to make it consitent with the way rtl_fm really works + //opts->audio_in_type = 3; //set to use RTL this way and maybe ditch the other shitty way + break; + + case 'D': //Set rtl device index number + sscanf (optarg, "%d", &opts.rtl_dev_index); + //opts->audio_in_type = 3; //set to use RTL this way and maybe ditch the other shitty way + break; + + case 'z': sscanf (optarg, "%d", &opts.scoperate); opts.errorbars = 0; @@ -542,6 +655,7 @@ main (int argc, char **argv) case 'i': strncpy(opts.audio_in_dev, optarg, 1023); opts.audio_in_dev[1023] = '\0'; + //printf("audio_in_dev = %s\n", opts.audio_in_dev); break; case 'o': strncpy(opts.audio_out_dev, optarg, 1023); @@ -552,6 +666,10 @@ main (int argc, char **argv) opts.mbe_out_dir[1023] = '\0'; printf ("Writing mbe data files to directory %s\n", opts.mbe_out_dir); break; + case 'c': + opts.rtlsdr_center_freq = (uint32_t)atofs(optarg); + printf("Tuning to frequency: %i\n", opts.rtlsdr_center_freq); + break; case 'g': sscanf (optarg, "%f", &opts.audio_gain); if (opts.audio_gain < (float) 0 ) @@ -815,9 +933,9 @@ main (int argc, char **argv) PaError err = Pa_Initialize(); if( err != paNoError ) { - fprintf( stderr, "An error occured while initializing portaudio\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + //fprintf( stderr, "An error occured while initializing portaudio\n" ); + //fprintf( stderr, "Error number: %d\n", err ); + //fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); exit(err); } } @@ -851,7 +969,25 @@ main (int argc, char **argv) openAudioOutDevice (&opts, SAMPLE_RATE_OUT); } openAudioInDevice (&opts); + setlocale(LC_ALL, ""); + //sleep(3); + //initscr(); //Initialize NCURSES screen window + start_color(); + init_pair(1, COLOR_YELLOW, COLOR_BLACK); //Yellow/Amber for frame sync/control channel, NV style + init_pair(2, COLOR_RED, COLOR_BLACK); //Red for Terminated Calls + init_pair(3, COLOR_GREEN, COLOR_BLACK); //Green for Active Calls + init_pair(4, COLOR_CYAN, COLOR_BLACK); //Cyan for Site Extra and Patches + init_pair(5, COLOR_MAGENTA, COLOR_BLACK); //Magenta for no frame sync/signal + noecho(); + cbreak(); + erase(); + for (short int i = 0; i < 7; i++) { + printw("%s \n", FM_banner[i]); } + attroff(COLOR_PAIR(4)); + refresh(); + printf("Press CTRL + C twice to close.\n"); //Kindly remind user to double tap CTRL + C } + else { opts.split = 0; @@ -865,10 +1001,12 @@ main (int argc, char **argv) { playMbeFiles (&opts, &state, argc, argv); } + else { liveScanner (&opts, &state); } cleanupAndExit (&opts, &state); + endwin(); return (0); } diff --git a/src/dsd_mbe.c b/src/dsd_mbe.c index 3e93a82..35c37c1 100644 --- a/src/dsd_mbe.c +++ b/src/dsd_mbe.c @@ -135,6 +135,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a if (opts->errorbars == 1) { printf ("%s", state->err_str); + //printw("%s", state->err_str); } state->debug_audio_errors += state->errs2; diff --git a/src/dsd_symbol.c b/src/dsd_symbol.c index f286ae2..d34c7e6 100644 --- a/src/dsd_symbol.c +++ b/src/dsd_symbol.c @@ -88,18 +88,25 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync) cleanupAndExit (opts, state); } } - else - { + else if (opts->audio_in_type == 2) + { #ifdef USE_PORTAUDIO - PaError err = Pa_ReadStream( opts->audio_in_pa_stream, &sample, 1 ); - if( err != paNoError ) - { - fprintf( stderr, "An error occured while using the portaudio input stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); - } + PaError err = Pa_ReadStream( opts->audio_in_pa_stream, &sample, 1 ); + if( err != paNoError ) + { + fprintf( stderr, "An error occured while using the portaudio input stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + } #endif - } + } + else if (opts->audio_in_type == 3) + { +#ifdef USE_RTLSDR + // TODO: need to read demodulated stream here + get_rtlsdr_sample(&sample); +#endif + } #ifdef TRACE_DSD state->debug_sample_index++; diff --git a/src/provoice.c b/src/provoice.c index 2c01329..799cbdd 100644 --- a/src/provoice.c +++ b/src/provoice.c @@ -5,25 +5,28 @@ void processProVoice (dsd_opts * opts, dsd_state * state) { int i, j, dibit; - + //char datestr[32]; char imbe7100_fr1[7][24]; char imbe7100_fr2[7][24]; const int *w, *x; + //struct tm timep; if (opts->errorbars == 1) { - printf ("VOICE e:"); + //strftime (datestr, 31, "%Y-%m-%d-%H%M%S", &timep); + //printf ("%s VOICE e:", datestr); + //printf ("VOICE e:"); } for (i = 0; i < 64; i++) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif } #ifdef PROVOICE_DUMP - printf (" "); + //printf (" "); #endif // lid @@ -31,22 +34,22 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif } #ifdef PROVOICE_DUMP - printf (" "); + //printf (" "); #endif for (i = 0; i < 64; i++) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif } #ifdef PROVOICE_DUMP - printf (" "); + //printf (" "); #endif // imbe frames 1,2 first half @@ -59,14 +62,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr1[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif w -= 6; x -= 6; @@ -74,14 +77,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr2[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif } @@ -89,14 +92,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr1[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif w -= 6; x -= 6; @@ -104,25 +107,25 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr2[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf (" "); + //printf (" "); #endif // spacer bits dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); - printf (" "); + //printf ("%i", dibit); + //printf (" "); #endif // imbe frames 1,2 second half @@ -131,14 +134,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr2[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif for (i = 0; i < 3; i++) @@ -147,14 +150,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr1[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif w -= 6; x -= 6; @@ -162,14 +165,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr2[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif } @@ -177,14 +180,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr1[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif w -= 5; x -= 5; @@ -192,14 +195,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr2[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif for (i = 0; i < 7; i++) @@ -208,14 +211,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr1[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif w -= 6; x -= 6; @@ -223,14 +226,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr2[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif } @@ -238,14 +241,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr1[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif w -= 5; x -= 5; @@ -253,14 +256,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr2[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf (" "); + //printf (" "); #endif processMbeFrame (opts, state, NULL, NULL, imbe7100_fr1); @@ -269,23 +272,23 @@ processProVoice (dsd_opts * opts, dsd_state * state) // spacer bits dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); - printf (" "); + //printf ("%i", dibit); + //printf (" "); #endif for (i = 0; i < 16; i++) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif } #ifdef PROVOICE_DUMP - printf (" "); + //printf (" "); #endif // imbe frames 3,4 first half @@ -297,14 +300,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr1[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif w -= 6; x -= 6; @@ -312,28 +315,28 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr2[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif } for (j = 0; j < 6; j++) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr1[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif w -= 6; x -= 6; @@ -341,25 +344,25 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr2[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf (" "); + //printf (" "); #endif // spacer bits dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); - printf ("_"); + //printf ("%i", dibit); + //printf ("_"); #endif // imbe frames 3,4 second half @@ -367,14 +370,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr2[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif for (i = 0; i < 3; i++) { @@ -382,14 +385,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr1[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif w -= 6; x -= 6; @@ -397,14 +400,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr2[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif } @@ -412,14 +415,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr1[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif w -= 5; x -= 5; @@ -427,14 +430,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr2[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf (" "); + //printf (" "); #endif for (i = 0; i < 7; i++) @@ -443,14 +446,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr1[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif w -= 6; x -= 6; @@ -458,14 +461,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr2[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif } @@ -473,14 +476,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr1[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf ("_"); + //printf ("_"); #endif w -= 5; x -= 5; @@ -488,14 +491,14 @@ processProVoice (dsd_opts * opts, dsd_state * state) { dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif imbe7100_fr2[*w][*x] = dibit; w++; x++; } #ifdef PROVOICE_DUMP - printf (" "); + //printf (" "); #endif processMbeFrame (opts, state, NULL, NULL, imbe7100_fr1); @@ -504,12 +507,12 @@ processProVoice (dsd_opts * opts, dsd_state * state) // spacer bits dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); + //printf ("%i", dibit); #endif dibit = getDibit (opts, state); #ifdef PROVOICE_DUMP - printf ("%i", dibit); - printf (" "); + //printf ("%i", dibit); + //printf (" "); #endif if (opts->errorbars == 1) diff --git a/src/rtl_sdr_fm.cpp b/src/rtl_sdr_fm.cpp new file mode 100644 index 0000000..a33b218 --- /dev/null +++ b/src/rtl_sdr_fm.cpp @@ -0,0 +1,1148 @@ +/* + * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver + * Copyright (C) 2012 by Steve Markgraf + * Copyright (C) 2012 by Hoernchen + * Copyright (C) 2012 by Kyle Keen + * Copyright (C) 2013 by Elias Oenal + * Copyright (C) 2014 by Kyle Keen + * + * 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dsd.h" + +#define DEFAULT_SAMPLE_RATE 48000 //48000 +#define DEFAULT_BUF_LENGTH (1 * 16384) +#define MAXIMUM_OVERSAMPLE 16 //16 +#define MAXIMUM_BUF_LENGTH (MAXIMUM_OVERSAMPLE * DEFAULT_BUF_LENGTH) +#define AUTO_GAIN -100 +#define BUFFER_DUMP 4096 + +#define FREQUENCIES_LIMIT 1000 + +static int lcm_post[17] = {1,1,1,3,1,5,3,7,1,9,5,11,3,13,7,15,1}; +static int ACTUAL_BUF_LENGTH; + +static int *atan_lut = NULL; +static int atan_lut_size = 131072; /* 512 KB */ +static int atan_lut_coef = 8; +//new for UDP +#include +#include +static pthread_t socket_freq; +// +struct dongle_state +{ + int exit_flag; + pthread_t thread; + rtlsdr_dev_t *dev; + int dev_index; + uint32_t freq; + uint32_t rate; + int gain; + uint16_t buf16[MAXIMUM_BUF_LENGTH]; + uint32_t buf_len; + int ppm_error; + int offset_tuning; + int direct_sampling; + int mute; + struct demod_state *demod_target; +}; + +struct demod_state +{ + int exit_flag; + pthread_t thread; + int16_t lowpassed[MAXIMUM_BUF_LENGTH]; + int lp_len; + int16_t lp_i_hist[10][6]; + int16_t lp_q_hist[10][6]; + int16_t result[MAXIMUM_BUF_LENGTH]; + int16_t droop_i_hist[9]; + int16_t droop_q_hist[9]; + int result_len; + int rate_in; + int rate_out; + int rate_out2; + int now_r, now_j; + int pre_r, pre_j; + int prev_index; + int downsample; /* min 1, max 256 */ + int post_downsample; + int output_scale; + int squelch_level, conseq_squelch, squelch_hits, terminate_on_squelch; + int downsample_passes; + int comp_fir_size; + int custom_atan; + int deemph, deemph_a; + int now_lpr; + int prev_lpr_index; + int dc_block, dc_avg; + void (*mode_demod)(struct demod_state*); + pthread_rwlock_t rw; + pthread_cond_t ready; + pthread_mutex_t ready_m; + struct output_state *output_target; +}; + +struct output_state +{ + int rate; + std::queue queue; + pthread_rwlock_t rw; + pthread_cond_t ready; + pthread_mutex_t ready_m; +}; + +struct controller_state +{ + int exit_flag; + pthread_t thread; + uint32_t freqs[FREQUENCIES_LIMIT]; + int freq_len; + int freq_now; + int edge; + int wb_mode; + pthread_cond_t hop; + pthread_mutex_t hop_m; +}; + +struct dongle_state dongle; +struct demod_state demod; +struct output_state output; +struct controller_state controller; + +#define safe_cond_signal(n, m) pthread_mutex_lock(m); pthread_cond_signal(n); pthread_mutex_unlock(m) +#define safe_cond_wait(n, m) pthread_mutex_lock(m); pthread_cond_wait(n, m); pthread_mutex_unlock(m) + +/* {length, coef, coef, coef} and scaled by 2^15 + for now, only length 9, optimal way to get +85% bandwidth */ +#define CIC_TABLE_MAX 10 +int cic_9_tables[][10] = { + {0,}, + {9, -156, -97, 2798, -15489, 61019, -15489, 2798, -97, -156}, + {9, -128, -568, 5593, -24125, 74126, -24125, 5593, -568, -128}, + {9, -129, -639, 6187, -26281, 77511, -26281, 6187, -639, -129}, + {9, -122, -612, 6082, -26353, 77818, -26353, 6082, -612, -122}, + {9, -120, -602, 6015, -26269, 77757, -26269, 6015, -602, -120}, + {9, -120, -582, 5951, -26128, 77542, -26128, 5951, -582, -120}, + {9, -119, -580, 5931, -26094, 77505, -26094, 5931, -580, -119}, + {9, -119, -578, 5921, -26077, 77484, -26077, 5921, -578, -119}, + {9, -119, -577, 5917, -26067, 77473, -26067, 5917, -577, -119}, + {9, -199, -362, 5303, -25505, 77489, -25505, 5303, -362, -199}, +}; + +void rotate_90(unsigned char *buf, uint32_t len) +/* 90 rotation is 1+0j, 0+1j, -1+0j, 0-1j + or [0, 1, -3, 2, -4, -5, 7, -6] */ +{ + uint32_t i; + unsigned char tmp; + for (i=0; ilp_len) { + d->now_r += d->lowpassed[i]; + d->now_j += d->lowpassed[i+1]; + i += 2; + d->prev_index++; + if (d->prev_index < d->downsample) { + continue; + } + d->lowpassed[i2] = d->now_r; // * d->output_scale; + d->lowpassed[i2+1] = d->now_j; // * d->output_scale; + d->prev_index = 0; + d->now_r = 0; + d->now_j = 0; + i2 += 2; + } + d->lp_len = i2; +} + +int low_pass_simple(int16_t *signal2, int len, int step) +// no wrap around, length must be multiple of step +{ + int i, i2, sum; + for(i=0; i < len; i+=step) { + sum = 0; + for(i2=0; i2rate_out; + int slow = s->rate_out2; + while (i < s->result_len) { + s->now_lpr += s->result[i]; + i++; + s->prev_lpr_index += slow; + if (s->prev_lpr_index < fast) { + continue; + } + s->result[i2] = (int16_t)(s->now_lpr / (fast/slow)); + s->prev_lpr_index -= fast; + s->now_lpr = 0; + i2 += 1; + } + s->result_len = i2; +} + +void fifth_order(int16_t *data, int length, int16_t *hist) +/* for half of interleaved data */ +{ + int i; + int16_t a, b, c, d, e, f; + a = hist[1]; + b = hist[2]; + c = hist[3]; + d = hist[4]; + e = hist[5]; + f = data[0]; + /* a downsample should improve resolution, so don't fully shift */ + data[0] = (a + (b+e)*5 + (c+d)*10 + f) >> 4; + for (i=4; i> 4; + } + /* archive */ + hist[0] = a; + hist[1] = b; + hist[2] = c; + hist[3] = d; + hist[4] = e; + hist[5] = f; +} + +void generic_fir(int16_t *data, int length, int *fir, int16_t *hist) +/* Okay, not at all generic. Assumes length 9, fix that eventually. */ +{ + int d, temp, sum; + for (d=0; d> 15 ; + hist[0] = hist[1]; + hist[1] = hist[2]; + hist[2] = hist[3]; + hist[3] = hist[4]; + hist[4] = hist[5]; + hist[5] = hist[6]; + hist[6] = hist[7]; + hist[7] = hist[8]; + hist[8] = temp; + } +} + +// define our own complex math ops because ARMv5 has no hardware float +void multiply(int ar, int aj, int br, int bj, int *cr, int *cj) +{ + *cr = ar*br - aj*bj; + *cj = aj*br + ar*bj; +} + +int polar_discriminant(int ar, int aj, int br, int bj) +{ + int cr, cj; + double angle; + multiply(ar, aj, br, -bj, &cr, &cj); + angle = atan2((double)cj, (double)cr); + return (int)(angle / 3.14159 * (1<<14)); +} + +int fast_atan2(int y, int x) +/* pre scaled for int16 */ +{ + int yabs, angle; + int pi4=(1<<12), pi34=3*(1<<12); // note pi = 1<<14 + if (x==0 && y==0) { + return 0; + } + yabs = y; + if (yabs < 0) { + yabs = -yabs; + } + if (x >= 0) { + angle = pi4 - pi4 * (x-yabs) / (x+yabs); + } else { + angle = pi34 - pi4 * (x+yabs) / (yabs-x); + } + if (y < 0) { + return -angle; + } + return angle; +} + +int polar_disc_fast(int ar, int aj, int br, int bj) +{ + int cr, cj; + multiply(ar, aj, br, -bj, &cr, &cj); + return fast_atan2(cj, cr); +} + +int atan_lut_init(void) +{ + int i = 0; + + atan_lut = static_cast(malloc(atan_lut_size * sizeof(int))); + + for (i = 0; i < atan_lut_size; i++) { + atan_lut[i] = (int) (atan((double) i / (1< 0) + {return 1 << 13;} + if (cr == 0 && cj < 0) + {return -(1 << 13);} + if (cj == 0 && cr > 0) + {return 0;} + if (cj == 0 && cr < 0) + {return 1 << 14;} + } + + /* real range -32768 - 32768 use 64x range -> absolute maximum: 2097152 */ + x = (cj << atan_lut_coef) / cr; + x_abs = abs(x); + + if (x_abs >= atan_lut_size) { + /* we can use linear range, but it is not necessary */ + return (cj > 0) ? 1<<13 : -(1<<13); + } + + if (x > 0) { + return (cj > 0) ? atan_lut[x] : atan_lut[x] - (1<<14); + } else { + return (cj > 0) ? (1<<14) - atan_lut[-x] : -atan_lut[-x]; + } + + return 0; +} + +void fm_demod(struct demod_state *fm) +{ + int i, pcm; + int16_t *lp = fm->lowpassed; + pcm = polar_discriminant(lp[0], lp[1], + fm->pre_r, fm->pre_j); + fm->result[0] = (int16_t)pcm; + for (i = 2; i < (fm->lp_len-1); i += 2) { + switch (fm->custom_atan) { + case 0: + pcm = polar_discriminant(lp[i], lp[i+1], + lp[i-2], lp[i-1]); + break; + case 1: + pcm = polar_disc_fast(lp[i], lp[i+1], + lp[i-2], lp[i-1]); + break; + case 2: + pcm = polar_disc_lut(lp[i], lp[i+1], + lp[i-2], lp[i-1]); + break; + } + fm->result[i/2] = (int16_t)pcm; + } + fm->pre_r = lp[fm->lp_len - 2]; + fm->pre_j = lp[fm->lp_len - 1]; + fm->result_len = fm->lp_len/2; +} + +void raw_demod(struct demod_state *fm) +{ + int i; + for (i = 0; i < fm->lp_len; i++) { + fm->result[i] = (int16_t)fm->lowpassed[i]; + } + fm->result_len = fm->lp_len; +} + +void deemph_filter(struct demod_state *fm) +{ + static int avg; // cheating... + int i, d; + // de-emph IIR + // avg = avg * (1 - alpha) + sample * alpha; + for (i = 0; i < fm->result_len; i++) { + d = fm->result[i] - avg; + if (d > 0) { + avg += (d + fm->deemph_a/2) / fm->deemph_a; + } else { + avg += (d - fm->deemph_a/2) / fm->deemph_a; + } + fm->result[i] = (int16_t)avg; + } +} + +void dc_block_filter(struct demod_state *fm) +{ + int i, avg; + int64_t sum = 0; + for (i=0; i < fm->result_len; i++) { + sum += fm->result[i]; + } + avg = sum / fm->result_len; + avg = (avg + fm->dc_avg * 9) / 10; + for (i=0; i < fm->result_len; i++) { + fm->result[i] -= avg; + } + fm->dc_avg = avg; +} + +int rms(int16_t *samples, int len, int step) +/* largely lifted from rtl_power */ +{ + int i; + long p, t, s; + double dc, err; + + p = t = 0L; + for (i=0; idownsample_passes; + if (ds_p) { + for (i=0; i < ds_p; i++) { + fifth_order(d->lowpassed, (d->lp_len >> i), d->lp_i_hist[i]); + fifth_order(d->lowpassed+1, (d->lp_len >> i) - 1, d->lp_q_hist[i]); + } + d->lp_len = d->lp_len >> ds_p; + /* droop compensation */ + if (d->comp_fir_size == 9 && ds_p <= CIC_TABLE_MAX) { + generic_fir(d->lowpassed, d->lp_len, + cic_9_tables[ds_p], d->droop_i_hist); + generic_fir(d->lowpassed+1, d->lp_len-1, + cic_9_tables[ds_p], d->droop_q_hist); + } + } else { + low_pass(d); + } + /* power squelch */ + if (d->squelch_level) { + sr = rms(d->lowpassed, d->lp_len, 1); + if (sr < d->squelch_level) { + d->squelch_hits++; + for (i=0; ilp_len; i++) { + d->lowpassed[i] = 0; + } + } else { + d->squelch_hits = 0;} + } + d->mode_demod(d); /* lowpassed -> result */ + if (d->mode_demod == &raw_demod) { + return; + } + /* todo, fm noise squelch */ + // use nicer filter here too? + if (d->post_downsample > 1) { + d->result_len = low_pass_simple(d->result, d->result_len, d->post_downsample);} + if (d->deemph) { + deemph_filter(d);} + if (d->dc_block) { + dc_block_filter(d);} + if (d->rate_out2 > 0) { + low_pass_real(d); + //arbitrary_resample(d->result, d->result, d->result_len, d->result_len * d->rate_out2 / d->rate_out); + } +} + +static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) +{ + int i; + struct dongle_state *s = static_cast(ctx); + struct demod_state *d = s->demod_target; + + if (exitflag) { + return;} + if (!ctx) { + return;} + if (s->mute) { + for (i=0; imute; i++) { + buf[i] = 127;} + s->mute = 0; + } + if (!s->offset_tuning) { + rotate_90(buf, len);} + for (i=0; i<(int)len; i++) { + s->buf16[i] = (int16_t)buf[i] - 127;} + pthread_rwlock_wrlock(&d->rw); + memcpy(d->lowpassed, s->buf16, 2*len); + d->lp_len = len; + pthread_rwlock_unlock(&d->rw); + safe_cond_signal(&d->ready, &d->ready_m); +} + +static void *dongle_thread_fn(void *arg) +{ + struct dongle_state *s = static_cast(arg); + rtlsdr_read_async(s->dev, rtlsdr_callback, s, 0, s->buf_len); + return 0; +} + +static void *demod_thread_fn(void *arg) +{ + struct demod_state *d = static_cast(arg); + struct output_state *o = d->output_target; + while (!exitflag) { + safe_cond_wait(&d->ready, &d->ready_m); + pthread_rwlock_wrlock(&d->rw); + full_demod(d); + pthread_rwlock_unlock(&d->rw); + if (d->exit_flag) { + exitflag = 1; + } + if (d->squelch_level && d->squelch_hits > d->conseq_squelch) { + d->squelch_hits = d->conseq_squelch + 1; /* hair trigger */ + safe_cond_signal(&controller.hop, &controller.hop_m); + continue; + } + pthread_rwlock_wrlock(&o->rw); + for (int i = 0; i < d->result_len; i++) + { + o->queue.push(d->result[i]); + } + pthread_rwlock_unlock(&o->rw); + safe_cond_signal(&o->ready, &o->ready_m); + } + return 0; +} + +int nearest_gain(rtlsdr_dev_t *dev, int target_gain) +{ + //target_gain = 36; + int i, r, err1, err2, count, nearest; + int* gains; + r = rtlsdr_set_tuner_gain_mode(dev, 1); + if (r < 0) { + fprintf(stderr, "WARNING: Failed to enable manual gain.\n"); + return r; + } + count = rtlsdr_get_tuner_gains(dev, NULL); + if (count <= 0) { + return 0; + } + gains = static_cast(malloc(sizeof(int) * count)); + count = rtlsdr_get_tuner_gains(dev, gains); + nearest = gains[0]; + for (i=0; idownsample = (1000000 / dm->rate_in) + 1; + if (dm->downsample_passes) { + dm->downsample_passes = (int)log2(dm->downsample) + 1; + dm->downsample = 1 << dm->downsample_passes; + } + capture_freq = freq; + capture_rate = dm->downsample * dm->rate_in; + if (!d->offset_tuning) { + capture_freq = freq + capture_rate/4;} + capture_freq += cs->edge * dm->rate_in / 2; + dm->output_scale = (1<<15) / (128 * dm->downsample); + if (dm->output_scale < 1) { + dm->output_scale = 1;} + if (dm->mode_demod == &fm_demod) { + dm->output_scale = 1;} + d->freq = (uint32_t)capture_freq; + d->rate = (uint32_t)capture_rate; +} + +static void *controller_thread_fn(void *arg) +{ + // thoughts for multiple dongles + // might be no good using a controller thread if retune/rate blocks + int i; + struct controller_state *s = static_cast(arg); + + if (s->wb_mode) { + for (i=0; i < s->freq_len; i++) { + s->freqs[i] += 16000;} + } + + /* set up primary channel */ + optimal_settings(s->freqs[0], demod.rate_in); + if (dongle.direct_sampling) { + verbose_direct_sampling(dongle.dev, 1);} + if (dongle.offset_tuning) { + verbose_offset_tuning(dongle.dev);} + + /* Set the frequency */ + verbose_set_frequency(dongle.dev, dongle.freq); + fprintf(stderr, "Oversampling input by: %ix.\n", demod.downsample); + fprintf(stderr, "Oversampling output by: %ix.\n", demod.post_downsample); + fprintf(stderr, "Buffer size: %0.2fms\n", + 1000 * 0.5 * (float)ACTUAL_BUF_LENGTH / (float)dongle.rate); + + /* Set the sample rate */ + verbose_set_sample_rate(dongle.dev, dongle.rate); + fprintf(stderr, "Output at %u Hz.\n", demod.rate_in/demod.post_downsample); + fprintf(stderr, "Press CTRL + C to close.\n"); //Kindly remind user to double tap CTRL + C + + while (!exitflag) { + safe_cond_wait(&s->hop, &s->hop_m); + if (s->freq_len <= 1) { + continue;} + /* hacky hopping */ + s->freq_now = (s->freq_now + 1) % s->freq_len; + optimal_settings(s->freqs[s->freq_now], demod.rate_in); + rtlsdr_set_center_freq(dongle.dev, dongle.freq); + dongle.mute = BUFFER_DUMP; + } + return 0; +} + +void dongle_init(struct dongle_state *s) +{ + s->rate = DEFAULT_SAMPLE_RATE; + s->gain = AUTO_GAIN; // tenths of a dB + s->mute = 0; + s->direct_sampling = 0; + s->offset_tuning = 0; //0 by default, but don't think it listens to proper frequency when 0, offset and tuned seem to be mixed up + s->demod_target = &demod; + //s->dev_index = 1; //need to set dev_index from dsd_main as well... +} + +void demod_init(struct demod_state *s) +{ + s->rate_in = DEFAULT_SAMPLE_RATE; + s->rate_out = DEFAULT_SAMPLE_RATE; + s->squelch_level = 0; + s->conseq_squelch = 10; + s->terminate_on_squelch = 0; + s->squelch_hits = 11; + s->downsample_passes = 0; + s->comp_fir_size = 0; + s->prev_index = 0; + s->post_downsample = 1; // once this works, default = 4 + s->custom_atan = 0; + s->deemph = 0; + s->rate_out2 = -1; // flag for disabled + s->mode_demod = &fm_demod; + s->pre_j = s->pre_r = s->now_r = s->now_j = 0; + s->prev_lpr_index = 0; + s->deemph_a = 0; + s->now_lpr = 0; + s->dc_block = 0; + s->dc_avg = 0; + pthread_rwlock_init(&s->rw, NULL); + pthread_cond_init(&s->ready, NULL); + pthread_mutex_init(&s->ready_m, NULL); + s->output_target = &output; +} + +void demod_cleanup(struct demod_state *s) +{ + pthread_rwlock_destroy(&s->rw); + pthread_cond_destroy(&s->ready); + pthread_mutex_destroy(&s->ready_m); +} + +void output_init(struct output_state *s) +{ + s->rate = DEFAULT_SAMPLE_RATE; + pthread_rwlock_init(&s->rw, NULL); + pthread_cond_init(&s->ready, NULL); + pthread_mutex_init(&s->ready_m, NULL); +} + +void output_cleanup(struct output_state *s) +{ + pthread_rwlock_destroy(&s->rw); + pthread_cond_destroy(&s->ready); + pthread_mutex_destroy(&s->ready_m); +} + +void controller_init(struct controller_state *s) +{ + s->freqs[0] = 446000000; + s->freq_len = 0; + s->edge = 0; + s->wb_mode = 0; + pthread_cond_init(&s->hop, NULL); + pthread_mutex_init(&s->hop_m, NULL); +} + +void controller_cleanup(struct controller_state *s) +{ + pthread_cond_destroy(&s->hop); + pthread_mutex_destroy(&s->hop_m); +} + +void sanity_checks(void) +{ + if (controller.freq_len == 0) { + fprintf(stderr, "Please specify a frequency.\n"); + exit(1); + } + + if (controller.freq_len >= FREQUENCIES_LIMIT) { + fprintf(stderr, "Too many channels, maximum %i.\n", FREQUENCIES_LIMIT); + exit(1); + } + + if (controller.freq_len > 1 && demod.squelch_level == 0) { + fprintf(stderr, "Please specify a squelch level. Required for scanning multiple frequencies.\n"); + exit(1); + } + +} +//UDP remote all below HERE +static unsigned int chars_to_int(unsigned char* buf) { + + int i; + unsigned int val = 0; + + for(i=1; i<5; i++) { + val = val | ((buf[i]) << ((i-1)*8)); + } + + return val; +} + +static void *socket_thread_fn(void *arg) { + //struct fm_state *fm = arg; + struct demod_state *d = static_cast(arg); + int port = 6020; + int r, n; + int sockfd, newsockfd, portno; + socklen_t clilen; + unsigned char buffer[5]; + struct sockaddr_in serv_addr, cli_addr; + + sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + if (sockfd < 0) { + perror("ERROR opening socket"); + } + + bzero((char *) &serv_addr, sizeof(serv_addr)); + + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_port = htons(port); + + if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + perror("ERROR on binding"); + } + + bzero(buffer,5); + + fprintf (stderr, "Main socket started! :-) Tuning enabled on UDP/%d \n", port); + + int new_freq, demod_type, new_squelch, new_gain, agc_mode; + + while((n = read(sockfd,buffer,5)) != 0) { + if(buffer[0] == 0) { + new_freq = chars_to_int(buffer); + dongle.freq = new_freq; //not sure if still needed, or ever was needed lul + optimal_settings(new_freq, demod.rate_in); + rtlsdr_set_center_freq(dongle.dev, dongle.freq); + //fprintf (stderr, "Tuning to: %d [Hz] (central freq: %d [Hz])\n", new_freq, new_freq + freq_offset); + fprintf (stderr, "Tuning to: %d [Hz] \n", new_freq); + } +/* + if(buffer[0] == 1) { + // change demod type + int type = chars_to_int(buffer); + + switch(type) { + case 0: + fprintf (stderr, "Changing demod type to FM\n"); + fm->mode_demod = &fm_demod; + break; + case 1: + fprintf (stderr, "Changing demod type to AM\n"); + fm->mode_demod = &am_demod; + break; + case 2: + fprintf (stderr, "Changing demod type to USB\n"); + fm->mode_demod = &usb_demod; + fm->custom_atan = 1; + break; + case 3: + fprintf (stderr, "Changing demod type to LSB\n"); + fm->mode_demod = &lsb_demod; + break; + default: + fprintf (stderr, "Unknown demod type %d\n", type); + break; + } + } +*/ + +//SQUELCH HERE + + if (buffer[0] == 2) { + new_squelch = chars_to_int(buffer); + demod.squelch_level = new_squelch; //demod, or d*? or d-> + //full_demod(d); + //new_freq = 850000000; + //dongle.freq = new_freq; + //optimal_settings(new_freq, demod.rate_in); //hacky way to 'squelch', just setting it to a dead freq instead + //rtlsdr_set_center_freq(dongle.dev, dongle.freq); + fprintf (stderr, "Changing squelch to %d \n", new_squelch); + } + +/* + if (buffer[0] == 3) { + new_gain = chars_to_int(buffer); + if (new_gain == AUTO_GAIN) { + r = rtlsdr_set_tuner_gain_mode(dev, 0); + } else { + r = rtlsdr_set_tuner_gain_mode(dev, 1); + new_gain = nearest_gain(new_gain); + r = rtlsdr_set_tuner_gain(dev, new_gain); + } + + if (r != 0) { + fprintf(stderr, "WARNING: Failed to set tuner gain.\n"); + } else if (new_gain == AUTO_GAIN) { + fprintf(stderr, "Tuner gain set to automatic.\n"); + } else { + fprintf(stderr, "Tuner gain set to %0.2f dB.\n", new_gain/10.0); + } + } +*/ +/* + if (buffer[0] == 8) { + agc_mode = chars_to_int(buffer); + if (agc_mode == 0 || agc_mode == 1) { + fprintf(stderr, "Setting AGC to %d\n", agc_mode); + rtlsdr_set_agc_mode(dev, agc_mode); + } else { + fprintf(stderr, "Failed to set AGC to %d\n", agc_mode); + } + } +*/ + } + + close(sockfd); + return 0; +} +//UDP Control all above HERE + +void rtlsdr_sighandler() +{ + fprintf(stderr, "Signal caught, exiting!\n"); + fprintf(stderr, "Press CTRL + C again to close. Thanks. \n"); //Kindly remind user to double tap CTRL + C + rtlsdr_cancel_async(dongle.dev); + //cleanup_rtlsdr_stream(); //thank the wraith for this one...if it works...didn't work + //_Exit(1); +} + +void open_rtlsdr_stream(dsd_opts *opts) +{ + struct sigaction sigact; + int r; + + dongle_init(&dongle); + demod_init(&demod); + output_init(&output); + controller_init(&controller); + + if (opts->rtlsdr_center_freq > 0) { + controller.freqs[controller.freq_len] = opts->rtlsdr_center_freq; + controller.freq_len++; + } + + if (opts->rtlsdr_ppm_error > 0 || opts->rtlsdr_ppm_error < 0) { //this way will adjust PPM based on negative values as well + dongle.ppm_error = opts->rtlsdr_ppm_error; + } + + if (opts->audio_in_type == 3) { + dongle.dev_index = opts->rtl_dev_index; + } + + if (opts->rtl_gain_value > 0) { + dongle.gain = opts->rtl_gain_value * 10; //multiple by ten to make it consitent with the way rtl_fm really works + } + + /* quadruple sample_rate to limit to Δθ to ±π/2 */ + demod.rate_in *= demod.post_downsample; + + if (!output.rate) output.rate = demod.rate_out; + + sanity_checks(); + + if (controller.freq_len > 1) demod.terminate_on_squelch = 0; + + ACTUAL_BUF_LENGTH = lcm_post[demod.post_downsample] * DEFAULT_BUF_LENGTH; + r = rtlsdr_open(&dongle.dev, (uint32_t)dongle.dev_index); + + if (r < 0) + { + fprintf(stderr, "Failed to open rtlsdr device %d.\n", dongle.dev_index); + exit(1); + } else { + fprintf(stderr, "Using RTLSDR Device Index: %d. \n", dongle.dev_index); //Kindly tell us which device we are using + } + + // sigact.sa_handler = sighandler; + // sigemptyset(&sigact.sa_mask); + // sigact.sa_flags = 0; + // sigaction(SIGINT, &sigact, NULL); + // sigaction(SIGTERM, &sigact, NULL); + // sigaction(SIGQUIT, &sigact, NULL); + // sigaction(SIGPIPE, &sigact, NULL); + + if (demod.deemph) { + demod.deemph_a = (int)round(1.0/((1.0-exp(-1.0/(demod.rate_out * 75e-6))))); + } + + /* Set the tuner gain */ + if (dongle.gain == AUTO_GAIN) { + verbose_auto_gain(dongle.dev); + } else { + dongle.gain = nearest_gain(dongle.dev, dongle.gain); + verbose_gain_set(dongle.dev, dongle.gain); + } + + verbose_ppm_set(dongle.dev, dongle.ppm_error); + + /* Reset endpoint before we start reading from it (mandatory) */ + verbose_reset_buffer(dongle.dev); + + pthread_create(&controller.thread, NULL, controller_thread_fn, (void*)(&controller)); + usleep(100000); + pthread_create(&demod.thread, NULL, demod_thread_fn, (void*)(&demod)); + pthread_create(&dongle.thread, NULL, dongle_thread_fn, (void*)(&dongle)); + pthread_create(&socket_freq, NULL, socket_thread_fn, (void *)(&controller)); +} + +void cleanup_rtlsdr_stream() +{ + printf("cleaning up...\n"); + rtlsdr_cancel_async(dongle.dev); + pthread_join(dongle.thread, NULL); + safe_cond_signal(&demod.ready, &demod.ready_m); + pthread_join(demod.thread, NULL); + safe_cond_signal(&output.ready, &output.ready_m); + safe_cond_signal(&controller.hop, &controller.hop_m); + pthread_join(controller.thread, NULL); + + //dongle_cleanup(&dongle); + demod_cleanup(&demod); + output_cleanup(&output); + controller_cleanup(&controller); + + rtlsdr_close(dongle.dev); + +} + +void get_rtlsdr_sample(int16_t *sample) +{ + if (output.queue.empty()) + { + safe_cond_wait(&output.ready, &output.ready_m); + } + pthread_rwlock_wrlock(&output.rw); + *sample = output.queue.front(); + output.queue.pop(); + pthread_rwlock_unlock(&output.rw); +}