/* -*- c++ -*- */
/*
 * Copyright 2004 Free Software Foundation, Inc.
 * 
 * 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.
 */

/*
 * gr-baz by Balint Seeber (http://spench.net/contact)
 * Information, documentation & samples: http://wiki.spench.net/wiki/gr-baz
 */

/*
 * 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 <baz_rtl_source_c.h>
#include <gnuradio/io_signature.h>
#include <stdio.h>

#include <iostream>	// cerr

#include "rtl2832.h"

#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
#define strcasecmp _stricmp
#endif

baz_rtl_source_c_sptr 
baz_make_rtl_source_c(bool defer_creation /*= false*/, int output_size /*= 0*/)
{
  return baz_rtl_source_c_sptr(new baz_rtl_source_c(defer_creation, output_size));
}

enum RtlStatusFlags {	// Matches BorIP values
  RTL_STATUS_OK					= 0x00,
  RTL_STATUS_HARDWARE_OVERRUN	= 0x01,
  RTL_STATUS_BUFFER_OVERRUN		= 0x04,
  RTL_STATUS_UNDERRUN			= 0x40,
  RTL_STATUS_TIMEOUT			= 0x80
};

// COMPAT /////////////////////////////////////////////////////////////////////
#define _T(x)					x
#define ZeroMemory(p,l)			memset(p, 0x00, l)
#define ZERO_MEMORY(p)			ZeroMemory(&p, sizeof(p))
#define SAFE_DELETE_ARRAY(p)	{ if (p) { delete [] p; p = NULL; } }
#define SAFE_DELETE(p)			{ if (p) { delete p; p = NULL; } }
#define min(a,b)				(a < b ? a : b)
///////////////////////////////////////////////////////////////////////////////

#define DEFAULT_READLEN			(/*16 * */16384 * 2)	// This is good compromise between maintaining a good buffer level and immediate response to parameter adjustment
#define DEFAULT_BUFFER_MUL		(4*2)
#define DEFAULT_BUFFER_LEVEL	0.5f
#define WAIT_FUDGE				(1.2+0.3)
#define RAW_SAMPLE_SIZE			(1+1)
//#define EXTREME_LOCKING		// Switched off to improve responsiveness (just don't call certain functions from different threads simultaneously!)

static float _char_to_float_lut[256] = { -1.000000f, -0.992188f, -0.984375f, -0.976562f, -0.968750f, -0.960938f, -0.953125f, -0.945312f, -0.937500f, -0.929688f, -0.921875f, -0.914062f, -0.906250f, -0.898438f, -0.890625f, -0.882812f, -0.875000f, -0.867188f, -0.859375f, -0.851562f, -0.843750f, -0.835938f, -0.828125f, -0.820312f, -0.812500f, -0.804688f, -0.796875f, -0.789062f, -0.781250f, -0.773438f, -0.765625f, -0.757812f, -0.750000f, -0.742188f, -0.734375f, -0.726562f, -0.718750f, -0.710938f, -0.703125f, -0.695312f, -0.687500f, -0.679688f, -0.671875f, -0.664062f, -0.656250f, -0.648438f, -0.640625f, -0.632812f, -0.625000f, -0.617188f, -0.609375f, -0.601562f, -0.593750f, -0.585938f, -0.578125f, -0.570312f, -0.562500f, -0.554688f, -0.546875f, -0.539062f, -0.531250f, -0.523438f, -0.515625f, -0.507812f, -0.500000f, -0.492188f, -0.484375f, -0.476562f, -0.468750f, -0.460938f, -0.453125f, -0.445312f, -0.437500f, -0.429688f, -0.421875f, -0.414062f, -0.406250f, -0.398438f, -0.390625f, -0.382812f, -0.375000f, -0.367188f, -0.359375f, -0.351562f, -0.343750f, -0.335938f, -0.328125f, -0.320312f, -0.312500f, -0.304688f, -0.296875f, -0.289062f, -0.281250f, -0.273438f, -0.265625f, -0.257812f, -0.250000f, -0.242188f, -0.234375f, -0.226562f, -0.218750f, -0.210938f, -0.203125f, -0.195312f, -0.187500f, -0.179688f, -0.171875f, -0.164062f, -0.156250f, -0.148438f, -0.140625f, -0.132812f, -0.125000f, -0.117188f, -0.109375f, -0.101562f, -0.093750f, -0.085938f, -0.078125f, -0.070312f, -0.062500f, -0.054688f, -0.046875f, -0.039062f, -0.031250f, -0.023438f, -0.015625f, -0.007812f, 0.000000f, 0.007812f, 0.015625f, 0.023438f, 0.031250f, 0.039062f, 0.046875f, 0.054688f, 0.062500f, 0.070312f, 0.078125f, 0.085938f, 0.093750f, 0.101562f, 0.109375f, 0.117188f, 0.125000f, 0.132812f, 0.140625f, 0.148438f, 0.156250f, 0.164062f, 0.171875f, 0.179688f, 0.187500f, 0.195312f, 0.203125f, 0.210938f, 0.218750f, 0.226562f, 0.234375f, 0.242188f, 0.250000f, 0.257812f, 0.265625f, 0.273438f, 0.281250f, 0.289062f, 0.296875f, 0.304688f, 0.312500f, 0.320312f, 0.328125f, 0.335938f, 0.343750f, 0.351562f, 0.359375f, 0.367188f, 0.375000f, 0.382812f, 0.390625f, 0.398438f, 0.406250f, 0.414062f, 0.421875f, 0.429688f, 0.437500f, 0.445312f, 0.453125f, 0.460938f, 0.468750f, 0.476562f, 0.484375f, 0.492188f, 0.500000f, 0.507812f, 0.515625f, 0.523438f, 0.531250f, 0.539062f, 0.546875f, 0.554688f, 0.562500f, 0.570312f, 0.578125f, 0.585938f, 0.593750f, 0.601562f, 0.609375f, 0.617188f, 0.625000f, 0.632812f, 0.640625f, 0.648438f, 0.656250f, 0.664062f, 0.671875f, 0.679688f, 0.687500f, 0.695312f, 0.703125f, 0.710938f, 0.718750f, 0.726562f, 0.734375f, 0.742188f, 0.750000f, 0.757812f, 0.765625f, 0.773438f, 0.781250f, 0.789062f, 0.796875f, 0.804688f, 0.812500f, 0.820312f, 0.828125f, 0.835938f, 0.843750f, 0.851562f, 0.859375f, 0.867188f, 0.875000f, 0.882812f, 0.890625f, 0.898438f, 0.906250f, 0.914062f, 0.921875f, 0.929688f, 0.937500f, 0.945312f, 0.953125f, 0.960938f, 0.968750f, 0.976562f, 0.984375f, 0.992188f };

///////////////////////////////////////////////////////////////////////////////

baz_rtl_source_c::baz_rtl_source_c (bool defer_creation /*= false*/, int output_size /*= 0*/)
  : gr::block ("baz_rtl_source",
	      gr::io_signature::make (0, 0, 0),
	      gr::io_signature::make (1, 1, ((output_size > 0) ? output_size : sizeof(gr_complex))))
	, m_nSamplesReceived(0)
	, m_nOverflows(0)
	, m_bRunning(false)
	, m_recv_samples_per_packet(0)
	, m_nBufferItems(0)
	, m_pUSBBuffer(NULL)
	, m_nBufferStart(0)
	, m_nBufferSize(0)
	, m_bBuffering(false)
	, m_nReadLength(DEFAULT_READLEN)
	, m_nBufferMultiplier(DEFAULT_BUFFER_MUL)
	, m_bUseBuffer(true)
	, m_fBufferLevel(DEFAULT_BUFFER_LEVEL)
	, m_nReadPacketCount(0)
	, m_nBufferOverflowCount(0)
	, m_nBufferUnderrunCount(0)
	, m_verbose(true)
	, m_relative_gain(false)
	, m_output_size(0)
{
  ZERO_MEMORY(m_demod_params);
 
#ifdef HAVE_XTIME
  ZERO_MEMORY(m_wait_delay);	// This is not future proof (will be initialised properly in 'set_sample_rate')

//  struct timespec res;
//  clock_getres(CLOCK_MONOTONIC, &res);
//  log_verbose("Res (%lld s, %lld ns)\n", res.tv_sec, res.tv_nsec);
#endif // HAVE_XTIME

  set_output_format(output_size);
  
  if ((defer_creation == false) && (create() == false))
	throw std::runtime_error("Failed to create RTL2832-based source");
}

/*
 * Our virtual destructor.
 */
baz_rtl_source_c::~baz_rtl_source_c ()
{
  destroy();
}

void baz_rtl_source_c::report_status(int status)
{
  //boost::recursive_mutex::scoped_lock lock(d_mutex);
  
  if (!m_status_queue)
	return;
  
  gr::message::sptr msg = gr::message::make(status);
  m_status_queue->/*handle*/insert_tail(msg);
}

int 
baz_rtl_source_c::general_work (int noutput_items,
			       gr_vector_int &ninput_items,
			       gr_vector_const_void_star &input_items,
			       gr_vector_void_star &output_items)
{
  //float *out = (float *)output_items[0];	// gr_complex = float * 2 [_M_real, _M_imag]
  gr_complex *out = (gr_complex *)output_items[0];
  short* out4 = (short*)output_items[0];
  char* out2 = (char*)output_items[0];
  int item_adjust = ((m_output_size == sizeof(gr_complex)) ? 1 : 2);
  
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

  boost::recursive_mutex::scoped_lock lock(d_mutex);

  if (m_bRunning == false)
  {
	log_error(_T("work called while not running!\n"));
	return -1;	// EOF
  }
  
  if (m_bUseBuffer == false)
  {
	int iToRead = (noutput_items/item_adjust) * RAW_SAMPLE_SIZE;
	if (iToRead > (m_nBufferSize * RAW_SAMPLE_SIZE))
	{
	  log_error(_T("work wants more than the buffer size!\n"));
	  iToRead = m_nBufferSize * RAW_SAMPLE_SIZE;
	}
	
	int iRead = 0;
	int res = m_demod.read_samples(m_pUSBBuffer, iToRead, &iRead);
	if ((res != 0) && (res != LIBUSB_ERROR_OVERFLOW))
	{
	  log_error(_T("libusb error: %s [%i]\n"), libusb_result_to_string(res), res);
	  return -1;
	}
	
	if ((uint32_t)iRead < iToRead)
	{
	  log_error(_T("Short bulk read: given %i bytes (expecting %lu)\n"), iRead, iToRead);
	}
	
	int iSampleCount = iRead / RAW_SAMPLE_SIZE;
	m_nSamplesReceived += iSampleCount;
	
	if (m_output_size == (sizeof(char)))
	{
	  memcpy(out2, m_pUSBBuffer, iRead);
	}
	else
	{
	  for (int n = 0; n < iSampleCount; n++)
	  {
		if (m_output_size == (sizeof(short)))	// No range expansion
		{
		  out4[n*2 + 0] = (unsigned char)m_pUSBBuffer[n*RAW_SAMPLE_SIZE + 0] - 128;
		  out4[n*2 + 1] = (unsigned char)m_pUSBBuffer[n*RAW_SAMPLE_SIZE + 1] - 128;
		}
		else if (m_output_size == sizeof(gr_complex))
		{
		  out[n] = gr_complex(
			_char_to_float_lut[m_pUSBBuffer[n*RAW_SAMPLE_SIZE + 0]],
			_char_to_float_lut[m_pUSBBuffer[n*RAW_SAMPLE_SIZE + 1]]
		  );
		}
	  }
	}
	
	if (res == LIBUSB_ERROR_OVERFLOW)
	{
	  ++m_nOverflows;
	  log_error(_T("rO"));
	  report_status(RTL_STATUS_HARDWARE_OVERRUN);
	}
	
	++m_nReadPacketCount;
	
	return noutput_items;
  }
  
  ///////////////////////////////////
  
  if ((noutput_items/item_adjust) > m_nBufferSize)
  {
	log_error(_T("work wants more than the buffer size!\n"));
	noutput_items = m_nBufferSize * item_adjust;
  }
retry_notify:
  while ((m_bBuffering) || (m_nBufferItems <= ((uint32_t)(m_fBufferLevel * (float)m_nBufferSize) + m_recv_samples_per_packet)))	// If getting too full, send them all through
  {
	bool notified = true;
	
	if (m_bBuffering)
	  m_hPacketEvent.wait(lock);	// Always wait for new samples to arrive while buffering
	else
	{
#ifdef HAVE_XTIME
	  xtime_get(&m_wait_next, CLOCK_MONOTONIC);
	  m_wait_next.nsec += m_wait_delay.nsec;
	  if (m_wait_next.nsec >= 1000000000)
	  {
		m_wait_next.sec += 1;
		m_wait_next.nsec -= 1000000000;
	  }
	  ///////////////////////////////////////////////////
	  notified = m_hPacketEvent.timed_wait(lock, m_wait_next);	// Wait for more samples to arrive, or wait just longer than it would have actually taken and use buffer samples
#else
      m_hPacketEvent.wait(lock);	// In this case read prediction will be disabled
#endif // HAVE_XTIME
	}
	
	if (notified == false)	// Timeout
	{
	  log_error("rT");
	  report_status(RTL_STATUS_TIMEOUT);
	  break;	// Running late, use up some of the buffer
	}
	
	if (m_bRunning == false)
	{
	  log_error(_T("No longer running after packet notification - signalling EOF...\n"));
	  return -1;	// EOF
	}
	
	if (m_bBuffering == false)	// No longer filling buffer, so send samples back to runtime
	  break;
	
	log_error(_T("Caught packet signal while buffering!\n"));
  }

  if (m_nBufferItems < m_recv_samples_per_packet)
  {
	//log_error(_T("Reading packet after signal, but not enough items in buffer (only %lu, need at least: %lu, start now %lu) [#%lu]\n"), m_nBufferItems, m_recv_samples_per_packet, m_nBufferStart, m_nReadPacketCount);
	log_error("rU");
	report_status(RTL_STATUS_UNDERRUN);
	
	m_bBuffering = true;
	++m_nBufferUnderrunCount;
	
	goto retry_notify;	// Keep waiting for buffer to fill back up sufficiently
  }
  else if (m_nBufferItems < (noutput_items/item_adjust))	// Double check
  {
	log_error(_T("Not enough items for work %lu items (only %lu, s/p: %lu, start now %lu) [#%lu]\n"), (noutput_items/item_adjust), m_nBufferItems, m_recv_samples_per_packet, m_nBufferStart, m_nReadPacketCount);
	noutput_items = m_nBufferItems * item_adjust;
  }
  
  ++m_nReadPacketCount;
  
  uint32_t nPart1 = min((noutput_items/item_adjust), m_nBufferSize - m_nBufferStart);
  uint8_t* p = m_pUSBBuffer + (m_nBufferStart * RAW_SAMPLE_SIZE);
  uint32_t nResidual = (noutput_items/item_adjust) - nPart1;
  
  if (m_output_size == (sizeof(char)))
  {
	memcpy(out2, p, nPart1 * RAW_SAMPLE_SIZE);
	if (nResidual)
	  memcpy(out2 + nPart1 * RAW_SAMPLE_SIZE, m_pUSBBuffer, nResidual * RAW_SAMPLE_SIZE);
  }
  else
  {
	for (uint32_t n = 0; n < nPart1; n++)
	{
	  if (m_output_size == (sizeof(short)))
	  {
		out4[n*2 + 0] = (unsigned char)p[n*2 + 0] - 128;
		out4[n*2 + 1] = (unsigned char)p[n*2 + 1] - 128;
	  }
	  else if (m_output_size == sizeof(gr_complex))
	  {
		//short i = p[n*2+0];
		//short q = p[n*2+1];
		//out[n] = gr_complex(i - 127, q - 127);
		out[n] = gr_complex(
		  _char_to_float_lut[p[n*2 + 0]],
		  _char_to_float_lut[p[n*2 + 1]]
		);
	  }
	}
	
	if (nResidual > 0)
	{
	  for (uint32_t n = 0; n < nResidual; n++)
	  {
		if (m_output_size == (sizeof(short)))
		{
		  out4[(nPart1 * 2) + n*2 + 0] = (unsigned char)m_pUSBBuffer[n*2 + 0] - 128;
		  out4[(nPart1 * 2) + n*2 + 1] = (unsigned char)m_pUSBBuffer[n*2 + 1] - 128;
		}
		else if (m_output_size == sizeof(gr_complex))
		{
		  //short i = m_pUSBBuffer[n*2+0];
		  //short q = m_pUSBBuffer[n*2+1];
		  //out[nPart1 + n] = gr_complex(i - 127, q - 127);
		  out[nPart1 + n] = gr_complex(
			_char_to_float_lut[m_pUSBBuffer[n*2 + 0]],
			_char_to_float_lut[m_pUSBBuffer[n*2 + 1]]
		  );
		}
	  }
	}
  }
  
  m_nSamplesReceived += (noutput_items/item_adjust);
  
  m_nBufferItems -= (noutput_items/item_adjust);
  
  if (m_nBufferItems > 0)
	  m_nBufferStart = (m_nBufferStart + (noutput_items/item_adjust)) % m_nBufferSize;
  
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  //consume_each(0);	// Tell runtime system how many input items we consumed on each input stream.
  
  return noutput_items;	// Tell runtime system how many output items we produced.
}

