--- /dev/null
+++ b/charleston/chas_rx2.c
@@ -0,0 +1,996 @@
+#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
--- /dev/null
+++ b/charleston/chas_rx2.h
@@ -0,0 +1,47 @@
+#define DIGILENT_PRODUCT_ID 0x0005
+#define DIGILENT_VENDOR_ID  0x1443
+
+#define NEXSDR_RFPATH_BYPASS 0
+#define NEXSDR_RFPATH_LPFLNA 1
+#define NEXSDR_LNAGAIN_HIGH  1
+#define NEXSDR_LNAGAIN_LOW   0
+#define NEXSDR_LNASLOPE_POS  0
+#define NEXSDR_LNASLOPE_NEG  1
+#define NEXSDR_ADC_RATE      76800000
+
+#define	A8201_REG_MCLKMODE	0x00
+#define	A8201_REG_CIC		0x05
+#define	A8201_REG_FIR1		0x07
+#define	A8201_REG_FIR2A		0x08
+#define	A8201_REG_FIR2B		0x09
+#define	A8201_REG_INTLV		0x0A
+#define	A8201_REG_DAC		0x0B
+#define	A8201_REG_PGAPD		0x0C
+
+#define A8201_PWD		0x01
+
+#define REGADR_FIFO0      0x00
+#define REGADR_FIFO1      0x08
+#define REGADR_CTRL       0x10
+#define REGADR_SPI_CMD_LO 0x1A
+#define REGADR_SPI_CMD_HI 0x1B
+#define REGADR_SPI_DAT_LO 0x18
+#define REGADR_SPI_DAT_HI 0x19
+#define REGADR_SPI_SEND   0x1C
+#define REGADR_FIFO_OVF   0x20
+
+#define CTRL_RFSW         0x01
+#define CTRL_HILO         0x02
+#define CTRL_MODE         0x04
+#define CTRL_RESET        0x08
+#define CTRL_SYNC         0x10
+#define CTRL_OVF_RST      0x20
+#define CTRL_FIFO0_RST    0x40
+#define CTRL_FIFO1_RST    0x80
+
+#define NEXSDR_BLOCK_SIZE 4096
+#define NEXSDR_NUM_BUFFERS 16
+#define ADC_MULT		65536
+
+static void quisk_start_chas_rx1(void);
+static void quisk_stop_chas_rx1(void);
--- /dev/null
+++ b/charleston/__init__.py
@@ -0,0 +1 @@
+#
--- /dev/null
+++ b/charleston/PKG-INFO
@@ -0,0 +1,26 @@
+Metadata-Version: 1.0
+Name: charleston
+Version: 1.0
+Summary: This module adds support for the Charleston RX-1 hardware to QUISK
+Home-page: www.amrad.org
+Author: Terry Fox
+Author-email: tfox@knology.net
+License: GPL
+Description: This module adds support for the Charleston Rx-1 SDR receiver.
+        The Rx-1 was designed by Dr. John Schwacke, of MUSC, in Charleston, SC, with
+        additional support provided by AMRAD, and WB4JFI.  It consists of a Digilent Nexys2
+        PFGA board with a special A/D module attached.  The A/D includes a preamp with gain
+        control, a LPF (both bypassable), and a TI AFEDRI8201 DDC chip.  The '8201 consists
+        of a 12-bit A/D (with a 76.8MHz clock), a frequency converter, a CIC filter, 
+        followed by two FIR filters.  The output of the '8201 is fed to the FPGA baord, 
+        which acts as a FIFO buffer and USB interface to the host computer
+Platform: POSIX
+Classifier: Development Status :: 6 - Mature
+Classifier: Environment :: X11 Applications
+Classifier: Intended Audience :: End Users/Desktop
+Classifier: License :: OSI Approved :: GNU General Public License (GPL)
+Classifier: Natural Language :: English
+Classifier: Operating System :: POSIX
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: C
+Classifier: Topic :: Communications :: Ham Radio
--- /dev/null
+++ b/charleston/quisk_conf.py
@@ -0,0 +1,73 @@
+# These are the configuration parameters for Quisk using the
+# SDR-IQ by RfSpace or the AMRAD Charleston Rx1 as the capture device.
+
+# Please do not change this sample file.
+# Instead copy it to your own .quisk_conf.py and make changes there.
+# See quisk_conf_defaults.py for more information.
+
+# Some decimation rates and resulting sampling frequencies:
+#  Charleston decimation must be evenly divisible by four
+#  Charleston 160 = 480k samples
+#  Charleston 200 = 384k samples
+#  Charleston 300 = 256k samples
+#  Charleston 320 = 240k samples
+#  Charleston 400 = 192k samples (DEFAULT)
+#  Charleston 640 = 120k samples
+#  Charleston 800 = 96k samples
+#  Charleston 1600 = 48k samples
+#  SDR-IQ decimation must be 360, 500, 600, or 1250
+#  SDR-IQ 360 = 185k samples (185,185)
+#  SDR-IQ 500 = 133k samples (133,333)
+#  SDR-IQ 600 = 111k samples (111,111)
+#  SDR-IQ 1250 = 53k samples (53,333)
+ 
+Charleston = 1		# sets up the SDR-IQ or Charleston test
+
+if Charleston == 1:
+  from charleston import quisk_widgets  # add Charleston widgets
+  from charleston import quisk_hardware # Use different hardware file
+  use_sdriq = 0							# do not use the SDR-IQ anymore
+  sdriq_name = "Digilent"				# Digilent USB interface
+  chas_rx1_clock = 76800000.0			# Charleston clock is 76.8MHz
+  sdriq_decimation = 400
+  sample_rate = int(float(chas_rx1_clock) / sdriq_decimation + 0.5)	# Don't change this
+  display_fraction = 0.90				# the edges of the full bandwidth are not valid
+else:
+  from sdriqpkg import quisk_hardware	# use SDR-IQ hardware file
+  use_sdriq = 1							# Use the SDR-IQ for sound sampling setup
+  sdriq_name = "/dev/ft2450"			# Name of the SDR-IQ device to open
+  ##sdriq_name = "/dev/ttyUSB0"			# name of the SDR-IQ driver to open
+  sdriq_clock = 66666667.0				# actual sample rate (66666667 nominal)
+  sdriq_decimation = 500				# Must be 360, 500, 600, or 1250
+  sample_rate = int(float(sdriq_clock) / sdriq_decimation + 0.5)	# Don't change this
+  display_fraction = 0.85				# the edges of the full bandwidth are not valid
+
+# The default hardware module is used for the SDR-IQ.
+
+# In ALSA, soundcards have these names:
+#name_of_sound_play = "hw:0"
+#name_of_sound_play = "hw:1"
+#name_of_sound_play = "plughw"
+#name_of_sound_play = "plughw:1"
+#name_of_sound_play = "default"
+
+#use_sdriq = 1				# Use the SDR-IQ for sound sampling setup (also chas board)
+#sample_rate = int(float(sdriq_clock) / sdriq_decimation + 0.5)	# Don't change this
+name_of_sound_capt = ""			# We do not capture from the soundcard
+name_of_sound_play = "hw:0"		# Play back on this soundcard
+# Note: For the SDR-IQ, playback is stereo at 48000 Hertz.
+channel_i = 0				# Soundcard index of left channel
+channel_q = 1				# Soundcard index of right channel
+
+mouse_tune_method = 1			# tune freq unchanged
+graph_refresh = 7
+latency_millisecs = 150
+filter_display = 1
+#graph_y_scale = 80
+#graph_y_zero  = 75
+
+waterfall_y_scale = 90
+waterfall_y_zero  = 70
+waterfall_graph_y_scale = 100
+waterfall_graph_y_zero = 70
+
--- /dev/null
+++ b/charleston/quisk_hardware.py
@@ -0,0 +1,59 @@
+# Please do not change this hardware control module.
+# It provides support for the Charleston Rx1.
+
+import _quisk as QS
+import chas_rx1
+import quisk_widgets
+from quisk_hardware_model import Hardware as BaseHardware
+
+class Hardware(BaseHardware):
+  decimations = [1600,800,640,400,320,300,200,160]	# Charleston available decimation rates
+  def __init__(self, app, conf):
+    BaseHardware.__init__(self, app, conf)
+    self.clock = 76800000.0
+    self.index = 3
+    chas_rx1.set_decimation(self.decimations[self.index])
+#    self.rf_gain_labels = ('PGA 0dB', 'PGA 1dB', 'PGA 2.5dB', 'PGA 4dB',
+#      'PGA 6dB', 'PGA 8.5dB', 'PGA 12dB')
+  def open(self):
+    return chas_rx1.open_samples()		# Return a config message
+  def close(self):
+    chas_rx1.close_samples()
+  def OnButtonRfGain(self, event):
+    btn = event.GetEventObject()
+    n = btn.index
+    chas_rx1.pga_gain_chas_rx1(n)
+  def ChangeFrequency(self, tune, vfo, source='', band='', event=None):
+    if vfo:
+      chas_rx1.freq_chas_rx1(vfo)
+    return tune, vfo
+  def ChangeBand(self, band):
+    # band is a string: "60", "40", "WWV", etc.
+#    btn = self.application.BtnRfGain
+    btn = chas_rx1.pga_gain_chas_rx1
+#    btn = BottomWidgets.OnBtnPGA
+#    if btn:
+    if band in ('160', '80', '60', '40'):
+#        btn.SetLabel('PGA 0dB', True)
+        chas_rx1.pga_gain_chas_rx1(6)
+    elif band in ('20',):
+#        btn.SetLabel('PGA 2.5dB', True)
+        chas_rx1.pga_gain_chas_rx1(6)
+    else:
+#        btn.SetLabel('PGA 4dB', True)
+        chas_rx1.pga_gain_chas_rx1(6)
+  def VarDecimGetChoices(self): # return text labels for the control
+    l = [] # a list of sample rates
+    for dec in self.decimations:
+      l.append(str(int(float(self.clock) / dec / 1e3 + 0.5)))
+    return l
+  def VarDecimGetLabel(self): # return a text label for the control
+    return "Sample rate ksps"
+  def VarDecimGetIndex(self): # return the current index
+    return self.index
+  def VarDecimSet(self, index=None): # set decimation, return sample rate
+    if index is not None:
+      self.index = index
+      chas_rx1.set_decimation(self.decimations[index])
+    return int(float(self.clock) / self.decimations[self.index] + 0.5)
+
--- /dev/null
+++ b/charleston/quisk_widgets.py
@@ -0,0 +1,113 @@
+# Please do not change this widgets module for Quisk.  Instead copy
+# it to your own quisk_widgets.py and make changes there.
+#
+# This module is used to add extra widgets to the QUISK screen.
+
+import wx.lib.stattext
+import _quisk as QS
+import math
+import chas_rx1 as CHASRX1
+
+class BottomWidgets:	# Add extra widgets to the bottom of the screen
+  def __init__(self, app, hardware, conf, frame, gbs, vertBox):
+    self.hardware = hardware
+######	SPOT BUTTON
+#    b = app.QuiskCycleCheckbutton(frame, self.OnBtnSpot,
+#       ('Spot', 'Spot -6db', 'Spot 0db'), color=conf.color_test)
+#
+#    gbs.Add(b, (4, 0), flag=wx.EXPAND)
+#    b = app.QuiskCycleCheckbutton(frame, self.OnBtnFDX,
+#        ('HALF', 'FULL'), color=conf.color_test)
+#    gbs.Add(b, (4, 1), flag=wx.EXPAND)
+    b = app.QuiskCycleCheckbutton(frame, self.OnBtnPGA,		# WB4JFI added for PGA Gain 
+        ('PGA 0dB', 'PGA 1dB', 'PGA 2.5dB', 'PGA 4dB', 'PGA 6dB', 'PGA 8.5dB', 'PGA 12dB'),
+        color=conf.color_test)
+    b.SetIndex(6)
+    gbs.Add(b, (4, 0), flag=wx.EXPAND)
+    b = app.QuiskCycleCheckbutton(frame, self.OnBtnPREAMP,	# WB4JFI added for Preamp/Bypass
+        ('PRE + LPF', 'BYPASS'), color=conf.color_test)
+    gbs.Add(b, (4, 1), flag=wx.EXPAND)
+    b = app.QuiskCycleCheckbutton(frame, self.OnBtnPREHILO,
+        ('PRE HIGH', 'PRE LOW'), color=conf.color_test)
+    gbs.Add(b, (4, 2), flag=wx.EXPAND)
+    b = app.QuiskCycleCheckbutton(frame, self.OnBtnSLOPE,
+        ('PRE POS', 'PRE NEG'), color=conf.color_test)
+    gbs.Add(b, (4, 3), flag=wx.EXPAND)
+#  Horizontal slider for setting Preamp gain - label first
+    lab = wx.StaticText(frame, -1, 'Preamp', style=wx.ALIGN_CENTER)
+    gbs.Add(lab, (5,0), flag=wx.EXPAND)
+#  and now the Horizontal slider itself
+    sl = wx.Slider(frame, -1, 2047, 0, 4095)			# parent, -1, initial, min, max
+    gbs.Add(sl, (5,1), (1, 5), flag=wx.EXPAND)			# was (5,1), (1,5)
+    sl.Bind(wx.EVT_SCROLL, self.OnPreamp)
+#  CPU Load display on lower-right
+#    lab = wx.StaticText(frame, -1, 'CPU load', style=wx.ALIGN_CENTER)
+#    gbs.Add(lab, (4,10), flag=wx.EXPAND)
+#  CPU Load display itself - in a window
+#   self.cpu_load = t = wx.lib.stattext.GenStaticText(frame, -1, '',
+#           style=wx.ST_NO_AUTORESIZE|wx.RAISED_BORDER)
+#    font = wx.Font(12, wx.FONTFAMILY_SWISS, wx.NORMAL, wx.FONTWEIGHT_NORMAL)
+#    t.SetFont(font)
+#    w, h = t.GetTextExtent("W")
+#    t.SetBackgroundColour(conf.color_freq)
+#    gbs.Add(self.cpu_load, (4, 11), (1, 1), flag=wx.EXPAND)
+#    self.UpdateCpu(1)
+#
+#    # RIT button
+#    self.ritButton = QuiskCheckbutton(frame, self.OnBtnRit, "RIT")
+#    gbs.Add(self.ritButton, (0, 7), flag=wx.ALIGN_CENTER)
+#
+#
+#    app.BtnRfGain.Disable()				# WB4JFI - disable original SDR-IQ gain button
+#
+    b = app.QuiskCheckbutton(frame, self.OnBtnIMD, text='IMD', color=conf.color_test)
+    b.Disable()
+    gbs.Add(b, (4, 12), flag=wx.EXPAND)
+#  def OnBtnFDX(self, btn):
+#    if btn.GetValue():
+#      self.hardware.FreezeTxFreq(1)
+#      QS.set_fdx(1)
+#    else:
+#      self.hardware.FreezeTxFreq(0)
+#      QS.set_fdx(0)
+  def OnBtnPGA(self, event):
+    btn = event.GetEventObject()
+#    print "PGA: %d" % (btn.index)
+    CHASRX1.pga_gain_chas_rx1(btn.index)
+  def OnBtnPREAMP(self, event):			# WB4JFI added for Charleston Rx Preamp/Bypass
+    btn = event.GetEventObject()
+    if btn.GetValue():
+      CHASRX1.rf_path_chas_rx1(0)
+    else:
+      CHASRX1.rf_path_chas_rx1(1)
+  def OnBtnPREHILO(self, event):		# WB4JFI added for Charleston Rx Preamp Hi/Lo gain
+    btn = event.GetEventObject()
+    if btn.GetValue():
+      CHASRX1.lna_range_chas_rx1(0)
+    else:
+      CHASRX1.lna_range_chas_rx1(1)
+  def OnBtnSLOPE(self, event):			# WB4JFI added for Charleston Rx Preamp Pos/Neg slope
+    btn = event.GetEventObject()
+    if btn.GetValue():
+      CHASRX1.lna_slope_chas_rx1(1)
+    else:
+      CHASRX1.lna_slope_chas_rx1(0)
+  def OnBtnIMD(self, btn):
+      QS.set_rx_mode(1)
+  def OnPreamp(self, event):			# WB4JFI added for Charleston Rx Preamp gain amount
+      CHASRX1.lna_gain_chas_rx1(event.GetPosition())
+#
+####	SPOT BUTTON
+#  def OnBtnSpot(self, event):
+#    btn = event.GetEventObject()
+#    QS.set_spot_mode(btn.index)
+#    self.hardware.anttuner.OnSpot(btn.index)
+#     self.ritFreq = value
+#      QS.set_tune(self.txFreq + self.ritFreq, self.txFreq)
+#      QS.set_sidetone(self.sidetone_volume, self.ritFreq, conf.keyupDelay)
+#
+#  def UpdateCpu(self, param1):	# Called by Hardware
+#    cpu=0.86 #param1
+#    text = "   %.1f" % (cpu * 100)
+#    self.cpu_load.SetLabel(text)
+#
--- /dev/null
+++ b/charleston/README.txt
@@ -0,0 +1,23 @@
+INSTALLATION
+============
+
+Unzip and untar this archive at the root of the Quisk directory;
+that is, where the file quisk.py is located.  In this example,
+the archive is named "charleston" and the path to quisk.py is
+/home/jim/quisk/quisk.py.
+
+  mv charleston-1.0.tar.gz  /home/jim/quisk
+  cd /home/jim/quisk
+  gunzip charleston-1.0.tar.gz
+  tar xf charleston-1.0.tar
+  # Make sure that directory charleston-1.0 exists before removing the archive.
+  rm charleston-1.0.tar		# tar file is no longer needed
+  mv charleston-1.0 charleston	# change to shorter name
+
+You now have a new Python package named charleston.  To import from this package:
+
+  from charleston import quisk_widgets
+  from charleston import quisk_hardware
+
+(note: there is a sample .quisk_conf.py in the charleston directory that already has the above two ines in it, along with other charleston parameters pre-configured)
+
--- /dev/null
+++ b/charleston/Readme_WB4JFI.txt
@@ -0,0 +1,54 @@
+Terry Fox, WB4JFI quick-notes on Quisk for Charleston SDR Receiver.  (written in gedit)	August 1, 2010
+
+Here are a few additional notes on this early release of Quisk 3.4.7 as modified by Terry Fox, WB4JFI, for the Charleston RX1 SDR Receiver board.  The Charleston SDR receiver consists of a Digilent Nexys2 FPGA board, with an auxillary board, designed by Dr. John Schwacke, plugged into two PMOD connectors.  The default connectors are PMOD JA and JB.  For more information on this setup, see the DSP/SDR section of the AMRAD website (www.amrad.org), or John Schwacke's website (http://sdrtrack.drupalcafe.com).
+
+Quisk is written for *nix systems by James Ahlstrom (N2ADR)(http://james.ahlstrom.name), in a mixture of Python and C.  You will need C development tools and Python to build the code.  The Charleston software uses standard USB library functions, so you will also need to add the libusb library.  Details depend on what version of Linux you are running.
+
+This code should still run on any of Jim's Quisk-supported hardware, including an SDR-IQ, in addition to the Charleston Receiver.
+
+As mentioned in Jim's documentation, you should move a hardware-specific configuration file into your home directory.  For convenience, I have included a version of my file here, called sample_chas_quisk_conf.py.  Copy this file into your home directory, with the name .quisk_conf.py (note the leading period), using something like: "cp -p sample_chas_quisk_conf.py ~/.quisk_conf.py(return)".  There are several variables set up in that file that you can change to adjust how Quisk operates.
+
+To use this code with an SDR-IQ, just comment out the line in your (home)/.quisk_config.py file that says Charleston = 1, or change it to: Charleston = 0.  I have an SDR-IQ in addition to the Charleston Receiver, and can use either radio by simply commenting or uncommenting that one line (or changing the value to zero).
+
+The Charleston Rx1 Receiver can support larger decimation factors than this code defaults to, which is 192k.  (Take 76.8MHz, and divide by the decimation number, and you get the sample rate sent from the Charleston Receiver to the host computer.  Therefore, 76.8MHz / 400 = 192ks/s.  The AFEDRI8201 has internal dividers of four, which means the CIC's actual divide is 400 / 4, or 100).  There are notes in the config file to set the decimation for other rates.  Decimation must be evenly divisible by four.  Feel free to experiment.
+
+I am seeing more buffer underuns and FFT errors if I run above about 240ks/s.  Jim says that he can run Quisk with his hardware at upwards of 900ks/s, so something is still off.  Maybe it's just my laptops (I regularly use two, both at least two years old).
+
+I have adjusted the coefficients for the two FIR filters inside the 8201 for 192k samples.  There are still a few more optimizations here before I declare complete success.
+
+One thing that I have noticed is that you can hear an "image" of stronger signals about 48kHz above and below the actual signal.  Those artifacts do NOT show up of the graphic display, but can be heard in the demodulated audio.  This may be related to FIR filter issues, or sample size issues, i'mnot sure yet.  Jim says that he has also heard some of these images with his SD-IQ, but I cannot seem to reproduce them with my SDR-IQ.
+
+This version of Quisk uses a slightly different architecture than previous versions.  Jim moved the SDR-IQ hardware-specific code out of quisk.py, quisk.h, and quisk.c, and put that code in sdriq.py, sdriq.h, and sdriq.c.  This allowed me to make equivalent files for the Charleston Receiver hardware, called chas_rx1.py, chas_rx1.h, and chas_rx1.c.  I did NOT write all this new code, it was a cut-and-paste transmorgification of Jim's SDR-IQ code and John Schwacke's Charleston Receiver support code for GNU Radio.
+
+I added a small window on the lower-right of the display to show CPU loading, but the hooks between the code sections have not been completed yet, so it's disabled remporarily.  I'm not sure this is useful, especially with dual-core and multiple CPU computers.  You can run the System Monitor to see CPU loading (Ubuntu: on top GUI menu, select: System/Administration/Sysem Monitor).
+
+Jim has added the ability to change decimation rate on-the-fly with a GUI button (in CONFIG), which can be real handy for things like "zooming into" a signal.  I've added the necessary code to implement this with the Charleston board.  I recently adjusted the code to properly calculate the optimal CIC shift and scale factors for the 8201 when changing decimation rate.  There is almost no change in the FIR filter coefficients for altering the decimation rate, so I will use a common table for now.
+
+Jim's code runs in two separate threads: a sound/sampling thread, and a GUI thread.  John's original Charleston Receiver GNU Radio code also had two threads, but split in a slightly different manner.  If you see some commented out code in the chas_rx1.c/h files related to threading, that was an attempt to work John's threading into Quisk.  I gave up on that fairly quickly when I saw that Jim's Quisk already had two threads.  So, ignore those comments for now, they will be deleted.
+
+Failures of this code to function are mine and mine alone.  Do NOT blame Jim if the Charleston code fails to work, instead let me know via email at: wb4jfi@knology.net.
+
+I have a variation of Jim's quisk.py code that also displays a filter-bandwidth shadow on the screen, that changes with the modulation, bandwidth, and frequency.  It is still in the experimental phase, as it is somewhat sluggish in moving around the screen.  If interested, contact me for how to add this.
+
+I am not a Linux or Python expert by any means, I am a simple code-hacker with C.  There are probably much better ways to do some of the stuff included here.  If so, please let me know, as I am always willing to learn more.  I've not messed with Python at all before this, and a little GNU Radio coding.
+
+In order to compile the Charleston code for Quisk, you need to first compile the main Quisk files.  Go to the quisk file (cd ~/quisk3.4.7), then compile the code with make (make).  Notes here are based on Ubuntu 10.04LTS.
+
+(add here about any quisk dependencies)
+
+The Charleston software uses libusb for the USB interface to the Digilent board.  Before compiling the Charleston suport software, make sure you have installed "libusb"(libusb-0.1-4) and "libusb-dev" (libusb-dev).  For Ubuntu, I usually use the Synoptics Package Manager and search for libusb.  Both files should be listed.
+
+Make sure the Charleston files are in a subdirectory of quisk named "charleston", move them there if necessary, otherwise the Python links won't work right.  Then cd to that directory and do a "make in that directory to compile the Charleston code.
+
+If after compiling, you get an error saying something like: 
+AttributeError: 'module' object has no attribute 'BitmapFromBuffer'
+You need to copy a wx gtk file, as follows:
+cd /usr/lib/python2.6/dist-packages(return)
+sudo cp wx-2.8-gtk2-unicode.pth wx-2.6-gtk2-unicode.pth(return)
+
+
+Good luck, and enjoy!
+73s
+Terry, WB4JFI
+
+
--- /dev/null
+++ b/charleston/setup.py
@@ -0,0 +1,41 @@
+from distutils.core import setup, Extension
+
+module1 = Extension ('chas_rx2',
+	libraries = [':_quisk.so', 'm'],
+	runtime_library_dirs = ['.'],
+	sources = ['chas_rx2.c'],
+	include_dirs = ['.', '..'],
+	)
+
+setup (
+	name = 'charleston',
+	version = 1.1,
+	description = 'This module adds support for the Charleston RX-1 hardware to QUISK',
+	long_description = """This module adds support for the Charleston Rx-1 SDR receiver.
+The Rx-1 was designed by Dr. John Schwacke, of MUSC, in Charleston, SC, with
+additional support provided by AMRAD, and WB4JFI.  It consists of a Digilent Nexys2
+PFGA board with a special A/D module attached.  The A/D includes a preamp with gain
+control, a LPF (both bypassable), and a TI AFEDRI8201 DDC chip.  The '8201 consists
+of a 12-bit A/D (with a 76.8MHz clock), a frequency converter, a CIC filter, 
+followed by two FIR filters.  The output of the '8201 is fed to the FPGA baord, 
+which acts as a FIFO buffer and USB interface to the host computer""",
+	author = 'Terry Fox',
+	author_email = 'tfox@knology.net',
+	url='www.amrad.org',
+	license = "GPL",
+	platforms = ['POSIX'],
+	packages = ['charleston'],
+	package_dir = {'charleston' : '.'},
+	ext_modules = [module1],
+	classifiers = [
+		'Development Status :: 6 - Mature',
+		'Environment :: X11 Applications',
+		'Intended Audience :: End Users/Desktop',
+		'License :: OSI Approved :: GNU General Public License (GPL)',
+		'Natural Language :: English',
+		'Operating System :: POSIX',
+		'Programming Language :: Python',
+		'Programming Language :: C',
+		'Topic :: Communications :: Ham Radio',
+	],
+)
