From 0xloem at gmail.com Wed Sep 19 19:46:56 2018 From: 0xloem at gmail.com (Karl Semich) Date: Wed, 19 Sep 2018 15:46:56 -0400 Subject: [PATCH] Add API for releasing zero-copy USB buffers Message-ID: Hi, I was trying to take advantage of the async zero-copy buffer feature in librtlsdr, but I found that the automatic resubmission of them made it hard to make use of them safely. This patch adds a couple function to manage buffers in a more advanced manner, so that latency and reuse can be tracked precisely. I also added some documentation. I can occasionally make a lot of mistakes, so if anybody has time to take a brief glance through, it would be really great. Karl -------------- next part -------------- A non-text attachment was scrubbed... Name: 0001-Add-API-for-releasing-zero-copy-USB-buffers.patch Type: application/octet-stream Size: 8877 bytes Desc: not available URL: From 0xloem at gmail.com Wed Sep 19 20:18:52 2018 From: 0xloem at gmail.com (Karl Semich) Date: Wed, 19 Sep 2018 16:18:52 -0400 Subject: [PATCH] Add support for message commands Message-ID: Hi, I've modified gr-osmosdr such that all devices respond to message commands ala USRP. I feel this is a really valuable addition, and I am happy to continue to work with it to meet any needs or desires. Please let me know that this e-mail was received. Thanks so much, Karl -------------- next part -------------- A non-text attachment was scrubbed... Name: 0001-add-support-for-message-commands-to-all-devices.patch Type: application/octet-stream Size: 112833 bytes Desc: not available URL: From 0xloem at gmail.com Wed Sep 19 21:24:11 2018 From: 0xloem at gmail.com (Karl Semich) Date: Wed, 19 Sep 2018 17:24:11 -0400 Subject: [PATCH} gr-osmosdr/rtl: overflow fixes Message-ID: RTLSDR driver is amended to handle overflows in a robust way. Previous code appeared to have many issues: overflows were happening more than needed and could result in data corruption. Handling is changed to stop the stream when buffers are exhausted, and restart it when they have drained. I apologize that my patches are sent as attachments; I only have access to webmail at this time. -------------- next part -------------- A non-text attachment was scrubbed... Name: 0001-rtl-drain-buffers-on-overflow.patch Type: application/octet-stream Size: 4079 bytes Desc: not available URL: From 0xloem at gmail.com Fri Sep 21 04:34:54 2018 From: 0xloem at gmail.com (Karl Semich) Date: Fri, 21 Sep 2018 00:34:54 -0400 Subject: [PATCH] rtl-sdr/lib: take 2 manually releasing async buffers Message-ID: New rtl-sdr manual buffer resubmission patch after discussion on IRC. This implementation uses the existing rtlsdr_read_async API and is more compartmentalized. -------------- next part -------------- A non-text attachment was scrubbed... Name: 0001-rtl-sdr-lib-Manually-releasing-async-buffers.patch Type: text/x-patch Size: 5942 bytes Desc: not available URL: From 0xloem at gmail.com Sun Sep 23 03:45:35 2018 From: 0xloem at gmail.com (Karl Semich) Date: Sun, 23 Sep 2018 03:45:35 +0000 Subject: [PATCH] rtl-sdr/lib: normalize error return values Message-ID: <20180923034535.18141-1-0xloem@gmail.com> All library functions updated to return error codes with consistent value and meaning, selected from nine comprehensive conditions inferred from context. Bitwise combinations of return values changed to conditionals, to preserve meaning. --- include/rtl-sdr.h | 20 ++- include/tuner_fc2580.h | 23 +-- src/librtlsdr.c | 374 +++++++++++++++++++++++++---------------- src/tuner_e4k.c | 65 ++++---- src/tuner_fc0012.c | 24 ++- src/tuner_fc0013.c | 47 ++++-- src/tuner_fc2580.c | 445 ++++++++++++++++++++++++++++++------------------- src/tuner_r82xx.c | 29 ++-- 8 files changed, 626 insertions(+), 401 deletions(-) diff --git a/include/rtl-sdr.h b/include/rtl-sdr.h index 3ed13ae..52d67e6 100644 --- a/include/rtl-sdr.h +++ b/include/rtl-sdr.h @@ -24,6 +24,7 @@ extern "C" { #endif +#include #include #include @@ -34,6 +35,23 @@ RTLSDR_API uint32_t rtlsdr_get_device_count(void); RTLSDR_API const char* rtlsdr_get_device_name(uint32_t index); /*! + * Error codes. Most functions return 0 on success or one of these + * codes on failure. + */ +enum rtlsdr_error { + RTLSDR_SUCCESS = 0, /* Success (no error) */ + RTLSDR_ERROR_NULL = -1, /* Passed handle is null pointer */ + RTLSDR_ERROR_INVALID = -EINVAL, /* Invalid argument provided */ + RTLSDR_ERROR_NOTFOUND = -3, /* Device was not found */ + RTLSDR_ERROR_SHORT = -4, /* Less data transferred than requested */ + RTLSDR_ERROR_NOOP = -5, /* No action to take */ + RTLSDR_ERROR_DEVTYPE = -6, /* Function not available for device */ + RTLSDR_ERROR_STATE = -7, /* Device in wrong state for call */ + RTLSDR_ERROR_NOMEM = -8, /* Memory allocation failed */ + RTLSDR_ERROR_USB = -9 /* Error returned by libusb */ +}; + +/*! * Get USB device strings. * * NOTE: The string arguments must provide space for up to 256 bytes. @@ -261,7 +279,7 @@ RTLSDR_API int rtlsdr_set_tuner_gain_mode(rtlsdr_dev_t *dev, int manual); * 225001 - 300000 Hz * 900001 - 3200000 Hz * sample loss is to be expected for rates > 2400000 - * \return 0 on success, -EINVAL on invalid rate + * \return 0 on success, RTLSDR_ERROR_INVALID on invalid rate */ RTLSDR_API int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t rate); diff --git a/include/tuner_fc2580.h b/include/tuner_fc2580.h index 9ebd935..72e9393 100644 --- a/include/tuner_fc2580.h +++ b/include/tuner_fc2580.h @@ -1,6 +1,8 @@ #ifndef __TUNER_FC2580_H #define __TUNER_FC2580_H +#include "rtl-sdr.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 @@ -16,21 +18,10 @@ typedef enum { FC2580_NO_BAND } fc2580_band_type; -typedef enum { - FC2580_FCI_FAIL, - FC2580_FCI_SUCCESS -} fc2580_fci_result_type; - -enum FUNCTION_STATUS -{ - FUNCTION_SUCCESS, - FUNCTION_ERROR, -}; - extern void fc2580_wait_msec(void *pTuner, int a); -fc2580_fci_result_type fc2580_i2c_write(void *pTuner, unsigned char reg, unsigned char val); -fc2580_fci_result_type fc2580_i2c_read(void *pTuner, unsigned char reg, unsigned char *read_data); +enum rtlsdr_error fc2580_i2c_write(void *pTuner, unsigned char reg, unsigned char val); +enum rtlsdr_error fc2580_i2c_read(void *pTuner, unsigned char reg, unsigned char *read_data); /*============================================================================== fc2580 initial setting @@ -47,7 +38,7 @@ fc2580_fci_result_type fc2580_i2c_read(void *pTuner, unsigned char reg, unsigned 2 : Voltage Control Mode ==============================================================================*/ -fc2580_fci_result_type fc2580_set_init(void *pTuner, int ifagc_mode, unsigned int freq_xtal ); +enum rtlsdr_error fc2580_set_init(void *pTuner, int ifagc_mode, unsigned int freq_xtal ); /*============================================================================== fc2580 frequency setting @@ -63,7 +54,7 @@ fc2580_fci_result_type fc2580_set_init(void *pTuner, int ifagc_mode, unsigned in ex) 2.6GHz = 2600000 ==============================================================================*/ -fc2580_fci_result_type fc2580_set_freq(void *pTuner, unsigned int f_lo, unsigned int freq_xtal ); +enum rtlsdr_error fc2580_set_freq(void *pTuner, unsigned int f_lo, unsigned int freq_xtal ); /*============================================================================== @@ -83,7 +74,7 @@ fc2580_fci_result_type fc2580_set_freq(void *pTuner, unsigned int f_lo, unsigned ==============================================================================*/ -fc2580_fci_result_type fc2580_set_filter( void *pTuner, unsigned char filter_bw, unsigned int freq_xtal ); +enum rtlsdr_error fc2580_set_filter( void *pTuner, unsigned char filter_bw, unsigned int freq_xtal ); // The following context is FC2580 tuner API source code // Definitions diff --git a/src/librtlsdr.c b/src/librtlsdr.c index 433ed5b..b83c54d 100644 --- a/src/librtlsdr.c +++ b/src/librtlsdr.c @@ -125,6 +125,7 @@ struct rtlsdr_dev { int dev_lost; int driver_active; unsigned int xfer_errors; + int libusb_err; }; void rtlsdr_set_gpio_bit(rtlsdr_dev_t *dev, uint8_t gpio, int val); @@ -164,16 +165,16 @@ int e4000_set_gain(void *dev, int gain) { #if 0 int enhgain = (gain - 420); #endif - if(e4k_set_lna_gain(&devt->e4k_s, min(300, gain - mixgain * 10)) == -EINVAL) - return -1; - if(e4k_mixer_gain_set(&devt->e4k_s, mixgain) == -EINVAL) - return -1; + if(e4k_set_lna_gain(&devt->e4k_s, min(300, gain - mixgain * 10)) == RTLSDR_ERROR_INVALID) + return RTLSDR_ERROR_INVALID; + if(e4k_mixer_gain_set(&devt->e4k_s, mixgain) == RTLSDR_ERROR_INVALID) + return RTLSDR_ERROR_INVALID; #if 0 /* enhanced mixer gain seems to have no effect */ if(enhgain >= 0) - if(e4k_set_enh_gain(&devt->e4k_s, enhgain) == -EINVAL) - return -1; + if(e4k_set_enh_gain(&devt->e4k_s, enhgain) == RTLSDR_ERROR_INVALID) + return RTLSDR_ERROR_INVALID; #endif - return 0; + return RTLSDR_SUCCESS; } int e4000_set_if_gain(void *dev, int stage, int gain) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; @@ -416,7 +417,12 @@ int rtlsdr_read_array(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_t * if (r < 0) fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); #endif - return r; + if (r < 0) { + dev->libusb_err = r; + return RTLSDR_ERROR_USB; + } else { + return r; + } } int rtlsdr_write_array(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_t *array, uint8_t len) @@ -429,7 +435,12 @@ int rtlsdr_write_array(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_t if (r < 0) fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); #endif - return r; + if (r < 0) { + dev->libusb_err = r; + return RTLSDR_ERROR_USB; + } else { + return r; + } } int rtlsdr_i2c_write_reg(rtlsdr_dev_t *dev, uint8_t i2c_addr, uint8_t reg, uint8_t val) @@ -458,7 +469,7 @@ int rtlsdr_i2c_write(rtlsdr_dev_t *dev, uint8_t i2c_addr, uint8_t *buffer, int l uint16_t addr = i2c_addr; if (!dev) - return -1; + return RTLSDR_ERROR_NULL; return rtlsdr_write_array(dev, IICB, addr, buffer, len); } @@ -468,12 +479,12 @@ int rtlsdr_i2c_read(rtlsdr_dev_t *dev, uint8_t i2c_addr, uint8_t *buffer, int le uint16_t addr = i2c_addr; if (!dev) - return -1; + return RTLSDR_ERROR_NULL; return rtlsdr_read_array(dev, IICB, addr, buffer, len); } -uint16_t rtlsdr_read_reg(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_t len) +int32_t rtlsdr_read_reg(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_t len) { int r; unsigned char data[2]; @@ -482,8 +493,11 @@ uint16_t rtlsdr_read_reg(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_ r = libusb_control_transfer(dev->devh, CTRL_IN, 0, addr, index, data, len, CTRL_TIMEOUT); - if (r < 0) + if (r < 0) { + dev->libusb_err = r; fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); + return RTLSDR_ERROR_USB; + } reg = (data[1] << 8) | data[0]; @@ -506,13 +520,16 @@ int rtlsdr_write_reg(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint16_t v r = libusb_control_transfer(dev->devh, CTRL_OUT, 0, addr, index, data, len, CTRL_TIMEOUT); - if (r < 0) + if (r < 0) { fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); - - return r; + dev->libusb_err = r; + return RTLSDR_ERROR_USB; + } else { + return RTLSDR_SUCCESS; + } } -uint16_t rtlsdr_demod_read_reg(rtlsdr_dev_t *dev, uint8_t page, uint16_t addr, uint8_t len) +int32_t rtlsdr_demod_read_reg(rtlsdr_dev_t *dev, uint8_t page, uint16_t addr, uint8_t len) { int r; unsigned char data[2]; @@ -523,8 +540,11 @@ uint16_t rtlsdr_demod_read_reg(rtlsdr_dev_t *dev, uint8_t page, uint16_t addr, u r = libusb_control_transfer(dev->devh, CTRL_IN, 0, addr, index, data, len, CTRL_TIMEOUT); - if (r < 0) + if (r < 0) { fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); + dev->libusb_err = r; + return RTLSDR_ERROR_USB; + } reg = (data[1] << 8) | data[0]; @@ -547,12 +567,15 @@ int rtlsdr_demod_write_reg(rtlsdr_dev_t *dev, uint8_t page, uint16_t addr, uint1 r = libusb_control_transfer(dev->devh, CTRL_OUT, 0, addr, index, data, len, CTRL_TIMEOUT); - if (r < 0) + if (r < 0) { fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); + dev->libusb_err = r; + return RTLSDR_ERROR_USB; + } rtlsdr_demod_read_reg(dev, 0x0a, 0x01, 1); - return (r == len) ? 0 : -1; + return (r == len) ? RTLSDR_SUCCESS : RTLSDR_ERROR_SHORT; } void rtlsdr_set_gpio_bit(rtlsdr_dev_t *dev, uint8_t gpio, int val) @@ -590,7 +613,7 @@ int rtlsdr_set_fir(rtlsdr_dev_t *dev) for (i = 0; i < 8; ++i) { const int val = dev->fir[i]; if (val < -128 || val > 127) { - return -1; + return RTLSDR_ERROR_INVALID; } fir[i] = val; } @@ -599,7 +622,7 @@ int rtlsdr_set_fir(rtlsdr_dev_t *dev) const int val0 = dev->fir[8+i]; const int val1 = dev->fir[8+i+1]; if (val0 < -2048 || val0 > 2047 || val1 < -2048 || val1 > 2047) { - return -1; + return RTLSDR_ERROR_INVALID; } fir[8+i*3/2] = val0 >> 4; fir[8+i*3/2+1] = (val0 << 4) | ((val1 >> 8) & 0x0f); @@ -607,11 +630,10 @@ int rtlsdr_set_fir(rtlsdr_dev_t *dev) } for (i = 0; i < (int)sizeof(fir); i++) { - if (rtlsdr_demod_write_reg(dev, 1, 0x1c + i, fir[i], 1)) - return -1; + return rtlsdr_demod_write_reg(dev, 1, 0x1c + i, fir[i], 1); } - return 0; + return RTLSDR_SUCCESS; } void rtlsdr_init_baseband(rtlsdr_dev_t *dev) @@ -673,7 +695,7 @@ int rtlsdr_deinit_baseband(rtlsdr_dev_t *dev) int r = 0; if (!dev) - return -1; + return RTLSDR_ERROR_NULL; if (dev->tuner && dev->tuner->exit) { rtlsdr_set_i2c_repeater(dev, 1); @@ -693,57 +715,67 @@ static int rtlsdr_set_if_freq(rtlsdr_dev_t *dev, uint32_t freq) int32_t if_freq; uint8_t tmp; int r; + int rfin; if (!dev) - return -1; + return RTLSDR_ERROR_NULL; /* read corrected clock value */ - if (rtlsdr_get_xtal_freq(dev, &rtl_xtal, NULL)) - return -2; + r = rtlsdr_get_xtal_freq(dev, &rtl_xtal, NULL); + if (r < 0) + return r; if_freq = ((freq * TWO_POW(22)) / rtl_xtal) * (-1); tmp = (if_freq >> 16) & 0x3f; - r = rtlsdr_demod_write_reg(dev, 1, 0x19, tmp, 1); + rfin = rtlsdr_demod_write_reg(dev, 1, 0x19, tmp, 1); tmp = (if_freq >> 8) & 0xff; - r |= rtlsdr_demod_write_reg(dev, 1, 0x1a, tmp, 1); + r = rtlsdr_demod_write_reg(dev, 1, 0x1a, tmp, 1); + if (r) rfin = r; tmp = if_freq & 0xff; - r |= rtlsdr_demod_write_reg(dev, 1, 0x1b, tmp, 1); + r = rtlsdr_demod_write_reg(dev, 1, 0x1b, tmp, 1); + if (r) rfin = r; - return r; + return rfin; } int rtlsdr_set_sample_freq_correction(rtlsdr_dev_t *dev, int ppm) { - int r = 0; + int r; + int rfin = 0; uint8_t tmp; int16_t offs = ppm * (-1) * TWO_POW(24) / 1000000; tmp = offs & 0xff; - r |= rtlsdr_demod_write_reg(dev, 1, 0x3f, tmp, 1); + r = rtlsdr_demod_write_reg(dev, 1, 0x3f, tmp, 1); + if (r) rfin = r; tmp = (offs >> 8) & 0x3f; - r |= rtlsdr_demod_write_reg(dev, 1, 0x3e, tmp, 1); + r = rtlsdr_demod_write_reg(dev, 1, 0x3e, tmp, 1); + if (r) rfin = r; - return r; + return rfin; } int rtlsdr_set_xtal_freq(rtlsdr_dev_t *dev, uint32_t rtl_freq, uint32_t tuner_freq) { - int r = 0; + int rfin = 0; + int r; if (!dev) - return -1; + return RTLSDR_ERROR_NULL; if (rtl_freq > 0 && (rtl_freq < MIN_RTL_XTAL_FREQ || rtl_freq > MAX_RTL_XTAL_FREQ)) - return -2; + return RTLSDR_ERROR_INVALID; if (rtl_freq > 0 && dev->rtl_xtal != rtl_freq) { dev->rtl_xtal = rtl_freq; /* update xtal-dependent settings */ - if (dev->rate) + if (dev->rate) { r = rtlsdr_set_sample_rate(dev, dev->rate); + if (r) rfin = r; + } } if (dev->tun_xtal != tuner_freq) { @@ -753,22 +785,26 @@ int rtlsdr_set_xtal_freq(rtlsdr_dev_t *dev, uint32_t rtl_freq, uint32_t tuner_fr dev->tun_xtal = tuner_freq; /* 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)) - return -3; + r = rtlsdr_get_xtal_freq(dev, NULL, &dev->e4k_s.vco.fosc); + if (r == RTLSDR_SUCCESS) + r = rtlsdr_get_xtal_freq(dev, NULL, &dev->r82xx_c.xtal); + if (r != RTLSDR_SUCCESS) + return r; /* update xtal-dependent settings */ - if (dev->freq) + if (dev->freq) { r = rtlsdr_set_center_freq(dev, dev->freq); + if (r) rfin = r; + } } - return r; + return rfin; } int rtlsdr_get_xtal_freq(rtlsdr_dev_t *dev, uint32_t *rtl_freq, uint32_t *tuner_freq) { if (!dev) - return -1; + return RTLSDR_ERROR_NULL; #define APPLY_PPM_CORR(val,ppm) (((val) * (1.0 + (ppm) / 1e6))) @@ -778,7 +814,7 @@ int rtlsdr_get_xtal_freq(rtlsdr_dev_t *dev, uint32_t *rtl_freq, uint32_t *tuner_ if (tuner_freq) *tuner_freq = (uint32_t) APPLY_PPM_CORR(dev->tun_xtal, dev->corr); - return 0; + return RTLSDR_SUCCESS; } int rtlsdr_get_usb_strings(rtlsdr_dev_t *dev, char *manufact, char *product, @@ -790,13 +826,15 @@ int rtlsdr_get_usb_strings(rtlsdr_dev_t *dev, char *manufact, char *product, int r = 0; if (!dev || !dev->devh) - return -1; + return RTLSDR_ERROR_NULL; device = libusb_get_device(dev->devh); r = libusb_get_device_descriptor(device, &dd); - if (r < 0) - return -1; + if (r < 0) { + dev->libusb_err = r; + return RTLSDR_ERROR_USB; + } if (manufact) { memset(manufact, 0, buf_max); @@ -819,7 +857,7 @@ int rtlsdr_get_usb_strings(rtlsdr_dev_t *dev, char *manufact, char *product, buf_max); } - return 0; + return RTLSDR_SUCCESS; } int rtlsdr_write_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16_t len) @@ -829,10 +867,10 @@ int rtlsdr_write_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16 uint8_t cmd[2]; if (!dev) - return -1; + return RTLSDR_ERROR_NULL; if ((len + offset) > 256) - return -2; + return RTLSDR_ERROR_INVALID; for (i = 0; i < len; i++) { cmd[0] = i + offset; @@ -846,7 +884,7 @@ int rtlsdr_write_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16 cmd[1] = data[i]; r = rtlsdr_write_array(dev, IICB, EEPROM_ADDR, cmd, 2); if (r != sizeof(cmd)) - return -3; + return r < 0 ? r : RTLSDR_ERROR_SHORT; /* for some EEPROMs (e.g. ATC 240LC02) we need a delay * between write operations, otherwise they will fail */ @@ -857,7 +895,7 @@ int rtlsdr_write_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16 #endif } - return 0; + return RTLSDR_SUCCESS; } int rtlsdr_read_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16_t len) @@ -866,20 +904,20 @@ int rtlsdr_read_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16_ int i; if (!dev) - return -1; + return RTLSDR_ERROR_NULL; if ((len + offset) > 256) - return -2; + return RTLSDR_ERROR_INVALID; r = rtlsdr_write_array(dev, IICB, EEPROM_ADDR, &offset, 1); if (r < 0) - return -3; + return r; for (i = 0; i < len; i++) { r = rtlsdr_read_array(dev, IICB, EEPROM_ADDR, data + i, 1); if (r < 0) - return -3; + return r; } return r; @@ -887,10 +925,10 @@ 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; + int r; if (!dev || !dev->tuner) - return -1; + return RTLSDR_ERROR_NULL; if (dev->direct_sampling) { r = rtlsdr_set_if_freq(dev, freq); @@ -898,6 +936,8 @@ int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq) rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->set_freq(dev, freq - dev->offs_freq); rtlsdr_set_i2c_repeater(dev, 0); + } else { + r = RTLSDR_ERROR_DEVTYPE; } if (!r) @@ -918,27 +958,33 @@ uint32_t rtlsdr_get_center_freq(rtlsdr_dev_t *dev) int rtlsdr_set_freq_correction(rtlsdr_dev_t *dev, int ppm) { - int r = 0; + int r; + int rfin; if (!dev) - return -1; + return RTLSDR_ERROR_NULL; if (dev->corr == ppm) - return -2; + return RTLSDR_ERROR_NOOP; dev->corr = ppm; - r |= rtlsdr_set_sample_freq_correction(dev, ppm); + rfin = rtlsdr_set_sample_freq_correction(dev, 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)) - return -3; + r = rtlsdr_get_xtal_freq(dev, NULL, &dev->e4k_s.vco.fosc); + if (r == RTLSDR_SUCCESS) + r = rtlsdr_get_xtal_freq(dev, NULL, &dev->r82xx_c.xtal); + if (r != RTLSDR_SUCCESS) + return r; - if (dev->freq) /* retune to apply new correction value */ - r |= rtlsdr_set_center_freq(dev, dev->freq); + if (dev->freq) { + /* retune to apply new correction value */ + r = rtlsdr_set_center_freq(dev, dev->freq); + if (r) rfin = r; + } - return r; + return rfin; } int rtlsdr_get_freq_correction(rtlsdr_dev_t *dev) @@ -977,7 +1023,7 @@ int rtlsdr_get_tuner_gains(rtlsdr_dev_t *dev, int *gains) int len = 0; if (!dev) - return -1; + return RTLSDR_ERROR_NULL; switch (dev->tuner_type) { case RTLSDR_TUNER_E4000: @@ -1016,7 +1062,7 @@ int rtlsdr_set_tuner_bandwidth(rtlsdr_dev_t *dev, uint32_t bw) int r = 0; if (!dev || !dev->tuner) - return -1; + return RTLSDR_ERROR_NULL; if (dev->tuner->set_bw) { rtlsdr_set_i2c_repeater(dev, 1); @@ -1034,7 +1080,7 @@ int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain) int r = 0; if (!dev || !dev->tuner) - return -1; + return RTLSDR_ERROR_NULL; if (dev->tuner->set_gain) { rtlsdr_set_i2c_repeater(dev, 1); @@ -1063,7 +1109,7 @@ int rtlsdr_set_tuner_if_gain(rtlsdr_dev_t *dev, int stage, int gain) int r = 0; if (!dev || !dev->tuner) - return -1; + return RTLSDR_ERROR_NULL; if (dev->tuner->set_if_gain) { rtlsdr_set_i2c_repeater(dev, 1); @@ -1079,7 +1125,7 @@ int rtlsdr_set_tuner_gain_mode(rtlsdr_dev_t *dev, int mode) int r = 0; if (!dev || !dev->tuner) - return -1; + return RTLSDR_ERROR_NULL; if (dev->tuner->set_gain_mode) { rtlsdr_set_i2c_repeater(dev, 1); @@ -1092,19 +1138,20 @@ int rtlsdr_set_tuner_gain_mode(rtlsdr_dev_t *dev, int mode) int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate) { - int r = 0; + int r; + int rfin = 0; uint16_t tmp; uint32_t rsamp_ratio, real_rsamp_ratio; double real_rate; if (!dev) - return -1; + return RTLSDR_ERROR_NULL; /* check if the rate is supported by the resampler */ if ((samp_rate <= 225000) || (samp_rate > 3200000) || ((samp_rate > 300000) && (samp_rate <= 900000))) { fprintf(stderr, "Invalid sample rate: %u Hz\n", samp_rate); - return -EINVAL; + return RTLSDR_ERROR_INVALID; } rsamp_ratio = (dev->rtl_xtal * TWO_POW(22)) / samp_rate; @@ -1125,21 +1172,26 @@ int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate) } tmp = (rsamp_ratio >> 16); - r |= rtlsdr_demod_write_reg(dev, 1, 0x9f, tmp, 2); + r = rtlsdr_demod_write_reg(dev, 1, 0x9f, tmp, 2); + if (r) rfin = r; tmp = rsamp_ratio & 0xffff; - r |= rtlsdr_demod_write_reg(dev, 1, 0xa1, tmp, 2); + r = rtlsdr_demod_write_reg(dev, 1, 0xa1, tmp, 2); + if (r) rfin = r; - r |= rtlsdr_set_sample_freq_correction(dev, dev->corr); + r = rtlsdr_set_sample_freq_correction(dev, dev->corr); + if (r) rfin = r; /* reset demod (bit 3, soft_rst) */ - r |= rtlsdr_demod_write_reg(dev, 1, 0x01, 0x14, 1); - r |= rtlsdr_demod_write_reg(dev, 1, 0x01, 0x10, 1); + r = rtlsdr_demod_write_reg(dev, 1, 0x01, 0x14, 1); + if (r) rfin = r; + r = rtlsdr_demod_write_reg(dev, 1, 0x01, 0x10, 1); + if (r) rfin = r; /* recalculate offset frequency if offset tuning is enabled */ if (dev->offs_freq) rtlsdr_set_offset_tuning(dev, 1); - return r; + return rfin; } uint32_t rtlsdr_get_sample_rate(rtlsdr_dev_t *dev) @@ -1153,7 +1205,7 @@ uint32_t rtlsdr_get_sample_rate(rtlsdr_dev_t *dev) int rtlsdr_set_testmode(rtlsdr_dev_t *dev, int on) { if (!dev) - return -1; + return RTLSDR_ERROR_NULL; return rtlsdr_demod_write_reg(dev, 0, 0x19, on ? 0x03 : 0x05, 1); } @@ -1161,17 +1213,18 @@ int rtlsdr_set_testmode(rtlsdr_dev_t *dev, int on) int rtlsdr_set_agc_mode(rtlsdr_dev_t *dev, int on) { if (!dev) - return -1; + return RTLSDR_ERROR_NULL; return rtlsdr_demod_write_reg(dev, 0, 0x19, on ? 0x25 : 0x05, 1); } int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on) { - int r = 0; + int r; + int rfin = 0; if (!dev) - return -1; + return RTLSDR_ERROR_NULL; if (on) { if (dev->tuner && dev->tuner->exit) { @@ -1181,80 +1234,93 @@ int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on) } /* disable Zero-IF mode */ - r |= rtlsdr_demod_write_reg(dev, 1, 0xb1, 0x1a, 1); + r = rtlsdr_demod_write_reg(dev, 1, 0xb1, 0x1a, 1); + if (r) rfin = r; /* disable spectrum inversion */ - r |= rtlsdr_demod_write_reg(dev, 1, 0x15, 0x00, 1); + r = rtlsdr_demod_write_reg(dev, 1, 0x15, 0x00, 1); + if (r) rfin = r; /* only enable In-phase ADC input */ - r |= rtlsdr_demod_write_reg(dev, 0, 0x08, 0x4d, 1); + r = rtlsdr_demod_write_reg(dev, 0, 0x08, 0x4d, 1); + if (r) rfin = r; /* swap I and Q ADC, this allows to select between two inputs */ - r |= rtlsdr_demod_write_reg(dev, 0, 0x06, (on > 1) ? 0x90 : 0x80, 1); + r = rtlsdr_demod_write_reg(dev, 0, 0x06, (on > 1) ? 0x90 : 0x80, 1); + if (r) rfin = r; fprintf(stderr, "Enabled direct sampling mode, input %i\n", on); dev->direct_sampling = on; } else { if (dev->tuner && dev->tuner->init) { rtlsdr_set_i2c_repeater(dev, 1); - r |= dev->tuner->init(dev); + r = dev->tuner->init(dev); + if (r) rfin = r; rtlsdr_set_i2c_repeater(dev, 0); } if ((dev->tuner_type == RTLSDR_TUNER_R820T) || (dev->tuner_type == RTLSDR_TUNER_R828D)) { - r |= rtlsdr_set_if_freq(dev, R82XX_IF_FREQ); + r = rtlsdr_set_if_freq(dev, R82XX_IF_FREQ); + if (r) rfin = r; /* enable spectrum inversion */ - r |= rtlsdr_demod_write_reg(dev, 1, 0x15, 0x01, 1); + r = rtlsdr_demod_write_reg(dev, 1, 0x15, 0x01, 1); + if (r) rfin = r; } else { - r |= rtlsdr_set_if_freq(dev, 0); + r = rtlsdr_set_if_freq(dev, 0); + if (r) rfin = r; /* enable In-phase + Quadrature ADC input */ - r |= rtlsdr_demod_write_reg(dev, 0, 0x08, 0xcd, 1); + r = rtlsdr_demod_write_reg(dev, 0, 0x08, 0xcd, 1); + if (r) rfin = r; /* Enable Zero-IF mode */ - r |= rtlsdr_demod_write_reg(dev, 1, 0xb1, 0x1b, 1); + r = rtlsdr_demod_write_reg(dev, 1, 0xb1, 0x1b, 1); + if (r) rfin = r; } /* opt_adc_iq = 0, default ADC_I/ADC_Q datapath */ - r |= rtlsdr_demod_write_reg(dev, 0, 0x06, 0x80, 1); + r = rtlsdr_demod_write_reg(dev, 0, 0x06, 0x80, 1); + if (r) rfin = r; fprintf(stderr, "Disabled direct sampling mode\n"); dev->direct_sampling = 0; } - r |= rtlsdr_set_center_freq(dev, dev->freq); + r = rtlsdr_set_center_freq(dev, dev->freq); + if (r) rfin = r; - return r; + return rfin; } int rtlsdr_get_direct_sampling(rtlsdr_dev_t *dev) { if (!dev) - return -1; + return RTLSDR_ERROR_NULL; return dev->direct_sampling; } int rtlsdr_set_offset_tuning(rtlsdr_dev_t *dev, int on) { - int r = 0; + int r; + int rfin = 0; int bw; if (!dev) - return -1; + return RTLSDR_ERROR_NULL; if ((dev->tuner_type == RTLSDR_TUNER_R820T) || (dev->tuner_type == RTLSDR_TUNER_R828D)) - return -2; + return RTLSDR_ERROR_DEVTYPE; if (dev->direct_sampling) - return -3; + return RTLSDR_ERROR_STATE; /* based on keenerds 1/f noise measurements */ dev->offs_freq = on ? ((dev->rate / 2) * 170 / 100) : 0; - r |= rtlsdr_set_if_freq(dev, dev->offs_freq); + rfin = rtlsdr_set_if_freq(dev, dev->offs_freq); if (dev->tuner && dev->tuner->set_bw) { rtlsdr_set_i2c_repeater(dev, 1); @@ -1269,16 +1335,18 @@ int rtlsdr_set_offset_tuning(rtlsdr_dev_t *dev, int on) rtlsdr_set_i2c_repeater(dev, 0); } - if (dev->freq > dev->offs_freq) - r |= rtlsdr_set_center_freq(dev, dev->freq); + if (dev->freq > dev->offs_freq) { + r = rtlsdr_set_center_freq(dev, dev->freq); + if (r) rfin = r; + } - return r; + return rfin; } int rtlsdr_get_offset_tuning(rtlsdr_dev_t *dev) { if (!dev) - return -1; + return RTLSDR_ERROR_NULL; return (dev->offs_freq) ? 1 : 0; } @@ -1369,7 +1437,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) { - int r = -2; + int r; int i; libusb_context *ctx; libusb_device **list; @@ -1381,7 +1449,7 @@ int rtlsdr_get_device_usb_strings(uint32_t index, char *manufact, r = libusb_init(&ctx); if(r < 0) - return r; + return RTLSDR_ERROR_USB; cnt = libusb_get_device_list(ctx, &list); @@ -1420,12 +1488,12 @@ int rtlsdr_get_index_by_serial(const char *serial) char str[256]; if (!serial) - return -1; + return RTLSDR_ERROR_NULL; cnt = rtlsdr_get_device_count(); if (!cnt) - return -2; + return RTLSDR_ERROR_NOTFOUND; for (i = 0; i < cnt; i++) { r = rtlsdr_get_device_usb_strings(i, NULL, NULL, str); @@ -1433,7 +1501,7 @@ int rtlsdr_get_index_by_serial(const char *serial) return i; } - return -3; + return RTLSDR_ERROR_NOTFOUND; } int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index) @@ -1450,7 +1518,7 @@ int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index) dev = malloc(sizeof(rtlsdr_dev_t)); if (NULL == dev) - return -ENOMEM; + return RTLSDR_ERROR_NOMEM; memset(dev, 0, sizeof(rtlsdr_dev_t)); memcpy(dev->fir, fir_default, sizeof(fir_default)); @@ -1458,7 +1526,7 @@ int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index) r = libusb_init(&dev->ctx); if(r < 0){ free(dev); - return -1; + return RTLSDR_ERROR_USB; } dev->dev_lost = 1; @@ -1481,7 +1549,7 @@ int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index) } if (!device) { - r = -1; + r = RTLSDR_ERROR_NOTFOUND; goto err; } @@ -1492,6 +1560,7 @@ int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index) if(r == LIBUSB_ERROR_ACCESS) fprintf(stderr, "Please fix the device permissions, e.g. " "by installing the udev rules file rtl-sdr.rules\n"); + r = RTLSDR_ERROR_USB; goto err; } @@ -1505,6 +1574,7 @@ int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index) fprintf(stderr, "Detached kernel driver\n"); } else { fprintf(stderr, "Detaching kernel driver failed!"); + r = RTLSDR_ERROR_USB; goto err; } #else @@ -1520,6 +1590,7 @@ int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index) r = libusb_claim_interface(dev->devh, 0); if (r < 0) { fprintf(stderr, "usb_claim_interface error %d\n", r); + r = RTLSDR_ERROR_USB; goto err; } @@ -1625,7 +1696,7 @@ found: *out_dev = dev; - return 0; + return RTLSDR_SUCCESS; err: if (dev) { if (dev->ctx) @@ -1640,7 +1711,7 @@ err: int rtlsdr_close(rtlsdr_dev_t *dev) { if (!dev) - return -1; + return RTLSDR_ERROR_NULL; if(!dev->dev_lost) { /* block until all async operations have been completed (if any) */ @@ -1672,26 +1743,34 @@ int rtlsdr_close(rtlsdr_dev_t *dev) free(dev); - return 0; + return RTLSDR_SUCCESS; } int rtlsdr_reset_buffer(rtlsdr_dev_t *dev) { if (!dev) - return -1; + return RTLSDR_ERROR_NULL; rtlsdr_write_reg(dev, USBB, USB_EPA_CTL, 0x1002, 2); rtlsdr_write_reg(dev, USBB, USB_EPA_CTL, 0x0000, 2); - return 0; + return RTLSDR_SUCCESS; } int rtlsdr_read_sync(rtlsdr_dev_t *dev, void *buf, int len, int *n_read) { + int r; + if (!dev) - return -1; + return RTLSDR_ERROR_NULL; - return libusb_bulk_transfer(dev->devh, 0x81, buf, len, n_read, BULK_TIMEOUT); + r = libusb_bulk_transfer(dev->devh, 0x81, buf, len, n_read, BULK_TIMEOUT); + if (r != 0) { + dev->libusb_err = r; + r = RTLSDR_ERROR_USB; + } + + return r; } static void LIBUSB_CALL _libusb_callback(struct libusb_transfer *xfer) @@ -1732,7 +1811,7 @@ static int _rtlsdr_alloc_async_buffers(rtlsdr_dev_t *dev) unsigned int i; if (!dev) - return -1; + return RTLSDR_ERROR_NULL; if (!dev->xfer) { dev->xfer = malloc(dev->xfer_buf_num * @@ -1743,7 +1822,7 @@ static int _rtlsdr_alloc_async_buffers(rtlsdr_dev_t *dev) } if (dev->xfer_buf) - return -2; + return RTLSDR_ERROR_STATE; dev->xfer_buf = malloc(dev->xfer_buf_num * sizeof(unsigned char *)); memset(dev->xfer_buf, 0, dev->xfer_buf_num * sizeof(unsigned char *)); @@ -1783,11 +1862,11 @@ static int _rtlsdr_alloc_async_buffers(rtlsdr_dev_t *dev) dev->xfer_buf[i] = malloc(dev->xfer_buf_len); if (!dev->xfer_buf[i]) - return -ENOMEM; + return RTLSDR_ERROR_NOMEM; } } - return 0; + return RTLSDR_SUCCESS; } static int _rtlsdr_free_async_buffers(rtlsdr_dev_t *dev) @@ -1795,7 +1874,7 @@ static int _rtlsdr_free_async_buffers(rtlsdr_dev_t *dev) unsigned int i; if (!dev) - return -1; + return RTLSDR_ERROR_NULL; if (dev->xfer) { for(i = 0; i < dev->xfer_buf_num; ++i) { @@ -1827,7 +1906,7 @@ static int _rtlsdr_free_async_buffers(rtlsdr_dev_t *dev) dev->xfer_buf = NULL; } - return 0; + return RTLSDR_SUCCESS; } int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, @@ -1840,10 +1919,10 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, enum rtlsdr_async_status next_status = RTLSDR_INACTIVE; if (!dev) - return -1; + return RTLSDR_ERROR_NULL; if (RTLSDR_INACTIVE != dev->async_status) - return -2; + return RTLSDR_ERROR_STATE; dev->async_status = RTLSDR_RUNNING; dev->async_cancel = 0; @@ -1882,6 +1961,8 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, "echo 0 > /sys/module/usbcore" "/parameters/usbfs_memory_mb\n", i); dev->async_status = RTLSDR_CANCELING; + dev->libusb_err = r; + r = RTLSDR_ERROR_USB; break; } } @@ -1891,6 +1972,8 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, &dev->async_cancel); if (r < 0) { /*fprintf(stderr, "handle_events returned: %d\n", r);*/ + dev->libusb_err = r; + r = RTLSDR_ERROR_USB; if (r == LIBUSB_ERROR_INTERRUPTED) /* stray signal */ continue; break; @@ -1914,8 +1997,11 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, * propagate */ libusb_handle_events_timeout_completed(dev->ctx, &zerotv, NULL); - if (r < 0) + if (r < 0) { + dev->libusb_err = r; + r = RTLSDR_ERROR_USB; continue; + } next_status = RTLSDR_CANCELING; } @@ -1942,13 +2028,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) { if (!dev) - return -1; + return RTLSDR_ERROR_NULL; /* if streaming, try to cancel gracefully */ if (RTLSDR_RUNNING == dev->async_status) { dev->async_status = RTLSDR_CANCELING; dev->async_cancel = 1; - return 0; + return RTLSDR_SUCCESS; } /* if called while in pending state, change the state forcefully */ @@ -1958,7 +2044,7 @@ int rtlsdr_cancel_async(rtlsdr_dev_t *dev) return 0; } #endif - return -2; + return RTLSDR_ERROR_STATE; } uint32_t rtlsdr_get_tuner_clock(void *dev) @@ -1980,7 +2066,7 @@ int rtlsdr_i2c_write_fn(void *dev, uint8_t addr, uint8_t *buf, int len) if (dev) return rtlsdr_i2c_write(((rtlsdr_dev_t *)dev), addr, buf, len); - return -1; + return RTLSDR_ERROR_NULL; } int rtlsdr_i2c_read_fn(void *dev, uint8_t addr, uint8_t *buf, int len) @@ -1988,16 +2074,16 @@ int rtlsdr_i2c_read_fn(void *dev, uint8_t addr, uint8_t *buf, int len) if (dev) return rtlsdr_i2c_read(((rtlsdr_dev_t *)dev), addr, buf, len); - return -1; + return RTLSDR_ERROR_NULL; } int rtlsdr_set_bias_tee(rtlsdr_dev_t *dev, int on) { if (!dev) - return -1; + return RTLSDR_ERROR_NULL; rtlsdr_set_gpio_output(dev, 0); rtlsdr_set_gpio_bit(dev, 0, on); - return 0; + return RTLSDR_SUCCESS; } diff --git a/src/tuner_e4k.c b/src/tuner_e4k.c index 400e745..fc2c63b 100644 --- a/src/tuner_e4k.c +++ b/src/tuner_e4k.c @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -70,7 +71,8 @@ static int e4k_reg_write(struct e4k_state *e4k, uint8_t reg, uint8_t val) data[1] = val; r = rtlsdr_i2c_write_fn(e4k->rtl_dev, e4k->i2c_addr, data, 2); - return r == 2 ? 0 : -1; + if (r < 0) return r; + return r == 2 ? RTLSDR_SUCCESS : RTLSDR_ERROR_SHORT; } /*! \brief Read a register of the tuner chip @@ -81,12 +83,15 @@ static int e4k_reg_write(struct e4k_state *e4k, uint8_t reg, uint8_t val) static int e4k_reg_read(struct e4k_state *e4k, uint8_t reg) { uint8_t data = reg; + int r; - if (rtlsdr_i2c_write_fn(e4k->rtl_dev, e4k->i2c_addr, &data, 1) < 1) - return -1; + r = rtlsdr_i2c_write_fn(e4k->rtl_dev, e4k->i2c_addr, &data, 1); + if (r < 0) return r; + if (r < 1) return RTLSDR_ERROR_SHORT; - if (rtlsdr_i2c_read_fn(e4k->rtl_dev, e4k->i2c_addr, &data, 1) < 1) - return -1; + r = rtlsdr_i2c_read_fn(e4k->rtl_dev, e4k->i2c_addr, &data, 1); + if (r < 0) return r; + if (r < 1) return RTLSDR_ERROR_SHORT; return data; } @@ -104,7 +109,7 @@ static int e4k_reg_set_mask(struct e4k_state *e4k, uint8_t reg, uint8_t tmp = e4k_reg_read(e4k, reg); if ((tmp & mask) == val) - return 0; + return RTLSDR_SUCCESS; return e4k_reg_write(e4k, reg, (tmp & ~mask) | (val & mask)); } @@ -203,7 +208,7 @@ static int choose_rf_filter(enum e4k_band band, uint32_t freq) freq); break; default: - rc = -EINVAL; + rc = RTLSDR_ERROR_INVALID; break; } @@ -277,7 +282,7 @@ static const struct reg_field if_filter_fields[] = { static int find_if_bw(enum e4k_if_filter filter, uint32_t bw) { if (filter >= ARRAY_SIZE(if_filter_bw)) - return -EINVAL; + return RTLSDR_ERROR_INVALID; return closest_arr_idx(if_filter_bw[filter], if_filter_bw_len[filter], bw); @@ -296,7 +301,7 @@ int e4k_if_filter_bw_set(struct e4k_state *e4k, enum e4k_if_filter filter, const struct reg_field *field; if (filter >= ARRAY_SIZE(if_filter_bw)) - return -EINVAL; + return RTLSDR_ERROR_INVALID; bw_idx = find_if_bw(filter, bandwidth); @@ -323,7 +328,7 @@ int e4k_if_filter_bw_get(struct e4k_state *e4k, enum e4k_if_filter filter) const struct reg_field *field; if (filter >= ARRAY_SIZE(if_filter_bw)) - return -EINVAL; + return RTLSDR_ERROR_INVALID; field = &if_filter_fields[filter]; @@ -443,7 +448,7 @@ static uint32_t compute_flo(uint32_t f_osc, uint8_t z, uint16_t x, uint8_t r) { uint64_t fvco = compute_fvco(f_osc, z, x); if (fvco == 0) - return -EINVAL; + return 0; return fvco / r; } @@ -579,7 +584,7 @@ int e4k_tune_freq(struct e4k_state *e4k, uint32_t freq) /* determine PLL parameters */ rc = e4k_compute_pll_params(&p, e4k->vco.fosc, freq); if (!rc) - return -EINVAL; + return RTLSDR_ERROR_INVALID; /* actually tune to those parameters */ rc = e4k_tune_params(e4k, &p); @@ -588,7 +593,7 @@ int e4k_tune_freq(struct e4k_state *e4k, uint32_t freq) rc = e4k_reg_read(e4k, E4K_REG_SYNTH1); if (!(rc & 0x01)) { fprintf(stderr, "[E4K] PLL not locked for %u Hz!\n", freq); - return -1; + return RTLSDR_ERROR_INVALID; } return 0; @@ -672,7 +677,7 @@ int e4k_set_lna_gain(struct e4k_state *e4k, int32_t gain) return gain; } } - return -EINVAL; + return RTLSDR_ERROR_INVALID; } int e4k_set_enh_gain(struct e4k_state *e4k, int32_t gain) @@ -688,9 +693,9 @@ int e4k_set_enh_gain(struct e4k_state *e4k, int32_t gain) /* special case: 0 = off*/ if(0 == gain) - return 0; + return RTLSDR_SUCCESS; else - return -EINVAL; + return RTLSDR_ERROR_INVALID; } int e4k_enable_manual_gain(struct e4k_state *e4k, uint8_t manual) @@ -710,7 +715,7 @@ int e4k_enable_manual_gain(struct e4k_state *e4k, uint8_t manual) e4k_reg_set_mask(e4k, E4K_REG_AGC11, 0x7, 0); } - return 0; + return RTLSDR_SUCCESS; } static int find_stage_gain(uint8_t stage, int8_t val) @@ -719,7 +724,7 @@ static int find_stage_gain(uint8_t stage, int8_t val) int i; if (stage >= ARRAY_SIZE(if_stage_gain)) - return -EINVAL; + return RTLSDR_ERROR_INVALID; arr = if_stage_gain[stage]; @@ -727,7 +732,7 @@ static int find_stage_gain(uint8_t stage, int8_t val) if (arr[i] == val) return i; } - return -EINVAL; + return RTLSDR_ERROR_INVALID; } /*! \brief Set the gain of one of the IF gain stages @@ -765,7 +770,7 @@ int e4k_mixer_gain_set(struct e4k_state *e4k, int8_t value) bit = 1; break; default: - return -EINVAL; + return RTLSDR_ERROR_INVALID; } return e4k_reg_set_mask(e4k, E4K_REG_GAIN2, 1, bit); @@ -774,9 +779,9 @@ int e4k_mixer_gain_set(struct e4k_state *e4k, int8_t value) int e4k_commonmode_set(struct e4k_state *e4k, int8_t value) { if(value < 0) - return -EINVAL; + return RTLSDR_ERROR_INVALID; else if(value > 7) - return -EINVAL; + return RTLSDR_ERROR_INVALID; return e4k_reg_set_mask(e4k, E4K_REG_DC7, 7, value); } @@ -789,13 +794,13 @@ int e4k_manual_dc_offset(struct e4k_state *e4k, int8_t iofs, int8_t irange, int8 int res; if((iofs < 0x00) || (iofs > 0x3f)) - return -EINVAL; + return RTLSDR_ERROR_INVALID; if((irange < 0x00) || (irange > 0x03)) - return -EINVAL; + return RTLSDR_ERROR_INVALID; if((qofs < 0x00) || (qofs > 0x3f)) - return -EINVAL; + return RTLSDR_ERROR_INVALID; if((qrange < 0x00) || (qrange > 0x03)) - return -EINVAL; + return RTLSDR_ERROR_INVALID; res = e4k_reg_set_mask(e4k, E4K_REG_DC2, 0x3f, iofs); if(res < 0) @@ -886,7 +891,7 @@ int e4k_dc_offset_gen_table(struct e4k_state *e4k) TO_LUT(offs_i, range_i)); } - return 0; + return RTLSDR_SUCCESS; } /*********************************************************************** @@ -899,7 +904,7 @@ int e4k_standby(struct e4k_state *e4k, int enable) e4k_reg_set_mask(e4k, E4K_REG_MASTER1, E4K_MASTER1_NORM_STBY, enable ? 0 : E4K_MASTER1_NORM_STBY); - return 0; + return RTLSDR_SUCCESS; } /*********************************************************************** @@ -916,7 +921,7 @@ static int magic_init(struct e4k_state *e4k) e4k_reg_write(e4k, 0x9f, 0x7f); e4k_reg_write(e4k, 0xa0, 0x07); - return 0; + return RTLSDR_SUCCESS; } /*! \brief Initialize the E4K tuner @@ -996,5 +1001,5 @@ int e4k_init(struct e4k_state *e4k) e4k_reg_set_mask(e4k, E4K_REG_DCTIME1, 0x03, 0); e4k_reg_set_mask(e4k, E4K_REG_DCTIME2, 0x03, 0); - return 0; + return RTLSDR_SUCCESS; } diff --git a/src/tuner_fc0012.c b/src/tuner_fc0012.c index 768cf1c..4378273 100644 --- a/src/tuner_fc0012.c +++ b/src/tuner_fc0012.c @@ -24,34 +24,40 @@ #include #include +#include "rtl-sdr.h" #include "rtlsdr_i2c.h" #include "tuner_fc0012.h" static int fc0012_writereg(void *dev, uint8_t reg, uint8_t val) { + int r; uint8_t data[2]; data[0] = reg; data[1] = val; - if (rtlsdr_i2c_write_fn(dev, FC0012_I2C_ADDR, data, 2) < 0) - return -1; + r = rtlsdr_i2c_write_fn(dev, FC0012_I2C_ADDR, data, 2); + if (r < 0) + return r; - return 0; + return RTLSDR_SUCCESS; } static int fc0012_readreg(void *dev, uint8_t reg, uint8_t *val) { + int r; uint8_t data = reg; - if (rtlsdr_i2c_write_fn(dev, FC0012_I2C_ADDR, &data, 1) < 0) - return -1; + r = rtlsdr_i2c_write_fn(dev, FC0012_I2C_ADDR, &data, 1); + if (r < 0) + return r; - if (rtlsdr_i2c_read_fn(dev, FC0012_I2C_ADDR, &data, 1) < 0) - return -1; + r = rtlsdr_i2c_read_fn(dev, FC0012_I2C_ADDR, &data, 1); + if (r < 0) + return r; *val = data; - return 0; + return RTLSDR_SUCCESS; } /* Incomplete list of register settings: @@ -230,7 +236,7 @@ int fc0012_set_params(void *dev, uint32_t freq, uint32_t bandwidth) if ((reg[1] > 15) || (reg[2] < 0x0b)) { fprintf(stderr, "[FC0012] no valid PLL combination " "found for %u Hz!\n", freq); - return -1; + return RTLSDR_ERROR_INVALID; } /* fix clock out */ diff --git a/src/tuner_fc0013.c b/src/tuner_fc0013.c index 78b696e..6df3afd 100644 --- a/src/tuner_fc0013.c +++ b/src/tuner_fc0013.c @@ -27,34 +27,40 @@ #include #include +#include "rtl-sdr.h" #include "rtlsdr_i2c.h" #include "tuner_fc0013.h" static int fc0013_writereg(void *dev, uint8_t reg, uint8_t val) { + int r; uint8_t data[2]; data[0] = reg; data[1] = val; - if (rtlsdr_i2c_write_fn(dev, FC0013_I2C_ADDR, data, 2) < 0) - return -1; + r = rtlsdr_i2c_write_fn(dev, FC0013_I2C_ADDR, data, 2); + if (r < 0) + return r; - return 0; + return RTLSDR_SUCCESS; } static int fc0013_readreg(void *dev, uint8_t reg, uint8_t *val) { + int r; uint8_t data = reg; - if (rtlsdr_i2c_write_fn(dev, FC0013_I2C_ADDR, &data, 1) < 0) - return -1; + r = rtlsdr_i2c_write_fn(dev, FC0013_I2C_ADDR, &data, 1); + if (r < 0) + return r; - if (rtlsdr_i2c_read_fn(dev, FC0013_I2C_ADDR, &data, 1) < 0) - return -1; + r = rtlsdr_i2c_read_fn(dev, FC0013_I2C_ADDR, &data, 1); + if (r < 0) + return r; *val = data; - return 0; + return RTLSDR_SUCCESS; } int fc0013_init(void *dev) @@ -335,7 +341,7 @@ int fc0013_set_params(void *dev, uint32_t freq, uint32_t bandwidth) if ((reg[1] > 15) || (reg[2] < 0x0b)) { fprintf(stderr, "[FC0013] no valid PLL combination " "found for %u Hz!\n", freq); - return -1; + return RTLSDR_ERROR_INVALID; } /* fix clock out */ @@ -428,22 +434,25 @@ exit: int fc0013_set_gain_mode(void *dev, int manual) { - int ret = 0; + int ret; + int fret = 0; uint8_t tmp = 0; - ret |= fc0013_readreg(dev, 0x0d, &tmp); + fret = fc0013_readreg(dev, 0x0d, &tmp); if (manual) tmp |= (1 << 3); else tmp &= ~(1 << 3); - ret |= fc0013_writereg(dev, 0x0d, tmp); + ret = fc0013_writereg(dev, 0x0d, tmp); + if (ret) fret = ret; /* set a fixed IF-gain for now */ - ret |= fc0013_writereg(dev, 0x13, 0x0a); + ret = fc0013_writereg(dev, 0x13, 0x0a); + if (ret) fret = ret; - return ret; + return fret; } int fc0013_lna_gains[] ={ @@ -477,11 +486,12 @@ int fc0013_lna_gains[] ={ int fc0013_set_lna_gain(void *dev, int gain) { - int ret = 0; + int ret; + int fret = 0; unsigned int i; uint8_t tmp = 0; - ret |= fc0013_readreg(dev, 0x14, &tmp); + fret = fc0013_readreg(dev, 0x14, &tmp); /* mask bits off */ tmp &= 0xe0; @@ -494,7 +504,8 @@ int fc0013_set_lna_gain(void *dev, int gain) } /* set gain */ - ret |= fc0013_writereg(dev, 0x14, tmp); + ret = fc0013_writereg(dev, 0x14, tmp); + if (ret) fret = ret; - return ret; + return fret; } diff --git a/src/tuner_fc2580.c b/src/tuner_fc2580.c index d2eeba5..9aa9ff7 100644 --- a/src/tuner_fc2580.c +++ b/src/tuner_fc2580.c @@ -8,6 +8,7 @@ #include +#include "rtl-sdr.h" #include "rtlsdr_i2c.h" #include "tuner_fc2580.h" @@ -16,32 +17,32 @@ /* glue functions to rtl-sdr code */ -fc2580_fci_result_type fc2580_i2c_write(void *pTuner, unsigned char reg, unsigned char val) +enum rtlsdr_error fc2580_i2c_write(void *pTuner, unsigned char reg, unsigned char val) { uint8_t data[2]; data[0] = reg; data[1] = val; - if (rtlsdr_i2c_write_fn(pTuner, FC2580_I2C_ADDR, data, 2) < 0) - return FC2580_FCI_FAIL; - - return FC2580_FCI_SUCCESS; + return rtlsdr_i2c_write_fn(pTuner, FC2580_I2C_ADDR, data, 2); } -fc2580_fci_result_type fc2580_i2c_read(void *pTuner, unsigned char reg, unsigned char *read_data) +enum rtlsdr_error fc2580_i2c_read(void *pTuner, unsigned char reg, unsigned char *read_data) { + int r; uint8_t data = reg; - if (rtlsdr_i2c_write_fn(pTuner, FC2580_I2C_ADDR, &data, 1) < 0) - return FC2580_FCI_FAIL; + r = rtlsdr_i2c_write_fn(pTuner, FC2580_I2C_ADDR, &data, 1); + if (r < 0) + return r; - if (rtlsdr_i2c_read_fn(pTuner, FC2580_I2C_ADDR, &data, 1) < 0) - return FC2580_FCI_FAIL; + r = rtlsdr_i2c_read_fn(pTuner, FC2580_I2C_ADDR, &data, 1); + if (r < 0) + return r; *read_data = data; - return FC2580_FCI_SUCCESS; + return RTLSDR_SUCCESS; } int @@ -59,15 +60,7 @@ fc2580_Initialize( // Note: CrystalFreqKhz = round(CrystalFreqHz / 1000) CrystalFreqKhz = (unsigned int)((CRYSTAL_FREQ + 500) / 1000); - if(fc2580_set_init(pTuner, AgcMode, CrystalFreqKhz) != FC2580_FCI_SUCCESS) - goto error_status_initialize_tuner; - - - return FUNCTION_SUCCESS; - - -error_status_initialize_tuner: - return FUNCTION_ERROR; + return fc2580_set_init(pTuner, AgcMode, CrystalFreqKhz); } int @@ -85,13 +78,7 @@ fc2580_SetRfFreqHz( RfFreqKhz = (unsigned int)((RfFreqHz + 500) / 1000); CrystalFreqKhz = (unsigned int)((CRYSTAL_FREQ + 500) / 1000); - if(fc2580_set_freq(pTuner, RfFreqKhz, CrystalFreqKhz) != FC2580_FCI_SUCCESS) - goto error_status_set_tuner_rf_frequency; - - return FUNCTION_SUCCESS; - -error_status_set_tuner_rf_frequency: - return FUNCTION_ERROR; + return fc2580_set_freq(pTuner, RfFreqKhz, CrystalFreqKhz); } /** @@ -111,14 +98,7 @@ fc2580_SetBandwidthMode( // Note: CrystalFreqKhz = round(CrystalFreqHz / 1000) CrystalFreqKhz = (unsigned int)((CRYSTAL_FREQ + 500) / 1000); - if(fc2580_set_filter(pTuner, (unsigned char)BandwidthMode, CrystalFreqKhz) != FC2580_FCI_SUCCESS) - goto error_status_set_tuner_bandwidth_mode; - - return FUNCTION_SUCCESS; - - -error_status_set_tuner_bandwidth_mode: - return FUNCTION_ERROR; + return fc2580_set_filter(pTuner, (unsigned char)BandwidthMode, CrystalFreqKhz); } void fc2580_wait_msec(void *pTuner, int a) @@ -143,37 +123,57 @@ 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 ) +enum rtlsdr_error fc2580_set_init( void *pTuner, int ifagc_mode, unsigned int freq_xtal ) { - fc2580_fci_result_type result = FC2580_FCI_SUCCESS; - - result &= fc2580_i2c_write(pTuner, 0x00, 0x00); /*** Confidential ***/ - result &= fc2580_i2c_write(pTuner, 0x12, 0x86); - result &= fc2580_i2c_write(pTuner, 0x14, 0x5C); - result &= fc2580_i2c_write(pTuner, 0x16, 0x3C); - result &= fc2580_i2c_write(pTuner, 0x1F, 0xD2); - result &= fc2580_i2c_write(pTuner, 0x09, 0xD7); - result &= fc2580_i2c_write(pTuner, 0x0B, 0xD5); - result &= fc2580_i2c_write(pTuner, 0x0C, 0x32); - result &= fc2580_i2c_write(pTuner, 0x0E, 0x43); - result &= fc2580_i2c_write(pTuner, 0x21, 0x0A); - result &= fc2580_i2c_write(pTuner, 0x22, 0x82); + enum rtlsdr_error result; + enum rtlsdr_error fresult = RTLSDR_SUCCESS; + + result = fc2580_i2c_write(pTuner, 0x00, 0x00); /*** Confidential ***/ + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x12, 0x86); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x14, 0x5C); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x16, 0x3C); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x1F, 0xD2); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x09, 0xD7); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x0B, 0xD5); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x0C, 0x32); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x0E, 0x43); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x21, 0x0A); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x22, 0x82); + if (result) fresult = result; if( ifagc_mode == 1 ) { - result &= fc2580_i2c_write(pTuner, 0x45, 0x10); //internal AGC - result &= fc2580_i2c_write(pTuner, 0x4C, 0x00); //HOLD_AGC polarity + result = fc2580_i2c_write(pTuner, 0x45, 0x10); //internal AGC + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x4C, 0x00); //HOLD_AGC polarity + if (result) fresult = result; } else if( ifagc_mode == 2 ) { - result &= fc2580_i2c_write(pTuner, 0x45, 0x20); //Voltage Control Mode - result &= fc2580_i2c_write(pTuner, 0x4C, 0x02); //HOLD_AGC polarity + result = fc2580_i2c_write(pTuner, 0x45, 0x20); //Voltage Control Mode + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x4C, 0x02); //HOLD_AGC polarity + if (result) fresult = result; } - result &= fc2580_i2c_write(pTuner, 0x3F, 0x88); - result &= fc2580_i2c_write(pTuner, 0x02, 0x0E); - result &= fc2580_i2c_write(pTuner, 0x58, 0x14); - result &= fc2580_set_filter(pTuner, 8, freq_xtal); //BW = 7.8MHz - - return result; + result = fc2580_i2c_write(pTuner, 0x3F, 0x88); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x02, 0x0E); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x58, 0x14); + if (result) fresult = result; + result = fc2580_set_filter(pTuner, 8, freq_xtal); //BW = 7.8MHz + if (result) fresult = result; + + return fresult; } @@ -192,7 +192,7 @@ 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 ) +enum rtlsdr_error 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; @@ -202,7 +202,8 @@ fc2580_fci_result_type fc2580_set_freq( void *pTuner, unsigned int f_lo, unsigne 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; + enum rtlsdr_error result; + enum rtlsdr_error fresult = RTLSDR_SUCCESS; f_vco = ( band == FC2580_UHF_BAND )? f_lo * 4 : (( band == FC2580_L_BAND )? f_lo * 2 : f_lo * 12); r_val = ( f_vco >= 2*76*freq_xtal )? 1 : ( f_vco >= 76*freq_xtal )? 2 : 4; @@ -227,102 +228,176 @@ fc2580_fci_result_type fc2580_set_freq( void *pTuner, unsigned int f_lo, unsigne case FC2580_UHF_BAND: data_0x02 = (data_0x02 & 0x3F); - result &= fc2580_i2c_write(pTuner, 0x25, 0xF0); - result &= fc2580_i2c_write(pTuner, 0x27, 0x77); - result &= fc2580_i2c_write(pTuner, 0x28, 0x53); - result &= fc2580_i2c_write(pTuner, 0x29, 0x60); - result &= fc2580_i2c_write(pTuner, 0x30, 0x09); - result &= fc2580_i2c_write(pTuner, 0x50, 0x8C); - result &= fc2580_i2c_write(pTuner, 0x53, 0x50); + result = fc2580_i2c_write(pTuner, 0x25, 0xF0); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x27, 0x77); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x28, 0x53); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x29, 0x60); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x30, 0x09); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x50, 0x8C); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x53, 0x50); + if (result) fresult = result; if( f_lo < 538000 ) - result &= fc2580_i2c_write(pTuner, 0x5F, 0x13); + result = fc2580_i2c_write(pTuner, 0x5F, 0x13); else - result &= fc2580_i2c_write(pTuner, 0x5F, 0x15); + result = fc2580_i2c_write(pTuner, 0x5F, 0x15); + if (result) fresult = result; if( f_lo < 538000 ) { - result &= fc2580_i2c_write(pTuner, 0x61, 0x07); - result &= fc2580_i2c_write(pTuner, 0x62, 0x06); - result &= fc2580_i2c_write(pTuner, 0x67, 0x06); - result &= fc2580_i2c_write(pTuner, 0x68, 0x08); - result &= fc2580_i2c_write(pTuner, 0x69, 0x10); - result &= fc2580_i2c_write(pTuner, 0x6A, 0x12); + result = fc2580_i2c_write(pTuner, 0x61, 0x07); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x62, 0x06); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x67, 0x06); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x68, 0x08); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x69, 0x10); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x6A, 0x12); + if (result) fresult = result; } else if( f_lo < 794000 ) { - result &= fc2580_i2c_write(pTuner, 0x61, 0x03); - result &= fc2580_i2c_write(pTuner, 0x62, 0x03); - result &= fc2580_i2c_write(pTuner, 0x67, 0x03); //ACI improve - result &= fc2580_i2c_write(pTuner, 0x68, 0x05); //ACI improve - result &= fc2580_i2c_write(pTuner, 0x69, 0x0C); - result &= fc2580_i2c_write(pTuner, 0x6A, 0x0E); + result = fc2580_i2c_write(pTuner, 0x61, 0x03); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x62, 0x03); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x67, 0x03); //ACI improve + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x68, 0x05); //ACI improve + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x69, 0x0C); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x6A, 0x0E); + if (result) fresult = result; } else { - result &= fc2580_i2c_write(pTuner, 0x61, 0x07); - result &= fc2580_i2c_write(pTuner, 0x62, 0x06); - result &= fc2580_i2c_write(pTuner, 0x67, 0x07); - result &= fc2580_i2c_write(pTuner, 0x68, 0x09); - result &= fc2580_i2c_write(pTuner, 0x69, 0x10); - result &= fc2580_i2c_write(pTuner, 0x6A, 0x12); + result = fc2580_i2c_write(pTuner, 0x61, 0x07); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x62, 0x06); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x67, 0x07); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x68, 0x09); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x69, 0x10); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x6A, 0x12); + if (result) fresult = result; } - result &= fc2580_i2c_write(pTuner, 0x63, 0x15); - - result &= fc2580_i2c_write(pTuner, 0x6B, 0x0B); - result &= fc2580_i2c_write(pTuner, 0x6C, 0x0C); - result &= fc2580_i2c_write(pTuner, 0x6D, 0x78); - result &= fc2580_i2c_write(pTuner, 0x6E, 0x32); - result &= fc2580_i2c_write(pTuner, 0x6F, 0x14); - result &= fc2580_set_filter(pTuner, 8, freq_xtal); //BW = 7.8MHz + result = fc2580_i2c_write(pTuner, 0x63, 0x15); + if (result) fresult = result; + + result = fc2580_i2c_write(pTuner, 0x6B, 0x0B); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x6C, 0x0C); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x6D, 0x78); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x6E, 0x32); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x6F, 0x14); + if (result) fresult = result; + result = fc2580_set_filter(pTuner, 8, freq_xtal); //BW = 7.8MHz + if (result) fresult = result; break; case FC2580_VHF_BAND: data_0x02 = (data_0x02 & 0x3F) | 0x80; - result &= fc2580_i2c_write(pTuner, 0x27, 0x77); - result &= fc2580_i2c_write(pTuner, 0x28, 0x33); - result &= fc2580_i2c_write(pTuner, 0x29, 0x40); - result &= fc2580_i2c_write(pTuner, 0x30, 0x09); - result &= fc2580_i2c_write(pTuner, 0x50, 0x8C); - result &= fc2580_i2c_write(pTuner, 0x53, 0x50); - result &= fc2580_i2c_write(pTuner, 0x5F, 0x0F); - result &= fc2580_i2c_write(pTuner, 0x61, 0x07); - result &= fc2580_i2c_write(pTuner, 0x62, 0x00); - result &= fc2580_i2c_write(pTuner, 0x63, 0x15); - result &= fc2580_i2c_write(pTuner, 0x67, 0x03); - result &= fc2580_i2c_write(pTuner, 0x68, 0x05); - result &= fc2580_i2c_write(pTuner, 0x69, 0x10); - result &= fc2580_i2c_write(pTuner, 0x6A, 0x12); - result &= fc2580_i2c_write(pTuner, 0x6B, 0x08); - result &= fc2580_i2c_write(pTuner, 0x6C, 0x0A); - result &= fc2580_i2c_write(pTuner, 0x6D, 0x78); - result &= fc2580_i2c_write(pTuner, 0x6E, 0x32); - result &= fc2580_i2c_write(pTuner, 0x6F, 0x54); - result &= fc2580_set_filter(pTuner, 7, freq_xtal); //BW = 6.8MHz + result = fc2580_i2c_write(pTuner, 0x27, 0x77); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x28, 0x33); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x29, 0x40); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x30, 0x09); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x50, 0x8C); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x53, 0x50); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x5F, 0x0F); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x61, 0x07); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x62, 0x00); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x63, 0x15); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x67, 0x03); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x68, 0x05); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x69, 0x10); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x6A, 0x12); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x6B, 0x08); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x6C, 0x0A); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x6D, 0x78); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x6E, 0x32); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x6F, 0x54); + if (result) fresult = result; + result = fc2580_set_filter(pTuner, 7, freq_xtal); //BW = 6.8MHz + if (result) fresult = result; break; case FC2580_L_BAND: data_0x02 = (data_0x02 & 0x3F) | 0x40; - result &= fc2580_i2c_write(pTuner, 0x2B, 0x70); - result &= fc2580_i2c_write(pTuner, 0x2C, 0x37); - result &= fc2580_i2c_write(pTuner, 0x2D, 0xE7); - result &= fc2580_i2c_write(pTuner, 0x30, 0x09); - result &= fc2580_i2c_write(pTuner, 0x44, 0x20); - result &= fc2580_i2c_write(pTuner, 0x50, 0x8C); - result &= fc2580_i2c_write(pTuner, 0x53, 0x50); - result &= fc2580_i2c_write(pTuner, 0x5F, 0x0F); - result &= fc2580_i2c_write(pTuner, 0x61, 0x0F); - result &= fc2580_i2c_write(pTuner, 0x62, 0x00); - result &= fc2580_i2c_write(pTuner, 0x63, 0x13); - result &= fc2580_i2c_write(pTuner, 0x67, 0x00); - result &= fc2580_i2c_write(pTuner, 0x68, 0x02); - result &= fc2580_i2c_write(pTuner, 0x69, 0x0C); - result &= fc2580_i2c_write(pTuner, 0x6A, 0x0E); - result &= fc2580_i2c_write(pTuner, 0x6B, 0x08); - result &= fc2580_i2c_write(pTuner, 0x6C, 0x0A); - result &= fc2580_i2c_write(pTuner, 0x6D, 0xA0); - result &= fc2580_i2c_write(pTuner, 0x6E, 0x50); - result &= fc2580_i2c_write(pTuner, 0x6F, 0x14); - result &= fc2580_set_filter(pTuner, 1, freq_xtal); //BW = 1.53MHz + result = fc2580_i2c_write(pTuner, 0x2B, 0x70); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x2C, 0x37); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x2D, 0xE7); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x30, 0x09); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x44, 0x20); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x50, 0x8C); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x53, 0x50); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x5F, 0x0F); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x61, 0x0F); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x62, 0x00); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x63, 0x13); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x67, 0x00); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x68, 0x02); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x69, 0x0C); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x6A, 0x0E); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x6B, 0x08); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x6C, 0x0A); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x6D, 0xA0); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x6E, 0x50); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x6F, 0x14); + if (result) fresult = result; + result = fc2580_set_filter(pTuner, 1, freq_xtal); //BW = 1.53MHz + if (result) fresult = result; break; default: break; @@ -331,23 +406,32 @@ fc2580_fci_result_type fc2580_set_freq( void *pTuner, unsigned int f_lo, unsigne // } //A command about AGC clock's pre-divide ratio - if( freq_xtal >= 28000 ) - result &= fc2580_i2c_write(pTuner, 0x4B, 0x22 ); + if( freq_xtal >= 28000 ) { + result = fc2580_i2c_write(pTuner, 0x4B, 0x22 ); + if (result) fresult = result; + } //Commands about VCO Band and PLL setting. - result &= fc2580_i2c_write(pTuner, 0x02, data_0x02); + result = fc2580_i2c_write(pTuner, 0x02, data_0x02); + if (result) fresult = result; data_0x18 = ( ( r_val == 1 )? 0x00 : ( ( r_val == 2 )? 0x10 : 0x20 ) ) + (unsigned char)(k_val >> 16); - result &= fc2580_i2c_write(pTuner, 0x18, data_0x18); //Load 'R' value and high part of 'K' values - result &= fc2580_i2c_write(pTuner, 0x1A, (unsigned char)( k_val >> 8 ) ); //Load middle part of 'K' value - result &= fc2580_i2c_write(pTuner, 0x1B, (unsigned char)( k_val ) ); //Load lower part of 'K' value - result &= fc2580_i2c_write(pTuner, 0x1C, (unsigned char)( n_val ) ); //Load 'N' value + result = fc2580_i2c_write(pTuner, 0x18, data_0x18); //Load 'R' value and high part of 'K' values + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x1A, (unsigned char)( k_val >> 8 ) ); //Load middle part of 'K' value + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x1B, (unsigned char)( k_val ) ); //Load lower part of 'K' value + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x1C, (unsigned char)( n_val ) ); //Load 'N' value + if (result) fresult = result; //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 + if( band == FC2580_UHF_BAND ) { + result = fc2580_i2c_write(pTuner, 0x2D, ( f_lo <= (unsigned int)794000 )? 0x9F : 0x8F ); //LNA_OUT_CAP + if (result) fresult = result; + } - return result; + return fresult; } @@ -369,57 +453,78 @@ fc2580_fci_result_type fc2580_set_freq( void *pTuner, unsigned int f_lo, unsigne ==============================================================================*/ -fc2580_fci_result_type fc2580_set_filter( void *pTuner, unsigned char filter_bw, unsigned int freq_xtal ) +enum rtlsdr_error 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; + enum rtlsdr_error result; + enum rtlsdr_error fresult = RTLSDR_SUCCESS; if(filter_bw == 1) { - result &= fc2580_i2c_write(pTuner, 0x36, 0x1C); - result &= fc2580_i2c_write(pTuner, 0x37, (unsigned char)(4151*freq_xtal/1000000) ); - result &= fc2580_i2c_write(pTuner, 0x39, 0x00); - result &= fc2580_i2c_write(pTuner, 0x2E, 0x09); + result = fc2580_i2c_write(pTuner, 0x36, 0x1C); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x37, (unsigned char)(4151*freq_xtal/1000000) ); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x39, 0x00); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x2E, 0x09); + if (result) fresult = result; } if(filter_bw == 6) { - result &= fc2580_i2c_write(pTuner, 0x36, 0x18); - result &= fc2580_i2c_write(pTuner, 0x37, (unsigned char)(4400*freq_xtal/1000000) ); - result &= fc2580_i2c_write(pTuner, 0x39, 0x00); - result &= fc2580_i2c_write(pTuner, 0x2E, 0x09); + result = fc2580_i2c_write(pTuner, 0x36, 0x18); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x37, (unsigned char)(4400*freq_xtal/1000000) ); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x39, 0x00); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x2E, 0x09); + if (result) fresult = result; } else if(filter_bw == 7) { - result &= fc2580_i2c_write(pTuner, 0x36, 0x18); - result &= fc2580_i2c_write(pTuner, 0x37, (unsigned char)(3910*freq_xtal/1000000) ); - result &= fc2580_i2c_write(pTuner, 0x39, 0x80); - result &= fc2580_i2c_write(pTuner, 0x2E, 0x09); + result = fc2580_i2c_write(pTuner, 0x36, 0x18); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x37, (unsigned char)(3910*freq_xtal/1000000) ); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x39, 0x80); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x2E, 0x09); + if (result) fresult = result; } else if(filter_bw == 8) { - result &= fc2580_i2c_write(pTuner, 0x36, 0x18); - result &= fc2580_i2c_write(pTuner, 0x37, (unsigned char)(3300*freq_xtal/1000000) ); - result &= fc2580_i2c_write(pTuner, 0x39, 0x80); - result &= fc2580_i2c_write(pTuner, 0x2E, 0x09); + result = fc2580_i2c_write(pTuner, 0x36, 0x18); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x37, (unsigned char)(3300*freq_xtal/1000000) ); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x39, 0x80); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x2E, 0x09); + if (result) fresult = result; } for(i=0; i<5; i++) { fc2580_wait_msec(pTuner, 5);//wait 5ms - result &= fc2580_i2c_read(pTuner, 0x2F, &cal_mon); + result = fc2580_i2c_read(pTuner, 0x2F, &cal_mon); + if (result) fresult = result; if( (cal_mon & 0xC0) != 0xC0) { - result &= fc2580_i2c_write(pTuner, 0x2E, 0x01); - result &= fc2580_i2c_write(pTuner, 0x2E, 0x09); + result = fc2580_i2c_write(pTuner, 0x2E, 0x01); + if (result) fresult = result; + result = fc2580_i2c_write(pTuner, 0x2E, 0x09); + if (result) fresult = result; } else break; } - result &= fc2580_i2c_write(pTuner, 0x2E, 0x01); + result = fc2580_i2c_write(pTuner, 0x2E, 0x01); + if (result) fresult = result; - return result; + return fresult; } /*============================================================================== diff --git a/src/tuner_r82xx.c b/src/tuner_r82xx.c index fe52bd9..9561c1f 100644 --- a/src/tuner_r82xx.c +++ b/src/tuner_r82xx.c @@ -26,6 +26,7 @@ #include #include +#include "rtl-sdr.h" #include "rtlsdr_i2c.h" #include "tuner_r82xx.h" @@ -275,7 +276,7 @@ static int r82xx_write(struct r82xx_priv *priv, uint8_t reg, const uint8_t *val, __FUNCTION__, rc, reg, size); if (rc < 0) return rc; - return -1; + return RTLSDR_ERROR_SHORT; } reg += size; @@ -283,7 +284,7 @@ static int r82xx_write(struct r82xx_priv *priv, uint8_t reg, const uint8_t *val, pos += size; } while (len > 0); - return 0; + return RTLSDR_SUCCESS; } static int r82xx_write_reg(struct r82xx_priv *priv, uint8_t reg, uint8_t val) @@ -298,7 +299,7 @@ static int r82xx_read_cache_reg(struct r82xx_priv *priv, int reg) if (reg >= 0 && reg < NUM_REGS) return priv->regs[reg]; else - return -1; + return RTLSDR_ERROR_INVALID; } static int r82xx_write_reg_mask(struct r82xx_priv *priv, uint8_t reg, uint8_t val, @@ -330,8 +331,10 @@ static int r82xx_read(struct r82xx_priv *priv, uint8_t reg, uint8_t *val, int le priv->buf[0] = reg; rc = rtlsdr_i2c_write_fn(priv->rtl_dev, priv->cfg->i2c_addr, priv->buf, 1); - if (rc < 1) + if (rc < 0) return rc; + if (rc < 1) + return RTLSDR_ERROR_SHORT; rc = rtlsdr_i2c_read_fn(priv->rtl_dev, priv->cfg->i2c_addr, p, len); @@ -340,14 +343,14 @@ static int r82xx_read(struct r82xx_priv *priv, uint8_t reg, uint8_t *val, int le __FUNCTION__, rc, reg, len); if (rc < 0) return rc; - return -1; + return RTLSDR_ERROR_SHORT; } /* Copy data to the output buffer */ for (i = 0; i < len; i++) val[i] = r82xx_bitrev(p[i]); - return 0; + return RTLSDR_SUCCESS; } /* @@ -490,7 +493,7 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) if (nint > ((128 / vco_power_ref) - 1)) { fprintf(stderr, "[R82XX] No valid PLL values for %u Hz!\n", freq); - return -1; + return RTLSDR_ERROR_INVALID; } ni = (nint - 13) / 4; @@ -549,7 +552,7 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) if (!(data[2] & 0x40)) { fprintf(stderr, "[R82XX] PLL not locked!\n"); priv->has_lock = 0; - return 0; + return RTLSDR_SUCCESS; } priv->has_lock = 1; @@ -754,7 +757,7 @@ static int r82xx_sysfreq_sel(struct r82xx_priv *priv, uint32_t freq, if (rc < 0) return rc; } - return 0; + return RTLSDR_SUCCESS; } static int r82xx_set_tv_standard(struct r82xx_priv *priv, @@ -911,7 +914,7 @@ static int r82xx_set_tv_standard(struct r82xx_priv *priv, priv->type = type; priv->bw = bw; - return 0; + return RTLSDR_SUCCESS; } static int r82xx_read_gain(struct r82xx_priv *priv) @@ -1010,7 +1013,7 @@ int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain) return rc; } - return 0; + return RTLSDR_SUCCESS; } /* Bandwidth contribution by low-pass filter. */ @@ -1131,7 +1134,7 @@ int r82xx_standby(struct r82xx_priv *priv) /* If device was not initialized yet, don't need to standby */ if (!priv->init_done) - return 0; + return RTLSDR_ERROR_NOOP; rc = r82xx_write_reg(priv, 0x06, 0xb1); if (rc < 0) @@ -1229,7 +1232,7 @@ static int r82xx_xtal_check(struct r82xx_priv *priv) } if (i == ARRAY_SIZE(r82xx_xtal_capacitor)) - return -1; + return RTLSDR_ERROR_INVALID; return r82xx_xtal_capacitor[i][1]; } -- 2.11.0 From 0xloem at gmail.com Sun Sep 23 12:22:10 2018 From: 0xloem at gmail.com (Karl Semich) Date: Sun, 23 Sep 2018 12:22:10 +0000 Subject: [PATCH] rtl-sdr/lib: UPDATE Manually releasing async buffers Message-ID: <20180923122210.22563-1-0xloem@gmail.com> Small API added to allow sample buffers to be used after the lifetime of the callback, and manually returned to librtlsdr. Allows multithreaded apps to track USB buffer buildup without requiring memcpy() be called in the callback. This update includes a fix to handle unsubmitted buffers on cancel. --- include/rtl-sdr.h | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/librtlsdr.c | 46 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 93 insertions(+), 3 deletions(-) diff --git a/include/rtl-sdr.h b/include/rtl-sdr.h index 3ed13ae..1ef508d 100644 --- a/include/rtl-sdr.h +++ b/include/rtl-sdr.h @@ -331,6 +331,7 @@ RTLSDR_API int rtlsdr_set_offset_tuning(rtlsdr_dev_t *dev, int on); */ RTLSDR_API int rtlsdr_get_offset_tuning(rtlsdr_dev_t *dev); + /* streaming functions */ RTLSDR_API int rtlsdr_reset_buffer(rtlsdr_dev_t *dev); @@ -381,6 +382,55 @@ RTLSDR_API int rtlsdr_read_async(rtlsdr_dev_t *dev, RTLSDR_API int rtlsdr_cancel_async(rtlsdr_dev_t *dev); /*! + * Enable or disable automatic resubmission of async transfer buffers. + * By default this is enabled, but if disabled then buffers will not + * be refilled with data until passed to rtlsdr_release_manual(). + * + * This allows applications to track buildup of streaming buffers + * by returning from the callback quickly, without requiring an + * extra memcpy to take the data out of the callback for processing. + * + * \param dev the device handle given by rtlsdr_open() + * \param on 0 means disabled, 1 enabled + * \return 0 on success + */ +RTLSDR_API int rtlsdr_set_automatic_release(rtlsdr_dev_t *dev, int on); + +/*! +* When automatic releases are disabled, release a long-lived buffer that +* was passed to an async callback. Such buffers will not be reused until +* released. +* +* There must be enough released buffers submitted to libusb at all times +* to accommodate driver and hardware communication latency. The total +* number of buffers filling this role, and the total number of buffers +* unreleased, can be calculated: +* +* buf_num = value passed to rtlsdr_read_async at stream start +* callback_calls = number of times a buf has been passed to callback +* buf_releases = number of successful calls to rtlsdr_release_manual() +* +* bufs_reserved = callback_calls - buf_releases +* bufs_in_flight = buf_num - bufs_reserved +* +* Note that this calculation assumes that all reserved buffers have been +* passed to the callback already. It is only useful if the callback +* returns very quickly every time. If the callback waits, buffers +* may accumulate in the waiting time, and only be found when it returns. +* +* Once bufs_in_flight falls too low, the integrity of the stream is +* compromised as the device rewrites over its full buffer. The cutoff +* for this depends on latency in the operating system and hardware. +* +* Once bufs_in_flight hits zero, streaming will stop completely. +* +* \param dev the device handle given by rtlsdr_open() +* \param buf a pointer to the start of the buffer +* \return 0 on success +*/ +RTLSDR_API int rtlsdr_release_manual(rtlsdr_dev_t *dev, unsigned char *buf); + +/*! * Enable or disable the bias tee on GPIO PIN 0. * * \param dev the device handle given by rtlsdr_open() diff --git a/src/librtlsdr.c b/src/librtlsdr.c index 433ed5b..e093ed7 100644 --- a/src/librtlsdr.c +++ b/src/librtlsdr.c @@ -103,6 +103,7 @@ struct rtlsdr_dev { void *cb_ctx; enum rtlsdr_async_status async_status; int async_cancel; + int manual_release; int use_zerocopy; /* rtl demod context */ uint32_t rate; /* Hz */ @@ -1702,7 +1703,8 @@ static void LIBUSB_CALL _libusb_callback(struct libusb_transfer *xfer) if (dev->cb) dev->cb(xfer->buffer, xfer->actual_length, dev->cb_ctx); - libusb_submit_transfer(xfer); /* resubmit transfer */ + if (!dev->manual_release) + libusb_submit_transfer(xfer); /* resubmit transfer */ dev->xfer_errors = 0; } else if (LIBUSB_TRANSFER_CANCELLED != xfer->status) { #ifndef _WIN32 @@ -1753,7 +1755,8 @@ static int _rtlsdr_alloc_async_buffers(rtlsdr_dev_t *dev) dev->use_zerocopy = 1; for (i = 0; i < dev->xfer_buf_num; ++i) { - dev->xfer_buf[i] = libusb_dev_mem_alloc(dev->devh, dev->xfer_buf_len); + dev->xfer_buf[i] = libusb_dev_mem_alloc(dev->devh, dev->xfer_buf_len + + sizeof(struct libusb_transfer *)); if (!dev->xfer_buf[i]) { fprintf(stderr, "Failed to allocate zero-copy " @@ -1780,7 +1783,8 @@ static int _rtlsdr_alloc_async_buffers(rtlsdr_dev_t *dev) /* no zero-copy available, allocate buffers in userspace */ if (!dev->use_zerocopy) { for (i = 0; i < dev->xfer_buf_num; ++i) { - dev->xfer_buf[i] = malloc(dev->xfer_buf_len); + dev->xfer_buf[i] = malloc(dev->xfer_buf_len + + sizeof(struct libusb_transfer *)); if (!dev->xfer_buf[i]) return -ENOMEM; @@ -1873,6 +1877,9 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, (void *)dev, BULK_TIMEOUT); + *(struct libusb_transfer **)(dev->xfer_buf[i] + dev->xfer_buf_len) = + dev->xfer[i]; + r = libusb_submit_transfer(dev->xfer[i]); if (r < 0) { fprintf(stderr, "Failed to submit transfer %i\n" @@ -1909,6 +1916,14 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, if (LIBUSB_TRANSFER_CANCELLED != dev->xfer[i]->status) { r = libusb_cancel_transfer(dev->xfer[i]); + if (r == LIBUSB_ERROR_NOT_FOUND) { + /* transfer was not in flight */ + r = 0; + dev->xfer[i]->status = + LIBUSB_TRANSFER_CANCELLED; + continue; + } + /* handle events after canceling * to allow transfer status to * propagate */ @@ -1961,6 +1976,31 @@ int rtlsdr_cancel_async(rtlsdr_dev_t *dev) return -2; } +int rtlsdr_set_automatic_release(rtlsdr_dev_t *dev, int on) +{ + if (!dev) + return -1; + + if (RTLSDR_INACTIVE != dev->async_status) + return -2; + + dev->manual_release = !on; + + return 0; +} + +int rtlsdr_release_manual(rtlsdr_dev_t *dev, unsigned char *buf) +{ + struct libusb_transfer * xfer; + + if (!dev) + return -1; + + xfer = *(struct libusb_transfer **)(buf + dev->xfer_buf_len); + + return libusb_submit_transfer(xfer); +} + uint32_t rtlsdr_get_tuner_clock(void *dev) { uint32_t tuner_freq; -- 2.11.0 From 0xloem at gmail.com Sun Sep 23 12:29:43 2018 From: 0xloem at gmail.com (Baffo32) Date: Sun, 23 Sep 2018 12:29:43 +0000 Subject: [PATCH] gr-osmosdr/lib: REPOST add support for msg cmds Message-ID: <20180923122943.24790-1-baffo32@gmail.com> From: Karl Semich <0xloem at gmail.com> Adds support for message commands ala UHD to all devices. Refactored just a little to provide for this. Even if this is too much to review, a comment on the submission of this patch would be a great gift. I'm very interested in this feature and want to do the work to find a way to make it work. --- include/osmosdr/CMakeLists.txt | 1 + include/osmosdr/messages.h | 37 ++ lib/CMakeLists.txt | 1 + lib/common_iface.h | 368 +++++++++++++++++++ lib/dev_manager.cc | 815 +++++++++++++++++++++++++++++++++++++++++ lib/dev_manager.h | 176 +++++++++ lib/sink_iface.h | 336 +---------------- lib/sink_impl.cc | 370 +++---------------- lib/sink_impl.h | 15 +- lib/source_iface.h | 337 +---------------- lib/source_impl.cc | 446 +++------------------- lib/source_impl.h | 20 +- lib/uhd/uhd_sink_c.cc | 5 + lib/uhd/uhd_source_c.cc | 5 + 14 files changed, 1530 insertions(+), 1402 deletions(-) create mode 100644 include/osmosdr/messages.h create mode 100644 lib/common_iface.h create mode 100644 lib/dev_manager.cc create mode 100644 lib/dev_manager.h diff --git a/include/osmosdr/CMakeLists.txt b/include/osmosdr/CMakeLists.txt index d185ee6..6f92eaa 100644 --- a/include/osmosdr/CMakeLists.txt +++ b/include/osmosdr/CMakeLists.txt @@ -26,6 +26,7 @@ install(FILES ranges.h time_spec.h device.h + messages.h source.h sink.h DESTINATION include/osmosdr diff --git a/include/osmosdr/messages.h b/include/osmosdr/messages.h new file mode 100644 index 0000000..b4aacf0 --- /dev/null +++ b/include/osmosdr/messages.h @@ -0,0 +1,37 @@ +/* -*- c++ -*- */ +/* + * Copyright 2018 Karl Semich <0xloem at gmail.com> + * + * GNU Radio 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 3, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDED_OSMOSDR_MESSAGES_H +#define INCLUDED_OSMOSDR_MESSAGES_H + +#include + +namespace osmosdr { + +const pmt::pmt_t CMD_PORT = pmt::mp("command"); +const pmt::pmt_t CMD_CHAN_KEY = pmt::mp("chan"); +const pmt::pmt_t CMD_GAIN_KEY = pmt::mp("gain"); +const pmt::pmt_t CMD_FREQ_KEY = pmt::mp("freq"); +const pmt::pmt_t CMD_RATE_KEY = pmt::mp("rate"); +const pmt::pmt_t CMD_ANTENNA_KEY = pmt::mp("antenna"); +const pmt::pmt_t CMD_BANDWIDTH_KEY = pmt::mp("bandwidth"); + +} /* namespace osmosdr */ + +#endif /* INCLUDED_OSMOSDR_MESSAGES_H */ diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index dbb175a..c2816c9 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -38,6 +38,7 @@ ENDMACRO(GR_OSMOSDR_APPEND_LIBS) GR_OSMOSDR_APPEND_SRCS( source_impl.cc sink_impl.cc + dev_manager.cc ranges.cc device.cc time_spec.cc diff --git a/lib/common_iface.h b/lib/common_iface.h new file mode 100644 index 0000000..b3c6b03 --- /dev/null +++ b/lib/common_iface.h @@ -0,0 +1,368 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 Dimitri Stolnikov + * + * GNU Radio 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 3, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef OSMOSDR_COMMON_IFACE_H +#define OSMOSDR_COMMON_IFACE_H + +#include +#include + +/*! + * TODO: document + * + */ +class common_iface +{ +public: + /*! + * Get the number of channels the underlying radio hardware offers. + * \return the number of available channels + */ + virtual size_t get_num_channels( void ) = 0; + + /*! + * Get the possible sample rates for the underlying radio hardware. + * \return a range of rates in Sps + */ + virtual osmosdr::meta_range_t get_sample_rates( void ) = 0; + + /*! + * Set the sample rate for the underlying radio hardware. + * This also will select the appropriate IF bandpass, if applicable. + * \param rate a new rate in Sps + */ + virtual double set_sample_rate( double rate ) = 0; + + /*! + * Get the sample rate for the underlying radio hardware. + * This is the actual sample rate and may differ from the rate set. + * \return the actual rate in Sps + */ + virtual double get_sample_rate( void ) = 0; + + /*! + * Get the tunable frequency range for the underlying radio hardware. + * \param chan the channel index 0 to N-1 + * \return the frequency range in Hz + */ + virtual osmosdr::freq_range_t get_freq_range( size_t chan = 0 ) = 0; + + /*! + * Tune the underlying radio hardware to the desired center frequency. + * This also will select the appropriate RF bandpass. + * \param freq the desired frequency in Hz + * \param chan the channel index 0 to N-1 + * \return the actual frequency in Hz + */ + virtual double set_center_freq( double freq, size_t chan = 0 ) = 0; + + /*! + * Get the center frequency the underlying radio hardware is tuned to. + * This is the actual frequency and may differ from the frequency set. + * \param chan the channel index 0 to N-1 + * \return the frequency in Hz + */ + virtual double get_center_freq( size_t chan = 0 ) = 0; + + /*! + * Set the frequency correction value in parts per million. + * \param ppm the desired correction value in parts per million + * \param chan the channel index 0 to N-1 + * \return correction value in parts per million + */ + virtual double set_freq_corr( double ppm, size_t chan = 0 ) = 0; + + /*! + * Get the frequency correction value. + * \param chan the channel index 0 to N-1 + * \return correction value in parts per million + */ + virtual double get_freq_corr( size_t chan = 0 ) = 0; + + /*! + * Get the gain stage names of the underlying radio hardware. + * \param chan the channel index 0 to N-1 + * \return a vector of strings containing the names of gain stages + */ + virtual std::vector get_gain_names( size_t chan = 0 ) = 0; + + /*! + * Get the settable overall gain range for the underlying radio hardware. + * \param chan the channel index 0 to N-1 + * \return the gain range in dB + */ + virtual osmosdr::gain_range_t get_gain_range( size_t chan = 0 ) = 0; + + /*! + * Get the settable gain range for a specific gain stage. + * \param name the name of the gain stage + * \param chan the channel index 0 to N-1 + * \return the gain range in dB + */ + virtual osmosdr::gain_range_t get_gain_range( const std::string & name, + size_t chan = 0 ) = 0; + + /*! + * Set the gain mode for the underlying radio hardware. + * This might be supported only for certain hardware types. + * \param automatic the gain mode (true means automatic gain mode) + * \param chan the channel index 0 to N-1 + * \return the actual gain mode + */ + virtual bool set_gain_mode( bool automatic, size_t chan = 0 ) { return false; } + + /*! + * Get the gain mode selected for the underlying radio hardware. + * \param chan the channel index 0 to N-1 + * \return the actual gain mode (true means automatic gain mode) + */ + virtual bool get_gain_mode( size_t chan = 0 ) { return false; } + + /*! + * Set the gain for the underlying radio hardware. + * This function will automatically distribute the desired gain value over + * available gain stages in an appropriate way and return the actual value. + * \param gain the gain in dB + * \param chan the channel index 0 to N-1 + * \return the actual gain in dB + */ + virtual double set_gain( double gain, size_t chan = 0 ) = 0; + + /*! + * Set the named gain on the underlying radio hardware. + * \param gain the gain in dB + * \param name the name of the gain stage + * \param chan the channel index 0 to N-1 + * \return the actual gain in dB + */ + virtual double set_gain( double gain, + const std::string & name, + size_t chan = 0 ) = 0; + + /*! + * Get the actual gain setting of the underlying radio hardware. + * \param chan the channel index 0 to N-1 + * \return the actual gain in dB + */ + virtual double get_gain( size_t chan = 0 ) = 0; + + /*! + * Get the actual gain setting of a named stage. + * \param name the name of the gain stage + * \param chan the channel index 0 to N-1 + * \return the actual gain in dB + */ + virtual double get_gain( const std::string & name, size_t chan = 0 ) = 0; + + /*! + * Set the IF gain for the underlying radio hardware. + * This function will automatically distribute the desired gain value over + * available IF gain stages in an appropriate way and return the actual value. + * \param gain the gain in dB + * \param chan the channel index 0 to N-1 + * \return the actual gain in dB + */ + virtual double set_if_gain( double gain, size_t chan = 0 ) { return 0; } + + /*! + * Set the BB gain for the underlying radio hardware. + * This function will automatically distribute the desired gain value over + * available BB gain stages in an appropriate way and return the actual value. + * \param gain the gain in dB + * \param chan the channel index 0 to N-1 + * \return the actual gain in dB + */ + virtual double set_bb_gain( double gain, size_t chan = 0 ) { return 0; } + + /*! + * Get the available antennas of the underlying radio hardware. + * \param chan the channel index 0 to N-1 + * \return a vector of strings containing the names of available antennas + */ + virtual std::vector< std::string > get_antennas( size_t chan = 0 ) = 0; + + /*! + * Select the active antenna of the underlying radio hardware. + * \param chan the channel index 0 to N-1 + * \return the actual antenna's name + */ + virtual std::string set_antenna( const std::string & antenna, + size_t chan = 0 ) = 0; + + /*! + * Get the actual underlying radio hardware antenna setting. + * \param chan the channel index 0 to N-1 + * \return the actual antenna's name + */ + virtual std::string get_antenna( size_t chan = 0 ) = 0; + + /*! + * Set the frontend DC offset value. + * The value is complex to control both I and Q. + * For RX, only set this when automatic correction is disabled. + * + * \param offset the dc offset (1.0 is full-scale) + * \param chan the channel index 0 to N-1 + */ + virtual void set_dc_offset( const std::complex &offset, size_t chan = 0 ) { } + + /*! + * Set the frontend IQ balance correction. + * Use this to adjust the magnitude and phase of I and Q. + * + * \param balance the complex correction value + * \param chan the channel index 0 to N-1 + */ + virtual void set_iq_balance( const std::complex &balance, size_t chan = 0 ) { } + + /*! + * Set the bandpass filter on the radio frontend. + * \param bandwidth the filter bandwidth in Hz, set to 0 for automatic selection + * \param chan the channel index 0 to N-1 + * \return the actual filter bandwidth in Hz + */ + virtual double set_bandwidth( double bandwidth, size_t chan = 0 ) { return 0; } + + /*! + * Get the actual bandpass filter setting on the radio frontend. + * \param chan the channel index 0 to N-1 + * \return the actual filter bandwidth in Hz + */ + virtual double get_bandwidth( size_t chan = 0 ) { return 0; } + + /*! + * Get the possible bandpass filter settings on the radio frontend. + * \param chan the channel index 0 to N-1 + * \return a range of bandwidths in Hz + */ + virtual osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0 ) + { return osmosdr::freq_range_t(); } + + /*! + * Set the time source for the device. + * This sets the method of time synchronization, + * typically a pulse per second or an encoded time. + * Typical options for source: external, MIMO. + * \param source a string representing the time source + * \param mboard which motherboard to set the config + */ + virtual void set_time_source(const std::string &source, + const size_t mboard = 0) { } + + /*! + * Get the currently set time source. + * \param mboard which motherboard to get the config + * \return the string representing the time source + */ + virtual std::string get_time_source(const size_t mboard) { return ""; } + + /*! + * Get a list of possible time sources. + * \param mboard which motherboard to get the list + * \return a vector of strings for possible settings + */ + virtual std::vector get_time_sources(const size_t mboard) + { + return std::vector(); + } + + /*! + * Set the clock source for the device. + * This sets the source for a 10 Mhz reference clock. + * Typical options for source: internal, external, MIMO. + * \param source a string representing the clock source + * \param mboard which motherboard to set the config + */ + virtual void set_clock_source(const std::string &source, + const size_t mboard = 0) { } + + /*! + * Get the currently set clock source. + * \param mboard which motherboard to get the config + * \return the string representing the clock source + */ + virtual std::string get_clock_source(const size_t mboard) { return ""; } + + /*! + * Get a list of possible clock sources. + * \param mboard which motherboard to get the list + * \return a vector of strings for possible settings + */ + virtual std::vector get_clock_sources(const size_t mboard) + { + return std::vector(); + } + + /*! + * Get the master clock rate. + * \param mboard the motherboard index 0 to M-1 + * \return the clock rate in Hz + */ + virtual double get_clock_rate(size_t mboard = 0) { return 0; } + + /*! + * Set the master clock rate. + * \param rate the new rate in Hz + * \param mboard the motherboard index 0 to M-1 + */ + virtual void set_clock_rate(double rate, size_t mboard = 0) { } + + /*! + * Get the current time registers. + * \param mboard the motherboard index 0 to M-1 + * \return the current device time + */ + virtual ::osmosdr::time_spec_t get_time_now(size_t mboard = 0) + { + return ::osmosdr::time_spec_t::get_system_time(); + } + + /*! + * Get the time when the last pps pulse occured. + * \param mboard the motherboard index 0 to M-1 + * \return the current device time + */ + virtual ::osmosdr::time_spec_t get_time_last_pps(size_t mboard = 0) + { + return ::osmosdr::time_spec_t::get_system_time(); + } + + /*! + * Sets the time registers immediately. + * \param time_spec the new time + * \param mboard the motherboard index 0 to M-1 + */ + virtual void set_time_now(const ::osmosdr::time_spec_t &time_spec, + size_t mboard = 0) { } + + /*! + * Set the time registers at the next pps. + * \param time_spec the new time + */ + virtual void set_time_next_pps(const ::osmosdr::time_spec_t &time_spec) { } + + /*! + * Sync the time registers with an unknown pps edge. + * \param time_spec the new time + */ + virtual void set_time_unknown_pps(const ::osmosdr::time_spec_t &time_spec) { } +}; + +#endif // OSMOSDR_COMMON_IFACE_H diff --git a/lib/dev_manager.cc b/lib/dev_manager.cc new file mode 100644 index 0000000..e2d0319 --- /dev/null +++ b/lib/dev_manager.cc @@ -0,0 +1,815 @@ +/* -*- c++ -*- */ +/* + * Copyright 2018 Karl Semich <0xloem at gmail.com> + * + * GNU Radio 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 3, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* + * config.h is generated by configure. It contains the results + * of probing for features, options etc. It should be the first + * file included in your .cc file. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "dev_manager.h" + +#include + +#include "source_iface.h" + +/* + * Create a new instance of dev_manager and return + * a boost shared_ptr. This is effectively the public constructor. + */ +dev_manager_sptr +make_dev_manager ( gr::hier_block2_sptr outer ) +{ + return gnuradio::get_initial_sptr( new dev_manager ( outer ) ); +} + +/* + * The private constructor + */ +dev_manager::dev_manager ( gr::hier_block2_sptr outer ) + : gr::block ( "dev_manager", + gr::io_signature::make(0, 0, 0), + gr::io_signature::make(0, 0, 0) ), + _outer(outer), + _channels(0), + _sample_rate(NAN) +{ + message_port_register_in( osmosdr::CMD_PORT ); + set_msg_handler( osmosdr::CMD_PORT, boost::bind(&dev_manager::msg_handler_command, this, _1) ); +} + +int dev_manager::general_work( int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + return 0; +} + +void dev_manager::add_device( gr::basic_block_sptr block, common_iface *iface ) +{ + if ( iface == nullptr || block.get() == nullptr ) { + if ( iface != nullptr || block.get() != nullptr ) + throw std::runtime_error("Either iface or block are NULL."); + return; + } + + size_t mboard = get_num_mboards(); + _devs.push_back( { iface, block, _channels } ); + + if ( is_sink( mboard ) ) { + // sink block + for (size_t i = 0; i < iface->get_num_channels(); i++) { + _outer->connect( _outer->self(), _channels, block, i ); + _chandevs.push_back( mboard ); + _channels++; + } + } else { + // source block + for (size_t i = 0; i < iface->get_num_channels(); i++) { +#ifdef HAVE_IQBALANCE + // add iqbalance blocks if enabled + gr::iqbalance::optimize_c::sptr iq_opt = gr::iqbalance::optimize_c::make( 0 ); + gr::iqbalance::fix_cc::sptr iq_fix = gr::iqbalance::fix_cc::make(); + + connect( block, i, iq_fix, 0 ); + connect( block, i, iq_opt, 0 ); + msg_connect( iq_opt, "iqbal_corr", iq_fix, "iqbal_corr" ); + + _iq_opt.push_back( iq_opt.get() ); + _iq_fix.push_back( iq_fix.get() ); + + _outer->connect( iq_fix, 0, _outer->self(), _channels ); + #else + _outer->connect( block, i, _outer->self(), _channels ); + #endif + _chandevs.push_back( mboard ); + _channels++; + } + } +} + +size_t dev_manager::get_num_mboards() const +{ + return _devs.size(); +} + +size_t dev_manager::get_mboard_for_channel( size_t chan ) const +{ + return _chandevs.at(chan); +} + +bool dev_manager::is_sink( size_t mboard ) const +{ + return get_block(mboard)->input_signature()->max_streams() > 0; +} + +gr::basic_block_sptr dev_manager::get_block( size_t mboard ) const +{ + return _devs.at(mboard).block; +} + +common_iface * dev_manager::get_iface( size_t mboard ) const +{ + return _devs.at(mboard).dev; +} + +size_t dev_manager::get_mboard_channel( size_t mboard ) const +{ + return _devs.at(mboard).chan; +} + +void dev_manager::msg_handler_command( pmt::pmt_t msg ) +{ + if ( !pmt::is_dict(msg) ) { + GR_LOG_ERROR( d_logger, boost::format("Command message is neither dict nor pair: %s") % msg ); + return; + } + + // if pair, this throws and converts to dict + try { + pmt::pmt_t keys = pmt::dict_keys(msg); + } catch ( const pmt::wrong_type & ) { + GR_LOG_DEBUG( d_debug_logger, boost::format("Converting pair to dict: %s") % msg ); + msg = pmt::dict_add( pmt::make_dict(), pmt::car(msg), pmt::cdr(msg) ); + } + + // read chan. defaults to -1, all chans at once + int chan; + try { + chan = int(pmt::to_long( + pmt::dict_ref( + msg, osmosdr::CMD_CHAN_KEY, + pmt::from_long(-1) + ) + )); + } catch ( const pmt::wrong_type & ) { + GR_LOG_ALERT( d_logger, boost::format("Invalid channel number %s") % pmt::dict_ref( msg, osmosdr::CMD_CHAN_KEY ) ); + return; + } + + // rate applies to all channels + pmt::pmt_t rate = pmt::dict_ref( msg, osmosdr::CMD_RATE_KEY, pmt::PMT_NIL ); + if ( rate != pmt::PMT_NIL ) { + if ( !pmt::is_real(rate) && !pmt::is_integer(rate) ) { + GR_LOG_ALERT( d_logger, boost::format("Invalid type for sample rate %s") % rate ); + return; + } + set_sample_rate( pmt::to_double(rate) ); + msg = pmt::dict_delete( msg, osmosdr::CMD_RATE_KEY ); + } + + // extract commands + pmt::pmt_t msg_items = pmt::dict_items(msg); + + // check every device + for (size_t mboard = 0; mboard < get_num_mboards(); mboard++) { + int cur_chan = get_mboard_channel( mboard ); + int next_chan = cur_chan + get_iface( mboard )->get_num_channels(); + if ( chan != -1 && chan >= next_chan ) + continue; + + // if device accepts command messages, just pass it along + gr::basic_block_sptr block = get_block( mboard ); + if ( block->has_msg_port(osmosdr::CMD_PORT) ) { + if ( chan != -1 ) + block->_post( osmosdr::CMD_PORT, pmt::dict_add(msg, osmosdr::CMD_CHAN_KEY, pmt::from_long(chan - cur_chan)) ); + else + block->_post( osmosdr::CMD_PORT, msg ); + continue; + } + + int start_chan, end_chan; + if ( chan == -1 ) { + start_chan = cur_chan; + end_chan = next_chan; + } else { + start_chan = chan; + end_chan = chan + 1; + } + + for (int it_chan = start_chan; it_chan < end_chan; ++ it_chan){ + // execute every command + + for (size_t i = 0; i < pmt::length(msg_items); ++ i){ + pmt::pmt_t const & item = pmt::nth(i, msg_items); + try { + exec_msg_cmd( pmt::car(item), pmt::cdr(item), it_chan ); + } catch (const pmt::wrong_type &) { + try { + GR_LOG_ALERT( d_logger, boost::format("Invalid command value for key %s: %s") % pmt::car(item) % pmt::cdr(item) ); + } catch (const pmt::wrong_type &) { + GR_LOG_ALERT( d_logger, boost::format("Command is not a pair: %s") % item ); + } + return; + } + } + } + } +} + +void dev_manager::exec_msg_cmd( const pmt::pmt_t & cmd, const pmt::pmt_t & val, size_t chan ) +{ + if ( cmd == osmosdr::CMD_FREQ_KEY ) { + set_center_freq( pmt::to_double(val), chan ); + } else if ( cmd == osmosdr::CMD_GAIN_KEY ) { + set_gain( pmt::to_double(val), chan ); + } else if ( cmd == osmosdr::CMD_RATE_KEY ) { + // already set in msg_handler_command + ; + } else if ( cmd == osmosdr::CMD_ANTENNA_KEY ) { + set_antenna( pmt::symbol_to_string(val), chan ); + } else if ( cmd == osmosdr::CMD_BANDWIDTH_KEY ) { + set_bandwidth( pmt::to_double(val), chan ); + } + // unfamiliar commands are ignored +} + +size_t dev_manager::get_num_channels() +{ + return _channels; +} + +bool dev_manager::seek( long seek_point, int whence, size_t chan ) +{ + if ( chan > get_num_channels() ) + return false; + + size_t mboard = get_mboard_for_channel( chan ); + if ( is_sink( mboard ) ) + return false; + + size_t dev_chan = chan - get_mboard_channel( mboard ); + source_iface * dev = static_cast< source_iface * >( get_iface( mboard ) ); + + return dev->seek( seek_point, whence, dev_chan ); +} + +#define NO_DEVICES_MSG "FATAL: No device(s) available to work with." + +osmosdr::meta_range_t dev_manager::get_sample_rates() +{ + if ( get_num_mboards() ) + return get_iface(0)->get_sample_rates(); // assume same devices used in the group +#if 0 + else + throw std::runtime_error(NO_DEVICES_MSG); +#endif + return osmosdr::meta_range_t(); +} + +double dev_manager::set_sample_rate( double rate ) +{ + double sample_rate = 0; + + if (_sample_rate != rate) { +#if 0 + if (!get_num_mboards()) + throw std::runtime_error(NO_DEVICES_MSG); +#endif + for (size_t mboard = 0; mboard < get_num_mboards(); mboard++) { + common_iface *dev = get_iface( mboard ); + sample_rate = dev->set_sample_rate(rate); + if ( !is_sink( mboard ) ) { +#ifdef HAVE_IQBALANCE + // source block, update iqbalance if enabled + size_t channel = get_mboard_channel( mboard ); + size_t end = channel + dev->get_num_channels(); + for (; channel < end; channel++) { + if ( channel < _iq_opt.size() ) { + gr::iqbalance::optimize_c *opt = _iq_opt[channel]; + + if ( opt->period() > 0 ) { /* optimize is enabled */ + opt->set_period( dev->get_sample_rate() / 5 ); + opt_reset(); + } + } + } +#endif + } + } + + _sample_rate = sample_rate; + } + + return sample_rate; +} + +double dev_manager::get_sample_rate() +{ + double sample_rate = 0; + + if (get_num_mboards()) + sample_rate = get_iface(0)->get_sample_rate(); // assume same devices used in the group +#if 0 + else + throw std::runtime_error(NO_DEVICES_MSG); +#endif + return sample_rate; +} + +osmosdr::freq_range_t dev_manager::get_freq_range( size_t chan ) +{ + if ( chan > get_num_channels() ) + return osmosdr::freq_range_t(); + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + return dev->get_freq_range( dev_chan ); +} + +double dev_manager::set_center_freq( double freq, size_t chan ) +{ + if ( chan > get_num_channels() ) + return 0; + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + if ( _center_freq[ chan ] != freq ) { + _center_freq[ chan ] = freq; + return dev->set_center_freq( freq, dev_chan ); + } else { return _center_freq[ chan ]; } +} + +double dev_manager::get_center_freq( size_t chan ) +{ + if ( chan > get_num_channels() ) + return 0; + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + return dev->get_center_freq( dev_chan ); +} + +double dev_manager::set_freq_corr( double ppm, size_t chan ) +{ + if ( chan > get_num_channels() ) + return 0; + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + if ( _freq_corr[ chan ] != ppm ) { + _freq_corr[ chan ] = ppm; + return dev->set_freq_corr( ppm, dev_chan ); + } else { return _freq_corr[ chan ]; } +} + +double dev_manager::get_freq_corr( size_t chan ) +{ + if ( chan > get_num_channels() ) + return 0; + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + return dev->get_freq_corr( dev_chan ); +} + +std::vector dev_manager::get_gain_names( size_t chan ) +{ + if ( chan > get_num_channels() ) + return std::vector< std::string >(); + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + return dev->get_gain_names( dev_chan ); +} + +osmosdr::gain_range_t dev_manager::get_gain_range( size_t chan ) +{ + if ( chan > get_num_channels() ) + return osmosdr::gain_range_t(); + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + return dev->get_gain_range( dev_chan ); +} + +osmosdr::gain_range_t dev_manager::get_gain_range( const std::string & name, size_t chan ) +{ + if ( chan > get_num_channels() ) + return osmosdr::gain_range_t(); + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + return dev->get_gain_range( name, dev_chan ); +} + +bool dev_manager::set_gain_mode( bool automatic, size_t chan ) +{ + if ( chan > get_num_channels() ) + return false; + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + if ( _gain_mode[ chan ] != automatic ) { + _gain_mode[ chan ] = automatic; + bool mode = dev->set_gain_mode( automatic, dev_chan ); + if (!automatic) // reapply gain value when switched to manual mode + dev->set_gain( _gain[ chan ], dev_chan ); + return mode; + } else { return _gain_mode[ chan ]; } +} + +bool dev_manager::get_gain_mode( size_t chan ) +{ + if ( chan > get_num_channels() ) + return false; + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + return dev->get_gain_mode( dev_chan ); +} + +double dev_manager::set_gain( double gain, size_t chan ) +{ + if ( chan > get_num_channels() ) + return 0; + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + if ( _gain[ chan ] != gain ) { + _gain[ chan ] = gain; + return dev->set_gain( gain, dev_chan ); + } else { return _gain[ chan ]; } +} + +double dev_manager::set_gain( double gain, const std::string & name, size_t chan) +{ + if ( chan > get_num_channels() ) + return 0; + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + return dev->set_gain( gain, name, dev_chan ); +} + +double dev_manager::get_gain( size_t chan ) +{ + if ( chan > get_num_channels() ) + return 0; + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + return dev->get_gain( dev_chan ); +} + +double dev_manager::get_gain( const std::string & name, size_t chan ) +{ + if ( chan > get_num_channels() ) + return 0; + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + return dev->get_gain( name, dev_chan ); +} + +double dev_manager::set_if_gain( double gain, size_t chan ) +{ + if ( chan > get_num_channels() ) + return 0; + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + if ( _if_gain[ chan ] != gain ) { + _if_gain[ chan ] = gain; + return dev->set_if_gain( gain, dev_chan ); + } else { return _if_gain[ chan ]; } +} + +double dev_manager::set_bb_gain( double gain, size_t chan ) +{ + if ( chan > get_num_channels() ) + return 0; + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + if ( _bb_gain[ chan ] != gain ) { + _bb_gain[ chan ] = gain; + return dev->set_bb_gain( gain, dev_chan ); + } else { return _bb_gain[ chan ]; } +} + +std::vector< std::string > dev_manager::get_antennas( size_t chan ) +{ + if ( chan > get_num_channels() ) + return std::vector< std::string >(); + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + return dev->get_antennas( dev_chan ); +} + +std::string dev_manager::set_antenna( const std::string & antenna, size_t chan ) +{ + if ( chan > get_num_channels() ) + return ""; + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + if ( _antenna[ chan ] != antenna ) { + _antenna[ chan ] = antenna; + return dev->set_antenna( antenna, dev_chan ); + } else { return _antenna[ chan ]; } +} + +std::string dev_manager::get_antenna( size_t chan ) +{ + if ( chan > get_num_channels() ) + return ""; + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + return dev->get_antenna( dev_chan ); +} + +void dev_manager::set_dc_offset_mode( int mode, size_t chan ) +{ + if ( chan > get_num_channels() ) + return; + + size_t mboard = get_mboard_for_channel( chan ); + if ( is_sink( mboard ) ) + return; + + size_t dev_chan = chan - get_mboard_channel( mboard ); + source_iface * dev = static_cast< source_iface * >( get_iface( mboard ) ); + + dev->set_dc_offset_mode( mode, dev_chan ); +} + +void dev_manager::set_dc_offset( const std::complex &offset, size_t chan ) +{ + if ( chan > get_num_channels() ) + return; + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + dev->set_dc_offset( offset, dev_chan ); +} + +void dev_manager::set_iq_balance_mode( int mode, size_t chan ) +{ + if ( chan > get_num_channels() ) + return; + + size_t mboard = get_mboard_for_channel( chan ); + if ( is_sink( mboard ) ) + return; + + size_t dev_chan = chan - get_mboard_channel( mboard ); + source_iface * dev = static_cast< source_iface * >( get_iface( mboard ) ); + +#ifdef HAVE_IQBALANCE + if ( chan < _iq_opt.size() && chan < _iq_fix.size() ) { + gr::iqbalance::optimize_c *opt = _iq_opt[chan]; + gr::iqbalance::fix_cc *fix = _iq_fix[chan]; + + if ( IQBalanceOff == mode ) { + opt->set_period( 0 ); + /* store current values in order to be able to restore them later */ + _vals[ chan ] = std::pair< float, float >( fix->mag(), fix->phase() ); + fix->set_mag( 0.0f ); + fix->set_phase( 0.0f ); + } else if ( IQBalanceManual == mode ) { + if ( opt->period() == 0 ) { /* transition from Off to Manual */ + /* restore previous values */ + std::pair< float, float > val = _vals[ chan ]; + fix->set_mag( val.first ); + fix->set_phase( val.second ); + } + opt->set_period( 0 ); + } else if ( IQBalanceAutomatic == mode ) { + opt->set_period( dev->get_sample_rate() / 5 ); + opt->reset(); + } + } +#else + return dev->set_iq_balance_mode( mode, dev_chan ); +#endif + +} + +void dev_manager::set_iq_balance( const std::complex &balance, size_t chan ) +{ + if ( chan > get_num_channels() ) + return; + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + +#ifdef HAVE_IQBALANCE + if ( ! is_sink( mboard ) ) { + // source block, update iqbalance if enabled + if ( chan < _iq_opt.size() && chan < _iq_fix.size() ) { + gr::iqbalance::optimize_c *opt = _iq_opt[chan]; + gr::iqbalance::fix_cc *fix = _iq_fix[chan]; + + if ( opt->period() == 0 ) { /* automatic optimization desabled */ + fix->set_mag( balance.real() ); + fix->set_phase( balance.imag() ); + } + } + } else { + dev->set_iq_balance( balance, dev_chan ); + } +#else + dev->set_iq_balance( balance, dev_chan ); +#endif +} + +double dev_manager::set_bandwidth( double bandwidth, size_t chan ) +{ + if ( chan > get_num_channels() ) + return 0; + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + if ( _bandwidth[ chan ] != bandwidth || 0.0f == bandwidth ) { + _bandwidth[ chan ] = bandwidth; + return dev->set_bandwidth( bandwidth, dev_chan ); + } else { return _bandwidth[ chan ]; } +} + +double dev_manager::get_bandwidth( size_t chan ) +{ + if ( chan > get_num_channels() ) + return 0; + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + return dev->get_bandwidth( dev_chan ); +} + +osmosdr::freq_range_t dev_manager::get_bandwidth_range( size_t chan ) +{ + if ( chan > get_num_channels() ) + return osmosdr::freq_range_t(); + + size_t mboard = get_mboard_for_channel( chan ); + common_iface *dev = get_iface( mboard ); + size_t dev_chan = chan - get_mboard_channel( mboard ); + + return dev->get_bandwidth_range( dev_chan ); +} + +void dev_manager::set_time_source( const std::string &source, const size_t mboard ) +{ + if ( mboard != osmosdr::ALL_MBOARDS ) { + get_iface( mboard )->set_time_source( source ); + return; + } + + for (size_t m = 0; m < get_num_mboards(); m++) { /* propagate ALL_MBOARDS */ + get_iface(m)->set_time_source( source, osmosdr::ALL_MBOARDS ); + } +} + +std::string dev_manager::get_time_source( const size_t mboard ) +{ + return get_iface( mboard )->get_time_source( mboard ); +} + +std::vector dev_manager::get_time_sources( const size_t mboard ) +{ + return get_iface( mboard )->get_time_sources( mboard ); +} + +void dev_manager::set_clock_source( const std::string &source, const size_t mboard ) +{ + if (mboard != osmosdr::ALL_MBOARDS){ + get_iface( mboard )->set_clock_source( source ); + return; + } + + for (size_t m = 0; m < get_num_mboards(); m++){ /* propagate ALL_MBOARDS */ + get_iface(m)->set_clock_source( source, osmosdr::ALL_MBOARDS ); + } +} + +std::string dev_manager::get_clock_source( const size_t mboard ) +{ + return get_iface( mboard )->get_clock_source( mboard ); +} + +std::vector dev_manager::get_clock_sources( const size_t mboard ) +{ + return get_iface( mboard )->get_clock_sources( mboard ); +} + +double dev_manager::get_clock_rate( size_t mboard ) +{ + return get_iface( mboard )->get_clock_rate( mboard ); +} + +void dev_manager::set_clock_rate( double rate, size_t mboard ) +{ + if ( mboard != osmosdr::ALL_MBOARDS ) { + get_iface( mboard )->set_clock_rate( rate ); + return; + } + + for (size_t m = 0; m < get_num_mboards(); m++) { /* propagate ALL_MBOARDS */ + get_iface(m)->set_clock_rate( rate, osmosdr::ALL_MBOARDS ); + } +} + +osmosdr::time_spec_t dev_manager::get_time_now( size_t mboard ) +{ + return get_iface( mboard )->get_time_now( mboard ); +} + +osmosdr::time_spec_t dev_manager::get_time_last_pps( size_t mboard ) +{ + return get_iface( mboard )->get_time_last_pps( mboard ); +} + +void dev_manager::set_time_now( const osmosdr::time_spec_t &time_spec, size_t mboard ) +{ + if ( mboard != osmosdr::ALL_MBOARDS ) { + get_iface( mboard )->set_time_now( time_spec ); + return; + } + + for (size_t m = 0; m < get_num_mboards(); m++) { /* propagate ALL_MBOARDS */ + get_iface(m)->set_time_now( time_spec, osmosdr::ALL_MBOARDS ); + } +} + +void dev_manager::set_time_next_pps( const osmosdr::time_spec_t &time_spec ) +{ + for (size_t i = 0; i < get_num_mboards(); i++) { + get_iface(i)->set_time_next_pps( time_spec ); + } +} + +void dev_manager::set_time_unknown_pps( const osmosdr::time_spec_t &time_spec ) +{ + for (size_t i = 0; i < get_num_mboards(); i++) { + get_iface(i)->set_time_unknown_pps( time_spec ); + } +} diff --git a/lib/dev_manager.h b/lib/dev_manager.h new file mode 100644 index 0000000..416573f --- /dev/null +++ b/lib/dev_manager.h @@ -0,0 +1,176 @@ +/* -*- c++ -*- */ +/* + * Copyright 2018 Karl Semich <0xloem at gmail.com> + * + * This file is part of GNU Radio + * + * GNU Radio 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 3, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DEV_CONTROL_H +#define INCLUDED_DEV_CONTROL_H + +#include +#include + +#include + +#include "common_iface.h" + +class dev_manager; + +/* + * We use boost::shared_ptr's instead of raw pointers for all access + * to gr::blocks (and many other data structures). The shared_ptr gets + * us transparent reference counting, which greatly simplifies storage + * management issues. This is especially helpful in our hybrid + * C++ / Python system. + * + * See http://www.boost.org/libs/smart_ptr/smart_ptr.htm + * + * As a convention, the _sptr suffix indicates a boost::shared_ptr + */ +typedef boost::shared_ptr dev_manager_sptr; + +/*! + * \brief Return a shared_ptr to a new instance of dev_manager. + * + * To avoid accidental use of raw pointers, dev_manager's + * constructor is private. make_dev_manager is the public + * interface for creating new instances. + */ +dev_manager_sptr make_dev_manager ( gr::hier_block2_sptr outer ); + +/*! + * \brief Manages a set of osmosdr devices. + * \ingroup block + * + */ +class dev_manager : public gr::block +{ +private: + // The friend declaration allows make_dev_manager to + // access the private constructor. + + friend dev_manager_sptr make_dev_manager ( gr::hier_block2_sptr outer ); + + /*! + * \brief Manages a set of osmosdr devices. + */ + dev_manager ( gr::hier_block2_sptr outer ); // private constructor + +public: + int general_work( int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items ); + + void add_device( gr::basic_block_sptr block, common_iface *iface ); // add a device driver block to be managed + size_t get_num_mboards() const; + size_t get_mboard_for_channel( size_t chan ) const; + bool is_sink( size_t mboard = 0 ) const; + gr::basic_block_sptr get_block( size_t mboard ) const; + common_iface * get_iface( size_t mboard ) const; + size_t get_mboard_channel( size_t mboard ) const; + + size_t get_num_channels(); + + bool seek( long seek_point, int whence, size_t chan); + + osmosdr::meta_range_t get_sample_rates( void ); + double set_sample_rate( double rate ); + double get_sample_rate( void ); + + osmosdr::freq_range_t get_freq_range( size_t chan ); + double set_center_freq( double freq, size_t chan ); + double get_center_freq( size_t chan ); + double set_freq_corr( double ppm, size_t chan ); + double get_freq_corr( size_t chan ); + + std::vector get_gain_names( size_t chan ); + osmosdr::gain_range_t get_gain_range( size_t chan ); + osmosdr::gain_range_t get_gain_range( const std::string & name, size_t chan ); + bool set_gain_mode( bool automatic, size_t chan ); + bool get_gain_mode( size_t chan ); + double set_gain( double gain, size_t chan ); + double set_gain( double gain, const std::string & name, size_t chan ); + double get_gain( size_t chan ); + double get_gain( const std::string & name, size_t chan ); + + double set_if_gain( double gain, size_t chan ); + double set_bb_gain( double gain, size_t chan ); + + std::vector< std::string > get_antennas( size_t chan ); + std::string set_antenna( const std::string & antenna, size_t chan ); + std::string get_antenna( size_t chan ); + + void set_dc_offset_mode( int mode, size_t chan ); + void set_dc_offset( const std::complex &offset, size_t chan ); + + void set_iq_balance_mode( int mode, size_t chan ); + void set_iq_balance( const std::complex &balance, size_t chan ); + + double set_bandwidth( double bandwidth, size_t chan ); + double get_bandwidth( size_t chan ); + osmosdr::freq_range_t get_bandwidth_range( size_t chan ); + + void set_time_source(const std::string &source, const size_t mboard = 0); + std::string get_time_source(const size_t mboard); + std::vector get_time_sources(const size_t mboard); + void set_clock_source(const std::string &source, const size_t mboard = 0); + std::string get_clock_source(const size_t mboard); + std::vector get_clock_sources(const size_t mboard); + double get_clock_rate(size_t mboard = 0); + void set_clock_rate(double rate, size_t mboard = 0); + ::osmosdr::time_spec_t get_time_now(size_t mboard = 0); + ::osmosdr::time_spec_t get_time_last_pps(size_t mboard = 0); + void set_time_now(const ::osmosdr::time_spec_t &time_spec, size_t mboard = 0); + void set_time_next_pps(const ::osmosdr::time_spec_t &time_spec); + void set_time_unknown_pps(const ::osmosdr::time_spec_t &time_spec); + +private: + void msg_handler_command(pmt::pmt_t msg); + void exec_msg_cmd(const pmt::pmt_t &cmd, const pmt::pmt_t &val, size_t chan); + + struct dev_t { + common_iface * dev; + gr::basic_block_sptr block; + size_t chan; + }; + + std::vector< dev_t > _devs; + std::vector< size_t > _chandevs; + gr::hier_block2_sptr _outer; + size_t _channels; + + /* cache to prevent multiple device calls with the same value coming from grc */ + double _sample_rate; + std::map< size_t, double > _center_freq; + std::map< size_t, double > _freq_corr; + std::map< size_t, bool > _gain_mode; + std::map< size_t, double > _gain; + std::map< size_t, double > _if_gain; + std::map< size_t, double > _bb_gain; + std::map< size_t, std::string > _antenna; +#ifdef HAVE_IQBALANCE + std::vector< gr::iqbalance::fix_cc * > _iq_fix; + std::vector< gr::iqbalance::optimize_c * > _iq_opt; + std::map< size_t, std::pair > _vals; +#endif + std::map< size_t, double > _bandwidth; +}; + +#endif /* INCLUDED_DEV_CONTROL_H */ diff --git a/lib/sink_iface.h b/lib/sink_iface.h index 39aabc7..f28e88b 100644 --- a/lib/sink_iface.h +++ b/lib/sink_iface.h @@ -25,344 +25,14 @@ #include #include +#include "common_iface.h" + /*! * TODO: document * */ -class sink_iface +class sink_iface : public common_iface { -public: - /*! - * Get the number of channels the underlying radio hardware offers. - * \return the number of available channels - */ - virtual size_t get_num_channels( void ) = 0; - - /*! - * Get the possible sample rates for the underlying radio hardware. - * \return a range of rates in Sps - */ - virtual osmosdr::meta_range_t get_sample_rates( void ) = 0; - - /*! - * Set the sample rate for the underlying radio hardware. - * This also will select the appropriate IF bandpass, if applicable. - * \param rate a new rate in Sps - */ - virtual double set_sample_rate( double rate ) = 0; - - /*! - * Get the sample rate for the underlying radio hardware. - * This is the actual sample rate and may differ from the rate set. - * \return the actual rate in Sps - */ - virtual double get_sample_rate( void ) = 0; - - /*! - * Get the tunable frequency range for the underlying radio hardware. - * \param chan the channel index 0 to N-1 - * \return the frequency range in Hz - */ - virtual osmosdr::freq_range_t get_freq_range( size_t chan = 0 ) = 0; - - /*! - * Tune the underlying radio hardware to the desired center frequency. - * This also will select the appropriate RF bandpass. - * \param freq the desired frequency in Hz - * \param chan the channel index 0 to N-1 - * \return the actual frequency in Hz - */ - virtual double set_center_freq( double freq, size_t chan = 0 ) = 0; - - /*! - * Get the center frequency the underlying radio hardware is tuned to. - * This is the actual frequency and may differ from the frequency set. - * \param chan the channel index 0 to N-1 - * \return the frequency in Hz - */ - virtual double get_center_freq( size_t chan = 0 ) = 0; - - /*! - * Set the frequency correction value in parts per million. - * \param ppm the desired correction value in parts per million - * \param chan the channel index 0 to N-1 - * \return correction value in parts per million - */ - virtual double set_freq_corr( double ppm, size_t chan = 0 ) = 0; - - /*! - * Get the frequency correction value. - * \param chan the channel index 0 to N-1 - * \return correction value in parts per million - */ - virtual double get_freq_corr( size_t chan = 0 ) = 0; - - /*! - * Get the gain stage names of the underlying radio hardware. - * \param chan the channel index 0 to N-1 - * \return a vector of strings containing the names of gain stages - */ - virtual std::vector get_gain_names( size_t chan = 0 ) = 0; - - /*! - * Get the settable overall gain range for the underlying radio hardware. - * \param chan the channel index 0 to N-1 - * \return the gain range in dB - */ - virtual osmosdr::gain_range_t get_gain_range( size_t chan = 0 ) = 0; - - /*! - * Get the settable gain range for a specific gain stage. - * \param name the name of the gain stage - * \param chan the channel index 0 to N-1 - * \return the gain range in dB - */ - virtual osmosdr::gain_range_t get_gain_range( const std::string & name, - size_t chan = 0 ) = 0; - - /*! - * Set the gain mode for the underlying radio hardware. - * This might be supported only for certain hardware types. - * \param automatic the gain mode (true means automatic gain mode) - * \param chan the channel index 0 to N-1 - * \return the actual gain mode - */ - virtual bool set_gain_mode( bool automatic, size_t chan = 0 ) { return false; } - - /*! - * Get the gain mode selected for the underlying radio hardware. - * \param chan the channel index 0 to N-1 - * \return the actual gain mode (true means automatic gain mode) - */ - virtual bool get_gain_mode( size_t chan = 0 ) { return false; } - - /*! - * Set the gain for the underlying radio hardware. - * This function will automatically distribute the desired gain value over - * available gain stages in an appropriate way and return the actual value. - * \param gain the gain in dB - * \param chan the channel index 0 to N-1 - * \return the actual gain in dB - */ - virtual double set_gain( double gain, size_t chan = 0 ) = 0; - - /*! - * Set the named gain on the underlying radio hardware. - * \param gain the gain in dB - * \param name the name of the gain stage - * \param chan the channel index 0 to N-1 - * \return the actual gain in dB - */ - virtual double set_gain( double gain, - const std::string & name, - size_t chan = 0 ) = 0; - - /*! - * Get the actual gain setting of the underlying radio hardware. - * \param chan the channel index 0 to N-1 - * \return the actual gain in dB - */ - virtual double get_gain( size_t chan = 0 ) = 0; - - /*! - * Get the actual gain setting of a named stage. - * \param name the name of the gain stage - * \param chan the channel index 0 to N-1 - * \return the actual gain in dB - */ - virtual double get_gain( const std::string & name, size_t chan = 0 ) = 0; - - /*! - * Set the IF gain for the underlying radio hardware. - * This function will automatically distribute the desired gain value over - * available IF gain stages in an appropriate way and return the actual value. - * \param gain the gain in dB - * \param chan the channel index 0 to N-1 - * \return the actual gain in dB - */ - virtual double set_if_gain( double gain, size_t chan = 0 ) { return 0; } - - /*! - * Set the BB gain for the underlying radio hardware. - * This function will automatically distribute the desired gain value over - * available BB gain stages in an appropriate way and return the actual value. - * \param gain the gain in dB - * \param chan the channel index 0 to N-1 - * \return the actual gain in dB - */ - virtual double set_bb_gain( double gain, size_t chan = 0 ) { return 0; } - - /*! - * Get the available antennas of the underlying radio hardware. - * \param chan the channel index 0 to N-1 - * \return a vector of strings containing the names of available antennas - */ - virtual std::vector< std::string > get_antennas( size_t chan = 0 ) = 0; - - /*! - * Select the active antenna of the underlying radio hardware. - * \param chan the channel index 0 to N-1 - * \return the actual antenna's name - */ - virtual std::string set_antenna( const std::string & antenna, - size_t chan = 0 ) = 0; - - /*! - * Get the actual underlying radio hardware antenna setting. - * \param chan the channel index 0 to N-1 - * \return the actual antenna's name - */ - virtual std::string get_antenna( size_t chan = 0 ) = 0; - - /*! - * Set the TX frontend DC offset value. - * The value is complex to control both I and Q. - * - * \param offset the dc offset (1.0 is full-scale) - * \param chan the channel index 0 to N-1 - */ - virtual void set_dc_offset( const std::complex &offset, size_t chan = 0 ) { } - - /*! - * Set the TX frontend IQ balance correction. - * Use this to adjust the magnitude and phase of I and Q. - * - * \param balance the complex correction value - * \param chan the channel index 0 to N-1 - */ - virtual void set_iq_balance( const std::complex &balance, size_t chan = 0 ) { } - - /*! - * Set the bandpass filter on the radio frontend. - * \param bandwidth the filter bandwidth in Hz, set to 0 for automatic selection - * \param chan the channel index 0 to N-1 - * \return the actual filter bandwidth in Hz - */ - virtual double set_bandwidth( double bandwidth, size_t chan = 0 ) { return 0; } - - /*! - * Get the actual bandpass filter setting on the radio frontend. - * \param chan the channel index 0 to N-1 - * \return the actual filter bandwidth in Hz - */ - virtual double get_bandwidth( size_t chan = 0 ) { return 0; } - - /*! - * Get the possible bandpass filter settings on the radio frontend. - * \param chan the channel index 0 to N-1 - * \return a range of bandwidths in Hz - */ - virtual osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0 ) - { return osmosdr::freq_range_t(); } - - /*! - * Set the time source for the device. - * This sets the method of time synchronization, - * typically a pulse per second or an encoded time. - * Typical options for source: external, MIMO. - * \param source a string representing the time source - * \param mboard which motherboard to set the config - */ - virtual void set_time_source(const std::string &source, - const size_t mboard = 0) { } - - /*! - * Get the currently set time source. - * \param mboard which motherboard to get the config - * \return the string representing the time source - */ - virtual std::string get_time_source(const size_t mboard) { return ""; } - - /*! - * Get a list of possible time sources. - * \param mboard which motherboard to get the list - * \return a vector of strings for possible settings - */ - virtual std::vector get_time_sources(const size_t mboard) - { - return std::vector(); - } - - /*! - * Set the clock source for the device. - * This sets the source for a 10 Mhz reference clock. - * Typical options for source: internal, external, MIMO. - * \param source a string representing the clock source - * \param mboard which motherboard to set the config - */ - virtual void set_clock_source(const std::string &source, - const size_t mboard = 0) { } - - /*! - * Get the currently set clock source. - * \param mboard which motherboard to get the config - * \return the string representing the clock source - */ - virtual std::string get_clock_source(const size_t mboard) { return ""; } - - /*! - * Get a list of possible clock sources. - * \param mboard which motherboard to get the list - * \return a vector of strings for possible settings - */ - virtual std::vector get_clock_sources(const size_t mboard) - { - return std::vector(); - } - - /*! - * Get the master clock rate. - * \param mboard the motherboard index 0 to M-1 - * \return the clock rate in Hz - */ - virtual double get_clock_rate(size_t mboard = 0) { return 0; } - - /*! - * Set the master clock rate. - * \param rate the new rate in Hz - * \param mboard the motherboard index 0 to M-1 - */ - virtual void set_clock_rate(double rate, size_t mboard = 0) { } - - /*! - * Get the current time registers. - * \param mboard the motherboard index 0 to M-1 - * \return the current device time - */ - virtual ::osmosdr::time_spec_t get_time_now(size_t mboard = 0) - { - return ::osmosdr::time_spec_t::get_system_time(); - } - - /*! - * Get the time when the last pps pulse occured. - * \param mboard the motherboard index 0 to M-1 - * \return the current device time - */ - virtual ::osmosdr::time_spec_t get_time_last_pps(size_t mboard = 0) - { - return ::osmosdr::time_spec_t::get_system_time(); - } - - /*! - * Sets the time registers immediately. - * \param time_spec the new time - * \param mboard the motherboard index 0 to M-1 - */ - virtual void set_time_now(const ::osmosdr::time_spec_t &time_spec, - size_t mboard = 0) { } - - /*! - * Set the time registers at the next pps. - * \param time_spec the new time - */ - virtual void set_time_next_pps(const ::osmosdr::time_spec_t &time_spec) { } - - /*! - * Sync the time registers with an unknown pps edge. - * \param time_spec the new time - */ - virtual void set_time_unknown_pps(const ::osmosdr::time_spec_t &time_spec) { } }; #endif // OSMOSDR_SINK_IFACE_H diff --git a/lib/sink_impl.cc b/lib/sink_impl.cc index 877b31f..1218425 100644 --- a/lib/sink_impl.cc +++ b/lib/sink_impl.cc @@ -72,11 +72,14 @@ sink_impl::sink_impl( const std::string &args ) : gr::hier_block2 ("sink_impl", args_to_io_signature(args), gr::io_signature::make(0, 0, 0)), - _sample_rate(NAN) + _manager(make_dev_manager(to_hier_block2())) { size_t channel = 0; bool device_specified = false; + message_port_register_hier_in( osmosdr::CMD_PORT ); + msg_connect( self(), osmosdr::CMD_PORT, _manager, osmosdr::CMD_PORT ); + std::vector< std::string > arg_list = args_to_vector(args); std::vector< std::string > dev_types; @@ -216,465 +219,214 @@ sink_impl::sink_impl( const std::string &args ) } #endif - if ( iface != NULL && long(block.get()) != 0 ) { - _devs.push_back( iface ); - - for (size_t i = 0; i < iface->get_num_channels(); i++) { - connect(self(), channel++, block, i); - } - } else if ( (iface != NULL) || (long(block.get()) != 0) ) - throw std::runtime_error("Either iface or block are NULL."); - + _manager->add_device( block, iface ); } - if (!_devs.size()) + if (!_manager->get_num_mboards()) throw std::runtime_error("No devices specified via device arguments."); } size_t sink_impl::get_num_channels() { - size_t channels = 0; - - BOOST_FOREACH( sink_iface *dev, _devs ) - channels += dev->get_num_channels(); - - return channels; + return _manager->get_num_channels(); } -#define NO_DEVICES_MSG "FATAL: No device(s) available to work with." - osmosdr::meta_range_t sink_impl::get_sample_rates() { - if ( ! _devs.empty() ) - return _devs[0]->get_sample_rates(); // assume same devices used in the group -#if 0 - else - throw std::runtime_error(NO_DEVICES_MSG); -#endif - return osmosdr::meta_range_t(); + return _manager->get_sample_rates(); } -double sink_impl::set_sample_rate(double rate) +double sink_impl::set_sample_rate( double rate ) { - double sample_rate = 0; - - if (_sample_rate != rate) { -#if 0 - if (_devs.empty()) - throw std::runtime_error(NO_DEVICES_MSG); -#endif - BOOST_FOREACH( sink_iface *dev, _devs ) - sample_rate = dev->set_sample_rate(rate); - - _sample_rate = sample_rate; - } - - return sample_rate; + return _manager->set_sample_rate( rate ); } double sink_impl::get_sample_rate() { - double sample_rate = 0; - - if (!_devs.empty()) - sample_rate = _devs[0]->get_sample_rate(); // assume same devices used in the group -#if 0 - else - throw std::runtime_error(NO_DEVICES_MSG); -#endif - return sample_rate; + return _manager->get_sample_rate(); } osmosdr::freq_range_t sink_impl::get_freq_range( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_freq_range( dev_chan ); - - return osmosdr::freq_range_t(); + return _manager->get_freq_range( chan ); } double sink_impl::set_center_freq( double freq, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) { - if ( _center_freq[ chan ] != freq ) { - _center_freq[ chan ] = freq; - return dev->set_center_freq( freq, dev_chan ); - } else { return _center_freq[ chan ]; } - } - - return 0; + return _manager->set_center_freq( freq, chan ); } double sink_impl::get_center_freq( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_center_freq( dev_chan ); - - return 0; + return _manager->get_center_freq( chan ); } double sink_impl::set_freq_corr( double ppm, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) { - if ( _freq_corr[ chan ] != ppm ) { - _freq_corr[ chan ] = ppm; - return dev->set_freq_corr( ppm, dev_chan ); - } else { return _freq_corr[ chan ]; } - } - - return 0; + return _manager->set_freq_corr( ppm, chan ); } double sink_impl::get_freq_corr( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_freq_corr( dev_chan ); - - return 0; + return _manager->get_freq_corr( chan ); } std::vector sink_impl::get_gain_names( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_gain_names( dev_chan ); - - return std::vector< std::string >(); + return _manager->get_gain_names( chan ); } osmosdr::gain_range_t sink_impl::get_gain_range( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_gain_range( dev_chan ); - - return osmosdr::gain_range_t(); + return _manager->get_gain_range( chan ); } osmosdr::gain_range_t sink_impl::get_gain_range( const std::string & name, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_gain_range( name, dev_chan ); - - return osmosdr::gain_range_t(); + return _manager->get_gain_range( name, chan ); } bool sink_impl::set_gain_mode( bool automatic, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) { - if ( _gain_mode[ chan ] != automatic ) { - _gain_mode[ chan ] = automatic; - bool mode = dev->set_gain_mode( automatic, dev_chan ); - if (!automatic) // reapply gain value when switched to manual mode - dev->set_gain( _gain[ chan ], dev_chan ); - return mode; - } else { return _gain_mode[ chan ]; } - } - - return false; + return _manager->set_gain_mode( automatic, chan ); } bool sink_impl::get_gain_mode( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_gain_mode( dev_chan ); - - return false; + return _manager->get_gain_mode( chan ); } double sink_impl::set_gain( double gain, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) { - if ( _gain[ chan ] != gain ) { - _gain[ chan ] = gain; - return dev->set_gain( gain, dev_chan ); - } else { return _gain[ chan ]; } - } - - return 0; + return _manager->set_gain( gain, chan ); } double sink_impl::set_gain( double gain, const std::string & name, size_t chan) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->set_gain( gain, name, dev_chan ); - - return 0; + return _manager->set_gain( gain, name, chan ); } double sink_impl::get_gain( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_gain( dev_chan ); - - return 0; + return _manager->get_gain( chan ); } double sink_impl::get_gain( const std::string & name, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_gain( name, dev_chan ); - - return 0; + return _manager->get_gain( name, chan ); } double sink_impl::set_if_gain( double gain, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) { - if ( _if_gain[ chan ] != gain ) { - _if_gain[ chan ] = gain; - return dev->set_if_gain( gain, dev_chan ); - } else { return _if_gain[ chan ]; } - } - - return 0; + return _manager->set_if_gain( gain, chan ); } double sink_impl::set_bb_gain( double gain, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) { - if ( _bb_gain[ chan ] != gain ) { - _bb_gain[ chan ] = gain; - return dev->set_bb_gain( gain, dev_chan ); - } else { return _bb_gain[ chan ]; } - } - - return 0; + return _manager->set_bb_gain( gain, chan ); } std::vector< std::string > sink_impl::get_antennas( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_antennas( dev_chan ); - - return std::vector< std::string >(); + return _manager->get_antennas( chan ); } std::string sink_impl::set_antenna( const std::string & antenna, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) { - if ( _antenna[ chan ] != antenna ) { - _antenna[ chan ] = antenna; - return dev->set_antenna( antenna, dev_chan ); - } else { return _antenna[ chan ]; } - } - - return ""; + return _manager->set_antenna( antenna, chan ); } std::string sink_impl::get_antenna( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_antenna( dev_chan ); - - return ""; + return _manager->get_antenna( chan ); } void sink_impl::set_dc_offset( const std::complex &offset, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - dev->set_dc_offset( offset, dev_chan ); + _manager->set_dc_offset( offset, chan ); } void sink_impl::set_iq_balance( const std::complex &balance, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - dev->set_iq_balance( balance, dev_chan ); + _manager->set_iq_balance( balance, chan ); } double sink_impl::set_bandwidth( double bandwidth, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) { - if ( _bandwidth[ chan ] != bandwidth || 0.0f == bandwidth ) { - _bandwidth[ chan ] = bandwidth; - return dev->set_bandwidth( bandwidth, dev_chan ); - } else { return _bandwidth[ chan ]; } - } - - return 0; + return _manager->set_bandwidth( bandwidth, chan ); } double sink_impl::get_bandwidth( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_bandwidth( dev_chan ); - - return 0; + return _manager->get_bandwidth( chan ); } osmosdr::freq_range_t sink_impl::get_bandwidth_range( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( sink_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_bandwidth_range( dev_chan ); - - return osmosdr::freq_range_t(); + return _manager->get_bandwidth_range( chan ); } -void sink_impl::set_time_source(const std::string &source, const size_t mboard) +void sink_impl::set_time_source( const std::string &source, const size_t mboard ) { - if (mboard != osmosdr::ALL_MBOARDS){ - _devs.at(mboard)->set_time_source( source ); - return; - } - - for (size_t m = 0; m < _devs.size(); m++){ /* propagate ALL_MBOARDS */ - _devs.at(m)->set_time_source( source, osmosdr::ALL_MBOARDS ); - } + return _manager->set_time_source( source, mboard ); } -std::string sink_impl::get_time_source(const size_t mboard) +std::string sink_impl::get_time_source( const size_t mboard ) { - return _devs.at(mboard)->get_time_source( mboard ); + return _manager->get_time_source( mboard ); } -std::vector sink_impl::get_time_sources(const size_t mboard) +std::vector sink_impl::get_time_sources( const size_t mboard ) { - return _devs.at(mboard)->get_time_sources( mboard ); + return _manager->get_time_sources( mboard ); } -void sink_impl::set_clock_source(const std::string &source, const size_t mboard) +void sink_impl::set_clock_source( const std::string &source, const size_t mboard ) { - if (mboard != osmosdr::ALL_MBOARDS){ - _devs.at(mboard)->set_clock_source( source ); - return; - } - - for (size_t m = 0; m < _devs.size(); m++){ /* propagate ALL_MBOARDS */ - _devs.at(m)->set_clock_source( source, osmosdr::ALL_MBOARDS ); - } + return _manager->set_clock_source( source, mboard ); } -std::string sink_impl::get_clock_source(const size_t mboard) +std::string sink_impl::get_clock_source( const size_t mboard ) { - return _devs.at(mboard)->get_clock_source( mboard ); + return _manager->get_clock_source( mboard ); } -std::vector sink_impl::get_clock_sources(const size_t mboard) +std::vector sink_impl::get_clock_sources( const size_t mboard ) { - return _devs.at(mboard)->get_clock_sources( mboard ); + return _manager->get_clock_sources( mboard ); } -double sink_impl::get_clock_rate(size_t mboard) +double sink_impl::get_clock_rate( size_t mboard ) { - return _devs.at(mboard)->get_clock_rate( mboard ); + return _manager->get_clock_rate( mboard ); } -void sink_impl::set_clock_rate(double rate, size_t mboard) +void sink_impl::set_clock_rate( double rate, size_t mboard ) { - if (mboard != osmosdr::ALL_MBOARDS){ - _devs.at(mboard)->set_clock_rate( rate ); - return; - } - - for (size_t m = 0; m < _devs.size(); m++){ /* propagate ALL_MBOARDS */ - _devs.at(m)->set_clock_rate( rate, osmosdr::ALL_MBOARDS ); - } + return _manager->set_clock_rate( rate, mboard ); } -osmosdr::time_spec_t sink_impl::get_time_now(size_t mboard) +osmosdr::time_spec_t sink_impl::get_time_now( size_t mboard ) { - return _devs.at(mboard)->get_time_now( mboard ); + return _manager->get_time_now( mboard ); } -osmosdr::time_spec_t sink_impl::get_time_last_pps(size_t mboard) +osmosdr::time_spec_t sink_impl::get_time_last_pps( size_t mboard ) { - return _devs.at(mboard)->get_time_last_pps( mboard ); + return _manager->get_time_last_pps( mboard ); } -void sink_impl::set_time_now(const osmosdr::time_spec_t &time_spec, size_t mboard) +void sink_impl::set_time_now( const osmosdr::time_spec_t &time_spec, size_t mboard ) { - if (mboard != osmosdr::ALL_MBOARDS){ - _devs.at(mboard)->set_time_now( time_spec ); - return; - } - - for (size_t m = 0; m < _devs.size(); m++){ /* propagate ALL_MBOARDS */ - _devs.at(m)->set_time_now( time_spec, osmosdr::ALL_MBOARDS ); - } + return _manager->set_time_now( time_spec, mboard ); } -void sink_impl::set_time_next_pps(const osmosdr::time_spec_t &time_spec) +void sink_impl::set_time_next_pps( const osmosdr::time_spec_t &time_spec ) { - BOOST_FOREACH( sink_iface *dev, _devs ) - { - dev->set_time_next_pps( time_spec ); - } + return _manager->set_time_next_pps( time_spec ); } -void sink_impl::set_time_unknown_pps(const osmosdr::time_spec_t &time_spec) +void sink_impl::set_time_unknown_pps( const osmosdr::time_spec_t &time_spec ) { - BOOST_FOREACH( sink_iface *dev, _devs ) - { - dev->set_time_unknown_pps( time_spec ); - } + return _manager->set_time_unknown_pps( time_spec ); } diff --git a/lib/sink_impl.h b/lib/sink_impl.h index 1642669..a4589b5 100644 --- a/lib/sink_impl.h +++ b/lib/sink_impl.h @@ -22,7 +22,7 @@ #include "osmosdr/sink.h" -#include "sink_iface.h" +#include "dev_manager.h" #include @@ -83,18 +83,7 @@ public: void set_time_unknown_pps(const ::osmosdr::time_spec_t &time_spec); private: - std::vector< sink_iface * > _devs; - - /* cache to prevent multiple device calls with the same value coming from grc */ - double _sample_rate; - std::map< size_t, double > _center_freq; - std::map< size_t, double > _freq_corr; - std::map< size_t, bool > _gain_mode; - std::map< size_t, double > _gain; - std::map< size_t, double > _if_gain; - std::map< size_t, double > _bb_gain; - std::map< size_t, std::string > _antenna; - std::map< size_t, double > _bandwidth; + dev_manager_sptr _manager; }; #endif /* INCLUDED_OSMOSDR_SINK_IMPL_H */ diff --git a/lib/source_iface.h b/lib/source_iface.h index abb70eb..ff0149f 100644 --- a/lib/source_iface.h +++ b/lib/source_iface.h @@ -25,20 +25,16 @@ #include #include +#include "common_iface.h" + /*! * TODO: document * */ -class source_iface +class source_iface : public common_iface { public: /*! - * Get the number of channels the underlying radio hardware offers. - * \return the number of available channels - */ - virtual size_t get_num_channels( void ) = 0; - - /*! * \brief seek file to \p seek_point relative to \p whence * * \param seek_point sample offset in file @@ -48,182 +44,6 @@ public: virtual bool seek( long seek_point, int whence, size_t chan = 0 ) { return false; } /*! - * Get the possible sample rates for the underlying radio hardware. - * \return a range of rates in Sps - */ - virtual osmosdr::meta_range_t get_sample_rates( void ) = 0; - - /*! - * Set the sample rate for the underlying radio hardware. - * This also will select the appropriate IF bandpass, if applicable. - * \param rate a new rate in Sps - */ - virtual double set_sample_rate( double rate ) = 0; - - /*! - * Get the sample rate for the underlying radio hardware. - * This is the actual sample rate and may differ from the rate set. - * \return the actual rate in Sps - */ - virtual double get_sample_rate( void ) = 0; - - /*! - * Get the tunable frequency range for the underlying radio hardware. - * \param chan the channel index 0 to N-1 - * \return the frequency range in Hz - */ - virtual osmosdr::freq_range_t get_freq_range( size_t chan = 0 ) = 0; - - /*! - * Tune the underlying radio hardware to the desired center frequency. - * This also will select the appropriate RF bandpass. - * \param freq the desired frequency in Hz - * \param chan the channel index 0 to N-1 - * \return the actual frequency in Hz - */ - virtual double set_center_freq( double freq, size_t chan = 0 ) = 0; - - /*! - * Get the center frequency the underlying radio hardware is tuned to. - * This is the actual frequency and may differ from the frequency set. - * \param chan the channel index 0 to N-1 - * \return the frequency in Hz - */ - virtual double get_center_freq( size_t chan = 0 ) = 0; - - /*! - * Set the frequency correction value in parts per million. - * \param ppm the desired correction value in parts per million - * \param chan the channel index 0 to N-1 - * \return correction value in parts per million - */ - virtual double set_freq_corr( double ppm, size_t chan = 0 ) = 0; - - /*! - * Get the frequency correction value. - * \param chan the channel index 0 to N-1 - * \return correction value in parts per million - */ - virtual double get_freq_corr( size_t chan = 0 ) = 0; - - /*! - * Get the gain stage names of the underlying radio hardware. - * \param chan the channel index 0 to N-1 - * \return a vector of strings containing the names of gain stages - */ - virtual std::vector get_gain_names( size_t chan = 0 ) = 0; - - /*! - * Get the settable overall gain range for the underlying radio hardware. - * \param chan the channel index 0 to N-1 - * \return the gain range in dB - */ - virtual osmosdr::gain_range_t get_gain_range( size_t chan = 0 ) = 0; - - /*! - * Get the settable gain range for a specific gain stage. - * \param name the name of the gain stage - * \param chan the channel index 0 to N-1 - * \return the gain range in dB - */ - virtual osmosdr::gain_range_t get_gain_range( const std::string & name, - size_t chan = 0 ) = 0; - - /*! - * Set the gain mode for the underlying radio hardware. - * This might be supported only for certain hardware types. - * \param automatic the gain mode (true means automatic gain mode) - * \param chan the channel index 0 to N-1 - * \return the actual gain mode - */ - virtual bool set_gain_mode( bool automatic, size_t chan = 0 ) { return false; } - - /*! - * Get the gain mode selected for the underlying radio hardware. - * \param chan the channel index 0 to N-1 - * \return the actual gain mode (true means automatic gain mode) - */ - virtual bool get_gain_mode( size_t chan = 0 ) { return false; } - - /*! - * Set the gain for the underlying radio hardware. - * This function will automatically distribute the desired gain value over - * available gain stages in an appropriate way and return the actual value. - * \param gain the gain in dB - * \param chan the channel index 0 to N-1 - * \return the actual gain in dB - */ - virtual double set_gain( double gain, size_t chan = 0 ) = 0; - - /*! - * Set the named gain on the underlying radio hardware. - * \param gain the gain in dB - * \param name the name of the gain stage - * \param chan the channel index 0 to N-1 - * \return the actual gain in dB - */ - virtual double set_gain( double gain, - const std::string & name, - size_t chan = 0 ) = 0; - - /*! - * Get the actual gain setting of the underlying radio hardware. - * \param chan the channel index 0 to N-1 - * \return the actual gain in dB - */ - virtual double get_gain( size_t chan = 0 ) = 0; - - /*! - * Get the actual gain setting of a named stage. - * \param name the name of the gain stage - * \param chan the channel index 0 to N-1 - * \return the actual gain in dB - */ - virtual double get_gain( const std::string & name, size_t chan = 0 ) = 0; - - /*! - * Set the IF gain for the underlying radio hardware. - * This function will automatically distribute the desired gain value over - * available IF gain stages in an appropriate way and return the actual value. - * \param gain the gain in dB - * \param chan the channel index 0 to N-1 - * \return the actual gain in dB - */ - virtual double set_if_gain( double gain, size_t chan = 0 ) { return 0; } - - /*! - * Set the BB gain for the underlying radio hardware. - * This function will automatically distribute the desired gain value over - * available BB gain stages in an appropriate way and return the actual value. - * \param gain the gain in dB - * \param chan the channel index 0 to N-1 - * \return the actual gain in dB - */ - virtual double set_bb_gain( double gain, size_t chan = 0 ) { return 0; } - - /*! - * Get the available antennas of the underlying radio hardware. - * \param chan the channel index 0 to N-1 - * \return a vector of strings containing the names of available antennas - */ - virtual std::vector< std::string > get_antennas( size_t chan = 0 ) = 0; - - /*! - * Select the active antenna of the underlying radio hardware. - * \param chan the channel index 0 to N-1 - * \return the actual antenna's name - */ - virtual std::string set_antenna( const std::string & antenna, - size_t chan = 0 ) = 0; - - /*! - * Get the actual underlying radio hardware antenna setting. - * \param chan the channel index 0 to N-1 - * \return the actual antenna's name - */ - virtual std::string get_antenna( size_t chan = 0 ) = 0; - - /*! * Set the RX frontend DC correction mode. * The automatic correction subtracts out the long-run average. * @@ -238,163 +58,12 @@ public: virtual void set_dc_offset_mode( int mode, size_t chan = 0 ) { } /*! - * Set a constant DC offset value. - * The value is complex to control both I and Q. - * Only set this when automatic correction is disabled. - * - * \param offset the dc offset (1.0 is full-scale) - * \param chan the channel index 0 to N-1 - */ - virtual void set_dc_offset( const std::complex &offset, size_t chan = 0 ) { } - - /*! * Set the RX frontend IQ balance mode. * * \param mode iq balance correction mode: 0 = Off, 1 = Manual, 2 = Automatic * \param chan the channel index 0 to N-1 */ virtual void set_iq_balance_mode( int mode, size_t chan = 0 ) { } - - /*! - * Set the RX frontend IQ balance correction. - * Use this to adjust the magnitude and phase of I and Q. - * - * \param balance the complex correction value - * \param chan the channel index 0 to N-1 - */ - virtual void set_iq_balance( const std::complex &balance, size_t chan = 0 ) { } - - /*! - * Set the bandpass filter on the radio frontend. - * \param bandwidth the filter bandwidth in Hz, set to 0 for automatic selection - * \param chan the channel index 0 to N-1 - * \return the actual filter bandwidth in Hz - */ - virtual double set_bandwidth( double bandwidth, size_t chan = 0 ) { return 0; } - - /*! - * Get the actual bandpass filter setting on the radio frontend. - * \param chan the channel index 0 to N-1 - * \return the actual filter bandwidth in Hz - */ - virtual double get_bandwidth( size_t chan = 0 ) { return 0; } - - /*! - * Get the possible bandpass filter settings on the radio frontend. - * \param chan the channel index 0 to N-1 - * \return a range of bandwidths in Hz - */ - virtual osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0 ) - { return osmosdr::freq_range_t(); } - - /*! - * Set the time source for the device. - * This sets the method of time synchronization, - * typically a pulse per second or an encoded time. - * Typical options for source: external, MIMO. - * \param source a string representing the time source - * \param mboard which motherboard to set the config - */ - virtual void set_time_source(const std::string &source, - const size_t mboard = 0) { } - - /*! - * Get the currently set time source. - * \param mboard which motherboard to get the config - * \return the string representing the time source - */ - virtual std::string get_time_source(const size_t mboard) { return ""; } - - /*! - * Get a list of possible time sources. - * \param mboard which motherboard to get the list - * \return a vector of strings for possible settings - */ - virtual std::vector get_time_sources(const size_t mboard) - { - return std::vector(); - } - - /*! - * Set the clock source for the device. - * This sets the source for a 10 Mhz reference clock. - * Typical options for source: internal, external, MIMO. - * \param source a string representing the clock source - * \param mboard which motherboard to set the config - */ - virtual void set_clock_source(const std::string &source, - const size_t mboard = 0) { } - - /*! - * Get the currently set clock source. - * \param mboard which motherboard to get the config - * \return the string representing the clock source - */ - virtual std::string get_clock_source(const size_t mboard) { return ""; } - - /*! - * Get a list of possible clock sources. - * \param mboard which motherboard to get the list - * \return a vector of strings for possible settings - */ - virtual std::vector get_clock_sources(const size_t mboard) - { - return std::vector(); - } - - /*! - * Get the master clock rate. - * \param mboard the motherboard index 0 to M-1 - * \return the clock rate in Hz - */ - virtual double get_clock_rate(size_t mboard = 0) { return 0; } - - /*! - * Set the master clock rate. - * \param rate the new rate in Hz - * \param mboard the motherboard index 0 to M-1 - */ - virtual void set_clock_rate(double rate, size_t mboard = 0) { } - - /*! - * Get the current time registers. - * \param mboard the motherboard index 0 to M-1 - * \return the current device time - */ - virtual ::osmosdr::time_spec_t get_time_now(size_t mboard = 0) - { - return ::osmosdr::time_spec_t::get_system_time(); - } - - /*! - * Get the time when the last pps pulse occured. - * \param mboard the motherboard index 0 to M-1 - * \return the current device time - */ - virtual ::osmosdr::time_spec_t get_time_last_pps(size_t mboard = 0) - { - return ::osmosdr::time_spec_t::get_system_time(); - } - - /*! - * Sets the time registers immediately. - * \param time_spec the new time - * \param mboard the motherboard index 0 to M-1 - */ - virtual void set_time_now(const ::osmosdr::time_spec_t &time_spec, - size_t mboard = 0) { } - - /*! - * Set the time registers at the next pps. - * \param time_spec the new time - */ - virtual void set_time_next_pps(const ::osmosdr::time_spec_t &time_spec) { } - - /*! - * Sync the time registers with an unknown pps edge. - * \param time_spec the new time - */ - virtual void set_time_unknown_pps(const ::osmosdr::time_spec_t &time_spec) { } }; #endif // OSMOSDR_SOURCE_IFACE_H diff --git a/lib/source_impl.cc b/lib/source_impl.cc index a8a3cec..f30e773 100644 --- a/lib/source_impl.cc +++ b/lib/source_impl.cc @@ -112,11 +112,14 @@ source_impl::source_impl( const std::string &args ) : gr::hier_block2 ("source_impl", gr::io_signature::make(0, 0, 0), args_to_io_signature(args)), - _sample_rate(NAN) + _manager(make_dev_manager(to_hier_block2())) { size_t channel = 0; bool device_specified = false; + message_port_register_hier_in( osmosdr::CMD_PORT ); + msg_connect( self(), osmosdr::CMD_PORT, _manager, osmosdr::CMD_PORT ); + std::vector< std::string > arg_list = args_to_vector(args); std::vector< std::string > dev_types; @@ -376,576 +379,229 @@ source_impl::source_impl( const std::string &args ) } #endif - if ( iface != NULL && long(block.get()) != 0 ) { - _devs.push_back( iface ); - - for (size_t i = 0; i < iface->get_num_channels(); i++) { -#ifdef HAVE_IQBALANCE - gr::iqbalance::optimize_c::sptr iq_opt = gr::iqbalance::optimize_c::make( 0 ); - gr::iqbalance::fix_cc::sptr iq_fix = gr::iqbalance::fix_cc::make(); - - connect(block, i, iq_fix, 0); - connect(iq_fix, 0, self(), channel++); - - connect(block, i, iq_opt, 0); - msg_connect(iq_opt, "iqbal_corr", iq_fix, "iqbal_corr"); - - _iq_opt.push_back( iq_opt.get() ); - _iq_fix.push_back( iq_fix.get() ); -#else - connect(block, i, self(), channel++); -#endif - } - } else if ( (iface != NULL) || (long(block.get()) != 0) ) - throw std::runtime_error("Either iface or block are NULL."); - + _manager->add_device( block, iface ); } - if (!_devs.size()) + if (!_manager->get_num_mboards()) throw std::runtime_error("No devices specified via device arguments."); } size_t source_impl::get_num_channels() { - size_t channels = 0; - - BOOST_FOREACH( source_iface *dev, _devs ) - channels += dev->get_num_channels(); - - return channels; + return _manager->get_num_channels(); } bool source_impl::seek( long seek_point, int whence, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->seek( seek_point, whence, dev_chan ); - - return false; + return _manager->seek( seek_point, whence, chan ); } -#define NO_DEVICES_MSG "FATAL: No device(s) available to work with." - osmosdr::meta_range_t source_impl::get_sample_rates() { - if ( ! _devs.empty() ) - return _devs[0]->get_sample_rates(); // assume same devices used in the group -#if 0 - else - throw std::runtime_error(NO_DEVICES_MSG); -#endif - return osmosdr::meta_range_t();; + return _manager->get_sample_rates(); } -double source_impl::set_sample_rate(double rate) +double source_impl::set_sample_rate( double rate ) { - double sample_rate = 0; - - if (_sample_rate != rate) { -#if 0 - if (_devs.empty()) - throw std::runtime_error(NO_DEVICES_MSG); -#endif - BOOST_FOREACH( source_iface *dev, _devs ) - sample_rate = dev->set_sample_rate(rate); - -#ifdef HAVE_IQBALANCE - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) { - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) { - if ( channel < _iq_opt.size() ) { - gr::iqbalance::optimize_c *opt = _iq_opt[channel]; - - if ( opt->period() > 0 ) { /* optimize is enabled */ - opt->set_period( dev->get_sample_rate() / 5 ); - opt->reset(); - } - } - - channel++; - } - } -#endif - - _sample_rate = sample_rate; - } - - return sample_rate; + return _manager->set_sample_rate(rate); } double source_impl::get_sample_rate() { - double sample_rate = 0; - - if (!_devs.empty()) - sample_rate = _devs[0]->get_sample_rate(); // assume same devices used in the group -#if 0 - else - throw std::runtime_error(NO_DEVICES_MSG); -#endif - return sample_rate; + return _manager->get_sample_rate(); } osmosdr::freq_range_t source_impl::get_freq_range( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_freq_range( dev_chan ); - - return osmosdr::freq_range_t(); + return _manager->get_freq_range( chan ); } double source_impl::set_center_freq( double freq, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) { - if ( _center_freq[ chan ] != freq ) { - _center_freq[ chan ] = freq; - return dev->set_center_freq( freq, dev_chan ); - } else { return _center_freq[ chan ]; } - } - - return 0; + return _manager->set_center_freq( freq, chan ); } double source_impl::get_center_freq( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_center_freq( dev_chan ); - - return 0; + return _manager->get_center_freq( chan ); } double source_impl::set_freq_corr( double ppm, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) { - if ( _freq_corr[ chan ] != ppm ) { - _freq_corr[ chan ] = ppm; - return dev->set_freq_corr( ppm, dev_chan ); - } else { return _freq_corr[ chan ]; } - } - - return 0; + return _manager->set_freq_corr( ppm, chan ); } double source_impl::get_freq_corr( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_freq_corr( dev_chan ); - - return 0; + return _manager->get_freq_corr( chan ); } std::vector source_impl::get_gain_names( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_gain_names( dev_chan ); - - return std::vector< std::string >(); + return _manager->get_gain_names( chan ); } osmosdr::gain_range_t source_impl::get_gain_range( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_gain_range( dev_chan ); - - return osmosdr::gain_range_t(); + return _manager->get_gain_range( chan ); } osmosdr::gain_range_t source_impl::get_gain_range( const std::string & name, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_gain_range( name, dev_chan ); - - return osmosdr::gain_range_t(); + return _manager->get_gain_range( name, chan ); } bool source_impl::set_gain_mode( bool automatic, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) { - if ( _gain_mode[ chan ] != automatic ) { - _gain_mode[ chan ] = automatic; - bool mode = dev->set_gain_mode( automatic, dev_chan ); - if (!automatic) // reapply gain value when switched to manual mode - dev->set_gain( _gain[ chan ], dev_chan ); - return mode; - } else { return _gain_mode[ chan ]; } - } - - return false; + return _manager->set_gain_mode( automatic, chan ); } bool source_impl::get_gain_mode( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_gain_mode( dev_chan ); - - return false; + return _manager->get_gain_mode( chan ); } double source_impl::set_gain( double gain, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) { - if ( _gain[ chan ] != gain ) { - _gain[ chan ] = gain; - return dev->set_gain( gain, dev_chan ); - } else { return _gain[ chan ]; } - } - - return 0; + return _manager->set_gain( gain, chan ); } double source_impl::set_gain( double gain, const std::string & name, size_t chan) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->set_gain( gain, name, dev_chan ); - - return 0; + return _manager->set_gain( gain, name, chan ); } double source_impl::get_gain( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_gain( dev_chan ); - - return 0; + return _manager->get_gain( chan ); } double source_impl::get_gain( const std::string & name, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_gain( name, dev_chan ); - - return 0; + return _manager->get_gain( name, chan ); } double source_impl::set_if_gain( double gain, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) { - if ( _if_gain[ chan ] != gain ) { - _if_gain[ chan ] = gain; - return dev->set_if_gain( gain, dev_chan ); - } else { return _if_gain[ chan ]; } - } - - return 0; + return _manager->set_if_gain( gain, chan ); } double source_impl::set_bb_gain( double gain, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) { - if ( _bb_gain[ chan ] != gain ) { - _bb_gain[ chan ] = gain; - return dev->set_bb_gain( gain, dev_chan ); - } else { return _bb_gain[ chan ]; } - } - - return 0; + return _manager->set_bb_gain( gain, chan ); } std::vector< std::string > source_impl::get_antennas( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_antennas( dev_chan ); - - return std::vector< std::string >(); + return _manager->get_antennas( chan ); } std::string source_impl::set_antenna( const std::string & antenna, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) { - if ( _antenna[ chan ] != antenna ) { - _antenna[ chan ] = antenna; - return dev->set_antenna( antenna, dev_chan ); - } else { return _antenna[ chan ]; } - } - - return ""; + return _manager->set_antenna( antenna, chan ); } std::string source_impl::get_antenna( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_antenna( dev_chan ); - - return ""; + return _manager->get_antenna( chan ); } void source_impl::set_dc_offset_mode( int mode, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - dev->set_dc_offset_mode( mode, dev_chan ); + _manager->set_dc_offset_mode( mode, chan ); } void source_impl::set_dc_offset( const std::complex &offset, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - dev->set_dc_offset( offset, dev_chan ); + _manager->set_dc_offset( offset, chan ); } void source_impl::set_iq_balance_mode( int mode, size_t chan ) { - size_t channel = 0; -#ifdef HAVE_IQBALANCE - BOOST_FOREACH( source_iface *dev, _devs ) { - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) { - if ( chan == channel++ ) { - if ( chan < _iq_opt.size() && chan < _iq_fix.size() ) { - gr::iqbalance::optimize_c *opt = _iq_opt[chan]; - gr::iqbalance::fix_cc *fix = _iq_fix[chan]; - - if ( IQBalanceOff == mode ) { - opt->set_period( 0 ); - /* store current values in order to be able to restore them later */ - _vals[ chan ] = std::pair< float, float >( fix->mag(), fix->phase() ); - fix->set_mag( 0.0f ); - fix->set_phase( 0.0f ); - } else if ( IQBalanceManual == mode ) { - if ( opt->period() == 0 ) { /* transition from Off to Manual */ - /* restore previous values */ - std::pair< float, float > val = _vals[ chan ]; - fix->set_mag( val.first ); - fix->set_phase( val.second ); - } - opt->set_period( 0 ); - } else if ( IQBalanceAutomatic == mode ) { - opt->set_period( dev->get_sample_rate() / 5 ); - opt->reset(); - } - } - } - } - } -#else - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->set_iq_balance_mode( mode, dev_chan ); -#endif + _manager->set_iq_balance_mode( mode, chan ); } void source_impl::set_iq_balance( const std::complex &balance, size_t chan ) { - size_t channel = 0; -#ifdef HAVE_IQBALANCE - BOOST_FOREACH( source_iface *dev, _devs ) { - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) { - if ( chan == channel++ ) { - if ( chan < _iq_opt.size() && chan < _iq_fix.size() ) { - gr::iqbalance::optimize_c *opt = _iq_opt[chan]; - gr::iqbalance::fix_cc *fix = _iq_fix[chan]; - - if ( opt->period() == 0 ) { /* automatic optimization desabled */ - fix->set_mag( balance.real() ); - fix->set_phase( balance.imag() ); - } - } - } - } - } -#else - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->set_iq_balance( balance, dev_chan ); -#endif + _manager->set_iq_balance( balance, chan ); } double source_impl::set_bandwidth( double bandwidth, size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) { - if ( _bandwidth[ chan ] != bandwidth || 0.0f == bandwidth ) { - _bandwidth[ chan ] = bandwidth; - return dev->set_bandwidth( bandwidth, dev_chan ); - } else { return _bandwidth[ chan ]; } - } - - return 0; + return _manager->set_bandwidth( bandwidth, chan ); } double source_impl::get_bandwidth( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_bandwidth( dev_chan ); - - return 0; + return _manager->get_bandwidth( chan ); } osmosdr::freq_range_t source_impl::get_bandwidth_range( size_t chan ) { - size_t channel = 0; - BOOST_FOREACH( source_iface *dev, _devs ) - for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) - if ( chan == channel++ ) - return dev->get_bandwidth_range( dev_chan ); - - return osmosdr::freq_range_t(); + return _manager->get_bandwidth_range( chan ); } void source_impl::set_time_source(const std::string &source, const size_t mboard) { - if (mboard != osmosdr::ALL_MBOARDS){ - _devs.at(mboard)->set_time_source( source ); - return; - } - - for (size_t m = 0; m < _devs.size(); m++){ /* propagate ALL_MBOARDS */ - _devs.at(m)->set_time_source( source, osmosdr::ALL_MBOARDS ); - } + return _manager->set_time_source( source, mboard ); } std::string source_impl::get_time_source(const size_t mboard) { - return _devs.at(mboard)->get_time_source( mboard ); + return _manager->get_time_source( mboard ); } std::vector source_impl::get_time_sources(const size_t mboard) { - return _devs.at(mboard)->get_time_sources( mboard ); + return _manager->get_time_sources( mboard ); } void source_impl::set_clock_source(const std::string &source, const size_t mboard) { - if (mboard != osmosdr::ALL_MBOARDS){ - _devs.at(mboard)->set_clock_source( source ); - return; - } - - for (size_t m = 0; m < _devs.size(); m++){ /* propagate ALL_MBOARDS */ - _devs.at(m)->set_clock_source( source, osmosdr::ALL_MBOARDS ); - } + return _manager->set_clock_source( source, mboard ); } std::string source_impl::get_clock_source(const size_t mboard) { - return _devs.at(mboard)->get_clock_source( mboard ); + return _manager->get_clock_source( mboard ); } std::vector source_impl::get_clock_sources(const size_t mboard) { - return _devs.at(mboard)->get_clock_sources( mboard ); + return _manager->get_clock_sources( mboard ); } double source_impl::get_clock_rate(size_t mboard) { - return _devs.at(mboard)->get_clock_rate( mboard ); + return _manager->get_clock_rate( mboard ); } void source_impl::set_clock_rate(double rate, size_t mboard) { - if (mboard != osmosdr::ALL_MBOARDS){ - _devs.at(mboard)->set_clock_rate( rate ); - return; - } - - for (size_t m = 0; m < _devs.size(); m++){ /* propagate ALL_MBOARDS */ - _devs.at(m)->set_clock_rate( rate, osmosdr::ALL_MBOARDS ); - } + return _manager->set_clock_rate( rate, mboard ); } osmosdr::time_spec_t source_impl::get_time_now(size_t mboard) { - return _devs.at(mboard)->get_time_now( mboard ); + return _manager->get_time_now( mboard ); } osmosdr::time_spec_t source_impl::get_time_last_pps(size_t mboard) { - return _devs.at(mboard)->get_time_last_pps( mboard ); + return _manager->get_time_last_pps( mboard ); } void source_impl::set_time_now(const osmosdr::time_spec_t &time_spec, size_t mboard) { - if (mboard != osmosdr::ALL_MBOARDS){ - _devs.at(mboard)->set_time_now( time_spec ); - return; - } - - for (size_t m = 0; m < _devs.size(); m++){ /* propagate ALL_MBOARDS */ - _devs.at(m)->set_time_now( time_spec, osmosdr::ALL_MBOARDS ); - } + return _manager->set_time_now( time_spec, mboard ); } void source_impl::set_time_next_pps(const osmosdr::time_spec_t &time_spec) { - BOOST_FOREACH( source_iface *dev, _devs ) - { - dev->set_time_next_pps( time_spec ); - } + return _manager->set_time_next_pps( time_spec ); } void source_impl::set_time_unknown_pps(const osmosdr::time_spec_t &time_spec) { - BOOST_FOREACH( source_iface *dev, _devs ) - { - dev->set_time_unknown_pps( time_spec ); - } + return _manager->set_time_unknown_pps( time_spec ); } diff --git a/lib/source_impl.h b/lib/source_impl.h index 4b65125..238bc6c 100644 --- a/lib/source_impl.h +++ b/lib/source_impl.h @@ -27,7 +27,7 @@ #include #endif -#include +#include "dev_manager.h" #include @@ -92,23 +92,7 @@ public: void set_time_unknown_pps(const ::osmosdr::time_spec_t &time_spec); private: - std::vector< source_iface * > _devs; - - /* cache to prevent multiple device calls with the same value coming from grc */ - double _sample_rate; - std::map< size_t, double > _center_freq; - std::map< size_t, double > _freq_corr; - std::map< size_t, bool > _gain_mode; - std::map< size_t, double > _gain; - std::map< size_t, double > _if_gain; - std::map< size_t, double > _bb_gain; - std::map< size_t, std::string > _antenna; -#ifdef HAVE_IQBALANCE - std::vector< gr::iqbalance::fix_cc * > _iq_fix; - std::vector< gr::iqbalance::optimize_c * > _iq_opt; - std::map< size_t, std::pair > _vals; -#endif - std::map< size_t, double > _bandwidth; + dev_manager_sptr _manager; }; #endif /* INCLUDED_OSMOSDR_SOURCE_IMPL_H */ diff --git a/lib/uhd/uhd_sink_c.cc b/lib/uhd/uhd_sink_c.cc index a154556..fdac2a6 100644 --- a/lib/uhd/uhd_sink_c.cc +++ b/lib/uhd/uhd_sink_c.cc @@ -24,6 +24,8 @@ //#include +#include + #include "arg_helpers.h" #include "uhd_sink_c.h" @@ -124,6 +126,9 @@ uhd_sink_c::uhd_sink_c(const std::string &args) : #endif for ( size_t i = 0; i < nchan; i++ ) connect( self(), i, _snk, i ); + + message_port_register_hier_in( osmosdr::CMD_PORT ); + msg_connect( self(), osmosdr::CMD_PORT, _snk, osmosdr::CMD_PORT ); } uhd_sink_c::~uhd_sink_c() diff --git a/lib/uhd/uhd_source_c.cc b/lib/uhd/uhd_source_c.cc index fc13017..98fd5f1 100644 --- a/lib/uhd/uhd_source_c.cc +++ b/lib/uhd/uhd_source_c.cc @@ -24,6 +24,8 @@ //#include +#include + #include "arg_helpers.h" #include "uhd_source_c.h" @@ -125,6 +127,9 @@ uhd_source_c::uhd_source_c(const std::string &args) : #endif for ( size_t i = 0; i < nchan; i++ ) connect( _src, i, self(), i ); + + message_port_register_hier_in( osmosdr::CMD_PORT ); + msg_connect( self(), osmosdr::CMD_PORT, _src, osmosdr::CMD_PORT ); } uhd_source_c::~uhd_source_c() -- 2.11.0 From 0xloem at gmail.com Sun Sep 23 12:32:19 2018 From: 0xloem at gmail.com (Baffo32) Date: Sun, 23 Sep 2018 12:32:19 +0000 Subject: [PATCH] gr-osmosdr/rtl: REPOST overflow fixes and draining Message-ID: <20180923123219.25250-1-baffo32@gmail.com> From: Karl Semich <0xloem at gmail.com> Previously overflow was handled by outputting "O" on stderr and placing the extra data at the start of the buffer queue. This had a number of issues: - as only 1 buffer advanced on overflow, many overflows could occur in sequence, as only small amounts of data was allowed to drain - if the work function was partway through reading the head of the queue, it would continue reading at the same offset from the new head rather than from the start, causing extra loss of data - buffer processing in work function is not guarded with a lock, so it was possible for overflow to overwrite data while it was being copied, resulting in garbage passed on This patch rewrites overflow handling such that streaming is paused until the work function can catch up. This resolves all 3 issues. --- lib/rtl/rtl_source_c.cc | 32 ++++++++++++++++++++++++++------ lib/rtl/rtl_source_c.h | 2 ++ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/lib/rtl/rtl_source_c.cc b/lib/rtl/rtl_source_c.cc index a371464..0d26831 100644 --- a/lib/rtl/rtl_source_c.cc +++ b/lib/rtl/rtl_source_c.cc @@ -48,6 +48,7 @@ using namespace boost::assign; #define BUF_LEN (16 * 32 * 512) /* must be multiple of 512 */ #define BUF_NUM 15 #define BUF_SKIP 1 // buffers to skip due to initial garbage +#define BUF_MIN 3 /* minimum buffers to run work function */ #define BYTES_PER_SAMPLE 2 // rtl device delivers 8 bit unsigned IQ data @@ -87,8 +88,7 @@ rtl_source_c::rtl_source_c (const std::string &args) _running(false), _no_tuner(false), _auto_gain(false), - _if_gain(0), - _skipped(0) + _if_gain(0) { int ret; int index; @@ -297,6 +297,9 @@ void rtl_source_c::rtlsdr_callback(unsigned char *buf, uint32_t len) return; } + if (_overflow) + return; + { boost::mutex::scoped_lock lock( _buf_mutex ); @@ -304,8 +307,9 @@ void rtl_source_c::rtlsdr_callback(unsigned char *buf, uint32_t len) memcpy(_buf[buf_tail], buf, len); if (_buf_used == _buf_num) { - std::cerr << "O" << std::flush; - _buf_head = (_buf_head + 1) % _buf_num; + std::cerr << "OVERFLOW: rtl-sdr stream restarting after draining unread buffers" << std::endl; + _overflow = true; + rtlsdr_cancel_async( _dev ); } else { _buf_used++; } @@ -321,7 +325,20 @@ void rtl_source_c::_rtlsdr_wait(rtl_source_c *obj) void rtl_source_c::rtlsdr_wait() { - int ret = rtlsdr_read_async( _dev, _rtlsdr_callback, (void *)this, _buf_num, _buf_len ); + int ret; + + do { + { + boost::mutex::scoped_lock lock( _buf_mutex ); + // let unread buffers from last run drain + while ( _buf_used >= BUF_MIN ) + _work_cond.wait( lock ); + } + + _overflow = false; + _skipped = 0; + ret = rtlsdr_read_async( _dev, _rtlsdr_callback, (void *)this, _buf_num, _buf_len ); + } while ( _overflow ); _running = false; @@ -340,7 +357,7 @@ int rtl_source_c::work( int noutput_items, { boost::mutex::scoped_lock lock( _buf_mutex ); - while (_buf_used < 3 && _running) // collect at least 3 buffers + while (_buf_used < BUF_MIN && _running) // collect at least BUF_MIN buffers _buf_cond.wait( lock ); } @@ -364,6 +381,9 @@ int rtl_source_c::work( int noutput_items, _buf_head = (_buf_head + 1) % _buf_num; _buf_used--; } + + _work_cond.notify_one(); + _samp_avail = _buf_len / BYTES_PER_SAMPLE; _buf_offset = 0; } else { diff --git a/lib/rtl/rtl_source_c.h b/lib/rtl/rtl_source_c.h index 902b386..aafea5f 100644 --- a/lib/rtl/rtl_source_c.h +++ b/lib/rtl/rtl_source_c.h @@ -133,6 +133,8 @@ private: unsigned int _buf_used; boost::mutex _buf_mutex; boost::condition_variable _buf_cond; + boost::condition_variable _work_cond; + bool _overflow; bool _running; unsigned int _buf_offset; -- 2.11.0 From 0xloem at gmail.com Sun Sep 23 12:38:37 2018 From: 0xloem at gmail.com (Karl Semich) Date: Sun, 23 Sep 2018 12:38:37 +0000 Subject: [PATCH] gr-osmosdr/rtl: bugfix: respect buffer len in cb Message-ID: <20180923123837.26620-1-0xloem@gmail.com> Callback now stores and makes use of rtlsdr data length, preventing corruption in the case of short reads. --- lib/rtl/rtl_source_c.cc | 13 +++++++------ lib/rtl/rtl_source_c.h | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/rtl/rtl_source_c.cc b/lib/rtl/rtl_source_c.cc index a371464..215500e 100644 --- a/lib/rtl/rtl_source_c.cc +++ b/lib/rtl/rtl_source_c.cc @@ -173,8 +173,6 @@ rtl_source_c::rtl_source_c (const std::string &args) << std::endl; } - _samp_avail = _buf_len / BYTES_PER_SAMPLE; - // create a lookup table for gr_complex values for (unsigned int i = 0; i < 0x100; i++) _lut.push_back((i - 127.4f) / 128.0f); @@ -232,6 +230,7 @@ rtl_source_c::rtl_source_c (const std::string &args) set_if_gain( 24 ); /* preset to a reasonable default (non-GRC use case) */ _buf = (unsigned char **)malloc(_buf_num * sizeof(unsigned char *)); + _samp_avails = (int *)malloc(_buf_num * sizeof(uint32_t)); if (_buf) { for(unsigned int i = 0; i < _buf_num; ++i) @@ -261,6 +260,8 @@ rtl_source_c::~rtl_source_c () free(_buf[i]); } + free(_samp_avails); + _samp_avails = NULL; free(_buf); _buf = NULL; } @@ -302,6 +303,7 @@ void rtl_source_c::rtlsdr_callback(unsigned char *buf, uint32_t len) int buf_tail = (_buf_head + _buf_used) % _buf_num; memcpy(_buf[buf_tail], buf, len); + _samp_avails[buf_tail] = len / BYTES_PER_SAMPLE; if (_buf_used == _buf_num) { std::cerr << "O" << std::flush; @@ -348,23 +350,22 @@ int rtl_source_c::work( int noutput_items, return WORK_DONE; while (noutput_items && _buf_used) { - const int nout = std::min(noutput_items, _samp_avail); + const int nout = std::min(noutput_items, _samp_avails[_buf_head]); const unsigned char *buf = _buf[_buf_head] + _buf_offset * 2; for (int i = 0; i < nout; ++i) *out++ = gr_complex(_lut[buf[i * 2]], _lut[buf[i * 2 + 1]]); noutput_items -= nout; - _samp_avail -= nout; + _samp_avails[_buf_head] -= nout; - if (!_samp_avail) { + if (!_samp_avails[_buf_head]) { { boost::mutex::scoped_lock lock( _buf_mutex ); _buf_head = (_buf_head + 1) % _buf_num; _buf_used--; } - _samp_avail = _buf_len / BYTES_PER_SAMPLE; _buf_offset = 0; } else { _buf_offset += nout; diff --git a/lib/rtl/rtl_source_c.h b/lib/rtl/rtl_source_c.h index 902b386..8e537a5 100644 --- a/lib/rtl/rtl_source_c.h +++ b/lib/rtl/rtl_source_c.h @@ -127,6 +127,7 @@ private: rtlsdr_dev_t *_dev; gr::thread::thread _thread; unsigned char **_buf; + int *_samp_avails; unsigned int _buf_num; unsigned int _buf_len; unsigned int _buf_head; @@ -136,7 +137,6 @@ private: bool _running; unsigned int _buf_offset; - int _samp_avail; bool _no_tuner; bool _auto_gain; -- 2.11.0 From 0xloem at gmail.com Sun Sep 23 12:41:45 2018 From: 0xloem at gmail.com (Karl Semich) Date: Sun, 23 Sep 2018 12:41:45 +0000 Subject: [PATCH] gr-osmosdr/rtl: small fix to send final samples Message-ID: <20180923124145.27080-1-0xloem@gmail.com> Small change to work function to finish draining buffers before closing when device disappears. Provides a little more data when device is unexpectedly lost. --- lib/rtl/rtl_source_c.cc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/rtl/rtl_source_c.cc b/lib/rtl/rtl_source_c.cc index a371464..c0ba0b3 100644 --- a/lib/rtl/rtl_source_c.cc +++ b/lib/rtl/rtl_source_c.cc @@ -340,13 +340,18 @@ int rtl_source_c::work( int noutput_items, { boost::mutex::scoped_lock lock( _buf_mutex ); - while (_buf_used < 3 && _running) // collect at least 3 buffers + while (_buf_used < 3) // collect at least 3 buffers + { + if (!_running) { + // finish when remaining samples are drained + if (!_buf_used) + return WORK_DONE; + break; + } _buf_cond.wait( lock ); + } } - if (!_running) - return WORK_DONE; - while (noutput_items && _buf_used) { const int nout = std::min(noutput_items, _samp_avail); const unsigned char *buf = _buf[_buf_head] + _buf_offset * 2; -- 2.11.0 From 0xloem at gmail.com Sun Sep 23 12:43:18 2018 From: 0xloem at gmail.com (Karl Semich) Date: Sun, 23 Sep 2018 12:43:18 +0000 Subject: [PATCH] gr-osmosdr/rtl: two concurrency bugfixes Message-ID: <20180923124318.27312-1-0xloem@gmail.com> 1. _running is guarded with a lock in the rtl event loop epilogue. this prevents a possible hang that could arise from _buf_cond being notified prior to work() waiting on _buf_cond, but after work() checks _running. 2. _buf_used is guarded with a lock in the work function to prevent it from spuriously reading zero if it is nonatomically written from the rtl event callback. --- lib/rtl/rtl_source_c.cc | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/rtl/rtl_source_c.cc b/lib/rtl/rtl_source_c.cc index a371464..bbd0140 100644 --- a/lib/rtl/rtl_source_c.cc +++ b/lib/rtl/rtl_source_c.cc @@ -323,7 +323,11 @@ void rtl_source_c::rtlsdr_wait() { int ret = rtlsdr_read_async( _dev, _rtlsdr_callback, (void *)this, _buf_num, _buf_len ); - _running = false; + { + boost::mutex::scoped_lock lock( _buf_mutex ); + + _running = false; + } if ( ret != 0 ) std::cerr << "rtlsdr_read_async returned with " << ret << std::endl; @@ -347,7 +351,7 @@ int rtl_source_c::work( int noutput_items, if (!_running) return WORK_DONE; - while (noutput_items && _buf_used) { + while (noutput_items) { const int nout = std::min(noutput_items, _samp_avail); const unsigned char *buf = _buf[_buf_head] + _buf_offset * 2; @@ -363,6 +367,9 @@ int rtl_source_c::work( int noutput_items, _buf_head = (_buf_head + 1) % _buf_num; _buf_used--; + + if (_buf_used == 0) + break; } _samp_avail = _buf_len / BYTES_PER_SAMPLE; _buf_offset = 0; -- 2.11.0 From gmkarl at gmail.com Wed Sep 19 20:16:52 2018 From: gmkarl at gmail.com (Karl) Date: Wed, 19 Sep 2018 20:16:52 -0000 Subject: [PATCH] Add support for message commands Message-ID: Hi, I've modified gr-osmosdr such that all devices respond to message commands ala USRP. I feel this is a really valuable addition, and I am happy to continue to work with it to meet any needs or desires. Please let me know that this e-mail was received. Thanks so much, Karl -------------- next part -------------- A non-text attachment was scrubbed... Name: 0001-add-support-for-message-commands-to-all-devices.patch Type: application/octet-stream Size: 112833 bytes Desc: not available URL: