#!/usr/bin/perl
# dl5di, 27.08.2002
# posit2rf 
# - liest vom TCP-Socket eines APRSD-Servers
# - filtert Daten nach Ursprungsserver
# - filtert Daten nach Absender-Prefix
# - ergaenzt Stationen mit fremdem Prefix, die sich innerhalb einer
#   vorgegebenen Entfernung befinden
# - prueft auf Fest-/Mobilstation
# - sendet Positionsdaten mit vorgegebenem Mindestzeitabstand (fest/mob)
#   ueber den UDP-Port eines APRSD-Servers aufs TNC
#
# Errorcodes im Debug-Output:
# (1) Feststation unterschreitet Bakenabstand time_fix
# (2) Mobilstation unterschreitet Bakenabstand time_mob
# (3) Position des Messageziels ausserhalb max_dist
# (4) Position des Messageziels unbekannt
# (5) Statusbakenabstand unterschritten
# (6) Position des Message-Absenders unbekannt
# (7/x) BAD: Ungueltiger Absender
# 	(7/1) unverified Absender
# 	(7/2) eigene Aussendung
# 	(7/3) Filter 1
# 	(7/4) Filter 2
# 	(7/5) Filter 3
# 	(7/6) nicht in Datenbank, Datensatz wird angelegt
#
# 27.08.2002, dl5di: q-Call des IGates wird weggefiltert
# 18.08.2002, dl5di: MIC-E codierte Positionen, Status- und Message-Baken beruecksichtigen
# 06.08.2000, dl5di: Entfernungsberechnung
# 08.08.2000, dl5di: Unterdrueckung von UDP-Baken (eigenes Call mit SSIDs)
# 12.08.2000, dl5di: Unterdrueckung von unverifizierten TCP/IP-Usern
# 13.08.2000, dl5di: Fehler Returncode "unverifizierter User" behoben
#

use Socket;
#
# Datenherkunft (Telnet-Server):
#$tcphost = "db0lj.ampr.org";
$tcphost = "localhost";
$tcpport = 10153;
#
# Datenziel (UDP-Server):
#$udphost = "db0lj.ampr.org";
$udphost = "localhost";
$udpport = 1315;
#
# Absender-Server: 
# Hostname oder Teil von Hostnamen
# (z.B. "aprs" fuer Internet-uebliche Hostnamen)
# leer = alle, ausser eigener
$source = "";
#
# Absendercall fuer TNC-Daten:
$OwnCall = "DB0LJ";
#
# Filter-Calls (beinhaltet der Pfad dieses Call, wird das Paket nicht gesendet)
$filter1 = "DB0QT";
$filter2 = "DB0DBU";
$filter3 = "DK6WX";

# Call-Prefix der zu uebertragenden Positionsdaten
# "-" = Funktion deaktiviert
$prefix = "D";
#
# max. Entfernung fuer Ausgabe von anderen Prefixen (in km)
# 0 = Funktion deaktiviert
$maxdist=100;
#
# Pfad fuer Datenbank
$dbpath = "/var/lib/aprsd2";
#
# Bakenabstand Feststationen (sec)
$time_fix = 890;
#
# Bakenabstand Mobilstationen (sec)
$time_mob = 110;
#
# Abstand Status-Messages
$time_status = 890;
#
# eigene Koordinaten
# noerdl. Breite    <grad>:<min>  (<min> mit Dezimalen)
# (Vorzeichen "-" fuer suedl. Breite)
$NB = "50:23.26";
# oestl. Laenge     <grad>:<min>  (<min> mit Dezimalen)
# (Vorzeichen "-" fuer westl. Laenge)
$EL = "7:20.48";
#
################### in DL nahezu konstant: ###############################
#
# Entfernung fuer 1 Breitengrad (111km)
$bdist = 111;
# Entfernung fuer 1 Laengengrad (70km Hoehe Koeln)
$ldist = 70;
#
#################### ab hier keine Variablen #############################

# Ausgabeverbindung zum UDP-Server
$udpserver = sockaddr_in ($udpport , inet_aton('$udphost'));
$udptype = getprotobyname('udp');
socket(OUTSOCKET,2,SOCK_DGRAM,$udptype) || die("No UDP-Socket\n");

# Eingangsverbindung zum TCP-Server
$tcpserver = sockaddr_in ($tcpport , inet_aton('$tcphost'));
$tcptype = getprotobyname('tcp');
socket(INSOCKET,PF_INET,SOCK_STREAM,$tcptype) || die("No TCP-Socket\n");
connect (INSOCKET, $tcpserver) || die("No TCP-Connect\n");

dbmopen(%mh,"$dbpath/mhtable",0644);

