#include <Python.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <complex.h>
#include <math.h>
#include <libusb.h>
#define IMPORT_QUISK_API
#include "chas_rx2.h"
#ifndef STANDALONE
#define DEBUG 0
#include "quisk.h"
#else
#define DEBUG 1
#endif

#define NAME_SIZE 16

libusb_device_handle *dev; // USB device handle for opening device

//int quisk_use_chas_rx1 = 0;	// WB4JFI: Added for Charleston Rx Ver1

static char sdr_name[NAME_SIZE];	// control item 1 (only used in this file 7 times)
static char sdr_serial[NAME_SIZE];	// item 2	(only used in this file five tmes)

///////////////////////////////////////////////////////////////////////////////////////////
////////////////	THIS IS NEW CODE FOR CHARLESTON SDR	///////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////

static int chas_rx1_freq = 7220000;     	// set Chas Rx initially to this freq
static int chas_rx1_sample_freq = NEXSDR_ADC_RATE;	// Charleston Rx RF sample frequency
static int chas_rx1_gain = 0;			// set Chas Rx PGA gain to this value
static int chas_rx1_rf_path = NEXSDR_RFPATH_LPFLNA; // set RF PATH TO LNA and LPF
static int chas_rx1_lna_range = NEXSDR_LNAGAIN_HIGH; // set LNA range to LOW
static int chas_rx1_lna_slope = NEXSDR_LNASLOPE_POS; // set LNA slope to positive
static int chas_rx1_lna_gain = 2047;		// set LNA gain initially to mid-range
static int chas_rx1_decim = 400;		// set decimation initially to 400

static int cur_freq, cur_gain, cur_path;	// current value of frequency and gain  
static int cur_lnarange, cur_lnaslope;		// current values storage
static int cur_lnagain;				// current values storage
static int cur_decimation;
static int sdr_close_read = 0;			// time to close read thread flag

static int chas_rx1_pwd = 0;			// Chas power-down storage, 0=oper, 1=pwr-dn
static int next_block_out;
static int next_buffer;
static long overrun;
static int running;
static int buffer_full[NEXSDR_NUM_BUFFERS];
short buffer[NEXSDR_NUM_BUFFERS*NEXSDR_BLOCK_SIZE*2];
float adc_adj;

/* AFEDRI8201 FIR Filter Coefficients (CIC droop compensation and LPF)	*/
static int fir1[32] = {		// parameters for 192ks/s
  -5,-44,-10,54,34,-66,-76,70,140,-50,-224,-11,312,132,-383,/*-321*/-322,401,580,-324,
  /*-895*/-896,102,/*1238*/1239,328,-1562,-1063,1785,2307,/*-1717*/-1718,
  /*-4696*/-4697,/*432*/431,11355,/*17121*/17122
};

/* AFEDRI8201 FIR FIlter Coefficients (LPF)	(2nd FIR, these are for 90% filter)*/
static int fir2[63] = {
  -4,-14,0,15,5,-15,-11,15,18,-11,-26,5,34,6,-39,-21,41,39,-36,-60,23,80,0,-95,
  -32,101,72,-94,-117,71,160,-28,-196,-33,216,111,-213,-200,179,292,-109,-375,0,
  435,147,-459,/*-328*/-329,430,536,-329,-759,138,983,173,-1194,-657,1377,1454,
  -1519,/*-3082*/-3083,1608,/*10299*/10300,/*14751*/14752
};

///////////////////////////////////////////////////////////////////////////////////////////
//////////////////	Charleston Internal Functions Only	///////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////////////
////////////////////////////	LOW-LEVEL COMMUNICATIONS	///////////////////////


////////////////////////	put_fpga_reg()		///////////////////////////////////
int
put_fpga_reg(unsigned char addr, unsigned char v) {
  unsigned char buffer[64];
  int transferred;
  int rv;

  memset(buffer,0x00,16);
  buffer[0] = 0x01;
  buffer[1] = addr;
  buffer[2] = v;

  rv  = libusb_bulk_transfer(dev, 0x01, buffer, 64, &transferred, 100);			// usb_bulk_write
  if (rv < 0) {
    fprintf(stderr,"Register write request failed (Send). [%d]\n",rv);
    return 0;
  }
  rv = libusb_bulk_transfer(dev, 0x81, buffer, 64, &transferred, 100);			// usb_bulk_read
  if (rv < 0) {
    fprintf(stderr,"Register write request failed (Receive). [%d]\n",rv);
    return 0;
  }
  return 1;
}

//////////////////////////	get_fpga_reg()		///////////////////////////////////
int
get_fpga_reg(unsigned char addr, unsigned char *v) {
  unsigned char buffer[64];
  int transferred;
  int rv;

  memset(buffer,0x00,64);
  buffer[0] = 0x02;
  buffer[1] = addr;

  rv  = libusb_bulk_transfer(dev, 0x01, buffer, 64, &transferred, 100);			// usb_bulk_write
  if (rv < 0) {
    fprintf(stderr,"get_fpga_reg read request failed (Send). [%d]\n",rv);
    fprintf(stderr," %s - %s\n",libusb_error_name(rv),libusb_strerror(rv));
    return 0;
  }
  rv = libusb_bulk_transfer(dev, 0x81, buffer, 64, &transferred, 100);			// usb_bulk_read
  if (rv < 0) {
    fprintf(stderr,"get_fpga_reg read request failed (Receive). [%d]\n",rv);
    fprintf(stderr," %s - %s\n",libusb_error_name(rv),libusb_strerror(rv));
    return 0;
  }
  *v = buffer[1];
  return 1;
}

