Ham-APRS-FAP

 view release on metacpan or  search on metacpan

FAP.pm  view on Meta::CPAN

# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.

# This allows declaration	use Ham::APRS::FAP ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
##our %EXPORT_TAGS = (
##	'all' => [ qw(
##
##	) ],
##);

our @EXPORT_OK = (
##	@{ $EXPORT_TAGS{'all'} },
	'&parseaprs',
	'&kiss_to_tnc2',
	'&tnc2_to_kiss',
	'&aprs_duplicate_parts',
	'&count_digihops',
	'&check_ax25_call',
	'&distance',
	'&direction',
	'&make_object',
	'&make_timestamp',
	'&make_position',
	'&mice_mbits_to_message',
);

##our @EXPORT = qw(
##	
##);

our $VERSION = '1.21';


# Preloaded methods go here.

# no debugging by default
my $debug = 0;

my %result_messages = (
	'unknown' => 'Unsupported packet format',
	
	'packet_no' => 'No packet given to parse',
	'packet_short' => 'Too short packet',
	'packet_nobody' => 'No body in packet',
	
	'srccall_noax25' => 'Source callsign is not a valid AX.25 call',
	'srccall_badchars' => 'Source callsign contains bad characters',
	
	'dstpath_toomany' => 'Too many destination path components to be AX.25',
	'dstcall_none' => 'No destination field in packet',
	'dstcall_noax25' => 'Destination callsign is not a valid AX.25 call',
	
	'digicall_noax25' => 'Digipeater callsign is not a valid AX.25 call',
	'digicall_badchars' => 'Digipeater callsign contains bad characters',
	
	'timestamp_inv_loc' => 'Invalid timestamp in location',
	'timestamp_inv_obj' => 'Invalid timestamp in object',
	'timestamp_inv_sta' => 'Invalid timestamp in status',
	'timestamp_inv_gpgga' => 'Invalid timestamp in GPGGA sentence',
	'timestamp_inv_gpgll' => 'Invalid timestamp in GPGLL sentence',
	
	'packet_invalid' => 'Invalid packet',
	
	'nmea_inv_cval' => 'Invalid coordinate value in NMEA sentence',
	'nmea_large_ew' => 'Too large value in NMEA sentence (east/west)',
	'nmea_large_ns' => 'Too large value in NMEA sentence (north/south)',
	'nmea_inv_sign' => 'Invalid lat/long sign in NMEA sentence',
	'nmea_inv_cksum' => 'Invalid checksum in NMEA sentence',
	
	'gprmc_fewfields' => 'Less than ten fields in GPRMC sentence ',
	'gprmc_nofix' => 'No GPS fix in GPRMC sentence',
	'gprmc_inv_time' => 'Invalid timestamp in GPRMC sentence',
	'gprmc_inv_date' => 'Invalid date in GPRMC sentence',
	'gprmc_date_out' => 'GPRMC date does not fit in an Unix timestamp',
	
	'gpgga_fewfields' => 'Less than 11 fields in GPGGA sentence',
	'gpgga_nofix' => 'No GPS fix in GPGGA sentence',
	
	'gpgll_fewfields' => 'Less than 5 fields in GPGLL sentence',
	'gpgll_nofix' => 'No GPS fix in GPGLL sentence',
	
	'nmea_unsupp' => 'Unsupported NMEA sentence type',
	
	'obj_short' => 'Too short object',
	'obj_inv' => 'Invalid object',
	'obj_dec_err' => 'Error in object location decoding',
	
	'item_short' => 'Too short item',
	'item_inv' => 'Invalid item',
	'item_dec_err' => 'Error in item location decoding',
	
	'loc_short' => 'Too short uncompressed location',
	'loc_inv' => 'Invalid uncompressed location',
	'loc_large' => 'Degree value too large',
	'loc_amb_inv' => 'Invalid position ambiguity',
	
	'mice_short' => 'Too short mic-e packet',
	'mice_inv' => 'Invalid characters in mic-e packet',
	'mice_inv_info' => 'Invalid characters in mic-e information field',
	'mice_amb_large' => 'Too much position ambiguity in mic-e packet',
	'mice_amb_inv' => 'Invalid position ambiguity in mic-e packet',
	'mice_amb_odd' => 'Odd position ambiguity in mic-e packet',
	
	'comp_inv' => 'Invalid compressed packet',
	
	'msg_inv' => 'Invalid message packet',
	
	'wx_unsupp' => 'Unsupported weather format',
	'user_unsupp' => 'Unsupported user format',
	
	'dx_inv_src' => 'Invalid DX spot source callsign',
	'dx_inf_freq' => 'Invalid DX spot frequency',
	'dx_no_dx' => 'No DX spot callsign found',
	
	'tlm_inv' => 'Invalid telemetry packet',
	'tlm_large' => 'Too large telemetry value',
	'tlm_unsupp' => 'Unsupported telemetry',
	
	'exp_unsupp' => 'Unsupported experimental',
	
	'sym_inv_table' => 'Invalid symbol table or overlay',
);

