/***********************************************************************
 *                                                                     *
 * Program to control LORAN-C radio                                    *
 *                                                                     *
 * Signal acquisition and tracking routines                            *
 *                                                                     *
 ***********************************************************************
 */

#include "loran.h"
#include <stdlib.h>

/*
 * External function declarations
 */
extern struct station *receive(void);
extern void timerq(struct station *, long);
extern void adj_freq(struct station *, double);
extern char station_type(struct station *);
extern double local_time(struct station *);

/*
 * Local function declarations
 */
void status(struct station *, double, double, char *);
void display(struct station *);
void update_station(struct station *);
void init_station(struct station *);
void init_track(struct station *);
void command(void);

/*
 * Imported from loran.c
 */
extern double rcvr_delay;		/* antenna-receiver delay (us) */

/*
 * Imported from subs.c
 */
extern double epoch;			/* elapsed time since startup (us) */

/*
 * Imported from tables.c
 */
extern double envcyc[];			/* LORAN-C model cycle envelope */
extern double wgtlms[];			/* initial LMS weights */

/*
 * Local data declarations
 */
double rank[RMAX];				/* pulse group rank */
long irank[RMAX];				/* offset of pulse group */
int nrank = 0;					/* number of ranks */
int nstation = 0;				/* number of stations */
struct station *chain[NSTA];	/* vector of station pointers */
double delay[NSTA];				/* vector of station offsets */
double master_delay;			/* master pulse-group delay (us) */
long gri;						/* group repetition interval (cycle) */
long fri;						/* frame interval (2 * gri) (cycle) */
long offset;					/* current frame offset (cycle) */
int synch;						/* station synch count */
int dindex = 9;					/* display station index */
int par;						/* mode register */

/*
 * Dac variables. The vcodac and agcdac variables are offsets which are
 * normally established during initial receiver alignment and master
 * oscillator calibration; however, they can be adjusted during
 * operation by keyboard commands. The vco and agc variables are
 * computed by the program to establish receiver gain and oscillator
 * frequency.
 */
int vco = VCO;					/* vco dac signal */
int vcodac = VCO;				/* vco dac bias (dac a) */
int agc = AGC;					/* agc dac signal */
int agcdac = AGC;				/* agc dac bias (dac b) */

/*
 * Adc variables. The iofs, qofs and agcofs variables are offsets which
 * are established during receiver calibration when this program is
 * first stated. The s_raw, acgavg and agcmax variables are a clumsy
 * attempt to bracket the peak-reading signal for use as an initial
 * gain estimate.
 */
double iofs = ADCOFS * 2;		/* i-integrator offset */
double qofs = ADCOFS * 2;		/* q-integrator offset */
int s_raw = 0;					/* s-signal (adc chan 2) */
double agcavg = 0;				/* receiver agc smoothed signal */
double agcofs = ADCOFS * 2;		/* receiver agc offset (zero signal) */
double agcmax = ADCOFS * 2;		/* receiver agc max signal (overload) */

/*
 * Subroutine pulse_group()
 *
 * This routine does most of the signal-processing functions.
 *
 * Calling sequence: pulse_group(sptr, i_sig, q_sig, s_sig)
 *	sptr		station structure pointer
 *	i_sig		adc i channel (0)
 *	q_sig		adc q channel (1)
 *	s_sig		adc s channel (2)
 *
 * Variables and functions used (math library)
 *	sqrt()		square rootsie
 *	fabs()		absolute value (may be macro)
 *	log10()		logarithm to base 10
 *	others...	to be documented
 */
