forked from tchernicum/bcapps
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbc-aprs-wx.pl
executable file
·273 lines (208 loc) · 6.78 KB
/
bc-aprs-wx.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
#!/bin/perl
# Obtains weather data from the APRS stream <h>and does absolutely
# nothing with it</h>
push(@INC,"/usr/local/lib");
require "bclib.pl";
use Fcntl;
chdir(tmpdir());
# <h>let's do the time zone again</h>
$ENV{TZ} = "GMT";
for(;;) {
# if we've been waiting too long for a line, something's wrong so restart;
# this also handles startup case where $lastlinetime isn't defined yet
$since = time()-$lastlinetime;
if ($since > 10) {do_connect();}
# read next line, but ignore + sleep if blank
unless ($line = <A>) {sleep 1; next;}
debug("LINE: $line");
# we have a valid line, so update lastlinetime
$lastlinetime = time();
# process line
%hash = parse_line($line);
unless (%hash) {next;}
debug("APRSWX!");
# nuke apostrophes in everything
for $i (keys %hash) {$hash{$i}=~s/\'//isg;}
# query
$query = "REPLACE INTO aprswx (station, time, lat, lon, temp, report) VALUES
('$hash{speaker}', '$hash{utime}', '$hash{lat}', '$hash{lon}', '$hash{temp}', '$hash{report}')";
# debug("QUERY: $query");
push(@queries, $query);
# how long has it been since last update (push to db?)
$dbsince = time()-$lastupdate;
if ($dbsince > 10) {do_update();}
}
sub do_update {
my(@vpoints, @temp, @stat, @polys, @mypolys);
debug("DO_UPDATE called");
# calling this resets $lastupdate
$lastupdate = time();
# nuke entries older than an hour or in the future
my($now) = time();
my($cull) = $now - 3600;
# debug("CULL: $cull");
push(@queries, "DELETE FROM aprswx WHERE time+0 < $cull OR time+0 > $now");
# run queries in transaction
unshift(@queries, "BEGIN");
push(@queries, "COMMIT;\n");
my($query) = join(";\n", @queries);
write_file($query, "queries");
# debug("QUERIES:",read_file("queries"));
system("sqlite3 /sites/DB/aprswx.db < queries");
# wipe out queries
@queries = ();
# now, pull data are create KML file
my(@res)=sqlite3hashlist("SELECT station,lat,lon,time,temp,report FROM aprswx", "/sites/DB/aprswx.db");
unless ($#res>=0) {
# debug("NO RECORDS YET...");
return;
}
for $i (0..$#res) {
my(%hash) = %{$res[$i]};
# the voronoi point list
push(@vpoints, $hash{lon}, $hash{lat});
}
# create diagram
# TODO: for now, using equiangular mapping
# debug("VPOINTS",@vpoints);
my(@poly) = voronoi(\@vpoints);
# debug("GOT BACK",unfold(@poly));
# create KML for each polygon, first determining color
for $i (0..$#poly) {
%hash = %{$res[$i]};
my($hue) = 5/6-($hash{temp}/100)*5/6;
my($col) = hsv2rgb($hue,1,1,"kml=1&opacity=80");
push(@mypolys, poly_kml($poly[$i], $col, "description=$hash{station} ($hash{temp}, $hash{lat}, $hash{lon})&point=$hash{lon},$hash{lat}"));
}
# KML header
my($kmlhead) = << "MARK";
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
MARK
;
# KML footer
my($kmlfoot) = "</Document></kml>\n";
# polygons
my($polystring) = join("\n",@mypolys)."\n";
# write to file
write_file("$kmlhead$polystring$kmlfoot", "/home/barrycarter/BCINFO/sites/DATA/aprswx.kml");
debug("UPDATED!");
return;
}
# given a polygon and a color, return KML for it (specific to this script)
sub poly_kml {
my($poly, $col, $options) = @_;
my(%opts) = parse_form($options);
my(@coords);
# pretend static var
$static{count}++;
# style
my($style) = << "MARK";
<Style id="$static{count}">
<PolyStyle><color>$col</color>
<fill>1</fill><outline>0</outline></PolyStyle></Style>
MARK
;
# polygon header
my($polyhead) = << "MARK";
<Placemark>
<styleUrl>\#$static{count}</styleUrl>
<title>$opts{title}</title>
<description>$opts{description}</description>
<Polygon><outerBoundaryIs><LinearRing><coordinates>
MARK
;
my(@poly) = @{$poly};
# no points? return blank
if ($#poly<0) {return;}
# check bounds
for $i (@poly) {
chomp($i);
($lon,$lat) = split(/\s+/,$i);
# debug("LON/LAT: $lon/$lat");
if (abs($lon)>180) {return;}
if (abs($lat)>90) {return;}
}
map(s/ /,/isg, @poly);
# the coordinates
my($polybody) = join("\n", @poly)."\n";
# footer
my($polyfoot) = << "MARK";
</coordinates></LinearRing></outerBoundaryIs></Polygon></Placemark>
MARK
;
=item ignore_for_now
<Placemark>
<gx:balloonVisibility>0</gx:balloonVisibility>
<Point>
<Icon><href>http://test.barrycarter.info/moon.png</href></Icon>
<coordinates>$opts{point}</coordinates>
</Point>
</Placemark>
=cut
return "$style$polyhead$polybody$polyfoot";
}
# make connection to APRS server, perhaps unsuccessfuly
sub do_connect {
# TODO: major hack until http://stackoverflow.com/questions/6074698/ resolved
my(@ips) = `host rotate.aprs.net`;
$ips[rand($#ip)] =~s/.*has address (.*?)\s*$/$1/;
my($ip) = $1;
# TODO: calling this resets lastlinetime, but should it?
$lastlinetime = time();
debug("(RE)CONNECTING to $ip");
close(A);
# could "use Socket" here but this is cooler?
open(A,"echo 'user READONLY pass -1' | ncat -w 10 $ip 23 |") || warn("FAIL: Error, $!");
debug("A opened");
# unblock socket just in case we get disconnected
fcntl(A,F_SETFL,O_NONBLOCK|O_NDELAY);
debug("do_connect() returning");
}
# parse an APRS line, returning a hash of values, or blank if line is
# not in WX format
sub parse_line {
my($line) = @_;
my(%hash) = ();
my($lat, $lats, $lon, $lons, $da, $ho, $mi);
# grab temperature data, return if none
# TODO: this is an inaccurate and improper way to find weather data
# 't' is case sensitive
unless (($hash{temp}) = ($line=~/t(\d{3})/)) {return();}
# latitude/longitude
unless (($lat, $lats, $lon, $lons) =
($line=~m%([\d\.]{3,})([N|S])/([\d\.]{3,})([E|W])%)) {return();}
# observation time
unless (($da, $ho, $mi)=($line=~m%(\d{2})(\d{2})(\d{2})z%i)) {return();}
# we now know we have a valid line, so process it
$hash{report} = $line;
chomp($hash{report});
$hash{report}=~s/[^ -~]/_/isg;
# convert time to unix time
# TODO: ignoring corner case: today is 1st of month, report was on
# last day of previous month
# find current month (rest of info is useless)
my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time());
# and now, mktime
$hash{utime} = mktime(0, $mi, $ho, $da, $mon, $year);
# "speaker" (probably shouldve been $hash{station}?)
$line=~m%^(.*?)>%;
$hash{speaker} = $1;
# decimalize latitude/longitude (TODO: functionalize this)
my($latd) = floor($lat/100);
my($latm) = $lat - $latd*100;
$hash{lat} = $latd+$latm/60;
if ($lats eq "S") {$hash{lat}*=-1;}
my($lond) = floor($lon/100);
my($lonm) = $lon - $lond*100;
$hash{lon} = $lond+$lonm/60;
if ($lons eq "W") {$hash{lon}*=-1;}
return %hash;
}
# Format: "@221533z2950.68N/09529.95W_308"
=item schema
database to hold these (latest report from station obsoletes prior report):
CREATE TABLE aprswx (station, time, lat, lon, temp, report);
CREATE UNIQUE INDEX i1 ON aprswx(station);
=cut