/***********************************************************************
 *                                                                     *
 * Program to control LORAN-C radio                                    *
 *                                                                     *
 * This program controls a special-purpose radio designed to receive   *
 * transmissions from the US Coast Guard LORAN-C navigation system.    *
 * These stations operate on an assigned radio frequency of 100 kHz    *
 * and can be received over the continental US, adjacent coastal areas *
 * and significant areas elsewhere in the world.                       *
 *                                                                     *
 * The analog radio and integrated digital controller are contained on *
 * an 8" PC card that plugs directly into the IBM PC bus. The radio    *
 * receives LORAN-C signals consisting of a eight-pulse biphase-       *
 * modulated pulse groups transmitted at a 1-kHz rate. Each of these   *
 * pulse groups is repeated at an interval characteristic of the       *
 * particular LORAN-C chain, which consists of a master station and up *
 * to four slave stations. The radio includes a synchronous detector   *
 * driven by a quadrature-phase clock, two integrators with adjustable *
 * gain and a peak-reading, signal-level detector.                     *
 *                                                                     *
 * The radio is controlled by this program using a special-purpose     *
 * interface, which processes the received signals using an            *
 * analog/digital converter and multiplexor. It generates the digital  *
 * timing and analog control signals using an AMD 9513A System Timing  *
 * Controller (STC) chip, two digital/analog converters and            *
 * miscellaneous logic components. The radio provides three analog     *
 * signals, one for the in-phase integrator, another for the           *
 * quadrature-phase integrator and a third for the signal-level        * 
 * detector. This program computes the master oscillator frequency-    *
 * adjustment voltage and receiver gain-control voltage.               *
 *                                                                     *
 * The receiver supports both an internal uncompensated crystal        *
 * oscillator and an external oven-controlled crystal oscillator used  *
 * to derive all timing signals used by the receiver and this program. *
 * The 5-MHz output of these oscillators is adjustable over a small    *
 * range by this program to coincide with the LORAN-C signal as        *
 * broadcast to within a few parts in 1e10 in both frequency and time. *
 * It is intended for use as a laboratory frequency standard. The      *
 * external oscillator should have good intrinsic stability and        *
 * setability to within less than 0.5 Hz at 5 MHz (0.1 ppm), since it  *
 * must maintain the master clock to within 100 us over the            *
 * pulse-group scan interval up to several minutes.                    *
 *                                                                     *
 * The PC running this program generates the control signals necessary *
 * to run the radio and produces a 1-pps signal synchronized to        *
 * UTC(LORAN) to within a fraction of a microsecond. When manually     *
 * adjusted using time-of-coincidence (TOC) data published by US Naval *
 * Observatory, this signal is suitable for use as a precision source  *
 * of standard time. The system can generate all sorts of external     *
 * signals as well, as programmed in the 9513A.                        *
 *                                                                     *
 * This program requires a data file ("loran.dat") containing the      *
 * geographic coordinates and other information about the LORAN-C      *
 * chains being used. The format of this file is described under the   *
 * subroutine heading init_loran(). There are four files containing    *
 * the source of this program:                                         *
 *                                                                     *
 * loran.c	main program (this file)                                   *
 * gri.c	signal processing subroutines                              *
 * subs.c	input/output and utility subroutines                       *
 * tables.c	initialization tables                                      *
 * loran.h	header file used by all programs                           *
 *                                                                     *
 * This program was developed using Microsoft QuickC for Windows, but  *
 * the production versions are normally compiled to run under ordinary *
 * polymorphic DOS versions.                                           * 
 *                                                                     *
 *      David L. Mills                                                 *
 *		Electrical Engineering Department                              *
 *		University of Delaware                                         *
 *		Newark, DE 19716                                               *
 *		mills@udel.edu                                                 *
 *                                                                     *
 ***********************************************************************
 */

#include "loran.h"
#include <stdlib.h>

/*
 * External function declarations
 */
extern void pulse_group(struct station *, double, double, double);
extern struct station *receive(void);
extern void timerq(struct station *, long);
extern void command(void);
extern void init_station(struct station *);
int init_loran(long);