//////////////////////////	get_fpga_stream()		///////////////////////////////////////
//////////////////////////		returns 0 if fail, nread if success	///////////////////////
//////////////////////////		nread has number of bytes read		///////////////////////
int
get_fpga_stream(char addr, unsigned char *data, int nbytes) {
  unsigned char buffer[64];
  int transferred;
  int nread;
  int rv;

  memset(buffer,0x00,16);
  buffer[0] = 0x06;
  buffer[1] = addr;
  buffer[4] = (nbytes >> 8) & 0xFF;
  buffer[5] = nbytes & 0xFF;

  rv  = libusb_bulk_transfer(dev, 0x01, buffer, 64, &transferred, 100);		// usb_bulk_write
  if (rv < 0) {
    fprintf(stderr,"get_fpga_stream 1: Block read request failed (Send). [%d]\n",rv);
    fprintf(stderr," %s - %s\n",libusb_error_name(rv),libusb_strerror(rv));
    return 0;
  }
  rv = libusb_bulk_transfer(dev, 0x81, buffer, 64, &transferred, 100);		// usb_bulk_read
  if (rv < 0) {
    fprintf(stderr,"get_fpga_stream 2: Block read request failed (Receive). [%d]\n",rv);
    fprintf(stderr," %s - %s\n",libusb_error_name(rv),libusb_strerror(rv));
    return 0;
  }
  rv  = libusb_bulk_transfer(dev, 0x86, data, nbytes & 0xFFFF, &transferred, 100); // usb_bulk_read
  if (rv < 0) {
    fprintf(stderr,"get_fpga_stream 3: Block read request failed (Send). [%d]\n",rv);
    fprintf(stderr," %s - %s\n",libusb_error_name(rv),libusb_strerror(rv));
    return 0;
  }
  nread = transferred;
  if (nread != nbytes)
    fprintf(stderr,"get_fpga_stream 4: Incomplete block read. [%d vs %d]\n",nread,nbytes);
  rv = libusb_bulk_transfer(dev, 0x81, buffer, 64, &transferred, 100);		// usb_bulk_read
  if (rv < 0) {
    fprintf(stderr,"get_fpga_stream 5: Block read request failed (Receive). [%d]\n",rv);
    fprintf(stderr," %s - %s\n",libusb_error_name(rv),libusb_strerror(rv));
    return 0;
  }
  return nread;
}

////////////////////////////	send_spi_command()	///////////////////////////////////
////////////////////////////	used in get and set 8201 functions	///////////////////
static int
send_spi_command(unsigned short cmd, unsigned short dat, unsigned short *resp) {
  unsigned char datlo;
  unsigned char dathi;
  if (!put_fpga_reg(REGADR_SPI_CMD_LO, (unsigned char) (cmd & 0xFF))) return 0;
  if (!put_fpga_reg(REGADR_SPI_CMD_HI, (unsigned char) ((cmd >> 8) & 0xFF))) return 0;
  if (!put_fpga_reg(REGADR_SPI_DAT_LO, (unsigned char) (dat & 0xFF))) return 0;
  if (!put_fpga_reg(REGADR_SPI_DAT_HI, (unsigned char) ((dat >> 8) & 0xFF))) return 0;
  if (!put_fpga_reg(REGADR_SPI_SEND, 0)) return 0;
  if (!get_fpga_reg(REGADR_SPI_DAT_LO, &datlo)) return 0;
  if (!get_fpga_reg(REGADR_SPI_DAT_HI, &dathi)) return 0;
  *resp = (((unsigned short) dathi) << 8) | datlo;
  return 1;
}

///////////////////////////	set_8201_register()	///////////////////////////////////////
static int
set_8201_register(int r, unsigned short v) {
  unsigned short resp;
  unsigned short cmd = (0x80 | (0x1F & r)) << 8;
  int rv = send_spi_command(cmd,v,&resp);
  if (rv != 1) {
    fprintf(stdout,"set_8201_register call failed.\n");
  }
  return rv;
}

////////////////////////////	get_8201_register()	///////////////////////////////////
//static int get_8201_register(int r, unsigned short *v) {
//	unsigned short cmd = (0x40 | (0x1F & r)) << 8;
//	int rv = send_spi_command(cmd,0x0000,v);
//	printf("%d\t%04X\n",r,*v);
//	if (rv != 1) {
//		fprintf(stdout,"get_8201_register call failed.\n");
//	}
//	return rv;
//}

////////////////////////////	set_8201_memory()	///////////////////////////////////
static int
set_8201_memory(int mem, int maddr, unsigned short v) {
  int cmd = ((0xA0 | (0x3 & mem)) << 8) | (maddr & 0xFF);
  unsigned short resp;
  int rv = send_spi_command(cmd,v,&resp);
  return rv;
}

///////////////////////////	get_8201_memory()	///////////////////////////////////
//static int get_8201_memory(int mem, int maddr, unsigned short *v) {
//	int cmd = ((0x60 | (0x3 & mem)) << 8) | (maddr & 0xFF);
//	int rv = send_spi_command(cmd,0x0000,v);
//	return rv;
//}


///////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////	MID-LEVEL FUNCTIONS	///////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////


///////////////////////	reset_8201()	resets the 8201		//////////////////////////
int reset_8201(void) {
  // Toggle the AFEDRI8201 reset flag (active low)
  unsigned char v;
  if (!get_fpga_reg(REGADR_CTRL,&v)) return 0;
  v = (v & ~CTRL_RESET) | (0 ? CTRL_RESET : 0 );
  if (!put_fpga_reg(REGADR_CTRL,v)) return 0;
  v = (v & ~CTRL_RESET) | (1 ? CTRL_RESET : 0 );
  if (!put_fpga_reg(REGADR_CTRL,v)) return 0;
  return 1;
}