void pulse_group(sptr, i_sig, q_sig, s_sig)
	struct station *sptr;		/* station structure pointer */
	double i_sig;				/* adc i channel (0) */
	double q_sig;				/* adc q channel (1) */
	double s_sig;				/* adc s channel (2) */
	{
	struct station *rptr;		/* station structure pointer temp */
	int i, j, k, temp;			/* utility temps */
	double dtemp, etemp, ftemp, gtemp; /* utility doubles */
	double ftmp[MMAX];			/* double temporary list */

	switch (sptr->mode) {

		/*
		 * In calibrate mode 1 the receiver gain is set to minimum. The
		 * program waits for the averages to settle and then calculates
		 * the integrator offsets and agc minimum value, which takes a
		 * few seconds. When done, the program enters MODE_MAX to
		 * complete receiver calibration.
		 *
		 * In this mode the vco dac is clamped and the agc dac is set to
		 * minimum (255).
		 */
		case MODE_MIN:
		vco = vcodac; sptr->level = 4095;
		iofs += i_sig / AGCFAC; qofs += q_sig / AGCFAC;
		agcofs += s_sig / AGCFAC;
		if (i_sig * i_sig + q_sig * q_sig + s_sig * s_sig < 3 &&
			sptr->count > AGCFAC * 2) {
			sptr->mode = MODE_MAX;
			sptr->count = 0;
			sprintf(sptr->report,
				"iofs %4.1lf  qofs %4.1lf  agcofs %4.1lf", iofs, qofs,
				agcofs);
			}
		return;

		/*
		 * In calibrate mode 2 the receiver gain is set to  maximum. The
		 * program waits for the averages to settle and then calculates
		 * the agc maximum value, which takes a few seconds. When done,
		 * the program enters MODE_AGC to establish the normal receiver
		 * gain configuration.
		 *
		 * In this mode the vco dac is clamped and the agc dac is set to
		 * maximum (0).
		 */
		case MODE_MAX:
		vco = vcodac; sptr->level = 0;
		dtemp = s_sig - agcmax; agcmax += dtemp/AGCFAC;
		if (dtemp * dtemp < 1 && sptr->count > AGCFAC * 2) {
			sptr->mode = MODE_AGC;
			sptr->count = 0;
			agcavg = 0;
			vco = vcodac; sptr->level = agcdac;
			}
		return;

		/*
		 * In calibrate mode 3 the receiver gain is determined by the
		 * receiver agc, which is a peak-indicating type sensitive to
		 * the peak pulse power in the 90-110 kHz spectrum. This step,
		 * which takes a few seconds, is necessary only to insure the
		 * receiver operates at the best signal/noise ratio and without
		 * overload during the scanning process. When done, the program
		 * enters MODE_PSRCH to begin the pulse-group scan.
		 *
		 * In this mode the vco dac is clamped and the agc dac is
		 * computed from the receiver agc smoothed output.
		 *
		 * *** This part not finished yet ***
		 */
		case MODE_AGC:
		vco = vcodac; sptr->level = agcdac;
		if (sptr->count > AGCFAC * 2) {
			sprintf(sptr->report,
				"agcmax %4.1lf  agcavg %4.1lf\nSearching for signal",
				agcmax, agcavg);

			/*
			 * Tricky, tricky. The first and third processes look for
			 * the a interval plus the first subinterval of the b
			 * interval, while the second and fourth look for the b
			 * interval less the first subinterval.
			 */
			for (synch = 0; synch < nstation; synch++) {
				rptr = chain[synch];
				init_station(rptr);
				if (rptr->index > 3)
					rptr->mindex = 0;
				else if (rptr->index == 1 || rptr->index == 3) {
					rptr->mindex = (int)(gri - (gri / nstation)) / 10;
					rptr->codesw--;
					}
				else
					rptr->mindex = (int)(gri + (gri / nstation)) / 10;
				rptr->phase += synch * (gri / nstation);
				if (synch < nstation - 1) {
					timerq(rptr, rptr->phase);
					rptr->phase = 0;
					}
				}
			}
		return;

		/*
		 * In acquisition mode 4 the radio scans at 100 us per sample
		 * over the entire frame fri = 2 * gri, which takes up to about
		 * 3.5 minutes in a two-way interleaved scan. There are four
		 * processes sampling the signal in the 400-us envelope gate
		 * staggered about 20 ms from each other. Two of these sample
		 * the master pulse code, while the other two sample the slave
		 * pulse code. The program accumulates the ten most recent
		 * received rms signal samples in a pulse-gate filter. The last
		 * nine stages are used as a matched filter. In the three most
		 * recent samples, including two in the filter and the current
		 * sample, either the first or last must be at least 1/XGATE the
		 * second, or the second is most likely a noise spike, which
		 * could be due to a pulse group from another chain just
		 * happening to appear in the pulse-code window. In addition,
		 * the program computes a weighted sum which favors the three
		 * samples at the middle of the last nine stages in the filter.
		 * The program then inserts the value, index and code type in a
		 * rank-order filter sorted by value. Note that sample selection
		 * from the pulse-group filter is valid only after ten samples
		 * have been received. Therefore, the program waits for ten
		 * samples before processing and wraps around for ten samples
		 * beyond the end of the frame. When the scan is complete, the
		 * program enters MODE_CSRCH to begin the envelope scan.
		 *
		 * In this mode the vco dac is clamped and the agc dac remains
		 * at the value computed in the previous mode.
		 */
		case MODE_PSRCH:
		sptr->phase += 10;

		/*
		 * This section is entered when the pulse-group search is
		 * complete, but before the stations have been initialized for
		 * cycle-search mode. Until the master shows up, just idle.
		 * Then, for each station in turn starting with the master,
		 * compute its offset relative to the master-station delay. Then
		 * initialize for the cycle-search mode and let the good times
		 * roll. For future reference, note that the specified guard
		 * interval for LORAN-C is 990 cycles (1090 cycles following the
		 * master pulse group); however, it currently is 1100 cycles
		 * (PGUARD) in this program, because the 386/387 cpu/fpu are too
		 * slow.
		 */
		if (synch <= 0) {
			if (sptr->index == 0)
				synch = 0;
			if (synch < 0)
				return;
			init_track(sptr);
			sptr->vfreq = sptr->vphase = 0;
			ftemp = (delay[sptr->index] + master_delay) / CYCLE -
				offset;
			sptr->phase += (long)ftemp;
			return;
			}

		/*
		 * Compute the rms envelope value and update the matched filter.
		 * Replace noise spikes with the average of the next previous
		 * and next following samples.
		 */
		dtemp =	i_sig * i_sig + q_sig * q_sig;
		sptr->fmin += dtemp;
		dtemp = sqrt(dtemp);
		if (sptr->rms[0] > dtemp * XGATE ||
			sptr->rms[0] > sptr->rms[1] * XGATE) {
			sptr->rms[0] = (dtemp + sptr->rms[1]) / 2;
			sptr->pncnt++;
			}
		for (i = NRMS - 1; i > 0; i--)
			sptr->rms[i] = sptr->rms[i - 1];
		sptr->rms[0] = dtemp;
		if (sptr->count < NRMS || sptr->count > sptr->mindex + NRMS)
			return;

		/*
		 * At the conclusion of pulse-group search, prune the master
		 * samples from the rank data structure and set up for the
		 * cycle-search mode.
		 */
		if (sptr->count == sptr->mindex + NRMS) {
			synch--;
			if (sptr->index == 0 || sptr->index == 2 ||
				sptr->index == 4)
				sptr->codesw--;
			delay[sptr->index] = sptr->ems_delay + sptr->path_delay +
				rcvr_delay;
			if (synch == 0) {
				synch = -1;
#if defined(DEBUG)
				for (i = 0; i < nrank; i++)
					printf("%3i%6li%6.1lf\n", i, irank[i], rank[i]);
#endif
				status(sptr, rank[0], rank[nrank - 1], " cycle search");
				master_delay = irank[0] * CYCLE - delay[0];
				}
			return;
			}

		/*
		 * This section runs every 100 us for each station. It
		 * implements a 300-us, edge-enhanced, matched filter and noise
		 * gate. With a width of three signal samples in the shift
		 * register, this gives about a 2dB improvement in the snr. The
		 * output samples are inserted in a list along with the
		 * associated index and station type and sorted by sample value.
		 * Note that (temporarily) the maximum master station sample is
		 * always inserted at the head of the list and the remainder of
		 * the list contains only slaves. This won't work well when the
		 * receiver is in close proximity to a slave station. Also note
		 * we keep only the highest RMAX samples.
		 */
		dtemp = sptr->rms[4] + sptr->rms[5] + sptr->rms[6];
		dtemp -= (sptr->rms[1] + sptr->rms[2] + sptr->rms[3] +
			sptr->rms[7] + sptr->rms[8] + sptr->rms[9]) / 2;
		if (dtemp > sptr->fmax) {
			sptr->fmax = dtemp;
			sptr->maxdex = (int)offset;
			}
		if (((sptr->count + 1) % 100) == 0) {
			status(sptr, sptr->fmax, sqrt(sptr->fmin / sptr->count),
				"\0");
			sptr->fmax = 0;
			}
		if (station_type(sptr) == 'm') {
			if (dtemp > rank[0]) {
				rank[0] = dtemp;
				irank[0] = offset - OFFSET;
				}
			return;
			}
		for (i = 1; i < nrank && dtemp <= rank[i]; i++);
		if (nrank < RMAX - 1)
			nrank++;
		for (j = nrank; j > i; j--) {
			rank[j] = rank[j - 1];
			irank[j] = irank[j - 1];
			}
		rank[i] = dtemp;
		irank[i] = offset - OFFSET;
		return;

		/*
		 * In cycle-search mode 5 and tracking mode 6, the program scans
		 * the i and q channels one sample per cycle over the 400-us
		 * pulse-group gate interval (mode 5) or 90-us cycle-search
		 * window (mode 6). The program develops the phase-orrection
		 * (vco) and gain-control (agc) signals from these signals.
		 *
		 * There are three subcycles which control this program, each
		 * associated with an index/counter. The envelope-scan cycle
		 * (icnt) decrements from an upper limit (envtop) to a lower
		 * limit (envbot), scanning over each cycle of the pulse. The
		 * integration cycle (mcnt) counts the number of envelope-scan
		 * cycles during which signal characteristics such as
		 * signal/noise ratios, median filters and strobe samples are
		 * accumulated. The parameter cycle (ecnt) increments over an
		 * interval depending on the time constant of integration. At
		 * the conclusion of each interval the time constant of
		 * integration (pfac) and agc voltage are updated.
		 *
		 * In this mode the vco dac follows the i channel and the
		 * agc follows the q channel, both averaged in complicated
		 * ways.
		 */
		default:

 		/*
		 * The phase signal is represented by the i channel and the
		 * amplitude signal by the q channel. Separate median filters
		 * for the phase (pmed) and amplitude (emed) signals are used
		 * for each cycle of the pulse to help cope with cross-rate
		 * interference and dual-rate transmitter blanking. If the
		 * filter is less than the maximum size (MMAX), the median
		 * position determines the output value; otherwise, the output
		 * value is the mean of the median sample and the samples on
		 * either side of it. The phase output (pcyc) is averaged by the
		 * phase-lock loop (pll) subroutine (adj_freq()), while the
		 * envelope-amplitude (ecyc) and envelope-energy (qcyc) signals
		 * are averaged using an adaptive time constant (pfac).
		 */
 		for (i = sptr->mcnt - 1; i > 0; i--) {
			sptr->pmed[sptr->env][i] = sptr->pmed[sptr->env][i - 1];
			sptr->emed[sptr->env][i] = sptr->emed[sptr->env][i - 1];
			}
		sptr->pmed[sptr->env][0] = -i_sig;
		sptr->emed[sptr->env][0] = -q_sig;
		for (i = 0; i < sptr->mcnt; i++){
			ftmp[i] = sptr->pmed[sptr->env][i];
			for (j = 0; j < i; j++) {
				if (ftmp[i] < ftmp[j]) {
					gtemp = ftmp[i]; ftmp[i] = ftmp[j];	ftmp[j] = gtemp;
					}
				}
			}
		i = sptr->mcnt / 2;
		if (sptr->mcnt < MMAX)
			dtemp = ftmp[i];
		else
			dtemp = (ftmp[i - 1] + ftmp[i] + ftmp[i + 1]) / 3;
		sptr->pcyc[sptr->env] = dtemp;
		for (i = 0; i < sptr->mcnt; i++){
			ftmp[i] = sptr->emed[sptr->env][i];
			for (j = 0; j < i; j++) {
				if (ftmp[i] < ftmp[j]) {
					gtemp = ftmp[i]; ftmp[i] = ftmp[j];	ftmp[j] = gtemp;
					}
				}
			}
		i = sptr->mcnt / 2;
		if (sptr->mcnt < MMAX)
			ftemp = ftmp[i];
		else
			ftemp = (ftmp[i - 1] + ftmp[i] + ftmp[i + 1]) / 3;
		sptr->ecyc[sptr->env] += (ftemp - sptr->ecyc[sptr->env])
			/ sptr->pfac;
		etemp = dtemp * dtemp + ftemp * ftemp;
		sptr->qcyc[sptr->env] += (etemp - sptr->qcyc[sptr->env])
			/ sptr->pfac;

		/*
		 * This section computes the rms envelope error and finds cycles
		 * of max energy and min error. The decision metric is computed
		 * as the min rms difference between the envelope and a
		 * prestored model. The LMS algorithm (unfinished) is used to
		 * clean up the waveshape in case of interfering CW signals, but
		 * only if the reference cycle (strobe point) has been found. If
		 * so, that cycle is used to adjust station frequency and phase;
		 * otherwise, the cycle of maximum energy is used.
		 */
		if (sptr->qcyc[sptr->env] > sptr->fmax) {
			sptr->fmax = sptr->qcyc[sptr->env];
			sptr->maxdex = sptr->env;
			}
		if (sptr->ecyc[sptr->maxdex] != 0)
			ftemp = 100 / sptr->ecyc[sptr->maxdex];
		else
			ftemp = 0;
		etemp = 0;
		sptr->lms[sptr->env] = 0;
		for (i = 0; i < NLMS; i++) {
			k = sptr->env - (NLMS - 1) + i;
			if (k < sptr->envbot)
				k = sptr->envbot;
			sptr->lms[sptr->env] += sptr->ecyc[k] * sptr->weight[i];
			if (i > NLMS - 2)
				continue;
			j = sptr->env + i;
			if (j > sptr->envtop)
				j = sptr->envtop;
			dtemp = (ftemp * sptr->ecyc[j] - envcyc[i + 1]);
			etemp += dtemp * dtemp;
			}
		sptr->erms[sptr->env] = sqrt(etemp / (NLMS - 2));
		if (sptr->erms[sptr->env] < sptr->fmin) {
			sptr->fmin = sptr->erms[sptr->env];
			sptr->mindex = sptr->env;
			}
		if (sptr->strobe != 0) {
			k = sptr->env - sptr->envbot;
			dtemp = envcyc[k] - sptr->lms[sptr->env];
			for (i = 0; i < NLMS; i++) {
				k = sptr->env - (NLMS - 1) + i;
				if (k < sptr->envbot)
					k = sptr->envbot;
			sptr->weight[i] += LMS * sptr->ecyc[k] * dtemp;
				}
			}

		/*
		 * This section is entered at the end of the envelope scan. It
		 * calls a subroutine (update_station()) to process the data
		 * collected so far, then displays quantities of interest and
		 * updates the cycle counters.
		 */
		if (sptr->env == sptr->envbot) {
			sptr->fmin = 999; sptr->fmax = -999;
			update_station(sptr);
			if (sptr->strobe != 0) {
				sptr->envbot = sptr->strobe - 3;
				sptr->envtop = sptr->strobe + 5;
				adj_freq(sptr, sptr->pcyc[sptr->strobe]);
				}
			else {
				sptr->envbot = 0;
				sptr->envtop = NENV-1;
				adj_freq(sptr, sptr->pcyc[sptr->maxdex]);
				}
			display(sptr);
			sptr->ptrenv++;
			if (sptr->ptrenv > sptr->envtop)
				sptr->ptrenv = sptr->envbot;
			if (sptr->ptrenv < sptr->envbot)
				sptr->ptrenv = sptr->envtop;
			if (sptr->mcnt < MMIN)
				sptr->mcnt++;
			}
		sptr->env--;
		if (sptr->env < sptr->envbot)
			sptr->env = sptr->envtop;
		if (sptr->env > sptr->envtop)
			sptr->env = sptr->envbot;
		}
	}

