Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include <u.h>
2
#include <libc.h>
3
#include <fcall.h>
4
#include <thread.h>
5
#include <ctype.h>
6
#include <9p.h>
7
#include "dat.h"
8
 
9
enum
10
{
11
	Numsize=	12,
12
	Vlnumsize=	22,
13
	Rawbuf=		0x10000,
14
	Rawmask=	Rawbuf-1,
15
};
16
 
17
#define	nsecperchar	((int)(1000000000.0 * 10.0 / baud))
18
 
19
typedef struct Fix Fix;
20
typedef struct Satellite Satellite;
21
typedef struct GPSfile GPSfile;
22
typedef struct Gpsmsg Gpsmsg;
23
 
24
struct Satellite {
25
	int		prn;
26
	int		elevation;
27
	int		azimuth;
28
	int		snr;
29
};
30
 
31
struct Fix {
32
	int		messages;	/* bitmap of types seen */
33
	Place;
34
	/*
35
	 * The following are in Plan 9 time format:
36
	 * seconds or nanoseconds since the epoch.
37
	 */
38
	vlong		localtime;	/* nsec() value when first byte was read */
39
	vlong		gpstime;	/* nsec() value from GPS */
40
	long		time;		/* time() value from GPS */
41
 
42
	double		zulu;
43
	int		date;
44
	char		valid;
45
	uchar		quality;
46
	ushort		satellites;
47
	double		pdop;
48
	double		hdop;
49
	double		vdop;
50
	double		altitude;
51
	double		sealevel;
52
	double		groundspeed;
53
	double		kmh;
54
	double		course;
55
	double		heading;
56
	double		magvar;
57
	Satellite	s[12];
58
};
59
 
60
struct GPSfile {
61
	char	*name;
62
	char*	(*rread)(Req*);
63
	int	mode;
64
	vlong	offset;		/* for raw: rawout - read-offset */
65
};
66
 
67
enum {
68
	ASTRAL,
69
	GPGGA,
70
	GPGLL,
71
	GPGSA,
72
	GPGSV,
73
	GPRMC,
74
	GPVTG,
75
	PRWIRID,
76
	PRWIZCH
77
};
78
 
79
struct Gpsmsg {
80
	char *name;
81
	int tokens;
82
	ulong errors;
83
};
84
 
85
char	raw[Rawbuf];
86
vlong	rawin;
87
vlong	rawout;
88
 
89
ulong	badlat, goodlat, suspectlat;
90
ulong	badlon, goodlon, suspectlon;
91
ulong	suspecttime, goodtime;
92
 
93
ulong histo[32];
94
 
95
char *serial = "/dev/eia0";
96
 
97
Gpsmsg gpsmsg[] = {
98
[ASTRAL]	= { "ASTRAL",	 0,	0},
99
[GPGGA]		= { "$GPGGA",	15,	0},
100
/* NMEA 2.3 permits optional 8th field, mode */
101
[GPGLL]		= { "$GPGLL",	 7,	0},
102
[GPGSA]		= { "$GPGSA",	18,	0},
103
[GPGSV]		= { "$GPGSV",	0,	0},
104
[GPRMC]		= { "$GPRMC",	0,	0},
105
[GPVTG]		= { "$GPVTG",	0,	0},
106
[PRWIRID]	= { "$PRWIRID",	0,	0},
107
[PRWIZCH]	= { "$PRWIZCH",	0,	0},
108
};
109
 
110
int ttyfd, ctlfd, debug;
111
int setrtc;
112
int baud = Baud;
113
char *baudstr = "b%dd1r1pns1l8i9";
114
ulong seconds;
115
ulong starttime;
116
ulong checksumerrors;
117
int gpsplayback;	/* If set, return times and positions with `invalid' marker set */
118
 
119
Place where = {-(74.0 + 23.9191/60.0), 40.0 + 41.1346/60.0};
120
 
121
Fix curfix;
122
Lock fixlock;
123
 