/////////////////////	sync_8201() resyncs the 8201	  ////////////////////////////
int sync_8201(void) {
  // Toggle the AFEDRI8201 sync flag (active high)
  unsigned char v;
  if (!get_fpga_reg(REGADR_CTRL,&v)) return 0;
  v = (v & ~CTRL_SYNC) | (1 ? CTRL_SYNC : 0 );
  if (!put_fpga_reg(REGADR_CTRL,v)) return 0;
  v = (v & ~CTRL_SYNC) | (0 ? CTRL_SYNC : 0 );
  if (!put_fpga_reg(REGADR_CTRL,v)) return 0;
  return 1;
}

/////////////////////	reset_fifo_overrun()	///////////////////////////////////////
int reset_fifo_overrun(void) {
  // Reset FPGA FIFO overflow flags
  unsigned char v;
  if (!get_fpga_reg(REGADR_CTRL,&v)) return 0;
  v = (v & ~CTRL_OVF_RST) | (1 ? CTRL_OVF_RST : 0 );
  if (!put_fpga_reg(REGADR_CTRL,v)) return 0;
  v = (v & ~CTRL_OVF_RST) | (0 ? CTRL_OVF_RST : 0 );
  if (!put_fpga_reg(REGADR_CTRL,v)) return 0;
  return 1;
}

/////////////////////		reset_fifo0()	///////////////////////////////////////////
int reset_fifo0(void) {
  // Reset FPGA FIFO 0 (clear the FIFO)
  unsigned char v;
  if (!get_fpga_reg(REGADR_CTRL,&v)) return 0;
  v = (v & ~CTRL_FIFO0_RST) | (1 ? CTRL_FIFO0_RST : 0 );
  if (!put_fpga_reg(REGADR_CTRL,v)) return 0;
  v = (v & ~CTRL_FIFO0_RST) | (0 ? CTRL_FIFO0_RST : 0 );
  if (!put_fpga_reg(REGADR_CTRL,v)) return 0;
  return 1;
}

//////////////////////		reset_fifo1()		///////////////////////////////////
//////////////////////		dont think this is necessary	///////////////////////////
//////////////////////		LEAVE IN FOR NOW, part of init	///////////////////////////
int reset_fifo1(void) {
  // Reset FPGA FIFO 1 (clear the FIFO)
  unsigned char v;
  if (!get_fpga_reg(REGADR_CTRL,&v)) return 0;
  v = (v & ~CTRL_FIFO1_RST) | (1 ? CTRL_FIFO1_RST : 0 );
  if (!put_fpga_reg(REGADR_CTRL,v)) return 0;
  v = (v & ~CTRL_FIFO1_RST) | (0 ? CTRL_FIFO1_RST : 0 );
  if (!put_fpga_reg(REGADR_CTRL,v)) return 0;
  return 1;
}

//////////////////////////	set_lna_range		///////////////////////////////////
//////////////////////////	CALLED IN NexysSDR::start()	///////////////////////////
static void set_lna_range(void) {
  unsigned char v;
  if (!get_fpga_reg(REGADR_CTRL,&v)) return;
  v = (v & ~CTRL_HILO) | (chas_rx1_lna_range ? CTRL_HILO : 0 );
  put_fpga_reg(REGADR_CTRL,v);
  cur_lnarange = chas_rx1_lna_range;
}

///////////////////////////	set_lna_slope()		///////////////////////////////////
///////////////////////////	CALLED IN NexysSDR::start()	///////////////////////////
static void set_lna_slope(void) {
  unsigned char v;
  if (!get_fpga_reg(REGADR_CTRL,&v)) return;
  v = (v & ~CTRL_MODE) | (chas_rx1_lna_slope ? CTRL_MODE : 0 );
  put_fpga_reg(REGADR_CTRL,v);
  cur_lnaslope = chas_rx1_lna_slope;
}

///////////////		set_lna_gain()	(from NexysSDR.cc)	///////////////////////////
/////////////////////	NOTE:  need to redo this to remove min & max calls	///////////
static void set_lna_gain(void) {
  if(chas_rx1_lna_gain > 4095)
    chas_rx1_lna_gain = 4095;
  set_8201_register(11,chas_rx1_lna_gain);
  cur_lnagain = chas_rx1_lna_gain;
}

////////////////	set_rf_path()	(from NexysSDR.cc)	///////////////////////////
static void set_rf_path(void) {
  unsigned char v;
  if (!get_fpga_reg(REGADR_CTRL,&v)) return;
  v = (v & ~CTRL_RFSW) | (chas_rx1_rf_path ? CTRL_RFSW : 0 );
  put_fpga_reg(REGADR_CTRL,v);
  cur_path = chas_rx1_rf_path;
}

