/* ICOM CI-V control for R-7100 */

#include <stdio.h>
#include <curses.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <time.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/times.h>
#include <sys/ioctl.h>

#undef KEYSFILE		"/ram/.X-keys/KEYS1"	/* file with X keypresses */

#define BUFLEN		32
#define DEVADDR		0x34			/* R-7100 addr */
#define OURADDR		0x01			/* PC addr */
#define BROADCAST	0x00			/* broadcast addr */

#define SAMPLEDELAY	200000
#define VOLUME		110

int LineFd;					/* fd for RS232 line */
int CoaxFd = -1;				/* fd for Coax switch */
int CoaxPos;
int Volume;

char *freqdesc(long freq);
int backupmem(char *filename);
int kbhit(void);
int memclear(void);
int memwrite(void);
int openline(char *name);
int readdescfile(char *name);
int readmode(void);
int readpkt(unsigned char *buf);
int readrange(char *line);
int readreply(unsigned char *buf);
int readsmeter(void);
int readsquelch(void);
int sendpkt(unsigned char cmd,unsigned char *buf,int len);
int setatt(int att);
int setcoax(int pos);
int setfrequency(long freq);
int setmemory(int memory);
int setmode(int mode);
int setscan(int scan1,int scan2);
int setts(int step);
int setvolume(int volume);
int setwindow(int window);
int specialscan(char *filename);
int spectrum(char *filename);
long readfrequency(void);
void decode_civ(unsigned char buf[],int len);
void intvolume (int volume);

struct scan
{
    struct scan *next;				/* linked list */
    struct scan *base;				/* link to base def of chan */
    int dline;					/* display line */
    int flags;					/* runtime flags */
#define F_SKIP	0x01				/* skip channel */
#define F_PASS	0x02				/* pass until quiet */
    int smeter;					/* last indicated s-meter */
    int totaltime;				/* total active time */
    int mem;					/* memory to scan */
    int class;					/* scanning class */
    int cont;					/* continue after xx sec */
    int hold;					/* hold after car loss */
    int smin;					/* mimimum s-meter value */
    int volume;					/* audio volume on/off */
    int coax;					/* coaxswitch position */
};

struct scan *scanhead;
struct scan **scantail;

struct desc
{
    struct desc *next;				/* linked list */
    long freq;					/* frequency in Hz */
    char *desc;					/* description */
};

#define DESCHASH	101

struct desc *descriptions[DESCHASH];		/* hashed list */



int
main (argc, argv)
int argc;
char *argv[];