124
int	type(char*);
125
void	setline(void);
126
int	getonechar(vlong*);
127
void	getline(char*, int, vlong*);
128
void	putline(char*);
129
int	gettime(Fix*);
130
int	getzulu(char *, Fix*);
131
int	getalt(char*, char*, Fix*);
132
int	getsea(char*, char*, Fix*);
133
int	getlat(char*, char*, Fix*);
134
int	getlon(char*, char*, Fix*);
135
int	getgs(char*, Fix *);
136
int	getkmh(char*, Fix*);
137
int	getcrs(char*, Fix*);
138
int	gethdg(char*, Fix*);
139
int	getdate(char*, Fix*);
140
int	getmagvar(char*, char*, Fix*);
141
void	printfix(int, Fix*);
142
void	ropen(Req *r);
143
void	rread(Req *r);
144
void	rend(Srv *s);
145
void	gpsinit(void);
146
char*	readposn(Req*);
147
char*	readtime(Req*);
148
char*	readsats(Req*);
149
char*	readstats(Req*);
150
char*	readraw(Req*);
151
 
152
GPSfile files[] = {
153
	{ "time",	readtime,	0444,	0 },
154
	{ "position",	readposn,	0444,	0 },
155
	{ "satellites",	readsats,	0444,	0 },
156
	{ "stats",	readstats,	0444,	0 },
157
	{ "raw",	readraw,	DMEXCL|0444, 0 },
158
};
159
 
160
Srv s = {
161
	.open	= ropen,
162
	.read	= rread,
163
 
164
	.end = rend,
165
};
166
 
167
File *root;
168
File *gpsdir;
169
 
170
void
171
rend(Srv *)
172
{
173
	sysfatal("gpsfs demised");
174
}
175
 
176
void
177
ropen(Req *r)
178
{
179
	respond(r, nil);
180
}
181
 
182
void
183
rread(Req *r)
184
{
185
	GPSfile *f;
186
 
187
	r->ofcall.count = 0;
188
	f = r->fid->file->aux;
189
	respond(r, f->rread(r));
190
}
191
 
192
void
193
fsinit(void)
194
{
195
	char* user;
196
	int i;
197
 
198
	user = getuser();
199
	s.tree = alloctree(user, user, 0555, nil);
200
	if(s.tree == nil)
201
		sysfatal("fsinit: alloctree: %r");
202
	root = s.tree->root;
203
	if((gpsdir = createfile(root, "gps", user, DMDIR|0555, nil)) == nil)
204
		sysfatal("fsinit: createfile: gps: %r");
205
	for(i = 0; i < nelem(files); i++)
206
		if(createfile(gpsdir, files[i].name, user, files[i].mode, files + i) == nil)
207
			sysfatal("fsinit: createfile: %s: %r", files[i].name);
208
}
209
 
210
void
211
threadmain(int argc, char*argv[])
212
{
213
	char *srvname, *mntpt;
214
 
215
	srvname = "gps";
216
	mntpt = "/mnt";
217
 
218
	ARGBEGIN {
219
	default:
220
		fprint(2, "usage: %s [-b baud] [-d device] [-l logfile] [-m mntpt] [-r] [-s postname]\n", argv0);
221
		exits("usage");
222
	case 'D':
223
		debug++;
224
		break;
225
	case 'b':
226
		baud = strtol(ARGF(), nil, 0);
227
		break;
228
	case 'd':
229
		serial = ARGF();
230
		break;
231
	case 'r':
232
		setrtc = 1;
233
		break;
234
	case 's':
235
		srvname = ARGF();
236
		break;
237
	case 'm':
238
		mntpt = ARGF();
239
		break;
240
	} ARGEND
241
 
242
	fmtinstall('L', placeconv);
243
 
244
	rfork(RFNOTEG);
245
 
246
	fsinit();
247
	gpsinit();
248
	threadpostmountsrv(&s, srvname, mntpt, MBEFORE);
249
	threadexits(nil);
250
}
251
 