=over

=item result_messages( )

Returns a reference to a hash containing all possible
return codes as the keys and their plain english descriptions
as the values of the hash.

=back

=cut

sub result_messages()
{
	return \%result_messages;
}

FAP.pm  view on Meta::CPAN

				_a_err($rethash, 'gprmc_inv_date', "$year $2 $1");
				return 0;
			}
			$month = $2 + 0; # force numeric
			$day = $1 + 0;
		} else {
			_a_err($rethash, 'gprmc_inv_date');
			return 0;
		}
		# Date_to_Time() can only handle 32-bit unix timestamps,
		# so make sure it is not used for those years that
		# are outside that range.
		if ($year >= 2038 || $year < 1970) {
			$rethash->{'timestamp'} = 0;
			_a_err($rethash, 'gprmc_date_out', $year);
			return 0;
		} else {
			$rethash->{'timestamp'} = Date_to_Time($year, $month, $day, $hour, $minute, $second);
		}

		# speed (knots) and course, make these optional
		# in the parsing sense (don't fail if speed/course
		# can't be decoded).
		if ($nmeafields[7] =~ /^\s*(\d+(|\.\d+))\s*$/o) {
			# convert to km/h
			$rethash->{'speed'} = $1 * $knot_to_kmh;
		}
		if ($nmeafields[8] =~ /^\s*(\d+(|\.\d+))\s*$/o) {
			# round to nearest integer
			my $course = int($1 + 0.5);
			# if zero, set to 360 because in APRS
			# zero means invalid course...
			if ($course == 0) {
				$course = 360;
			} elsif ($course > 360) {
				$course = 0; # invalid
			}
			$rethash->{'course'} = $course;
		} else {
			$rethash->{'course'} = 0; # unknown
		}

		# latitude and longitude
		my $latitude = _nmea_getlatlon($nmeafields[3], $nmeafields[4], $rethash);
		if (not(defined($latitude))) {
			return 0;
		}
		$rethash->{'latitude'} = $latitude;
		my $longitude = _nmea_getlatlon($nmeafields[5], $nmeafields[6], $rethash);
		if (not(defined($longitude))) {
			return 0;
		}
		$rethash->{'longitude'} = $longitude;

		# we have everything we want, return
		return 1;

	} elsif ($nmeafields[0] eq 'GPGGA') {
		# we want at least 11 fields
		if (@nmeafields < 11) {
			_a_err($rethash, 'gpgga_fewfields', scalar(@nmeafields));
			return 0;
		}

		# check for position validity
		if ($nmeafields[6] =~ /^\s*(\d+)\s*$/o) {
			if ($1 < 1) {
				_a_err($rethash, 'gpgga_nofix', $1);
				return 0;
			}
		} else {
			_a_err($rethash, 'gpgga_nofix');
			return 0;
		}

		# Use the APRS time parsing routines to check
		# the time and convert it to timestamp.
		# But before that, remove a possible decimal part
		$nmeafields[1] =~ s/\.\d+$//;
		$rethash->{'timestamp'} = _parse_timestamp($options, $nmeafields[1] . 'h');
		if ($rethash->{'timestamp'} == 0) {
			_a_err($rethash, 'timestamp_inv_gpgga');
			return 0;
		}

		# latitude and longitude
		my $latitude = _nmea_getlatlon($nmeafields[2], $nmeafields[3], $rethash);
		if (not(defined($latitude))) {
			return 0;
		}
		$rethash->{'latitude'} = $latitude;
		my $longitude = _nmea_getlatlon($nmeafields[4], $nmeafields[5], $rethash);
		if (not(defined($longitude))) {
			return 0;
		}
		$rethash->{'longitude'} = $longitude;

		# altitude, only meters are accepted
		if ($nmeafields[10] eq 'M' &&
		    $nmeafields[9] =~ /^(-?\d+(|\.\d+))$/o) {
			# force numeric interpretation
			$rethash->{'altitude'} = $1 + 0;
		}

		# ok
		return 1;

	} elsif ($nmeafields[0] eq 'GPGLL') {
		# we want at least 5 fields
		if (@nmeafields < 5) {
			_a_err($rethash, 'gpgll_fewfields', scalar(@nmeafields));
			return 0;
		}

		# latitude and longitude
		my $latitude = _nmea_getlatlon($nmeafields[1], $nmeafields[2], $rethash);
		if (not(defined($latitude))) {
			return 0;
		}
		$rethash->{'latitude'} = $latitude;
		my $longitude = _nmea_getlatlon($nmeafields[3], $nmeafields[4], $rethash);
		if (not(defined($longitude))) {
			return 0;
		}
		$rethash->{'longitude'} = $longitude;

		# Use the APRS time parsing routines to check
		# the time and convert it to timestamp.
		# But before that, remove a possible decimal part
		if (@nmeafields >= 6) {
			$nmeafields[5] =~ s/\.\d+$//;
			$rethash->{'timestamp'} = _parse_timestamp($options, $nmeafields[5] . 'h');
			if ($rethash->{'timestamp'} == 0) {
				_a_err($rethash, 'timestamp_inv_gpgll');
				return 0;
			}
		}

		if (@nmeafields >= 7) {
			# GPS fix validity supplied
			if ($nmeafields[6] ne 'A') {
				_a_err($rethash, 'gpgll_nofix');
				return 0;
			}
		}

		# ok
		return 1;

	##} elsif ($nmeafields[0] eq 'GPVTG') {
	##} elsif ($nmeafields[0] eq 'GPWPT') {
	} else {
		$nmeafields[0] =~ tr/[\x00-\x1f]//d;
		_a_err($rethash, 'nmea_unsupp', $nmeafields[0]);
		return 0;
	}

	return 0;
}