{
    int len,cmd,v,quit = 0;
    char *p;
    fd_set select_set;
    unsigned char buf[BUFLEN];
    char line[80];

    if (argc < 2) {
	fprintf(stderr,"Usage: %s <r7100-tty-device> [<coax-tty-device>]\n",argv[0]);
	exit(1);
    }

    if (openline(argv[1]))
	exit(2);

    if (argc > 2 && (CoaxFd = open(argv[2],O_WRONLY)) < 0) {
	perror(argv[2]);
	exit(3);
    }

    readdescfile("freq.desc");

    initscr();
    cbreak();
    noecho();
    intrflush(stdscr, FALSE);
    keypad(stdscr, TRUE);

    move(6,0);
    printw("LINES=%d COLS=%d",LINES,COLS);

    if (readrange(line) > 0) {
	move(8,0);
	printw("Frequency range: %s",line);
    }

    intvolume(VOLUME);
    setwindow(0);

    move(4,0);
    clrtoeol();

    FD_ZERO(&select_set);

    while (!quit)
    {
	move(0,0);
	printw("cmd> ");
	clrtoeol();
	refresh();

	FD_SET(0,&select_set);
	FD_SET(LineFd,&select_set);

	if (select(LineFd + 1,&select_set,NULL,NULL,NULL) < 0)
	    break;

	if (FD_ISSET(0,&select_set)) {
	    move(0,5);
	    refresh();
	    echo();
	    getstr(line);
	    noecho();
	    move(0,0);
	    clrtoeol();
	    refresh();

	    setscan(0,-1);

	    if ((p = strchr(line,'\r')) != NULL)
		*p = '\0';

	    if (!line[0])
		continue;

	    p = line;
	    sscanf(p,"%x",&cmd);

	    while (*p != '\0' && !isspace(*p))
		p++;
	    while (*p != '\0' && isspace(*p))
		p++;

	    switch (line[0])
	    {
	    case 'a':
		intvolume(0);
		setvolume(0);
		break;

	    case 'c':
		setcoax(atoi(p));
		break;

	    case 'f':
		setfrequency((long) (1000000 * atof(p) + 0.5));
		setts(1);
		readmode();
		readfrequency();
		break;

	    case 'm':
		setmemory(atoi(p));
		readmode();
		readfrequency();
		break;

	    case 'n':
		intvolume(VOLUME);
		break;

	    case 'p':
		setmemory(100 * atoi(p));
		setscan(0x23,-1);
		break;

	    case 'q':
		quit++;
		break;

	    case 's':
		specialscan(*p != '\0'? p : "r7100.scan");
		break;

	    case 't':
		while (!kbhit()) {
		    readmode();
		    readfrequency();
		    readsquelch();
		    readsmeter();
		    move(0,0);
		    refresh();
		    usleep(SAMPLEDELAY);
		}
		getch();
		break;

	    case 'v':
		intvolume(atoi(p));
		setvolume(Volume);
		break;

	    case 'x':
		spectrum(*p != '\0'? p : "r7100.spec");
		break;

	    case 'z':
		backupmem(p);
		break;

	    default:
		len = 0;

		while (*p != '\0')
		{
		    sscanf(p,"%x",&v);
		    buf[5 + len++] = v;
		    while (*p != '\0' && !isspace(*p))
			p++;
		    while (*p != '\0' && isspace(*p))
			p++;
		}

		sendpkt(cmd,buf,len);
		break;
	    }
	}

	if (FD_ISSET(LineFd,&select_set) && (len = readpkt(buf)) != 0)
	    decode_civ(buf,len);
    }

    endwin();
    exit(0);
}

/* CI-V high level routines */

void
decode_civ(buf,len)
unsigned char buf[];
int len;

{
    int i;
    long freq;
    char *p;
    char line[80];

    switch (buf[4])
    {
    case 0:					/* freq report */
    case 3:
	move(3,13);
	clrtoeol();

	switch (len)
	{
	case 6:
	    move(2,13);
	    printw("  --.---.- MHz");
	    break;

	case 9:
	case 10:
	    freq = 0;

	    for (i = len - 1; i >= 5; i--) {
		freq *= 100;
		freq += 10 * (buf[i] >> 4) + (buf[i] & 0x0f);
	    }

	    move(2,13);
	    sprintf(line,"%10ld",freq);
	    printw("%.4s.%.3s.%.1s MHz",line,line + 4,line + 7);

	    if ((p = freqdesc(freq)) != NULL) {
		move(3,13);
		printw("%s",p);
	    }
	    break;
	
	default:
	    goto dump;
	}
	break;

    case 1:					/* mode report */
    case 4:
	move(2,29);
	switch (buf[5])
	{
	case 0:
	    printw("LSB");
	    break;

	case 1:
	    printw("USB");
	    break;

	case 2:
	    printw("AM");
	    if (buf[6] == 1)
		printw(" W");
	    break;

	case 5:
	    printw("FM");
	    if (buf[6] == 2)
		printw(" N");
	    break;

	case 6:
	    printw("WFM");
	    break;

	case 0xff:
	    printw("--");
	    break;

	default:
	    printw("%02x",buf[5]);
	    break;
	}
	printw("  ");
	break;

    case 0x15:
	switch (buf[5])
	{
	case 1:
	    move(2,35);
	    attron(A_BOLD);
	    printw("%s",buf[6]? "BUSY":"    ");
	    attroff(A_BOLD);
	    break;

	case 2:
	    move(2,41);
	    sprintf(line,"%x%02x",buf[6],buf[7]);
	    printw("S-meter: %d  ",atoi(line));
	    break;

	default:
	    goto dump;
	}
	break;

    case 0xfa:
	move(2,70);
	attron(A_REVERSE);
	printw("ERROR");
	attroff(A_REVERSE);
	break;

    case 0xfb:
	move(2,70);
	printw("OK   ");
	break;

    default:
dump:
	move(4,2);
	clrtoeol();
	printw("cmd %02x:",buf[4]);

	for (i = 5; i < len; i++)
	    printw(" %02x",buf[i]);

	break;
    }
}