252
static void
253
gpstrack(void *)
254
{
255
	Fix fix;
256
	static char buf[256], *t[32];
257
	int n, i, k, tp;
258
	vlong localtime;
259
	double d;
260
 
261
	setline();
262
	fix.messages = 0;
263
	fix.lon = 181.0;
264
	fix.lat = 91.0;
265
	fix.zulu = 0;
266
	fix.date = 0;
267
	fix.valid = 0;
268
	fix.quality = 0;
269
	fix.satellites = 0;
270
	fix.pdop = 0.0;
271
	fix.hdop = 0.0;
272
	fix.vdop = 0.0;
273
	fix.altitude = 0.0;
274
	fix.sealevel = 0.0;
275
	fix.groundspeed = 0.0;
276
	fix.kmh = 0.0;
277
	fix.course = 0.0;
278
	fix.heading = 0.0;
279
	fix.magvar = 0.0;
280
	for(;;){
281
		getline(buf, sizeof buf, &localtime);
282
		n = getfields(buf, t, nelem(t), 0,",\r\n");
283
		if(n == 0)
284
			continue;
285
		tp = type(t[0]);
286
		if(tp >= 0 && tp < nelem(gpsmsg) && gpsmsg[tp].tokens &&
287
		    gpsmsg[tp].tokens > n){
288
			gpsmsg[tp].errors++;
289
			if(debug)
290
				fprint(2, "%s: Expect %d tokens, got %d\n",
291
					gpsmsg[tp].name, gpsmsg[tp].tokens, n);
292
			continue;
293
		}
294
		switch(tp){
295
		case ASTRAL:
296
			putline("$IIGPQ,ASTRAL*73");
297
			putline("$PRWIILOG,GGA,A,T,10,0");
298
			putline("$PRWIILOG,RMC,A,T,10,0");
299
			putline("$PRWIILOG,GSA,A,T,10,0");
300
			putline("$PRWIILOG,GSV,V,,,");
301
			fprint(2, "Reply: %s\n", "$IIGPQ,ASTRAL*73");
302
			break;
303
		case PRWIRID:
304
		case PRWIZCH:
305
			for(i = 0; i < n; i++) fprint(2, "%s,", t[i]);
306
			fprint(2, "(%d tokens)\n", n);
307
			break;
308
		case GPGGA:
309
			if(getlat(t[2], t[3], &fix))
310
				break;
311
			if(getlon(t[4], t[5], &fix))
312
				break;
313
			getzulu(t[1], &fix);
314
			if(fix.date && gettime(&fix))
315
				break;
316
			if(isdigit(*t[7]))
317
				fix.satellites = strtol(t[7], nil, 10);
318
			if(isdigit(*t[8])){
319
				d = strtod(t[8], nil);
320
				if(!isNaN(d))
321
					fix.hdop = d;
322
			}
323
			getalt(t[9], t[10], &fix);
324
			getsea(t[11], t[12], &fix);
325
			fix.localtime = localtime;
326
			fix.quality = strtol(t[6], nil, 10);
327
			fix.messages |= 1 << tp;
328
			break;
329
		case GPRMC:
330
			fix.valid = *t[2];
331
			getgs(t[7], &fix);
332
			getcrs(t[8], &fix);
333
			getdate(t[9], &fix);
334
			getmagvar(t[10], t[11], &fix);
335
			if((fix.messages & (1 << GPGGA)) == 0){
336
				if(getlat(t[3], t[4], &fix))
337
					break;
338
				if(getlon(t[5], t[6], &fix))
339
					break;
340
				fix.localtime = localtime;
341
				getzulu(t[1], &fix);
342
				if(fix.date)
343
					gettime(&fix);
344
			}
345
			fix.messages |= 1 << tp;
346
			break;
347
		case GPGSA:
348
			if(*t[15]){
349
				d = strtod(t[15], nil);
350
				if(!isNaN(d))
351
					fix.pdop = d;
352
			}
353
			if(*t[16]){
354
				d = strtod(t[16], nil);
355
				if(!isNaN(d))
356
					fix.hdop = d;
357
			}
358
			if(*t[17]){
359
				d = strtod(t[17], nil);
360
				if(!isNaN(d))
361
					fix.vdop = d;
362
			}
363
			fix.messages |= 1 << tp;
364
			break;
365
		case GPGLL:
366
			if(getlat(t[1], t[2], &fix))
367
				break;
368
			if(getlon(t[3], t[4], &fix))
369
				break;
370
			getzulu(t[5], &fix);
371
			fix.messages |= 1 << tp;
372
			break;
373
		case GPGSV:
374
			if(n < 8){
375
				gpsmsg[tp].errors++;
376
				if(debug)
377
					fprint(2, "%s: Expect at least 8 tokens, got %d\n",
378
						gpsmsg[tp].name, n);
379
				break;
380
			}
381
			i = 4*(strtol(t[2], nil, 10)-1);	/* starting entry in satellite table */
382
			fix.satellites = strtol(t[3], nil, 10);
383
			k = 4;
384
			while(i < nelem(fix.s) && k + 3 < n){
385
				fix.s[i].prn = strtol(t[k++], nil, 10);
386
				fix.s[i].elevation = strtol(t[k++], nil, 10);
387
				fix.s[i].azimuth = strtol(t[k++], nil, 10);
388
				fix.s[i].snr = strtol(t[k++], nil, 10);
389
				k += 4;
390
				i++;
391
			} 
392
			fix.messages |= 1 << tp;
393
			break;
394
		case GPVTG:
395
			if(n < 8){
396
				gpsmsg[tp].errors++;
397
				if(debug)
398
					fprint(2, "%s: Expect at least 8 tokens, got %d\n",
399
						gpsmsg[tp].name, n);
400
				break;
401
			}
402
			getcrs(t[2], &fix);
403
			gethdg(t[4], &fix);
404
			getgs(t[6], &fix);
405
			if(n > 8)
406
				getkmh(t[8], &fix);
407
			fix.messages |= 1 << tp;
408
			break;
409
		default:
410
			if(debug && fix.date)
411
				fprint(2, "Don't know %s\n", t[0]);
412
			break;
413
		}
414
		if(fix.valid){
415
			seconds++;
416
			lock(&fixlock);
417
			memmove(&curfix, &fix, sizeof fix);
418
			unlock(&fixlock);
419
			if(debug)
420
				printfix(2, &fix);
421
			fix.valid = 0;
422
			fix.messages = 0;
423
			for(i = 0; i < nelem(fix.s); i++)
424
				fix.s[i].prn = 0;
425
			if(gpsplayback)
426
				sleep(100);
427
		}
428
	}
429
}
430
 
