#include <Python.h>			// needed for SDR-IQ support
#include <sys/types.h>	// NOT needed for SDR-IQ support
#include <sys/stat.h>		// NOT needed for SDR-IQ support
#include <time.h>			// NOT NEEDED for SDR-IQ support
#include <fcntl.h>			// needed for SDR-IQ support
#include <termios.h>		// needed for SDR-IQ support
#include <stdio.h>		// NOT NEEDED for SDR-IQ support
#include <string.h>		// NOT NEEDED for SDR-IQ support
#include <errno.h>		// NOT NEEDED for SDR-IQ support
#include <stdlib.h>		// NOT NEEDED for SDR-IQ support
#include <unistd.h>		// NOT NEEDED for SDR-IQ support
#include <complex.h>		// needed for SDR-IQ support
#include <math.h>			// TF added for Charleston Rx1 support
#include "quisk.h"
#include "chas_rx1.h"

#define DEBUG		0

#define NAME_SIZE		16

//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;


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


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


////////////////////////	put_fpga_reg()		///////////////////////////////////
int put_fpga_reg(unsigned char addr, unsigned char v) {

	char buffer[64];

	sem_wait(&iflock);

	memset(buffer,0x00,16);
	int rv;

	buffer[0] = 0x01;
	buffer[1] = addr;
	buffer[2] = v;

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

//////////////////////////	get_fpga_reg()		///////////////////////////////////
int get_fpga_reg(unsigned char addr, unsigned char *v) {

	char buffer[64];

	sem_wait(&iflock);

	memset(buffer,0x00,16);
	int rv;

	buffer[0] = 0x02;
	buffer[1] = addr;

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

//////////////////////////	get_fpga_stream()		///////////////////////////////////////
//////////////////////////		returns 0 if fail, nread if success	///////////////////////
//////////////////////////		nread has number of bytes read		///////////////////////
int get_fpga_stream(char addr, char *data, int nbytes) {

	char buffer[64];

	sem_wait(&iflock);

	memset(buffer,0x00,16);
	int rv;

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

	rv  = usb_bulk_write(dev, 0x01, buffer, 64, 100);					// usb_bulk_write
	if (rv < 0) {
		fprintf(stderr,"get_fpga_stream: Block read request failed (Send). [%d]\n",rv);
		sem_post(&iflock);
		return 0;
	}
	rv = usb_bulk_read(dev, 0x01, buffer, 64, 100);						// usb_bulk_read
	if (rv <= 0) {
		fprintf(stderr,"get_fpga_stream: Block read request failed (Receive). [%d]\n",rv);
		sem_post(&iflock);
		return 0;
	}
	int nread = rv  = usb_bulk_read(dev, 0x06, data, nbytes & 0xFFFF, 100); // usb_bulk_read
	if (rv < 0) {
		fprintf(stderr,"get_fpga_stream: Block read request failed (Send). [%d]\n",rv);
		sem_post(&iflock);
		return 0;
	}
	if (nread != nbytes)
			fprintf(stderr,"get_fpga_stream: Incomplete block read. [%d vs %d]\n",nread,nbytes);
	rv = usb_bulk_read(dev, 0x01, buffer, 64, 100);						// usb_bulk_read
	if (rv <= 0) {
		fprintf(stderr,"get_fpga_stream: Block read request failed (Receive). [%d]\n",rv);
		sem_post(&iflock);
		return 0;
	}
	sem_post(&iflock);
	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("\ncalc 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("\nInterim 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("\nFINAL 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,( 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 ubusses, udevices;
	int rv, rq1=0, rq2=0;
	char buffer[16];
	struct usb_bus *bus;
	struct usb_device *digidev = 0;

	usb_init();									// initialize the USB interface
    ubusses = usb_find_busses();				// now load the bus structure
    udevices = usb_find_devices();				// load the devices structure

#if	DEBUG
	printf("USB found: %d Busses, %d Devices\n", ubusses, udevices);
#endif

	for (bus = usb_get_busses(); bus; bus = bus->next) {
		for (udev = bus->devices; udev; udev = udev->next) {
			if ((udev->descriptor.idVendor == DIGILENT_VENDOR_ID) &&
			    (udev->descriptor.idProduct == DIGILENT_PRODUCT_ID))
				{
				digidev = udev;
#if DEBUG
				printf("Found Digilent Board\n");
#endif
				}
		}
	}

	dev = NULL;
	if (digidev != 0)
		{
		dev = usb_open(digidev);					// FINALLY, open the USB device
		if (dev != NULL)
			{
#if DEBUG
			printf("USB Open Success\n");
#endif
			usleep(1000);
			sem_wait(&iflock);
			memset(buffer,0x00,16);
			rv  = usb_control_msg(dev, 0xC0, 0xE4, 0x00, 0x00, buffer, 13, 100);
			if (rv < 0)
				{
				fprintf(stderr,"Vendor request failed (First Request). [%d]\n",rv);
				sem_post(&iflock);
				rq1 = 1;
				}
#if DEBUG
			else
				fprintf(stderr,"Vendor request SUCCESS (First Request). [%d]\n",rv);
#endif
			rv = usb_control_msg(dev, 0xC0, 0xE6, 0x00, 0x00, buffer, 4, 100);
			if (rv < 0)
				{
				fprintf(stderr,"Vendor request failed (Second Request). [%d]\n",rv);
				sem_post(&iflock);
				rq2 = 1;
				}
#if DEBUG
			else
				fprintf(stderr,"Vendor request SUCCESS (Second Request). [%d]\n",rv);
#endif
			sem_post(&iflock);

			if(rq1 || rq2)
				return 0;
			else
				return 1;

			}
		else
			{
			fprintf(stderr,"Vendor Open failed.\n");
			return 0;
			}
		}
#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
    sem_init(&iflock,0,1);					// inititalize the locking

    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("\nQuisk 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;
			}
		}
	else
		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;
		}
	else
		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("\nQuisk_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(!dev)
//		return Py_None;																	//////////// WB4JFI

#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
		usb_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(!dev)
//		return PyString_FromString(0);		// return a string message						///////////////// WB4JFI

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

// Record our C-language Start/Stop/Read functions for use by sound.c.
	pt_sample_start = &quisk_start_chas_rx1;
	pt_sample_stop = &quisk_stop_chas_rx1;
	pt_sample_read = &quisk_read_chas_rx1;

	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);
}