////////////////////////////	set_decimation_scale()	///////////////////////////////////
////////////////////////////	CALLED IN set_decimation_rate()	    ///////////////////////
int set_decimation_scale(int d) {
  int scale = 0;
  int shift = 0;
  int s, t;
  unsigned short v;
  double gain = 0, g;
  for (t=0;t<64;t++) {		// go through the shift values (t)
    for (s=0;s<64;s++) {	// go through the scale values (s)
      g = pow(d,5)*(((double) s)/32.0/(pow(2,t))); // calculate a gain value
#if DEBUG
      printf("calc Decim: %d, s: %d, t: %d, g: %f, GAIN: %f, abs(g*1000) %d, abs(gain*1000) %d\n",
	     d, s, t, g, gain,  abs((g-1.0)*1000), abs((gain-1.0)*1000));
#endif
      if ((g <= 1.0) && (abs((g-1.0)*1000) < abs((gain-1.0)*1000))) { // if we have a winner...
	gain = g;	// set gain to interim result
	shift = t;	// set shift to interim result
	scale = s;	// set scale to interim result
#if DEBUG
	printf("Interim Decim: %d, Scale: %d, Shift %d, Gain: %f\n", d, scale, shift, gain);
#endif
      }
    }	// try some more values of s (scale)
  }	// try some more values of t (shift)
#if DEBUG
  printf("FINAL Decim: %d, Scale: %d, Shift %d, Gain: %f\n", d, scale, shift, gain);
#endif
  v = (scale << 6) | shift;
  if (!set_8201_register(6, v)) return 0;
  return 1;
}

////////////////////////////	set_decimation_rate()	////////////////////////////////
////////////////////////////	sets BOTH decimation rate and scale	////////////////////
////////////////////////////	valid decimation rates are:	        //////////////////// 
////////////////////////////	1600,800,640,400,320,300,200,160	////////////////////
int set_decimation_rate(int d) {
  if (d%4 != 0) {
    fprintf(stderr,"Decimation must be divisible by 4.\n");
    return 0;
  }
  if (!set_8201_register(5, abs(d)/4)) return 0;
  if (!set_decimation_scale(abs(d)/4)) return 0;
  cur_decimation = d;
  return 1;
}

//////////////////////////	set_freq_chas_rx1()	///////////////////////////////////
static void set_freq_chas_rx1(void)
{
  unsigned long fx;
  unsigned short fx0, fx1;

  fx = (long)((double)chas_rx1_freq/chas_rx1_sample_freq*pow(2.0, 32.0));	// calc FTW
  fx0 = (unsigned short) (fx & 0xFFFF);		// setup bits 15-0
  fx1 = (unsigned short) ((fx >> 16) & 0xFFFF);	// setup bits 31-16
  set_8201_register(1, fx0);			// send the low bits
  set_8201_register(2, fx1);			// and the high bits
  cur_freq = chas_rx1_freq;			// update current freq storage
}

//////////////////////////	set_gain_chas_rx1	///////////////////////////////////
// gain button index 0 = PGA gain 1.00, 0 dB, 	8201 code = 000
// gain button index 1 = PGA gain 1.14, 1 dB,	8201 code = 100
// gain button index 2 = PGA gain 1.33, 2.5 dB,	8201 code = 010
// gain button index 3 = PGA gain 1.60, 4 dB,	8201 code = 110
// gain button index 4 = PGA gain 2.00, 6 dB,	8201 code = 001
// gain button index 5 = PGA gain 2.67, 8.5 dB,	8201 code = 101
// gain button index 6 = PGA gain 4.00, 12dB,	8201 code = 011

static void set_gain_chas_rx1(void)
{
  int code = 0;			// preset code variable to 0

  switch (chas_rx1_gain) {
  case 0:			// button index 0, gain is 0
    code = 0x00; break;		// 8201 code is 000 (binary)
  case 1:			// button index 1, gain is 1dB
    code = 0x10; break;	        // 8201 code is 100 (shifted two bits)
  case 2:			// button index 2, gain is 2.5dB
    code = 0x08; break;		// 8201 code is 010
  case 3:			// button index 3, gain is 4dB
    code = 0x18; break;		// 8201 code is 110
  case 4:			// button index 4, gain is 6dB
    code = 0x04; break;		// 8201 code is 001
  case 5:			// button index 5, gain is 8.5dB
    code = 0x14; break;		// 8201 code is 101
  case 6:			// button index 6, gain is 12dB
    code = 0x0C; break;		// 8201 code is 011
  default:			// shold not get here, but...
    code = 0x00; break;		// set gain to 0 anyway
  }
  code = (code & 0x001C) | chas_rx1_pwd;	// add power-down flag to gain "code"
  set_8201_register(12,code);		// send it off to the board
  cur_gain = chas_rx1_gain;		// update current gain storage
}

////////////////////////	NOTE:  WAS A SEPARATE READ THREAD,			///////////
////////////////////////	fiforead()	CALLS get_fpga_stream()			///////////
////////////////////////	USED IN: NexysSDR::start() to create read thread	///////////
void fiforead(void)
{
  short v[NEXSDR_BLOCK_SIZE*2];
  int i;
  int fifobytes;
  //#if DEBUG
  //	printf("Separate fiforead thread started\n");
  //	printf("fiforead() running: %d\n", running);
  //#endif
  if (running) {
    fifobytes = get_fpga_stream(REGADR_FIFO0,(unsigned char *) v,
				NEXSDR_BLOCK_SIZE*sizeof(short)*2); 		// read block from FGPA
    // printf("Fiforead: Get_fpga = %d, next_buffer = %d\n", fifobytes, next_buffer);
    if(fifobytes) {
      if (buffer_full[next_buffer] == 0) {	// If space is available, store data
	for (i=0;i<NEXSDR_BLOCK_SIZE*2;i++)	// convert data to float
	  buffer[next_buffer*NEXSDR_BLOCK_SIZE*2+i] = (v[i]); // << 4) & 0xFFF0;
	//			printf("filled buffer %d\n", next_buffer);
	buffer_full[next_buffer] = 1;		// show this buffer now full
	next_buffer = (next_buffer+1)%NEXSDR_NUM_BUFFERS;  // point to next buffer
      }  else {					// otherwise, if space not available
	overrun++;				// increment buffer overrun count
	printf("Overruns: %ld\n", overrun);
      }
    }
  }
}