/*
 * Subroutine update_station()
 *
 * This subroutine is entered at the end of the envelope scan. It
 * establishes the reference cycle and calculates signal/noise (snr)
 * ratios and adaptive time constants. The reference cycle is determined
 * from the envelope cycle of minimum rms error (mindex) relative to a
 * prestored model. A median filter is used to improve the reliability
 * of this determination. The span of the values in the median filter is
 * calculated for later use as a noise gate.
 */
void update_station(sptr)
	struct station *sptr;
	{
	int i, j, temp;				/* int temps */
	double dtemp, etemp;		/* double temps */
	int tmp[MMAX];				/* int temporary list */

	/*
	 * Figure out what to put in the strobe median filter and determine
	 * the median, which will determine the reference cycle. The
	 * reference cycle is arbitrarily positioned relative to the middle
	 * of the cycle-search window if the minimum and maximum pointers
	 * are too low and positioned relative to the maximum pointer
	 * (maxdex) if the minimum pointer (mindex) is outside a sanity
	 * interval. Otherwise the reference cycle is positioned relative to
	 * the minimum pointer. Note that the first cycle of the window
	 * (cycle zero) is protected, since it is used to develop a noise'
	 * estimate. Of all processing, this section required the most
	 * handcraft.
	 */
	for (i = sptr->mcnt - 1; i > 0; i--)
		sptr->stb[i] = sptr->stb[i - 1];
	if (sptr->mindex < 1 || sptr->maxdex < 8)
		sptr->stb[0] = (sptr->envbot + sptr->envtop) / 2;
	else if (sptr->mindex < sptr->maxdex - 8 || sptr->mindex >
		sptr->maxdex - 4)
		sptr->stb[0] = sptr->maxdex - 6;
	else
		sptr->stb[0] = sptr->mindex;
	for (i = 0; i < sptr->mcnt; i++){
		tmp[i] = sptr->stb[i];
		for (j = 0; j < i; j++) {
			if (tmp[i] < tmp[j]) {
				temp = tmp[i]; tmp[i] = tmp[j];	tmp[j] = temp;
				}
			}
		}
	if (tmp[0] <= 0) {
		sptr->sgate = sptr->mcnt;
		sptr->cycle = sptr->stb[0] + 2;
		}
	else {
		sptr->sgate = tmp[sptr->mcnt - 1] - tmp[0];
		sptr->cycle = tmp[sptr->mcnt / 2] + 2;
		}
	strcpy(sptr->msg, "\0");

	/*
	 * This section runs at the end of the integration cycle (mcnt). It
	 * adjusts the length of the strobe median filters and computes the
	 * metric for cycle identification and the signal/noise ratio. The
	 * metric is the ratio of the reference cycle amplitude to the rms
	 * envelope-cycle difference, which was computed previously. The
	 * receiver signal/noise ratio (snr) is computed as the ratio of the
	 * reference-cycle energy to the cycle-zero (cycle before the first
	 * cycle of the envelope) energy. Note that this ratio is 3dB above
	 * the input snr, due to the processing gain of the synchronous
	 * detector over the two (a and b) gri intervals of the frame.
	 */
	sptr->icnt++;
	if (sptr->icnt >= sptr->mcnt) {
		sptr->icnt = 0;
		if (sptr->mcnt < (int)(sptr->pfac / 8) && sptr->mcnt < MMAX)
			sptr->mcnt++;
		if (sptr->strobe != 0 && sptr->ecyc[sptr->strobe] > envcyc[2])
			etemp = sptr->ecyc[sptr->cycle];
		else
			etemp = sqrt(sptr->qcyc[sptr->cycle]);
		if (etemp > .01) {
			dtemp = sptr->erms[sptr->cycle - 2];
			if (dtemp > .01)
				sptr->esnr = etemp / dtemp;
			dtemp = sqrt(sptr->qcyc[sptr->cycle - 3]);
			if (dtemp > .01)
				sptr->rsnr = etemp / dtemp;
			}
		else
			sptr->esnr = sptr->rsnr = .01;
		}

	/*
	 * This section runs at intervals determined by the time constant of
	 * integration. If the cycle-search metric is greater than the EGATE
	 * threshold and the strobe span is less than the SGATE threshold,
	 * the station is assumed synchronized. If the strobe offset is
	 * outside the central portion of the pulse-group gate (mode 5) or
	 * cycle-search window (mode 6), a cycle slip occurs, the pulse-
	 * group gate is repositioned and the cycle-search process
	 * restarted. If the new offset is more than 400 us below the
	 * nominal delay computed for the receiver position, the new offset
	 * is adjusted upwards 800 us. This artistry is in response to the
	 * receiver's observed tendency to slip downwards under low-
	 * signal/noise conditions and provides a crude scanning mechanism,
	 * while protecting synchronized stations from other stations
	 * marauding the gri looking for slaves. The value of 400 us
	 * corresponds to about one degree of uncertainty in the receiver's
	 * position. If the strobe is inside the central portion, but
	 * different from the preceding value, a strobe slip occurs and the
	 * cycle-search window is repositioned within the current pulse-
	 * group gate.
	 */
	sptr->ecnt++;
	if (sptr->ecnt >= (int)sptr->pfac) {
		sptr->ecnt = 0;
		if (sptr->esnr > EGATE && sptr->sgate < SGATE) {
			if (sptr->cycle < 10 || sptr->cycle > NENV - 10) {
				dtemp = delay[sptr->index] - sptr->system_delay;
				if (dtemp < -400) {
					init_track(sptr);
					dtemp += 800;
					sptr->phase += (long)dtemp;
					}
				else {
					init_track(sptr);
					sptr->phase += sptr->cycle - NENV / 2;
					}
				sprintf(sptr->msg, " cycle slip %-i", sptr->phase);
				return;
				}
			else if (sptr->cycle != sptr->strobe) {
				sprintf(sptr->msg, " strobe slip %-i", sptr->cycle -
					sptr->strobe);
				sptr->mode = MODE_TRACK;
				sptr->strobe = sptr->cycle;
				sptr->borrow = 0;
				sptr->sscnt++;
				}
			}

	/*
	 * If the reference cycle has been found and its amplitude is
	 * sufficient, the receiver gain is determined from the amplitude of
	 * that cycle; otherwise, the gain is determined from the energy of
	 * the maximum cycle. In either case the agc is diddled to set a
	 * normalized value consistent with the prestored model. In the
	 * first case when the mean phase error is less than the PGATE
	 * threshold, the time constant of integration is incremented by a
	 * tad. If not, the tad is taken back. This artistry is designed to
	 * discourage increasing the time constant until the initial phase
	 * transients die down.
	 */
		if (sptr->strobe != 0 && sptr->ecyc[sptr->strobe] > envcyc[2])
			sptr->level += (sptr->ecyc[sptr->strobe] - envcyc[3])
				/ AFAC;
		else
			sptr->level += (sqrt(sptr->qcyc[sptr->maxdex]) - envcyc[7])
				/ AFAC;
		if (sptr->level > 4095)
			sptr->level = 4095;
		else if (sptr->level < 0)
			sptr->level = 0;
		if (sptr->strobe != 0 && sptr->ecyc[sptr->strobe] > envcyc[2] &&
			fabs(sptr->psnr) < PGATE / sptr->pfac && sptr->pfac < PMAX)
			sptr->pfac *= PFAC;
		else if (sptr->pfac > PMIN)
			sptr->pfac /= PFAC;
		}
	}