int
backupmem (filename)
char *filename;

{
    int mem,mode;
    long freq;
    FILE *fp;

    if ((fp = fopen(filename,"w")) == NULL) {
	perror(filename);
	return 0;
    }

    move(0,10);
    printw("Memory backup to \"%s\"",filename);
    refresh();

    for (mem = 0; mem < 999; mem++) {
	move(2,0);
	clrtoeol();

	if (!setmemory(mem))
	    continue;

	mode = readmode();
	freq = readfrequency();

	refresh();

	if (freq != 0)
	    fprintf(fp,"%03d\t%10ld\t%03d\n",mem,freq,mode);
    }

    fclose(fp);

    move(2,0);
    clrtoeol();
    refresh();

    return 1;
}

int
specialscan (filename)
char *filename;

{
    struct scan *s,*s1;
    long freq;
    int mode,smeter,slim,squelch,line,ch,class,quit;
    struct timeval tim,tim2,timeout;
    FILE *fp;
    char buf[80];

    if ((fp = fopen(filename,"r")) == NULL) {
	perror(filename);
	return 0;
    }

    scanhead = NULL;
    scantail = &scanhead;
    move(2,0);
    clrtobot();
    move(6,0);
    printw("   Ch  Cl Co  Frequency   LastTime  TotalTime  Smeter Smin");
    line = 7;
    move(line,0);
    intvolume(Volume);

    while (fgets(buf,sizeof(buf),fp) != NULL)
    {
	if (buf[0] == '#')			/* comment */
	    continue;

	if (buf[0] == '-') {			/* empty display line */
	    if (++line >= LINES)
		line = LINES - 1;
	    continue;
	}

	if ((s = calloc(1,sizeof(struct scan))) == NULL)
	    break;

	/* try to scan a full channel spec */

	if (sscanf(buf,"%d %x %d %d %d %d %d",
		   &s->mem,&s->class,&s->cont,&s->hold,
		   &s->smin,&s->volume,&s->coax) == 7)
	{
	    s->dline = line;
	    if (++line >= (LINES - 1))
		line = LINES - 2;

	    move(s->dline,2);
	    printw("%03d",s->mem);
	    move(s->dline,8);
	    printw("%x",s->class);
	    move(s->dline,11);
	    printw("%d",s->coax);
	    move(s->dline,55);
	    printw("%3d",s->smin);
	} else {
	    /* try to scan a linked channel */

	    memset(s,0,sizeof(s));

	    if (sscanf(buf,"%d %x",&s->mem,&s->class) == 2) {
		for (s1 = scanhead; s1 != NULL && &s1->next != scantail; s1 = s1->next)
		    if (s1->mem == s->mem)
			break;

		if (s1 != NULL && s1->mem == s->mem) {
		    s->base = s1;
		    s->dline = s1->dline;
		} else {
		    free(s);
		    continue;
		}
	    } else {
		free(s);
		continue;
	    }
	}

	*scantail = s;
	s->next = scanhead;
	scantail = &s->next;
    }

    fclose(fp);

    move(0,10);
    printw("Scanning from \"%s\"",filename);
    refresh();

    setscan(0,-1);
    setvolume(0);
    setwindow(1);
    CoaxPos = 0;
    setcoax(1);

    s = s1 = scanhead;
    class = 0xff;
    quit = 0;

    while (!quit)
    {
	ch = 0;

	if (kbhit()) {
#ifdef KEYSFILE
	    if ((fp = fopen(KEYSFILE,"r")) != NULL) {
		if (fgets(buf,sizeof(buf),fp) != NULL && buf[0] == 'F')
		    ch = KEY_F0 + atoi(buf + 1);
		    		fclose(fp);
		unlink(KEYSFILE);
	    }
	    else
#endif
	    ch = getch();
	}

	if (ch) {
	    switch (ch)
	    {
	    case '0':
		class = 0xff;
		move(0,60);
		clrtoeol();
		break;

	    case '1':
	    case '2':
	    case '3':
	    case '4':
	    case '5':
	    case '6':
	    case '7':
	    case '8':
	    case '9':
		class = ch - '0';
		move(0,60);
		printw("Class: %x",class);
		break;

	    case 'a':
	    case 'b':
	    case 'c':
	    case 'd':
	    case 'e':
	    case 'f':
		class = ch - 'a' + 10;
		move(0,60);
		printw("Class: %x",class);
		break;

	    case ' ':
	    case KEY_F(1):
		move(0,70);
		attron(A_REVERSE);
		printw("PAUSE");
		attroff(A_REVERSE);
		readmode();
		readfrequency();

		gettimeofday(&tim,NULL);

		while ((readsquelch() > 0 || readsquelch() > 0) && !kbhit()) {
		    s->smeter = readsmeter();
		    move(0,0);
		    refresh();
		    usleep(SAMPLEDELAY);
		}

		gettimeofday(&tim2,NULL);
		strftime(buf,sizeof(buf),"%H:%M:%S",localtime((time_t *)&tim2.tv_sec));
		move(s->dline,26);
		printw("%s",buf);

		s->totaltime += 10 * (tim2.tv_sec - tim.tv_sec) +
				     (tim2.tv_usec - tim.tv_usec) / 100000;

		move(s->dline,37);
		printw("%8d",s->totaltime / 10);

		move(0,70);
		clrtoeol();
		if (s->volume && !kbhit())
		    setvolume(0);
		move(2,0);
		clrtoeol();
		move(0,0);
		refresh();
		continue;

	    case KEY_F(2):
		break;

	    case 'm':
	    case KEY_F(3):
		if (Volume) {
		    intvolume(0);
		    setvolume(0);
		} else
		    intvolume(VOLUME);
		break;

	    case '+':
	    case '=':
		intvolume(Volume + 5);
		break;

	    case '-':
		intvolume(Volume - 5);
		break;

	    case 's':
		s->flags |= F_SKIP;
		move(s->dline,6);
		printw("s");
		break;

	    case 'S':
		s = scanhead;
		do {
		    if (s->flags & F_SKIP) {
			move(s->dline,6);
			printw(" ");
			s->flags &= ~F_SKIP;
		    }
		    s = s->next;
		} while (s != scanhead);
		break;

	    case 'p':
	    case KEY_F(4):
		s->flags |= F_PASS;
		move(s->dline,6);
		printw("p");
		break;

	    case 'q':
		quit++;
		break;

	    case 'r':
		s = s1 = scanhead;
		break;
	    }

	    setvolume(0);
	}

	move(s1->dline,2);

	if (!(s1->class & class)) {
	    if (s1->base == NULL) {
		attron(A_REVERSE);
		printw("%03d",s1->mem);
		attroff(A_BOLD|A_REVERSE);
	    }

	    if (--line < 0) {
		move(0,0);
		refresh();
		usleep(100000);
	    }

	    s1 = s1->next;
	    continue;
	}

	line = LINES - 7;

	if ((s = s1)->base != NULL)
	    s = s1->base;

	s1 = s1->next;

	if (s->flags & F_SKIP)			/* permanently skipped */
	    continue;

	attron(A_BOLD);
	printw("%03d",s->mem);
	attroff(A_BOLD|A_REVERSE);

	setcoax(s->coax);
	setmemory(s->mem);
	mode = readmode();
	freq = readfrequency();

	sprintf(buf,"%10ld",freq);
	move(s->dline,13);
	printw("%.4s.%.3s.%.1s",buf,buf + 4,buf + 7);

	if (s->flags & F_PASS) {		/* temporary pass */
	    move(0,0);
	    refresh();

	    slim = s->smeter / 10;		/* 10 % variation */
	    if (slim < 5)
		slim = 5;			/* minimum of 5 clicks */

	    if (readsquelch() > 0 &&
		(smeter = readsmeter()) >= s->smeter - slim &&
		smeter <= s->smeter + slim)
	    {
		move(s->dline,2);
		printw("%03d",s->mem);
		move(0,0);
		refresh();
		continue;
	    }

	    s->flags &= ~F_PASS;		/* no longer passed */
	    move(s->dline,6);
	    printw(" ");
	}

	move(0,0);
	refresh();

	if (readsquelch() > 0 && (s->smeter = readsmeter()) >= s->smin) {
	    if (s->volume)
		setvolume(Volume);

	    gettimeofday(&tim,NULL);

	    strftime(buf,sizeof(buf),"%H:%M:%S",localtime((time_t *)&tim.tv_sec));
	    move(s->dline,26);
	    printw("%s",buf);
	    move(s->dline,50);
	    printw("%3d",s->smeter);
	    refresh();

hold:
	    while ((squelch = readsquelch()) > 0 && !kbhit()) {
		s->smeter = readsmeter();
		move(0,0);
		refresh();
		usleep(SAMPLEDELAY);

		if (s->cont) {
		    gettimeofday(&timeout,NULL);
		    if ((timeout.tv_usec - tim.tv_usec +
			1000000 * (timeout.tv_sec - tim.tv_sec)) >=
			(1000000 * s->cont))
			break;
		}
	    }

	    gettimeofday(&tim2,NULL);

	    if (squelch <= 0 && s->hold)
	    {
		do {
		    s->smeter = readsmeter();
		    move(0,0);
		    refresh();
		    usleep(SAMPLEDELAY);

		    if (readsquelch() > 0)
			goto hold;

		    gettimeofday(&timeout,NULL);
		} while ((timeout.tv_usec - tim2.tv_usec +
			1000000 * (timeout.tv_sec - tim2.tv_sec)) <
			(100000 * s->hold) && !kbhit());
	    }

	    s->totaltime += 10 * (tim2.tv_sec - tim.tv_sec) +
				 (tim2.tv_usec - tim.tv_usec) / 100000;

	    move(s->dline,37);
	    printw("%8d",s->totaltime / 10);

	    if (s->volume && !kbhit())
		setvolume(0);

	    move(0,0);
	    refresh();
	}

	move(s->dline,2);
	printw("%03d",s->mem);

	move(2,0);
	clrtoeol();
    }

    CoaxPos = 0;
    setcoax(1);
    setvolume(Volume);
    setwindow(0);

    for (s = scanhead; s != NULL; s = s1) {
	if ((s1 = s->next) == scanhead)
	    s1 = NULL;
	free(s);
    }

    return 0;
}

int
spectrum (filename)
char *filename;

{
    long freq;
    int smeter,ch,lines;
    FILE *out;
#ifdef KEYSFILE
    FILE *fp;
    char buf[80];
#endif

    if ((out = fopen(filename,"w")) == NULL) {
	perror(filename);
	return 0;
    }

    move(2,0);
    clrtoeol();
    move(6,0);
    clrtobot();

    move(0,10);
    printw("Spectrum scan into \"%s\"",filename);
    refresh();

    setscan(0,-1);
    setvolume(0);
    setwindow(1);
    setmode(502);
    readmode();

    lines = 0;

    for (freq = 25000000; freq < 2000000000; freq += 10000)
    {
	if (kbhit()) {
#ifdef KEYSFILE
	    if ((fp = fopen(KEYSFILE,"r")) != NULL) {
		if (fgets(buf,sizeof(buf),fp) != NULL && buf[0] == 'F')
		    ch = KEY_F0 + atoi(buf + 1);
		    		fclose(fp);
		unlink(KEYSFILE);
	    }
	    else
#endif
	    ch = getch();
	    break;
	}

	setfrequency(freq);
	readfrequency();
	usleep(30000);
	smeter = readsmeter();

	move(2,0);
	refresh();

	fprintf(out,"%ld\t%d\n",freq,smeter);

	if (++lines == 100) {
	    fflush(out);
	    lines = 0;
	}
    }

    fclose(out);
    setwindow(0);
    return 0;
}

void
intvolume (volume)
int volume;

{
    Volume = volume;
    move(4,2);
    clrtoeol();
    printw("Volume: %d",Volume);
}


/* CI-V low level routines */

/* read frequency range, return as formatted line */

int
readrange (line)
char *line;

{
    int len;
    unsigned char buf[BUFLEN];

    line[0] = '\0';

    sendpkt(0x02,buf,0);

    if ((len = readreply(buf)) != 16 || buf[4] != 2 || buf[10] != 0x2d)
	return -1;

    if (buf[9] != 0)
	sprintf(line,"%2x",buf[9]);

    sprintf(line + strlen(line),"%02x.%02x%x.%x-",
	    buf[8],buf[7],buf[6] >> 4,buf[6] & 0x0f);

    if (buf[15] != 0)
	sprintf(line + strlen(line),"%2x",buf[15]);

    sprintf(line + strlen(line),"%02x.%02x%x.%x MHz",
	    buf[14],buf[13],buf[12] >> 4,buf[12] & 0x0f);

    return 1;
}

/* read current frequency, return in Hz */

long
readfrequency ()

{
    long freq = 0;
    int i,len;
    unsigned char buf[BUFLEN];

    sendpkt(0x03,buf,0);

    if ((len = readreply(buf)) < 6 || buf[4] != 3)
	return -1;

    if (len == 9 || len == 10)
	for (i = len - 1; i >= 5; i--) {
	    freq *= 100;
	    freq += 10 * (buf[i] >> 4) + (buf[i] & 0x0f);
	}
    else
	if (len != 6)
	    return -1;

    return freq;
}

/* read current mode/bandwidth */

int
readmode ()

{
    unsigned char buf[BUFLEN];

    sendpkt(0x04,buf,0);

    if (readreply(buf) != 7 || buf[4] != 4)
	return -1;

    return 100 * buf[5] + buf[6];
}

/* read squelch status */

int
readsquelch ()

{
    unsigned char buf[BUFLEN];

    buf[5] = 1;					/* read squelch subcmd */
    sendpkt(0x15,buf,1);

    if (readreply(buf) != 7 || buf[4] != 0x15 || buf[5] != 1)
	return -1;

    return buf[6];				/* 0=closed 1=open */
}

/* read S-meter value (0..255) */

int
readsmeter ()

{
    unsigned char buf[BUFLEN];

    buf[5] = 2;					/* read s-meter subcmd */
    sendpkt(0x15,buf,1);

    if (readreply(buf) != 8 || buf[4] != 0x15 || buf[5] != 2)
	return -1;

    return 100 * buf[6] + 10 * (buf[7] >> 4) + (buf[7] & 0x0f);
}

/* set VFO frequency */

int
setfrequency (freq)
long freq;

{
    int i,subfreq;
    unsigned char buf[BUFLEN];

    for (i = 5; i <= 9; i++) {
	subfreq = (int)(freq % 100);
	freq /= 100;
	buf[i] = 16 * (subfreq / 10) + (subfreq % 10);
    }

    sendpkt(0x05,buf,5);

    return readreply(buf) == 5 && buf[4] == 0xfb;
}

/* set mode/bandwidth */

int
setmode (mode)
int mode;

{
    unsigned char buf[BUFLEN];

    buf[5] = mode / 100;			/* convert to BCD */
    mode -= 100 * buf[5];
    buf[6] = 16 * (mode / 10) + (mode % 10);
    sendpkt(0x06,buf,2);

    return readreply(buf) == 5 && buf[4] == 0xfb;
}

/* set window (0/1) on R-7100 */

int
setwindow (window)
int window;

{
    unsigned char buf[BUFLEN];

    buf[5] = 0xe0;				/* set window subcmd */
    buf[6] = window;
    sendpkt(0x07,buf,2);

    return readreply(buf) == 5 && buf[4] == 0xfb;
}

/* select memory */

int
setmemory (memory)
int memory;

{
    int m;
    unsigned char buf[BUFLEN];

    buf[5] = memory / 100;			/* convert to BCD */
    m = memory - 100 * buf[5];
    buf[6] = 16 * (m / 10) + (m % 10);
    sendpkt(0x08,buf,2);

    if (readreply(buf) == 5 && buf[4] == 0xfb) {
	move(2,2);
	printw("Mem: %03d",memory);
	return 1;
    } else
	return 0;
}

/* write current freq/mode to selected memory */

int
memwrite ()

{
    unsigned char buf[BUFLEN];

    sendpkt(0x09,buf,0);

    return readreply(buf) == 5 && buf[4] == 0xfb;
}

/* clear current memory */

int
memclear ()

{
    unsigned char buf[BUFLEN];

    sendpkt(0x0b,buf,0);

    return readreply(buf) == 5 && buf[4] == 0xfb;
}