//////////////////////////	open_USB()  opens the USB device //////////////////////////
//////////////////////////	returns 1=open OK, 0=open fail	 //////////////////////////
int open_USB(void) {
  int config;
  int rv;
  unsigned char cdata[256];
  libusb_device **list;
  libusb_device *found = NULL;
  struct libusb_device_descriptor desc;
  ssize_t cnt ;
  ssize_t i = 0;
  int err = 0;
  int rq1 = 0;
  int rq2 = 0;

  rv = libusb_init(NULL);		   // initialize the USB interface
  if(rv) {
    fprintf(stderr,"open_USB: %s - %s",libusb_error_name(rv),libusb_strerror(rv));
    return(0);
  }
  
  // discover devices
  cnt = libusb_get_device_list(NULL, &list);
  if (cnt < 0) {
    fprintf(stderr,"No USB devices found!\n");
    return(0);
  }
  for (i = 0; i < cnt; i++) {
    libusb_device *device = list[i];
    libusb_get_device_descriptor(device,&desc);
    if ((desc.idVendor == DIGILENT_VENDOR_ID) &&
	(desc.idProduct == DIGILENT_PRODUCT_ID)) {
#if DEBUG
	printf("Found Digilent Board\n");
#endif
	found = device;
      break;
    }
  }
  if (found) {
    err = libusb_open(found, &dev);
    if (err) {
      fprintf(stderr,"open_USB[%d]: %s - %s\n",err,libusb_error_name(err),libusb_strerror(err));
      return(0);
    }
    err = libusb_get_configuration(dev,&config);
    if (err) {
      fprintf(stderr,"get config[%d]: %s - %s\n",err,libusb_error_name(err),libusb_strerror(err));
    }
    //    err = libusb_set_configuration(dev,config);
    //if (err) {
    //  fprintf(stderr,"set config[%d]: %s - %s\n",err,libusb_error_name(err),libusb_strerror(err));
    //}
    if (libusb_kernel_driver_active(dev,0)) printf("USB kernel driver active\n");
    err = libusb_claim_interface(dev,0);
    if (err) {
      fprintf(stderr,"claim interface[%d]: %s - %s\n",err,libusb_error_name(err),libusb_strerror(err));
    }
    //err = libusb_reset_device(dev);
    //if (err) {
    //  fprintf(stderr,"reset[%d]: %s - %s\n",err,libusb_error_name(err),libusb_strerror(err));
    //}
#if DEBUG
    printf("USB Open Success\n");
    printf("bus number %d\n",libusb_get_bus_number(found));
    printf("bus address %d\n",libusb_get_device_address(found));
    switch(libusb_get_device_speed(found)) {
    case LIBUSB_SPEED_LOW:
      printf("Low speed usb device\n");
      break;
    case LIBUSB_SPEED_FULL:
      printf("Full speed usb device\n");
      break;
    case LIBUSB_SPEED_HIGH:
      printf("High speed usb device\n");
      break;
    case LIBUSB_SPEED_SUPER:
      printf("Super speed usb device\n");
      break;
    case LIBUSB_SPEED_UNKNOWN:
    default:
      printf("Unknown usb device speed.\n");
      break;
    }
    printf("Using config %d\n",config);
#endif
    usleep(1000);
    memset(cdata,0x00,16);
    rv  = libusb_control_transfer(dev, 0xC0, 0xE4, 0x00, 0x00, cdata, 13, 100);
    if (rv < 0) {
      fprintf(stderr,"Vendor request failed (First Request). [%d]\n",rv);
      libusb_release_interface(dev,0);
      libusb_reset_device(dev);
      libusb_exit(NULL);
      exit(1);
      rq1 = 1;
    }
#if DEBUG
    else
      fprintf(stderr,"Vendor request SUCCESS (First Request). [%d]\n",rv);
#endif
    rv = libusb_control_transfer(dev, 0xC0, 0xE6, 0x00, 0x00, cdata, 4, 100);
    if (rv < 0)
      {
	fprintf(stderr,"Vendor request failed (Second Request). [%d]\n",rv);
	rq2 = 1;
      }
#if DEBUG
    else
      fprintf(stderr,"Vendor request SUCCESS (Second Request). [%d]\n",rv);
#endif
    
    if(rq1 || rq2)
      return 0;
    else
      return 1;
  }
  else
    {
      fprintf(stderr,"Vendor Open failed.\n");
      return 0;
    }

  libusb_free_device_list(list, 1);
#if DEBUG
  fprintf(stderr,"Digilent Open failed.\n");
#endif
  return 0;
}