/*
 * Subroutine init_station()
 *
 * This subroutine is used to initialize station data structures.
 */
void init_station(sptr)
	struct station *sptr;
	{

	sptr->mode = MODE_PSRCH;
	sptr->count = 0;
	sptr->env = 0; sptr->ptrenv = 0;
	sptr->count = 0;
	sptr->phase = 0;
	sptr->cscnt = sptr->sscnt = sptr->pncnt = 0;
	sptr->cycle = sptr->strobe = sptr->slew = sptr->borrow = 0;
	nrank = 0; sptr->pfac = 0;
	sptr->fmax = 0; sptr->fmin = 0;
	sptr->report[0] = '\0'; sptr->msg[0] = '\0';
	sptr->level = agcdac;
	}

/*
 * Subroutine init_track()
 *
 * This subroutine is used to initialize a station data structure for
 * the tracking mode. It is called upon first synchronizing a station
 * and when the envelope gate is repositioned in tracking mode.
 */
void init_track(sptr)
	struct station *sptr;
	{
	int i;

	sptr->mode = MODE_CSRCH;
	sptr->envbot = sptr->envtop = NENV-1;
	sptr->env = NENV - 1; sptr->ptrenv = 0;
	sptr->mindex = sptr->maxdex = 0;
	for (i = 0; i < NLMS; i++) {
		sptr->weight[i] = wgtlms[i];
		sptr->lms[i] = 0; sptr->erms[i] = 0;
		}
	for (i = 0; i < MMAX; i++)
		sptr->stb[i] = -1;
	for (i = 0; i < NENV; i++) {
		sptr->ecyc[i] = 0; sptr->pcyc[i] = 0;
		}
	sptr->pfac = PMIN;
	sptr->esnr = sptr->rsnr = .01; sptr->psnr = PGATE / PMIN;
	sptr->mcnt = 1;
	sptr->ecnt = sptr->icnt = 0;
	sptr->cycle = sptr->strobe = sptr->slew = sptr->borrow = 0;
	sptr->fmin = 999; sptr->fmax = -999;
	sptr->update = epoch;
 	sptr->cscnt++;
	}