431
void
432
gpsinit(void)
433
{
434
	proccreate(gpstrack, nil, 4096);
435
}
436
 
437
void
438
printfix(int f, Fix *fix){
439
	int i;
440
 
441
	fprint(f, "%L, ", fix->Place);
442
	fprint(f, "%g, ", fix->magvar);
443
	fprint(f, "%gm - %gm = %gm, ", fix->altitude, fix->sealevel, fix->altitude - fix->sealevel);
444
	fprint(f, "%06dZ(%g)-", (int)fix->zulu, fix->zulu);
445
	fprint(f, "%06d\n", fix->date);
446
	if(fix->lat >= 0)
447
		fprint(f, "%11.8fN, ", fix->lat);
448
	else
449
		fprint(f, "%11.8fS, ", -fix->lat);		
450
	if(fix->lon >= 0)
451
		fprint(f, "%12.8fE, ", fix->lon);
452
	else
453
		fprint(f, "%12.8fW, ", -fix->lon);
454
	fprint(f, "%g@%g, ", fix->course, fix->groundspeed);
455
	fprint(f, "(%c, %ds)\n", fix->valid, fix->satellites);
456
	for(i = 0; i < nelem(fix->s); i++){
457
		if(fix->s[i].prn == 0)
458
			continue;
459
		fprint(f, "[%d, %d°, %d°, %d]\n",
460
			fix->s[i].prn, fix->s[i].elevation, fix->s[i].azimuth, fix->s[i].snr);
461
	}
462
}
463
 
464
char*
465
readposn(Req *r)
466
{
467
	Fix f;
468
	char buf[256];
469
 
470
	lock(&fixlock);
471
	memmove(&f, &curfix, sizeof f);
472
	unlock(&fixlock);
473
	snprint(buf, sizeof buf, "%x	%06dZ	%lud	%g	%g	%g	%g	%g	%g",
474
		gpsplayback|f.quality, (int)f.zulu, f.time, f.lon, f.lat, f.altitude - f.sealevel,
475
		f.course, f.groundspeed, f.magvar);
476
	readstr(r, buf);
477
	return nil;
478
}
479
 
480
char*
481
readtime(Req *r)
482
{
483
	Fix f;
484
	char buf[Numsize+Vlnumsize+Vlnumsize+8];
485
 
486
	lock(&fixlock);
487
	memmove(&f, &curfix, sizeof f);
488
	unlock(&fixlock);
489
	seprint(buf, buf + sizeof buf, "%*.0lud %*.0llud %*.0llud %c",
490
		Numsize-1, f.time,
491
		Vlnumsize-1, f.gpstime,
492
		Vlnumsize-1, f.localtime, f.valid + (gpsplayback?1:0));
493
	readstr(r, buf);
494
	return nil;
495
}
496
 
497
char*
498
readstats(Req *r)
499
{
500
	int i;
501
	char buf[1024], *p;
502
 
503
	p = buf;
504
	p = seprint(p, buf + sizeof buf, "%lld bytes read, %ld samples processed in %ld seconds\n",
505
		rawin, seconds, curfix.time - starttime);
506
	p = seprint(p, buf + sizeof buf, "%lud checksum errors\n", checksumerrors);
507
	p = seprint(p, buf + sizeof buf, "format errors:");
508
	for(i = 0; i < nelem(gpsmsg); i++){
509
		p = seprint(p, buf + sizeof buf, "[%s]: %ld, ",
510
			gpsmsg[i].name, gpsmsg[i].errors);
511
	}
512
	p = seprint(p, buf + sizeof buf, "\nhistogram of # bytes received per buffer:\n");
513
	for(i = 0; i < nelem(histo); i++){
514
		p = seprint(p, buf + sizeof buf, "[%d]: %ld ",
515
			i, histo[i]);
516
	}
517
	p = seprint(p, buf + sizeof buf, "\n");
518
	p = seprint(p, buf + sizeof buf, "bad/good/suspect lat: %lud/%lud/%lud\n",
519
		badlat, goodlat, suspectlat);
520
	p = seprint(p, buf + sizeof buf, "bad/good/suspect lon: %lud/%lud/%lud\n",
521
		badlon, goodlon, suspectlon);
522
	p = seprint(p, buf + sizeof buf, "good/suspect time: %lud/%lud\n", goodtime, suspecttime);
523
	USED(p);
524
	readstr(r, buf);
525
	return nil;
526
}
527
 
528
char*
529
readsats(Req *r)
530
{
531
	Fix f;
532
	int i;
533
	char buf[1024], *p;
534
 
535
	lock(&fixlock);
536
	memmove(&f, &curfix, sizeof f);
537
	unlock(&fixlock);
538
	p = seprint(buf, buf + sizeof buf, "%d	%d\n", gpsplayback|f.quality, f.satellites);
539
	for(i = 0; i < nelem(f.s); i++){
540
		if(f.s[i].prn == 0)
541
			continue;
542
		p = seprint(p, buf + sizeof buf, "%d	%d	%d	%d\n",
543
			f.s[i].prn, f.s[i].elevation, f.s[i].azimuth, f.s[i].snr);
544
	}
545
	readstr(r, buf);
546
	return nil;
547
}
548
 