/*
 * Imported from gri.c
 */
extern int vcodac;				/* vco dac bias (dac a) */
extern int agcdac;				/* agc dac bias (dac b) */
extern int nstation;			/* number of stations allocated */
extern struct station *chain[];	/* station structure pointers */
extern int dindex;				/* display index */
extern int par;					/* mode register */
extern long gri;				/* group repetition interval (CYCLE) */
extern long fri;				/* frame interval (2 * gri) (CYCLE) */
extern long offset;				/* current frame offset (CYCLE) */

/*
 * Imported from tables.c
 */
extern int init[];				/* stc initialization vector */

/*
 * Local data declarations
 */
double iofs;					/* i-integrator offset */
double qofs;					/* q-integrator offset */
int peak_detect;				/* s-signal (adc chan 2) */
double agcavg;					/* receiver agc smoothed signal */
double agcofs;					/* receiver agc offset (zero signal) */

/*
 * Navigation stuff
 */
char rcvr_name[20];				/* receiver name */
double rcvr_lat;				/* receiver latitude (rad) */
double rcvr_lon;				/* receiver longitude (rad) */
double rcvr_delay;				/* antenna-receiver delay (us) */

/*
 * File stuff
 */
FILE *fp_in;					/* file handle */
char loran_data[] = LORSTA;		/* loran-c station data file */

/*
 * Main program
 *
 * Programming note: There is usually enough time between gri intervals
 * for one display line, but not two, at least on a 386/20. The compile
 * parameter DGUARD can be changed to allow more time for this. Note
 * also that the floating-point coprocessor is necessary for this thing
 * to run at all.
 *
 * Usage: <program name> <gri> <codes> <agc> <vco>
 * <gri>	assigned LORAN-C group repitition interval (default 9960)
 * <agcdac>	initial agc dac (0-4095) (default agc parameter)
 * <vcodac>	initial vco dac (0-4095) (default vco parameter)
 *
 * Variables and functions used
 * sptr		station structure pointer
 * agcdac	agc dac bias (dac b)
 * vcodac	vco dac bias (dac a)
 * gri		group repetition interval (CYCLE)
 * fri		frame interval (2 * gri) (CYCLE)
 * offset	current frame offset (CYCLE)
 * nstation	number of stations allocated
 * chain[]	vector of station pointers
 * peak_detect s-signal (adc chan 2)
 * agcavg	receiver agc smoothed signal
 * agcofs	receiver agc offset (zero signal)
 * command() decode keyboard tinkle
 * pulse_group() process pulse group
 * init_loran() initialize loran chain data
 * init_station() initialize station data structure
 * receive() wait for next gri and read adc
 * timerq()	insert delay on timer queue
 */
