zef:librasteve

This module is an abstraction on Physics::Measure that provides Latitude, Longitude, Bearing, Position, Course and Buoy classes.

Provides Measure objects that have value, units and error and can be used in many common physics calculations. Uses Physics::Unit and Physics::Error.

Instructions

`zef --verbose install Physics::Navigation`

and, conversely, `zef uninstall Physics::Navigation`

For a gentler introduction to and explanation of these features, please refer to raku Yacht Navigation for a descriptive Jupyter notebook

Synopsis

```use lib '../lib';
use Physics::Measure;

# objects can be set up longhand as in Physics::Measure, now joined by Latitude, Longitude, etc.
\$Physics::Measure::round-val = 0.1;

my Distance \$d1   .=new( value => 42,  units => 'nmile' );	  say ~\$d1;
my Time     \$t1   .=new( value => 1.5, units => 'hr' );		  say ~\$t1;
my Latitude \$lat1 .=new( value => 45, compass => <N> );		  say ~\$lat1;

# the emoji ♓️ <pisces> operator shorthand does all that the Physics::Measure emoji ♎️ <libra> operator does
my \$d2 = ♓️'42 nmile';						  say ~\$d2;
my \$t2 = ♓️'1.5 hr';						  say ~\$t2;
my \$s2 = \$d2 / \$t2;						  say ~\$s2.in('knots');

# and then some - parses final NEWS letter to determine lat or long
my \$lat2 = ♓️<43°30′30″S>;				          say ~\$lat2;
my \$long1 = ♓️<45°W>;						  say ~\$long1;
my \$long2 = ♓️<22°E>;						  say ~\$long2;

# the .in conversion method works and the identity 1° lat === 1 nautical mile is preserved
my \$lat3 = in-lat( \$d1 );					  say ~\$lat3;
my \$d3 = \$lat2.in('nmiles');			                  say ~\$d3;

# you can do "Measure math" using the standard operators <[+-*/**]>
my \$d4 = \$d3 * 2;						  say ~\$d4;
my \$lat4 = \$lat2 - \$lat1;                                         say ~\$lat4;

# Bearings can be True(T) or Magnetic(M), with Variation and Deviation [Vw|Ve|Dw|De]

my \$bear1 = ♓️<80°T>;                                             say ~\$bear1;
my \$bear2 = ♓️<43°30′30″M>;                                       say ~\$bear2;
my \$bear3 = \$bear2 + \$bear1.M;                                    say ~\$bear3;

# here's how to steer your boat to Port (Pt) or Starboard (Sb)
my \$steer = ♓️<50Pt°>;                                            say ~\$steer;
my \$bear4 = \$bear2 + \$steer;                                      say ~\$bear4;
my \$bear5 = \$bear4.back;                                          say ~\$bear5; #and take a back bearing

try {
my \$bear-nope = \$bear2 + \$bear1;
}
if \$! { say "Can't mix True & Magnetic ... \$!" }

my Position \$start  .=new( \$lat1, \$long1 );	                  say ~\$start;
my Position \$finish .=new( \$lat2, \$long2 );	                  say ~\$finish;

# get great circle distance and initial azimuth bearing
say ~\$start.haversine-dist(\$finish).in('km');
say ~\$start.forward-azimuth(\$finish);

# make a Vector (angle + distance) and move by that amount
my \$vector  = \$start.diff(\$finish);			         say ~\$vector;
my \$finish2 = \$start.move(\$vector);			         say ~\$finish2;

# use Measure math to make Velocity objects
my \$dur     = ♓️'3 weeks';			                 say ~\$dur;
my \$vel     = \$vector.divide: \$dur;			         say ~\$vel;
my \$vector2 = \$vel.multiply: \$dur;		                 say ~\$vector2;

my \$pos-A = Position.new( ♓️<51.5072°N>, ♓️<0.1276°W> );
my \$pos-B = Position.new( ♓️<51.5072°N>, ♓️<0.1110°W> );
my \$pos-C = Position.new( ♓️<51.5072°N>, ♓️<0.1100°W> );

# there are Fixes, Estimated positions and Transits
my \$fix-A = Fix.new( direction => ♓️<112°T>, location  => \$pos-A );
my \$fix-B = Fix.new( direction => ♓️<25°T>,  location  => \$pos-B );

my \$ep = Estimate.new( :\$fix-A, :\$fix-B );                      say ~\$ep;

my \$tr = Transit.new( :\$pos-A, :\$pos-B );                       say \$tr.aligned( \$pos-C );

# Course objects handle Course To Steer, Tidal Flow, Course Over Ground (and leeway)
%course-info<leeway> =  ♓️<112°Pt>;
my \$tidal-flow = Velocity.new( θ => ♓️<112°T>, s => ♓️'2.2 knots' );
my Course \$course .= new( over-ground => ♓️<22°T>, :\$tidal-flow );  say ~\$course;
say \$course.speed-over-ground.in('knots');

# Buoy objects know their shape, colour, light pattern, etc.
my \$scm = SouthCardinal.new( position => \$pos-A );               say ~\$scm;
my \$ncm = NorthCardinal.new( position => \$pos-A );               say ~\$ncm;

# Lateral Buoys know about IALA standards
\$IALA = A;
my \$plm = PortLateral.new( position => \$pos-B );                 say ~\$plm;

# use .light-defn to get an English description
say \$plm.light-defn;

# .duration and .patterm methods also support SVG animation
say "SVG-animation: duration is {\$plm.light-svg.duration}s, pattern is: [{\$plm.light-svg.pattern}];";
```

Summary

The family of Physics::Navigation, Physics::Measure, Physics::Unit, Physics::Error and Physics::Constants raku modules is a consistent and extensible toolkit intended for science and education. It provides a comprehensive library of both metric (SI) and non-metric units, it is built on a Type Object foundation, it has a unit expression Grammar and implements math, conversion and comparison methods.

Contribution

You are welcome to contribute in any way - please open a pull request.

Any feedback is welcome to librasteve / via the github Issues above.

Copyright (c) Henley Cloud Consulting Ltd. 2021-2023