/*
 * Subroutine display()
 *
 * This subroutine determines what to display about the status of the
 * receiver and tracking information. A single line of status is
 * displayed for each station at the completion of the cycle scan. Every
 * line includes a header produced by the status() subroutine followed
 * by the integrated q-channel signal and the ecd metric calculated for
 * each cycle separately. The strobe (reference) cycle is marked with an
 * 'x' followed by the value of the local clock at that cycle. The
 * second cycle after that includes "tc" followed by the time constant
 * of integration, the third cycle includes "snr" followed first by the
 * ecd metric, then by the snr of the strobe cycle (dB), then by the
 * "agc" and value of the agc DAC. The cycle after that includes "vco"
 * and the value of the vco DAC for the master station and "pos" and the
 * estimated position error (us) along the great-circle path through the
 * transmitter and receiver. These displays subject to capricious
 * changes from time to time.
 */
void display(sptr)
	struct station *sptr;
	{

    double dtemp, etemp;
    int k;
	char s[20];

	sptr->count = 0;
	dtemp = sptr->ecyc[sptr->ptrenv];
	etemp = sqrt(sptr->qcyc[sptr->ptrenv]);
	if (sptr->ptrenv == sptr->strobe)
		sprintf(sptr->msg, " x %-3.1lf", local_time(sptr) / 1e6);
	if (sptr->kbd == ' ')
		etemp = sptr->lms[sptr->ptrenv];
	if (sptr->ptrenv == sptr->strobe + 2)
		sprintf(sptr->msg, " tc %-3.1lf", sptr->pfac);
	if (sptr->ptrenv == sptr->strobe + 3)
		sprintf(sptr->msg, " snr %-3.2lf %-3.2lf",
			20 * log10(sptr->esnr), 20 * log10(sptr->rsnr / sqrt(2)));
	if (sptr->ptrenv == sptr->strobe + 4)
		sprintf(sptr->msg, " agc %-3.0lf", sptr->level);
	if (sptr->ptrenv == sptr->strobe + 5) {
		if (station_type(sptr) == 'm')
			sprintf(sptr->msg, " vco %-3i", vco);
		else {
			sprintf(sptr->msg, " pos %-3.2lf",
				delay[sptr->index] - sptr->system_delay);
			}
		}
	status(sptr, dtemp, etemp, sptr->msg);
	}

