Update to current reality. We can now track several LORSTA on separate

minor devices.

Improve PLL/OCXO DAC dithering.

General remodeling.

Performance is now 2.5e-11 in frequency and +/- 100 nsec in time, both
of which are actually the limits of the transmitted signal.
This commit is contained in:
phk 1998-10-24 19:55:09 +00:00
parent 14fa987f11
commit bfaec8d4e5

View File

@ -6,7 +6,7 @@
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
* ----------------------------------------------------------------------------
*
* $Id: loran.c,v 1.9 1998/10/22 05:58:39 bde Exp $
* $Id: loran.c,v 1.10 1998/10/23 10:45:10 phk Exp $
*
* This device-driver helps the userland controlprogram for a LORAN-C
* receiver avoid monopolizing the CPU.
@ -32,13 +32,13 @@
#include <i386/isa/isa_device.h>
#endif /* KERNEL */
typedef TAILQ_HEAD(, datapoint) dphead_t;
struct datapoint {
void *ident;
int index;
/* Fields used by kernel */
u_int64_t scheduled;
u_int delay;
u_int code;
u_int gri;
u_int fri;
u_int agc;
u_int phase;
u_int width;
@ -47,16 +47,22 @@ struct datapoint {
u_int qsig;
u_int ssig;
u_int64_t epoch;
struct timespec actual;
TAILQ_ENTRY(datapoint) list;
u_char status;
int vco;
pid_t pid;
int priority;
dphead_t *home;
/* Fields used only in userland */
void *ident;
int index;
double ival;
double qval;
double sval;
double mval;
u_char status;
u_int vco;
int count;
int remain;
};
/*
@ -77,14 +83,21 @@ struct datapoint {
#define EN5 0x40 /* enable counter 5 bit */
#define ENG 0x80 /* enable gri bit */
#define VCO 2048 /* initial vco dac (0 V)*/
#define VCO_SHIFT 8 /* bits of fraction on VCO value */
#define VCO (2048 << VCO_SHIFT) /* initial vco dac (0 V)*/
#define PGUARD 990 /* program guard time (cycle) (990!) */
#ifdef KERNEL
#define NLORAN 10 /* Allow ten minor devices */
#define NDUMMY 4 /* How many idlers we want */
#define PORT 0x0300 /* controller port address */
#define PGUARD 990 /* program guard time (cycle) (990!) */
#define GRI 800 /* pulse-group gate (cycle) */
@ -183,9 +196,9 @@ struct datapoint {
/**********************************************************************/
static TAILQ_HEAD(qhead, datapoint) qdone, qready;
dphead_t minors[NLORAN], working, holding;
static struct datapoint dummy;
static struct datapoint dummy[NDUMMY];
static u_int64_t ticker;
@ -198,8 +211,11 @@ static MALLOC_DEFINE(M_LORAN, "Loran", "Loran datapoints");
static int loranerror;
static char lorantext[80];
static u_int vco_is;
static u_int vco_should;
static u_int vco_is;
static u_int vco_should;
static u_int vco_want;
static u_int64_t vco_when;
static int64_t vco_error;
static int lorantc_magic;
@ -214,7 +230,7 @@ static d_close_t loranclose;
static d_read_t loranread;
static d_write_t loranwrite;
static ointhand2_t loranintr;
extern struct timecounter loran_timecounter[];
extern struct timecounter loran_timecounter;
/**********************************************************************/
@ -229,10 +245,10 @@ loranprobe(struct isa_device *dvp)
}
u_short tg_init[] = { /* stc initialization vector */
0x0562, 12, 13, /* counter 1 (p0) */
0x0262, PGUARD, GRI, /* counter 2 (gri) */
0x0562, 12, 13, /* counter 1 (p0) Mode J */
0x0262, PGUARD, GRI, /* counter 2 (gri) Mode J */
0x8562, PCX, 5000 - PCX, /* counter 3 (pcx) */
0xc562, 0, STROBE, /* counter 4 (stb) */
0xc562, 0, STROBE, /* counter 4 (stb) Mode L */
0x052a, 0, 0 /* counter 5 (out) */
};
@ -265,26 +281,29 @@ loranattach(struct isa_device *isdp)
printf("loran0: LORAN-C Receiver\n");
vco_is = VCO;
LOAD_DAC(DACA, VCO);
vco_should = vco_is = VCO;
LOAD_DAC(DACA, vco_is >> VCO_SHIFT);
init_tgc();
init_timecounter(&loran_timecounter);
TAILQ_INIT(&qdone);
TAILQ_INIT(&qready);
TAILQ_INIT(&working);
TAILQ_INIT(&holding);
for (i = 0; i < NLORAN; i++) {
TAILQ_INIT(&minors[i]);
}
dummy.agc = 4095;
dummy.code = 0xac;
dummy.delay = PGUARD - GRI;
dummy.gri = PGUARD;
dummy.phase = 50;
dummy.width = 50;
TAILQ_INSERT_HEAD(&qready, &dummy, list);
this = &dummy;
next = &dummy;
for (i = 0; i < NDUMMY; i++) {
dummy[i].agc = 4095;
dummy[i].code = 0xac;
dummy[i].fri = PGUARD;
dummy[i].phase = 50;
dummy[i].width = 50;
dummy[i].priority = 9999;
TAILQ_INSERT_TAIL(&working, &dummy[i], list);
}
inb(ADC); /* Flush any old result */
outb(ADC, ADC_S);
@ -300,26 +319,18 @@ loranopen (dev_t dev, int flags, int fmt, struct proc *p)
{
u_long ef;
struct datapoint *this;
int idx;
idx = minor(dev);
if (idx >= NLORAN)
return (ENODEV);
while (!TAILQ_EMPTY(&qdone)) {
ef = read_eflags();
disable_intr();
this = TAILQ_FIRST(&qdone);
TAILQ_REMOVE(&qdone, this, list);
write_eflags(ef);
FREE(this, M_LORAN);
}
init_tgc();
loranerror = 0;
return(0);
}
static int
loranclose(dev_t dev, int flags, int fmt, struct proc *p)
{
/*
* Lower ENG
*/
return(0);
}
@ -329,19 +340,22 @@ loranread(dev_t dev, struct uio * uio, int ioflag)
u_long ef;
struct datapoint *this;
int err, c;
int idx;
idx = minor(dev);
if (loranerror) {
printf("Loran0: %s", lorantext);
return(EIO);
}
if (TAILQ_EMPTY(&qdone))
tsleep ((caddr_t)&qdone, PZERO + 8 |PCATCH, "loranrd", hz*2);
if (TAILQ_EMPTY(&qdone))
if (TAILQ_EMPTY(&minors[idx]))
tsleep ((caddr_t)&minors[idx], PZERO + 8 |PCATCH, "loranrd", hz*2);
if (TAILQ_EMPTY(&minors[idx]))
return(0);
this = TAILQ_FIRST(&qdone);
this = TAILQ_FIRST(&minors[idx]);
ef = read_eflags();
disable_intr();
TAILQ_REMOVE(&qdone, this, list);
TAILQ_REMOVE(&minors[idx], this, list);
write_eflags(ef);
c = imin(uio->uio_resid, (int)sizeof *this);
@ -351,55 +365,114 @@ loranread(dev_t dev, struct uio * uio, int ioflag)
}
static void
loranenqueue(struct datapoint *this)
loranenqueue(struct datapoint *dp)
{
struct datapoint *p, *q;
u_long ef;
u_int64_t x;
struct datapoint *dpp, *dpn;
if (this->scheduled < ticker) {
x = (ticker - this->scheduled) / (2 * this->gri);
this->scheduled += x * 2 * this->gri;
}
while(1) {
/*
* The first two elements on "working" are sacred,
* they're already partly setup in hardware, so the
* earliest slot we can use is #3
*/
dpp = TAILQ_FIRST(&working);
dpp = TAILQ_NEXT(dpp, list);
dpn = TAILQ_NEXT(dpp, list);
while (1) {
/*
* We cannot bump "dpp", so if "dp" overlaps it
* skip a beat.
* XXX: should use better algorithm ?
*/
if (dpp->scheduled + PGUARD > dp->scheduled) {
dp->scheduled += dp->fri;
continue;
}
ef = read_eflags();
disable_intr();
/*
* If "dpn" will be done before "dp" wants to go,
* we must look further down the list.
*/
if (dpn && dpn->scheduled + PGUARD < dp->scheduled) {
dpp = dpn;
dpn = TAILQ_NEXT(dpp, list);
continue;
}
p = TAILQ_FIRST(&qready);
while (1) {
while (this->scheduled < p->scheduled + PGUARD)
this->scheduled += 2 * this->gri;
q = TAILQ_NEXT(p, list);
if (!q) {
this->delay = this->scheduled - p->scheduled - GRI;
TAILQ_INSERT_TAIL(&qready, this, list);
break;
/*
* If at end of list, put "dp" there
*/
if (!dpn) {
TAILQ_INSERT_AFTER(&working, dpp, dp, list);
break;
}
/*
* If "dp" fits before "dpn", insert it there
*/
if (dpn->scheduled > dp->scheduled + PGUARD) {
TAILQ_INSERT_AFTER(&working, dpp, dp, list);
break;
}
/*
* If "dpn" is less important, bump it.
*/
if (dp->priority < dpn->priority) {
TAILQ_REMOVE(&working, dpn, list);
TAILQ_INSERT_TAIL(&holding, dpn, list);
dpn = TAILQ_NEXT(dpp, list);
continue;
}
/*
* "dpn" was more or equally important, "dp" must
* take yet turn.
*/
dp->scheduled += dp->fri;
}
if (this->scheduled + PGUARD < q->scheduled) {
this->delay = this->scheduled - p->scheduled - GRI;
TAILQ_INSERT_BEFORE(q, this, list);
q->delay = q->scheduled - this->scheduled - GRI;
/*
* If anything was bumped, put it back as best we can
*/
if (TAILQ_EMPTY(&holding))
break;
}
p = q;
dp = TAILQ_FIRST(&holding);
TAILQ_REMOVE(&holding, dp, list);
}
write_eflags(ef);
}
static int
loranwrite(dev_t dev, struct uio * uio, int ioflag)
{
u_long ef;
int err = 0, c;
struct datapoint *this;
int idx;
u_int64_t dt;
idx = minor(dev);
MALLOC(this, struct datapoint *, sizeof *this, M_LORAN, M_WAITOK);
c = imin(uio->uio_resid, (int)sizeof *this);
err = uiomove((caddr_t)this, c, uio);
if (!err && this->gri == 0)
if (!err && this->fri == 0)
err = EINVAL;
/* XXX more checks */
this->home = &minors[idx];
this->priority = idx;
if (ticker > this->scheduled) {
dt = ticker - this->scheduled;
dt -= dt % this->fri;
this->scheduled += dt;
}
if (!err) {
ef = read_eflags();
disable_intr();
loranenqueue(this);
vco_should = this->vco;
write_eflags(ef);
if (this->vco >= 0)
vco_want = this->vco;
} else {
FREE(this, M_LORAN);
}
@ -411,31 +484,14 @@ loranintr(int unit)
{
u_long ef;
int status = 0, count = 0, i;
struct datapoint *this, *next;
int delay;
ef = read_eflags();
disable_intr();
if (this != &dummy) {
outb(TGC, DSABDPS);
outb(TGC, TG_LOADDP + 0x12); /* hold counter #2 */
this->remain = -1;
i = 2;
for (i = 0; i < 2; i++) {
count = this->remain;
do {
outb(TGC, TG_SAVE + 0x12);
this->remain = inb(TGD) & 0xff;
this->remain |= inb(TGD) << 8;
} while (count == this->remain);
}
lorantc_magic = 1;
nanotime(&this->actual);
lorantc_magic = 0;
outb(TGC, TG_LOADDP + 0x0a);
this->count = inb(TGD);
this->count |= inb(TGD) << 8;
LOAD_9513(0x12, GRI)
}
this = TAILQ_FIRST(&working);
TAILQ_REMOVE(&working, this, list);
this->ssig = inb(ADC);
@ -458,29 +514,18 @@ loranintr(int unit)
this->epoch = ticker;
if (this != &dummy) {
TAILQ_INSERT_TAIL(&qdone, this, list);
wakeup((caddr_t)&qdone);
if (this->home) {
TAILQ_INSERT_TAIL(this->home, this, list);
wakeup((caddr_t)this->home);
} else {
loranenqueue(this);
}
if (next != &dummy || TAILQ_NEXT(next, list))
TAILQ_REMOVE(&qready, next, list);
this = TAILQ_FIRST(&working);
next = TAILQ_NEXT(this, list);
this = next;
ticker += GRI;
ticker += this->delay;
next = TAILQ_FIRST(&qready);
if (!next) {
next = &dummy;
TAILQ_INSERT_HEAD(&qready, next, list);
} else if (next->delay + GRI > PGUARD * 2) {
next->delay -= PGUARD;
next = &dummy;
TAILQ_INSERT_HEAD(&qready, next, list);
}
if (next == &dummy)
next->scheduled = ticker + GRI + next->delay;
delay = next->scheduled - this->scheduled;
delay -= GRI;
/* load this->params */
par &= ~(INTEG|GATE);
@ -490,7 +535,7 @@ loranintr(int unit)
outb(CODE, this->code);
LOAD_9513(0x0a, next->delay);
LOAD_9513(0x0a, delay);
/*
* We need to load this from the opposite register * due to some
@ -502,10 +547,17 @@ loranintr(int unit)
outb(TGC, TG_LOADARM + 0x08);
LOAD_9513(0x14, this->width);
if (vco_is != vco_should) {
LOAD_DAC(DACA, vco_should);
vco_is = vco_should;
vco_error += ((vco_is << VCO_SHIFT) - vco_should) * (ticker - vco_when);
vco_should = vco_want;
i = vco_should >> VCO_SHIFT;
if (vco_error < 0)
i++;
if (vco_is != i) {
LOAD_DAC(DACA, i);
vco_is = i;
}
vco_when = ticker;
this->status = inb(TGC);
#if 1
@ -526,15 +578,12 @@ loranintr(int unit)
if (status) {
sprintf(lorantext, "Missed: %02x %d %d this:%p next:%p (dummy=%p)\n",
status, count, next->delay, this, next, &dummy);
loranerror = 1;
}
if (next->delay < PGUARD - GRI) {
sprintf(lorantext, "Bogus: %02x %d %d\n",
status, count, next->delay);
status, count, delay, this, next, &dummy);
loranerror = 1;
}
ticker = this->scheduled;
write_eflags(ef);
}
@ -569,7 +618,7 @@ static struct timecounter loran_timecounter = {
};
SYSCTL_OPAQUE(_debug, OID_AUTO, loran_timecounter, CTLFLAG_RD,
loran_timecounter, sizeof(loran_timecounter), "S,timecounter", "");
&loran_timecounter, sizeof(loran_timecounter), "S,timecounter", "");
/**********************************************************************/