549
char*
550
readraw(Req *r)
551
{
552
	int n;
553
	GPSfile *f;
554
 
555
	f = r->fid->file->aux;
556
	if(rawin - rawout > Rawbuf){
557
		rawout = rawin - Rawbuf;
558
		f->offset = rawout - r->ifcall.offset;
559
	}
560
	n = Rawbuf - (rawout&Rawmask);
561
	if(rawin - rawout < n)
562
		n = rawin - rawout;
563
	if(r->ifcall.count < n)
564
		n = r->ifcall.count;
565
	r->ofcall.count = n;
566
	if(n > 0){
567
		memmove(r->ofcall.data, raw + (rawout & Rawmask), n);
568
		rawout += n;
569
	}
570
	return nil;
571
}
572
 
573
void
574
rtcset(long t)
575
{
576
	static int fd;
577
	long r;
578
	int n;
579
	char buf[32];
580
 
581
	if(fd <= 0 && (fd = open("#r/rtc", ORDWR)) < 0){
582
		fprint(2, "Can't open #r/rtc: %r\n");
583
		return;
584
	}
585
	n = read(fd, buf, sizeof buf - 1);
586
	if(n <= 0){
587
		fprint(2, "Can't read #r/rtc: %r\n");
588
		return;
589
	}
590
	buf[n] = '\0';
591
	r = strtol(buf, nil, 0);
592
	if(r <= 0){
593
		fprint(2, "ridiculous #r/rtc: %ld\n", r);
594
		return;
595
	}
596
	if(r - t > 1 || t - r > 0){
597
		seek(fd, 0, 0);
598
		fprint(fd, "%ld", t);
599
		fprint(2, "correcting #r/rtc: %ld → %ld\n", r, t);
600
	}
601
	seek(fd, 0, 0);
602
}
603
 
604
int
605
gettime(Fix *f){
606
	/* Convert zulu time and date to Plan9 time(2) */
607
	Tm tm;
608
	int zulu;
609
	double d;
610
	long t;
611
	static int count;
612
 
613
	zulu = f->zulu;
614
	memset(&tm, 0, sizeof tm );
615
	tm.sec = zulu % 100;
616
	tm.min = (zulu/100) % 100;
617
	tm.hour = zulu / 10000;
618
	tm.year = f->date % 100 + 100;	/* This'll only work until 2099 */
619
	tm.mon = ((f->date/100) % 100) - 1;
620
	tm.mday = f->date / 10000;
621
	strcpy(tm.zone, "GMT");
622
	t = tm2sec(&tm);
623
	if(f->time && count < 3 && (t - f->time > 10 || t - f->time <= 0)){
624
		count++;
625
		suspecttime++;
626
		return -1;
627
	}
628
	goodtime++;
629
	f->time = t;
630
	count = 0;
631
	if(starttime == 0) starttime = t;
632
	f->gpstime = 1000000000LL * t + 1000000 * (int)modf(f->zulu, &d);
633
	if(setrtc){
634
		if(setrtc == 1 || (t % 300) == 0){
635
			rtcset(t);
636
			setrtc++;
637
		}
638
	}
639
	return 0;
640
}
641
 
642
int
643
getzulu(char *s, Fix *f){
644
	double d;
645
 
646
	if(*s == '\0') return 0;
647
	if(isdigit(*s)){
648
		d = strtod(s, nil);
649
		if(!isNaN(d))
650
			f->zulu = d;
651
		return 1;
652
	}
653
	return 0;
654
}
655
 
656
int
657
getdate(char *s, Fix *f){
658
	if(*s == 0) return 0;
659
	if(isdigit(*s)){
660
		f->date = strtol(s, nil, 10);
661
		return 1;
662
	}
663
	return 0;
664
}
665
 
666
int
667
getgs(char *s, Fix *f){
668
	double d;
669
 
670
	if(*s == 0) return 0;
671
	if(isdigit(*s)){
672
		d = strtod(s, nil);
673
		if(!isNaN(d))
674
			f->groundspeed = d;
675
		return 1;
676
	}
677
	return 0;
678
}
679
 
680
int
681
getkmh(char *s, Fix *f){
682
	double d;
683
 
684
	if(*s == 0) return 0;
685
	if(isdigit(*s)){
686
		d = strtod(s, nil);
687
		if(!isNaN(d))
688
			f->kmh = d;
689
		return 1;
690
	}
691
	return 0;
692
}
693
 
