diff --git a/.gitignore b/.gitignore index 5ef0532..84e02eb 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,15 @@ CMakeCache.txt */CMakeFiles CMakeFiles *.cmake +build + +**/*.o +**/*.so* +**/*.a +src/rtl_adsb +src/rtl_eeprom +src/rtl_fm +src/rtl_power +src/rtl_test + +debianize/*.deb diff --git a/CMakeLists.txt b/CMakeLists.txt index 0597600..4bc4721 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,7 @@ endif() # Set the version information here set(VERSION_INFO_MAJOR_VERSION 0) # increment major on api compatibility changes -set(VERSION_INFO_MINOR_VERSION 5) # increment minor on feature-level changes +set(VERSION_INFO_MINOR_VERSION 6) # increment minor on feature-level changes set(VERSION_INFO_PATCH_VERSION git) # increment patch for bug fixes and docs include(Version) # setup version info @@ -57,6 +57,14 @@ if(CMAKE_COMPILER_IS_GNUCC AND NOT WIN32) add_definitions(-fvisibility=hidden) endif() +######################################################################### +# Bug Fix +######################################################################### +OPTION(NEED_PTHREADS_WORKARROUND "PThreads Workarround for timespec") +IF (DEFINED NEED_PTHREADS_WORKARROUND) + ADD_DEFINITIONS(-DNEED_PTHREADS_WORKARROUND) +ENDIF() + ######################################################################## # Find build dependencies ######################################################################## diff --git a/README.rtlsdr_rpc b/README.rtlsdr_rpc new file mode 100644 index 0000000..646b13d --- /dev/null +++ b/README.rtlsdr_rpc @@ -0,0 +1,43 @@ +This implementation of librtlsdr makes remote dongles +appear to the local software as if they were on the +same computer. It works by forwarding librtlsdr calls +to the remote computer over TCP. + +It allows one to use existing tools without modifying +them. Also, it allows a developer to use the same API +no matter weither the dongle is local or distant. + +To use it, one must compile and install the library +with CMAKE the usual way. Note that you may need to +uninstall the existing librtlsdr, as people reported +runtime errors due to conflicting installs. + +Then, a server (called rtl_rpcd) must be run on the +remote location. + +In my case, the dongle is in a beagle bone black is +at address 192.168.0.43: +beagleboneblack #> ./rtl_rpcd + +Then, the existing tool (for instance rtlizer) can be +run on the local computer using: +RTLSDR_RPC_IS_ENABLED=1 RTLSDR_RPC_SERV_ADDR=192.168.0.43 \ +rtlizer + +This implementation still has some limitations, but +works well in most cases. Please report any bug to +texane@gmail.com + +Also, note that the latest version of libusb should be +used as librtlsdr crashed when used with older version +(esp. the rtlsdr_read_async routine): +https://github.com/libusb/libusb.git + +list of known working software: +rtl_fm +rtl_power +rtlsdr-waterfall +rtlizer +gnuradio-companion +cubicsdr +gqrx \ No newline at end of file diff --git a/circle.yml b/circle.yml deleted file mode 100644 index b55d711..0000000 --- a/circle.yml +++ /dev/null @@ -1,8 +0,0 @@ -dependencies: - pre: - - sudo apt-get update; sudo apt-get install libusb-1.0-0-dev; - override: - - cd ~/; git clone https://github.com/librtlsdr/librtlsdr.git; cd librtlsdr; mkdir build; cd build; cmake ../; make; sudo make install; sudo ldconfig; -test: - override: - - echo "----- done -----"; diff --git a/debian/.gitignore b/debian/.gitignore new file mode 100644 index 0000000..c00df13 --- /dev/null +++ b/debian/.gitignore @@ -0,0 +1 @@ +*.deb diff --git a/debian/debianize b/debian/debianize new file mode 100755 index 0000000..32d8233 --- /dev/null +++ b/debian/debianize @@ -0,0 +1,119 @@ +#!/bin/bash + +REPO_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/../" + +G_REV=`git rev-parse --short=8 HEAD` +DATE=`date +"%Y%m%d%H%M%S"` +#VERSION="0.5.3-git+${DATE}.${G_REV}~$1" +VERSION=`git describe | cut -dv -f2` +# +# librtlsdr0 +# + +rm -fr /tmp/librtlsdr0/ +mkdir -p /tmp/librtlsdr0/ +mkdir -p /tmp/librtlsdr0/usr/lib/x86_64-linux-gnu/ +mkdir -p /tmp/librtlsdr0/DEBIAN + +cat <<- EOF > /tmp/librtlsdr0/DEBIAN/control +Package: librtlsdr0 +Source: rtl-sdr +Version: ${VERSION} +Architecture: amd64 +Maintainer: Lucas Teske +Pre-Depends: multiarch-support +Depends: libc6 (>= 2.14), libusb-1.0-0 (>= 2:1.0.9) +Section: libs +Priority: extra +Multi-Arch: same +Homepage: http://sdr.osmocom.org/trac/wiki/rtl-sdr +Description: Software defined radio receiver for Realtek RTL2832U (library) + rtl-sdr is a software defined radio (SDR) receiver software for certain + low-cost DVB-T/DAB(+) USB dongles based on the Realtek RTL2832U chip. + . + This package contains the shared library. +EOF + +DEB_PKG="librtlsdr0_${VERSION}_amd64.deb" + +cp -rf ${REPO_DIR}/build/src/lib*so* /tmp/librtlsdr0/usr/lib/x86_64-linux-gnu/ +dpkg-deb -b /tmp/librtlsdr0/ ./${DEB_PKG} + +echo ${DEB_PKG} + +# +# rtl-sdr +# + +rm -fr /tmp/rtl-sdr/ +mkdir -p /tmp/rtl-sdr/ +mkdir -p /tmp/rtl-sdr/usr/bin/ +mkdir -p /tmp/rtl-sdr/DEBIAN + +cat <<- EOF > /tmp/rtl-sdr/DEBIAN/control +Package: rtl-sdr +Version: ${VERSION} +Architecture: amd64 +Maintainer: Lucas Teske +Depends: librtlsdr0 (= ${VERSION}), libc6 (>= 2.15) +Section: libs +Priority: extra +Homepage: http://sdr.osmocom.org/trac/wiki/rtl-sdr +Description: Software defined radio receiver for Realtek RTL2832U (tools) + rtl-sdr is a software defined radio (SDR) receiver software for certain + low-cost DVB-T/DAB(+) USB dongles based on the Realtek RTL2832U chip. + . + This package contains a set of command line utilities: + * rtl_adsb: a simple ADS-B decoder for RTL2832 based DVB-T receivers + * rtl_eeprom: an EEPROM programming tool for RTL2832 based DVB-T receivers + * rtl_fm: a narrow band FM demodulator for RTL2832 based DVB-T receivers + * rtl_sdr: an I/Q recorder for RTL2832 based DVB-T receivers + * rtl_tcp: an I/Q spectrum server for RTL2832 based DVB-T receivers + * rtl_test: a benchmark tool for RTL2832 based DVB-T receivers + + +EOF + +DEB_PKG="rtl-sdr_${VERSION}_amd64.deb" + +cp -rf ${REPO_DIR}/build/src/rtl_* /tmp/rtl-sdr/usr/bin/ +dpkg-deb -b /tmp/rtl-sdr/ ./${DEB_PKG} + +echo ${DEB_PKG} + + +# +# librtlsdr-dev +# + +rm -fr /tmp/librtlsdr-dev/ +mkdir -p /tmp/librtlsdr-dev/ +mkdir -p /tmp/librtlsdr-dev/usr/include +mkdir -p /tmp/librtlsdr-dev/usr/lib/x86_64-linux-gnu/pkgconfig +mkdir -p /tmp/librtlsdr-dev/DEBIAN + +cat <<- EOF > /tmp/librtlsdr-dev/DEBIAN/control +Package: librtlsdr-dev +Source: rtl-sdr +Version: ${VERSION} +Architecture: amd64 +Maintainer: Lucas Teske +Pre-Depends: multiarch-support +Depends: librtlsdr0 (= ${VERSION}) +Section: libdevel +Priority: extra +Homepage: http://sdr.osmocom.org/trac/wiki/rtl-sdr +Description: Software defined radio receiver for Realtek RTL2832U (development files) + rtl-sdr is a software defined radio (SDR) receiver software for certain + low-cost DVB-T/DAB(+) USB dongles based on the Realtek RTL2832U chip. + . + This package contains development files. + +EOF + +DEB_PKG="librtlsdr-dev_${VERSION}_amd64.deb" + +cp -rf ${REPO_DIR}/include/*.h /tmp/librtlsdr-dev/usr/include +dpkg-deb -b /tmp/librtlsdr-dev/ ./${DEB_PKG} + +echo ${DEB_PKG} \ No newline at end of file diff --git a/include/rtl-sdr.h b/include/rtl-sdr.h old mode 100644 new mode 100755 index fe64bea..09ad2df --- a/include/rtl-sdr.h +++ b/include/rtl-sdr.h @@ -24,8 +24,14 @@ extern "C" { #endif +#ifndef WIN32 +#define _ENABLE_RPC +#endif + + #include #include +#include typedef struct rtlsdr_dev rtlsdr_dev_t; @@ -142,6 +148,13 @@ RTLSDR_API int rtlsdr_write_eeprom(rtlsdr_dev_t *dev, uint8_t *data, RTLSDR_API int rtlsdr_read_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16_t len); +/*! + * Set the frequency the device is tuned to. + * + * \param dev the device handle given by rtlsdr_open() + * \param frequency in Hz + * \return 0 on error, frequency in Hz otherwise + */ RTLSDR_API int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq); /*! @@ -220,9 +233,14 @@ RTLSDR_API int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain); * * \param dev the device handle given by rtlsdr_open() * \param bw bandwidth in Hz. Zero means automatic BW selection. + * \param applied_bw is applied bandwidth in Hz, or 0 if unknown + * \param apply_bw: 1 to really apply configure the tuner chip; 0 for just returning applied_bw * \return 0 on success */ -RTLSDR_API int rtlsdr_set_tuner_bandwidth(rtlsdr_dev_t *dev, uint32_t bw); +RTLSDR_API int rtlsdr_set_and_get_tuner_bandwidth(rtlsdr_dev_t *dev, uint32_t bw, uint32_t *applied_bw, int apply_bw ); + +RTLSDR_API int rtlsdr_set_tuner_bandwidth(rtlsdr_dev_t *dev, uint32_t bw ); + /*! * Get actual gain the device is configured to. @@ -232,6 +250,17 @@ RTLSDR_API int rtlsdr_set_tuner_bandwidth(rtlsdr_dev_t *dev, uint32_t bw); */ RTLSDR_API int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev); +/*! + * Set LNA / Mixer / VGA Device Gain for R820T device is configured to. + * + * \param dev the device handle given by rtlsdr_open() + * \param lna_gain in tenths of a dB, -30 means -3.0 dB. + * \param mixer_gain in tenths of a dB, -30 means -3.0 dB. + * \param vga_gain in tenths of a dB, -30 means -3.0 dB. + * \return 0 on success + */ +RTLSDR_API int rtlsdr_set_tuner_gain_ext(rtlsdr_dev_t *dev, int lna_gain, int mixer_gain, int vga_gain); + /*! * Set the intermediate frequency gain for the device. * @@ -380,6 +409,16 @@ RTLSDR_API int rtlsdr_read_async(rtlsdr_dev_t *dev, */ RTLSDR_API int rtlsdr_cancel_async(rtlsdr_dev_t *dev); +/*! + * Read from the remote control (RC) infrared (IR) sensor + * + * \param dev the device handle given by rtlsdr_open() + * \param buf buffer to write IR signal (MSB=pulse/space, 7LSB=duration*20usec), recommended 128-bytes + * \param buf_len size of buf + * \return 0 if no signal, >0 number of bytes written into buf, <0 for error + */ +RTLSDR_API int rtlsdr_ir_query(rtlsdr_dev_t *dev, uint8_t *buf, size_t buf_len); + #ifdef __cplusplus } #endif diff --git a/include/rtl_tcp.h b/include/rtl_tcp.h new file mode 100644 index 0000000..fb8d613 --- /dev/null +++ b/include/rtl_tcp.h @@ -0,0 +1,51 @@ +/* + * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver + * Copyright (C) 2012-2013 by Steve Markgraf + * Copyright (C) 2012 by Dimitri Stolnikov + * + * 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 . + */ + +#ifndef __RTL_TCP_H +#define __RTL_TCP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * This enum defines the possible commands in rtl_tcp + */ +enum RTL_TCP_COMMANDS { + SET_FREQUENCY = 0x01, + SET_SAMPLE_RATE = 0x02, + SET_GAIN_MODE = 0x03, + SET_GAIN = 0x04, + SET_FREQUENCY_CORRECTION = 0x05, + SET_IF_STAGE = 0x06, + SET_TEST_MODE = 0x07, + SET_AGC_MODE = 0x08, + SET_DIRECT_SAMPLING = 0x09, + SET_OFFSET_TUNING = 0x0A, + SET_RTL_CRYSTAL = 0x0B, + SET_TUNER_CRYSTAL = 0x0C, + SET_TUNER_GAIN_BY_INDEX = 0x0D, + SET_TUNER_BANDWIDTH = 0x0E +}; + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/include/rtlsdr_rpc.h b/include/rtlsdr_rpc.h new file mode 100644 index 0000000..eb782c6 --- /dev/null +++ b/include/rtlsdr_rpc.h @@ -0,0 +1,123 @@ +#ifndef RTLSDR_RPC_H_INCLUDED +#define RTLSDR_RPC_H_INCLUDED + + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*rtlsdr_rpc_read_async_cb_t) +(unsigned char*, uint32_t, void*); + +uint32_t rtlsdr_rpc_get_device_count(void); + +const char* rtlsdr_rpc_get_device_name +(uint32_t nidex); + +int rtlsdr_rpc_get_device_usb_strings +(uint32_t index, char* manufact, char* product, char* serial); + +int rtlsdr_rpc_get_index_by_serial +(const char* serial); + +int rtlsdr_rpc_open +(void** dev, uint32_t index); + +int rtlsdr_rpc_close +(void* dev); + +int rtlsdr_rpc_set_xtal_freq +(void* dev, uint32_t rtl_freq, uint32_t tuner_freq); + +int rtlsdr_rpc_get_xtal_freq +(void* dev, uint32_t* rtl_freq, uint32_t* tuner_freq); + +int rtlsdr_rpc_get_usb_strings +(void* dev, char* manufact, char* product, char* serial); + +int rtlsdr_rpc_write_eeprom +(void* dev, uint8_t* data, uint8_t offset, uint16_t len); + +int rtlsdr_rpc_read_eeprom +(void* dev, uint8_t* data, uint8_t offset, uint16_t len); + +int rtlsdr_rpc_set_center_freq +(void* dev, uint32_t freq); + +uint32_t rtlsdr_rpc_get_center_freq +(void* dev); + +int rtlsdr_rpc_set_freq_correction +(void* dev, int ppm); + +int rtlsdr_rpc_get_freq_correction +(void *dev); + +int rtlsdr_rpc_get_tuner_type +(void* dev); + +int rtlsdr_rpc_get_tuner_gains +(void* dev, int* gainsp); + +int rtlsdr_rpc_set_tuner_gain +(void *dev, int gain); + +int rtlsdr_rpc_get_tuner_gain +(void* dev); + +int rtlsdr_rpc_set_tuner_if_gain +(void* dev, int stage, int gain); + +int rtlsdr_rpc_set_tuner_gain_mode +(void* dev, int manual); + +int rtlsdr_rpc_set_sample_rate +(void* dev, uint32_t rate); + +uint32_t rtlsdr_rpc_get_sample_rate +(void* dev); + +int rtlsdr_rpc_set_testmode +(void* dev, int on); + +int rtlsdr_rpc_set_agc_mode +(void* dev, int on); + +int rtlsdr_rpc_set_direct_sampling +(void* dev, int on); + +int rtlsdr_rpc_get_direct_sampling +(void* dev); + +int rtlsdr_rpc_set_offset_tuning +(void* dev, int on); + +int rtlsdr_rpc_get_offset_tuning +(void* dev); + +int rtlsdr_rpc_reset_buffer +(void* dev); + +int rtlsdr_rpc_read_sync +(void* dev, void* buf, int len, int* n_read); + +int rtlsdr_rpc_wait_async +(void* dev, rtlsdr_rpc_read_async_cb_t cb, void* ctx); + +int rtlsdr_rpc_read_async +(void* dev, rtlsdr_rpc_read_async_cb_t cb, void* ctx, uint32_t buf_num, uint32_t buf_len); + +int rtlsdr_rpc_cancel_async +(void* dev); + +unsigned int rtlsdr_rpc_is_enabled(void); + +#ifdef __cplusplus +} +#endif + + +#endif /* RTLSDR_RPC_H_INCLUDED */ diff --git a/include/rtlsdr_rpc_msg.h b/include/rtlsdr_rpc_msg.h new file mode 100644 index 0000000..2ad9bd6 --- /dev/null +++ b/include/rtlsdr_rpc_msg.h @@ -0,0 +1,94 @@ +#ifndef RTLSDR_RPC_MSG_H_INCLUDED +#define RTLSDR_RPC_MSG_H_INCLUDED + + +#include +#include + +typedef enum +{ + RTLSDR_RPC_OP_GET_DEVICE_COUNT = 0, + RTLSDR_RPC_OP_GET_DEVICE_NAME, + RTLSDR_RPC_OP_GET_DEVICE_USB_STRINGS, + RTLSDR_RPC_OP_GET_INDEX_BY_SERIAL, + RTLSDR_RPC_OP_OPEN, + RTLSDR_RPC_OP_CLOSE, + RTLSDR_RPC_OP_SET_XTAL_FREQ, + RTLSDR_RPC_OP_GET_XTAL_FREQ, + RTLSDR_RPC_OP_GET_USB_STRINGS, + RTLSDR_RPC_OP_WRITE_EEPROM, + RTLSDR_RPC_OP_READ_EEPROM, + RTLSDR_RPC_OP_SET_CENTER_FREQ, + RTLSDR_RPC_OP_GET_CENTER_FREQ, + RTLSDR_RPC_OP_SET_FREQ_CORRECTION, + RTLSDR_RPC_OP_GET_FREQ_CORRECTION, + RTLSDR_RPC_OP_GET_TUNER_TYPE, + RTLSDR_RPC_OP_GET_TUNER_GAINS, + RTLSDR_RPC_OP_SET_TUNER_GAIN, + RTLSDR_RPC_OP_GET_TUNER_GAIN, + RTLSDR_RPC_OP_SET_TUNER_IF_GAIN, + RTLSDR_RPC_OP_SET_TUNER_GAIN_MODE, + RTLSDR_RPC_OP_SET_SAMPLE_RATE, + RTLSDR_RPC_OP_GET_SAMPLE_RATE, + RTLSDR_RPC_OP_SET_TESTMODE, + RTLSDR_RPC_OP_SET_AGC_MODE, + RTLSDR_RPC_OP_SET_DIRECT_SAMPLING, + RTLSDR_RPC_OP_GET_DIRECT_SAMPLING, + RTLSDR_RPC_OP_SET_OFFSET_TUNING, + RTLSDR_RPC_OP_GET_OFFSET_TUNING, + RTLSDR_RPC_OP_RESET_BUFFER, + RTLSDR_RPC_OP_READ_SYNC, + RTLSDR_RPC_OP_WAIT_ASYNC, + RTLSDR_RPC_OP_READ_ASYNC, + RTLSDR_RPC_OP_CANCEL_ASYNC, + + /* non api operations */ + RTLSDR_RPC_OP_EVENT_STATE, + + RTLSDR_RPC_OP_INVALID +} rtlsdr_rpc_op_t; + +typedef struct +{ + /* raw network format */ + uint32_t size; + uint8_t op; + uint8_t id; + uint32_t err; + uint8_t data[1]; +} __attribute__((packed)) rtlsdr_rpc_fmt_t; + +typedef struct +{ + size_t off; + size_t size; + uint8_t* fmt; +} rtlsdr_rpc_msg_t; + +int rtlsdr_rpc_msg_init(rtlsdr_rpc_msg_t*, size_t); +int rtlsdr_rpc_msg_fini(rtlsdr_rpc_msg_t*); +void rtlsdr_rpc_msg_reset(rtlsdr_rpc_msg_t*); +int rtlsdr_rpc_msg_realloc(rtlsdr_rpc_msg_t*, size_t); + +void rtlsdr_rpc_msg_set_size(rtlsdr_rpc_msg_t*, size_t); +size_t rtlsdr_rpc_msg_get_size(const rtlsdr_rpc_msg_t*); +void rtlsdr_rpc_msg_set_op(rtlsdr_rpc_msg_t*, rtlsdr_rpc_op_t); +rtlsdr_rpc_op_t rtlsdr_rpc_msg_get_op(const rtlsdr_rpc_msg_t*); +void rtlsdr_rpc_msg_set_id(rtlsdr_rpc_msg_t*, uint8_t); +uint8_t rtlsdr_rpc_msg_get_id(const rtlsdr_rpc_msg_t*); +void rtlsdr_rpc_msg_set_err(rtlsdr_rpc_msg_t*, int); +int rtlsdr_rpc_msg_get_err(const rtlsdr_rpc_msg_t*); + +int rtlsdr_rpc_msg_push_int32(rtlsdr_rpc_msg_t*, int32_t); +int rtlsdr_rpc_msg_push_uint32(rtlsdr_rpc_msg_t*, uint32_t); +void rtlsdr_rpc_msg_push_uint32_safe(rtlsdr_rpc_msg_t*, uint32_t); +int rtlsdr_rpc_msg_push_str(rtlsdr_rpc_msg_t*, const char*); +int rtlsdr_rpc_msg_push_buf(rtlsdr_rpc_msg_t*, const uint8_t*, size_t); +void rtlsdr_rpc_msg_skip_safe(rtlsdr_rpc_msg_t*, size_t); +int rtlsdr_rpc_msg_pop_int32(rtlsdr_rpc_msg_t*, int32_t*); +int rtlsdr_rpc_msg_pop_uint32(rtlsdr_rpc_msg_t*, uint32_t*); +int rtlsdr_rpc_msg_pop_str(rtlsdr_rpc_msg_t*, const char**); +int rtlsdr_rpc_msg_pop_buf(rtlsdr_rpc_msg_t*, const uint8_t**, size_t*); + + +#endif /* RTLSDR_RPC_MSG_H_INCLUDED */ diff --git a/include/tuner_e4k.h b/include/tuner_e4k.h index 79591ce..36f6aa1 100644 --- a/include/tuner_e4k.h +++ b/include/tuner_e4k.h @@ -29,94 +29,94 @@ #define E4K_CHECK_VAL 0x40 enum e4k_reg { - E4K_REG_MASTER1 = 0x00, - E4K_REG_MASTER2 = 0x01, - E4K_REG_MASTER3 = 0x02, - E4K_REG_MASTER4 = 0x03, - E4K_REG_MASTER5 = 0x04, - E4K_REG_CLK_INP = 0x05, - E4K_REG_REF_CLK = 0x06, - E4K_REG_SYNTH1 = 0x07, - E4K_REG_SYNTH2 = 0x08, - E4K_REG_SYNTH3 = 0x09, - E4K_REG_SYNTH4 = 0x0a, - E4K_REG_SYNTH5 = 0x0b, - E4K_REG_SYNTH6 = 0x0c, - E4K_REG_SYNTH7 = 0x0d, - E4K_REG_SYNTH8 = 0x0e, - E4K_REG_SYNTH9 = 0x0f, - E4K_REG_FILT1 = 0x10, - E4K_REG_FILT2 = 0x11, - E4K_REG_FILT3 = 0x12, + E4K_REG_MASTER1 = 0x00, + E4K_REG_MASTER2 = 0x01, + E4K_REG_MASTER3 = 0x02, + E4K_REG_MASTER4 = 0x03, + E4K_REG_MASTER5 = 0x04, + E4K_REG_CLK_INP = 0x05, + E4K_REG_REF_CLK = 0x06, + E4K_REG_SYNTH1 = 0x07, + E4K_REG_SYNTH2 = 0x08, + E4K_REG_SYNTH3 = 0x09, + E4K_REG_SYNTH4 = 0x0a, + E4K_REG_SYNTH5 = 0x0b, + E4K_REG_SYNTH6 = 0x0c, + E4K_REG_SYNTH7 = 0x0d, + E4K_REG_SYNTH8 = 0x0e, + E4K_REG_SYNTH9 = 0x0f, + E4K_REG_FILT1 = 0x10, + E4K_REG_FILT2 = 0x11, + E4K_REG_FILT3 = 0x12, // gap - E4K_REG_GAIN1 = 0x14, - E4K_REG_GAIN2 = 0x15, - E4K_REG_GAIN3 = 0x16, - E4K_REG_GAIN4 = 0x17, + E4K_REG_GAIN1 = 0x14, + E4K_REG_GAIN2 = 0x15, + E4K_REG_GAIN3 = 0x16, + E4K_REG_GAIN4 = 0x17, // gap - E4K_REG_AGC1 = 0x1a, - E4K_REG_AGC2 = 0x1b, - E4K_REG_AGC3 = 0x1c, - E4K_REG_AGC4 = 0x1d, - E4K_REG_AGC5 = 0x1e, - E4K_REG_AGC6 = 0x1f, - E4K_REG_AGC7 = 0x20, - E4K_REG_AGC8 = 0x21, + E4K_REG_AGC1 = 0x1a, + E4K_REG_AGC2 = 0x1b, + E4K_REG_AGC3 = 0x1c, + E4K_REG_AGC4 = 0x1d, + E4K_REG_AGC5 = 0x1e, + E4K_REG_AGC6 = 0x1f, + E4K_REG_AGC7 = 0x20, + E4K_REG_AGC8 = 0x21, // gap - E4K_REG_AGC11 = 0x24, - E4K_REG_AGC12 = 0x25, + E4K_REG_AGC11 = 0x24, + E4K_REG_AGC12 = 0x25, // gap - E4K_REG_DC1 = 0x29, - E4K_REG_DC2 = 0x2a, - E4K_REG_DC3 = 0x2b, - E4K_REG_DC4 = 0x2c, - E4K_REG_DC5 = 0x2d, - E4K_REG_DC6 = 0x2e, - E4K_REG_DC7 = 0x2f, - E4K_REG_DC8 = 0x30, + E4K_REG_DC1 = 0x29, + E4K_REG_DC2 = 0x2a, + E4K_REG_DC3 = 0x2b, + E4K_REG_DC4 = 0x2c, + E4K_REG_DC5 = 0x2d, + E4K_REG_DC6 = 0x2e, + E4K_REG_DC7 = 0x2f, + E4K_REG_DC8 = 0x30, // gap - E4K_REG_QLUT0 = 0x50, - E4K_REG_QLUT1 = 0x51, - E4K_REG_QLUT2 = 0x52, - E4K_REG_QLUT3 = 0x53, + E4K_REG_QLUT0 = 0x50, + E4K_REG_QLUT1 = 0x51, + E4K_REG_QLUT2 = 0x52, + E4K_REG_QLUT3 = 0x53, // gap - E4K_REG_ILUT0 = 0x60, - E4K_REG_ILUT1 = 0x61, - E4K_REG_ILUT2 = 0x62, - E4K_REG_ILUT3 = 0x63, + E4K_REG_ILUT0 = 0x60, + E4K_REG_ILUT1 = 0x61, + E4K_REG_ILUT2 = 0x62, + E4K_REG_ILUT3 = 0x63, // gap - E4K_REG_DCTIME1 = 0x70, - E4K_REG_DCTIME2 = 0x71, - E4K_REG_DCTIME3 = 0x72, - E4K_REG_DCTIME4 = 0x73, - E4K_REG_PWM1 = 0x74, - E4K_REG_PWM2 = 0x75, - E4K_REG_PWM3 = 0x76, - E4K_REG_PWM4 = 0x77, - E4K_REG_BIAS = 0x78, - E4K_REG_CLKOUT_PWDN = 0x7a, + E4K_REG_DCTIME1 = 0x70, + E4K_REG_DCTIME2 = 0x71, + E4K_REG_DCTIME3 = 0x72, + E4K_REG_DCTIME4 = 0x73, + E4K_REG_PWM1 = 0x74, + E4K_REG_PWM2 = 0x75, + E4K_REG_PWM3 = 0x76, + E4K_REG_PWM4 = 0x77, + E4K_REG_BIAS = 0x78, + E4K_REG_CLKOUT_PWDN = 0x7a, E4K_REG_CHFILT_CALIB = 0x7b, E4K_REG_I2C_REG_ADDR = 0x7d, // FIXME }; -#define E4K_MASTER1_RESET (1 << 0) +#define E4K_MASTER1_RESET (1 << 0) #define E4K_MASTER1_NORM_STBY (1 << 1) -#define E4K_MASTER1_POR_DET (1 << 2) +#define E4K_MASTER1_POR_DET (1 << 2) -#define E4K_SYNTH1_PLL_LOCK (1 << 0) +#define E4K_SYNTH1_PLL_LOCK (1 << 0) #define E4K_SYNTH1_BAND_SHIF 1 #define E4K_SYNTH7_3PHASE_EN (1 << 3) #define E4K_SYNTH8_VCOCAL_UPD (1 << 2) -#define E4K_FILT3_DISABLE (1 << 5) +#define E4K_FILT3_DISABLE (1 << 5) -#define E4K_AGC1_LIN_MODE (1 << 4) -#define E4K_AGC1_LNA_UPDATE (1 << 5) -#define E4K_AGC1_LNA_G_LOW (1 << 6) -#define E4K_AGC1_LNA_G_HIGH (1 << 7) +#define E4K_AGC1_LIN_MODE (1 << 4) +#define E4K_AGC1_LNA_UPDATE (1 << 5) +#define E4K_AGC1_LNA_G_LOW (1 << 6) +#define E4K_AGC1_LNA_G_HIGH (1 << 7) #define E4K_AGC6_LNA_CAL_REQ (1 << 4) @@ -127,27 +127,27 @@ enum e4k_reg { #define E4K_AGC11_LNA_GAIN_ENH (1 << 0) -#define E4K_DC1_CAL_REQ (1 << 0) +#define E4K_DC1_CAL_REQ (1 << 0) -#define E4K_DC5_I_LUT_EN (1 << 0) -#define E4K_DC5_Q_LUT_EN (1 << 1) +#define E4K_DC5_I_LUT_EN (1 << 0) +#define E4K_DC5_Q_LUT_EN (1 << 1) #define E4K_DC5_RANGE_DET_EN (1 << 2) -#define E4K_DC5_RANGE_EN (1 << 3) -#define E4K_DC5_TIMEVAR_EN (1 << 4) +#define E4K_DC5_RANGE_EN (1 << 3) +#define E4K_DC5_TIMEVAR_EN (1 << 4) -#define E4K_CLKOUT_DISABLE 0x96 +#define E4K_CLKOUT_DISABLE 0x96 -#define E4K_CHFCALIB_CMD (1 << 0) +#define E4K_CHFCALIB_CMD (1 << 0) -#define E4K_AGC1_MOD_MASK 0xF +#define E4K_AGC1_MOD_MASK 0xF enum e4k_agc_mode { - E4K_AGC_MOD_SERIAL = 0x0, + E4K_AGC_MOD_SERIAL = 0x0, E4K_AGC_MOD_IF_PWM_LNA_SERIAL = 0x1, E4K_AGC_MOD_IF_PWM_LNA_AUTONL = 0x2, E4K_AGC_MOD_IF_PWM_LNA_SUPERV = 0x3, E4K_AGC_MOD_IF_SERIAL_LNA_PWM = 0x4, - E4K_AGC_MOD_IF_PWM_LNA_PWM = 0x5, + E4K_AGC_MOD_IF_PWM_LNA_PWM = 0x5, E4K_AGC_MOD_IF_DIG_LNA_SERIAL = 0x6, E4K_AGC_MOD_IF_DIG_LNA_AUTON = 0x7, E4K_AGC_MOD_IF_DIG_LNA_SUPERV = 0x8, @@ -159,7 +159,7 @@ enum e4k_band { E4K_BAND_VHF2 = 0, E4K_BAND_VHF3 = 1, E4K_BAND_UHF = 2, - E4K_BAND_L = 3, + E4K_BAND_L = 3, }; enum e4k_mixer_filter_bw { diff --git a/include/tuner_fc2580.h b/include/tuner_fc2580.h index 9ebd935..6eb08b7 100644 --- a/include/tuner_fc2580.h +++ b/include/tuner_fc2580.h @@ -1,9 +1,9 @@ #ifndef __TUNER_FC2580_H #define __TUNER_FC2580_H -#define BORDER_FREQ 2600000 //2.6GHz : The border frequency which determines whether Low VCO or High VCO is used -#define USE_EXT_CLK 0 //0 : Use internal XTAL Oscillator / 1 : Use External Clock input -#define OFS_RSSI 57 +#define BORDER_FREQ 2600000 /* 2.6GHz : The border frequency which determines whether Low VCO or High VCO is used */ +#define USE_EXT_CLK 0 /* 0 : Use internal XTAL Oscillator / 1 : Use External Clock input */ +#define OFS_RSSI 57 #define FC2580_I2C_ADDR 0xac #define FC2580_CHECK_ADDR 0x01 diff --git a/include/tuner_r82xx.h b/include/tuner_r82xx.h old mode 100644 new mode 100755 index f6c206a..a940731 --- a/include/tuner_r82xx.h +++ b/include/tuner_r82xx.h @@ -35,11 +35,11 @@ #define R82XX_IF_FREQ 3570000 #define REG_SHADOW_START 5 -#define NUM_REGS 30 -#define NUM_IMR 5 -#define IMR_TRIAL 9 +#define NUM_REGS 30 +#define NUM_IMR 5 +#define IMR_TRIAL 9 -#define VER_NUM 49 +#define VER_NUM 49 enum r82xx_chip { CHIP_R820T, @@ -75,23 +75,21 @@ struct r82xx_config { struct r82xx_priv { struct r82xx_config *cfg; - uint8_t regs[NUM_REGS]; - uint8_t buf[NUM_REGS + 1]; + uint8_t regs[NUM_REGS]; + uint8_t buf[NUM_REGS + 1]; enum r82xx_xtal_cap_value xtal_cap_sel; - uint16_t pll; /* kHz */ - uint32_t int_freq; - uint8_t fil_cal_code; - uint8_t input; - int has_lock; - int init_done; + uint16_t pll; /* kHz */ + uint32_t int_freq; + uint8_t fil_cal_code; + uint8_t input; + int has_lock; + int init_done; /* Store current mode */ - uint32_t delsys; - enum r82xx_tuner_type type; - - uint32_t bw; /* in MHz */ - - void *rtl_dev; + uint32_t delsys; + enum r82xx_tuner_type type; + uint32_t bw; /* in MHz */ + void *rtl_dev; }; struct r82xx_freq_range { @@ -114,7 +112,9 @@ enum r82xx_delivery_system { int r82xx_standby(struct r82xx_priv *priv); int r82xx_init(struct r82xx_priv *priv); int r82xx_set_freq(struct r82xx_priv *priv, uint32_t freq); -int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain); -int r82xx_set_bandwidth(struct r82xx_priv *priv, int bandwidth, uint32_t rate); +//int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain); +int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain, int extended_mode, int lna_gain, int mixer_gain, int vga_gain); + +int r82xx_set_bandwidth(struct r82xx_priv *priv, int bandwidth, uint32_t rate, uint32_t * applied_bw, int apply); #endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 07d64ab..1d89acb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,6 +21,7 @@ MACRO(RTLSDR_APPEND_SRCS) LIST(APPEND rtlsdr_srcs ${ARGV}) ENDMACRO(RTLSDR_APPEND_SRCS) +if(NOT WIN32) RTLSDR_APPEND_SRCS( librtlsdr.c tuner_e4k.c @@ -28,7 +29,19 @@ RTLSDR_APPEND_SRCS( tuner_fc0013.c tuner_fc2580.c tuner_r82xx.c + rtlsdr_rpc.c + rtlsdr_rpc_msg.c ) +else() +RTLSDR_APPEND_SRCS( + librtlsdr.c + tuner_e4k.c + tuner_fc0012.c + tuner_fc0013.c + tuner_fc2580.c + tuner_r82xx.c +) +endif() ######################################################################## # Set up Windows DLL resource files @@ -88,10 +101,16 @@ add_executable(rtl_sdr rtl_sdr.c) add_executable(rtl_tcp rtl_tcp.c) add_executable(rtl_test rtl_test.c) add_executable(rtl_fm rtl_fm.c) +add_executable(rtl_ir rtl_ir.c) add_executable(rtl_eeprom rtl_eeprom.c) add_executable(rtl_adsb rtl_adsb.c) add_executable(rtl_power rtl_power.c) -set(INSTALL_TARGETS rtlsdr_shared rtlsdr_static rtl_sdr rtl_tcp rtl_test rtl_fm rtl_eeprom rtl_adsb rtl_power) +if (NOT WIN32) + add_executable(rtl_rpcd rtl_rpcd.c rtlsdr_rpc_msg.c) + set(INSTALL_TARGETS rtlsdr_shared rtlsdr_static rtl_sdr rtl_tcp rtl_test rtl_fm rtl_ir rtl_eeprom rtl_adsb rtl_power rtl_rpcd) +else() + set(INSTALL_TARGETS rtlsdr_shared rtlsdr_static rtl_sdr rtl_tcp rtl_test rtl_fm rtl_ir rtl_eeprom rtl_adsb rtl_power) +endif() target_link_libraries(rtl_sdr rtlsdr_shared convenience_static ${LIBUSB_LIBRARIES} @@ -109,6 +128,10 @@ target_link_libraries(rtl_fm rtlsdr_shared convenience_static ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) +target_link_libraries(rtl_ir rtlsdr_shared convenience_static + ${LIBUSB_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} +) target_link_libraries(rtl_eeprom rtlsdr_shared convenience_static ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} @@ -121,8 +144,16 @@ target_link_libraries(rtl_power rtlsdr_shared convenience_static ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) +if(NOT WIN32) +target_link_libraries(rtl_rpcd rtlsdr_shared convenience_static + ${LIBUSB_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} +) +endif() + if(UNIX) target_link_libraries(rtl_fm m) +target_link_libraries(rtl_ir m) target_link_libraries(rtl_adsb m) target_link_libraries(rtl_power m) if(APPLE) @@ -137,16 +168,20 @@ target_link_libraries(rtl_sdr libgetopt_static) target_link_libraries(rtl_tcp ws2_32 libgetopt_static) target_link_libraries(rtl_test libgetopt_static) target_link_libraries(rtl_fm libgetopt_static) +target_link_libraries(rtl_ir libgetopt_static) target_link_libraries(rtl_eeprom libgetopt_static) target_link_libraries(rtl_adsb libgetopt_static) target_link_libraries(rtl_power libgetopt_static) +#target_link_libraries(rtl_rpcd ws2_32 libgetopt_static) set_property(TARGET rtl_sdr APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_tcp APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_test APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_fm APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) +set_property(TARGET rtl_ir APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_eeprom APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_adsb APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_power APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) +#set_property(TARGET rtl_rpcd APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) endif() ######################################################################## # Install built library files & utilities diff --git a/src/Makefile.am b/src/Makefile.am index 200990a..cdd0d1c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,10 +9,10 @@ AM_CFLAGS = ${CFLAGS} -fPIC ${SYMBOL_VISIBILITY} lib_LTLIBRARIES = librtlsdr.la -librtlsdr_la_SOURCES = librtlsdr.c tuner_e4k.c tuner_fc0012.c tuner_fc0013.c tuner_fc2580.c tuner_r82xx.c +librtlsdr_la_SOURCES = librtlsdr.c tuner_e4k.c tuner_fc0012.c tuner_fc0013.c tuner_fc2580.c tuner_r82xx.c rtlsdr_rpc.c rtlsdr_rpc_msg.c librtlsdr_la_LDFLAGS = -version-info $(LIBVERSION) -bin_PROGRAMS = rtl_sdr rtl_tcp rtl_test rtl_fm rtl_eeprom rtl_adsb rtl_power +bin_PROGRAMS = rtl_sdr rtl_tcp rtl_test rtl_fm rtl_ir rtl_eeprom rtl_adsb rtl_power rtl_rpcd rtl_sdr_SOURCES = rtl_sdr.c convenience/convenience.c rtl_sdr_LDADD = librtlsdr.la @@ -26,6 +26,9 @@ rtl_test_LDADD = librtlsdr.la $(LIBM) rtl_fm_SOURCES = rtl_fm.c convenience/convenience.c rtl_fm_LDADD = librtlsdr.la $(LIBM) +rtl_ir_SOURCES = rtl_ir.c convenience/convenience.c +rtl_ir_LDADD = librtlsdr.la $(LIBM) + rtl_eeprom_SOURCES = rtl_eeprom.c convenience/convenience.c rtl_eeprom_LDADD = librtlsdr.la $(LIBM) @@ -34,3 +37,6 @@ rtl_adsb_LDADD = librtlsdr.la $(LIBM) rtl_power_SOURCES = rtl_power.c convenience/convenience.c rtl_power_LDADD = librtlsdr.la $(LIBM) + +rtl_rpcd_SOURCES = rtl_rpcd.c rtlsdr_rpc_msg.c convenience/convenience.c +rtl_rpcd_LDADD = librtlsdr.la diff --git a/src/convenience/convenience.c b/src/convenience/convenience.c old mode 100644 new mode 100755 index 517dc4e..e234333 --- a/src/convenience/convenience.c +++ b/src/convenience/convenience.c @@ -160,6 +160,25 @@ int verbose_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate) return r; } +int verbose_set_bandwidth(rtlsdr_dev_t *dev, uint32_t bandwidth) +{ + int r; + uint32_t applied_bw = 0; + /* r = rtlsdr_set_tuner_bandwidth(dev, bandwidth); */ + r = rtlsdr_set_and_get_tuner_bandwidth(dev, bandwidth, &applied_bw, 1 /* =apply_bw */); + if (r < 0) { + fprintf(stderr, "WARNING: Failed to set bandwidth.\n"); + } else if (bandwidth > 0) { + if (applied_bw) + fprintf(stderr, "Bandwidth parameter %u Hz resulted in %u Hz.\n", bandwidth, applied_bw); + else + fprintf(stderr, "Set bandwidth parameter %u Hz.\n", bandwidth); + } else { + fprintf(stderr, "Bandwidth set to automatic resulted in %u Hz.\n", applied_bw); + } + return r; +} + int verbose_direct_sampling(rtlsdr_dev_t *dev, int on) { int r; @@ -182,7 +201,12 @@ int verbose_offset_tuning(rtlsdr_dev_t *dev) int r; r = rtlsdr_set_offset_tuning(dev, 1); if (r != 0) { - fprintf(stderr, "WARNING: Failed to set offset tuning.\n"); + if ( r == -2 ) + fprintf(stderr, "WARNING: Failed to set offset tuning: tuner doesn't support offset tuning!\n"); + else if ( r == -3 ) + fprintf(stderr, "WARNING: Failed to set offset tuning: direct sampling not combinable with offset tuning!\n"); + else + fprintf(stderr, "WARNING: Failed to set offset tuning.\n"); } else { fprintf(stderr, "Offset tuning mode enabled.\n"); } diff --git a/src/convenience/convenience.h b/src/convenience/convenience.h index 1faa2af..d3b46dc 100644 --- a/src/convenience/convenience.h +++ b/src/convenience/convenience.h @@ -14,6 +14,9 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#ifndef __CONVENIENCE_H +#define __CONVENIENCE_H + /* a collection of user friendly tools */ @@ -74,6 +77,17 @@ int verbose_set_frequency(rtlsdr_dev_t *dev, uint32_t frequency); int verbose_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate); +/*! + * Set device bandwidth and report status on stderr + * + * \param dev the device handle given by rtlsdr_open() + * \param frequency in Hz + * \return 0 on success + */ + +int verbose_set_bandwidth(rtlsdr_dev_t *dev, uint32_t bandwidth); + + /*! * Enable or disable the direct sampling mode and report status on stderr * @@ -140,3 +154,4 @@ int verbose_reset_buffer(rtlsdr_dev_t *dev); int verbose_device_search(char *s); +#endif /*__CONVENIENCE_H*/ diff --git a/src/librtlsdr.c b/src/librtlsdr.c index 0650606..258cf03 100644 --- a/src/librtlsdr.c +++ b/src/librtlsdr.c @@ -10,11 +10,11 @@ * * 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 + * 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 . + * along with this program. If not, see . */ #include @@ -28,7 +28,9 @@ #endif #include - +#ifndef WIN32 +#include "rtlsdr_rpc.h" +#endif /* * All libusb callback functions should be marked with the LIBUSB_CALL macro * to ensure that they are compiled with the same calling convention as libusb. @@ -60,7 +62,7 @@ typedef struct rtlsdr_tuner_iface { int (*init)(void *); int (*exit)(void *); int (*set_freq)(void *, uint32_t freq /* Hz */); - int (*set_bw)(void *, int bw /* Hz */); + int (*set_bw)(void *, int bw /* Hz */, uint32_t *applied_bw /* configured bw in Hz */, int apply /* 1 == configure it!, 0 == deliver applied_bw */); int (*set_gain)(void *, int gain /* tenth dB */); int (*set_if_gain)(void *, int stage, int gain /* tenth dB */); int (*set_gain_mode)(void *, int manual); @@ -124,6 +126,7 @@ struct rtlsdr_dev { int dev_lost; int driver_active; unsigned int xfer_errors; + int rc_active; }; void rtlsdr_set_gpio_bit(rtlsdr_dev_t *dev, uint8_t gpio, int val); @@ -146,9 +149,11 @@ int e4000_set_freq(void *dev, uint32_t freq) { return e4k_tune_freq(&devt->e4k_s, freq); } -int e4000_set_bw(void *dev, int bw) { +int e4000_set_bw(void *dev, int bw, uint32_t *applied_bw, int apply) { int r = 0; rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + if(!apply) + return 0; r |= e4k_if_filter_bw_set(&devt->e4k_s, E4K_IF_FILTER_MIX, bw); r |= e4k_if_filter_bw_set(&devt->e4k_s, E4K_IF_FILTER_RC, bw); @@ -190,7 +195,7 @@ int fc0012_set_freq(void *dev, uint32_t freq) { rtlsdr_set_gpio_bit(dev, 6, (freq > 300000000) ? 1 : 0); return fc0012_set_params(dev, freq, 6000000); } -int fc0012_set_bw(void *dev, int bw) { return 0; } +int fc0012_set_bw(void *dev, int bw, uint32_t *applied_bw, int apply) { return 0; } int _fc0012_set_gain(void *dev, int gain) { return fc0012_set_gain(dev, gain); } int fc0012_set_gain_mode(void *dev, int manual) { return 0; } @@ -199,7 +204,7 @@ int fc0013_exit(void *dev) { return 0; } int fc0013_set_freq(void *dev, uint32_t freq) { return fc0013_set_params(dev, freq, 6000000); } -int fc0013_set_bw(void *dev, int bw) { return 0; } +int fc0013_set_bw(void *dev, int bw, uint32_t *applied_bw, int apply) { return 0; } int _fc0013_set_gain(void *dev, int gain) { return fc0013_set_lna_gain(dev, gain); } int fc2580_init(void *dev) { return fc2580_Initialize(dev); } @@ -207,7 +212,11 @@ int fc2580_exit(void *dev) { return 0; } int _fc2580_set_freq(void *dev, uint32_t freq) { return fc2580_SetRfFreqHz(dev, freq); } -int fc2580_set_bw(void *dev, int bw) { return fc2580_SetBandwidthMode(dev, 1); } +int fc2580_set_bw(void *dev, int bw, uint32_t *applied_bw, int apply) { + if(!apply) + return 0; + return fc2580_SetBandwidthMode(dev, 1); +} int fc2580_set_gain(void *dev, int gain) { return 0; } int fc2580_set_gain_mode(void *dev, int manual) { return 0; } @@ -241,13 +250,16 @@ int r820t_set_freq(void *dev, uint32_t freq) { return r82xx_set_freq(&devt->r82xx_p, freq); } -int r820t_set_bw(void *dev, int bw) { +int r820t_set_bw(void *dev, int bw, uint32_t *applied_bw, int apply) { int r; rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; - r = r82xx_set_bandwidth(&devt->r82xx_p, bw, devt->rate); + r = r82xx_set_bandwidth(&devt->r82xx_p, bw, devt->rate, applied_bw, apply); + if(!apply) + return 0; if(r < 0) - return r; + return r; + r = rtlsdr_set_if_freq(devt, r); if (r) return r; @@ -256,11 +268,17 @@ int r820t_set_bw(void *dev, int bw) { int r820t_set_gain(void *dev, int gain) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; - return r82xx_set_gain(&devt->r82xx_p, 1, gain); + return r82xx_set_gain(&devt->r82xx_p, 1, gain, 0, 0, 0, 0); +} + +int r820t_set_gain_ext(void *dev, int lna_gain, int mixer_gain, int vga_gain) { + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + return r82xx_set_gain(&devt->r82xx_p, 0, 0, 1, lna_gain, mixer_gain, vga_gain); } + int r820t_set_gain_mode(void *dev, int manual) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; - return r82xx_set_gain(&devt->r82xx_p, manual, 0); + return r82xx_set_gain(&devt->r82xx_p, manual, 0, 0, 0, 0, 0); } /* definition order must match enum rtlsdr_tuner */ @@ -360,19 +378,19 @@ static rtlsdr_dongle_t known_devices[] = { #define MIN_RTL_XTAL_FREQ (DEF_RTL_XTAL_FREQ - 1000) #define MAX_RTL_XTAL_FREQ (DEF_RTL_XTAL_FREQ + 1000) -#define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN) -#define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT) +#define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN) +#define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT) #define CTRL_TIMEOUT 300 #define BULK_TIMEOUT 0 #define EEPROM_ADDR 0xa0 enum usb_reg { - USB_SYSCTL = 0x2000, - USB_CTRL = 0x2010, - USB_STAT = 0x2014, - USB_EPA_CFG = 0x2144, - USB_EPA_CTL = 0x2148, + USB_SYSCTL = 0x2000, + USB_CTRL = 0x2010, + USB_STAT = 0x2014, + USB_EPA_CFG = 0x2144, + USB_EPA_CTL = 0x2148, USB_EPA_MAXPKT = 0x2158, USB_EPA_MAXPKT_2 = 0x215a, USB_EPA_FIFO_CFG = 0x2160, @@ -380,10 +398,10 @@ enum usb_reg { enum sys_reg { DEMOD_CTL = 0x3000, - GPO = 0x3001, - GPI = 0x3002, + GPO = 0x3001, + GPI = 0x3002, GPOE = 0x3003, - GPD = 0x3004, + GPD = 0x3004, SYSINTE = 0x3005, SYSINTS = 0x3006, GP_CFG0 = 0x3007, @@ -392,6 +410,51 @@ enum sys_reg { SYSINTS_1 = 0x300a, DEMOD_CTL_1 = 0x300b, IR_SUSPEND = 0x300c, + + /* IrDA registers */ + SYS_IRRC_PSR = 0x3020, /* IR protocol selection */ + SYS_IRRC_PER = 0x3024, /* IR protocol extension */ + SYS_IRRC_SF = 0x3028, /* IR sampling frequency */ + SYS_IRRC_DPIR = 0x302C, /* IR data package interval */ + SYS_IRRC_CR = 0x3030, /* IR control */ + SYS_IRRC_RP = 0x3034, /* IR read port */ + SYS_IRRC_SR = 0x3038, /* IR status */ + /* I2C master registers */ + SYS_I2CCR = 0x3040, /* I2C clock */ + SYS_I2CMCR = 0x3044, /* I2C master control */ + SYS_I2CMSTR = 0x3048, /* I2C master SCL timing */ + SYS_I2CMSR = 0x304C, /* I2C master status */ + SYS_I2CMFR = 0x3050, /* I2C master FIFO */ + + /* + * IR registers + */ + IR_RX_BUF = 0xFC00, + IR_RX_IE = 0xFD00, + IR_RX_IF = 0xFD01, + IR_RX_CTRL = 0xFD02, + IR_RX_CFG = 0xFD03, + IR_MAX_DURATION0 = 0xFD04, + IR_MAX_DURATION1 = 0xFD05, + IR_IDLE_LEN0 = 0xFD06, + IR_IDLE_LEN1 = 0xFD07, + IR_GLITCH_LEN = 0xFD08, + IR_RX_BUF_CTRL = 0xFD09, + IR_RX_BUF_DATA = 0xFD0A, + IR_RX_BC = 0xFD0B, + IR_RX_CLK = 0xFD0C, + IR_RX_C_COUNT_L = 0xFD0D, + IR_RX_C_COUNT_H = 0xFD0E, + IR_SUSPEND_CTRL = 0xFD10, + IR_ERR_TOL_CTRL = 0xFD11, + IR_UNIT_LEN = 0xFD12, + IR_ERR_TOL_LEN = 0xFD13, + IR_MAX_H_TOL_LEN = 0xFD14, + IR_MAX_L_TOL_LEN = 0xFD15, + IR_MASK_CTRL = 0xFD16, + IR_MASK_DATA = 0xFD17, + IR_RES_MASK_ADDR = 0xFD18, + IR_RES_MASK_T_LEN = 0xFD19, }; enum blocks { @@ -400,7 +463,7 @@ enum blocks { SYSB = 2, TUNB = 3, ROMB = 4, - IRB = 5, + IRB = 5, IICB = 6, }; @@ -408,6 +471,7 @@ int rtlsdr_read_array(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_t * { int r; uint16_t index = (block << 8); + if (block == IRB) index = (SYSB << 8) | 0x01; r = libusb_control_transfer(dev->devh, CTRL_IN, 0, addr, index, array, len, CTRL_TIMEOUT); #if 0 @@ -421,6 +485,7 @@ int rtlsdr_write_array(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_t { int r; uint16_t index = (block << 8) | 0x10; + if (block == IRB) index = (SYSB << 8) | 0x11; r = libusb_control_transfer(dev->devh, CTRL_OUT, 0, addr, index, array, len, CTRL_TIMEOUT); #if 0 @@ -475,8 +540,9 @@ uint16_t rtlsdr_read_reg(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_ { int r; unsigned char data[2]; - uint16_t index = (block << 8); uint16_t reg; + uint16_t index = (block << 8); + if (block == IRB) index = (SYSB << 8) | 0x01; r = libusb_control_transfer(dev->devh, CTRL_IN, 0, addr, index, data, len, CTRL_TIMEOUT); @@ -494,6 +560,7 @@ int rtlsdr_write_reg(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint16_t v unsigned char data[2]; uint16_t index = (block << 8) | 0x10; + if (block == IRB) index = (SYSB << 8) | 0x11; if (len == 1) data[0] = val & 0xff; @@ -633,7 +700,7 @@ void rtlsdr_init_baseband(rtlsdr_dev_t *dev) rtlsdr_demod_write_reg(dev, 1, 0x15, 0x00, 1); rtlsdr_demod_write_reg(dev, 1, 0x16, 0x0000, 2); - /* clear both DDC shift and IF frequency registers */ + /* clear both DDC shift and IF frequency registers */ for (i = 0; i < 6; i++) rtlsdr_demod_write_reg(dev, 1, 0x16 + i, 0x00, 1); @@ -729,6 +796,13 @@ int rtlsdr_set_xtal_freq(rtlsdr_dev_t *dev, uint32_t rtl_freq, uint32_t tuner_fr { int r = 0; + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_xtal_freq(dev, rtl_freq, tuner_freq); + } + #endif + if (!dev) return -1; @@ -752,7 +826,7 @@ int rtlsdr_set_xtal_freq(rtlsdr_dev_t *dev, uint32_t rtl_freq, uint32_t tuner_fr /* read corrected clock value into e4k and r82xx structure */ if (rtlsdr_get_xtal_freq(dev, NULL, &dev->e4k_s.vco.fosc) || - rtlsdr_get_xtal_freq(dev, NULL, &dev->r82xx_c.xtal)) + rtlsdr_get_xtal_freq(dev, NULL, &dev->r82xx_c.xtal)) return -3; /* update xtal-dependent settings */ @@ -765,6 +839,13 @@ int rtlsdr_set_xtal_freq(rtlsdr_dev_t *dev, uint32_t rtl_freq, uint32_t tuner_fr int rtlsdr_get_xtal_freq(rtlsdr_dev_t *dev, uint32_t *rtl_freq, uint32_t *tuner_freq) { + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_xtal_freq(dev, rtl_freq, tuner_freq); + } + #endif + if (!dev) return -1; @@ -780,13 +861,20 @@ int rtlsdr_get_xtal_freq(rtlsdr_dev_t *dev, uint32_t *rtl_freq, uint32_t *tuner_ } int rtlsdr_get_usb_strings(rtlsdr_dev_t *dev, char *manufact, char *product, - char *serial) + char *serial) { struct libusb_device_descriptor dd; libusb_device *device = NULL; const int buf_max = 256; int r = 0; + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_usb_strings(dev, manufact, product, serial); + } + #endif + if (!dev || !dev->devh) return -1; @@ -799,22 +887,22 @@ int rtlsdr_get_usb_strings(rtlsdr_dev_t *dev, char *manufact, char *product, if (manufact) { memset(manufact, 0, buf_max); libusb_get_string_descriptor_ascii(dev->devh, dd.iManufacturer, - (unsigned char *)manufact, - buf_max); + (unsigned char *)manufact, + buf_max); } if (product) { memset(product, 0, buf_max); libusb_get_string_descriptor_ascii(dev->devh, dd.iProduct, - (unsigned char *)product, - buf_max); + (unsigned char *)product, + buf_max); } if (serial) { memset(serial, 0, buf_max); libusb_get_string_descriptor_ascii(dev->devh, dd.iSerialNumber, - (unsigned char *)serial, - buf_max); + (unsigned char *)serial, + buf_max); } return 0; @@ -826,6 +914,13 @@ int rtlsdr_write_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16 int i; uint8_t cmd[2]; + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_write_eeprom(dev, data, offset, len); + } + #endif + if (!dev) return -1; @@ -862,6 +957,12 @@ int rtlsdr_read_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16_ { int r = 0; int i; + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_read_eeprom(dev, data, offset, len); + } + #endif if (!dev) return -1; @@ -886,7 +987,12 @@ int rtlsdr_read_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16_ int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq) { int r = -1; - + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_center_freq(dev, freq); + } + #endif if (!dev || !dev->tuner) return -1; @@ -908,6 +1014,13 @@ int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq) uint32_t rtlsdr_get_center_freq(rtlsdr_dev_t *dev) { + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_center_freq(dev); + } + #endif + if (!dev) return 0; @@ -917,6 +1030,12 @@ uint32_t rtlsdr_get_center_freq(rtlsdr_dev_t *dev) int rtlsdr_set_freq_correction(rtlsdr_dev_t *dev, int ppm) { int r = 0; + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_freq_correction(dev, ppm); + } + #endif if (!dev) return -1; @@ -930,7 +1049,7 @@ int rtlsdr_set_freq_correction(rtlsdr_dev_t *dev, int ppm) /* read corrected clock value into e4k and r82xx structure */ if (rtlsdr_get_xtal_freq(dev, NULL, &dev->e4k_s.vco.fosc) || - rtlsdr_get_xtal_freq(dev, NULL, &dev->r82xx_c.xtal)) + rtlsdr_get_xtal_freq(dev, NULL, &dev->r82xx_c.xtal)) return -3; if (dev->freq) /* retune to apply new correction value */ @@ -941,6 +1060,13 @@ int rtlsdr_set_freq_correction(rtlsdr_dev_t *dev, int ppm) int rtlsdr_get_freq_correction(rtlsdr_dev_t *dev) { + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_freq_correction(dev); + } + #endif + if (!dev) return 0; @@ -949,6 +1075,13 @@ int rtlsdr_get_freq_correction(rtlsdr_dev_t *dev) enum rtlsdr_tuner rtlsdr_get_tuner_type(rtlsdr_dev_t *dev) { + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return (enum rtlsdr_tuner)rtlsdr_rpc_get_tuner_type(dev); + } + #endif + if (!dev) return RTLSDR_TUNER_UNKNOWN; @@ -959,21 +1092,28 @@ int rtlsdr_get_tuner_gains(rtlsdr_dev_t *dev, int *gains) { /* all gain values are expressed in tenths of a dB */ const int e4k_gains[] = { -10, 15, 40, 65, 90, 115, 140, 165, 190, 215, - 240, 290, 340, 420 }; + 240, 290, 340, 420 }; const int fc0012_gains[] = { -99, -40, 71, 179, 192 }; const int fc0013_gains[] = { -99, -73, -65, -63, -60, -58, -54, 58, 61, - 63, 65, 67, 68, 70, 71, 179, 181, 182, - 184, 186, 188, 191, 197 }; + 63, 65, 67, 68, 70, 71, 179, 181, 182, + 184, 186, 188, 191, 197 }; const int fc2580_gains[] = { 0 /* no gain values */ }; const int r82xx_gains[] = { 0, 9, 14, 27, 37, 77, 87, 125, 144, 157, - 166, 197, 207, 229, 254, 280, 297, 328, - 338, 364, 372, 386, 402, 421, 434, 439, - 445, 480, 496 }; + 166, 197, 207, 229, 254, 280, 297, 328, + 338, 364, 372, 386, 402, 421, 434, 439, + 445, 480, 496 }; const int unknown_gains[] = { 0 /* no gain values */ }; const int *ptr = NULL; int len = 0; + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_tuner_gains(dev, gains); + } + #endif + if (!dev) return -1; @@ -1009,16 +1149,26 @@ int rtlsdr_get_tuner_gains(rtlsdr_dev_t *dev, int *gains) } } -int rtlsdr_set_tuner_bandwidth(rtlsdr_dev_t *dev, uint32_t bw) +int rtlsdr_set_and_get_tuner_bandwidth(rtlsdr_dev_t *dev, uint32_t bw, uint32_t *applied_bw, int apply_bw ) { int r = 0; + *applied_bw = 0; /* unknown */ + if (!dev || !dev->tuner) return -1; + if(!apply_bw) + { + if (dev->tuner->set_bw) { + r = dev->tuner->set_bw(dev, bw > 0 ? bw : dev->rate, applied_bw, apply_bw); + } + return r; + } + if (dev->tuner->set_bw) { rtlsdr_set_i2c_repeater(dev, 1); - r = dev->tuner->set_bw(dev, bw > 0 ? bw : dev->rate); + r = dev->tuner->set_bw(dev, bw > 0 ? bw : dev->rate, applied_bw, apply_bw); rtlsdr_set_i2c_repeater(dev, 0); if (r) return r; @@ -1027,10 +1177,25 @@ int rtlsdr_set_tuner_bandwidth(rtlsdr_dev_t *dev, uint32_t bw) return r; } +int rtlsdr_set_tuner_bandwidth(rtlsdr_dev_t *dev, uint32_t bw ) +{ + uint32_t applied_bw = 0; + return rtlsdr_set_and_get_tuner_bandwidth(dev, bw, &applied_bw, 1 /* =apply_bw */ ); +} + + + int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain) { int r = 0; + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_tuner_gain(dev, gain); + } + #endif + if (!dev || !dev->tuner) return -1; @@ -1048,8 +1213,36 @@ int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain) return r; } +int rtlsdr_set_tuner_gain_ext(rtlsdr_dev_t *dev, int lna_gain, int mixer_gain, int vga_gain) +{ + int r = 0; + + if (!dev || !dev->tuner) + return -1; + + if (dev->tuner->set_gain) { + rtlsdr_set_i2c_repeater(dev, 1); + r = r820t_set_gain_ext((void *)dev, lna_gain, mixer_gain, vga_gain); + rtlsdr_set_i2c_repeater(dev, 0); + } + + if (!r) + dev->gain = lna_gain + mixer_gain + vga_gain; + else + dev->gain = 0; + + return r; +} + int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev) { + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_tuner_gain(dev); + } + #endif + if (!dev) return 0; @@ -1060,6 +1253,13 @@ int rtlsdr_set_tuner_if_gain(rtlsdr_dev_t *dev, int stage, int gain) { int r = 0; + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_tuner_if_gain(dev, stage, gain); + } + #endif + if (!dev || !dev->tuner) return -1; @@ -1076,6 +1276,13 @@ int rtlsdr_set_tuner_gain_mode(rtlsdr_dev_t *dev, int mode) { int r = 0; + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_tuner_gain_mode(dev, mode); + } + #endif + if (!dev || !dev->tuner) return -1; @@ -1095,12 +1302,19 @@ int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate) uint32_t rsamp_ratio, real_rsamp_ratio; double real_rate; + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_sample_rate(dev, samp_rate); + } + #endif + if (!dev) return -1; /* check if the rate is supported by the resampler */ if ((samp_rate <= 225000) || (samp_rate > 3200000) || - ((samp_rate > 300000) && (samp_rate <= 900000))) { + ((samp_rate > 300000) && (samp_rate <= 900000))) { fprintf(stderr, "Invalid sample rate: %u Hz\n", samp_rate); return -EINVAL; } @@ -1117,8 +1331,9 @@ int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate) dev->rate = (uint32_t)real_rate; if (dev->tuner && dev->tuner->set_bw) { + uint32_t applied_bw = 0; rtlsdr_set_i2c_repeater(dev, 1); - dev->tuner->set_bw(dev, dev->bw > 0 ? dev->bw : dev->rate); + dev->tuner->set_bw(dev, dev->bw > 0 ? dev->bw : dev->rate, &applied_bw, 1); rtlsdr_set_i2c_repeater(dev, 0); } @@ -1142,6 +1357,13 @@ int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate) uint32_t rtlsdr_get_sample_rate(rtlsdr_dev_t *dev) { + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_sample_rate(dev); + } + #endif + if (!dev) return 0; @@ -1150,6 +1372,13 @@ uint32_t rtlsdr_get_sample_rate(rtlsdr_dev_t *dev) int rtlsdr_set_testmode(rtlsdr_dev_t *dev, int on) { + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_testmode(dev, on); + } + #endif + if (!dev) return -1; @@ -1158,6 +1387,13 @@ int rtlsdr_set_testmode(rtlsdr_dev_t *dev, int on) int rtlsdr_set_agc_mode(rtlsdr_dev_t *dev, int on) { + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_agc_mode(dev, on); + } + #endif + if (!dev) return -1; @@ -1168,6 +1404,13 @@ int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on) { int r = 0; + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_direct_sampling(dev, on); + } + #endif + if (!dev) return -1; @@ -1200,7 +1443,7 @@ int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on) } if ((dev->tuner_type == RTLSDR_TUNER_R820T) || - (dev->tuner_type == RTLSDR_TUNER_R828D)) { + (dev->tuner_type == RTLSDR_TUNER_R828D)) { r |= rtlsdr_set_if_freq(dev, R82XX_IF_FREQ); /* enable spectrum inversion */ @@ -1229,6 +1472,13 @@ int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on) int rtlsdr_get_direct_sampling(rtlsdr_dev_t *dev) { + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_direct_sampling(dev); + } + #endif + if (!dev) return -1; @@ -1240,11 +1490,18 @@ int rtlsdr_set_offset_tuning(rtlsdr_dev_t *dev, int on) int r = 0; int bw; + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_offset_tuning(dev, on); + } + #endif + if (!dev) return -1; if ((dev->tuner_type == RTLSDR_TUNER_R820T) || - (dev->tuner_type == RTLSDR_TUNER_R828D)) + (dev->tuner_type == RTLSDR_TUNER_R828D)) return -2; if (dev->direct_sampling) @@ -1255,6 +1512,7 @@ int rtlsdr_set_offset_tuning(rtlsdr_dev_t *dev, int on) r |= rtlsdr_set_if_freq(dev, dev->offs_freq); if (dev->tuner && dev->tuner->set_bw) { + uint32_t applied_bw = 0; rtlsdr_set_i2c_repeater(dev, 1); if (on) { bw = 2 * dev->offs_freq; @@ -1263,7 +1521,7 @@ int rtlsdr_set_offset_tuning(rtlsdr_dev_t *dev, int on) } else { bw = dev->rate; } - dev->tuner->set_bw(dev, bw); + dev->tuner->set_bw(dev, bw, &applied_bw, 1); rtlsdr_set_i2c_repeater(dev, 0); } @@ -1275,6 +1533,13 @@ int rtlsdr_set_offset_tuning(rtlsdr_dev_t *dev, int on) int rtlsdr_get_offset_tuning(rtlsdr_dev_t *dev) { + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_offset_tuning(dev); + } + #endif + if (!dev) return -1; @@ -1298,14 +1563,23 @@ static rtlsdr_dongle_t *find_known_device(uint16_t vid, uint16_t pid) uint32_t rtlsdr_get_device_count(void) { - int i; + int i,r; libusb_context *ctx; libusb_device **list; uint32_t device_count = 0; struct libusb_device_descriptor dd; ssize_t cnt; - libusb_init(&ctx); + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_device_count(); + } + #endif + + r = libusb_init(&ctx); + if(r < 0) + return 0; cnt = libusb_get_device_list(ctx, &list); @@ -1325,7 +1599,7 @@ uint32_t rtlsdr_get_device_count(void) const char *rtlsdr_get_device_name(uint32_t index) { - int i; + int i,r; libusb_context *ctx; libusb_device **list; struct libusb_device_descriptor dd; @@ -1333,7 +1607,16 @@ const char *rtlsdr_get_device_name(uint32_t index) uint32_t device_count = 0; ssize_t cnt; - libusb_init(&ctx); + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_device_name(index); + } + #endif + + r = libusb_init(&ctx); + if(r < 0) + return ""; cnt = libusb_get_device_list(ctx, &list); @@ -1361,7 +1644,7 @@ const char *rtlsdr_get_device_name(uint32_t index) } int rtlsdr_get_device_usb_strings(uint32_t index, char *manufact, - char *product, char *serial) + char *product, char *serial) { int r = -2; int i; @@ -1373,7 +1656,17 @@ int rtlsdr_get_device_usb_strings(uint32_t index, char *manufact, uint32_t device_count = 0; ssize_t cnt; - libusb_init(&ctx); + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_device_usb_strings + (index, manufact, product, serial); + } + #endif + + r = libusb_init(&ctx); + if(r < 0) + return r; cnt = libusb_get_device_list(ctx, &list); @@ -1389,9 +1682,9 @@ int rtlsdr_get_device_usb_strings(uint32_t index, char *manufact, r = libusb_open(list[i], &devt.devh); if (!r) { r = rtlsdr_get_usb_strings(&devt, - manufact, - product, - serial); + manufact, + product, + serial); libusb_close(devt.devh); } break; @@ -1411,6 +1704,13 @@ int rtlsdr_get_index_by_serial(const char *serial) int i, cnt, r; char str[256]; + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_index_by_serial(serial); + } + #endif + if (!serial) return -1; @@ -1440,6 +1740,13 @@ int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index) uint8_t reg; ssize_t cnt; + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_open((void**)out_dev, index); + } + #endif + dev = malloc(sizeof(rtlsdr_dev_t)); if (NULL == dev) return -ENOMEM; @@ -1447,7 +1754,11 @@ int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index) memset(dev, 0, sizeof(rtlsdr_dev_t)); memcpy(dev->fir, fir_default, sizeof(fir_default)); - libusb_init(&dev->ctx); + r = libusb_init(&dev->ctx); + if(r < 0){ + free(dev); + return -1; + } dev->dev_lost = 1; @@ -1626,6 +1937,13 @@ int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index) int rtlsdr_close(rtlsdr_dev_t *dev) { + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_close(dev); + } + #endif + if (!dev) return -1; @@ -1664,6 +1982,13 @@ int rtlsdr_close(rtlsdr_dev_t *dev) int rtlsdr_reset_buffer(rtlsdr_dev_t *dev) { + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_reset_buffer(dev); + } + #endif + if (!dev) return -1; @@ -1675,6 +2000,13 @@ int rtlsdr_reset_buffer(rtlsdr_dev_t *dev) int rtlsdr_read_sync(rtlsdr_dev_t *dev, void *buf, int len, int *n_read) { + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_read_sync(dev, buf, len, n_read); + } + #endif + if (!dev) return -1; @@ -1697,7 +2029,7 @@ static void LIBUSB_CALL _libusb_callback(struct libusb_transfer *xfer) dev->xfer_errors++; if (dev->xfer_errors >= dev->xfer_buf_num || - LIBUSB_TRANSFER_NO_DEVICE == xfer->status) { + LIBUSB_TRANSFER_NO_DEVICE == xfer->status) { #endif dev->dev_lost = 1; rtlsdr_cancel_async(dev); @@ -1711,6 +2043,13 @@ static void LIBUSB_CALL _libusb_callback(struct libusb_transfer *xfer) int rtlsdr_wait_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx) { + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_wait_async(dev, cb, ctx); + } + #endif + return rtlsdr_read_async(dev, cb, ctx, 0, 0); } @@ -1723,7 +2062,7 @@ static int _rtlsdr_alloc_async_buffers(rtlsdr_dev_t *dev) if (!dev->xfer) { dev->xfer = malloc(dev->xfer_buf_num * - sizeof(struct libusb_transfer *)); + sizeof(struct libusb_transfer *)); for(i = 0; i < dev->xfer_buf_num; ++i) dev->xfer[i] = libusb_alloc_transfer(0); @@ -1731,7 +2070,7 @@ static int _rtlsdr_alloc_async_buffers(rtlsdr_dev_t *dev) if (!dev->xfer_buf) { dev->xfer_buf = malloc(dev->xfer_buf_num * - sizeof(unsigned char *)); + sizeof(unsigned char *)); for(i = 0; i < dev->xfer_buf_num; ++i) dev->xfer_buf[i] = malloc(dev->xfer_buf_len); @@ -1772,7 +2111,7 @@ static int _rtlsdr_free_async_buffers(rtlsdr_dev_t *dev) } int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, - uint32_t buf_num, uint32_t buf_len) + uint32_t buf_num, uint32_t buf_len) { unsigned int i; int r = 0; @@ -1780,6 +2119,13 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, struct timeval zerotv = { 0, 0 }; enum rtlsdr_async_status next_status = RTLSDR_INACTIVE; + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_read_async(dev, cb, ctx, buf_num, buf_len); + } + #endif + if (!dev) return -1; @@ -1806,13 +2152,13 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, for(i = 0; i < dev->xfer_buf_num; ++i) { libusb_fill_bulk_transfer(dev->xfer[i], - dev->devh, - 0x81, - dev->xfer_buf[i], - dev->xfer_buf_len, - _libusb_callback, - (void *)dev, - BULK_TIMEOUT); + dev->devh, + 0x81, + dev->xfer_buf[i], + dev->xfer_buf_len, + _libusb_callback, + (void *)dev, + BULK_TIMEOUT); r = libusb_submit_transfer(dev->xfer[i]); if (r < 0) { @@ -1824,7 +2170,7 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, while (RTLSDR_INACTIVE != dev->async_status) { r = libusb_handle_events_timeout_completed(dev->ctx, &tv, - &dev->async_cancel); + &dev->async_cancel); if (r < 0) { /*fprintf(stderr, "handle_events returned: %d\n", r);*/ if (r == LIBUSB_ERROR_INTERRUPTED) /* stray signal */ @@ -1849,7 +2195,7 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, * to allow transfer status to * propagate */ libusb_handle_events_timeout_completed(dev->ctx, - &zerotv, NULL); + &zerotv, NULL); if (r < 0) continue; @@ -1862,7 +2208,7 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, * be handled before exiting after we * just cancelled all transfers */ libusb_handle_events_timeout_completed(dev->ctx, - &zerotv, NULL); + &zerotv, NULL); break; } } @@ -1877,6 +2223,13 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, int rtlsdr_cancel_async(rtlsdr_dev_t *dev) { + #ifdef _ENABLE_RPC + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_cancel_async(dev); + } + #endif + if (!dev) return -1; @@ -1926,3 +2279,160 @@ int rtlsdr_i2c_read_fn(void *dev, uint8_t addr, uint8_t *buf, int len) return -1; } + + +/* Infrared (IR) sensor support + * based on Linux dvb_usb_rtl28xxu drivers/media/usb/dvb-usb-v2/rtl28xxu.h + * Copyright (C) 2009 Antti Palosaari + * Copyright (C) 2011 Antti Palosaari + * Copyright (C) 2012 Thomas Mair + */ + +struct rtl28xxu_req { + uint16_t value; + uint16_t index; + uint16_t size; + uint8_t *data; +}; + +struct rtl28xxu_reg_val { + uint16_t reg; + uint8_t val; +}; + +struct rtl28xxu_reg_val_mask { + int block; + uint16_t reg; + uint8_t val; + uint8_t mask; +}; + +static int rtlsdr_read_regs(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_t *data, uint8_t len) +{ + int r; + uint16_t index = (block << 8); + if (block == IRB) index = (SYSB << 8) | 0x01; + + r = libusb_control_transfer(dev->devh, CTRL_IN, 0, addr, index, data, len, CTRL_TIMEOUT); + + if (r < 0) + fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); + + return r; +} + +static int rtlsdr_write_reg_mask(rtlsdr_dev_t *d, int block, uint16_t reg, uint8_t val, + uint8_t mask) +{ + int ret; + uint8_t tmp; + + /* no need for read if whole reg is written */ + if (mask != 0xff) { + tmp = rtlsdr_read_reg(d, block, reg, 1); + + val &= mask; + tmp &= ~mask; + val |= tmp; + } + + return rtlsdr_write_reg(d, block, reg, (uint16_t)val, 1); +} + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +int rtlsdr_ir_query(rtlsdr_dev_t *d, uint8_t *buf, size_t buf_len) +{ + int ret = -1; + size_t i, len; + static const struct rtl28xxu_reg_val_mask refresh_tab[] = { + {IRB, IR_RX_IF, 0x03, 0xff}, + {IRB, IR_RX_BUF_CTRL, 0x80, 0xff}, + {IRB, IR_RX_CTRL, 0x80, 0xff}, + }; + + /* init remote controller */ + if (!d->rc_active) { + //fprintf(stderr, "initializing remote controller\n"); + static const struct rtl28xxu_reg_val_mask init_tab[] = { + {USBB, DEMOD_CTL, 0x00, 0x04}, + {USBB, DEMOD_CTL, 0x00, 0x08}, + {USBB, USB_CTRL, 0x20, 0x20}, + {USBB, GPD, 0x00, 0x08}, + {USBB, GPOE, 0x08, 0x08}, + {USBB, GPO, 0x08, 0x08}, + {IRB, IR_MAX_DURATION0, 0xd0, 0xff}, + {IRB, IR_MAX_DURATION1, 0x07, 0xff}, + {IRB, IR_IDLE_LEN0, 0xc0, 0xff}, + {IRB, IR_IDLE_LEN1, 0x00, 0xff}, + {IRB, IR_GLITCH_LEN, 0x03, 0xff}, + {IRB, IR_RX_CLK, 0x09, 0xff}, + {IRB, IR_RX_CFG, 0x1c, 0xff}, + {IRB, IR_MAX_H_TOL_LEN, 0x1e, 0xff}, + {IRB, IR_MAX_L_TOL_LEN, 0x1e, 0xff}, + {IRB, IR_RX_CTRL, 0x80, 0xff}, + }; + + for (i = 0; i < ARRAY_SIZE(init_tab); i++) { + ret = rtlsdr_write_reg_mask(d, init_tab[i].block, init_tab[i].reg, + init_tab[i].val, init_tab[i].mask); + if (ret < 0) { + fprintf(stderr, "write %d reg %d %.4x %.2x %.2x failed\n", i, init_tab[i].block, + init_tab[i].reg, init_tab[i].val, init_tab[i].mask); + goto err; + } + } + + d->rc_active = 1; + //fprintf(stderr, "rc active\n"); + } + // TODO: option to ir disable + + buf[0] = rtlsdr_read_reg(d, IRB, IR_RX_IF, 1); + + if (buf[0] != 0x83) { + if (buf[0] == 0 || // no IR signal + // also observed: 0x82, 0x81 - with lengths 1, 5, 0.. unknown, sometimes occurs at edges + // "IR not ready"? causes a -7 timeout if we read + buf[0] == 0x82 || buf[0] == 0x81) { + // graceful exit + } else { + fprintf(stderr, "read IR_RX_IF unexpected: %.2x\n", buf[0]); + } + + ret = 0; + goto exit; + } + + buf[0] = rtlsdr_read_reg(d, IRB, IR_RX_BC, 1); + + len = buf[0]; + //fprintf(stderr, "read IR_RX_BC len=%d\n", len); + + if (len > buf_len) { + //fprintf(stderr, "read IR_RX_BC too large for buffer, %lu > %lu\n", buf_len, buf_len); + goto exit; + } + + /* read raw code from hw */ + ret = rtlsdr_read_regs(d, IRB, IR_RX_BUF, buf, len); + if (ret < 0) + goto err; + + /* let hw receive new code */ + for (i = 0; i < ARRAY_SIZE(refresh_tab); i++) { + ret = rtlsdr_write_reg_mask(d, refresh_tab[i].block, refresh_tab[i].reg, + refresh_tab[i].val, refresh_tab[i].mask); + if (ret < 0) + goto err; + } + + // On success return length + ret = len; + +exit: + return ret; +err: + printf("failed=%d\n", ret); + return ret; +} \ No newline at end of file diff --git a/src/rtl_adsb.c b/src/rtl_adsb.c index 7b10306..fd9e422 100644 --- a/src/rtl_adsb.c +++ b/src/rtl_adsb.c @@ -36,7 +36,10 @@ #include #include "getopt/getopt.h" #endif - + +#ifdef NEED_PTHREADS_WORKARROUND +#define HAVE_STRUCT_TIMESPEC +#endif #include #include @@ -45,8 +48,10 @@ #ifdef _WIN32 #define sleep Sleep +#if defined(_MSC_VER) && (_MSC_VER < 1800) #define round(x) (x > 0.0 ? floor(x + 0.5): ceil(x - 0.5)) #endif +#endif #define ADSB_RATE 2000000 #define ADSB_FREQ 1090000000 @@ -74,7 +79,7 @@ int quality = 10; int allowed_errors = 5; FILE *file; int adsb_frame[14]; -#define preamble_len 16 +#define preamble_len 16 #define long_frame 112 #define short_frame 56 diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 8a8430b..7ec9e17 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -4,6 +4,7 @@ * Copyright (C) 2012 by Hoernchen * Copyright (C) 2012 by Kyle Keen * Copyright (C) 2013 by Elias Oenal + * Copyright (C) 2015 by Hayati Ayguen * * 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 @@ -29,23 +30,23 @@ * (no many-to-many locks) * * todo: - * sanity checks - * scale squelch to other input parameters - * test all the demodulations - * pad output on hop - * frequency ranges could be stored better - * scaled AM demod amplification - * auto-hop after time limit - * peak detector to tune onto stronger signals - * fifo for active hop frequency - * clips - * noise squelch - * merge stereo patch - * merge soft agc patch - * merge udp patch - * testmode to detect overruns - * watchdog to reset bad dongle - * fix oversampling + * sanity checks + * scale squelch to other input parameters + * test all the demodulations + * pad output on hop + * frequency ranges could be stored better + * scaled AM demod amplification + * auto-hop after time limit + * peak detector to tune onto stronger signals + * fifo for active hop frequency + * clips + * noise squelch + * merge stereo patch + * merge soft agc patch + * merge udp patch + * testmode to detect overruns + * watchdog to reset bad dongle + * fix oversampling */ #include @@ -62,12 +63,16 @@ #include #include "getopt/getopt.h" #define usleep(x) Sleep(x/1000) -#ifdef _MSC_VER +#if defined(_MSC_VER) && (_MSC_VER < 1800) #define round(x) (x > 0.0 ? floor(x + 0.5): ceil(x - 0.5)) #endif #define _USE_MATH_DEFINES #endif +#ifdef NEED_PTHREADS_WORKARROUND +#define HAVE_STRUCT_TIMESPEC +#endif + #include #include #include @@ -79,8 +84,8 @@ #define DEFAULT_BUF_LENGTH (1 * 16384) #define MAXIMUM_OVERSAMPLE 16 #define MAXIMUM_BUF_LENGTH (MAXIMUM_OVERSAMPLE * DEFAULT_BUF_LENGTH) -#define AUTO_GAIN -100 -#define BUFFER_DUMP 4096 +#define AUTO_GAIN -100 +#define BUFFER_DUMP 4096 #define FREQUENCIES_LIMIT 1000 @@ -92,54 +97,63 @@ static int *atan_lut = NULL; static int atan_lut_size = 131072; /* 512 KB */ static int atan_lut_coef = 8; +static int verbosity = 0; +static int printLevels = 0; +static int printLevelNo = 1; +static int levelMax = 0; +static int levelMaxMax = 0; +static double levelSum = 0.0; + struct dongle_state { - int exit_flag; + int exit_flag; pthread_t thread; rtlsdr_dev_t *dev; - int dev_index; + int dev_index; uint32_t freq; uint32_t rate; - int gain; - uint16_t buf16[MAXIMUM_BUF_LENGTH]; + uint32_t bandwidth; + int gain; + int16_t buf16[MAXIMUM_BUF_LENGTH]; uint32_t buf_len; - int ppm_error; - int offset_tuning; - int direct_sampling; - int mute; + int ppm_error; + int offset_tuning; + int direct_sampling; + int mute; struct demod_state *demod_target; }; struct demod_state { - int exit_flag; + int exit_flag; pthread_t thread; int16_t lowpassed[MAXIMUM_BUF_LENGTH]; - int lp_len; + 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*); + 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_audio, dc_avg, adc_block_const; + int dc_block_raw, dc_avgI, dc_avgQ, rdc_block_const; + void (*mode_demod)(struct demod_state*); pthread_rwlock_t rw; pthread_cond_t ready; pthread_mutex_t ready_m; @@ -148,13 +162,13 @@ struct demod_state struct output_state { - int exit_flag; + int exit_flag; pthread_t thread; - FILE *file; - char *filename; + FILE *file; + char *filename; int16_t result[MAXIMUM_BUF_LENGTH]; - int result_len; - int rate; + int result_len; + int rate; pthread_rwlock_t rw; pthread_cond_t ready; pthread_mutex_t ready_m; @@ -162,13 +176,13 @@ struct output_state struct controller_state { - int exit_flag; + int exit_flag; pthread_t thread; uint32_t freqs[FREQUENCIES_LIMIT]; - int freq_len; - int freq_now; - int edge; - int wb_mode; + int freq_len; + int freq_now; + int edge; + int wb_mode; pthread_cond_t hop; pthread_mutex_t hop_m; }; @@ -185,35 +199,46 @@ void usage(void) "rtl_fm, a simple narrow band FM demodulator for RTL2832 based DVB-T receivers\n\n" "Use:\trtl_fm -f freq [-options] [filename]\n" "\t-f frequency_to_tune_to [Hz]\n" - "\t use multiple -f for scanning (requires squelch)\n" - "\t ranges supported, -f 118M:137M:25k\n" + "\t use multiple -f for scanning (requires squelch)\n" + "\t ranges supported, -f 118M:137M:25k\n" + "\t[-v increase verbosity (default: 0)]\n" "\t[-M modulation (default: fm)]\n" - "\t fm, wbfm, raw, am, usb, lsb\n" - "\t wbfm == -M fm -s 170k -o 4 -A fast -r 32k -l 0 -E deemp\n" - "\t raw mode outputs 2x16 bit IQ pairs\n" + "\t fm or nbfm or nfm, wbfm or wfm, raw or iq, am, usb, lsb\n" + "\t wbfm == -M fm -s 170k -o 4 -A fast -r 32k -l 0 -E deemp\n" + "\t raw mode outputs 2x16 bit IQ pairs\n" "\t[-s sample_rate (default: 24k)]\n" "\t[-d device_index (default: 0)]\n" "\t[-g tuner_gain (default: automatic)]\n" + "\t[-w tuner_bandwidth (default: automatic. enables offset tuning)]\n" "\t[-l squelch_level (default: 0/off)]\n" - //"\t for fm squelch is inverted\n" - //"\t[-o oversampling (default: 1, 4 recommended)]\n" + "\t[-L N prints levels every N calculations]\n" + "\t output are comma separated values (csv):\n" + "\t mean since last output, max since last output, overall max, squelch\n" + "\t[-c de-emphasis_time_constant in us for wbfm. 'us' or 'eu' for 75/50 us (default: us)]\n" + //"\t for fm squelch is inverted\n" + "\t[-o oversampling (default: 1, 4 recommended)]\n" "\t[-p ppm_error (default: 0)]\n" "\t[-E enable_option (default: none)]\n" - "\t use multiple -E to enable multiple options\n" - "\t edge: enable lower edge tuning\n" - "\t dc: enable dc blocking filter\n" - "\t deemp: enable de-emphasis filter\n" - "\t direct: enable direct sampling\n" - "\t offset: enable offset tuning\n" + "\t use multiple -E to enable multiple options\n" + "\t edge: enable lower edge tuning\n" + "\t rdc: enable dc blocking filter on raw I/Q data at capture rate\n" + "\t adc: enable dc blocking filter on demodulated audio\n" + "\t dc: same as adc\n" + "\t rtlagc: enable rtl2832's digital agc (default: off)\n" + "\t agc: same as rtlagc\n" + "\t deemp: enable de-emphasis filter\n" + "\t direct: enable direct sampling (bypasses tuner, uses rtl2832 xtal)\n" + "\t offset: enable offset tuning (only e4000 tuner)\n" + "\t[-q dc_avg_factor for option rdc (default: 9)]\n" "\tfilename ('-' means stdout)\n" - "\t omitting the filename also uses stdout\n\n" + "\t omitting the filename also uses stdout\n\n" "Experimental options:\n" "\t[-r resample_rate (default: none / same as -s)]\n" "\t[-t squelch_delay (default: 10)]\n" - "\t +values will mute/scan, -values will exit\n" + "\t +values will mute/scan, -values will exit\n" "\t[-F fir_size (default: off)]\n" - "\t enables low-leakage downsample filter\n" - "\t size can be 0 or 9. 0 has bad roll off\n" + "\t enables low-leakage downsample filter\n" + "\t size can be 0 or 9. 0 has bad roll off\n" "\t[-A std/fast/lut choose atan math (default: std)]\n" //"\t[-C clip_path (default: off)\n" //"\t (create time stamped raw clips, requires squelch)\n" @@ -223,7 +248,7 @@ void usage(void) "\n" "Produces signed 16 bit ints, use Sox or aplay to hear them.\n" "\trtl_fm ... | play -t raw -r 24k -es -b 16 -c 1 -V1 -\n" - "\t | aplay -r 24k -f S16_LE -t raw -c 1\n" + "\t | aplay -r 24k -f S16_LE -t raw -c 1\n" "\t -M wbfm | play -r 32k ... \n" "\t -s 22050 | multimon -t raw /dev/stdin\n\n"); exit(1); @@ -271,13 +296,34 @@ int cic_9_tables[][10] = { {9, -199, -362, 5303, -25505, 77489, -25505, 5303, -362, -199}, }; -#ifdef _MSC_VER +#if defined(_MSC_VER) && (_MSC_VER < 1800) double log2(double n) { return log(n) / log(2.0); } #endif +void rotate16_90(int16_t *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; + int16_t tmp; + for (i=0; i> 15 ; hist[0] = hist[1]; hist[1] = hist[2]; @@ -610,7 +656,7 @@ void deemph_filter(struct demod_state *fm) } } -void dc_block_filter(struct demod_state *fm) +void dc_block_audio_filter(struct demod_state *fm) { int i, avg; int64_t sum = 0; @@ -618,13 +664,36 @@ void dc_block_filter(struct demod_state *fm) sum += fm->result[i]; } avg = sum / fm->result_len; - avg = (avg + fm->dc_avg * 9) / 10; + avg = (avg + fm->dc_avg * fm->adc_block_const) / ( fm->adc_block_const + 1 ); for (i=0; i < fm->result_len; i++) { fm->result[i] -= avg; } fm->dc_avg = avg; } +void dc_block_raw_filter(struct demod_state *fm, int16_t *buf, int len) +{ + /* derived from dc_block_audio_filter, + running over the raw I/Q components + */ + int i, avgI, avgQ; + int64_t sumI = 0; + int64_t sumQ = 0; + for (i = 0; i < len; i += 2) { + sumI += buf[i]; + sumQ += buf[i+1]; + } + avgI = sumI / ( len / 2 ); + avgQ = sumQ / ( len / 2 ); + avgI = (avgI + fm->dc_avgI * fm->rdc_block_const) / ( fm->rdc_block_const + 1 ); + avgQ = (avgQ + fm->dc_avgQ * fm->rdc_block_const) / ( fm->rdc_block_const + 1 ); + for (i = 0; i < len; i += 2) { + buf[i] -= avgI; + buf[i+1] -= avgQ; + } + fm->dc_avgI = avgI; + fm->dc_avgQ = avgQ; +} int mad(int16_t *samples, int len, int step) /* mean average deviation */ { @@ -759,6 +828,23 @@ void full_demod(struct demod_state *d) } else { d->squelch_hits = 0;} } + + if (printLevels) { + if (!sr) + sr = rms(d->lowpassed, d->lp_len, 1); + --printLevelNo; + if (printLevels) { + levelSum += sr; + if (levelMax < sr) levelMax = sr; + if (levelMaxMax < sr) levelMaxMax = sr; + if (!printLevelNo) { + printLevelNo = printLevels; + fprintf(stderr, "%f, %d, %d, %d\n", (levelSum / printLevels), levelMax, levelMaxMax, d->squelch_level ); + levelMax = 0; + levelSum = 0; + } + } + } d->mode_demod(d); /* lowpassed -> result */ if (d->mode_demod == &raw_demod) { return; @@ -769,8 +855,8 @@ void full_demod(struct demod_state *d) 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->dc_block_audio) { + dc_block_audio_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); @@ -792,10 +878,19 @@ static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) buf[i] = 127;} s->mute = 0; } - if (!s->offset_tuning) { - rotate_90(buf, len);} + /* 1st: convert to 16 bit - to allow easier calculation of DC */ for (i=0; i<(int)len; i++) { - s->buf16[i] = (int16_t)buf[i] - 127;} + s->buf16[i] = ( (int16_t)buf[i] - 127 ); + } + /* 2nd: do DC filtering BEFORE up-mixing */ + if (d->dc_block_raw) { + dc_block_raw_filter(d, s->buf16, (int)len); + } + /* 3rd: up-mixing */ + if (!s->offset_tuning) { + rotate16_90(s->buf16, (int)len); + /* rotate_90(buf, len); */ + } pthread_rwlock_wrlock(&d->rw); memcpy(d->lowpassed, s->buf16, 2*len); d->lp_len = len; @@ -862,11 +957,21 @@ static void optimal_settings(int freq, int rate) dm->downsample_passes = (int)log2(dm->downsample) + 1; dm->downsample = 1 << dm->downsample_passes; } + if (verbosity) { + fprintf(stderr, "downsample_passes = %d (= # of fifth_order() iterations), downsample = %d\n", dm->downsample_passes, dm->downsample ); + } capture_freq = freq; capture_rate = dm->downsample * dm->rate_in; + if (verbosity) + fprintf(stderr, "capture_rate = dm->downsample * dm->rate_in = %d * %d = %d\n", dm->downsample, dm->rate_in, capture_rate ); if (!d->offset_tuning) { - capture_freq = freq + capture_rate/4;} + capture_freq = freq + capture_rate/4; + if (verbosity) + fprintf(stderr, "optimal_settings(freq = %d): capture_freq = freq + capture_rate/4 = %d\n", freq, capture_freq ); + } capture_freq += cs->edge * dm->rate_in / 2; + if (verbosity) + fprintf(stderr, "optimal_settings(freq = %d): capture_freq += cs->edge * dm->rate_in / 2 = %d * %d / 2 = %d\n", freq, cs->edge, dm->rate_in, capture_freq ); dm->output_scale = (1<<15) / (128 * dm->downsample); if (dm->output_scale < 1) { dm->output_scale = 1;} @@ -874,6 +979,8 @@ static void optimal_settings(int freq, int rate) dm->output_scale = 1;} d->freq = (uint32_t)capture_freq; d->rate = (uint32_t)capture_rate; + if (verbosity) + fprintf(stderr, "optimal_settings(freq = %d) delivers freq %.0f, rate %.0f\n", freq, (double)d->freq, (double)d->rate ); } static void *controller_thread_fn(void *arg) @@ -884,6 +991,8 @@ static void *controller_thread_fn(void *arg) struct controller_state *s = arg; if (s->wb_mode) { + if (verbosity) + fprintf(stderr, "wbfm: adding 16000 Hz to every input frequency\n"); for (i=0; i < s->freq_len; i++) { s->freqs[i] += 16000;} } @@ -896,6 +1005,11 @@ static void *controller_thread_fn(void *arg) verbose_offset_tuning(dongle.dev);} /* Set the frequency */ + if (verbosity) { + fprintf(stderr, "verbose_set_frequency(%.0f Hz)\n", (double)dongle.freq); + if (!dongle.offset_tuning) + fprintf(stderr, " frequency is away from parametrized one, to avoid negative impact from dc\n"); + } 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); @@ -903,6 +1017,8 @@ static void *controller_thread_fn(void *arg) 1000 * 0.5 * (float)ACTUAL_BUF_LENGTH / (float)dongle.rate); /* Set the sample rate */ + if (verbosity) + fprintf(stderr, "verbose_set_sample_rate(%.0f Hz)\n", (double)dongle.rate); verbose_set_sample_rate(dongle.dev, dongle.rate); fprintf(stderr, "Output at %u Hz.\n", demod.rate_in/demod.post_downsample); @@ -947,6 +1063,7 @@ void dongle_init(struct dongle_state *s) s->direct_sampling = 0; s->offset_tuning = 0; s->demod_target = &demod; + s->bandwidth = 0; } void demod_init(struct demod_state *s) @@ -960,17 +1077,22 @@ void demod_init(struct demod_state *s) s->downsample_passes = 0; s->comp_fir_size = 0; s->prev_index = 0; - s->post_downsample = 1; // once this works, default = 4 + s->post_downsample = 1; // once this works, default = 4 s->custom_atan = 0; s->deemph = 0; - s->rate_out2 = -1; // flag for disabled + 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_block_audio = 0; s->dc_avg = 0; + s->adc_block_const = 9; + s->dc_block_raw = 0; + s->dc_avgI = 0; + s->dc_avgQ = 0; + s->rdc_block_const = 9; pthread_rwlock_init(&s->rw, NULL); pthread_cond_init(&s->ready, NULL); pthread_mutex_init(&s->ready_m, NULL); @@ -1042,12 +1164,14 @@ int main(int argc, char **argv) int r, opt; int dev_given = 0; int custom_ppm = 0; + int timeConstant = 75; /* default: U.S. 75 uS */ + int rtlagc = 0; dongle_init(&dongle); demod_init(&demod); output_init(&output); controller_init(&controller); - while ((opt = getopt(argc, argv, "d:f:g:s:b:l:o:t:r:p:E:F:A:M:h")) != -1) { + while ((opt = getopt(argc, argv, "d:f:g:s:b:l:L:o:t:r:p:E:q:F:A:M:c:h:w:v")) != -1) { switch (opt) { case 'd': dongle.dev_index = verbose_device_search(optarg); @@ -1070,6 +1194,9 @@ int main(int argc, char **argv) case 'l': demod.squelch_level = (int)atof(optarg); break; + case 'L': + printLevels = (int)atof(optarg); + break; case 's': demod.rate_in = (uint32_t)atofs(optarg); demod.rate_out = (uint32_t)atofs(optarg); @@ -1098,14 +1225,21 @@ int main(int argc, char **argv) case 'E': if (strcmp("edge", optarg) == 0) { controller.edge = 1;} - if (strcmp("dc", optarg) == 0) { - demod.dc_block = 1;} + if (strcmp("dc", optarg) == 0 || strcmp("adc", optarg) == 0) { + demod.dc_block_audio = 1;} + if (strcmp("rdc", optarg) == 0) { + demod.dc_block_raw = 1;} if (strcmp("deemp", optarg) == 0) { demod.deemph = 1;} if (strcmp("direct", optarg) == 0) { dongle.direct_sampling = 1;} if (strcmp("offset", optarg) == 0) { dongle.offset_tuning = 1;} + if (strcmp("rtlagc", optarg) == 0 || strcmp("agc", optarg) == 0) { + rtlagc = 1;} + break; + case 'q': + demod.rdc_block_const = atoi(optarg); break; case 'F': demod.downsample_passes = 1; /* truthy placeholder */ @@ -1121,9 +1255,9 @@ int main(int argc, char **argv) demod.custom_atan = 2;} break; case 'M': - if (strcmp("fm", optarg) == 0) { + if (strcmp("nbfm", optarg) == 0 || strcmp("nfm", optarg) == 0 || strcmp("fm", optarg) == 0) { demod.mode_demod = &fm_demod;} - if (strcmp("raw", optarg) == 0) { + if (strcmp("raw", optarg) == 0 || strcmp("iq", optarg) == 0) { demod.mode_demod = &raw_demod;} if (strcmp("am", optarg) == 0) { demod.mode_demod = &am_demod;} @@ -1131,7 +1265,7 @@ int main(int argc, char **argv) demod.mode_demod = &usb_demod;} if (strcmp("lsb", optarg) == 0) { demod.mode_demod = &lsb_demod;} - if (strcmp("wbfm", optarg) == 0) { + if (strcmp("wbfm", optarg) == 0 || strcmp("wfm", optarg) == 0) { controller.wb_mode = 1; demod.mode_demod = &fm_demod; demod.rate_in = 170000; @@ -1142,13 +1276,33 @@ int main(int argc, char **argv) demod.deemph = 1; demod.squelch_level = 0;} break; + case 'c': + if (strcmp("us", optarg) == 0) + timeConstant = 75; + else if (strcmp("eu", optarg) == 0) + timeConstant = 50; + else + timeConstant = (int)atof(optarg); + break; + case 'v': + ++verbosity; + break; + case 'w': + dongle.bandwidth = (uint32_t)atofs(optarg); + if (dongle.bandwidth) + dongle.offset_tuning = 1; /* automatically switch offset tuning, when using bandwidth filter */ + break; case 'h': + case '?': default: usage(); break; } } + if (verbosity) + fprintf(stderr, "verbosity set to %d\n", verbosity); + /* quadruple sample_rate to limit to Δθ to ±π/2 */ demod.rate_in *= demod.post_downsample; @@ -1194,7 +1348,10 @@ int main(int argc, char **argv) #endif if (demod.deemph) { - demod.deemph_a = (int)round(1.0/((1.0-exp(-1.0/(demod.rate_out * 75e-6))))); + double tc = (double)timeConstant * 1e-6; + demod.deemph_a = (int)round(1.0/((1.0-exp(-1.0/(demod.rate_out * tc))))); + if (verbosity) + fprintf(stderr, "using wbfm deemphasis filter with time constant %d us\n", timeConstant ); } /* Set the tuner gain */ @@ -1205,8 +1362,27 @@ int main(int argc, char **argv) verbose_gain_set(dongle.dev, dongle.gain); } + rtlsdr_set_agc_mode(dongle.dev, rtlagc); + verbose_ppm_set(dongle.dev, dongle.ppm_error); + verbose_set_bandwidth(dongle.dev, dongle.bandwidth); + + if (verbosity && dongle.bandwidth) + { + int r; + uint32_t in_bw, out_bw, last_bw = 0; + fprintf(stderr, "Supported bandwidth values in kHz:\n"); + for ( in_bw = 1; in_bw < 3200; ++in_bw ) + { + r = rtlsdr_set_and_get_tuner_bandwidth(dongle.dev, in_bw*1000, &out_bw, 0 /* =apply_bw */); + if ( r == 0 && out_bw != 0 && ( out_bw != last_bw || in_bw == 1 ) ) + fprintf(stderr, "%s%.1f", (in_bw==1 ? "" : ", "), out_bw/1000.0 ); + last_bw = out_bw; + } + fprintf(stderr,"\n"); + } + if (strcmp(output.filename, "-") == 0) { /* Write samples to stdout */ output.file = stdout; #ifdef _WIN32 diff --git a/src/rtl_ir.c b/src/rtl_ir.c new file mode 100644 index 0000000..a291cd4 --- /dev/null +++ b/src/rtl_ir.c @@ -0,0 +1,218 @@ +/* + * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver + * Copyright (C) 2009 Antti Palosaari + * Copyright (C) 2011 Antti Palosaari + * Copyright (C) 2012 Thomas Mair + * 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) 2016 by Robert X. Seger + * + * 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 + +#ifndef _WIN32 +#include +#else +#include +#include +#include +#include "getopt/getopt.h" +#define usleep(x) Sleep(x/1000) +#ifdef _MSC_VER +#define round(x) (x > 0.0 ? floor(x + 0.5): ceil(x - 0.5)) +#endif +#define _USE_MATH_DEFINES +#endif + +#include +#include + +#include "rtl-sdr.h" +#include "convenience/convenience.h" + +static volatile int do_exit = 0; + +struct dongle_state +{ + int exit_flag; + rtlsdr_dev_t *dev; + int dev_index; +}; + +void dongle_init(struct dongle_state *s) +{ + bzero(s, sizeof(struct dongle_state)); +} + +struct dongle_state dongle; + +void usage(void) +{ + fprintf(stderr, + "rtl_ir\n\n" + "Use:\trtl_ir [-options]\n" + "\t[-d device_index (default: 0)]\n" + "\t[-w wait_usec]\tDelay to wait before each iteration (10000)\n" + "\t[-c max_count]\tMaximum number of loop iterations (0)\n" + "\t[-b]\tDisplay output in binary (default), pulse=1, space=0; each 20 usec\n" + "\t[-t]\tDisplay output in text format\n" + "\t[-x]\tDisplay output in raw packed bytes, MSB=pulse/space, 7LSB=duration*20 usec\n" + "\t[-h\t]Help\n" + ); + exit(1); +} + +#ifdef _WIN32 +BOOL WINAPI +sighandler(int signum) +{ + if (CTRL_C_EVENT == signum) { + fprintf(stderr, "Signal caught, exiting!\n"); + do_exit = 1; + rtlsdr_cancel_async(dongle.dev); + return TRUE; + } + return FALSE; +} +#else +static void sighandler(int signum) +{ + fprintf(stderr, "Signal caught, exiting!\n"); + do_exit = 1; + rtlsdr_cancel_async(dongle.dev); +} +#endif + + +int main(int argc, char **argv) { +#ifndef _WIN32 + struct sigaction sigact; +#endif + int r, opt; + int i, j; + int dev_given = 0; + unsigned int wait_usec = 100000; + int max_count = 0, iteration_count = 0; + int output_binary = 0, output_text = 0, output_packed = 0; + uint8_t buf[128] = { 0 }; + + dongle_init(&dongle); + + while ((opt = getopt(argc, argv, "d:c:w:btxh")) != -1) { + switch (opt) { + case 'd': + dongle.dev_index = verbose_device_search(optarg); + dev_given = 1; + break; + case 'w': + wait_usec = atoi(optarg); + break; + case 'c': + max_count = atoi(optarg); + break; + case 'b': + output_binary = 1; + break; + case 't': + output_text = 1; + break; + case 'x': + output_packed = 1; + break; + case 'h': + default: + usage(); + break; + } + } + + if (dongle.dev_index < 0) { + exit(1); + } + + 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); + } +#ifndef _WIN32 + 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); +#else + SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); +#endif + + verbose_reset_buffer(dongle.dev); + + if (!output_binary && !output_text && !output_packed) + output_binary = 1; + + while (!do_exit) { + usleep(wait_usec); + + r = rtlsdr_ir_query(dongle.dev, buf, sizeof(buf)); + if (r < 0) { + fprintf(stderr, "rtlsdr_ir_query failed: %d\n", r); + } + + for (i = 0; i < r; i++) { + int pulse = buf[i] >> 7; + int duration = buf[i] & 0x7f; + + if (output_text) { + printf("pulse %d, duration %d usec\n", pulse, duration * 20); + } + + if (output_binary) { + for (j = 0; j < duration; ++j) { + printf("%d", pulse); + } + } + + if (output_packed) { + putchar(buf[i]); + } + } + if (r != 0) printf("\n"); + fflush(stdout); + + if (max_count != 0 && ++iteration_count >= max_count) do_exit = 1; + } + + if (do_exit) { + fprintf(stderr, "\nUser cancel, exiting...\n");} + else { + fprintf(stderr, "\nLibrary error %d, exiting...\n", r);} + + rtlsdr_cancel_async(dongle.dev); + + rtlsdr_close(dongle.dev); + return r >= 0 ? r : -r; + + return 0; +} diff --git a/src/rtl_power.c b/src/rtl_power.c index 7eb1d06..96d2b09 100644 --- a/src/rtl_power.c +++ b/src/rtl_power.c @@ -53,13 +53,16 @@ #include #include "getopt/getopt.h" #define usleep(x) Sleep(x/1000) -#ifdef _MSC_VER +#if defined(_MSC_VER) && (_MSC_VER < 1800) #define round(x) (x > 0.0 ? floor(x + 0.5): ceil(x - 0.5)) #endif #define _USE_MATH_DEFINES #endif #include +#ifdef NEED_PTHREADS_WORKARROUND +#define HAVE_STRUCT_TIMESPEC +#endif #include #include @@ -69,8 +72,8 @@ #define MAX(x, y) (((x) > (y)) ? (x) : (y)) #define DEFAULT_BUF_LENGTH (1 * 16384) -#define AUTO_GAIN -100 -#define BUFFER_DUMP (1<<12) +#define AUTO_GAIN -100 +#define BUFFER_DUMP (1<<12) #define MAXIMUM_RATE 2800000 #define MINIMUM_RATE 1000000 @@ -220,7 +223,7 @@ int cic_9_tables[][10] = { {9, -199, -362, 5303, -25505, 77489, -25505, 5303, -362, -199}, }; -#ifdef _MSC_VER +#if defined(_MSC_VER) && (_MSC_VER < 1800) double log2(double n) { return log(n) / log(2.0); diff --git a/src/rtl_rpcd.c b/src/rtl_rpcd.c new file mode 100644 index 0000000..3c41d13 --- /dev/null +++ b/src/rtl_rpcd.c @@ -0,0 +1,1261 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rtlsdr_rpc_msg.h" + + +#if 1 +#include +#define PRINTF(__s, ...) \ +do { \ + fprintf(stderr, __s, ##__VA_ARGS__); \ + fflush(stderr); \ +} while (0) +#define TRACE() PRINTF("[t] %s,%u\n", __FILE__, __LINE__) +#define ERROR() PRINTF("[e] %s,%u\n", __FILE__, __LINE__) +#else +#define TRACE() +#define ERROR() +#define PRINTF(...) +#endif + + +typedef struct +{ + int listen_sock; + int cli_sock; + + rtlsdr_dev_t* dev; + uint32_t did; + + rtlsdr_rpc_msg_t query_msg; + rtlsdr_rpc_msg_t reply_msg; + rtlsdr_rpc_msg_t event_msg; + + unsigned int async_replied; + uint8_t async_id; + + int port_ir; + const char *addr_ir; + int wait_ir; +} rpcd_t; + +static int resolve_ip_addr +( + struct sockaddr_storage saddr_both[2], size_t size_both[2], + const char* addr, const char* port +) +{ + struct addrinfo ai; + struct addrinfo* aip = NULL; + int err = -1; + size_t i; + + memset(&ai, 0, sizeof(ai)); + ai.ai_family = AF_UNSPEC; + ai.ai_socktype = SOCK_STREAM; + ai.ai_flags = AI_PASSIVE; + + if (getaddrinfo(addr, port, &ai, &aip)) goto on_error; + + size_both[0] = 0; + size_both[1] = 0; + i = 0; + for (; (i != 2) && (aip != NULL); aip = aip->ai_next) + { + if ((aip->ai_family != AF_INET) && (aip->ai_family != AF_INET6)) continue ; + if (aip->ai_addrlen == 0) continue ; + memcpy(&saddr_both[i], aip->ai_addr, aip->ai_addrlen); + size_both[i] = aip->ai_addrlen; + ++i; + } + + if (i == 0) goto on_error; + + err = 0; + on_error: + if (aip != NULL) freeaddrinfo(aip); + return err; +} + +static int open_nonblock_socket +( + struct sockaddr_storage saddr_both[2], size_t size_both[2], + int type, int proto, + struct sockaddr_storage** saddr_used, size_t* size_used +) +{ + size_t i; + int fd = -1; + + for (i = 0; (i != 2) && (size_both[i]); ++i) + { + const struct sockaddr* const sa = (const struct sockaddr*)&saddr_both[i]; + fd = socket(sa->sa_family, type, proto); + if (fd != -1) break ; + } + + if ((i == 2) || (size_both[i] == 0)) return -1; + + *saddr_used = &saddr_both[i]; + *size_used = size_both[i]; + + if (fcntl(fd, F_SETFL, O_NONBLOCK)) + { + close(fd); + return -1; + } + + return fd; +} + +static int init_rpcd(rpcd_t* rpcd, const char* addr, const char* port) +{ + struct sockaddr_storage saddrs[2]; + struct sockaddr_storage* saddr; + size_t sizes[2]; + size_t size; + int err; + int enable = 1; + + /* TODO: handle errors */ + rtlsdr_rpc_msg_init(&rpcd->query_msg, 0); + rtlsdr_rpc_msg_init(&rpcd->reply_msg, 0); + rtlsdr_rpc_msg_init(&rpcd->event_msg, 0); + + if (resolve_ip_addr(saddrs, sizes, addr, port)) + { + ERROR(); + goto on_error_0; + } + + rpcd->listen_sock = open_nonblock_socket + (saddrs, sizes, SOCK_STREAM, IPPROTO_TCP, &saddr, &size); + if (rpcd->listen_sock == -1) + { + ERROR(); + goto on_error_0; + } + + err = setsockopt + (rpcd->listen_sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)); + if (err) + { + ERROR(); + goto on_error_1; + } + + err = bind + (rpcd->listen_sock, (const struct sockaddr*)saddr, (socklen_t)size); + if (err) + { + ERROR(); + goto on_error_1; + } + + if (listen(rpcd->listen_sock, 5)) + { + ERROR(); + goto on_error_1; + } + + rpcd->cli_sock = -1; + rpcd->dev = NULL; + + return 0; + + on_error_1: + close(rpcd->listen_sock); + on_error_0: + return -1; +} + +static int fini_rpcd(rpcd_t* rpcd) +{ + if (rpcd->cli_sock != -1) + { + shutdown(rpcd->cli_sock, SHUT_RDWR); + close(rpcd->cli_sock); + } + + shutdown(rpcd->listen_sock, SHUT_RDWR); + close(rpcd->listen_sock); + + rtlsdr_rpc_msg_fini(&rpcd->query_msg); + rtlsdr_rpc_msg_fini(&rpcd->reply_msg); + rtlsdr_rpc_msg_fini(&rpcd->event_msg); + + return 0; +} + +static int recv_all(int fd, uint8_t* buf, size_t size) +{ + ssize_t n; + fd_set rset; + + while (1) + { + errno = 0; + n = recv(fd, buf, size, 0); + if (n <= 0) + { + if ((errno != EWOULDBLOCK) || (errno != EAGAIN)) + return -1; + } + else + { + size -= (size_t)n; + buf += (size_t)n; + if (size == 0) break ; + } + + FD_ZERO(&rset); + FD_SET(fd, &rset); + if (select(fd + 1, &rset, NULL, NULL, NULL) <= 0) + { + return -1; + } + } + + return 0; +} + +static int send_all_check_recv +(int fd, const uint8_t* buf, size_t size, unsigned int* is_recv) +{ + ssize_t n; + fd_set wset; + fd_set rset; + fd_set* rsetp; + + rsetp = NULL; + if (is_recv != NULL) *is_recv = 0; + + while (1) + { + FD_ZERO(&wset); + FD_SET(fd, &wset); + + rsetp = NULL; + if ((is_recv != NULL) && (*is_recv == 0)) + { + FD_ZERO(&rset); + FD_SET(fd, &rset); + rsetp = &rset; + } + + if (select(fd + 1, rsetp, &wset, NULL, NULL) <= 0) + { + return -1; + } + + if ((rsetp != NULL) && FD_ISSET(fd, rsetp)) + { + *is_recv = 1; + } + + if (FD_ISSET(fd, &wset)) + { + errno = 0; + n = send(fd, buf, size, 0); + if (n <= 0) + { + if ((errno != EWOULDBLOCK) || (errno != EAGAIN)) + return -1; + } + else + { + size -= (size_t)n; + buf += (size_t)n; + if (size == 0) break ; + } + } + } + + return 0; +} + +static int send_all(int fd, const uint8_t* buf, size_t size) +{ + return send_all_check_recv(fd, buf, size, NULL); +} + +static int recv_msg(int fd, rtlsdr_rpc_msg_t* m) +{ + uint32_t size; + + if (recv_all(fd, (uint8_t*)&size, sizeof(uint32_t))) + { + ERROR(); + return -1; + } + + rtlsdr_rpc_msg_set_size(m, size); + size = rtlsdr_rpc_msg_get_size(m); + + if (rtlsdr_rpc_msg_realloc(m, size)) + { + ERROR(); + return -1; + } + + if (recv_all(fd, m->fmt + sizeof(uint32_t), size - sizeof(uint32_t))) + { + ERROR(); + return -1; + } + + return 0; +} + +static int send_msg(int fd, rtlsdr_rpc_msg_t* m) +{ + return send_all(fd, m->fmt, m->off); +} + +static int recv_query(rpcd_t* rpcd, rtlsdr_rpc_msg_t** q) +{ + *q = NULL; + rtlsdr_rpc_msg_reset(&rpcd->query_msg); + if (recv_msg(rpcd->cli_sock, &rpcd->query_msg)) return -1; + *q = &rpcd->query_msg; + return 0; +} + +static int send_reply(rpcd_t* rpcd, rtlsdr_rpc_msg_t* r) +{ + return send_msg(rpcd->cli_sock, r); +} + +static void read_async_cb +(unsigned char* buf, uint32_t len, void* ctx) +{ + const size_t off = offsetof(rtlsdr_rpc_fmt_t, data); + + rpcd_t* const rpcd = ctx; + uint8_t fmt[offsetof(rtlsdr_rpc_fmt_t, data)]; + rtlsdr_rpc_msg_t msg; + unsigned int is_recv; + + if (rpcd->async_replied == 0) + { + send_reply(rpcd, &rpcd->reply_msg); + rpcd->async_replied = 1; + } + + msg.off = off; + msg.size = off + len; + msg.fmt = fmt; + rtlsdr_rpc_msg_set_size(&msg, msg.size); + rtlsdr_rpc_msg_set_op(&msg, RTLSDR_RPC_OP_READ_ASYNC); + rtlsdr_rpc_msg_set_id(&msg, rpcd->async_id); + rtlsdr_rpc_msg_set_err(&msg, 0); + + send_all(rpcd->cli_sock, fmt, off); + send_all_check_recv(rpcd->cli_sock, buf, len, &is_recv); + + if (is_recv) rtlsdr_cancel_async(rpcd->dev); +} + +__attribute__((unused)) +static const char* op_to_string(rtlsdr_rpc_op_t op) +{ + const char* const s[] = + { + "RTLSDR_RPC_OP_GET_DEVICE_COUNT", + "RTLSDR_RPC_OP_GET_DEVICE_NAME", + "RTLSDR_RPC_OP_GET_DEVICE_USB_STRINGS", + "RTLSDR_RPC_OP_GET_INDEX_BY_SERIAL", + "RTLSDR_RPC_OP_OPEN", + "RTLSDR_RPC_OP_CLOSE", + "RTLSDR_RPC_OP_SET_XTAL_FREQ", + "RTLSDR_RPC_OP_GET_XTAL_FREQ", + "RTLSDR_RPC_OP_GET_USB_STRINGS", + "RTLSDR_RPC_OP_WRITE_EEPROM", + "RTLSDR_RPC_OP_READ_EEPROM", + "RTLSDR_RPC_OP_SET_CENTER_FREQ", + "RTLSDR_RPC_OP_GET_CENTER_FREQ", + "RTLSDR_RPC_OP_SET_FREQ_CORRECTION", + "RTLSDR_RPC_OP_GET_FREQ_CORRECTION", + "RTLSDR_RPC_OP_GET_TUNER_TYPE", + "RTLSDR_RPC_OP_GET_TUNER_GAINS", + "RTLSDR_RPC_OP_SET_TUNER_GAIN", + "RTLSDR_RPC_OP_GET_TUNER_GAIN", + "RTLSDR_RPC_OP_SET_TUNER_IF_GAIN", + "RTLSDR_RPC_OP_SET_TUNER_GAIN_MODE", + "RTLSDR_RPC_OP_SET_SAMPLE_RATE", + "RTLSDR_RPC_OP_GET_SAMPLE_RATE", + "RTLSDR_RPC_OP_SET_TESTMODE", + "RTLSDR_RPC_OP_SET_AGC_MODE", + "RTLSDR_RPC_OP_SET_DIRECT_SAMPLING", + "RTLSDR_RPC_OP_GET_DIRECT_SAMPLING", + "RTLSDR_RPC_OP_SET_OFFSET_TUNING", + "RTLSDR_RPC_OP_GET_OFFSET_TUNING", + "RTLSDR_RPC_OP_RESET_BUFFER", + "RTLSDR_RPC_OP_READ_SYNC", + "RTLSDR_RPC_OP_WAIT_ASYNC", + "RTLSDR_RPC_OP_READ_ASYNC", + "RTLSDR_RPC_OP_CANCEL_ASYNC", + "RTLSDR_RPC_OP_EVENT_STATE", + "RTLSDR_RPC_OP_INVALID" + }; + if (op >= RTLSDR_RPC_OP_INVALID) op = RTLSDR_RPC_OP_INVALID; + return s[op]; +} + +static int handle_query +(rpcd_t* rpcd, rtlsdr_rpc_msg_t* q, rtlsdr_rpc_msg_t** rr) +{ + rtlsdr_rpc_msg_t* const r = &rpcd->reply_msg; + rtlsdr_rpc_op_t op; + int err = -1; + + *rr = NULL; + + rtlsdr_rpc_msg_reset(r); + + op = rtlsdr_rpc_msg_get_op(q); + switch (op) + { + case RTLSDR_RPC_OP_GET_DEVICE_COUNT: + { + uint32_t n; + + n = rtlsdr_get_device_count(); + if (rtlsdr_rpc_msg_push_uint32(r, n)) goto on_error; + err = 0; + break ; + } + + case RTLSDR_RPC_OP_GET_DEVICE_NAME: + { + const char* s; + uint32_t i; + + if (rtlsdr_rpc_msg_pop_uint32(q, &i)) goto on_error; + + s = rtlsdr_get_device_name(i); + if (s == NULL) s = ""; + + if (rtlsdr_rpc_msg_push_str(r, s)) goto on_error; + + err = 0; + + break ; + } + + case RTLSDR_RPC_OP_GET_DEVICE_USB_STRINGS: + { + char manuf[256]; + char product[256]; + char serial[256]; + uint32_t i; + + if (rtlsdr_rpc_msg_pop_uint32(q, &i)) goto on_error; + + err = rtlsdr_get_device_usb_strings(i, manuf, product, serial); + if (err) goto on_error; + + if (rtlsdr_rpc_msg_push_str(r, manuf)) + { + err = -1; + goto on_error; + } + + if (rtlsdr_rpc_msg_push_str(r, product)) + { + err = -1; + goto on_error; + } + + if (rtlsdr_rpc_msg_push_str(r, serial)) + { + err = -1; + goto on_error; + } + + break ; + } + + case RTLSDR_RPC_OP_GET_INDEX_BY_SERIAL: + { + const char* serial; + + if (rtlsdr_rpc_msg_pop_str(q, &serial)) goto on_error; + err = rtlsdr_get_index_by_serial(serial); + if (err < 0) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_OPEN: + { + if (rpcd->dev != NULL) + { + /* already opened */ + err = 0; + goto on_error; + } + + if (rtlsdr_rpc_msg_pop_uint32(q, &rpcd->did)) goto on_error; + err = rtlsdr_open(&rpcd->dev, rpcd->did); + if (err) + { + rpcd->dev = NULL; + goto on_error; + } + + break ; + } + + case RTLSDR_RPC_OP_CLOSE: + { + uint32_t did; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + err = rtlsdr_close(rpcd->dev); + rpcd->dev = NULL; + + break ; + } + + case RTLSDR_RPC_OP_SET_XTAL_FREQ: + { + uint32_t did; + uint32_t rtl_freq; + uint32_t tuner_freq; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &rtl_freq)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &tuner_freq)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_xtal_freq(rpcd->dev, rtl_freq, tuner_freq); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_GET_XTAL_FREQ: + { + uint32_t did; + uint32_t rtl_freq; + uint32_t tuner_freq; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_get_xtal_freq(rpcd->dev, &rtl_freq, &tuner_freq); + if (err) goto on_error; + + if (rtlsdr_rpc_msg_push_uint32(r, rtl_freq)) + { + err = -1; + goto on_error; + } + + if (rtlsdr_rpc_msg_push_uint32(r, tuner_freq)) + { + err = -1; + goto on_error; + } + + break ; + } + + case RTLSDR_RPC_OP_GET_USB_STRINGS: + { + uint32_t did; + char manuf[256]; + char product[256]; + char serial[256]; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_get_usb_strings(rpcd->dev, manuf, product, serial); + if (err) goto on_error; + + if (rtlsdr_rpc_msg_push_str(r, manuf)) + { + err = -1; + goto on_error; + } + + if (rtlsdr_rpc_msg_push_str(r, product)) + { + err = -1; + goto on_error; + } + + if (rtlsdr_rpc_msg_push_str(r, serial)) + { + err = -1; + goto on_error; + } + + break ; + } + + case RTLSDR_RPC_OP_SET_CENTER_FREQ: + { + uint32_t did; + uint32_t freq; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &freq)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_center_freq(rpcd->dev, freq); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_GET_CENTER_FREQ: + { + uint32_t did; + uint32_t freq; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + freq = rtlsdr_get_center_freq(rpcd->dev); + if (freq == 0) goto on_error; + if (rtlsdr_rpc_msg_push_uint32(r, freq)) goto on_error; + err = 0; + + break ; + } + + case RTLSDR_RPC_OP_SET_FREQ_CORRECTION: + { + uint32_t did; + uint32_t ppm; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &ppm)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_freq_correction(rpcd->dev, (int)ppm); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_GET_FREQ_CORRECTION: + { + uint32_t did; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_get_freq_correction(rpcd->dev); + + break ; + } + + case RTLSDR_RPC_OP_GET_TUNER_TYPE: + { + uint32_t did; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_get_tuner_type(rpcd->dev); + + break ; + } + + case RTLSDR_RPC_OP_GET_TUNER_GAINS: + { + uint32_t did; + uint32_t is_null; + uint32_t gain_size; + size_t new_size; + uint8_t* buf; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &is_null)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &gain_size)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + if (is_null) + { + err = rtlsdr_get_tuner_gains(rpcd->dev, NULL); + if (err <= 0) goto on_error; + } + else + { + new_size = r->off + sizeof(uint32_t) + gain_size; + if (rtlsdr_rpc_msg_realloc(r, new_size)) goto on_error; + buf = r->fmt + r->off + sizeof(uint32_t); + + err = rtlsdr_get_tuner_gains(rpcd->dev, (int*)buf); + if (err <= 0) goto on_error; + + rtlsdr_rpc_msg_push_uint32_safe(r, gain_size); + rtlsdr_rpc_msg_skip_safe(r, gain_size); + } + + break ; + } + + case RTLSDR_RPC_OP_SET_TUNER_GAIN: + { + uint32_t did; + uint32_t gain; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &gain)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_tuner_gain(rpcd->dev, (int)gain); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_GET_TUNER_GAIN: + { + uint32_t did; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_get_tuner_gain(rpcd->dev); + + break ; + } + + case RTLSDR_RPC_OP_SET_TUNER_IF_GAIN: + { + uint32_t did; + uint32_t stage; + uint32_t gain; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &stage)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &gain)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_tuner_if_gain(rpcd->dev, (int)stage, (int)gain); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_SET_TUNER_GAIN_MODE: + { + uint32_t did; + uint32_t manual; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &manual)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_tuner_gain_mode(rpcd->dev, (int)manual); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_SET_SAMPLE_RATE: + { + uint32_t did; + uint32_t rate; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &rate)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_sample_rate(rpcd->dev, rate); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_GET_SAMPLE_RATE: + { + uint32_t did; + uint32_t rate; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + rate = rtlsdr_get_sample_rate(rpcd->dev); + if (rate == 0) goto on_error; + if (rtlsdr_rpc_msg_push_uint32(r, rate)) goto on_error; + err = 0; + + break ; + } + + case RTLSDR_RPC_OP_SET_TESTMODE: + { + uint32_t did; + uint32_t on; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &on)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_testmode(rpcd->dev, (int)on); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_SET_AGC_MODE: + { + uint32_t did; + uint32_t on; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &on)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_agc_mode(rpcd->dev, (int)on); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_SET_DIRECT_SAMPLING: + { + uint32_t did; + uint32_t on; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &on)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_direct_sampling(rpcd->dev, (int)on); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_GET_DIRECT_SAMPLING: + { + uint32_t did; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_get_direct_sampling(rpcd->dev); + + break ; + } + + case RTLSDR_RPC_OP_SET_OFFSET_TUNING: + { + uint32_t did; + uint32_t on; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &on)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_offset_tuning(rpcd->dev, (int)on); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_GET_OFFSET_TUNING: + { + uint32_t did; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_get_offset_tuning(rpcd->dev); + + break ; + } + + case RTLSDR_RPC_OP_RESET_BUFFER: + { + uint32_t did; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_reset_buffer(rpcd->dev); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_READ_ASYNC: + { + uint32_t did; + uint32_t buf_num; + uint32_t buf_len; + rtlsdr_rpc_op_t new_op; + rtlsdr_rpc_msg_t* new_q; + rtlsdr_rpc_msg_t* new_r; + uint8_t id; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &buf_num)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &buf_len)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + /* prepare the reply here */ + id = rtlsdr_rpc_msg_get_id(q); + rtlsdr_rpc_msg_set_size(r, r->off); + rtlsdr_rpc_msg_set_op(r, op); + rtlsdr_rpc_msg_set_id(r, id); + rtlsdr_rpc_msg_set_err(r, 0); + + rpcd->async_id = id; + rpcd->async_replied = 0; + while (1) + { + rtlsdr_read_async + (rpcd->dev, read_async_cb, rpcd, buf_num, buf_len); + + if (rpcd->async_replied == 0) goto on_error; + + if (recv_query(rpcd, &new_q)) goto on_error; + + new_op = rtlsdr_rpc_msg_get_op(new_q); + + /* do not reply is cancel_async */ + if (new_op == RTLSDR_RPC_OP_CANCEL_ASYNC) return 0; + handle_query(rpcd, new_q, &new_r); + + if (new_r != NULL) send_reply(rpcd, new_r); + } + + /* do not resend reply */ + if (rpcd->async_replied) return 0; + goto on_error; + break ; + } + + case RTLSDR_RPC_OP_CANCEL_ASYNC: + { + uint32_t did; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + /* already cancelled if here */ + err = 0; + + break ; + } + + case RTLSDR_RPC_OP_READ_SYNC: + { + uint32_t did; + uint32_t len; + int n_read; + uint8_t* buf; + size_t new_size; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &len)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + new_size = r->off + sizeof(uint32_t) + len; + if (rtlsdr_rpc_msg_realloc(r, new_size)) goto on_error; + buf = r->fmt + r->off + sizeof(uint32_t); + + err = rtlsdr_read_sync(rpcd->dev, buf, (int)len, &n_read); + if (err) goto on_error; + + rtlsdr_rpc_msg_push_uint32_safe(r, (uint32_t)n_read); + rtlsdr_rpc_msg_skip_safe(r, (size_t)n_read); + + break ; + } + + default: + { + PRINTF("invalid op: %u\n", op); + break ; + } + } + + on_error: + rtlsdr_rpc_msg_set_size(r, r->off); + rtlsdr_rpc_msg_set_op(r, op); + rtlsdr_rpc_msg_set_id(r, rtlsdr_rpc_msg_get_id(q)); + rtlsdr_rpc_msg_set_err(r, err); + *rr = r; + return 0; +} + +static int do_rpcd(rpcd_t* rpcd) +{ + fd_set rset; + int max_fd; + + while (1) + { + FD_ZERO(&rset); + + if (rpcd->cli_sock != -1) + { + FD_SET(rpcd->cli_sock, &rset); + max_fd = rpcd->cli_sock; + } + else + { + FD_SET(rpcd->listen_sock, &rset); + max_fd = rpcd->listen_sock; + } + + if (select(max_fd + 1, &rset, NULL, NULL, NULL) <= 0) + { + ERROR(); + return -1; + } + + if (FD_ISSET(rpcd->listen_sock, &rset)) + { + PRINTF("new client\n"); + + rpcd->cli_sock = accept(rpcd->listen_sock, NULL, NULL); + if (rpcd->cli_sock == -1) + { + if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) + continue ; + ERROR(); + break ; + } + + if (fcntl(rpcd->cli_sock, F_SETFL, O_NONBLOCK)) + { + ERROR(); + break ; + } + } + else if (FD_ISSET(rpcd->cli_sock, &rset)) + { + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + + if (recv_query(rpcd, &q)) + { + PRINTF("cli closed\n"); + shutdown(rpcd->cli_sock, SHUT_RDWR); + close(rpcd->cli_sock); + rpcd->cli_sock = -1; + } + else if (q != NULL) + { + handle_query(rpcd, q, &r); + if (r != NULL) send_reply(rpcd, r); + } + } + } + + return 0; +} + +void usage(void) +{ + fprintf(stderr, + "rtl_rpcd, a Remote Procedure Call server for RTL-SDR dongles\n\n" + "Use:\trtl_rpcd [-options]\n" + "\t[-a address]\tAddress to listen on (default: 0.0.0.0 for all), or RTLSDR_RPC_SERV_ADDR\n" + "\t[-p port]\tPort number to listen on (default: 40000), or RTLSDR_RPC_SERV_PORT\n" + "\t[-I port_ir]\tinfrared sensor listen port (default: 0=none)\n" + "\t[-w wait_ir]\tinfrared sensor query wait interval usec (default: 10000)\n" + "\t[-h]\t\tHelp\n" + "\n" + "On the remote client, set RTLSDR_RPC_IS_ENABLED and matching address/port\n" + ); + exit(1); +} + +void *ir_thread_fn(void *arg) +{ + int r = 1; + struct linger ling = {1,0}; + int listensocket; + int irsocket; + struct sockaddr_in local, remote; + socklen_t rlen; + uint8_t buf[128]; + int ret = 0, len; + struct timeval tv; + fd_set writefds, errorfds; + + rpcd_t *rpcd = (rpcd_t *)arg; + + rtlsdr_dev_t *dev = rpcd->dev; + const char *addr = rpcd->addr_ir; + int port = rpcd->port_ir; + int wait = rpcd->wait_ir; + + + memset(&local,0,sizeof(local)); + local.sin_family = AF_INET; + local.sin_port = htons(port); + local.sin_addr.s_addr = inet_addr(addr); + + listensocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + r = 1; + setsockopt(listensocket, SOL_SOCKET, SO_REUSEADDR, (char *)&r, sizeof(int)); + setsockopt(listensocket, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling)); + bind(listensocket,(struct sockaddr *)&local,sizeof(local)); + + + while(1) { + printf("listening on IR port %d...\n", port); + listen(listensocket,1); + + irsocket = accept(listensocket,(struct sockaddr *)&remote, &rlen); + setsockopt(irsocket, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling)); + + printf("IR client accepted!\n"); + + while(1) { + dev = rpcd->dev; + if (!dev) { + printf("no device available yet\n"); + // Keep waiting until RPC connects and opens device + usleep(1000000); + continue; + } + + ret = rtlsdr_ir_query(dev, buf, sizeof(buf)); + if (ret < 0) { + printf("rtlsdr_ir_query error %d\n", ret); + break; + } + + len = ret; + + FD_ZERO(&writefds); + FD_SET(irsocket, &writefds); + + FD_ZERO(&errorfds); + FD_SET(irsocket, &errorfds); + + tv.tv_sec = 1; + tv.tv_usec = 0; + select(irsocket+1, NULL, &writefds, &errorfds, &tv); + + if (FD_ISSET(irsocket, &errorfds)) { + printf("error condition from irsocket, breaking\n"); + break; + } + + if (FD_ISSET(irsocket, &writefds)) { + ret = send(irsocket, buf, len, 0); + if (ret != len){ + printf("incomplete write to ir client: %d != %d\n", ret,len); + break; + } + } else { + printf("irsocket not ready to write, breaking\n"); + break; + } + + usleep(wait); + } + + close(irsocket); + } + + return 0; +} + + +int main(int ac, char** av) +{ + rpcd_t rpcd; + const char* addr = NULL; + const char* port = NULL; + + int opt; + int port_ir = 0, wait_ir = 10000; + pthread_t thread_ir; + + while ((opt = getopt(ac, av, "a:p:I:w:h")) != -1) { + switch (opt) { + case 'a': + addr = optarg; + break; + case 'p': + port = optarg; + break; + case 'I': + port_ir = atoi(optarg); + break; + case 'w': + wait_ir = atoi(optarg); + break; + case 'h': + default: + usage(); + } + } + + if (addr == NULL) addr = getenv("RTLSDR_RPC_SERV_ADDR"); + if (addr == NULL) addr = "0.0.0.0"; + + if (port == NULL) port = getenv("RTLSDR_RPC_SERV_PORT"); + if (port == NULL) port = "40000"; + + if (init_rpcd(&rpcd, addr, port)) return -1; + if (port_ir) { + rpcd.port_ir = port_ir; + rpcd.addr_ir = addr; + rpcd.wait_ir = wait_ir; + pthread_create(&thread_ir, NULL, &ir_thread_fn, (void *)(&rpcd)); + } + + do_rpcd(&rpcd); + fini_rpcd(&rpcd); + + return 0; +} diff --git a/src/rtl_tcp.c b/src/rtl_tcp.c index 317e0f3..76ab63b 100644 --- a/src/rtl_tcp.c +++ b/src/rtl_tcp.c @@ -37,6 +37,9 @@ #include "getopt/getopt.h" #endif +#ifdef NEED_PTHREADS_WORKARROUND +#define HAVE_STRUCT_TIMESPEC +#endif #include #include "rtl-sdr.h" @@ -78,6 +81,8 @@ typedef struct { /* structure size must be multiple of 2 bytes */ static rtlsdr_dev_t *dev = NULL; +static int verbosity = 0; +static uint32_t bandwidth = 0; static int global_numq = 0; static struct llist *ll_buffers = 0; static int llbuf_num = 500; @@ -89,13 +94,18 @@ void usage(void) printf("rtl_tcp, an I/Q spectrum server for RTL2832 based DVB-T receivers\n\n" "Usage:\t[-a listen address]\n" "\t[-p listen port (default: 1234)]\n" + "\t[-I infrared sensor listen port (default: 0=none)]\n" + "\t[-W infrared sensor query wait interval usec (default: 10000)]\n" "\t[-f frequency to tune to [Hz]]\n" - "\t[-g gain (default: 0 for auto)]\n" + "\t[-g gain in dB (default: 0 for auto)]\n" "\t[-s samplerate in Hz (default: 2048000 Hz)]\n" "\t[-b number of buffers (default: 15, set by library)]\n" + "\t[-l length of single buffer in units of 512 samples (default: 64 was 256)]\n" "\t[-n max number of linked list buffers to keep (default: 500)]\n" + "\t[-w rtlsdr tuner bandwidth [Hz] (for R820T and E4000 tuners)]\n" "\t[-d device index (default: 0)]\n" - "\t[-P ppm_error (default: 0)]\n"); + "\t[-P ppm_error (default: 0)]\n" + "\t[-v increase verbosity (default: 0)]\n"); exit(1); } @@ -174,10 +184,13 @@ void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) cur->next = rpt; - if (num_queued > global_numq) - printf("ll+, now %d\n", num_queued); - else if (num_queued < global_numq) - printf("ll-, now %d\n", num_queued); + if ( verbosity ) + { + if (num_queued > global_numq) + printf("ll+, now %d\n", num_queued); + else if (num_queued < global_numq) + printf("ll-, now %d\n", num_queued); + } global_numq = num_queued; } @@ -202,7 +215,7 @@ static void *tcp_worker(void *arg) pthread_mutex_lock(&ll_mutex); gettimeofday(&tp, NULL); - ts.tv_sec = tp.tv_sec+5; + ts.tv_sec = tp.tv_sec+1; ts.tv_nsec = tp.tv_usec * 1000; r = pthread_cond_timedwait(&cond, &ll_mutex, &ts); if(r == ETIMEDOUT) { @@ -256,6 +269,8 @@ static int set_gain_by_index(rtlsdr_dev_t *_dev, unsigned int index) count = rtlsdr_get_tuner_gains(_dev, gains); res = rtlsdr_set_tuner_gain(_dev, gains[index]); + if (verbosity) + fprintf(stderr, "set tuner gain to %.1f dB\n", gains[index] / 10.0); free(gains); } @@ -302,59 +317,65 @@ static void *command_worker(void *arg) } } switch(cmd.cmd) { - case 0x01: + case SET_FREQUENCY: printf("set freq %d\n", ntohl(cmd.param)); rtlsdr_set_center_freq(dev,ntohl(cmd.param)); break; - case 0x02: + case SET_SAMPLE_RATE: printf("set sample rate %d\n", ntohl(cmd.param)); rtlsdr_set_sample_rate(dev, ntohl(cmd.param)); + /*verbose_set_bandwidth(dev, bandwidth);*/ break; - case 0x03: + case SET_GAIN_MODE: printf("set gain mode %d\n", ntohl(cmd.param)); rtlsdr_set_tuner_gain_mode(dev, ntohl(cmd.param)); break; - case 0x04: + case SET_GAIN: printf("set gain %d\n", ntohl(cmd.param)); rtlsdr_set_tuner_gain(dev, ntohl(cmd.param)); break; - case 0x05: + case SET_FREQUENCY_CORRECTION: printf("set freq correction %d\n", ntohl(cmd.param)); rtlsdr_set_freq_correction(dev, ntohl(cmd.param)); break; - case 0x06: + case SET_IF_STAGE: tmp = ntohl(cmd.param); printf("set if stage %d gain %d\n", tmp >> 16, (short)(tmp & 0xffff)); rtlsdr_set_tuner_if_gain(dev, tmp >> 16, (short)(tmp & 0xffff)); break; - case 0x07: + case SET_TEST_MODE: printf("set test mode %d\n", ntohl(cmd.param)); rtlsdr_set_testmode(dev, ntohl(cmd.param)); break; - case 0x08: + case SET_AGC_MODE: printf("set agc mode %d\n", ntohl(cmd.param)); rtlsdr_set_agc_mode(dev, ntohl(cmd.param)); break; - case 0x09: + case SET_DIRECT_SAMPLING: printf("set direct sampling %d\n", ntohl(cmd.param)); rtlsdr_set_direct_sampling(dev, ntohl(cmd.param)); break; - case 0x0a: + case SET_OFFSET_TUNING: printf("set offset tuning %d\n", ntohl(cmd.param)); rtlsdr_set_offset_tuning(dev, ntohl(cmd.param)); break; - case 0x0b: + case SET_RTL_CRYSTAL: printf("set rtl xtal %d\n", ntohl(cmd.param)); rtlsdr_set_xtal_freq(dev, ntohl(cmd.param), 0); break; - case 0x0c: + case SET_TUNER_CRYSTAL: printf("set tuner xtal %d\n", ntohl(cmd.param)); rtlsdr_set_xtal_freq(dev, 0, ntohl(cmd.param)); break; - case 0x0d: + case SET_TUNER_GAIN_BY_INDEX: printf("set tuner gain by index %d\n", ntohl(cmd.param)); set_gain_by_index(dev, ntohl(cmd.param)); break; + case SET_TUNER_BANDWIDTH: + bandwidth = ntohl(cmd.param); + printf("set tuner bandwidth to %i Hz\n", bandwidth); + verbose_set_bandwidth(dev, bandwidth); + break; default: break; } @@ -362,14 +383,99 @@ static void *command_worker(void *arg) } } +struct ir_thread_data +{ + rtlsdr_dev_t *dev; + SOCKET port; + int wait; + char *addr; +}; + +void *ir_thread_fn(void *arg) +{ + int r = 1; + struct linger ling = {1,0}; + SOCKET listensocket; + SOCKET irsocket; + struct sockaddr_in local, remote; + socklen_t rlen; + uint8_t buf[128]; + int ret = 0, len; + + struct ir_thread_data *data = (struct ir_thread_data *)arg; + + rtlsdr_dev_t *dev = data->dev; + int port = data->port; + int wait = data->wait; + char *addr = data->addr; + + + memset(&local,0,sizeof(local)); + local.sin_family = AF_INET; + local.sin_port = htons(port); + local.sin_addr.s_addr = inet_addr(addr); + + listensocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + r = 1; + setsockopt(listensocket, SOL_SOCKET, SO_REUSEADDR, (char *)&r, sizeof(int)); + setsockopt(listensocket, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling)); + bind(listensocket,(struct sockaddr *)&local,sizeof(local)); + + + while(1) { + printf("listening on IR port %d...\n", port); + listen(listensocket,1); + + irsocket = accept(listensocket,(struct sockaddr *)&remote, &rlen); + setsockopt(irsocket, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling)); + + printf("IR client accepted!\n"); + + while(1) { + ret = rtlsdr_ir_query(dev, buf, sizeof(buf)); + if (ret < 0) { + printf("rtlsdr_ir_query error %d\n", ret); + break; + } + + len = ret; + + ret = send(irsocket, buf, len, 0); + if (ret != len){ + printf("incomplete write to ir client: %d != %d\n", ret,len); + break; + } + + usleep(wait); + } + + closesocket(irsocket); + } + + return 0; +} + int main(int argc, char **argv) { int r, opt, i; char* addr = "127.0.0.1"; int port = 1234; + int port_ir = 0; + int wait_ir = 10000; + pthread_t thread_ir; uint32_t frequency = 100000000, samp_rate = 2048000; struct sockaddr_in local, remote; uint32_t buf_num = 0; + /* buf_len: + * -> 256 -> 262 ms @ 250 kS or 20.48 ms @ 3.2 MS (internal default) + * -> 128 -> 131 ms @ 250 kS or 10.24 ms @ 3.2 MS + * -> 64 -> 65 ms @ 250 kS or 5.12 ms @ 3.2 MS (new default) + * + * usual soundcard as reference: + * 512 samples @ 48 kHz ~= 10.6 ms + * 512 samples @ 8 kHz = 64 ms + */ + uint32_t buf_len = 64; int dev_index = 0; int dev_given = 0; int gain = 0; @@ -384,6 +490,7 @@ int main(int argc, char **argv) fd_set readfds; u_long blockmode = 1; dongle_info_t dongle_info; + int gains[100]; #ifdef _WIN32 WSADATA wsd; i = WSAStartup(MAKEWORD(2,2), &wsd); @@ -391,7 +498,7 @@ int main(int argc, char **argv) struct sigaction sigact, sigign; #endif - while ((opt = getopt(argc, argv, "a:p:f:g:s:b:n:d:P:")) != -1) { + while ((opt = getopt(argc, argv, "a:p:I:W:f:g:s:b:l:n:d:P:w:v")) != -1) { switch (opt) { case 'd': dev_index = verbose_device_search(optarg); @@ -412,15 +519,30 @@ int main(int argc, char **argv) case 'p': port = atoi(optarg); break; + case 'I': + port_ir = atoi(optarg); + break; + case 'W': + wait_ir = atoi(optarg); + break; case 'b': buf_num = atoi(optarg); break; + case 'l': + buf_len = 512 * atoi(optarg); + break; case 'n': llbuf_num = atoi(optarg); break; case 'P': ppm_error = atoi(optarg); break; + case 'w': + bandwidth = (uint32_t)atofs(optarg); + break; + case 'v': + ++verbosity; + break; default: usage(); break; @@ -430,6 +552,9 @@ int main(int argc, char **argv) if (argc < optind) usage(); + if (verbosity) + fprintf(stderr, "verbosity set to %d\n", verbosity); + if (!dev_given) { dev_index = verbose_device_search("0"); } @@ -491,6 +616,8 @@ int main(int argc, char **argv) fprintf(stderr, "Tuner gain set to %f dB.\n", gain/10.0); } + verbose_set_bandwidth(dev, bandwidth); + /* Reset endpoint before we start reading from it (mandatory) */ r = rtlsdr_reset_buffer(dev); if (r < 0) @@ -502,6 +629,13 @@ int main(int argc, char **argv) pthread_cond_init(&cond, NULL); pthread_cond_init(&exit_cond, NULL); + if (port_ir) { + struct ir_thread_data data = {.dev = dev, .port = port_ir, .wait = wait_ir, .addr = addr}; + + pthread_create(&thread_ir, NULL, &ir_thread_fn, (void *)(&data)); + } + + memset(&local,0,sizeof(local)); local.sin_family = AF_INET; local.sin_port = htons(port); @@ -555,9 +689,16 @@ int main(int argc, char **argv) if (r >= 0) dongle_info.tuner_type = htonl(r); - r = rtlsdr_get_tuner_gains(dev, NULL); + r = rtlsdr_get_tuner_gains(dev, gains); if (r >= 0) dongle_info.tuner_gain_count = htonl(r); + if (verbosity) + { + fprintf(stderr, "Supported gain values (%d): ", r); + for (i = 0; i < r; i++) + fprintf(stderr, "%.1f ", gains[i] / 10.0); + fprintf(stderr, "\n"); + } r = send(s, (const char *)&dongle_info, sizeof(dongle_info), 0); if (sizeof(dongle_info) != r) @@ -569,7 +710,7 @@ int main(int argc, char **argv) r = pthread_create(&command_thread, &attr, command_worker, NULL); pthread_attr_destroy(&attr); - r = rtlsdr_read_async(dev, rtlsdr_callback, NULL, buf_num, 0); + r = rtlsdr_read_async(dev, rtlsdr_callback, NULL, buf_num, buf_len); pthread_join(tcp_worker_thread, &status); pthread_join(command_thread, &status); @@ -594,6 +735,7 @@ int main(int argc, char **argv) out: rtlsdr_close(dev); closesocket(listensocket); + //if (port_ir) pthread_join(thread_ir, &status); closesocket(s); #ifdef _WIN32 WSACleanup(); diff --git a/src/rtl_test.c b/src/rtl_test.c index 9a6cfda..b3b74ba 100644 --- a/src/rtl_test.c +++ b/src/rtl_test.c @@ -48,7 +48,7 @@ #define MINIMAL_BUF_LENGTH 512 #define MAXIMAL_BUF_LENGTH (256 * 16384) -#define MHZ(x) ((x)*1000*1000) +#define MHZ(x) ((x)*1000*1000) #define PPM_DURATION 10 #define PPM_DUMP_TIME 5 diff --git a/src/rtlsdr_rpc.c b/src/rtlsdr_rpc.c new file mode 100644 index 0000000..b51fb48 --- /dev/null +++ b/src/rtlsdr_rpc.c @@ -0,0 +1,1342 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rtlsdr_rpc_msg.h" + + +#if 1 +#include +#define PRINTF(__s, ...) fprintf(stderr, __s, ##__VA_ARGS__) +#define TRACE() PRINTF("[t] %s,%u\n", __FILE__, __LINE__) +#define ERROR() PRINTF("[e] %s,%u\n", __FILE__, __LINE__) +#define UNIMPL() PRINTF("[u] %s,%u\n", __FILE__, __LINE__) +#else +#define PRINTF(...) +#define TRACE() +#define ERROR() +#define UNIMPL() +#endif + + +typedef struct +{ + volatile unsigned int is_locked; + volatile unsigned int is_init; + int sock; + +#define QR_COUNT 32 + pthread_mutex_t qr_lock; + volatile uint32_t qr_mask; + volatile uint32_t qr_box; + rtlsdr_rpc_msg_t query[QR_COUNT]; + rtlsdr_rpc_msg_t reply[QR_COUNT]; + + pthread_mutex_t send_lock; + pthread_mutex_t recv_lock; + + volatile unsigned int is_async_cancel; + +} rtlsdr_rpc_cli_t; + +static rtlsdr_rpc_cli_t rtlsdr_rpc_cli = { 0, }; + +typedef void (*rtlsdr_rpc_read_async_cb_t) +(unsigned char*, uint32_t, void*); + +typedef struct rtlsdr_rpc_dev +{ + uint32_t index; + size_t gain_count; + rtlsdr_rpc_cli_t* cli; +} rtlsdr_rpc_dev_t; + +static int resolve_ip_addr +( + struct sockaddr_storage saddr_both[2], size_t size_both[2], + const char* addr, const char* port +) +{ + struct addrinfo ai; + struct addrinfo* aip = NULL; + int err = -1; + size_t i; + + memset(&ai, 0, sizeof(ai)); + ai.ai_family = AF_UNSPEC; + ai.ai_socktype = SOCK_STREAM; + ai.ai_flags = AI_PASSIVE; + + if (getaddrinfo(addr, port, &ai, &aip)) goto on_error; + + size_both[0] = 0; + size_both[1] = 0; + i = 0; + for (; (i != 2) && (aip != NULL); aip = aip->ai_next) + { + if ((aip->ai_family != AF_INET) && (aip->ai_family != AF_INET6)) continue ; + if (aip->ai_addrlen == 0) continue ; + memcpy(&saddr_both[i], aip->ai_addr, aip->ai_addrlen); + size_both[i] = aip->ai_addrlen; + ++i; + } + + if (i == 0) goto on_error; + + err = 0; + on_error: + if (aip != NULL) freeaddrinfo(aip); + return err; +} + +static int open_socket +( + struct sockaddr_storage saddr_both[2], size_t size_both[2], + int type, int proto, + struct sockaddr_storage** saddr_used, size_t* size_used +) +{ + size_t i; + int fd; + + for (i = 0; (i != 2) && (size_both[i]); ++i) + { + const struct sockaddr* const sa = (const struct sockaddr*)&saddr_both[i]; + fd = socket(sa->sa_family, type, proto); + if (fd != -1) break ; + } + + if ((i == 2) || (size_both[i] == 0)) return -1; + + *saddr_used = &saddr_both[i]; + *size_used = size_both[i]; + + return fd; +} + +static int init_cli(rtlsdr_rpc_cli_t* cli) +{ + struct sockaddr_storage saddrs[2]; + struct sockaddr_storage* saddr; + size_t sizes[2]; + size_t size; + const char* addr; + const char* port; + size_t i; + size_t j; + int err = -1; + + /* no better way in this case ... */ + while (cli->is_locked) usleep(10000); + cli->is_locked = 1; + + if (cli->is_init) goto on_success; + + addr = getenv("RTLSDR_RPC_SERV_ADDR"); + if (addr == NULL) addr = "127.0.0.1"; + + port = getenv("RTLSDR_RPC_SERV_PORT"); + if (port == NULL) port = "40000"; + + if (resolve_ip_addr(saddrs, sizes, addr, port)) + { + ERROR(); + goto on_error_0; + } + + cli->sock = open_socket + (saddrs, sizes, SOCK_STREAM, IPPROTO_TCP, &saddr, &size); + if (cli->sock == -1) + { + ERROR(); + goto on_error_0; + } + + if (connect(cli->sock, (const struct sockaddr*)saddr, (socklen_t)size)) + { + ERROR(); + goto on_error_1; + } + + if (fcntl(cli->sock, F_SETFL, O_NONBLOCK)) + { + ERROR(); + goto on_error_1; + } + + for (i = 0; i != QR_COUNT; ++i) + { + rtlsdr_rpc_msg_t* const q = &cli->query[i]; + rtlsdr_rpc_msg_t* const r = &cli->reply[i]; + + if (rtlsdr_rpc_msg_init(q, 0)) + goto on_error_2; + + if (rtlsdr_rpc_msg_init(r, 0)) + { + rtlsdr_rpc_msg_fini(q); + goto on_error_2; + } + } + pthread_mutex_init(&cli->qr_lock, NULL); + cli->qr_mask = 0; + cli->qr_box = 0; + + pthread_mutex_init(&cli->send_lock, NULL); + pthread_mutex_init(&cli->recv_lock, NULL); + + cli->is_init = 1; + + on_success: + err = 0; + goto on_error_0; + + on_error_2: + for (j = 0; j != i; ++j) + { + rtlsdr_rpc_msg_fini(&cli->query[j]); + rtlsdr_rpc_msg_fini(&cli->reply[j]); + } + + on_error_1: + shutdown(cli->sock, SHUT_RDWR); + close(cli->sock); + + on_error_0: + cli->is_locked = 0; + return err; +} + +__attribute__((unused)) +static int fini_cli(rtlsdr_rpc_cli_t* cli) +{ + size_t i; + + for (i = 0; i != QR_COUNT; ++i) + { + rtlsdr_rpc_msg_fini(&cli->query[i]); + rtlsdr_rpc_msg_fini(&cli->reply[i]); + } + pthread_mutex_destroy(&cli->qr_lock); + + pthread_mutex_destroy(&cli->send_lock); + pthread_mutex_destroy(&cli->recv_lock); + + shutdown(cli->sock, SHUT_RDWR); + close(cli->sock); + + return 0; +} + +static int alloc_qr +(rtlsdr_rpc_cli_t* cli, rtlsdr_rpc_msg_t** q, rtlsdr_rpc_msg_t** r) +{ + size_t i; + + pthread_mutex_lock(&cli->qr_lock); + for (i = 0; i != QR_COUNT; ++i) + { + const uint32_t m = 1 << i; + if ((cli->qr_mask & m) == 0) + { + cli->qr_mask |= m; + break ; + } + } + pthread_mutex_unlock(&cli->qr_lock); + + if (i == QR_COUNT) return -1; + + *q = &cli->query[i]; + *r = &cli->reply[i]; + + /* set the query id */ + rtlsdr_rpc_msg_reset(*q); + rtlsdr_rpc_msg_set_id(*q, (uint8_t)i); + + return 0; +} + +static void free_qr +(rtlsdr_rpc_cli_t* cli, rtlsdr_rpc_msg_t* q, rtlsdr_rpc_msg_t* r) +{ + const uint32_t m = 1 << (uint32_t)rtlsdr_rpc_msg_get_id(q); + pthread_mutex_lock(&cli->qr_lock); + cli->qr_mask &= ~m; + pthread_mutex_unlock(&cli->qr_lock); +} + +static int recv_all(int fd, uint8_t* buf, size_t size) +{ + ssize_t n; + fd_set rset; + + while (1) + { + errno = 0; + n = recv(fd, buf, size, 0); + if (n <= 0) + { + if ((errno != EWOULDBLOCK) || (errno != EAGAIN)) + return -1; + } + else + { + size -= (size_t)n; + buf += (size_t)n; + if (size == 0) break ; + } + + FD_ZERO(&rset); + FD_SET(fd, &rset); + if (select(fd + 1, &rset, NULL, NULL, NULL) <= 0) + { + return -1; + } + } + + return 0; +} + +static int send_all(int fd, const uint8_t* buf, size_t size) +{ + ssize_t n; + fd_set wset; + + while (1) + { + errno = 0; + n = send(fd, buf, size, 0); + if (n <= 0) + { + if ((errno != EWOULDBLOCK) || (errno != EAGAIN)) + return -1; + } + else + { + size -= (size_t)n; + buf += (size_t)n; + if (size == 0) break ; + } + + FD_ZERO(&wset); + FD_SET(fd, &wset); + if (select(fd + 1, NULL, &wset, NULL, NULL) <= 0) + { + return -1; + } + } + + return 0; +} + +static int recv_msg +(rtlsdr_rpc_cli_t* cli, uint8_t id, rtlsdr_rpc_msg_t* m) +{ + static const size_t fmt_size = offsetof(rtlsdr_rpc_fmt_t, data); + const uint32_t mask = 1 << (uint32_t)id; + const int fd = cli->sock; + uint32_t size; + uint8_t to_id; + int err = -1; + rtlsdr_rpc_msg_t* to_m; + + pthread_mutex_lock(&cli->recv_lock); + + if (cli->qr_box & mask) + { + cli->qr_box &= ~mask; + goto on_success; + } + + while (1) + { + /* receive next message */ + if (recv_all(fd, m->fmt, fmt_size)) goto on_error; + + /* get destination message by id */ + to_id = rtlsdr_rpc_msg_get_id(m); + + if (to_id >= QR_COUNT) goto on_error; + to_m = &cli->reply[to_id]; + if (to_id != id) memcpy(to_m->fmt, m->fmt, fmt_size); + + size = rtlsdr_rpc_msg_get_size(to_m); + if (size < fmt_size) goto on_error; + + if (rtlsdr_rpc_msg_realloc(to_m, size)) goto on_error; + + size -= fmt_size; + if (size) + { + if (recv_all(fd, to_m->fmt + fmt_size, size)) goto on_error; + } + + if (to_id == id) goto on_success; + + /* message not for this query, forward */ + cli->qr_box |= 1 << (uint32_t)to_id; + } + + on_success: + err = 0; + on_error: + pthread_mutex_unlock(&cli->recv_lock); + return err; +} + +static int send_msg(rtlsdr_rpc_cli_t* cli, rtlsdr_rpc_msg_t* m) +{ + int err; + + rtlsdr_rpc_msg_set_size(m, (uint32_t)m->off); + + pthread_mutex_lock(&cli->send_lock); + err = send_all(cli->sock, m->fmt, m->off); + pthread_mutex_unlock(&cli->send_lock); + + return err; +} + +static int send_recv_msg +(rtlsdr_rpc_cli_t* cli, rtlsdr_rpc_msg_t* q, rtlsdr_rpc_msg_t* r) +{ + const uint8_t id = rtlsdr_rpc_msg_get_id(q); + + if (send_msg(cli, q)) return -1; + if (recv_msg(cli, id, r)) return -1; + rtlsdr_rpc_msg_reset(r); + + return 0; +} + +static int send_flush_msgs +(rtlsdr_rpc_cli_t* cli, rtlsdr_rpc_msg_t* q) +{ + struct timeval tm; + ssize_t n; + fd_set rset; + uint8_t buf[256]; + + if (send_msg(cli, q)) return -1; + + pthread_mutex_lock(&cli->recv_lock); + + while (1) + { + FD_ZERO(&rset); + FD_SET(cli->sock, &rset); + tm.tv_sec = 0; + tm.tv_usec = 200000; + if (select(cli->sock + 1, &rset, NULL, NULL, &tm) < 0) break ; + if (recv(cli->sock, buf, sizeof(buf), 0) <= 0) break ; + } + + pthread_mutex_unlock(&cli->recv_lock); + + return 0; +} + +uint32_t rtlsdr_rpc_get_device_count(void) +{ + rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + uint32_t n = 0; + + if (init_cli(cli)) goto on_error_0; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_DEVICE_COUNT); + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + if (rtlsdr_rpc_msg_pop_uint32(r, &n)) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return n; +} + +const char* rtlsdr_rpc_get_device_name +( + uint32_t index +) +{ + rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + const char* s = NULL; + + if (init_cli(cli)) goto on_error_0; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_DEVICE_NAME); + if (rtlsdr_rpc_msg_push_uint32(q, index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; + /* TODO: memory leak here */ + s = strdup(s); + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return s; +} + +int rtlsdr_rpc_get_device_usb_strings +(uint32_t index, char* manufact, char* product, char* serial) +{ + rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + const char* s; + int err = -1; + + if (init_cli(cli)) goto on_error_0; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_DEVICE_USB_STRINGS); + if (rtlsdr_rpc_msg_push_uint32(q, index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; + strcpy(manufact, s); + if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; + strcpy(product, s); + if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; + strcpy(serial, s); + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_get_index_by_serial(const char* serial) +{ + rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (init_cli(cli)) goto on_error_0; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_INDEX_BY_SERIAL); + if (rtlsdr_rpc_msg_push_str(q, serial)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_open(void** devp, uint32_t index) +{ + rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + rtlsdr_rpc_dev_t* dev; + int err = -1; + + *devp = NULL; + + if (init_cli(cli)) goto on_error_0; + + dev = malloc(sizeof(rtlsdr_rpc_dev_t)); + if (dev == NULL) goto on_error_0; + + if (alloc_qr(cli, &q, &r)) goto on_error_1; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_OPEN); + if (rtlsdr_rpc_msg_push_uint32(q, index)) goto on_error_2; + + if (send_recv_msg(cli, q, r)) goto on_error_2; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_2; + + dev->index = index; + dev->gain_count = 32; + dev->cli = cli; + *devp = dev; + + on_error_2: + free_qr(cli, q, r); + on_error_1: + if (err) free(dev); + on_error_0: + return err; +} + +int rtlsdr_rpc_close(void* devp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_CLOSE); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + free(dev); + return err; +} + +int rtlsdr_rpc_set_xtal_freq +(void* devp, uint32_t rtl_freq, uint32_t tuner_freq) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_XTAL_FREQ); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, rtl_freq)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, tuner_freq)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_get_xtal_freq +(void* devp, uint32_t* rtl_freq, uint32_t* tuner_freq) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_XTAL_FREQ); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + if (rtlsdr_rpc_msg_pop_uint32(r, rtl_freq)) + { + err = -1; + goto on_error_1; + } + + if (rtlsdr_rpc_msg_pop_uint32(r, tuner_freq)) + { + err = -1; + goto on_error_1; + } + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_get_usb_strings +(void* devp, char* manufact, char* product, char* serial) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + const char* s; + int err = -1; + + if (init_cli(cli)) goto on_error_0; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_USB_STRINGS); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; + strcpy(manufact, s); + if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; + strcpy(product, s); + if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; + strcpy(serial, s); + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_write_eeprom +(void* dev, uint8_t* data, uint8_t offset, uint16_t len) +{ + UNIMPL(); + return -1; +} + +int rtlsdr_rpc_read_eeprom +(void* dev, uint8_t* data, uint8_t offset, uint16_t len) +{ + UNIMPL(); + return -1; +} + +int rtlsdr_rpc_set_center_freq(void* devp, uint32_t freq) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_CENTER_FREQ); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, freq)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +uint32_t rtlsdr_rpc_get_center_freq(void* devp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + uint32_t freq = 0; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_CENTER_FREQ); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + if (rtlsdr_rpc_msg_get_err(r)) goto on_error_1; + if (rtlsdr_rpc_msg_pop_uint32(r, &freq)) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return freq; +} + +int rtlsdr_rpc_set_freq_correction(void* devp, int ppm) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_FREQ_CORRECTION); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)ppm)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_get_freq_correction(void* devp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_FREQ_CORRECTION); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_get_tuner_type(void* devp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_TUNER_TYPE); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_get_tuner_gains(void* devp, int* gainsp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + const uint32_t is_null = (gainsp == NULL); + const uint32_t gain_size = (uint32_t)dev->gain_count * sizeof(int); + const uint8_t* tmp; + size_t size; + int err = 0; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_TUNER_GAINS); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, is_null)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, gain_size)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + + if (err <= 0) goto on_error_1; + + dev->gain_count = (size_t)err; + + if (is_null == 0) + { + if (rtlsdr_rpc_msg_pop_buf(r, &tmp, &size)) + { + err = 0; + goto on_error_1; + } + + /* TODO: endianess */ + memcpy(gainsp, tmp, size); + } + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_set_tuner_gain(void* devp, int gain) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_TUNER_GAIN); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)gain)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_get_tuner_gain(void* devp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_TUNER_GAIN); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_set_tuner_if_gain(void* devp, int stage, int gain) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_TUNER_IF_GAIN); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)stage)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)gain)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_set_tuner_gain_mode(void* devp, int manual) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_TUNER_GAIN_MODE); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, manual)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_set_sample_rate(void* devp, uint32_t rate) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_SAMPLE_RATE); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, rate)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +uint32_t rtlsdr_rpc_get_sample_rate(void* devp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + uint32_t rate = 0; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_SAMPLE_RATE); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + if (rtlsdr_rpc_msg_get_err(r)) goto on_error_1; + if (rtlsdr_rpc_msg_pop_uint32(r, &rate)) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return rate; +} + +int rtlsdr_rpc_set_testmode(void* devp, int on) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_TESTMODE); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)on)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_set_agc_mode(void* devp, int on) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_AGC_MODE); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)on)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_set_direct_sampling(void* devp, int on) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_DIRECT_SAMPLING); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)on)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_get_direct_sampling(void* devp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_DIRECT_SAMPLING); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_set_offset_tuning(void* devp, int on) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_OFFSET_TUNING); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)on)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_get_offset_tuning(void* devp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_OFFSET_TUNING); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_reset_buffer(void* devp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_RESET_BUFFER); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_read_sync +(void* devp, void* buf, int len, int* n_read) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + const uint8_t* tmp; + size_t size; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_READ_SYNC); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)len)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + if (rtlsdr_rpc_msg_pop_buf(r, &tmp, &size)) + { + err = -1; + goto on_error_1; + } + + if (size > (size_t)len) size = len; + + memcpy(buf, tmp, size); + + *n_read = (int)size; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + + +static volatile unsigned int is_cancel; +int rtlsdr_rpc_read_async +( + void* devp, + rtlsdr_rpc_read_async_cb_t cb, void* ctx, + uint32_t buf_num, uint32_t buf_len +) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + uint8_t id; + size_t size; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + id = rtlsdr_rpc_msg_get_id(q); + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_READ_ASYNC); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, buf_num)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, buf_len)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + cli->is_async_cancel = 0; + while (cli->is_async_cancel == 0) + { + static const size_t off = offsetof(rtlsdr_rpc_fmt_t, data); + + if (recv_msg(cli, id, r)) + { + err = -1; + goto on_error_1; + } + + size = rtlsdr_rpc_msg_get_size(r); + cb(r->fmt + off, size - off, ctx); + } + + rtlsdr_rpc_msg_reset(q); + rtlsdr_rpc_msg_set_id(q, id); + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_CANCEL_ASYNC); + rtlsdr_rpc_msg_push_uint32(q, dev->index); + send_flush_msgs(cli, q); + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_wait_async +( + void* dev, + rtlsdr_rpc_read_async_cb_t cb, void* ctx +) +{ + return rtlsdr_rpc_read_async(dev, cb, ctx, 0, 0); +} + +int rtlsdr_rpc_cancel_async(void* devp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + cli->is_async_cancel = 1; + return 0; +} + +unsigned int rtlsdr_rpc_is_enabled(void) +{ + static unsigned int is_enabled = (unsigned int)-1; + if (is_enabled == (unsigned int)-1) + is_enabled = (getenv("RTLSDR_RPC_IS_ENABLED") != NULL); + return is_enabled; +} diff --git a/src/rtlsdr_rpc_msg.c b/src/rtlsdr_rpc_msg.c new file mode 100644 index 0000000..ea9e787 --- /dev/null +++ b/src/rtlsdr_rpc_msg.c @@ -0,0 +1,298 @@ +#include +#include +#include +#include +#include "rtlsdr_rpc_msg.h" + +#if 1 +#include +#define PRINTF(__s, ...) fprintf(stderr, __s, ##__VA_ARGS__) +#define TRACE() PRINTF("[t] %s,%u\n", __FILE__, __LINE__) +#define ERROR() PRINTF("[e] %s,%u\n", __FILE__, __LINE__) +#else +#define TRACE() +#define ERROR() +#define PRINTF(...) +#endif + + +int rtlsdr_rpc_msg_init(rtlsdr_rpc_msg_t* msg, size_t data_size) +{ + size_t fmt_size; + + if (data_size == 0) data_size = 64; + + fmt_size = offsetof(rtlsdr_rpc_fmt_t, data) + data_size; + msg->fmt = malloc(fmt_size); + if (msg->fmt == NULL) return -1; + + msg->off = offsetof(rtlsdr_rpc_fmt_t, data); + msg->size = fmt_size; + + return 0; +} + +int rtlsdr_rpc_msg_fini(rtlsdr_rpc_msg_t* msg) +{ + free(msg->fmt); + return 0; +} + +void rtlsdr_rpc_msg_reset(rtlsdr_rpc_msg_t* msg) +{ + msg->off = offsetof(rtlsdr_rpc_fmt_t, data); +} + +int rtlsdr_rpc_msg_realloc(rtlsdr_rpc_msg_t* msg, size_t size) +{ + uint8_t* new_fmt; + + if (msg->size >= size) return 0; + + new_fmt = malloc(size); + if (new_fmt == NULL) return -1; + + memcpy(new_fmt, msg->fmt, msg->off); + free(msg->fmt); + msg->fmt = new_fmt; + msg->size = size; + + return 0; +} + +static int check_size(const rtlsdr_rpc_msg_t* msg, size_t size) +{ + if ((msg->off + size) > msg->size) return -1; + return 0; +} + +static int check_size_or_realloc(rtlsdr_rpc_msg_t* msg, size_t size) +{ + uint8_t* new_fmt; + size_t new_size; + + if (check_size(msg, size) == 0) return 0; + + new_size = (msg->off + size + 256) & ~(256 - 1); + new_fmt = malloc(new_size); + if (new_fmt == NULL) return -1; + + memcpy(new_fmt, msg->fmt, msg->off); + free(msg->fmt); + + msg->fmt = new_fmt; + msg->size = new_size; + + return 0; +} + +static int pop_uint32(rtlsdr_rpc_msg_t* msg, uint32_t* x) +{ +#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#error "unsupported endianness" +#endif + + if (check_size(msg, sizeof(uint32_t))) return -1; + *x = *(const uint32_t*)(msg->fmt + msg->off); + msg->off += sizeof(uint32_t); + return 0; +} + +static void push_mem_safe(rtlsdr_rpc_msg_t* msg, const uint8_t* x, size_t n) +{ + memcpy(msg->fmt + msg->off, x, n); + msg->off += n; +} + +static void push_uint32_safe(rtlsdr_rpc_msg_t* msg, uint32_t x) +{ +#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#error "unsupported endianness" +#endif + + push_mem_safe(msg, (const uint8_t*)&x, sizeof(uint32_t)); +} + +int rtlsdr_rpc_msg_push_int32(rtlsdr_rpc_msg_t* msg, int x) +{ + if (check_size_or_realloc(msg, sizeof(x))) return -1; + push_uint32_safe(msg, (uint32_t)x); + return 0; +} + +int rtlsdr_rpc_msg_push_uint32(rtlsdr_rpc_msg_t* msg, uint32_t x) +{ + if (check_size_or_realloc(msg, sizeof(x))) return -1; + push_uint32_safe(msg, x); + return 0; +} + +void rtlsdr_rpc_msg_push_uint32_safe(rtlsdr_rpc_msg_t* msg, uint32_t x) +{ + push_uint32_safe(msg, x); +} + +int rtlsdr_rpc_msg_push_str(rtlsdr_rpc_msg_t* msg, const char* s) +{ + if (check_size_or_realloc(msg, strlen(s) + 1)) return -1; + push_mem_safe(msg, (const uint8_t*)s, strlen(s) + 1); + return 0; +} + +int rtlsdr_rpc_msg_push_buf(rtlsdr_rpc_msg_t* msg, const uint8_t* buf, size_t size) +{ + size_t total_size = sizeof(uint32_t) + size; + if (check_size_or_realloc(msg, total_size)) return -1; + push_uint32_safe(msg, (uint32_t)size); + push_mem_safe(msg, buf, size); + return 0; +} + +void rtlsdr_rpc_msg_skip_safe(rtlsdr_rpc_msg_t* msg, size_t size) +{ + msg->off += size; +} + +int rtlsdr_rpc_msg_pop_int(rtlsdr_rpc_msg_t* msg, int* x) +{ + return pop_uint32(msg, (uint32_t*)x); +} + +int rtlsdr_rpc_msg_pop_uint32(rtlsdr_rpc_msg_t* msg, uint32_t* x) +{ + return pop_uint32(msg, x); +} + +int rtlsdr_rpc_msg_pop_str(rtlsdr_rpc_msg_t* msg, const char** s) +{ + size_t i; + + *s = (const char*)(msg->fmt + msg->off); + + for (i = msg->off; i != msg->size; ++i) + { + if (msg->fmt[i] == 0) + { + msg->off = i + 1; + return 0; + } + } + + return -1; +} + +int rtlsdr_rpc_msg_pop_buf +(rtlsdr_rpc_msg_t* msg, const uint8_t** buf, size_t* size) +{ + uint32_t x; + + if (pop_uint32(msg, &x)) return -1; + if ((msg->off + x) > msg->size) return -1; + + *buf = (const uint8_t*)(msg->fmt + msg->off); + msg->off += x; + + *size = (size_t)x; + + return 0; +} + +static void put_uint8(void* p, uint8_t x) +{ + memcpy(p, (const void*)&x, sizeof(x)); +} + +static void put_uint16(void* p, uint16_t x) +{ +#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#error "unsupported endianness" +#endif + + memcpy(p, (const void*)&x, sizeof(x)); +} + +static void put_uint32(void* p, uint32_t x) +{ +#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#error "unsupported endianness" +#endif + + memcpy(p, (const void*)&x, sizeof(x)); +} + +static uint8_t get_uint8(const void* p) +{ + uint8_t x; + memcpy((void*)&x, p, sizeof(x)); + return x; +} + +static uint16_t get_uint16(const void* p) +{ +#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#error "unsupported endianness" +#endif + + uint16_t x; + memcpy((void*)&x, p, sizeof(x)); + return x; +} + +static uint32_t get_uint32(const void* p) +{ +#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#error "unsupported endianness" +#endif + + uint32_t x; + memcpy((void*)&x, p, sizeof(x)); + return x; +} + +void rtlsdr_rpc_msg_set_size(rtlsdr_rpc_msg_t* msg, size_t size) +{ + rtlsdr_rpc_fmt_t* const fmt = (rtlsdr_rpc_fmt_t*)msg->fmt; + put_uint32(&fmt->size, (uint32_t)size); +} + +size_t rtlsdr_rpc_msg_get_size(const rtlsdr_rpc_msg_t* msg) +{ + const rtlsdr_rpc_fmt_t* const fmt = (const rtlsdr_rpc_fmt_t*)msg->fmt; + return (size_t)get_uint32(&fmt->size); +} + +void rtlsdr_rpc_msg_set_op(rtlsdr_rpc_msg_t* msg, rtlsdr_rpc_op_t op) +{ + rtlsdr_rpc_fmt_t* const fmt = (rtlsdr_rpc_fmt_t*)msg->fmt; + put_uint8(&fmt->op, (uint8_t)op); +} + +rtlsdr_rpc_op_t rtlsdr_rpc_msg_get_op(const rtlsdr_rpc_msg_t* msg) +{ + const rtlsdr_rpc_fmt_t* const fmt = (const rtlsdr_rpc_fmt_t*)msg->fmt; + return (rtlsdr_rpc_op_t)get_uint8(&fmt->op); +} + +void rtlsdr_rpc_msg_set_id(rtlsdr_rpc_msg_t* msg, uint8_t id) +{ + rtlsdr_rpc_fmt_t* const fmt = (rtlsdr_rpc_fmt_t*)msg->fmt; + put_uint16(&fmt->id, id); +} + +uint8_t rtlsdr_rpc_msg_get_id(const rtlsdr_rpc_msg_t* msg) +{ + const rtlsdr_rpc_fmt_t* const fmt = (const rtlsdr_rpc_fmt_t*)msg->fmt; + return get_uint8(&fmt->id); +} + +void rtlsdr_rpc_msg_set_err(rtlsdr_rpc_msg_t* msg, int err) +{ + rtlsdr_rpc_fmt_t* const fmt = (rtlsdr_rpc_fmt_t*)msg->fmt; + put_uint32(&fmt->err, (uint32_t)err); +} + +int rtlsdr_rpc_msg_get_err(const rtlsdr_rpc_msg_t* msg) +{ + const rtlsdr_rpc_fmt_t* const fmt = (const rtlsdr_rpc_fmt_t*)msg->fmt; + return (int)get_uint32(&fmt->err); +} diff --git a/src/tuner_e4k.c b/src/tuner_e4k.c index 400e745..c7c13bc 100644 --- a/src/tuner_e4k.c +++ b/src/tuner_e4k.c @@ -342,7 +342,7 @@ int e4k_if_filter_bw_get(struct e4k_state *e4k, enum e4k_if_filter filter) #define E4K_FVCO_MIN_KHZ 2600000 /* 2.6 GHz */ #define E4K_FVCO_MAX_KHZ 3900000 /* 3.9 GHz */ -#define E4K_PLL_Y 65536 +#define E4K_PLL_Y 65536 #ifdef OUT_OF_SPEC #define E4K_FLO_MIN_MHZ 50 @@ -646,10 +646,10 @@ static const struct reg_field if_stage_gain_regs[] = { static const int32_t lnagain[] = { -50, 0, -25, 1, - 0, 4, - 25, 5, - 50, 6, - 75, 7, + 0, 4, + 25, 5, + 50, 6, + 75, 7, 100, 8, 125, 9, 150, 10, diff --git a/src/tuner_fc0013.c b/src/tuner_fc0013.c index 78b696e..8c4b365 100644 --- a/src/tuner_fc0013.c +++ b/src/tuner_fc0013.c @@ -455,14 +455,14 @@ int fc0013_lna_gains[] ={ -60, 0x07, -58, 0x01, -54, 0x06, - 58, 0x0f, - 61, 0x0e, - 63, 0x0d, - 65, 0x0c, - 67, 0x0b, - 68, 0x0a, - 70, 0x09, - 71, 0x08, + 58, 0x0f, + 61, 0x0e, + 63, 0x0d, + 65, 0x0c, + 67, 0x0b, + 68, 0x0a, + 70, 0x09, + 71, 0x08, 179, 0x17, 181, 0x16, 182, 0x15, diff --git a/src/tuner_fc2580.c b/src/tuner_fc2580.c index d2eeba5..ca233fb 100644 --- a/src/tuner_fc2580.c +++ b/src/tuner_fc2580.c @@ -44,10 +44,7 @@ fc2580_fci_result_type fc2580_i2c_read(void *pTuner, unsigned char reg, unsigned return FC2580_FCI_SUCCESS; } -int -fc2580_Initialize( - void *pTuner - ) +int fc2580_Initialize(void *pTuner) { int AgcMode; unsigned int CrystalFreqKhz; @@ -70,11 +67,7 @@ fc2580_Initialize( return FUNCTION_ERROR; } -int -fc2580_SetRfFreqHz( - void *pTuner, - unsigned long RfFreqHz - ) +int fc2580_SetRfFreqHz(void *pTuner, unsigned long RfFreqHz) { unsigned int RfFreqKhz; unsigned int CrystalFreqKhz; @@ -99,11 +92,7 @@ fc2580_SetRfFreqHz( @brief Set FC2580 tuner bandwidth mode. */ -int -fc2580_SetBandwidthMode( - void *pTuner, - int BandwidthMode - ) +int fc2580_SetBandwidthMode(void *pTuner, int BandwidthMode) { unsigned int CrystalFreqKhz; @@ -143,7 +132,7 @@ void fc2580_wait_msec(void *pTuner, int a) 2 : Voltage Control Mode ==============================================================================*/ -fc2580_fci_result_type fc2580_set_init( void *pTuner, int ifagc_mode, unsigned int freq_xtal ) +fc2580_fci_result_type fc2580_set_init(void *pTuner, int ifagc_mode, unsigned int freq_xtal) { fc2580_fci_result_type result = FC2580_FCI_SUCCESS; @@ -192,14 +181,14 @@ fc2580_fci_result_type fc2580_set_init( void *pTuner, int ifagc_mode, unsigned i ex) 2.6GHz = 2600000 ==============================================================================*/ -fc2580_fci_result_type fc2580_set_freq( void *pTuner, unsigned int f_lo, unsigned int freq_xtal ) +fc2580_fci_result_type fc2580_set_freq(void *pTuner, unsigned int f_lo, unsigned int freq_xtal) { unsigned int f_diff, f_diff_shifted, n_val, k_val; unsigned int f_vco, r_val, f_comp; unsigned char pre_shift_bits = 4;// number of preshift to prevent overflow in shifting f_diff to f_diff_shifted unsigned char data_0x18; unsigned char data_0x02 = (USE_EXT_CLK<<5)|0x0E; - + fc2580_band_type band = ( f_lo > 1000000 )? FC2580_L_BAND : ( f_lo > 400000 )? FC2580_UHF_BAND : FC2580_VHF_BAND; fc2580_fci_result_type result = FC2580_FCI_SUCCESS; @@ -208,19 +197,19 @@ fc2580_fci_result_type fc2580_set_freq( void *pTuner, unsigned int f_lo, unsigne r_val = ( f_vco >= 2*76*freq_xtal )? 1 : ( f_vco >= 76*freq_xtal )? 2 : 4; f_comp = freq_xtal/r_val; n_val = ( f_vco / 2 ) / f_comp; - + f_diff = f_vco - 2* f_comp * n_val; f_diff_shifted = f_diff << ( 20 - pre_shift_bits ); k_val = f_diff_shifted / ( ( 2* f_comp ) >> pre_shift_bits ); - + if( f_diff_shifted - k_val * ( ( 2* f_comp ) >> pre_shift_bits ) >= ( f_comp >> pre_shift_bits ) ) k_val = k_val + 1; - + if( f_vco >= BORDER_FREQ ) //Select VCO Band data_0x02 = data_0x02 | 0x08; //0x02[3] = 1; else data_0x02 = data_0x02 & 0xF7; //0x02[3] = 0; - + // if( band != curr_band ) { switch(band) { @@ -237,7 +226,7 @@ fc2580_fci_result_type fc2580_set_freq( void *pTuner, unsigned int f_lo, unsigne if( f_lo < 538000 ) result &= fc2580_i2c_write(pTuner, 0x5F, 0x13); - else + else result &= fc2580_i2c_write(pTuner, 0x5F, 0x15); if( f_lo < 538000 ) @@ -345,7 +334,7 @@ fc2580_fci_result_type fc2580_set_freq( void *pTuner, unsigned int f_lo, unsigne //A command about UHF LNA Load Cap if( band == FC2580_UHF_BAND ) result &= fc2580_i2c_write(pTuner, 0x2D, ( f_lo <= (unsigned int)794000 )? 0x9F : 0x8F ); //LNA_OUT_CAP - + return result; } @@ -366,10 +355,10 @@ fc2580_fci_result_type fc2580_set_freq( void *pTuner, unsigned int f_lo, unsigne 6 : 6MHz (Bandwidth 6MHz) 7 : 6.8MHz (Bandwidth 7MHz) 8 : 7.8MHz (Bandwidth 8MHz) - + ==============================================================================*/ -fc2580_fci_result_type fc2580_set_filter( void *pTuner, unsigned char filter_bw, unsigned int freq_xtal ) +fc2580_fci_result_type fc2580_set_filter(void *pTuner, unsigned char filter_bw, unsigned int freq_xtal) { unsigned char cal_mon = 0, i; fc2580_fci_result_type result = FC2580_FCI_SUCCESS; @@ -403,7 +392,7 @@ fc2580_fci_result_type fc2580_set_filter( void *pTuner, unsigned char filter_bw, result &= fc2580_i2c_write(pTuner, 0x2E, 0x09); } - + for(i=0; i<5; i++) { fc2580_wait_msec(pTuner, 5);//wait 5ms @@ -426,7 +415,7 @@ fc2580_fci_result_type fc2580_set_filter( void *pTuner, unsigned char filter_bw, fc2580 RSSI function This function is a generic function which returns fc2580's - + current RSSI value. @@ -438,7 +427,7 @@ fc2580_fci_result_type fc2580_set_filter( void *pTuner, unsigned char filter_bw, ==============================================================================*/ //int fc2580_get_rssi(void) { -// +// // unsigned char s_lna, s_rfvga, s_cfs, s_ifvga; // int ofs_lna, ofs_rfvga, ofs_csf, ofs_ifvga, rssi; // @@ -446,9 +435,9 @@ fc2580_fci_result_type fc2580_set_filter( void *pTuner, unsigned char filter_bw, // fc2580_i2c_read(0x72, &s_rfvga ); // fc2580_i2c_read(0x73, &s_cfs ); // fc2580_i2c_read(0x74, &s_ifvga ); -// // -// ofs_lna = +// +// ofs_lna = // (curr_band==FC2580_UHF_BAND)? // (s_lna==0)? 0 : // (s_lna==1)? -6 : @@ -470,18 +459,18 @@ fc2580_fci_result_type fc2580_set_filter( void *pTuner, unsigned char filter_bw, // ofs_ifvga = s_ifvga/4; // // return rssi = ofs_lna+ofs_rfvga+ofs_csf+ofs_ifvga+OFS_RSSI; -// +// //} /*============================================================================== fc2580 Xtal frequency Setting - This function is a generic function which sets - + This function is a generic function which sets + the frequency of xtal. - + - + frequency frequency value of internal(external) Xtal(clock) in kHz unit. diff --git a/src/tuner_r82xx.c b/src/tuner_r82xx.c index f620238..ad54b55 100644 --- a/src/tuner_r82xx.c +++ b/src/tuner_r82xx.c @@ -39,7 +39,7 @@ /* Those initial values start from REG_SHADOW_START */ static const uint8_t r82xx_init_array[NUM_REGS] = { - 0x83, 0x32, 0x75, /* 05 to 07 */ + 0x83, 0x32, 0x75, /* 05 to 07 */ 0xc0, 0x40, 0xd6, 0x6c, /* 08 to 0b */ 0xf5, 0x63, 0x75, 0x68, /* 0c to 0f */ 0x6c, 0x83, 0x80, 0x00, /* 10 to 13 */ @@ -51,173 +51,173 @@ static const uint8_t r82xx_init_array[NUM_REGS] = { /* Tuner frequency ranges */ static const struct r82xx_freq_range freq_ranges[] = { { - /* .freq = */ 0, /* Start freq, in MHz */ - /* .open_d = */ 0x08, /* low */ + /* .freq = */ 0, /* Start freq, in MHz */ + /* .open_d = */ 0x08, /* low */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0xdf, /* R27[7:0] band2,band0 */ + /* .tf_c = */ 0xdf, /* R27[7:0] band2,band0 */ /* .xtal_cap20p = */ 0x02, /* R16[1:0] 20pF (10) */ /* .xtal_cap10p = */ 0x01, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, }, { - /* .freq = */ 50, /* Start freq, in MHz */ - /* .open_d = */ 0x08, /* low */ + /* .freq = */ 50, /* Start freq, in MHz */ + /* .open_d = */ 0x08, /* low */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0xbe, /* R27[7:0] band4,band1 */ + /* .tf_c = */ 0xbe, /* R27[7:0] band4,band1 */ /* .xtal_cap20p = */ 0x02, /* R16[1:0] 20pF (10) */ /* .xtal_cap10p = */ 0x01, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, }, { - /* .freq = */ 55, /* Start freq, in MHz */ - /* .open_d = */ 0x08, /* low */ + /* .freq = */ 55, /* Start freq, in MHz */ + /* .open_d = */ 0x08, /* low */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x8b, /* R27[7:0] band7,band4 */ + /* .tf_c = */ 0x8b, /* R27[7:0] band7,band4 */ /* .xtal_cap20p = */ 0x02, /* R16[1:0] 20pF (10) */ /* .xtal_cap10p = */ 0x01, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, }, { - /* .freq = */ 60, /* Start freq, in MHz */ - /* .open_d = */ 0x08, /* low */ + /* .freq = */ 60, /* Start freq, in MHz */ + /* .open_d = */ 0x08, /* low */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x7b, /* R27[7:0] band8,band4 */ + /* .tf_c = */ 0x7b, /* R27[7:0] band8,band4 */ /* .xtal_cap20p = */ 0x02, /* R16[1:0] 20pF (10) */ /* .xtal_cap10p = */ 0x01, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, }, { - /* .freq = */ 65, /* Start freq, in MHz */ - /* .open_d = */ 0x08, /* low */ + /* .freq = */ 65, /* Start freq, in MHz */ + /* .open_d = */ 0x08, /* low */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x69, /* R27[7:0] band9,band6 */ + /* .tf_c = */ 0x69, /* R27[7:0] band9,band6 */ /* .xtal_cap20p = */ 0x02, /* R16[1:0] 20pF (10) */ /* .xtal_cap10p = */ 0x01, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, }, { - /* .freq = */ 70, /* Start freq, in MHz */ - /* .open_d = */ 0x08, /* low */ + /* .freq = */ 70, /* Start freq, in MHz */ + /* .open_d = */ 0x08, /* low */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x58, /* R27[7:0] band10,band7 */ + /* .tf_c = */ 0x58, /* R27[7:0] band10,band7 */ /* .xtal_cap20p = */ 0x02, /* R16[1:0] 20pF (10) */ /* .xtal_cap10p = */ 0x01, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, }, { - /* .freq = */ 75, /* Start freq, in MHz */ - /* .open_d = */ 0x00, /* high */ + /* .freq = */ 75, /* Start freq, in MHz */ + /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x44, /* R27[7:0] band11,band11 */ + /* .tf_c = */ 0x44, /* R27[7:0] band11,band11 */ /* .xtal_cap20p = */ 0x02, /* R16[1:0] 20pF (10) */ /* .xtal_cap10p = */ 0x01, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, }, { - /* .freq = */ 80, /* Start freq, in MHz */ - /* .open_d = */ 0x00, /* high */ + /* .freq = */ 80, /* Start freq, in MHz */ + /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x44, /* R27[7:0] band11,band11 */ + /* .tf_c = */ 0x44, /* R27[7:0] band11,band11 */ /* .xtal_cap20p = */ 0x02, /* R16[1:0] 20pF (10) */ /* .xtal_cap10p = */ 0x01, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, }, { - /* .freq = */ 90, /* Start freq, in MHz */ - /* .open_d = */ 0x00, /* high */ + /* .freq = */ 90, /* Start freq, in MHz */ + /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x34, /* R27[7:0] band12,band11 */ + /* .tf_c = */ 0x34, /* R27[7:0] band12,band11 */ /* .xtal_cap20p = */ 0x01, /* R16[1:0] 10pF (01) */ /* .xtal_cap10p = */ 0x01, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, }, { - /* .freq = */ 100, /* Start freq, in MHz */ - /* .open_d = */ 0x00, /* high */ + /* .freq = */ 100, /* Start freq, in MHz */ + /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x34, /* R27[7:0] band12,band11 */ + /* .tf_c = */ 0x34, /* R27[7:0] band12,band11 */ /* .xtal_cap20p = */ 0x01, /* R16[1:0] 10pF (01) */ /* .xtal_cap10p = */ 0x01, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, }, { - /* .freq = */ 110, /* Start freq, in MHz */ - /* .open_d = */ 0x00, /* high */ + /* .freq = */ 110, /* Start freq, in MHz */ + /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x24, /* R27[7:0] band13,band11 */ + /* .tf_c = */ 0x24, /* R27[7:0] band13,band11 */ /* .xtal_cap20p = */ 0x01, /* R16[1:0] 10pF (01) */ /* .xtal_cap10p = */ 0x01, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, }, { - /* .freq = */ 120, /* Start freq, in MHz */ - /* .open_d = */ 0x00, /* high */ + /* .freq = */ 120, /* Start freq, in MHz */ + /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x24, /* R27[7:0] band13,band11 */ + /* .tf_c = */ 0x24, /* R27[7:0] band13,band11 */ /* .xtal_cap20p = */ 0x01, /* R16[1:0] 10pF (01) */ /* .xtal_cap10p = */ 0x01, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, }, { - /* .freq = */ 140, /* Start freq, in MHz */ - /* .open_d = */ 0x00, /* high */ + /* .freq = */ 140, /* Start freq, in MHz */ + /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x14, /* R27[7:0] band14,band11 */ + /* .tf_c = */ 0x14, /* R27[7:0] band14,band11 */ /* .xtal_cap20p = */ 0x01, /* R16[1:0] 10pF (01) */ /* .xtal_cap10p = */ 0x01, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, }, { - /* .freq = */ 180, /* Start freq, in MHz */ - /* .open_d = */ 0x00, /* high */ + /* .freq = */ 180, /* Start freq, in MHz */ + /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x13, /* R27[7:0] band14,band12 */ + /* .tf_c = */ 0x13, /* R27[7:0] band14,band12 */ /* .xtal_cap20p = */ 0x00, /* R16[1:0] 0pF (00) */ /* .xtal_cap10p = */ 0x00, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, }, { - /* .freq = */ 220, /* Start freq, in MHz */ - /* .open_d = */ 0x00, /* high */ + /* .freq = */ 220, /* Start freq, in MHz */ + /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x13, /* R27[7:0] band14,band12 */ + /* .tf_c = */ 0x13, /* R27[7:0] band14,band12 */ /* .xtal_cap20p = */ 0x00, /* R16[1:0] 0pF (00) */ /* .xtal_cap10p = */ 0x00, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, }, { - /* .freq = */ 250, /* Start freq, in MHz */ - /* .open_d = */ 0x00, /* high */ + /* .freq = */ 250, /* Start freq, in MHz */ + /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x11, /* R27[7:0] highest,highest */ + /* .tf_c = */ 0x11, /* R27[7:0] highest,highest */ /* .xtal_cap20p = */ 0x00, /* R16[1:0] 0pF (00) */ /* .xtal_cap10p = */ 0x00, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, }, { - /* .freq = */ 280, /* Start freq, in MHz */ - /* .open_d = */ 0x00, /* high */ + /* .freq = */ 280, /* Start freq, in MHz */ + /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x00, /* R27[7:0] highest,highest */ + /* .tf_c = */ 0x00, /* R27[7:0] highest,highest */ /* .xtal_cap20p = */ 0x00, /* R16[1:0] 0pF (00) */ /* .xtal_cap10p = */ 0x00, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, }, { - /* .freq = */ 310, /* Start freq, in MHz */ - /* .open_d = */ 0x00, /* high */ + /* .freq = */ 310, /* Start freq, in MHz */ + /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x41, /* R26[7:6]=1 (bypass) R26[1:0]=1 (middle) */ - /* .tf_c = */ 0x00, /* R27[7:0] highest,highest */ + /* .tf_c = */ 0x00, /* R27[7:0] highest,highest */ /* .xtal_cap20p = */ 0x00, /* R16[1:0] 0pF (00) */ /* .xtal_cap10p = */ 0x00, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, }, { - /* .freq = */ 450, /* Start freq, in MHz */ - /* .open_d = */ 0x00, /* high */ + /* .freq = */ 450, /* Start freq, in MHz */ + /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x41, /* R26[7:6]=1 (bypass) R26[1:0]=1 (middle) */ - /* .tf_c = */ 0x00, /* R27[7:0] highest,highest */ + /* .tf_c = */ 0x00, /* R27[7:0] highest,highest */ /* .xtal_cap20p = */ 0x00, /* R16[1:0] 0pF (00) */ /* .xtal_cap10p = */ 0x00, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, }, { - /* .freq = */ 588, /* Start freq, in MHz */ - /* .open_d = */ 0x00, /* high */ + /* .freq = */ 588, /* Start freq, in MHz */ + /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x40, /* R26[7:6]=1 (bypass) R26[1:0]=0 (highest) */ - /* .tf_c = */ 0x00, /* R27[7:0] highest,highest */ + /* .tf_c = */ 0x00, /* R27[7:0] highest,highest */ /* .xtal_cap20p = */ 0x00, /* R16[1:0] 0pF (00) */ /* .xtal_cap10p = */ 0x00, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, }, { - /* .freq = */ 650, /* Start freq, in MHz */ - /* .open_d = */ 0x00, /* high */ + /* .freq = */ 650, /* Start freq, in MHz */ + /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x40, /* R26[7:6]=1 (bypass) R26[1:0]=0 (highest) */ - /* .tf_c = */ 0x00, /* R27[7:0] highest,highest */ + /* .tf_c = */ 0x00, /* R27[7:0] highest,highest */ /* .xtal_cap20p = */ 0x00, /* R16[1:0] 0pF (00) */ /* .xtal_cap10p = */ 0x00, - /* .xtal_cap0p = */ 0x00, + /* .xtal_cap0p = */ 0x00, } }; @@ -250,7 +250,7 @@ static void shadow_store(struct r82xx_priv *priv, uint8_t reg, const uint8_t *va } static int r82xx_write(struct r82xx_priv *priv, uint8_t reg, const uint8_t *val, - unsigned int len) + unsigned int len) { int rc, size, pos = 0; @@ -561,8 +561,8 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) } static int r82xx_sysfreq_sel(struct r82xx_priv *priv, uint32_t freq, - enum r82xx_tuner_type type, - uint32_t delsys) + enum r82xx_tuner_type type, + uint32_t delsys) { int rc; uint8_t mixer_top, lna_top, cp_cur, div_buf_cur, lna_vth_l, mixer_vth_l; @@ -1004,7 +1004,7 @@ static const int r82xx_mixer_gain_steps[] = { 0, 5, 10, 10, 19, 9, 10, 25, 17, 10, 8, 16, 13, 6, 3, -8 }; -int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain) +int r82xx_set_gain_old(struct r82xx_priv *priv, int set_manual_gain, int gain) { int rc; @@ -1073,6 +1073,113 @@ int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain) return 0; } +int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain, + int extended_mode, int lna_gain, int mixer_gain, int vga_gain) +{ + int rc; + + int i, total_gain = 0; + uint8_t mix_index = 0, lna_index = 0; + uint8_t data[4]; + + if (extended_mode) { + /* + // LNA auto off + rc = r82xx_write_reg_mask(priv, 0x05, 0x10, 0x10); + if (rc < 0) + return rc; + + // Mixer auto off + rc = r82xx_write_reg_mask(priv, 0x07, 0, 0x10); + if (rc < 0) + return rc; + + rc = r82xx_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + return rc; + */ + + /* Set LNA */ + rc = r82xx_write_reg_mask(priv, 0x05, lna_gain, 0x0f); + if (rc < 0) + return rc; + + /* Set Mixer */ + rc = r82xx_write_reg_mask(priv, 0x07, mixer_gain, 0x0f); + if (rc < 0) + return rc; + + /* Set VGA */ + rc = r82xx_write_reg_mask(priv, 0x0c, vga_gain, 0x9f); + if (rc < 0) + return rc; + + return 0; + } + + if (set_manual_gain) { + + /* LNA auto off */ + rc = r82xx_write_reg_mask(priv, 0x05, 0x10, 0x10); + if (rc < 0) + return rc; + + /* Mixer auto off */ + rc = r82xx_write_reg_mask(priv, 0x07, 0, 0x10); + if (rc < 0) + return rc; + + rc = r82xx_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + return rc; + + /* set fixed VGA gain for now (16.3 dB) */ + rc = r82xx_write_reg_mask(priv, 0x0c, 0x08, 0x9f); + if (rc < 0) + return rc; + + for (i = 0; i < 15; i++) { + if (total_gain >= gain) + break; + + total_gain += r82xx_lna_gain_steps[++lna_index]; + + if (total_gain >= gain) + break; + + total_gain += r82xx_mixer_gain_steps[++mix_index]; + } + + /* set LNA gain */ + rc = r82xx_write_reg_mask(priv, 0x05, lna_index, 0x0f); + if (rc < 0) + return rc; + + /* set Mixer gain */ + rc = r82xx_write_reg_mask(priv, 0x07, mix_index, 0x0f); + if (rc < 0) + return rc; + } else { + /* LNA */ + rc = r82xx_write_reg_mask(priv, 0x05, 0, 0x10); + if (rc < 0) + return rc; + + /* Mixer */ + rc = r82xx_write_reg_mask(priv, 0x07, 0x10, 0x10); + if (rc < 0) + return rc; + + /* set fixed VGA gain for now (26.5 dB) */ + rc = r82xx_write_reg_mask(priv, 0x0c, 0x0b, 0x9f); + if (rc < 0) + return rc; + } + + return 0; +} + + /* Bandwidth contribution by low-pass filter. */ static const int r82xx_if_low_pass_bw_table[] = { 1700000, 1600000, 1550000, 1450000, 1200000, 900000, 700000, 550000, 450000, 350000 @@ -1080,7 +1187,7 @@ static const int r82xx_if_low_pass_bw_table[] = { #define FILT_HP_BW1 350000 #define FILT_HP_BW2 380000 -int r82xx_set_bandwidth(struct r82xx_priv *priv, int bw, uint32_t rate) +int r82xx_set_bandwidth(struct r82xx_priv *priv, int bw, uint32_t rate, uint32_t * applied_bw, int apply) { int rc; unsigned int i; @@ -1090,27 +1197,35 @@ int r82xx_set_bandwidth(struct r82xx_priv *priv, int bw, uint32_t rate) if (bw > 7000000) { // BW: 8 MHz + *applied_bw = 8000000; reg_0a = 0x10; reg_0b = 0x0b; - priv->int_freq = 4570000; + if (apply) + priv->int_freq = 4570000; } else if (bw > 6000000) { // BW: 7 MHz + *applied_bw = 7000000; reg_0a = 0x10; reg_0b = 0x2a; - priv->int_freq = 4570000; + if (apply) + priv->int_freq = 4570000; } else if (bw > r82xx_if_low_pass_bw_table[0] + FILT_HP_BW1 + FILT_HP_BW2) { // BW: 6 MHz + *applied_bw = 6000000; reg_0a = 0x10; reg_0b = 0x6b; - priv->int_freq = 3570000; + if (apply) + priv->int_freq = 3570000; } else { reg_0a = 0x00; reg_0b = 0x80; - priv->int_freq = 2300000; + if (apply) + priv->int_freq = 2300000; if (bw > r82xx_if_low_pass_bw_table[0] + FILT_HP_BW1) { bw -= FILT_HP_BW2; - priv->int_freq += FILT_HP_BW2; + if (apply) + priv->int_freq += FILT_HP_BW2; real_bw += FILT_HP_BW2; } else { reg_0b |= 0x20; @@ -1118,7 +1233,8 @@ int r82xx_set_bandwidth(struct r82xx_priv *priv, int bw, uint32_t rate) if (bw > r82xx_if_low_pass_bw_table[0]) { bw -= FILT_HP_BW1; - priv->int_freq += FILT_HP_BW1; + if (apply) + priv->int_freq += FILT_HP_BW1; real_bw += FILT_HP_BW1; } else { reg_0b |= 0x40; @@ -1133,9 +1249,15 @@ int r82xx_set_bandwidth(struct r82xx_priv *priv, int bw, uint32_t rate) reg_0b |= 15 - i; real_bw += r82xx_if_low_pass_bw_table[i]; - priv->int_freq -= real_bw / 2; + *applied_bw = real_bw; + + if (apply) + priv->int_freq -= real_bw / 2; } + if (!apply) + return 0; + rc = r82xx_write_reg_mask(priv, 0x0a, reg_0a, 0x10); if (rc < 0) return rc; @@ -1170,7 +1292,7 @@ int r82xx_set_freq(struct r82xx_priv *priv, uint32_t freq) air_cable1_in = (freq > MHZ(345)) ? 0x00 : 0x60; if ((priv->cfg->rafael_chip == CHIP_R828D) && - (air_cable1_in != priv->input)) { + (air_cable1_in != priv->input)) { priv->input = air_cable1_in; rc = r82xx_write_reg_mask(priv, 0x05, air_cable1_in, 0x60); } diff --git a/win32-qtcreator/CMakeLists.txt b/win32-qtcreator/CMakeLists.txt new file mode 100644 index 0000000..539eab4 --- /dev/null +++ b/win32-qtcreator/CMakeLists.txt @@ -0,0 +1,75 @@ +project(rtlsdr) +cmake_minimum_required(VERSION 2.8) + +# created and tested with +# Qt 5.5.1 for Windows 32-bit (MinGW 4.9.2) from +# https://www.qt.io/download-open-source/#section-2 +# +# and QtCreator 4.0.0 +# +# and LibUSB 1.0.20 from +# https://sourceforge.net/projects/libusb/files/ +# libusb-1.0.20.7z +# + +# edit this path +SET( LIBUSBBASE C:/src/_foreign/libusb-1.0.20 ) + + +add_definitions( -DWIN32 -D_WIN32 -DNDEBUG ) + +include_directories( + ../include + ${LIBUSBBASE}/include/libusb-1.0 +) + +SET( LIBUSB ${LIBUSBBASE}/MinGW32/static/libusb-1.0.a ) + +SET( SOCKLIBS ws2_32 wsock32 ) + +SET( RTLLIBFILES + ../include/rtl_tcp.h + ../include/reg_field.h + ../include/rtlsdr_i2c.h + + ../src/convenience/convenience.c + ../src/convenience/convenience.h + + ../src/getopt/getopt.c + ../src/getopt/getopt.h + + ../include/rtl-sdr_export.h + ../include/rtl-sdr.h + ../src/librtlsdr.c + + ../include/tuner_e4k.h + ../src/tuner_e4k.c + + ../include/tuner_fc0012.h + ../src/tuner_fc0012.c + + ../include/tuner_fc0013.h + ../src/tuner_fc0013.c + + ../include/tuner_fc2580.h + ../src/tuner_fc2580.c + + ../include/tuner_r82xx.h + ../src/tuner_r82xx.c +) + +add_executable( rtl_test ../src/rtl_test.c ${RTLLIBFILES} ) +target_link_libraries( rtl_test ${LIBUSB} ) + +add_executable( rtl_fm ../src/rtl_fm.c ${RTLLIBFILES} ) +target_link_libraries( rtl_fm ${LIBUSB} ) + +add_executable( rtl_tcp ../src/rtl_tcp.c ${RTLLIBFILES} ) +target_link_libraries( rtl_tcp ${LIBUSB} ${SOCKLIBS} ) + +add_executable( rtl_adsb ../src/rtl_adsb.c ${RTLLIBFILES} ) +target_link_libraries( rtl_adsb ${LIBUSB} ) + +add_executable( rtl_power ../src/rtl_power.c ${RTLLIBFILES} ) +target_link_libraries( rtl_power ${LIBUSB} ) + diff --git a/win32-qtcreator/README.txt b/win32-qtcreator/README.txt new file mode 100644 index 0000000..e92aabb --- /dev/null +++ b/win32-qtcreator/README.txt @@ -0,0 +1,31 @@ + +there is an outdated "How to compile new releases of librtlsdr (and tools) on Windows" at +https://www.reddit.com/r/RTLSDR/comments/uce3e/how_to_compile_new_releases_of_librtlsdr_and/ +unfortunately the link to the CMakeLists.txt is broken! + +so, i needed to find another solution .. + + +1) aquire and install Qt 5.5.x for Windows 32-bit (MinGW 4.9.2) from +https://www.qt.io/download-open-source/#section-2 + +2) aquire and install QtCreator 4.0.x +from same site as 1) +probably this step is not necessary and you can use the qtcreator IDE from 1) + +3) aquire LibUSB 1.0.20 from +https://sourceforge.net/projects/libusb/files/ +last tested: libusb-1.0.20.7z + +and place the file at C:/src/_foreign/libusb-1.0.20 + +or replace LIBUSBBASE path in CMakeLists.txt + +4) start qtcreator and open the (modified) CMakeLists.txt +configure compiler/environment and compile + + +the resulting executables have no other dependencies than libwinpthread-1.dll +from the MINGW system at C:\Qt\Qt5.5.1\Tools\mingw492_32\bin\ + or C:\Qt\Qt5.5.1\5.5\mingw492_32\bin +