void baz_rtl_source_c::log(int level, const char* message, va_list args)
{
  if ((level >= LOG_LEVEL_VERBOSE) && (m_verbose == false))
	return;
  
  vfprintf(stderr, message, args);
}

void baz_rtl_source_c::set_defaults()
{
  memset(&m_demod_params, 0x00, sizeof(m_demod_params));

  // Buffer parameters
  m_nReadLength			= DEFAULT_READLEN;
  m_nBufferMultiplier	= DEFAULT_BUFFER_MUL;
  m_fBufferLevel		= DEFAULT_BUFFER_LEVEL;
  m_bUseBuffer			= true;
}

bool baz_rtl_source_c::set_output_format(int size)
{
  switch (size)
  {
	case (sizeof(char)):	// Byte
	case (sizeof(short)):	// Short
	case sizeof(gr_complex):	// 2 * Float
	  break;
	default:
	  return false;
  }
  
  boost::recursive_mutex::scoped_lock lock(d_mutex);
  
  m_output_size = size;
  
  if (m_recv_samples_per_packet > 0)
	set_output_multiple(m_recv_samples_per_packet * ((m_output_size == sizeof(gr_complex)) ? 1 : 2));
  
  return true;
}

void baz_rtl_source_c::set_status_msgq(gr::msg_queue::sptr queue)	// Only call this once before beginning capture! (otherwise locking required)
{
  //boost::recursive_mutex::scoped_lock lock(d_mutex);
  
  m_status_queue = queue;
}