/* stop or start a scan, see below */
/* scan1=0x00		Stop scan / stop window scan */
/* scan1=0x01		Start programmed scan / memory scan */
/* scan1=0x02		Start programmed scan */
/* scan1=0x03		Start delta-f scan */
/* scan1=0x04		Start auto memory-write scan */
/* scan1=0x12		Start fine programmed scan */
/* scan1=0x13		Start fine delta-f scan */
/* scan1=0x22		Start memory scan */
/* scan1=0x23		Start selected number memory scan */
/* scan1=0x24		Start selected mode memory scan */
/* scan1=0x42		Start priority / window scan */
/* scan1=0xA0		Unfix center freq for delta-f scan */
/* scan1=0xAA		Fix center freq. for delta-f scan */
/* scan1=0xA1		Set delta-f to +- 2.5kHz */
/* scan1=0xA2		Set delta-f to +- 5kHz */
/* scan1=0xA3		Set delta-f to +- 10kHz */
/* scan1=0xA4		Set delta-f to +- 20kHz */
/* scan1=0xA5		Set delta-f to +- 50kHz */
/* scan1=0xA6		Set delta-f to +- 500kHz */
/* scan1=0xA7		Set delta-f to +- 1000kHz */
/* scan1=0xB0		Include memory for scan */
/* scan1=0xB1		Exclude memory for scan */
/* scan1=0xB2		Set scan number */
/* scan1=0xC0		VSC off */
/* scan1=0xC1		VSC on */
/* scan1=0xD0		Scan resume indefinetely */
/* scan1=0xD1		Scan resume OFF IC-R9000 */
/* scan1=0xD2		Scan resume "B" IC-R9000 */
/* scan1=0xD3		Scan resume "A" IC-R9000 */

int
setscan (scan1,scan2)
int scan1,scan2;

{
    int len = 1;
    unsigned char buf[BUFLEN];

    buf[5] = scan1;
    if (scan2 >= 0) {
	buf[6] = scan2;
	len++;
    }

    sendpkt(0x0e,buf,len);

    return readreply(buf) == 5 && buf[4] == 0xfb;
}

/* set tuning step, see below for R-7100 steps */
/* step=0	100 Hz */
/* step=1	1 KHz */
/* step=2	5 KHz */
/* step=3	10 KHz */
/* step=4	12.5 KHz */
/* step=5	20 KHz */
/* step=6	25 KHz */
/* step=7	100 KHz */

int
setts (step)
int step;

{
    unsigned char buf[BUFLEN];

    buf[5] = step;
    sendpkt(0x10,buf,1);

    return readreply(buf) == 5 && buf[4] == 0xfb;
}

/* set attenuator in dB.  R-7100 accepts 0 and 20 */

int
setatt (att)
int att;

{
    unsigned char buf[BUFLEN];

    buf[5] = att;
    sendpkt(0x11,buf,1);

    return readreply(buf) == 5 && buf[4] == 0xfb;
}

/* set audio volume (0..255) */

int
setvolume (volume)
int volume;

{
    int v;
    unsigned char buf[BUFLEN];

    buf[5] = 1;					/* set volume subcmd */
    buf[6] = volume / 100;			/* convert to BCD */
    v = volume - 100 * buf[6];
    buf[7] = 16 * (v / 10) + (v % 10);
    sendpkt(0x14,buf,3);

    if (readreply(buf) == 5 && buf[4] == 0xfb) {
	move(2,55);
	printw("Volume: %d  ",volume);
	return 1;
    } else
	return 0;
}

int
readreply (buf)
unsigned char *buf;

{
    int len;

    while ((len = readpkt(buf)) > 0 && buf[2] != OURADDR)
	decode_civ(buf,len);

    if (len > 0 && (buf[4] != 0xfa && buf[4] != 0xfb))
	decode_civ(buf,len);			/* don't decode OK/ERROR */

    return len;
}

/* RS232-line routines (initialization and CI-V pkt send/receive) */

int
openline (name)
char *name;

{
    struct termios tios;

    if ((LineFd = open(name,O_RDWR)) < 0) {
	perror(name);
	return 1;
    }

    if (ioctl(LineFd,TCGETS,&tios) < 0) {
	perror(name);
	return 1;
    }

    tios.c_iflag = IGNBRK|IGNPAR;
    tios.c_oflag = 0;
    tios.c_cflag = B9600|CS8|CREAD|CLOCAL;
    tios.c_lflag = ICANON;
    tios.c_line = N_TTY;
    memset(tios.c_cc,0,sizeof(tios.c_cc));
    tios.c_cc[VKILL] = 0xfc;			/* jamming char */
    tios.c_cc[VEOL] = 0xfd;			/* end of packet char */

    if (ioctl(LineFd,TCSETS,&tios) < 0) {
	perror(name);
	return 1;
    }

    return 0;
}