694
int
695
getcrs(char *s1, Fix *f){
696
	double d;
697
 
698
	if(*s1 == 0) return 0;
699
	if(isdigit(*s1)){
700
		d = strtod(s1, nil);
701
		if(!isNaN(d))
702
			f->course = d;
703
		return 1;
704
	}
705
	return 0;
706
}
707
 
708
int
709
gethdg(char *s1, Fix *f){
710
	double d;
711
 
712
	if(*s1 == 0) return 0;
713
	if(isdigit(*s1)){
714
		d = strtod(s1, nil);
715
		if(!isNaN(d))
716
			f->heading = d;
717
		return 1;
718
	}
719
	return 0;
720
}
721
 
722
int
723
getalt(char *s1, char *s2, Fix *f){
724
	double alt;
725
 
726
	if(*s1 == 0) return 0;
727
	if(isdigit(*s1)){
728
		alt = strtod(s1, nil);
729
		if(*s2 == 'M' && !isNaN(alt)){
730
			f->altitude = alt;
731
			return 1;
732
		}
733
		return 0;
734
	}
735
	return 0;
736
}
737
 
738
int
739
getsea(char *s1, char *s2, Fix *f){
740
	double alt;
741
 
742
	if(*s1 == 0) return 0;
743
	if(isdigit(*s1)){
744
		alt = strtod(s1, nil);
745
		if(*s2 == 'M'){
746
			f->sealevel = alt;
747
			return 1;
748
		}
749
		return 0;
750
	}
751
	return 0;
752
}
753
 
754
int
755
getlat(char *s1, char *s2, Fix *f){
756
	double lat;
757
	static count;
758
 
759
	if(*s1 == 0 || !isdigit(*s1) || strlen(s1) <= 5){
760
		badlat++;
761
		return -1;
762
	}
763
	lat = strtod(s1+2, nil);
764
	if(isNaN(lat)){
765
		badlat++;
766
		return -1;
767
	}
768
	lat /= 60.0;
769
	lat += 10*(s1[0] - '0') + s1[1] - '0';
770
	if(lat < 0 || lat > 90.0){
771
		badlat++;
772
		return -1;
773
	}
774
	switch(*s2){
775
	default:
776
		badlat++;
777
		return -1;
778
	case 'S':
779
		lat = -lat;
780
	case 'N':
781
		break;
782
	}
783
	if(f->lat <= 90.0 && count < 3 && fabs(f->lat - lat) > 10.0){
784
		count++;
785
		suspectlat++;
786
		return -1;
787
	}
788
	f->lat = lat;
789
	count = 0;
790
	goodlat++;
791
	return 0;
792
}
793
 
794
int
795
getlon(char *s1, char *s2, Fix *f){
796
	double lon;
797
	static count;
798
 
799
	if(*s1 == 0 || ! isdigit(*s1) || strlen(s1) <= 5){
800
		badlon++;
801
		return -1;
802
	}
803
	lon = strtod(s1+3, nil);
804
	if(isNaN(lon)){
805
		badlon++;
806
		return -1;
807
	}
808
	lon /= 60.0;
809
	lon += 100*(s1[0] - '0') + 10*(s1[1] - '0') + s1[2] - '0';
810
	if(lon < 0 || lon > 180.0){
811
		badlon++;
812
		return -1;
813
	}
814
	switch(*s2){
815
	default:
816
		badlon++;
817
		return -1;
818
	case 'W':
819
		lon = -lon;
820
	case 'E':
821
		break;
822
	}
823
	if(f->lon <= 180.0 && count < 3 && fabs(f->lon - lon) > 10.0){
824
		count++;
825
		suspectlon++;
826
		return -1;
827
	}
828
	f->lon = lon;
829
	goodlon++;
830
	count = 0;
831
	return 0;
832
}
833
 
