/*
 * Routines to control TenTec RX-320 radios by Tim Shoppa.
 * Just to be confusing I call this "icom.c" and replace the
 * stock NTP distribution file with it.  It would be a Good Thing
 * to somehow allow a build-time or run-time selection of the
 * Icom driver vs RX-320 driver (vs other drivers).
 *
 * This is a ripoff of A. Maitland Bottoms <a4hs@amrad.org> RX 320
 * routines, which are GPL'ed.
 * 
 * We provide icom_init() and icom_freq() routines.
 * icom_freq() by necessity also chooses mode, bandwidth, etc. because
 * of the way the RX-320 works.  While we're at it, may as well set
 * volumes too (completely initialize all settable RX-320 parameters).
 * Note that the RX-320 has no knobs... all parameters are set through
 * the serial port.
 */
#include "icom.h"
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>

#include <ctype.h>
#include <math.h>
#include <sys/ioctl.h>

#include "ntp_tty.h"
#include "l_stdlib.h"

/*
 * Local variables
 */
int Mcor=0;
int Fcor=0;
int Cbfo=0;

int rx320fd;
FILE *serport;

int flags;


/*
 * Local prototypes
 */
static int sendcmd(char *cmd,int len);
static int sndoctet(int fd, int x);

/*
 * MODE: AM USB LSB CW
 */
int
rx320mode(char *mode)
{
  char mcmd[3];

  mcmd[0] = 'M';
  mcmd[2] = 0x0d;
  switch (tolower(mode[0])) {
  case 'u':
    Mcor = 1;  Cbfo = 0; mcmd[1]='1'; break;
  case 'l':
    Mcor = -1; Cbfo = 0; mcmd[1]='2'; break;
  case 'c':
    Mcor = -1; Cbfo = 1000; mcmd[1]='3'; break;
  default:
  case 'a':
    Mcor = 0; mcmd[1]='0'; break;
    break;
  }
  return(sendcmd(mcmd,3));
}

/*
 * FILTER: (34 of them)
 */
typedef struct {
  int bandwidth;
  int filter;
} FEntry;

FEntry Filters[] = {
  {8000, 33},
  {6000, 0},
  {5700, 1},
  {5400, 2},
  {5100, 3},
  {4800, 4},
  {4500, 5},
  {4200, 6},
  {3900, 7},
  {3600, 8},
  {3300, 9},
  {3000, 10},
  {2850, 11},
  {2700, 12},
  {2550, 13},
  {2400, 14},
  {2250, 15},
  {2100, 16},
  {1950, 17},
  {1800, 18},
  {1650, 19},
  {1500, 20},
  {1350, 21},
  {1200, 22},
  {1050, 23},
  {900, 24},
  {750, 25},
  {675, 26},
  {600, 27},
  {525, 28},
  {450, 29},
  {375, 30},
  {330, 31},
  {300, 32},
  {0, 32}
};
int
rx320filter(int filt)
{
  char fcmd[3];
  FEntry *fe;
  if (filt>34) {
    /* Allow selection by kHz as well as number */
    for (fe = Filters; fe->bandwidth>0; fe++)
      if (filt>fe->bandwidth) break;
    filt = fe->filter;
  };
  fcmd[0] = 'W';
  fcmd[1] = filt;
  fcmd[2] = 0x0d;

  /* Need to set Fcor */
  for (fe = Filters; filt!=fe->filter; fe++);
  Fcor=(fe->bandwidth/2)+200;
  return(sendcmd(fcmd,3));
}

/*
 * VOLUME: (speaker,line-out,both) 0-63
 */
int
rx320volume(int output,int vol)
{
  char vcmd[4];
  switch (output) {
  case RX320SPEAKER: vcmd[0] = 'V'; break;
  case RX320LINE: vcmd[0] = 'A'; break;
  case RX320BOTH:
  default:
    vcmd[0] = 'C'; break;
  }
  vcmd[1]=0; vcmd[3]=0x0d;
  if ((vol>=0) && (vol<64)) {
    vcmd[2] = vol;
    return(sendcmd(vcmd,4));
  }
  return(0);
}

/*
 * AGC: SLOW MEDIUM FAST
 */
int
rx320agc(char *agc)
{
  char agccmd[3];

  agccmd[0]='G';
  switch (tolower(agc[0])) {
  case 's': agccmd[1] = '1'; break;
  case 'm': agccmd[1] = '2'; break;
  case 'f': agccmd[1] = '3'; break;
  default:  agccmd[1] = '3'; break;
  }
  agccmd[2]=0x0d;
  return(sendcmd(agccmd,3));
}
/*
 * FREQUENCY: COARSE FINE BFO
 */
int rx320frequency(float freq)
{
  char fcmd[8];
  double intpart;
  float AdjTfreq; /* Adjusted Tuned Frequency */
  int Atf; /* Integer AdjTfreq */
  int Ctf; /* Coarse tuning factor */
  int Ftf; /* Fine Tuning Factor */
  int Btf; /* BFO Tuning factor */

  AdjTfreq = freq - 1.25 + (Mcor*(Fcor+Cbfo))/1000.0;
  Atf = AdjTfreq;
  Ctf = AdjTfreq/2.5+18000;
  Ftf = modf(AdjTfreq/2.5,&intpart)*2500*5.46;
  Btf = (Fcor+Cbfo+8000)*2.73;

  fcmd[0] = 'N';
  fcmd[1] = 0xFF&(Ctf>>8);
  fcmd[2] = 0xFF&Ctf;
  fcmd[3] = 0XFF&(Ftf>>8);
  fcmd[4] = 0xFF&Ftf;
  fcmd[5] = 0xFF&(Btf>>8);
  fcmd[6] = 0xFF&Btf;
  fcmd[7] = 0x0d;
  return(sendcmd(fcmd,8));
}


/*
 * icom_freq(fd, ident, freq) - load radio frequency
 * For RX-320, ident has no meaning.
 * For RX-320, choose bandwidth=3000Hz
 *                    mode=AM
 *                    agc=FAST
 *                    volume=6
 */
int
icom_freq(			/* returns 0 (ok), EIO (error) */
	int fd,			/* file descriptor */
	int ident,		/* ICOM radio identifier */
	double freq		/* frequency (MHz) */
	)
{
	rx320fd = fd;
	float freq1;
	freq1=freq*1000;  /* Need this as a float in kHz, not a double in MHz*/
	rx320mode("AM");
	rx320filter(3000);
        rx320frequency(freq1);
	rx320agc("FAST");
	rx320volume(RX320BOTH,6);
	return(0);
}

/*
 * icom_init() - open and initialize serial interface
 * For RX-320, ignore the speed.
 *
 */
int
icom_init(
	char *device,		/* device name/link */
	int speed,		/* line speed */
	int trace		/* trace flags */	)
{
  int fd;
  struct termios ts;
  int off = 0;
  int modemlines;

  flags = trace;

  fd = open(device,O_RDWR|O_NOCTTY|O_NONBLOCK,0);
  if (!isatty(fd)) {
    fprintf(stderr,"Oops, need to connect to a tty\n");
    return(0); /* nothing to do */
  }
  if (tcgetattr(fd,&ts)==-1) {
    fprintf(stderr,"Oops, didn't get tty settings\n");
    return(0);
  }
  cfmakeraw(&ts);
  ts.c_iflag = IGNBRK; /* | IGNCR; */
  ts.c_oflag = 0;
  ts.c_cflag = CS8 | CREAD | CLOCAL;
  ts.c_lflag = 0;
  ts.c_cc[VMIN] = 1;
  ts.c_cc[VTIME] = 0;
  if (cfsetospeed(&ts, B1200)==-1) {
    perror("output speed");
    return(0);
  }
  if (cfsetispeed(&ts, B1200)==-1) {
    perror("input speed");
    return(0);
  }
  if (tcsetattr(fd,TCSAFLUSH,&ts)==-1) {
    perror("serial_port_setup");
    return(0);
  }
  /* Set the line back to blocking mode after setting CLOCAL. */
  if (ioctl(fd, FIONBIO, &off) < 0) {
    perror("setting blocking mode");
    return(0);
  } 
  modemlines = TIOCM_RTS; 
  if (ioctl(fd, TIOCMBIC, &modemlines)) {
    perror("clear RTS line");
    return(0);
  }
  serport = fdopen(fd,"r+");
  return(fd);
}

static int
sendcmd(
	char *cmd,		/* command vector */
	int len		        /* response vector */
	)
{
  int i;
  for (i=0;i<len;i++)
    fputc(cmd[i],serport);
  fflush(serport);
	return (0);
}

/*
 * Interface routines
 */
/*
 * sndoctet(fd, x) - send octet
 */
static int
sndoctet(			/* returns octet */
	int fd,			/* file descriptor */
	int x			/* octet */
	)
{
	u_char y;
	int n;

	y = (u_char)x;
	n = write(fd, &y, 1);
	return (x);
}