int
readpkt (buf)
unsigned char *buf;

{
    int len;
    fd_set select_set;
    struct timeval timeval;

    FD_ZERO(&select_set);

    do {
	FD_SET(LineFd,&select_set);
	timeval.tv_sec = 3;
	timeval.tv_usec = 0;

	if (select(LineFd + 1,&select_set,NULL,NULL,&timeval) < 0)
	    return 0;

	if (!FD_ISSET(LineFd,&select_set))
	    return 0;

	if ((len = read(LineFd,buf,BUFLEN)) <= 0)
	    return 0;
    }
    while (buf[0] != 0xfe || buf[1] != 0xfe ||	/* check preamble */
	   (buf[2] != BROADCAST && buf[2] != OURADDR) || /* check dest */
	   buf[3] != DEVADDR);			/* check source */

    return len - 1;
}

int
sendpkt (cmd,buf,len)
unsigned char cmd;
unsigned char *buf;
int len;

{
    buf[0] = buf[1] = 0xfe;			/* preambles */
    buf[2] = DEVADDR;				/* destination */
    buf[3] = OURADDR;				/* source */
    buf[4] = cmd;				/* command byte */
    buf[len + 5] = 0xfd;			/* end of packet */

    return (write(LineFd,buf,len + 6) == (len + 6));
}

/* general-purpose routines */

int
kbhit ()					/* check if kb input present */

{
    fd_set select_set;
    struct timeval tv;

    FD_ZERO(&select_set);
    memset(&tv,0,sizeof(tv));

    FD_SET(0,&select_set);

    if (select(1,&select_set,NULL,NULL,&tv) > 0 && FD_ISSET(0,&select_set))
	return 1;

#ifdef KEYSFILE
    if (!access(KEYSFILE,0))
	return 1;
#endif

    return 0;
}

/* set coax switch */
/* the switch is responding to pulse width.  the wider the pulse, the */
/* higher the selected switch position.  widths are 20/40/60/80ms, which */
/* is a startbit plus 0-3 zerobits at 50 baud */

int
setcoax (pos)
int pos;

{
    char buf[1];

    if (pos < 1 || pos > 4)			/* check validity */
	pos = 1;

    move(4,16);
    printw("Coax: %d",pos);

    if (pos == CoaxPos)				/* same pos as before? */
	return 0;

    CoaxPos = pos;				/* remember pos */

    if (CoaxFd < 0)
	return -1;

    buf[0] = 0xff << (pos - 1);			/* 1 bit per position */
    return write(CoaxFd,buf,1);
}

/* read frequency description list into memory */
/* each line of the file has frequency in MHz, tab(s), description, newline */

int
readdescfile (name)
char *name;

{
    FILE *inp;
    struct desc *new;
    double freq;
    char *p,*q;
    unsigned int hash;
    char line[256];

    if ((inp = fopen(name,"r")) == NULL)
	return -1;
    
    while (fgets(line,sizeof(line),inp) != NULL) {
	freq = strtod(line,&p);

	if (p == line || freq < 0)
	    continue;				/* skip garbage */

	while (isspace(*p))
	    p++;				/* skip whitespace */

	if ((q = strrchr(p,'\n')) != NULL)
	    *q = '\0';				/* trim newline */

	if ((q = strrchr(p,'\r')) != NULL)
	    *q = '\0';				/* same for CR (DOS file?) */

	if ((new = calloc(1,sizeof(struct desc))) == NULL ||
	    (new->desc = strdup(p)) == NULL)
	    break;

	new->freq = (long) (1000000.0*freq + 0.5); /* freq in Hz */

	hash = new->freq % DESCHASH;		/* insert in hash chain */
	new->next = descriptions[hash];
	descriptions[hash] = new;
    }

    fclose(inp);
    return 0;
}

/* find description for frequency */

char *
freqdesc (freq)
long freq;

{
    struct desc *sp;
    unsigned int hash;

    hash = freq % DESCHASH;

    for (sp = descriptions[hash]; sp != NULL; sp = sp->next)
	if (freq == sp->freq)
	    return sp->desc;

    return NULL;
}