bool baz_rtl_source_c::create(bool reset_buffer_defaults /*= false*/)
{
  destroy();
  
  if (reset_buffer_defaults)
	set_defaults();
  
  /////////////////////////
  
  m_demod_params.message_output = this;
  m_demod_params.verbose = m_verbose;

  /////////////////////////
  
  m_recv_samples_per_packet = m_nReadLength / RAW_SAMPLE_SIZE;	// Must be the same since rate is determined by the libusb reads!
  set_output_format(m_output_size);
  
  m_nBufferSize = m_recv_samples_per_packet * m_nBufferMultiplier;
  m_pUSBBuffer = new uint8_t[m_nBufferSize * RAW_SAMPLE_SIZE];
  assert(m_pUSBBuffer);
  ZeroMemory(m_pUSBBuffer, m_nBufferSize * RAW_SAMPLE_SIZE);
  
  log_verbose(_T("RTL2832 Source block configuration:\n")
	_T("\tRead length (bytes): %lu\n")
	_T("\tBuffer enabled: %s\n")
	_T("\tBuffer multiplier: %lu\n")
	_T("\tBuffer size (samples): %lu\n")
	_T("\tSamples per read: %lu\n")
	_T("\tBuffer level: %.1f%%\n"),
	m_nReadLength,
	(m_bUseBuffer ? _T("yes") : _T("no")),
	m_nBufferMultiplier,
	m_nBufferSize,
	m_recv_samples_per_packet,
	(100.0f * m_fBufferLevel)
  );

  /////////////////////////

  return (m_demod.initialise(&m_demod_params) == RTL2832_NAMESPACE::SUCCESS);
}

