>OH6KTT |
>OH6KTT |
Rivi 15: |
Rivi 15: |
| #!/usr/bin/php -q | | #!/usr/bin/php -q |
| <?php | | <?php |
| /*
| | ini_set('memory_limit','50M'); |
| aprs-repeater-advertiser.php v1.10 - repeater advertiser to APRS users
| | $debug = false; |
| Copyright (C) 2010 Kari Karvonen <oh6ktt@toimii.net>
| | $stationdbfile = "/tmp/oh6ruc-aprs-db.txt"; /* aprs-repeater-advertiser.php scripts database */ |
| | |
| This program is free software: you can redistribute it and/or modify
| |
| it under the terms of the GNU General Public License as published by
| |
| the Free Software Foundation, either version 3 of the License, or
| |
| (at your option) any later version.
| |
| | |
| This program is distributed in the hope that it will be useful,
| |
| but WITHOUT ANY WARRANTY; without even the implied warranty of
| |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
| |
| GNU General Public License for more details.
| |
| | |
| You should have received a copy of the GNU General Public License
| |
| along with this program. If not, see <http://www.gnu.org/licenses/>.
| |
| | |
| */
| |
| $debug = 0; /* 0 = no debug, 1 = little debug, 2 = more debug, 3 = annoying */ | |
| $aprxrflogfile = "/tmp/aprx-rf.log";
| |
| $stationdbfile = "/tmp/oh6ruc-aprs-db.txt"; | |
| $repeater_call = "OH6RUC"; /* need to match aprs-rf.log */
| |
| $repeater_lat = "63.53666";
| |
| $repeater_lon = "23.69180";
| |
| $floodprotect = 60*60*24; /* after this time old station will be notified again. Seconds. */
| |
| $maxdistance = 30; /* km */
| |
| $maxloglines = 1000; /* max loglines to analyze (tail) */
| |
| $denytxfile = "/tmp/ptt-on"; /* if this file exist, no advertises will be send */
| |
| $prefixwave = "/opt/thelinkbox/wav/aprs-repeater-advertiser-1.wav"; /* wave before callsigns */
| |
| $suffixwave = "/opt/thelinkbox/wav/aprs-repeater-advertiser-2.wav"; /* wave after callsigns */
| |
| $tempwavetxt = "/tmp/aprs-repeater-advertiser.txt";
| |
| $tempwave = "/tmp/aprs-repeater-advertiser.wav";
| |
| $repeatedmatch = ",OH"; /* if this is exists on path (eg APRS,OH6RUC*,OH8RDU*,WIDE) assume packet digirepeated and discard it */
| |
| | |
| /* code begins */
| |
| $parserdb = array();
| |
| $victims = array();
| |
| $stationdb = unserialize(@file_get_contents($stationdbfile)); | | $stationdb = unserialize(@file_get_contents($stationdbfile)); |
| if (empty($stationdb)) $stationdb = array();
| | $timelimit = time()-3600*24; |
| $aprxrflog = @file($aprxrflogfile); | | $max = 10; /* max stations to speak out */ |
| if ($aprxrflog == false) die("Cannot open $aprxrflogfile\n");
| |
| | |
| function min_to_dec($deg, $min, $decsec) {
| |
| return($deg/1.0 + $min/60.0 + $decsec/10000.0);
| |
| }
| |
| | |
| function latlongbearing($lat1, $lon1, $lat2, $lon2) {
| |
| $lat1 = deg2rad($lat1);
| |
| $lat2 = deg2rad($lat2);
| |
| $lon1 = deg2rad ($lon1);
| |
| $lon2 = deg2rad ($lon2);
| |
| $dLon = $lon2-$lon1;
| |
| $y = sin($dLon) * cos($lat2);
| |
| $x = cos($lat1) * sin($lat2) - sin($lat1)*cos($lat2)*cos($dLon);
| |
| $bearrad = atan2($y, $x);
| |
| $beardeg = (rad2deg($bearrad)+360)%360;
| |
| return ($beardeg);
| |
| }
| |
| | |
| function decimal_distance($lat1 = "", $lon1 = "", $lat2 = "", $lon2 = "") {
| |
| $radius = 6371;
| |
| $lat1 = deg2rad ($lat1);
| |
| $lat2 = deg2rad ($lat2);
| |
| $lon1 = deg2rad ($lon1);
| |
| $lon2 = deg2rad ($lon2);
| |
| $dlon = $lon2-$lon1;
| |
| $dlat = $lat2-$lat1;
| |
| $sinlat = sin($dlat/2);
| |
| $sinlon = sin($dlon/2);
| |
| $a = ($sinlat * $sinlat) + cos($lat1) * cos($lat2) * ($sinlon*$sinlon);
| |
| $c = 2 * asin(min(1,sqrt($a)));
| |
| $d = $radius * $c;
| |
| return round($d,2);
| |
| }
| |
|
| |
|
| function callsign2finnish($callsign) { | | function callsign2finnish($callsign) { |
Rivi 145: |
Rivi 77: |
| } | | } |
|
| |
|
| | | $hamssilkm = 0; |
| $aprxrfloglen = count($aprxrflog); | | $jorina = ""; |
| if ($aprxrfloglen > $maxloglines) $loglinestart = $aprxrfloglen-$maxloglines; else $loglinestart=0;
| | foreach ($stationdb as $callsign => $attcheddata) { |
| if ($debug>1) echo "aprxrfloglen $aprxrfloglen maxloglines $maxloglines loglinestart $loglinestart\n";
| | $lastheard = $attcheddata["lastheard"]; |
| | | if ($timelimit <= $lastheard) { |
| for ($tmp = $loglinestart; $tmp < $aprxrfloglen; $tmp++) {
| | if ($hamssitlkm >= $max) continue; |
| $line = $aprxrflog[$tmp]; | | $hamssitlkm++; |
| if (preg_match('/^([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2}).([0-9]{3}) '.$repeater_call.' R (.+)>(.+):(.{1})(.+)/', $line, $matches)) { | | $distance = ceil($attcheddata["distance"]); |
| $callsign = strtolower(trim($matches[8]));
| | $lat = $attcheddata["lat"]; |
| if (strpos($callsign,">")) {
| | $long = $attcheddata["long"]; |
| /* Callsign parse failed (ugly trace packets) */
| | $coordtype = $attcheddata["coordtype"]; |
| if ($debug>2) echo "Ugly callsign $callsign. Discard.\n";
| | $bearing = round($attcheddata["bearing"],0); |
| continue;
| | $jorina .= callsign2finnish($callsign) . " $distance kilometriä suunnassa $bearing astetta. "; |
| }
| | } |
| if (strstr($matches[9],$repeatedmatch)) { | |
| /* digirepeated patcket, discard */
| |
| if ($debug>2) echo "Digirepeated packet. Discard.\n";
| |
| continue;
| |
| }
| |
| if (strpos("\!=/@", $matches[10])) {
| |
| /* ! Position without timestamp (no APRS messaging), or Ultimeter 2000 WX Station */
| |
| /* = Position without timestamp (with APRS messaging) */
| |
| /* / Position with timestamp (no APRS messaging) */
| |
| /* @ Position with timestamp (with APRS messaging) */
| |
| } else {
| |
| /* is not position format */
| |
| if ($debug>1) echo "Unknown position identification ". $matches[10].". Discard.\n";
| |
| continue;
| |
| } | |
|
| |
| if (preg_match('/([0-9]{2})([0-9]{2}).([0-9]{1,})N(.{1})([0-9]{3})([0-9]{2}).([0-9]{1,})E/', $matches[11], $plaincoords)) {
| |
| /* coordinates are plain format */
| |
| $coordtype = "plain";
| |
| $lat = min_to_dec($plaincoords[1], $plaincoords[2], $plaincoords[3]);
| |
| $long = min_to_dec(intval($plaincoords[5]), $plaincoords[6], $plaincoords[7]);
| |
| } else {
| |
| /* coordinates are packed */
| |
| $coordtype = "packed";
| |
| $y1 = ord(substr($matches[11],1,1));
| |
| $y2 = ord(substr($matches[11],2,1));
| |
| $y3 = ord(substr($matches[11],3,1));
| |
| $y4 = ord(substr($matches[11],4,1));
| |
| $x1 = ord(substr($matches[11],5,1));
| |
| $x2 = ord(substr($matches[11],6,1));
| |
| $x3 = ord(substr($matches[11],7,1));
| |
| $x4 = ord(substr($matches[11],8,1));
| |
| $lat = 90 - (($y1-33) * 91*91*91 + ($y2-33) * 91*91 + ($y3-33) * 91 + $y4-33) / 380926;
| |
| $long = -180 + (($x1-33) * 91*91*91 + ($x2-33) * 91*91 + ($x3-33) * 91 + $x4-33) / 190463;
| |
| }
| |
| $distance = decimal_distance($repeater_lat, $repeater_lon, $lat, $long);
| |
| $bearing = latlongbearing($repeater_lat, $repeater_lon, $lat, $long);
| |
| $lastheard = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); | |
| $parserdb[$callsign] = array("lastheard" => $lastheard, "distance" => $distance, "bearing" => $bearing, "lat" => $lat, "long" => $long, "coordtype" => $coordtype);
| |
| }
| |
| }
| |
| | |
| | |
| ksort($parserdb);
| |
| foreach ($parserdb as $callsign => $attcheddata) {
| |
| $distance = $attcheddata["distance"];
| |
| $lastheard = $attcheddata["lastheard"];
| |
| $lat = $attcheddata["lat"];
| |
| $long = $attcheddata["long"];
| |
| $coordtype = $attcheddata["coordtype"];
| |
| $bearing = $attcheddata["bearing"];
| |
| $lastheardtxt = date("r", $lastheard);
| |
| $advertisedtime = $stationdb[$callsign]["advertisedtime"];
| |
| $advertisedtimetxt = date("r",$advertisedtime);
| |
| if ($debug>0) echo "Call $callsign. Lat $lat. Long $long. Coordtype $coordtype. Distance $distance. Bearing $bearing. Lastheard $lastheardtxt. Advertised $advertisedtimetxt. ";
| |
| if (array_key_exists($callsign, $stationdb)) {
| |
| if ($debug>0) echo "Found from stationdb. ";
| |
| if ($advertisedtime < (time()-$floodprotect) && $lastheard>$stationdb[$callsign]["lastheard"]) {
| |
| if ($debug>0) echo "Floodprotect disabled. ";
| |
| if ($distance < $maxdistance) {
| |
| /* Exists in stationdb. Too old and inside circle */
| |
| if ($debug>0) echo "Inside $maxdistance km cicle. Add to victims.";
| |
| $victims[] = $callsign;
| |
| $advertisedtime = time();
| |
|
| |
| } else {
| |
| /* Station too far */
| |
| if ($debug>0) echo "Outside $maxdistance km circle. Skip.";
| |
| }
| |
| } else {
| |
| if ($debug>0) echo "Floodprotect enabled. ";
| |
| }
| |
| } else {
| |
| if ($debug>0) echo "Not found from stationdb. ";
| |
| if ($distance < $maxdistance) {
| |
| if ($debug>0) echo "Inside ${maxdistance}km circle. Add to victims.";
| |
| $victims[] = $callsign;
| |
| $advertisedtime = time();
| |
|
| |
| } else {
| |
| if ($debug>0) echo "Outside ${maxdistance}km circle. Skip.";
| |
| }
| |
| }
| |
| $stationdb[$callsign]["lastheard"] = $lastheard;
| |
| $stationdb[$callsign]["distance"] = $distance;
| |
| $stationdb[$callsign]["bearing"] = $bearing;
| |
| $stationdb[$callsign]["lat"] = $lat;
| |
| $stationdb[$callsign]["long"] = $long;
| |
| $stationdb[$callsign]["coordtype"] = $coordtype;
| |
| $stationdb[$callsign]["advertisedtime"] = $advertisedtime; | |
|
| |
| if ($debug>0) echo "\n";
| |
| } | | } |
|
| |
|
| if ($debug>0) echo "Updating $stationdbfile\n";
| | $msg = "oohookuusärruusee toistimen alueella "; |
| file_put_contents($stationdbfile, serialize($stationdb));
| | if ($hamssitlkm == 0) { |
| | | $msg .= "ei ole kuultu yhtään aapeeärräss asemaa, "; |
| $msg = ""; | |
| if (count($victims) == 0) { | |
| /* do nothing */ | |
| } else { | | } else { |
| if (!file_exists($denytxfile)) { | | if (count($hamssit) == 1) { |
| @unlink($tempwavetxt); | | $msg .="on kuultu vain aapeeärräss asema "; |
| @unlink($tempwave);
| |
| foreach ($victims as $victim) {
| |
| $msg .= callsign2finnish($victim).", ";
| |
| }
| |
| $msg = substr($msg,0,-2);
| |
| if ($debug>0) echo "$msg\n";
| |
| if ($debug<1) file_put_contents($tempwavetxt, utf8_decode($msg));
| |
| if ($debug<1) system("/usr/bin/text2wave -F 8000 < ".$tempwavetxt." -o ".$tempwave);
| |
| if ($debug<1) system("/usr/sbin/tlbcmd \"port OH6RUC-Kaustinen; say -c ".$prefixwave."; say -c ".$tempwave."; say -c ".$suffixwave."\"");
| |
| } else { | | } else { |
| if ($debug>0) echo "Cannot advertise. Advertises denied by $denytxfile lock file.\n"; | | $msg .="on kuultu seuraavat aapeeärräss asemat. "; |
| } | | } |
| | $msg .= $jorina; |
| | } |
| | if ($hamssitlkm >0 && $hamssitlkm < $max) $msg .= " Ei muita. "; |
| | if ($hamssitlkm >= $max) $msg.=" Muitakin vielä olisi. "; |
| | $msg = substr($msg,0,-2); |
| | $msg = preg_replace( "{[ \t]+}", ' ', $msg ); |
| | /* Toistetaan wav tiedosto radioporttiin */ |
| | if (!$debug) { |
| | system("/usr/sbin/tlbcmd \"port OH6RUC-Kaustinen; say -c $msg\""); |
| | } else { |
| | echo $msg; |
| } | | } |
| ?> | | ?> |
Scripti joka tutkii ajastettuna OH6RUC toistimen linuxissa aprx ohjelman tekemää /tmp/aprx-rf.log tiedostoa. Jos logissa näkyy APRS-asema joka on lähempänä kuin 30km OH6RUC toistinta ja kyseistä APRS-asemaa ei ole kuultu edellisen 24h kuluessa, ja toistin on vapaana, scripti aukaisee toistimen ja mainostaa iseaään APRS-asemalle puheella. Puhe generoidaan festival puhesyntetisaattorilla.
"Tervetuloa OH6RUC toistinaseman kuuluvuusalueelle OH6KTT. Minä olen Kaustisen OH6RUC toistinasema taajuudella 434.900 MHz. Ripiitterierotukseni on -2 MHz. Toistan. Ripiitterierotus on -2 MHz. Automaattinen tiedote ripiitteriä lähestyville APRS-asemille päättyy."
Sama scripti pitää logia suoraan RF:llä kuulluista APRS-asemista.
Ideoita jatkokehitystä varten:
- 2m tropokelit on mahdollista havaita suoraan RF:llä kuulluista asemista
- Pitkän historian avulla db:stä voisi saada selville digiripiitterin kuuntelukyky eri ilmansuuntiin
#!/usr/bin/php -q
<?php
ini_set('memory_limit','50M');
$debug = false;
$stationdbfile = "/tmp/oh6ruc-aprs-db.txt"; /* aprs-repeater-advertiser.php scripts database */
$stationdb = unserialize(@file_get_contents($stationdbfile));
$timelimit = time()-3600*24;
$max = 10; /* max stations to speak out */
function callsign2finnish($callsign) {
$chartospeech = array();
$chartospeech["-"] = "viiva";
$chartospeech["a"] = "aa";
$chartospeech["b"] = "bee";
$chartospeech["c"] = "see";
$chartospeech["d"] = "dee";
$chartospeech["e"] = "ee";
$chartospeech["f"] = "äf";
$chartospeech["g"] = "gee";
$chartospeech["h"] = "hoo";
$chartospeech["i"] = "hee";
$chartospeech["j"] = "jii";
$chartospeech["k"] = "koo";
$chartospeech["l"] = "äl";
$chartospeech["m"] = "äm";
$chartospeech["n"] = "äm";
$chartospeech["o"] = "oo";
$chartospeech["p"] = "pee";
$chartospeech["q"] = "kuu";
$chartospeech["r"] = "är";
$chartospeech["s"] = "äs";
$chartospeech["t"] = "tee";
$chartospeech["u"] = "uu";
$chartospeech["v"] = "vee";
$chartospeech["w"] = "tuplavee";
$chartospeech["x"] = "äks";
$chartospeech["y"] = "yy";
$chartospeech["z"] = "tseta";
$chartospeech["å"] = "oo";
$chartospeech["ä"] = "ää";
$chartospeech["ö"] = "öö";
$chartospeech["1"] = "yks";
$chartospeech["2"] = "kaks";
$chartospeech["3"] = "kol";
$chartospeech["4"] = "nel";
$chartospeech["5"] = "viis";
$chartospeech["6"] = "kuus";
$chartospeech["7"] = "seiska";
$chartospeech["8"] = "kasi";
$chartospeech["9"] = "ysi";
$chartospeech["0"] = "nolla";
$msg = "";
for ($tmp = 0; $tmp < strlen($callsign); $tmp++) {
$character = substr($callsign, $tmp, 1);
/* don't say -9 */
if ($character == "-") {
return $msg;
}
$vocal = $chartospeech[$character];
$msg.= $vocal;
}
return ($msg);
}
$hamssilkm = 0;
$jorina = "";
foreach ($stationdb as $callsign => $attcheddata) {
$lastheard = $attcheddata["lastheard"];
if ($timelimit <= $lastheard) {
if ($hamssitlkm >= $max) continue;
$hamssitlkm++;
$distance = ceil($attcheddata["distance"]);
$lat = $attcheddata["lat"];
$long = $attcheddata["long"];
$coordtype = $attcheddata["coordtype"];
$bearing = round($attcheddata["bearing"],0);
$jorina .= callsign2finnish($callsign) . " $distance kilometriä suunnassa $bearing astetta. ";
}
}
$msg = "oohookuusärruusee toistimen alueella ";
if ($hamssitlkm == 0) {
$msg .= "ei ole kuultu yhtään aapeeärräss asemaa, ";
} else {
if (count($hamssit) == 1) {
$msg .="on kuultu vain aapeeärräss asema ";
} else {
$msg .="on kuultu seuraavat aapeeärräss asemat. ";
}
$msg .= $jorina;
}
if ($hamssitlkm >0 && $hamssitlkm < $max) $msg .= " Ei muita. ";
if ($hamssitlkm >= $max) $msg.=" Muitakin vielä olisi. ";
$msg = substr($msg,0,-2);
$msg = preg_replace( "{[ \t]+}", ' ', $msg );
/* Toistetaan wav tiedosto radioporttiin */
if (!$debug) {
system("/usr/sbin/tlbcmd \"port OH6RUC-Kaustinen; say -c $msg\"");
} else {
echo $msg;
}
?>
Tämä scripti on ajastettu crontabissa
# Katsotaan onko kuuluvuusalueella uusia APRS-asemia ja mainostetaan jos on
4,8,12,16,20,24,28,32,36,40,44,48,52,56 * * * * tlb /opt/thelinkbox/scripts/aprs-repeater-advertiser.php > /dev/null