/*
 * Subroutine command()
 *
 * This subroutine is called once during the gri b interval to scan for
 * keyboard commands. As development proceeds, this subroutine is to
 * wither and die.
 *
 * Commands are one- or two-character sequences. While most command
 * functions are completed in one frame, some may persist indefinately
 * until canceled by another keystroke or automatically by the program.
 *
 *	)	adjust receiver gain up
 *	(	adjust receiver gain down
 *	>	adjust vco bias up
 *	<	adjust vco bias down
 *	m	use master pulse codes
 *	s	use slave pulse codes
 *	x	flip pulse code a/b
 *	q	debugging query
 *	digit select display station index
 */
void command()
	{
	struct station *sptr;
	int i, j;					/* int temps */
	char kbd;					/* char temps */

#if	!defined(DEBUG)
	kbd = getch();
#endif
	if (isdigit(kbd))
		dindex = kbd & 0x0f;
	if (dindex < 1 || dindex > nstation)
		return;
	sptr = chain[dindex]; sptr->kbd = kbd;
	switch (sptr->kbd) {

		/*
		 * The following commands adjust various receiver vco
		 * and agc bias values. The exact values are determined
		 * at initial receiver alignment and compiled in the
		 * source code and normally need not be changed in
		 * regular operation.
		 */
		case ')':				/* adjust receiver gain up */
			agcdac--; agc--; break;

		case '(':				/* adjust receiver gain down */
			agcdac++; agc++; break;

		case '>':				/* adjust vco bias up */
			vcodac--; break;

		case '<':				/* adjust vco bias down */
			vcodac++; break;

		/*
		 * The following commands select which pulse codes are
		 * used and determine which set (a or b) to use. These
		 * are normally needed only for manual signal
		 * acquisition.
		 */
		case 'x':				/* flip pulse code a/b */
			sptr->codesw--; break;

		case 'm':				/* use master codes */
			sptr->type = 'm'; break;

		case 's':				/* use slave codes */
			sptr->type = 's'; break;

		/*
		 * Display receiver status (debug). Note that the
		 * display may take longer than a gri, so that the
		 * receiver can loose synchronization. Usually,
		 * synchronization can be resynchronized simply by
		 * flipping the code phase ("x" command).
		 */
		case 'q':
			outp(TGC, LOADDP+MASTER);
			printf("status %02x  master mode %02x %02x",
				inp(TGC), inp(TGD), inp(TGD));
			outp(TGC, LOADDP+1);
			printf("counters\n");
			for (i = 1; i < 6; i++) {
				printf("%2i", i);
				for (j=1; j<7; j++) printf(" %02x",
					inp(TGD));
				printf("\n");
				}
			printf("agcdac %5i  vcodac %5i\n",
				agcdac, vcodac);
			break;
		}
	}