void baz_rtl_source_c::destroy()
{
  stop();

  m_demod.destroy();

  SAFE_DELETE_ARRAY(m_pUSBBuffer);
}

void baz_rtl_source_c::_capture_thread(baz_rtl_source_c* p)
{
  return p->capture_thread();
}

void baz_rtl_source_c::reset()
{
  boost::recursive_mutex::scoped_lock lock(d_mutex);
  
  m_nBufferStart = 0;
  m_nBufferItems = 0;
  m_nSamplesReceived = 0;
  m_nOverflows = 0;
  
  m_nReadPacketCount = 0;
  m_nBufferOverflowCount = 0;
  m_nBufferUnderrunCount = 0;
}

bool baz_rtl_source_c::start()
{
  boost::recursive_mutex::scoped_lock lock(d_mutex);
  
  if (m_bRunning)
	return true;

  reset();

  if (m_demod.reset() != RTL2832_NAMESPACE::SUCCESS)
	return false;

  m_bRunning = true;	// Need to set this BEFORE starting thread (otherwise it will exit)
  
  if (m_bUseBuffer)
  {
	m_bBuffering = true;
	m_pCaptureThread = boost::thread(_capture_thread, this);
  }

  return true;
}

bool baz_rtl_source_c::stop()
{
  boost::recursive_mutex::scoped_lock lock(d_mutex);

  if (m_bRunning == false)
	return true;
  
  m_bRunning = false;	// Must set before 'join' as this will signal capture thread to exit
  
  if (m_bUseBuffer == false)
	return true;
  
  m_hPacketEvent.notify_one();	// In case general_work is waiting

  lock.unlock();	// Release lock AFTER notification, so waiting thread resumes in lockstep
  
  m_pCaptureThread.join();	// Wait for capture thread to finish
  
  return true;
}

bool baz_rtl_source_c::set_gain(double dGain)
{
#ifdef EXTREME_LOCKING
  boost::recursive_mutex::scoped_lock lock(d_mutex);
#endif // EXTREME_LOCKING

  if (m_relative_gain)
  {
	if ((dGain < 0) || (dGain > 1))
	  return false;

	RTL2832_NAMESPACE::range_t gain_range = m_demod.active_tuner()->gain_range();
	if (RTL2832_NAMESPACE::is_valid_range(gain_range) == false)
	  return false;

	double range = RTL2832_NAMESPACE::calc_range(gain_range);
	dGain = gain_range.first + range * dGain;
  }

  return (m_demod.active_tuner()->set_gain(dGain) == RTL2832_NAMESPACE::SUCCESS);
}

bool baz_rtl_source_c::set_frequency(double dFreq)
{
#ifdef EXTREME_LOCKING
  boost::recursive_mutex::scoped_lock lock(d_mutex);
#endif // EXTREME_LOCKING

  return (m_demod.active_tuner()->set_frequency(dFreq) == RTL2832_NAMESPACE::SUCCESS);
}