834
int
835
getmagvar(char *s1, char *s2, Fix *f){
836
	double magvar;
837
 
838
	if(*s1 == 0) return 0;
839
	if(isdigit(*s1) && strlen(s1) > 5){
840
		magvar = strtod(s1+3, nil);
841
		if(isNaN(magvar))
842
			return 0;
843
		magvar /= 60.0;
844
		magvar += 100*(s1[0] - '0') + 10*(s1[1] - '0') + s1[2] - '0';
845
		if(*s2 == 'W'){
846
			f->magvar = -magvar;
847
			return 1;
848
		}
849
		if(*s2 == 'E'){
850
			f->magvar = magvar;
851
			return 1;
852
		}
853
		return 0;
854
	}
855
	return 0;
856
}
857
 
858
void
859
putline(char *s){
860
	write(ttyfd, s, strlen(s));
861
	write(ttyfd, "\r\n", 2);
862
}
863
 
864
int
865
type(char *s){
866
	int i;
867
 
868
	for(i = 0; i < nelem(gpsmsg); i++){
869
		if(strcmp(s, gpsmsg[i].name) == 0) return i;
870
	}
871
	return -1;
872
}
873
 
874
void
875
setline(void){
876
	char *serialctl;
877
 
878
	serialctl = smprint("%sctl", serial);
879
	if((ttyfd = open(serial, ORDWR)) < 0)
880
		sysfatal("%s: %r", serial);
881
	if((ctlfd = open(serialctl, OWRITE)) >= 0){
882
		if(fprint(ctlfd, baudstr, baud) < 0)
883
			sysfatal("%s: %r", serialctl);
884
	}else
885
		gpsplayback = 0x8;
886
	free(serialctl);
887
}
888
 
889
int getonechar(vlong *t){
890
	static char buf[32], *p;
891
	static int n;
892
 
893
	if(n == 0){
894
		n = read(ttyfd, buf, sizeof(buf));
895
		if(t) *t = nsec();
896
		if(n < 0)
897
			sysfatal("%s: %r", serial);
898
		if(n == 0)
899
			threadexits(nil);
900
		/*
901
		 * We received n characters, so the first must have been there
902
		 * at least n/(10*baud) seconds (10 is 1 start
903
		 * bit, one stop bit and 8 data bits per character)
904
		 */
905
		if(t) {
906
			*t -= n * nsecperchar;
907
			histo[n]++;
908
		}
909
		p = buf;
910
	}
911
	n--;
912
	return *p++;
913
}
914
 
915
void
916
getline(char *s, int size, vlong *t){
917
	uchar c;
918
	char *p;
919
	int n, cs;
920
 
921
tryagain:
922
	for(;;){
923
		p = s;
924
		n = 0;
925
		while((c = getonechar(t)) != '\n' && n < size){
926
			t = nil;
927
			if(c != '\r'){
928
				*p++ = c;
929
				n++;
930
			}
931
		}
932
		if(n < size)
933
			break;
934
		while(getonechar(t) != '\n' && n < 4096)
935
			n++;
936
		if(n == 4096)
937
			sysfatal("preposterous gps line, wrong baud rate?");
938
		fprint(2, "ridiculous gps line: %d bytes\n", n);
939
	}
940
	*p = 0;
941
	for(p = s; isdigit(*p); p++)
942
		;
943
	if(*p++ == '	')
944
		memmove(s, p, strlen(p)+1);
945
	if(s[0] == '$'){
946
		if(n > 4 && s[n-3] == '*'){
947
			s[n-3] = 0;
948
			p = s+1;
949
			cs = 0;
950
			while(*p) cs ^= *p++;
951
			n = strtol(&s[n-2], nil, 16);
952
			if(n != cs){
953
				if(debug)
954
					fprint(2, "Checksum error %s, 0x%x, 0x%x\n",
955
						s, n, cs);
956
				checksumerrors++;
957
				goto tryagain;
958
			}
959
		}
960
	}
961
	for(p = s; *p; rawin++)
962
		raw[rawin & Rawmask] = *p++;
963
	raw[rawin & Rawmask] = '\n';
964
	rawin++;
965
}