////////////////////////	init_chas_rx1()		///////////////////////////////////////////
////////////////////////	actually, initializes hardware		///////////////////////////
////////////////////////	returns 0 if error, 1 if OK		///////////////////////////
int init_chas_rx1(void)
{
  int i, j;

#if DEBUG
  printf("Init Chas Rx1: starting USB\n");
#endif

  int rv = open_USB();				// Open connection to the board
  if (!rv) {
    printf("\nInit Chas Rx1: Chas Init failed!");
    return 0;
  }

  usleep(1000);
#if DEBUG
  printf("Init Chas Rx1: Initializing Charleston Receiver\n");
#endif
  // Set control lines
  sync_8201();
  sync_8201();
  sync_8201();
  set_rf_path();	// not in 8201, go ahead and set up RF path HW
  set_lna_slope();	// not in 8201, go ahead and set up LNA slope HW
  set_lna_range();	// not in 8201, go ahead and set up LNA range HW
  reset_8201();		// Issue reset to AFEDRI8201

  // Init the AFEDRI8201	
  set_8201_register(0, 0x0003); // MCLK DIV=0, Data Out MODE=1
  set_8201_register(1, 0x6666); // NCO FREQUENCY bits 15-0
  set_8201_register(2, 0x0266); // NCO FREQUENCY bits 31-16
  set_8201_register(3, 0x0000); // NCO PHASE bits 15-0
  set_8201_register(4, 0x0000); // NCO PHASE bits 31-16
  //	set_8201_register(5, 0x0028); // CIC DECIMATION_RATE=160/4=40 for 76.8 clock, 480k
  //	set_8201_register(5, 0x0032); // CIC DECIMATION_RATE=200/4=50 for 76.8 clock, 384k
  //	set_8201_register(5, 0x004B); // CIC DECIMATION_RATE=300/4=75 for 76.8 clock, 256k
  //	set_8201_register(5, 0x0050); // CIC DECIMATION_RATE=320/4=80 for 76.8 clock, 240k
  set_8201_register(5, 0x0064); // CIC DECIMATION_RATE=400/4=100 for 76.8 clock, 192k
  //	set_8201_register(5, 0x00A0); // CIC DECIMATION_RATE=640/4=160 for 76.8 clock, 120k
  //	set_8201_register(5, 0x00C8); // CIC DECIMATION_RATE=800/4=200 for 76.8 clock, 96k
  set_8201_register(6, 0x0819); // SCALE=21, SHIFT=26
  set_8201_register(7, 0x0080); // BASE_ADDR=0, NCOEFF=32, FIR1 MODE=0
  set_8201_register(8, 0x00FC); // BASE_ADDR=0, NCOEFF=63, FIR2A MODE=0
  set_8201_register(9, 0x00FC); // BASE_ADDR=0, NCOEFF=63, FIR2B MODE=0
  set_8201_register(10,0x0000); // Set FIR1 and FIR2 into non-interleaved mode
  set_8201_register(11,0x0000); // AUXILIARY DAC=0x000
  set_8201_register(12,0x000C); // PGA GAIN=12dB, PWD=0
  set_8201_register(0, 0x0003); // MCLK DIV=0, Data Out MODE=1

  set_decimation_rate(400);

  // Set FIR filter coefficient memory
  for (j=0;j<32;j++) set_8201_memory(0,j,fir1[j]);
  for (j=0;j<63;j++) set_8201_memory(1,j,fir2[j]);
  for (j=0;j<63;j++) set_8201_memory(2,j,fir2[j]);

  // Clear the FIFOs
  reset_fifo0();
  reset_fifo1();
  reset_fifo_overrun();

  set_lna_gain();
  chas_rx1_gain = 6;
  set_gain_chas_rx1();

  // Clear buffer full flags
  for (i=0;i<NEXSDR_NUM_BUFFERS;i++) buffer_full[i] = 0;
  adc_adj = ADC_MULT;
  running = 1;
  overrun = 0;
  next_block_out = 0;
  next_buffer = 0;
  return 1;
}

///////////////////////////////////////////////////////////////////////////////////////////
///////////////////	FOLLOWING IS THE CHAS OPEN CODE			///////////////////
///////////////////	called with * name = USB port name,		///////////////////
///////////////////	buf points to char string to put name, etc in	///////////////////
///////////////////	bufsize is the size of the buf buffer		///////////////////

void quisk_open_chas_rx1(/*const char * name,*/ char * buf, int bufsize)
{
  dev = NULL;	// initialize the device

  if (!init_chas_rx1()) {	//	open the Charleston Receiver USB and init HW
    strncpy(buf, "Open Chas Rx1: ", bufsize);	// Uh oh, could not open Chas Rx USB
    strncat(buf, strerror(errno), bufsize);
    printf("Quisk Open Chas Rx1: Failed\n");
    return;
  } else {				// USB and HW opened OK and initialized...
    strcpy(sdr_name, "Charleston");	// Show that we are using a Charleston receiver
    strcpy(sdr_serial, "Rx1");		// and the type of Charleston receiver is Rx1
    quisk_stop_chas_rx1();		// Charleston - set to idle
    usleep(1000);			// Charleston  wait
    set_freq_chas_rx1();		// set the initial operating frequency
    snprintf(buf, bufsize, "from %s version %s.", sdr_name, sdr_serial);

#if DEBUG
    printf ("%s\n", buf);
#endif
    sdr_close_read = 0;			// clear the close-thread flag
  }
}

///////////////////////////////////////////////////////////////////////////////////////////
//////////////////	External/Public Functions follow		///////////////////
//////////////////	THESE FUNCTIONs ARE THE ACCESS TO OUTSIDE WORLD	///////////////////
///////////////////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////
// The API requires at least two Python functions for Open and Close, plus
// additional Python functions as needed.  And it requires exactly three
// C funcions for Start, Stop and Read samples.  Quisk runs in two threads,
// a GUI thread and a sound thread.  You must not call the GUI or any Python
// code from the sound thread.  You must return promptly from functions called
// by the sound thread.
//
// The calling sequence is Open, Start, then repeated calls to Read, then
// Stop, then Close.

// Start of Application Programming Interface (API) code:

//////////////////////////	Start sample capture; called from the sound thread.	////////
////////////////////////////////////////////////////////////////////////////////////////
static void quisk_start_chas_rx1(void)
{
  if(dev != 0) {
    if (running != 1) {
      chas_rx1_pwd = 0;			// change power-down flag to OFF
      set_gain_chas_rx1();		// and send to 8201 via set gain function
      running = 1;
    }
  }
  return; 
}

