/*********************************************************************** * * * Program to control LORAN-C radio * * * * Signal acquisition and tracking routines * * * *********************************************************************** */ #include "loran.h" #include /* * 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 */