# Parse the possible APRS data extension
# as well as comment
sub _comments_to_decimal($$$) {
	my $rest = shift @_;
	my $srccallsign = shift @_;
	my $rethash = shift @_;
	
	# First check the possible APRS data extension,
	# immediately following the packet
	if (length($rest) >= 7) {
		if ($rest =~ /^([0-9. ]{3})\/([0-9. ]{3})/o) {
			my $course = $1;
			my $speed = $2;
			if ($course =~ /^\d{3}$/o &&
			    $course <= 360 &&
			    $course >= 1) {
				# force numeric interpretation
				$course += 0;
				$rethash->{'course'} = $course;
			} else {
				# course is invalid, set it to zero
				$rethash->{'course'} = 0;
			}
			if ($speed =~ /^\d{3}$/o) {
				# force numeric interpretation
				# and convert to km/h
				$rethash->{'speed'} = $speed * $knot_to_kmh;
			} else {
				# If speed is invalid, don't set it
				# (zero speed is a valid speed).
			}
			$rest = substr($rest, 7);

		} elsif ($rest =~ /^PHG(\d[\x30-\x7e]\d\d[0-9A-Z])\//o) {
			# PHGR
			$rethash->{'phg'} = $1;
			$rest = substr($rest, 8);

		} elsif ($rest =~ /^PHG(\d[\x30-\x7e]\d\d)/o) {
			# don't do anything fancy with PHG, just store it
			$rethash->{'phg'} = $1;



( run in 1.826 second using v1.01-cache-2.11-cpan-e1769b4cff6 )