/////////////////////////	Stop sample capture; called from the sound thread.	////////
////////////////////////////////////////////////////////////////////////////////////////
static void quisk_stop_chas_rx1(void)
{
  if(dev != 0) {
    if (running != 0)
      chas_rx1_pwd = 1;			// change power-down flag to ON
    set_gain_chas_rx1();		// and send to 8201 via set gain function
    running = 0;
  }
  return;
}

////////////////////////	quisk_read_chas_rx1() (gathers data)	///////////////////
////////////////////////	THis is the NON-BLOCKING code to check	///////////////////
////////////////////////	if samples are ready. from sound thread	///////////////////
static int quisk_read_chas_rx1 (complex * cSamples)
{
  int i, j;
  int length = 0;

  if(!dev) {
    usleep(5000);
#if DEBUG
    printf("Quisk_read_chas exit: no device\n");
#endif
    return 0;
  }
#if DEBUG
  printf("Called quisk_read_chas\n");
#endif
  fiforead();
#if DEBUG
  printf("Next_block_out = %d, value = %d\n", next_block_out, buffer_full[next_block_out]);
#endif
  if (buffer_full[next_block_out] == 1) {// buffer_full is an array showing which 
    // of the 16 buffers are full of data
    j=0;
    for (i=0;i<NEXSDR_BLOCK_SIZE;i++) {	// copy samples from short iq array buffer[j, j+1]
      // into complex output_items[] array
      cSamples[length] = adc_adj * buffer[(next_block_out*NEXSDR_BLOCK_SIZE*2+j)] + 
	adc_adj * buffer[(next_block_out*NEXSDR_BLOCK_SIZE*2 + j + 1)] * I;
      length++;
      j+=2;
    }
    buffer_full[next_block_out] = 0;	// show present buffer_full flag is now empty
    next_block_out = (next_block_out+1)%NEXSDR_NUM_BUFFERS;	// increment to next block out.
  }
  else {		// if no samples are ready...
    usleep(10);		// just wait
  }

  // now check for changes requested during GUI thread
  if (cur_freq != chas_rx1_freq)		// check frequency
    set_freq_chas_rx1();			// freq changed, update it
  if (cur_gain != chas_rx1_gain) 		// check gain
    set_gain_chas_rx1();			// gain changed, update it
  if (cur_path != chas_rx1_rf_path)		// check RF path (Preamp/Bypass)
    set_rf_path();				// Path changed, update it
  if (cur_lnarange != chas_rx1_lna_range) 	// check LNA range (HI/LOW)
    set_lna_range();				// LNA range changed, update it
  if (cur_lnaslope != chas_rx1_lna_slope)	// check LNA slope (Pos/neg)
    set_lna_slope();				// LNA slope changed, update it
  if (cur_lnagain != chas_rx1_lna_gain)		// check LAN gain (0-4095)
    set_lna_gain();				// LNA gain changed, update it
  if (cur_decimation != chas_rx1_decim) {	// check decimation for a change
    quisk_stop_chas_rx1();			// changed, first turn off receiver
    set_decimation_rate(chas_rx1_decim);	// next, change decimation rate
    quisk_start_chas_rx1();			// and turn receiver back on
  }
  return length;
}

///////////////////////////////////////////////////////////////////////////////////////////
/////////////////	close_samples()				 //////////////////////////
/////////////////	no parameters passed or returned	 //////////////////////////
/////////////////	called from the GUI thread.		 ///////////////////////////
static PyObject * close_samples(PyObject * self, PyObject * args)
{
#if DEBUG
  printf("closing USB\n");
#endif
  if (!PyArg_ParseTuple (args, ""))
    return NULL;
  if (dev != NULL) {
    quisk_stop_chas_rx1();	// turn OFF hardware sampling first
    running = 0;		// next, stop all processes & threads
    libusb_release_interface(dev,0);
    libusb_close(dev);		// close the USB port
    dev = NULL;
    usleep(1000);
    sdr_close_read = 1;		// show the read thread time to shutdown
  }
  Py_INCREF (Py_None);
  return Py_None;
}

///////////////////////////////////////////////////////////////////////////////////////////
///////////////////	FOLLOWING IS THE CHAS OPEN CODE		///////////////////////////
/////////////////// Called to open the sample source; 	        ///////////////////////////
///////////////////	called from the GUI thread.	        ///////////////////////////
static PyObject * open_samples(PyObject * self, PyObject * args)
{
  char buf[128];

  if (!PyArg_ParseTuple (args, ""))
    return NULL;

#ifndef STANDALONE
  // Record our C-language Start/Stop/Read functions for use by sound.c.
  quisk_sample_source(&quisk_start_chas_rx1, &quisk_stop_chas_rx1, &quisk_read_chas_rx1);
#endif

  quisk_open_chas_rx1(buf, 128);	// Charleston RX1 specific
  return PyString_FromString(buf);	// return a string message
}


// Miscellaneous functions needed by the Charleston board; called from the GUI thread as
// a result of button presses.

//////////////	Set the receive frequency; called from the GUI thread.	///////////////////
static PyObject * freq_chas_rx1(PyObject * self, PyObject * args)
{
  if (!PyArg_ParseTuple (args, "i", &chas_rx1_freq))
    return NULL;
  Py_INCREF (Py_None);
  return Py_None;
}

