Thanks, Dimitri. This helps a lot.
I tried to make some suggested additional comments to the header file,
which I have attached. You can take them or leave them. I think perhaps for
a sophisticated c programmer the API is obvious, but for me I needed a bit
more help.
I have also written a small program that uses the async API to serve as a
minimalist working example on how to use the library which folks might find
helpful. Based on your answers below, I just copy the buffer in the
callback into my own ring of buffers, then work on the buffers in a
separate thread.
The program just writes captured data directly to disk. It works on my
computer, but I haven't tested it extensively.
Both files are attached.
Regards,
Dave J
On Sun, May 19, 2013 at 6:28 AM, Dimitri Stolnikov <[email protected]> wrote:
> Hi David,
>
>
> I am working on a personal project to use SDR techniques to decode
>> aviation navigation signals (VOR). I've got the signal processing mostly
>> working from recorded signals, but am now trying to integrate my SW with
>> the radio in real time.
>>
>
> - What exactly is offset tuning? How is offset tuning different from
>> tuning to an offset?
>>
>
> For E4000 tuners only this will shift thei 0-IF point outside of RTL
> sampling bandwidth which may help if you have a wideband (DECT or such)
> signal you want to sample but don't want it to be distorted by the usual
> LO-leakage peak in the middle. The drawback is that you might get hit by a
> image signal.
>
>
> Is this a feature that mostly benefits people who are not going to put
>> their IF through another mixer? In my application I am already tuning to an
>> offset, and pulling down a wide enough IF that actually holds many channels
>> of interest. (VOR channels have 50kHz spacing). I then use a software
>> mixer/channelizer to choose the channel I want. Am I correct in assuming
>> that offset tuning is of no use to me?
>>
>
> For narrowband signals you are good to do offset tuning yourself.
>
>
> - regarding AGC, what is the difference between AGC and auto gain?
>>
>
> AGC is for RTL chip, auto gain is for the tuner (if implemented).
>
>
> RTLSDR_API int rtlsdr_set_tuner_gain_mode(**rtlsdr_dev_t *dev, int
>> manual)
>>
>
> Works for E4000 tuners only AFAIR.
>
> Steve: do other tuners provide AGC functions as well?
>
>
> RTLSDR_API int rtlsdr_set_agc_mode(rtlsdr_**dev_t *dev, int on);
>>
>> I'm guessing that these affect different AGCs. One for the tuner and one
>> for the RTL device.
>>
>
> Yes.
>
>
> What are the benefits and costs of having either or both on?
>>
>
> It depends on the application. While in presence of strong interference
> the tuner auto gain seems to perform well on E4000 devices, the RTL gain
> from my understanding only scales the signal to optimally use the dynamic
> range of 8 bits. Within controlled environment (narrowband
> antenna/LNA/filters) i usually let them off entirely and set the gains for
> best SNR manually.
>
> Historically we have implemented them with some time in between so now
> both gains have their own setters.
>
>
>
>> - regarding rtlsdr_read_async(...) and related functions.
>>
>> I take it that the library is setting up a ring buffer and calling me
>> back when it has a new buffer of data for me.
>>
>
> Yes.
>
>
> How long to I have to work with this buffer? Obviously, if I want to work
>> in real-time I need to keep up with the sample rate. But my application can
>> afford to throw away buffers since it can decode a few ms of data from one
>> station and then revisit it much later. However, I'd like to know how long
>> I have until the buffer gets clobbered. I'm presuming it's stable until all
>> the n-1 other buffers have been hit.
>>
>
> You should spend as little time as possible inside a callback, this is a
> libusb requirement. You might copy some buffers to be passed to a worker
> thread later on and keep skipping buffers inside the callback until your
> worker thread is ready for the next shot.
>
>
> - generally how fast can the RTL devices tune? I know, this is not an
>> rtlsdr question per se, but I'm curious. I noticed that when I tune, I get
>> a delay.
>>
>
> Can't tell, never evaluated that myself. But again, this largely depends
> on the tuner interactions involved.
>
>
> smidge more documentation. I'd be happy to submit a comments-only patch
>> if there's interest. :-)
>>
>
> This would be appreciated.
>
>
> Best regards,
> Dimitri
>
/*
* rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver
* Copyright (C) 2012 by Steve Markgraf <[email protected]>
* Copyright (C) 2012 by Dimitri Stolnikov <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __RTL_SDR_H
#define __RTL_SDR_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <rtl-sdr_export.h>
typedef struct rtlsdr_dev rtlsdr_dev_t;
RTLSDR_API uint32_t rtlsdr_get_device_count(void);
RTLSDR_API const char* rtlsdr_get_device_name(uint32_t index);
/*!
* Get USB device strings.
*
* NOTE: The string arguments must provide space for up to 256 bytes.
*
* \param index the device index
* \param manufact manufacturer name, may be NULL
* \param product product name, may be NULL
* \param serial serial number, may be NULL
* \return 0 on success
*/
RTLSDR_API int rtlsdr_get_device_usb_strings(uint32_t index,
char *manufact,
char *product,
char *serial);
/*!
* Get device index by USB serial string descriptor.
*
* \param serial serial string of the device
* \return device index of first device where the name matched
* \return -1 if name is NULL
* \return -2 if no devices were found at all
* \return -3 if devices were found, but none with matching name
*/
RTLSDR_API int rtlsdr_get_index_by_serial(const char *serial);
RTLSDR_API int rtlsdr_open(rtlsdr_dev_t **dev, uint32_t index);
RTLSDR_API int rtlsdr_close(rtlsdr_dev_t *dev);
/* configuration functions */
/*!
* Set crystal oscillator frequencies used for the RTL2832 and the tuner IC.
*
* Usually both ICs use the same clock. Changing the clock may make sense if
* you are applying an external clock to the tuner or to compensate the
* frequency (and samplerate) error caused by the original (cheap) crystal.
*
* NOTE: Call this function only if you fully understand the implications.
*
* \param dev the device handle given by rtlsdr_open()
* \param rtl_freq frequency value used to clock the RTL2832 in Hz
* \param tuner_freq frequency value used to clock the tuner IC in Hz
* \return 0 on success
*/
RTLSDR_API int rtlsdr_set_xtal_freq(rtlsdr_dev_t *dev, uint32_t rtl_freq,
uint32_t tuner_freq);
/*!
* Get crystal oscillator frequencies used for the RTL2832 and the tuner IC.
*
* Usually both ICs use the same clock.
*
* \param dev the device handle given by rtlsdr_open()
* \param rtl_freq frequency value used to clock the RTL2832 in Hz
* \param tuner_freq frequency value used to clock the tuner IC in Hz
* \return 0 on success
*/
RTLSDR_API int rtlsdr_get_xtal_freq(rtlsdr_dev_t *dev, uint32_t *rtl_freq,
uint32_t *tuner_freq);
/*!
* Get USB device strings.
*
* NOTE: The string arguments must provide space for up to 256 bytes.
*
* \param dev the device handle given by rtlsdr_open()
* \param manufact manufacturer name, may be NULL
* \param product product name, may be NULL
* \param serial serial number, may be NULL
* \return 0 on success
*/
RTLSDR_API int rtlsdr_get_usb_strings(rtlsdr_dev_t *dev, char *manufact,
char *product, char *serial);
/*!
* Write the device EEPROM
*
* \param dev the device handle given by rtlsdr_open()
* \param data buffer of data to be written
* \param offset address where the data should be written
* \param len length of the data
* \return 0 on success
* \return -1 if device handle is invalid
* \return -2 if EEPROM size is exceeded
* \return -3 if no EEPROM was found
*/
RTLSDR_API int rtlsdr_write_eeprom(rtlsdr_dev_t *dev, uint8_t *data,
uint8_t offset, uint16_t len);
/*!
* Read the device EEPROM
*
* \param dev the device handle given by rtlsdr_open()
* \param data buffer where the data should be written
* \param offset address where the data should be read from
* \param len length of the data
* \return 0 on success
* \return -1 if device handle is invalid
* \return -2 if EEPROM size is exceeded
* \return -3 if no EEPROM was found
*/
RTLSDR_API int rtlsdr_read_eeprom(rtlsdr_dev_t *dev, uint8_t *data,
uint8_t offset, uint16_t len);
RTLSDR_API int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq);
/*!
* Get actual frequency the device is tuned to.
*
* \param dev the device handle given by rtlsdr_open()
* \return 0 on error, frequency in Hz otherwise
*/
RTLSDR_API uint32_t rtlsdr_get_center_freq(rtlsdr_dev_t *dev);
/*!
* Set the frequency correction value for the device.
*
* \param dev the device handle given by rtlsdr_open()
* \param ppm correction value in parts per million (ppm)
* \return 0 on success
*/
RTLSDR_API int rtlsdr_set_freq_correction(rtlsdr_dev_t *dev, int ppm);
/*!
* Get actual frequency correction value of the device.
*
* \param dev the device handle given by rtlsdr_open()
* \return correction value in parts per million (ppm)
*/
RTLSDR_API int rtlsdr_get_freq_correction(rtlsdr_dev_t *dev);
enum rtlsdr_tuner {
RTLSDR_TUNER_UNKNOWN = 0,
RTLSDR_TUNER_E4000,
RTLSDR_TUNER_FC0012,
RTLSDR_TUNER_FC0013,
RTLSDR_TUNER_FC2580,
RTLSDR_TUNER_R820T
};
/*!
* Get the tuner type.
*
* \param dev the device handle given by rtlsdr_open()
* \return RTLSDR_TUNER_UNKNOWN on error, tuner type otherwise
*/
RTLSDR_API enum rtlsdr_tuner rtlsdr_get_tuner_type(rtlsdr_dev_t *dev);
/*
* A note on the gain setter/getter functions below:
*
* These devices have two chips and set gain in two stages, the tuner/mixer
* (E4000, R820T and similar) and the ADC chip (RTL2832). The tuner gain
* can be set in two places (tuner gain and tuner IF gain) and the RTL
* device also has controllable gain.
*/
/*!
* Get a list of gains supported by the tuner.
*
* NOTE: The gains argument must be preallocated by the caller. If NULL is
* being given instead, the number of available gain values will be returned.
*
* \param dev the device handle given by rtlsdr_open()
* \param gains array of gain values. In tenths of a dB, 115 means 11.5 dB.
* \return <= 0 on error, number of available (returned) gain values otherwise
*/
RTLSDR_API int rtlsdr_get_tuner_gains(rtlsdr_dev_t *dev, int *gains);
/*!
* Set the gain for the tuner chip.
* Manual gain mode must be enabled for this to work.
*
* Valid gain values (in tenths of a dB) for the E4000 tuner:
* -10, 15, 40, 65, 90, 115, 140, 165, 190,
* 215, 240, 290, 340, 420, 430, 450, 470, 490
*
* Valid gain values may be queried with \ref rtlsdr_get_tuner_gains function.
*
* \param dev the device handle given by rtlsdr_open()
* \param gain in tenths of a dB, 115 means 11.5 dB.
* \return 0 on success
*/
RTLSDR_API int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain);
/*!
* Get actual gain the tuner chip is configured to.
*
* \param dev the device handle given by rtlsdr_open()
* \return 0 on error, gain in tenths of a dB, 115 means 11.5 dB.
*/
RTLSDR_API int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev);
/*!
* Set the tuner intermediate frequency gain for the tuner chip.
*
* \param dev the device handle given by rtlsdr_open()
* \param stage intermediate frequency gain stage number (1 to 6 for E4000)
* \param gain in tenths of a dB, -30 means -3.0 dB.
* \return 0 on success
*/
RTLSDR_API int rtlsdr_set_tuner_if_gain(rtlsdr_dev_t *dev, int stage, int gain);
/*!
* Set the gain mode (automatic/manual) for the tuner chip.
* Manual gain mode must be enabled for the gain setter function to work.
*
* \param dev the device handle given by rtlsdr_open()
* \param manual gain mode, 1 means manual gain mode shall be enabled.
* \return 0 on success
*/
RTLSDR_API int rtlsdr_set_tuner_gain_mode(rtlsdr_dev_t *dev, int manual);
/* this will select the baseband filters according to the requested sample rate */
RTLSDR_API int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t rate);
/*!
* Get actual sample rate the device is configured to.
*
* \param dev the device handle given by rtlsdr_open()
* \return 0 on error, sample rate in Hz otherwise
*/
RTLSDR_API uint32_t rtlsdr_get_sample_rate(rtlsdr_dev_t *dev);
/*!
* Enable test mode that returns an 8 bit counter instead of the samples.
* Each byte returned increments by one. There is no separate counter for
* I and Q samples.
*
* The counter is generated inside the RTL2832.
*
* \param dev the device handle given by rtlsdr_open()
* \param test mode, 1 means enabled, 0 disabled
* \return 0 on success
*/
RTLSDR_API int rtlsdr_set_testmode(rtlsdr_dev_t *dev, int on);
/*!
* Enable or disable the internal digital AGC of the RTL2832.
*
* \param dev the device handle given by rtlsdr_open()
* \param digital AGC mode, 1 means enabled, 0 disabled
* \return 0 on success
*/
RTLSDR_API int rtlsdr_set_agc_mode(rtlsdr_dev_t *dev, int on);
/*!
* Enable or disable the direct sampling mode. When enabled, the IF mode
* of the RTL2832 is activated, and rtlsdr_set_center_freq() will control
* the IF-frequency of the DDC, which can be used to tune from 0 to 28.8 MHz
* (xtal frequency of the RTL2832).
*
* For most devices, effective use of this mode will require physically
* accessing the input pins of the RTL2832 and attaching an antenna
* lead, since this most bypasses the tuner chip entirely.
*
* \param dev the device handle given by rtlsdr_open()
* \param on 0 means disabled, 1 I-ADC input enabled, 2 Q-ADC input enabled
* \return 0 on success
*/
RTLSDR_API int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on);
/*!
* Get state of the direct sampling mode
*
* \param dev the device handle given by rtlsdr_open()
* \return -1 on error, 0 means disabled, 1 I-ADC input enabled
* 2 Q-ADC input enabled
*/
RTLSDR_API int rtlsdr_get_direct_sampling(rtlsdr_dev_t *dev);
/*!
* Enable or disable offset tuning for zero-IF tuners, which allows to avoid
* problems caused by the DC offset of the ADCs and 1/f noise.
*
* \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_offset_tuning(rtlsdr_dev_t *dev, int on);
/*!
* Get state of the offset tuning mode
*
* \param dev the device handle given by rtlsdr_open()
* \return -1 on error, 0 means disabled, 1 enabled
*/
RTLSDR_API int rtlsdr_get_offset_tuning(rtlsdr_dev_t *dev);
/* streaming functions */
/* Streaming functionality overview:
*
* This library provides two ways to get data out of your RTL device. The
* first is simple enough: call rtlsdr_read_sync(...). When it returns, it
* does so with a buffer of data. However, a synchronous interface is wasteful
* of time since your program blocks waiting for a new buffer. This wait
* can generally be avoided using the asynchronous interface.
*
* In order to use the asynchronous interface, you must call
* rtlsdr_read_async(...) and as a parameter provide the address of a
* function that librtlsdr can call each time it has a new buffer
* ready. You could actually process the data received in that callback function,
* or more practically, copy the data to another buffer and return immediately.
* Then, in a separate thread, you can process the data.
*
* rtlsdr_read_async(...) will call back to the supplied function
* every time it has a new buffer of data until you call
* rtlsdr_cancel_async(...). At that time, the read_async function
* will return.
*
* To make effective use of this facility, you will need a multi-
* threaded program. All the programs supplied with the rtlsdr libray
* are multithreaded and can serve as good examples. Additionally, a
* minimalist working example is provided in XXXXX.c
*/
/*!
* Reset the librtlsdr buffer.
*
* This function should be called before calling rtlsdr_read_async().
*
* \param dev the device handle given by rtlsdr_open()
*/
RTLSDR_API int rtlsdr_reset_buffer(rtlsdr_dev_t *dev);
/*!
* Synchronous read function. For non-threaded programs.
*
* \param dev the device handle given by rtlsdr_open()
* \param buf a pointer to a buffer to receive the data
* \param len, the size of the buffer
* \param n_red, a pointer to an int whose value will be updated to reflect the
* actual number of bytes returned.
*/
RTLSDR_API int rtlsdr_read_sync(rtlsdr_dev_t *dev, void *buf, int len, int *n_read);
/*!
* This is the signature of the callback function to pass to
* rtlsdr_read_async(). Note that it includes a parameters for
* a receiving buffer (which librtlsdr will have written the
* data to, a length parameter, and a void pointer which you can
* use to pass your callback whatever context it may need. The
* context pointer is a copy of the one you can optionally provide
* when calling rtlsdr_read_async()
*/
typedef void(*rtlsdr_read_async_cb_t)(unsigned char *buf, uint32_t len, void *ctx);
/*!
* Read samples from the device asynchronously. This function will block until
* it is being canceled using rtlsdr_cancel_async()
*
* NOTE: This function is deprecated and is subject for removal.
*
* \param dev the device handle given by rtlsdr_open()
* \param cb callback function to return received samples
* \param ctx user specific context to pass via the callback function
* \return 0 on success
*/
RTLSDR_API int rtlsdr_wait_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx);
/*!
* Read samples from the device asynchronously. This function will block until
* it is being canceled using rtlsdr_cancel_async()
*
* \param dev the device handle given by rtlsdr_open()
* \param cb callback function to return received samples
* \param ctx user specific context to pass via the callback function
* \param buf_num optional buffer count, buf_num * buf_len = overall buffer size
* set to 0 for default buffer count (32)
* \param buf_len optional buffer length, must be multiple of 512,
* set to 0 for default buffer length (16 * 32 * 512)
* \return 0 on success
*/
RTLSDR_API int rtlsdr_read_async(rtlsdr_dev_t *dev,
rtlsdr_read_async_cb_t cb,
void *ctx,
uint32_t buf_num,
uint32_t buf_len);
/*!
* Cancel all pending asynchronous operations on the device.
* rtlsdr_read_async() will return after this function has been called.
*
* \param dev the device handle given by rtlsdr_open()
* \return 0 on success
*/
RTLSDR_API int rtlsdr_cancel_async(rtlsdr_dev_t *dev);
#ifdef __cplusplus
}
#endif
#endif /* __RTL_SDR_H */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>
#include <string.h>
#include "rtl-sdr.h"
/*
* This C program is intended to show the minimal amount of code
* necessary to work with the rtlsdr library asynchronously. The
* tools rtl_fm, rtl_tcp, etc, all do something like this, but they
* may be a little intimidating for casual users who merely
* wants to link against rtlsdr and get "up and running" quickly.
*
* Hopefully this program is useful to someone.
*
*
* The basic structure of this program is that it kicks off two
* threads. One receives callbacks from the rtlsdr library,
* the other does "work" -- the case below, splatting the data
* to a file. The two threads communicate through a simple
* buffer, which is protected by mutexes so that the threads
* to not clobber each other or deadlock.
*
* There are actually two buffers in this program. One is hidden
* from view. The hidden one is behind the scenes inside librtlsdr.
* When you rtlsdr_read_async() is called, that buffer is set up
* with the sizes you specify. Later, when read_async calls back to
* the call back you provide, the callback is given a pointer to
* the element of that buffer that it is safe to copy from. The
* general requirement for libusb and thus rtlsdr is that one
* should not dally with this callback function. Just copy the
* data and return -- which is what this program does.
*
* The second buffer is the one allocated below in the
* ringbuffer_t structure. Our callback function will copy from
* librtlsdr's buffer to a block in this buffer, and then return.
* The "worker" thread will read from a block in this buffer and
* do it's thing, whatever that might be.
*
* It's perhaps a little memory inefficient to use two sets of
* buffers, but because we can't see into librtlsdr/libusb's use
* of them, the best course of action is to have our own buffers
* and minimize the time we are accessing the librtlsdr buffer.
*
*
*
* Regards,
* Davd J
* May 2013
*/
/* hopefully self explanatory! */
#define BUF_COUNT (8)
#define BUF_SIZE (1024*8)
#define MAX_ITERS (500)
#define EXAMP_FILENAME "examples_iq.dat"
#define FREQUENCY (104500000)
#define SAMPLE_RATE (256000)
void *worker_thread_fn(void *f);
void *callback_setup_thread_fn(void *f);
void callback(unsigned char *, uint32_t , void *);
/*
* simple struct to hold everything you need for a
* simple producer/consumer ringbuffer
*/
typedef struct ringbuffer_t {
char ringbuffer[BUF_COUNT][BUF_SIZE];
pthread_mutex_t ringbuffer_mutex;
pthread_cond_t ringbuffer_written_cond;
int reader_unread;
int writer_next;
} ringbuffer_t;
/* globals: device pointer and ringbuffer data struct
* These don't need to be globals and probably should not
* be in a more complex program, and of course, if you
* hope to talk to more than one radio at once.
**/
rtlsdr_dev_t *dev;
ringbuffer_t rbuf;
int main(int argc, char*argv[]) {
pthread_t worker_thread;
pthread_t callback_thread;
memset(&rbuf,0,sizeof(rbuf));
/* open and set up the RTL device */
int fail = rtlsdr_open(&dev,0);
if (fail) { printf("-err- could not open rtl device\n"); exit(-1); };
fail = rtlsdr_set_sample_rate(dev,SAMPLE_RATE);
if (fail) { printf("-err- could not set sample rate\n"); exit(-2); };
fail = rtlsdr_set_center_freq(dev,FREQUENCY);
if (fail) { printf("-err- could not set center freq\n"); exit(-3); };
/* set up the mutex and condition variable. Wait, what is a condition
* variable you ask? It is a variable that one thread can write to
* to wake up another thread that is watching for it. This allows
* the program to sleep the worker thread when there is no data to
* work, and wake it when there is. It is for maximum efficiency.
*/
pthread_mutex_init(&rbuf.ringbuffer_mutex, 0);
pthread_cond_init(&rbuf.ringbuffer_written_cond, 0);
/* start the threads */
pthread_create(&callback_thread,NULL,callback_setup_thread_fn,NULL);
pthread_create(&worker_thread,NULL,worker_thread_fn,NULL);
/* the original program thread will wait here until the worker
* thread returns
*/
pthread_join(worker_thread, NULL);
pthread_cancel(callback_thread);
rtlsdr_close(dev);
};
void *worker_thread_fn(void *f) {
char done = 0;
int iters = 0;
int i;
FILE *ofile;
ofile = fopen(EXAMP_FILENAME,"wb");
if (!ofile) {
printf("-error- could not open output file.\n");
exit(-2);
}
while (!done) {
/* lock -> wait on cond -> use -> decrement -> unlock */
pthread_mutex_lock(&rbuf.ringbuffer_mutex);
if (rbuf.reader_unread ==0) {
/* if there is no data to work on, wait (sleep) on getting woken up
* by the condition variable. The way cond_wait works is interesting.
* It atomically releases the mutex so the other thread can do
* some work and signal it's done. Then it takes back the mutex and
* program flow continues in this thread.
*/
pthread_cond_wait(&rbuf.ringbuffer_written_cond, &rbuf.ringbuffer_mutex);
}
/* otherwise, operate on a buffer of data */
if (rbuf.reader_unread > 0) {
int pos = rbuf.writer_next - rbuf.reader_unread;
if (pos<0) { pos += BUF_COUNT; }
/*
* do something interesting with the data we have collected.
* In the example below we just splat it to a file.
*/
int written = fwrite(rbuf.ringbuffer[pos],1,BUF_SIZE,ofile);
if (written != BUF_SIZE) {
printf("-warn- only %d bytes written of %d buffer\n",written,BUF_SIZE);
}
--rbuf.reader_unread;
}
pthread_mutex_unlock(&rbuf.ringbuffer_mutex);
if (iters++ > MAX_ITERS) {
done = 1;
}
}
fclose(ofile);
/* calling this causes librtlsdr to stop calling back, and the
* callback thread will unblock and complete
*/
rtlsdr_cancel_async(dev);
return NULL;
};
void *callback_setup_thread_fn(void *f) {
/* librtlsdr requires call to reset async */
rtlsdr_reset_buffer(dev);
/* this function sets up the callbacks. rtlsdr will call
* the function specified below every time it has a new
* buffer of data. rtlsdr_read_async itself will not
* itself return, however, until it has been "turned
* off" by someone calling rtlsdr_cancel_async().
* (Which is in this case is done just before the worker
* thread exits.
*/
rtlsdr_read_async(dev,&callback,f,BUF_COUNT,BUF_SIZE);
return NULL;
};
/* the actual callback function */
void callback(unsigned char *ibuf, uint32_t len, void *f) {
char overflow = 0;
/*
* lock -> copy -> increment -> signal -> unlock
*/
pthread_mutex_lock(&rbuf.ringbuffer_mutex);
memcpy(rbuf.ringbuffer[rbuf.writer_next],ibuf,len);
rbuf.writer_next = (rbuf.writer_next + 1) % BUF_COUNT;
if (rbuf.reader_unread < BUF_COUNT) {
++rbuf.reader_unread;
} else {
/* in some applications one might want to the producer
* if the consumer has fallen behind. We're not doing that in
* this example at all. Instead, if the consumer hasn't
* consumed a given buffer, just issue a warning and move
* on. This makes sense in a radio application where the
* incoming data cannot be stopped, so why pretend and
* stop copying it to the buffer?
*/
printf("-warn- write overflow\n");
};
pthread_cond_signal(&rbuf.ringbuffer_written_cond);
pthread_mutex_unlock(&rbuf.ringbuffer_mutex);
};