# eigene ReferenzPos. in km umrechnen
($NBG, $NBM) = split(/:/,$NB);
($ELG, $ELM) = split(/:/,$EL);
$N = (($NBG * 60) + $NBM) * ($bdist / 60);
$E = (($ELG * 60) + $ELM) * ($ldist / 60);

print "\n\nposit2rf Version 17.8.2002, dl5di\@darc.de\n\n";
print "eigene Position: $NBG grd $NBM min N, $ELG grd $ELM min E\n";
print "zu meldender Prefix: $prefix\n";
if($maxdist > 0) {
    print "Meldeentfernung fuer andere Prefixe: max. $maxdist km\n";
} else {
    print "keine Meldung von anderen Prefixen aktiviert\n";
}   
print "TNC-Call: $OwnCall\n";
print "Connecteter APRS-Server: $tcphost, Port $tcpport\n";
print "APRS-Server mit UDP/TNC: $udphost, Port $udpport\n";
if($source) {
    print "Source-Host: *$source*\n";
} else {
    print "Source-Host: alle\n";
}
print "Mindestabstand Positionsbaken: mobil: $time_mob sec / stat: $time_fix sec\n\n";

# auf gehts im Endlosloop

while ($line = <INSOCKET> ) {

    $bad = 0; $fb = 1; $fl = 1; $dist = 99999; $pos = 0; $pos_b = 0; $pos_l = 0; $micpos = 0;
    $status = 0; $lastu = 0;

    ($call,$satz) = split(/[>]/,$line,2);
    ($call1,$ssid) = split(/[-]/,$call,2);
# print "$call\n";		## DEBUG

# auf Call-Format pruefen
    $tmp = $call1;				# Call ohne SSID
    $cnt1 = $tmp =~ tr/0-9//;			# Anzahl Ziffern
    $tmp = $call1;
    $cnt2 = $tmp =~ tr/A-Za-z//;		# Anzahl Buchstaben
    if (($cnt1 == 1) && ($cnt2 > 3) && ($cnt2 < 6) && ($call1 ne $OwnCall)) {

# Datenfeld abschneiden und auf Positionsdaten pruefen
	($path,$rest) = split(/[:]/,$line,2);
	($path,$datas,$junk) = split(/[:]/,$line,3);
	$pattern = $datas;
	$pattern =~ tr/0-9/#/;

	if(index($path,"TCPXX") > 0) {
	    $bad = 1;
	}

	if(index($path,$OwnCall) > 0) {
	    $bad = 2;
	}

	if(index($path,$filter1) > 0) {
	    $bad = 3;
	}

	if(index($path,$filter2) > 0) {
	    $bad = 4;
	}

	if(index($path,$filter3) > 0) {
	    $bad = 5;
	}

        # strip off q-Path of igates from ax25path
	if(($pos = index($path,",q")) > 0) {
	    $path = substr($path,0,$pos);
	}

	if(($b = index($pattern,"####.##N")) < 0) {
	    $b = index($pattern,"####.##S");
	    $fb = -1;
	}

	if(($l = index($pattern,"#####.##E")) < 0) {
	    $l = index($pattern,"#####.##W");
	    $fl = -1;
	}

	if((index($datas,'\'') == 0) || (index($datas,'`') == 0) || (index($datas,'') == 0) || (index($datas,'') == 0)) {
	    $micpos = substr($datas,1,3); 	# (3 Position-Bytes)
	    $pos = 1;				# MIC-E Position ohne Entfernung
	}

	if((index($datas,'=') == 0) && ($pos == 0)) {
	    $micpos = substr($datas,1,3); 	# (3 Position-Bytes)
	    $pos = 1;				# MIC-E Position ohne Entfernung
	}

	if(($b > 0) && ($l > 0)) {

# Positionen extrahieren und Entfernung berechnen
	    $pos_b = substr($datas, $b, 8);		# Breite
	    $pos_l = substr($datas, $l, 9);		# Laenge

    	    $nbg = substr($datas, $b, 2);		# Breitengrad
	    $nbm = substr($datas, $b+2, 5);		# B-Minute
	    $elg = substr($datas, $l, 3);		# Laengengrad
	    $elm = substr($datas, $l+3, 5);		# L-Minute

	    $n = (($nbg * 60) + $nbm) * ($bdist / 60) * $fb;
	    $e = (($elg * 60) + $elm) * ($ldist / 60) * $fl;

	    $dist = int(sqrt((($N - $n) ** 2 ) + (($E - $e) ** 2)));
	    $pos = 2;			# Klartext mit Entfernung
	}

# auf D-Prefix oder MaxEntfernung testen und mit Datenbankwerten vergleichen

	if($mh{$call}) {
	    $usrdat = $mh{$call};
	    ($lastu,$pos_B,$pos_L,$MicPos,$status,$Dist) = split(/:/,$usrdat,6);
	    $diff = time - $lastu;

	    if (($pos == 1) && ($Dist > 0)) {  # Mic-E ohne Entfernung, aber Entfernung aus Klartext in DB
		$dist = $Dist;
	        $pos = 3;			# MIC-E mit Entfernung aus alter Klartextbake
	    }
	} elsif ($pos > 1) {
	    # neuen Datensatz anlegen
	    $bad = 6;
	}

        if((index($call,$prefix) == 0) || ($dist < $maxdist)) {

#	    if($pos == 1) {		# MIC-E	ohne Entfernung		## DEBUG
#		printf "mic-e >$3.3s< : ",$micpos;			## DEBUG
#	    } elsif($pos == 2) {	# Klartext + Entfernung		## DEBUG
#		printf "%5d km : ",$dist;				## DEBUG
#	    } elsif($pos == 3) {	# MIC-E + alte Entfernung	## DEBUG
#		printf "mic-e >$3.3s< %5d km : ",$micpos,$dist;	## DEBUG
#	    }								## DEBUG

	    if($bad > 0) {
		    $lastu = time;
    		    $usrdat = "$lastu:$pos_b:$pos_l:$micpos:$status:$dist";
		    $mh{$call} = $usrdat;
#		    print("$call $diff - (7/$bad) $lastu:$pos_b:$pos_l:$micpos:$status:$dist\n");	## DEBUG
		    next;
	    }

# Zeitabstand der Baken nach Fest- und Mobilstation pruefen
	    if($pos > 0) {
		if ((($pos == 1) && ($pos_b eq $pos_B) && ($pos_l eq $pos_L)) ||
		    (($pos > 1) && ($micpos eq $MicPos))){
		    if ($diff > $time_fix) {
	    		send OUTSOCKET, "$OwnCall>TNC:}$path:$rest\0" , 0 , $udpserver;
			$lastu = time;
			$usrdat = "$lastu:$pos_b:$pos_l:$micpos:$status:$dist";
			$mh{$call} = $usrdat;
#			print("$call $diff +\n");			## DEBUG
#		    } else {						## DEBUG
#			print("$call - $diff (1)\n");			## DEBUG
		    }
		} else {
		    if ($diff > $time_mob) {
        		send OUTSOCKET, "$OwnCall>TNC:}$path:$rest\0" , 0 , $udpserver;
			$lastu = time;
			$usrdat = "$lastu:$pos_b:$pos_l:$micpos:$status:$dist";
			$mh{$call} = $usrdat;
#			print("$call mob $diff +\n");		## DEBUG
#		    } else {					## DEBUG
#			print("$call mob $diff - (2)\n");	## DEBUG
		    }
		}
	    } else {

		# Messages	    
		if($usrdat) {
		    # Messages an Stationen, deren Position bekannt ist
		    if (index($datas,':') == 0) {
			($junk,$ziel,$junk) = split(/[: ]/,$datas,3);
			if ($mh{$ziel}) {
			    $zieldat = $mh{$ziel};
			    ($junk,$junk,$junk,$junk,$junk,$Dist) = split(/:/,$zieldat,6);
			    if($Dist < $maxdist) {
				send OUTSOCKET, "$OwnCall>TNC:}$line\0" , 0 , $udpserver;
#				print "msg + : $line";				## DEBUG
#			    } else {						## DEBUG
#				printf "msg - %5d km (3) %s",$Dist,$line;	## DEBUG
			    }
#			} else {						## DEBUG
#			    print "msg - (4): $line";				## DEBUG
			}
		    # Statusinfos an Stationen, deren Position bekannt ist
		    } elsif ((index($datas,'>') == 0) && (index($datas,'DX') != 8)) {
			$diff = time - $status;
			if ($diff > $time_status) {
			    send OUTSOCKET, "$OwnCall>TNC:}$line\0" , 0 , $udpserver;
			    $status = time;
			    $usrdat = "$lastu:$pos_b:$pos_l:$micpos:$status:$dist";
			    $mh{$call} = $usrdat;
#			    print "msg + $diff : $line";		## DEBUG
#			} else {    					## DEBUG
#    			    print "msg - $diff (5): $line";		## DEBUG
			}
		    }
#		} else {    						## DEBUG
#    		    print "msg - (6): $line";				## DEBUG
		}
	    }    
	}
    }
}

# alles schliessen und beenden

close(INSOCKET);
close(OUTSOCKET);
dbmclose(%mh);
