Compare commits

...

3 commits

Author SHA1 Message Date
Stephan I. Böttcher
33e95043fc commandline options
follow mode
output formats
2023-09-15 12:58:30 +02:00
4e56fbd3f4 target nc% to capture different ports 2023-09-15 10:33:52 +02:00
383ffbfb69 fix DATE parser field order 2023-09-15 10:33:12 +02:00
3 changed files with 174 additions and 38 deletions

1
.gitignore vendored
View file

@ -1,4 +1,5 @@
cron.log cron.log
tk102-*.gpx tk102-*.gpx
tk102-*.log tk102-*.log
tk102-*.txt

View file

@ -6,5 +6,11 @@ all: $(patsubst %, %.gpx, $(FILES))
%.gpx: %.log %.gpx: %.log
./tk102gpx.py $< > $@ ./tk102gpx.py $< > $@
capture: nc%: tk102-%.log
(while true; do nc -lp 10201; done) | tee -a tk102-1.log | cat -v (while true; do nc -lp 1020$* -w 999; done) | tee -a $< | cat -v
tail%: tk102-%.log
./tk102gpx.py -t1w -CFf $< | tee tk102-$*.txt
%.txt: t%.log
./tk102gpx.py -CF $< > $@

View file

@ -1,9 +1,58 @@
#! /usr/bin/python3 #! /usr/bin/python3
import sys, re import sys, re, time
import gpxpy import gpxpy
import gpxpy.gpx import gpxpy.gpx
from datetime import datetime from datetime import datetime
from getopt import getopt
options, files = getopt(sys.argv[1:],
"qvft:s:RDCXF",
["quiet", "verbose", "follow", "time=", "serial=",
"raw", "data", "cooked", "gpx", "fieldnames"])
follow = False
serial = b"0"
verbose = 1
raw = 0
fieldnames = False
print(options, file=sys.stderr)
for o,v in options:
o = " "+o
if o in " -q --quiet":
verbose = 0
if o in " -v --verbose":
verbose += 1
if o in " -f --follow":
follow = True
if o in " -D --data":
raw = 1
if o in " -C --cooked":
raw = 2
if o in " -R --raw":
raw = 3
if o in " -X --gpx":
raw = 0
if o in " -F --fieldnames":
fieldnames = True
if o in " -s --serial":
serial = v.encode()
if o in " -t --time":
if v[-1]=="m":
t = float(v[:-1])*60
elif v[-1]=="h":
t = float(v[:-1])*3600
elif v[-1]=="d":
t = float(v[:-1])*86400
elif v[-1]=="w":
t = float(v[:-1])*86400*7
else:
t = float(v)
serial = time.strftime("%y%m%d%H%M", time.gmtime(time.time()-t)).encode()
if verbose:
print(f"--serial={serial}", file=sys.stderr)
tk_re = re.compile(b"".join(( tk_re = re.compile(b"".join((
b"(?P<SERIAL>[0-9]+)?,", b"(?P<SERIAL>[0-9]+)?,",
@ -21,7 +70,7 @@ tk_re = re.compile(b"".join((
b"(?P<VAR>[^,]+)?,", b"(?P<VAR>[^,]+)?,",
b"(?P<WHAT>[^,]*,)?", b"(?P<WHAT>[^,]*,)?",
b"A\*(?P<GPCKS>[0-9A-F][0-9A-F]),", b"A\*(?P<GPCKS>[0-9A-F][0-9A-F]),",
b"(?P<F>[A-Z])?,", b"(?P<BAT>[FL])?,",
b"imei:(?P<IMEI>[0-9]+),", b"imei:(?P<IMEI>[0-9]+),",
b"(?P<LEN>[0-9]{3})", b"(?P<LEN>[0-9]{3})",
b"(?P<BCKS>..)" b"(?P<BCKS>..)"
@ -36,50 +85,132 @@ def gprmc2deg(d,dd):
class TKGPX(gpxpy.gpx.GPX): class TKGPX(gpxpy.gpx.GPX):
def __init__(self, inp): def tk_newtrack(self, files):
if files:
super().__init__() fn = files[0]
track = gpxpy.gpx.GPXTrack() files[:1] = []
inp = open(fn, "rb")
else:
fn = None
inp = sys.stdin.buffer
track = gpxpy.gpx.GPXTrack(name=fn)
self.tracks.append(track) self.tracks.append(track)
segment = gpxpy.gpx.GPXTrackSegment() segment = gpxpy.gpx.GPXTrackSegment()
track.segments.append(segment) track.segments.append(segment)
return inp, segment
def __init__(self, files):
super().__init__()
inp, segment = self.tk_newtrack(files)
slept = False
data = inp.read(0x100000) data = inp.read(0x100000)
while data: while data or follow or files:
if len(data)<256:
data += inp.read(0x100000)
m = tk_re.search(data) m = tk_re.search(data)
if m is None or m.end()<1: while not m:
ndata = inp.read(0x100000)
while not ndata and files:
inp.close()
imp, segment = self.tk_newtrack(files)
ndata = inp.read(0x100000)
if not ndata:
break break
if m.start(): data += ndata
if m is None:
if not follow:
break
sys.stdout.flush()
try:
if verbose >= 2:
slept = True
sys.stderr.write(".")
sys.stderr.flush()
time.sleep(60)
except KeyboardInterrupt:
break
continue
if slept:
sys.stderr.write("\n")
slept = False
if m.start() and verbose >= 2:
print(f"discarded: {repr(data[:m.start()])}", file=sys.stderr) print(f"discarded: {repr(data[:m.start()])}", file=sys.stderr)
data = data[m.end():] data = data[m.end():]
d = m.groupdict() rd = m.groupdict()
print(int(d["LEN"]), m.end()-m.start()-int(d["LEN"]), repr(m.group()), file=sys.stderr)
print(repr(d), file=sys.stderr)
if not d["LAT"] or not d["LON"]:
break
pd = {
"latitude": gprmc2deg(d["LAT"], d["NS"]),
"longitude": gprmc2deg(d["LON"], d["EW"]),
}
if d["TIME"] and d["DATE"]:
pd["time"] = datetime(
int(d["DATE"][0:2])+2000,
int(d["DATE"][2:4]),
int(d["DATE"][4:6]),
int(d["TIME"][0:2]),
int(d["TIME"][2:4]),
int(d["TIME"][4:6])
)
if d["SOG"]:
pd["speed"] = float(d["SOG"]) # knots
if not rd["LAT"] or not rd["LON"] or rd["SERIAL"] < serial:
if verbose >= 3:
print(f"DROPPED: {repr(rd)}", file=sys.stderr)
continue
if verbose >= 3:
print(repr(rd), file=sys.stderr)
if verbose>0 and rd["BAT"] != b"F":
print(f"Battery is {repr(rd['BAT'])} at {rd['SERIAL']}", file=sys.stderr)
pd = {
"latitude": gprmc2deg(rd["LAT"], rd["NS"]),
"longitude": gprmc2deg(rd["LON"], rd["EW"]),
}
if rd["TIME"] and rd["DATE"]:
pd["time"] = datetime(
int(rd["DATE"][4:6])+2000,
int(rd["DATE"][2:4]),
int(rd["DATE"][0:2]),
int(rd["TIME"][0:2]),
int(rd["TIME"][2:4]),
int(rd["TIME"][4:6])
)
if rd["SOG"]:
pd["speed"] = float(rd["SOG"]) # knots
if verbose == 2:
print(repr(pd), file=sys.stderr) print(repr(pd), file=sys.stderr)
if raw==1:
if fieldnames:
print(", ".join([f"{k}={pd[k]}" for k in pd]))
else:
print(" ".join([f"{pd[k]}" for k in pd]))
if raw==2:
cd = {}
cd.update(strorbytes(rd, 'BAT'))
cd.update(strorbytes(rd, 'SERIAL'))
cd.update(strorbytes(rd, 'PHONE'))
cd['time'] = pd['time'].isoformat()+'Z'
cd['lat'] = f"{rd['LAT'][:2].decode()}°{rd['LAT'][2:].decode()}'{rd['NS'].decode()}"
cd['lon'] = f"{rd['LON'][:3].decode()}°{rd['LAT'][3:].decode()}'{rd['EW'].decode()}"
cd.update(strorbytes(rd, 'SOG'))
cd.update(strorbytes(rd, 'COG'))
if fieldnames:
print(", ".join([f"{k}={cd[k]}" for k in cd]))
else:
print(" ".join([cd[k] for k in cd]))
if raw==3:
if fieldnames:
print(", ".join([f"{k}={repr(rd[k])[2:-1]}" for k in rd if rd[k]]))
else:
print(" ".join([f"{repr(rd[k])[2:-1]}" for k in rd if rd[k]]))
point = gpxpy.gpx.GPXTrackPoint(**pd) point = gpxpy.gpx.GPXTrackPoint(**pd)
segment.points.append(point) segment.points.append(point)
if follow and not raw and verbose < 2:
print(repr(point), file=sys.stderr)
def strorbytes(rd, f):
if not f in rd:
return {}
try:
return {f: rd[f].decode()}
except:
pass
return {f: repr(rd[f])[2:-1]}
# gpxpy.gpx.GPXTrackPoint( # gpxpy.gpx.GPXTrackPoint(
# latitude: Optional[float] = None, # latitude: Optional[float] = None,
# longitude: Optional[float] = None, # longitude: Optional[float] = None,
@ -94,8 +225,6 @@ class TKGPX(gpxpy.gpx.GPX):
# name: Optional[str] = None, # name: Optional[str] = None,
# ) -> None # ) -> None
inp = sys.stdin.buffer X = TKGPX(files)
if len(sys.argv) > 1: if not raw:
inp = open(sys.argv[1], "rb") print(X.to_xml())
print(TKGPX(inp).to_xml())