//***************	NOT USED FOR CHARLESTON BOARD	***************************************
//////////////	Set the preamp gain; called from the GUI thread.		///////////////////
//////////////		NULL THIS OUT FOR NOW, USE PGA setting instead		///////////////////
static PyObject * gain_chas_rx1(PyObject * self, PyObject * args)	// Called from GUI thread
{	// gstate == 0:  Gain must be 0, -10, -20, or -30
	// gstate == 1:  Attenuator is on  and gain is 0 to 127 (7 bits)
	// gstate == 2:  Attenuator is off and gain is 0 to 127 (7 bits)

  //	if (!PyArg_ParseTuple (args, "ii", &chas_rx1_gstate, &chas_rx1_gain))
  //		return NULL;
  Py_INCREF (Py_None);
  return Py_None;
}

//////////////	Set the RF path (PREAMP & LNA or BYPASS);	///////////////////////////////
//////////////	 called from the GUI thread.			///////////////////////////////
static PyObject * rf_path_chas_rx1(PyObject * self, PyObject * args)
{
  if (!PyArg_ParseTuple (args, "i", &chas_rx1_rf_path))
    return NULL;
  Py_INCREF (Py_None);
  return Py_None;
}

//////////////	Set the LNA RANGE HIGH or LOW;		///////////////////////////////////////
//////////////	 called from the GUI thread.		///////////////////////////////////////
static PyObject * lna_range_chas_rx1(PyObject * self, PyObject * args)
{
  if (!PyArg_ParseTuple (args, "i", &chas_rx1_lna_range))
    return NULL;
  Py_INCREF (Py_None);
  return Py_None;
}

//////////////	Set the LNA SLOPE, POS or NEG;		///////////////////////////////////////
//////////////	 called from the GUI thread.		///////////////////////////////////////
static PyObject * lna_slope_chas_rx1(PyObject * self, PyObject * args)
{
  if (!PyArg_ParseTuple (args, "i", &chas_rx1_lna_slope))
    return NULL;
  Py_INCREF (Py_None);
  return Py_None;
}

//////////////	Set the LNA GAIN (0-4095);		///////////////////////////////////////
//////////////	 called from the GUI thread.		///////////////////////////////////////
static PyObject * lna_gain_chas_rx1(PyObject * self, PyObject * args)
{
  if (!PyArg_ParseTuple (args, "i", &chas_rx1_lna_gain))
    return NULL;
  Py_INCREF (Py_None);
  return Py_None;
}

//////////////	Set the 8201 PGA GAIN (0-7);		///////////////////////////////////////
//////////////	 called from the GUI thread.		///////////////////////////////////////
static PyObject * pga_gain_chas_rx1(PyObject * self, PyObject * args)
{
  if (!PyArg_ParseTuple (args, "i", &chas_rx1_gain))
    return NULL;
  Py_INCREF (Py_None);
  return Py_None;
}


/////////////	Set the 8201 Decimation rate;		///////////////////////////////////////
//////////////	 called from the GUI thread.		///////////////////////////////////////
static PyObject * set_decimation(PyObject * self, PyObject * args)
{
  if (!PyArg_ParseTuple (args, "i", &chas_rx1_decim))
    return NULL;
  Py_INCREF (Py_None);
  return Py_None;
}

// Functions callable from Python are listed here:
static PyMethodDef QuiskMethods[] = {
  {"open_samples", open_samples, METH_VARARGS, "Open the Charleston Rx1."},
  {"close_samples", close_samples, METH_VARARGS, "Close the Charleston Rx1."},
  {"freq_chas_rx1", freq_chas_rx1, METH_VARARGS, "Set the frequency of the Chas Rx1"},
  {"gain_chas_rx1", gain_chas_rx1, METH_VARARGS, "Set the gain of the Chas Rx1"},
  {"rf_path_chas_rx1", rf_path_chas_rx1, METH_VARARGS, "Set Preamp/Bypass of Chas Rx1"},
  {"lna_range_chas_rx1", lna_range_chas_rx1, METH_VARARGS, "Set LAN HI/LO of Chas Rx1"},
  {"lna_slope_chas_rx1", lna_slope_chas_rx1, METH_VARARGS, "Set Preamp Slope Chas Rx1"},
  {"lna_gain_chas_rx1", lna_gain_chas_rx1, METH_VARARGS, "Set LNA Gain of Chas Rx1"},
  {"pga_gain_chas_rx1", pga_gain_chas_rx1, METH_VARARGS, "Set 8201 PGA Gain, Chas Rx1"},
  {"set_decimation", set_decimation, METH_VARARGS, "Set the decimation of the Chas Rx1"},
  {NULL, NULL, 0, NULL}		/* Sentinel */
};

// Initialization, and registration of public symbol "initchas_rx1":
PyMODINIT_FUNC initchas_rx1 (void)
{
  Py_InitModule ("chas_rx1", QuiskMethods);
  // Import pointers to functions and variables from module _quisk
#ifndef STANDALONE
  if (import_quisk_api()) {
    printf("Failure to import pointers from _quisk\n");
    return;		//Error
  }
#endif
}

#ifdef STANDALONE
///////////// STANDALONE testing without Python
int
main(int argc, char *argv[]) {
  int i;
  char buf[128];
  complex cSamples[NEXSDR_BLOCK_SIZE];

  quisk_open_chas_rx1(buf,128);
  quisk_start_chas_rx1();
  for (i=0; i<1;i++)
    quisk_read_chas_rx1 (cSamples);
  quisk_stop_chas_rx1();
  libusb_close(dev);
  for (i=0; i<NEXSDR_BLOCK_SIZE;i++)
    printf("%d (%f, %f)\n",i,creal(cSamples[i]),cimag(cSamples[i]));
  return(0);
}
#endif