bool baz_rtl_source_c::set_sample_rate(double dSampleRate)
{
  if (dSampleRate <= 0)
	return false;
  
  boost::recursive_mutex::scoped_lock lock(d_mutex);
  
  if (m_demod.set_sample_rate((uint32_t)dSampleRate, &dSampleRate) != RTL2832_NAMESPACE::SUCCESS)
  {
	if (dSampleRate == 0)
	  log_verbose("Hardware sample rate could not be calculated from give rate: %f\n", dSampleRate);

	return false;
  }

  double dDelay = 1000000000ULL * WAIT_FUDGE / (double)((dSampleRate * RAW_SAMPLE_SIZE) / (double)m_nReadLength);
  if (m_bUseBuffer)
	log_verbose("Wait delay: %.3f ms\n", (dDelay / 1000000.0));
  uint64_t delay = (uint64_t)ceil(dDelay);
#ifdef HAVE_XTIME
  m_wait_delay.sec = delay / 1000000000ULL;
  m_wait_delay.nsec = delay % 1000000000ULL;
  //if (m_bUseBuffer)
  //  log_verbose("\t(%lld s, %lld ns)\n", m_wait_delay.sec, m_wait_delay.nsec);
#endif // HAVE_XTIME

  return true;
}

bool baz_rtl_source_c::set_bandwidth(double bandwidth)
{
#ifdef EXTREME_LOCKING
  boost::recursive_mutex::scoped_lock lock(d_mutex);
#endif // EXTREME_LOCKING

  return (m_demod.active_tuner()->set_bandwidth(bandwidth) == RTL2832_NAMESPACE::SUCCESS);
}

bool baz_rtl_source_c::set_gain_mode(int mode)
{
#ifdef EXTREME_LOCKING
  boost::recursive_mutex::scoped_lock lock(d_mutex);
#endif // EXTREME_LOCKING

  return (m_demod.active_tuner()->set_gain_mode(mode) == RTL2832_NAMESPACE::SUCCESS);
}

std::string baz_rtl_source_c::gain_mode_string() const
{
  RTL2832_NAMESPACE::num_name_map_t map = gain_modes();
  int mode = gain_mode();
  RTL2832_NAMESPACE::num_name_map_t::iterator it = map.find(mode);
  if (it == map.end())
	  return ((mode == RTL2832_NAMESPACE::tuner::DEFAULT) ? "(default)" : "(unknown)");
  
  return it->second;
}

bool baz_rtl_source_c::set_gain_mode(const char* mode)
{
  if (mode == NULL)
	  return set_gain_mode(RTL2832_NAMESPACE::tuner::DEFAULT);
  
  RTL2832_NAMESPACE::num_name_map_t map = gain_modes();
  for (RTL2832_NAMESPACE::num_name_map_t::iterator it = map.begin(); it != map.end(); ++it)
  {
	  if (strcasecmp(mode, it->second.c_str()) == 0)
		  return set_gain_mode(it->first);
  }
  
  log_error("Invalid gain mode: \"%s\"\n", mode);
  
  return false;
}

bool baz_rtl_source_c::set_auto_gain_mode(bool on /*= true*/)
{
#ifdef EXTREME_LOCKING
  boost::recursive_mutex::scoped_lock lock(d_mutex);
#endif // EXTREME_LOCKING

  return (m_demod.active_tuner()->set_auto_gain_mode(on) == RTL2832_NAMESPACE::SUCCESS);
}

///////////////////////////////////////////////////////////////////////////////