/*
 * Subroutine status()
 *
 * This subrouine encodes and displays a status line in standard form:
 *
 * kggggm n ttt ff ooooo cc uu.u vv.v message
 *
 *  kbd	assigned gri
 *  k		last keystroke
 *  gggg	assigned gri
 *	m		station ident (m, w, x, y, z)
 *	n		operating mode number
 *	ttt		amplitude time constant
 *	ttt		phase time constant
 *	ff		frame offset relative to turnon
 *	ooooo	cycle offset within the frame (cycle steps) to gri
 *	cc		cycle offset within the gri (cycle steps)
 *	uu.u	signal value at this position
 *	vv.v	error value at this position
 *	message	information message
 */
void status(sptr, val1, val2, string)
	struct station *sptr;
	double val1, val2;
	char *string;
	{

	if (sptr->index != dindex && dindex != 9)
		return;
	sprintf(sptr->report, "%i %c %c%2i%6li%+03i%+03i%6.1lf%6.1lf%8.2lf%8.2lf%s",
		sptr->index, sptr->kbd, station_type(sptr), sptr->mode,
		offset + sptr->strobe, sptr->ptrenv, sptr->slew + sptr->borrow,
		val1, val2,
		sptr->vphase * Ko * CYCLE * 1e3,
		sptr->vfreq * Ko * CYCLE * 1e3, string);
	}


/* end program */