int main(argc, argv) int argc; char *argv[];
	{
	struct station *sptr;		/* station pointer temps */
	int i;						/* utility ints */

/*
 * Decode command-line arguments
 *
 */
	gri = 9960;				/* default northeast chain */
	if (argc > 1)
		sscanf(argv[1], "%li", &gri);
	if (argc > 2)
		sscanf(argv[3], "%i", &agcdac);
	if (argc > 3)
		sscanf(argv[4], "%i", &vcodac);

/*
 * Initialization
 *
 * This section runs only once. It resets the timing generator,
 * loads its registers with default values and clears arrays. The
 * program then begins the initialization sequence using the default
 * station structure.
 */
#if !defined(DEBUG)
	outp(TGC, RESET); outp(TGC, LOAD+0x1f); /* reset STC chip */
	outp(TGC, LOADDP+MASTER); outp(TGD, 0xf0); outp(TGD, 0x8a);
	outp(TGC, LOADDP+1);
	for (i = 0; i < 5*3; i++) {
		outp(TGD, init[i]); outp(TGD, init[i] >> 8);
		}
	outp(TGC, LOADARM+0x1f);	/* let the good times roll */
/*      par = outp(PAR, par | ENG); outp(ADC, I); /* i */
#endif

	/*
	 * Initialize up to six stations for selected chain. Start the first
	 * one in mode 1 to calibrate the receiver. The others will be
	 * started in mode 4. Give up if there are no stations found.
	 */
	for (i = 0; i < NSTA; i++) {
		sptr = (struct station *)malloc(sizeof(struct station));
		chain[i] = sptr;
		memset(sptr, 0, sizeof(struct station));
		sptr->index = i;
		init_station(sptr);
		sptr->type = ' ';
		sptr->kbd = 'l';			/* *** temp *** */
		}
	nstation = init_loran(gri);
	if (nstation == 0) {
		printf("No stations found\n");
		return (-1);
		}
	fri = 2 * gri;
	offset = 0;
	sptr = chain[nstation - 1];
	sptr->mode = MODE_MIN;
	timerq(sptr, 0);
	sprintf(sptr->report, "Calibrating receiver");

	/*
	 * Main loop
	 *
	 * This is the main receiver loop and runs until escaped by a ^C
	 * signal. The main loop runs twice per frame or once each gri
	 * (pulse groups a and b) and performs the main receiver update
	 * between the end of pulse group b and the beginning of pulse group
	 * a.
	 */
	while (1) {

		/*
		 * Process i-phase, q-phase and agc
		 *
		 * Note that a LORAN frame consists of two gri intervals a and
		 * b, each with individual pulse codes. The receiver integrates
		 * each gri using the assigned pulse codes. There are two sets
		 * of pulse codes, one for the master station and the other for
		 * slave stations, of which there may be as many as four. Each
		 * LORAN chain is assigned a unique gri interval in the range
		 * 40-100 ms.
		 */
		sptr = receive();
		if (sptr->codesw > 0) {
			sptr->codesw -= 2;

			/*
			 * At the end of gri b the receiver variables are updated
			 * and set up for the next frame (gri a and gri b).
			 */
			sptr->count++;
			pulse_group(sptr, sptr->isig - iofs, sptr->qsig - qofs,
				peak_detect - agcofs);
			}
		else {

			/*
			 * At the end of gri a the command interpreter is
			 * interrogated and the display updated, if necessary.
			 */
#if !defined(DEBUG)
			if (kbhit())
				command();
#endif
			if (sptr->report[0] != '\0') {
				puts(sptr->report);
				sptr->report[0] = '\0';
				}
			}
		sptr->codesw++;
		timerq(sptr, sptr->phase);
		sptr->phase = 0;
		}
	}

/*
 * Subroutine init_loran()
 *
 * This subroutine to fetch station data and initializes the station
 * data structures. See the tables.c module for an index of the chain
 * gri, chain names and location names.
 *
 * The first line in the station data file has the format:
 *
 * 39 40 48.18  75 45 03.06 10.0 Newark, DE
 * xxxxxxxxxxx  yyyyyyyyyyy dddd nnnnnnnnnn
 *
 * x	receiver north latitude (deg, min, sec.frac)
 * y	receiver west longitude (deg, min, sec.frac)
 * d	receiver internal delay (us)
 * n	receiver location name
 *
 * The remaining lines have the format:
 *
 * 9960 x 41 15 12.046  69 58 38.536 26969.93  325 Nantucket, MA
 * gggg s xxxxxxxxxxxx  yyyyyyyyyyyy dddddddd  ppp nnnnnnnnnnnnn
 *
 * g	chain gri (cycles)
 * s	transmitter station identifier (master m; slaves w,...)
 * x	transmitter north latitude (deg, min, sec.frac)
 * y	transmitter west longitude (deg, min, sec.frac)
 * d	transmitter emission delay (us)
 * p	transmitter radiated power (kW)
 * n	transmitter location name
 *
 * Note: the stations must be listed master first followed by the slaves
 * in order of emission delay.
 *
 * Calling sequence: n = init_loran(sel)
 *
 * sel			gri of selected chain
 * n			number of stations allocated
 *
 * Variables and functions used
 * sptr			station data structure
 * loran_data	name of station data file
 * chain[]		vector of station data structure pointers
 */
