[scripts] Add OwlPS::{CSV,TimeInterpolation}
Add Perl modules OwlPS::CSV and OwlPS::TimeInterpolation.
This commit is contained in:
parent
8ef5a55b07
commit
9073013997
|
@ -0,0 +1,203 @@
|
|||
# This file is part of the Owl Positioning System (OwlPS) project. It is
|
||||
# subject to the copyright notice and license terms in the COPYRIGHT.t2t file
|
||||
# found in the top-level directory of this distribution and at
|
||||
# http://code.lm7.fr/p/owlps/source/tree/master/COPYRIGHT.t2t
|
||||
# No part of the OwlPS Project, including this file, may be copied, modified,
|
||||
# propagated, or distributed except according to the terms contained in the
|
||||
# COPYRIGHT.t2t file; the COPYRIGHT.t2t file must be distributed along with this
|
||||
# file, either separately or by replacing this notice by the COPYRIGHT.t2t
|
||||
# file's contents.
|
||||
|
||||
|
||||
=head1 NAME
|
||||
|
||||
OwlPS::CSV - manipulate OwlPS CSV strings
|
||||
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use OwlPS::CSV;
|
||||
|
||||
get_field("a;b;c;d", 1); # returns "b"
|
||||
update_field("a;b;c", 0, "z"); # returns "z;b;c"
|
||||
split_timestamp("42.123456789"); # returns (42, 123456789)
|
||||
split_point_2d("4.3;8.1"); # returns (4.3, 8.1)
|
||||
split_point_2d("3;8;7"); # returns (3, 8, 7)
|
||||
round2(3.14159); # returns "3.14"
|
||||
round2(0); # returns "0.00"
|
||||
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The OwlPS::CSV Perl module provides functions to deal with the CSV format used
|
||||
in OwlPS. Upon error, all these functions will kill the program. This may change
|
||||
in future versions.
|
||||
|
||||
The following functions are provided and exported by default:
|
||||
|
||||
=over
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
package OwlPS::CSV;
|
||||
require Exporter;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw(
|
||||
get_field
|
||||
update_field
|
||||
split_timestamp
|
||||
split_point_2d
|
||||
split_point_3d
|
||||
round2
|
||||
);
|
||||
our $VERSION = 1.00;
|
||||
|
||||
use strict;
|
||||
use Carp;
|
||||
use Scalar::Util qw(looks_like_number);
|
||||
|
||||
|
||||
=item get_field($string, $field_idx)
|
||||
|
||||
This function returns the field number $field_idx of the CSV string $string.
|
||||
The first field of the string has number 0.
|
||||
=cut
|
||||
sub get_field($$) {
|
||||
my ($string, $field_idx) = @_;
|
||||
|
||||
# Split the line
|
||||
my @fields = split(';', $string);
|
||||
if (@fields <= $field_idx) {
|
||||
croak "Bad number of fields in CSV string \"$string\"";
|
||||
}
|
||||
|
||||
return $fields[$field_idx];
|
||||
}
|
||||
|
||||
|
||||
=item update_field($string, $field_idx, $value)
|
||||
|
||||
This function updates the field number $field_idx the CSV string $string with
|
||||
$value.
|
||||
It returns the modified string ($string is left untouched).
|
||||
The first field of the string has number 0.
|
||||
=cut
|
||||
sub update_field($$$) {
|
||||
my ($string, $field_idx, $value) = @_;
|
||||
|
||||
# Split the line
|
||||
my @fields = split(';', $string);
|
||||
if (@fields <= $field_idx) {
|
||||
croak "Bad number of fields in CSV string \"$string\"";
|
||||
}
|
||||
|
||||
# Update the corresponding field
|
||||
$fields[$field_idx] = $value;
|
||||
|
||||
# Reconstitute the line
|
||||
my $newstring = shift @fields;
|
||||
foreach my $field (@fields) { $newstring .= ";$field" }
|
||||
|
||||
return $newstring;
|
||||
}
|
||||
|
||||
|
||||
=item split_timestamp($timestamp)
|
||||
|
||||
This function splits a timestamp $timestamp which is a string
|
||||
"seconds.nanoseconds".
|
||||
It returns an array ($seconds, $nanoseconds).
|
||||
=cut
|
||||
sub split_timestamp($) {
|
||||
my $timestamp = $_[0];
|
||||
my ($sec, $nsec) = split('\.', $timestamp, 2);
|
||||
|
||||
foreach ($sec, $nsec) {
|
||||
if (!(defined && looks_like_number($_))) {
|
||||
croak "Malformed timestamp string \"$timestamp\"";
|
||||
}
|
||||
}
|
||||
|
||||
if (length($nsec) != 9) {
|
||||
croak "Malformed timestamp string \"$timestamp\": the nanosecond field"
|
||||
. " must have exactly 9 digits";
|
||||
}
|
||||
|
||||
return ($sec, $nsec);
|
||||
}
|
||||
|
||||
|
||||
=item split_point_2d($point)
|
||||
|
||||
This funtion splits the 2-D point $point (string format: "X;Y").
|
||||
It returns an array ($x, $y).
|
||||
=cut
|
||||
sub split_point_2d($) {
|
||||
my $point = $_[0];
|
||||
my ($x, $y) = split(';', $point, 2);
|
||||
|
||||
foreach ($x, $y) {
|
||||
if (!(defined && looks_like_number($_))) {
|
||||
croak "Malformed 2-D point string \"$point\"";
|
||||
}
|
||||
}
|
||||
|
||||
return ($x, $y);
|
||||
}
|
||||
|
||||
|
||||
=item split_point_3d($point)
|
||||
|
||||
This funtion splits the 3-D point $point (string format: "X;Y;Z").
|
||||
It returns an array ($x, $y, $z).
|
||||
=cut
|
||||
sub split_point_3d($) {
|
||||
my $point = $_[0];
|
||||
my ($x, $y, $z) = split(';', $point, 3);
|
||||
|
||||
foreach ($x, $y) {
|
||||
if (!(defined && looks_like_number($_))) {
|
||||
croak "Malformed 3-D point string \"$point\"";
|
||||
}
|
||||
}
|
||||
|
||||
return ($x, $y, $z);
|
||||
}
|
||||
|
||||
|
||||
=item round2($f)
|
||||
|
||||
This function rounds a floating point number $f to two decimals.
|
||||
It returns the rounded value of $f as a string.
|
||||
=cut
|
||||
sub round2($) {
|
||||
my $f = $_[0];
|
||||
|
||||
if (!(defined($f) && looks_like_number($f))) {
|
||||
croak "Not a valid number \"$f\"";
|
||||
}
|
||||
|
||||
return sprintf("%.2f", $f);
|
||||
}
|
||||
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYING
|
||||
|
||||
This module and its documentation are part of the Owl Positioning System (OwlPS)
|
||||
project. They are subject to the copyright notice and license terms in the
|
||||
COPYRIGHT.t2t file found in the top-level directory of the OwlPS distribution
|
||||
and at http://code.lm7.fr/p/owlps/source/tree/master/COPYRIGHT.t2t
|
||||
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
owlps(7)
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
# vim: tabstop=4:shiftwidth=4:expandtab:textwidth=80
|
|
@ -0,0 +1,334 @@
|
|||
# This file is part of the Owl Positioning System (OwlPS) project. It is
|
||||
# subject to the copyright notice and license terms in the COPYRIGHT.t2t file
|
||||
# found in the top-level directory of this distribution and at
|
||||
# http://code.lm7.fr/p/owlps/source/tree/master/COPYRIGHT.t2t
|
||||
# No part of the OwlPS Project, including this file, may be copied, modified,
|
||||
# propagated, or distributed except according to the terms contained in the
|
||||
# COPYRIGHT.t2t file; the COPYRIGHT.t2t file must be distributed along with this
|
||||
# file, either separately or by replacing this notice by the COPYRIGHT.t2t
|
||||
# file's contents.
|
||||
|
||||
|
||||
=head1 NAME
|
||||
|
||||
OwlPS::TimeInterpolation - interpolate timestamped coordinates
|
||||
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use OwlPS::TimeInterpolation;
|
||||
|
||||
interpolate_1d(1, "1368637546.331881794",
|
||||
10, "1368637879.566242145",
|
||||
"1368637726.677787231");
|
||||
interpolate_2d("1;2", "1368637546.331881794",
|
||||
"10;5", "1368637879.566242145",
|
||||
"1368637726.677787231");
|
||||
interpolate_3d("1;2;1", "1368637546.331881794",
|
||||
"10;5;2", "1368637879.566242145",
|
||||
"1368637726.677787231");
|
||||
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The OwlPS::TimeInterpolation Perl module offers function to interpolate
|
||||
timestamped coordinates. Its main functions are interpolate_1d(),
|
||||
interpolate_2d() and interpolate_3d() (cf. section L</"Main functions"> below).
|
||||
|
||||
=head2 Helper functions (not exported by default)
|
||||
|
||||
The following functions are provided but not exported by default.
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
package OwlPS::TimeInterpolation;
|
||||
require Exporter;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw(
|
||||
interpolate_1d
|
||||
interpolate_2d
|
||||
interpolate_3d
|
||||
);
|
||||
our @EXPORT_OK = qw(
|
||||
timestamp_cmp
|
||||
elapsed
|
||||
elapsed_sec
|
||||
travel_speed
|
||||
travel_speed_1d
|
||||
distance_travelled
|
||||
linear_interpolation
|
||||
);
|
||||
our $VERSION = 1.00;
|
||||
|
||||
use strict;
|
||||
use Carp;
|
||||
use OwlPS::CSV;
|
||||
use Scalar::Util qw(looks_like_number);
|
||||
|
||||
|
||||
=head3 Time-related functions
|
||||
|
||||
=over
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
=item timestamp_cmp($t0, $t1)
|
||||
|
||||
This function compares two timestamps $t0 and $t1, which are strings under the
|
||||
format "seconds.nanoseconds".
|
||||
It returns:
|
||||
|
||||
=over
|
||||
|
||||
=item * 1 if $t0 is before $t1
|
||||
|
||||
=item * -1 if $t0 is after $t1
|
||||
|
||||
=item * 0 if $t0 and $t1 are equal
|
||||
|
||||
=back
|
||||
=cut
|
||||
sub timestamp_cmp($$) {
|
||||
my ($t0, $t1) = @_;
|
||||
my ($t0_sec, $t0_nsec) = split_timestamp($t0);
|
||||
my ($t1_sec, $t1_nsec) = split_timestamp($t1);
|
||||
|
||||
# First test the seconds
|
||||
if ($t1_sec > $t0_sec) { return 1 }
|
||||
if ($t1_sec < $t0_sec) { return -1 }
|
||||
|
||||
# Seconds are equal, test the nanoseconds
|
||||
if ($t1_nsec > $t0_nsec) { return 1 }
|
||||
if ($t1_nsec < $t0_nsec) { return -1 }
|
||||
|
||||
# Seconds and nanoseconds are equal
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
=item elapsed($t0, $t1)
|
||||
|
||||
This function calculates the elapsed time between two timestamps $t0 and $t1
|
||||
(string format: "seconds.nanoseconds").
|
||||
It returns an array ($seconds, $nanoseconds).
|
||||
=cut
|
||||
sub elapsed($$) {
|
||||
my ($t0, $t1) = @_;
|
||||
my ($t0_sec, $t0_nsec) = split_timestamp($t0);
|
||||
my ($t1_sec, $t1_nsec) = split_timestamp($t1);
|
||||
|
||||
my $sec = $t1_sec - $t0_sec;
|
||||
my $nsec = $t1_nsec - $t0_nsec;
|
||||
|
||||
if ($sec == 0) { return (0, abs($nsec)) }
|
||||
|
||||
if ($sec > 0) {
|
||||
if ($nsec >= 0) { return ($sec, $nsec) }
|
||||
|
||||
# $nsec < 0
|
||||
return ($sec - 1, $nsec + 1000000000);
|
||||
}
|
||||
|
||||
# $sec < 0
|
||||
if ($nsec > 0) { return (abs($sec) - 1, 1000000000 - $nsec) }
|
||||
|
||||
# $nsec <= 0
|
||||
return (abs($sec), abs($nsec));
|
||||
}
|
||||
|
||||
|
||||
=item elapsed_sec($t0, $t1)
|
||||
|
||||
This function calculates the elapsed time between two timestamps $t0 and $t1, in
|
||||
seconds.
|
||||
It returns the number of seconds (floating point value).
|
||||
=cut
|
||||
sub elapsed_sec($$) {
|
||||
my ($t0, $t1) = @_;
|
||||
my ($t0t1_sec, $t0t1_nsec) = elapsed($t0, $t1);
|
||||
my $total_nsec = $t0t1_sec * 1000000000 + $t0t1_nsec;
|
||||
return $total_nsec / 1000000000;
|
||||
}
|
||||
|
||||
|
||||
=back
|
||||
|
||||
=head3 Distance and speed-related functions
|
||||
|
||||
=over
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
=item travel_speed($start_time, $stop_time, $distance)
|
||||
|
||||
This function returns the movement speed, given two timestamp $start_time and
|
||||
$stop_time and a travelled distance $distance.
|
||||
=cut
|
||||
sub travel_speed($$$) {
|
||||
my ($start_time, $stop_time, $distance) = @_;
|
||||
my $time_elapsed = elapsed_sec($start_time, $stop_time);
|
||||
return $distance / $time_elapsed;
|
||||
}
|
||||
|
||||
|
||||
=item travel_speed_1d($start_point, $start_time, $stop_point, $stop_time)
|
||||
|
||||
This function returns the movement speed between two coordinates (1-D points),
|
||||
given the start position $start_point at a timestamp $start_time, and the stop
|
||||
position $stop_point at a timestamp $stop_time.
|
||||
=cut
|
||||
sub travel_speed_1d($$$$) {
|
||||
my ($start_point, $start_time, $stop_point, $stop_time) = @_;
|
||||
if (!looks_like_number($start_point) || !looks_like_number($stop_point)) {
|
||||
croak "1-D coordinates must be numbers";
|
||||
}
|
||||
my $distance_travelled = abs($stop_point - $start_point);
|
||||
return travel_speed($start_time, $stop_time, $distance_travelled);
|
||||
}
|
||||
|
||||
|
||||
=item distance_travelled($start_time, $stop_time, $speed)
|
||||
|
||||
This function returns the distance travelled between two timestamps $start_time
|
||||
and $stop_time, given the movement speed $speed.
|
||||
=cut
|
||||
sub distance_travelled($$$) {
|
||||
my ($start_time, $stop_time, $speed) = @_;
|
||||
my $time_travelled = elapsed_sec($start_time, $stop_time);
|
||||
return $time_travelled * $speed;
|
||||
}
|
||||
|
||||
|
||||
=back
|
||||
|
||||
=head3 Interpolation-related functions
|
||||
|
||||
=over
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
=item linear_interpolation($start_point, $stop_point, $distance)
|
||||
|
||||
Given two 1-D coordinates $start_point and $stop_point, this function computes
|
||||
the interpolated coordinate at $distance from $start_point in the direction of
|
||||
$stop_point.
|
||||
It returns the interpolated 1-D coordinate.
|
||||
=cut
|
||||
sub linear_interpolation($$$) {
|
||||
my ($start_point, $stop_point, $distance) = @_;
|
||||
if (!looks_like_number($start_point) || !looks_like_number($stop_point)) {
|
||||
croak "1-D coordinates must be numbers";
|
||||
}
|
||||
if ($start_point <= $stop_point) {
|
||||
return $start_point + $distance;
|
||||
}
|
||||
return $start_point - $distance;
|
||||
}
|
||||
|
||||
|
||||
=back
|
||||
|
||||
=head2 Main functions
|
||||
|
||||
The following functions are exported by default.
|
||||
|
||||
=over
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
=item interpolate_1d($start_point, $start_time, $stop_point, $stop_time, $time)
|
||||
|
||||
Given two 1-D coordinates $start_point and $stop_point with their associated
|
||||
timestamps $start_time and $stop_time, and an additional timestamp $time, this
|
||||
function computes the coordinates associated with this additional timestamp. A
|
||||
constant pace is assumed between the two timestamped coordinates. $time should
|
||||
be after the $start_time, and can also be after $stop_time.
|
||||
The interpolated coordinate is returned.
|
||||
=cut
|
||||
sub interpolate_1d($$$$$) {
|
||||
my ($start_point, $start_time, $stop_point, $stop_time, $time) = @_;
|
||||
|
||||
# Check the timestamps
|
||||
if (timestamp_cmp($start_time, $stop_time) <= 0) {
|
||||
croak "\$start_time must be before \$stop_time";
|
||||
}
|
||||
if (timestamp_cmp($start_time, $time) < 0) {
|
||||
croak "\$start_time must be equal or before \$time";
|
||||
}
|
||||
|
||||
# Compute the travel speed between the two known points
|
||||
my $speed =
|
||||
travel_speed_1d($start_point, $start_time, $stop_point, $stop_time);
|
||||
|
||||
# Compute the distance travelled between `start_point` and the point
|
||||
# to interpolate
|
||||
my $distance = distance_travelled($start_time, $time, $speed);
|
||||
|
||||
# Compute and return the interpolated coordinates
|
||||
return linear_interpolation($start_point, $stop_point, $distance);
|
||||
}
|
||||
|
||||
|
||||
=item interpolate_2d($start_point, $start_time, $stop_point, $stop_time, $time)
|
||||
|
||||
This function is similar to interpolate_1d(), but works with 2-D coordinates:
|
||||
$start_point and $stop_point must be strings "X;Y".
|
||||
It returns an array ($x, $y) representing the interpolated point.
|
||||
=cut
|
||||
sub interpolate_2d($$$$$) {
|
||||
my ($start_point, $start_time, $stop_point, $stop_time, $time) = @_;
|
||||
|
||||
my ($start_x, $start_y) = split_point_2d($start_point);
|
||||
my ($stop_x, $stop_y) = split_point_2d($stop_point);
|
||||
|
||||
return (
|
||||
interpolate_1d($start_x, $start_time, $stop_x, $stop_time, $time),
|
||||
interpolate_1d($start_y, $start_time, $stop_y, $stop_time, $time)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
=item interpolate_3d($start_point, $start_time, $stop_point, $stop_time, $time)
|
||||
|
||||
This function is similar to interpolate_1d() and interpolate_2d(), but works
|
||||
with 3-D coordinates: $start_point and $stop_point must be strings "X;Y;Z".
|
||||
It returns an array ($x, $y, $z) representing the interpolated point.
|
||||
=cut
|
||||
sub interpolate_3d($$$$$) {
|
||||
my ($start_point, $start_time, $stop_point, $stop_time, $time) = @_;
|
||||
|
||||
my ($start_x, $start_y, $start_z) = split_point_3d($start_point);
|
||||
my ($stop_x, $stop_y, $stop_z) = split_point_3d($stop_point);
|
||||
|
||||
return (
|
||||
interpolate_1d($start_x, $start_time, $stop_x, $stop_time, $time),
|
||||
interpolate_1d($start_y, $start_time, $stop_y, $stop_time, $time),
|
||||
interpolate_1d($start_z, $start_time, $stop_z, $stop_time, $time)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYING
|
||||
|
||||
This module and its documentation are part of the Owl Positioning System (OwlPS)
|
||||
project. They are subject to the copyright notice and license terms in the
|
||||
COPYRIGHT.t2t file found in the top-level directory of the OwlPS distribution
|
||||
and at http://code.lm7.fr/p/owlps/source/tree/master/COPYRIGHT.t2t
|
||||
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
owlps(7)
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
# vim: tabstop=4:shiftwidth=4:expandtab:textwidth=80
|
Loading…
Reference in New Issue