void baz_rtl_source_c::capture_thread()
{
  boost::recursive_mutex::scoped_lock lock(d_mutex, boost::defer_lock);
  
  if ((m_nReadLength == 0) ||
	  (m_pUSBBuffer == NULL) ||
	  (m_nBufferSize == 0) ||
	  (m_fBufferLevel < 0))
  {
	lock.lock();
	m_bRunning = false;	// This will signal EOF
	m_hPacketEvent.notify_one();
	lock.unlock();
	
	if (m_verbose)
	  std::cerr << "Capture threading NOT starting due to state error: " << boost::this_thread::get_id() << std::endl;
	return;
  }
  
  if (m_verbose)
	std::cerr << "Capture threading starting: " << boost::this_thread::get_id() << std::endl;
  
  uint32_t nCount = 0;
  
  uint8_t* pBuffer = new uint8_t[m_nReadLength];
  
  while (true)
  {
	lock.lock();
	if (m_bRunning == false)
	  break;
	lock.unlock();
	
	int lLockSize = 0;
	int res = m_demod.read_samples(pBuffer, m_nReadLength, &lLockSize);
	if (res == LIBUSB_ERROR_OVERFLOW)
	{
	  log_error(_T("rO"));
	  report_status(RTL_STATUS_HARDWARE_OVERRUN);
	}
	else if (res != 0)
	{
	  log_error(_T("libusb error: %s [%i]\n"), libusb_result_to_string(res), res);
	  
	  lock.lock();
	  m_bRunning = false;	// This will signal EOF
	  m_hPacketEvent.notify_one();
	  lock.unlock();
	  
	  if (m_verbose)
		std::cerr << "Capture threading aborting due to libusb error: " << boost::this_thread::get_id() << std::endl;
	  goto capture_thread_exit;
	}
	
	if ((uint32_t)lLockSize < m_nReadLength)
	{
	  log_error(_T("Short bulk read: given %i bytes (expecting %lu)\n"), lLockSize, m_nReadLength);
	}
	
	lock.lock();
	
	++nCount;
	
	if (res == LIBUSB_ERROR_OVERFLOW)
	  ++m_nOverflows;
	
	uint32_t nRemaining = min(m_nBufferSize - m_nBufferItems, (uint32_t)lLockSize / RAW_SAMPLE_SIZE);
	
	bool bSignal = true;
	
	if (nRemaining > 0)
	{
	  //log_verbose(_T("Buffer write: count=%lu, start=%lu, items=%lu\n"), nCount, m_nBufferStart, m_nBufferItems);
	  
	  uint8_t* p = m_pUSBBuffer + (((m_nBufferStart + m_nBufferItems) % m_nBufferSize) * RAW_SAMPLE_SIZE);
	  
	  uint32_t nPart1 = (m_nBufferStart + m_nBufferItems) % m_nBufferSize;
	  uint32_t nSize1 = min(nRemaining, m_nBufferSize - nPart1);
	  
	  memcpy(p, pBuffer, nSize1 * RAW_SAMPLE_SIZE);
	  
	  uint32_t nResidual = nRemaining - nSize1;
	  if (nResidual > 0)
		  memcpy(m_pUSBBuffer, pBuffer + (nSize1 * 2), nResidual * RAW_SAMPLE_SIZE);
	  
	  m_nBufferItems += nRemaining;
	  
	  if ((m_bBuffering) && (m_nBufferItems >= (uint32_t)(m_recv_samples_per_packet + (float)m_nBufferSize * m_fBufferLevel)))	// Add additional amount that is about to be read back out in ReadPacket
	  {
		log_verbose(_T("Finished buffering (%lu/%lu) [#%lu]\n"), m_nBufferItems, m_nBufferSize, m_nReadPacketCount);
		m_bBuffering = false;
	  }
	  
	  bSignal = !m_bBuffering;
	}
	else
	{
	  log_error("rB");
	  report_status(RTL_STATUS_BUFFER_OVERRUN);
	  ++m_nBufferOverflowCount;
	}
	
	lock.unlock();
	
	if (bSignal)
	  m_hPacketEvent.notify_one();
  }
  
  if (m_verbose)
	std::cerr << "Capture threading exiting: " << boost::this_thread::get_id() << std::endl;
capture_thread_exit:
	SAFE_DELETE_ARRAY(pBuffer);
}