int init_loran(sel)
	long sel;					/* gri of selected chain */
	{
	long gri;					/* group repetition interval (CYCLE) */
	char type;					/* station ident (m, w, x, y, z) */
	double lat[3];				/* north latitude (deg, min, sec) */
	double lon[3];				/* west longitude (deg, min, sec) */
	double ems_delay;			/* emission delay (us) */
	double rad_power;			/* radiated power (kw) */
	char name[20];				/* location name */
	int temp;					/* int temps */
	struct station *sptr;		/* station pointer temps */
	double theta, d;			/* double temps */

	/*
	 * Open data file read-only and display header.
	 */
	temp = 0;
	if ((fp_in = fopen (loran_data, "r")) == NULL) {
		printf("LORAN-C data file %s not found\n", loran_data);
		return (temp);
		}
	fscanf(fp_in, "%lf%lf%lf%lf%lf%lf%lf %20[^\n]", &lat[0], &lat[1],
		&lat[2], &lon[0], &lon[1], &lon[2], &rcvr_delay, &rcvr_name);
	rcvr_lat = (lat[0] + (lat[1] + lat[2] / 60) / 60) * D2R;
	rcvr_lon = (lon[0] + (lon[1] + lon[2] / 60) / 60) * D2R;
	printf("LORAN-C stations at GRI%5i\nLocation Name           Latitude  Longitude  Em Delay  Pa Delay  kW\n", sel);
	printf("%-20s %c%10.5lf %10.5lf%10.2lf\n", rcvr_name, 'r',
		rcvr_lat * R2D, rcvr_lon * R2D, rcvr_delay);

	/*
	 * Read next record and filter out all except requested gri.
	 */
	while (fscanf(fp_in, "%li %c%lf%lf%lf%lf%lf%lf%lf%lf %20[^\n]",
			&gri, &type, &lat[0], &lat[1], &lat[2], &lon[0], &lon[1],
			&lon[2], &ems_delay, &rad_power, name) != EOF) {
		if (sel != gri)
			continue;

		/*
		 * Allocate and initialize data structure. Convert geographic
		 * coordinates to radians north and radians west.
		 */
		sptr = chain[temp];
		temp ++;
		sptr->type = type;
		sptr->lat = (lat[0] + (lat[1] + lat[2] / 60) / 60) * D2R;
		sptr->lon = (lon[0] + (lon[1] + lon[2] / 60) / 60) * D2R;
		sptr->ems_delay = ems_delay;
		sptr->rad_power = rad_power;
		strcpy(sptr->name, name);

		/*
		 * Compute transmitter and receiver bearings, great-circle
		 * distance and path delay.
		 */
		theta = rcvr_lon - sptr->lon;
		if (theta >= PI)
			theta = theta - PID;
		if (theta <= -PI)
			theta = theta + PID;
		d = acos(sin(rcvr_lat) * sin(sptr->lat) + cos(rcvr_lat) *
			cos(sptr->lat) * cos(theta));
		if (d < 0)
			d = PI + d;
		sptr->rcvr_azim = acos((sin(sptr->lat) - sin(rcvr_lat) *
			cos(d)) / (cos(rcvr_lat) * sin(d)));
		if (sptr->rcvr_azim < 0)
			sptr->rcvr_azim = PI + sptr->rcvr_azim;
		if (theta < 0)
			sptr->rcvr_azim = PID - sptr->rcvr_azim;
		sptr->xmtr_azim = acos((sin(rcvr_lat) - sin(sptr->lat) *
			cos(d)) / (cos(sptr->lat) * sin(d)));
		if (sptr->xmtr_azim < 0)
			sptr->xmtr_azim = PI + sptr->xmtr_azim;
		if (theta >= 0)
			sptr->xmtr_azim = PID - sptr->xmtr_azim;
		sptr->path_delay = R * d / VOFL * 1e9;
		printf("%-20s %c%10.5lf %10.5lf%10.2lf%10.2lf%5.0lf\n",
			sptr->name, sptr->type, sptr->lat * R2D, sptr->lon * R2D,
			sptr->ems_delay, sptr->path_delay, sptr->rad_power);
		}
	fclose(fp_in);
	return (temp);
	}

/* end program */
