From a3fcfd53123541f51949d3bd046a1e1362e2b957 Mon Sep 17 00:00:00 2001 From: "Sebastien Rosset (serosset)" Date: Mon, 1 Jul 2024 15:06:20 -0700 Subject: [PATCH 1/8] Add code comments --- include/Boat.h | 4 ++ include/RouteMap.h | 105 +++++++++++++++++++++++++++++++++++++-------- src/Polar.cpp | 13 +++++- src/RouteMap.cpp | 7 ++- 4 files changed, 110 insertions(+), 19 deletions(-) diff --git a/include/Boat.h b/include/Boat.h index 4b31c4d4..76470881 100644 --- a/include/Boat.h +++ b/include/Boat.h @@ -25,6 +25,10 @@ #include "Polar.h" +/* + * This class is responsible for loading and saving the polars of a given boat to disk. + * and for finding the fastest polar for a given wind and heading. +*/ class Boat { public: diff --git a/include/RouteMap.h b/include/RouteMap.h index 3a7836b5..80d65404 100644 --- a/include/RouteMap.h +++ b/include/RouteMap.h @@ -38,6 +38,7 @@ class IsoRoute; typedef std::list IsoRouteList; class PlotData; + class RoutePoint { public: RoutePoint(double latitude = 0., double longitude = 0., int sp = -1, int t=0, bool d = false) : @@ -53,11 +54,16 @@ class RoutePoint { bool grib_is_data_deficient; bool GetPlotData(RoutePoint *next, double dt, RouteMapConfiguration &configuration, PlotData &data); + // @brief Return the wind data at the route point. bool GetWindData(RouteMapConfiguration &configuration, double &W, double &VW, int &data_mask); + // @brief Return the current data at the route point. bool GetCurrentData(RouteMapConfiguration &configuration, double &C, double &VC, int &data_mask); + // @brief Return true if the route point crosses land. bool CrossesLand(double dlat, double dlon); + // @brief Return true if the route point enters a boundary. bool EntersBoundary(double dlat, double dlon); + // @brief Propagate the route point to a specific point. double PropagateToPoint(double dlat, double dlon, RouteMapConfiguration &cf, double &H, int &data_mask, bool end = true); }; @@ -66,8 +72,18 @@ class PlotData : public RoutePoint public: wxDateTime time; double delta; - double VBG, BG, VB, B, VW, W, VWG, WG, VC, C, WVHT; - double VW_GUST; + double VBG; // Velocity of Boat over ground, in knots. + double BG; // Boat direction over ground. + double VB; // Velocity of Boat over water, in knots. + double B; // Boat direction over water. + double VW; // Velocity of wind over water, in knots. + double W; // Wind direction over water. + double VWG; // Velocity of wind over ground, in knots. + double WG; // Wind direction over ground. + double VC; // Velocity of Current over ground, in knots. + double C; // Sea Current Direction over ground. + double WVHT; // Swell height, in meters. + double VW_GUST; // Gust wind speed, in knots. }; class SkipPosition; @@ -121,7 +137,7 @@ class SkipPosition int quadrant; }; -/* a closed loop of positions */ +/* a closed loop of isochrone positions */ class IsoRoute { public: @@ -305,50 +321,102 @@ struct RouteMapPosition { }; +/* Configuration parameters for a route between two points. */ struct RouteMapConfiguration { RouteMapConfiguration () : StartLon(0), EndLon(0), grib(nullptr), grib_is_data_deficient(false) {} /* avoid waiting forever in update longitudes */ bool Update(); wxString RouteGUID; /* Route GUID if any */ - wxString Start; + wxString Start; /* The name of starting position, which is resolved to StartLat/StartLon. */ wxString StartGUID; - wxString End; + wxString End; /* The name of the destination position, which is resolved to EndLat/EndLon. */ wxString EndGUID; - wxDateTime StartTime; + wxDateTime StartTime; /* The time when the boat leaves the starting position. */ double DeltaTime; /* default time in seconds between propagations */ double UsedDeltaTime; /* time in seconds between propagations */ - Boat boat; - wxString boatFileName; + Boat boat; /* The polars of the boat, used for the route calculation. */ + wxString boatFileName; /* The name of the boat XML file referencing polars. */ enum IntegratorType { NEWTON, RUNGE_KUTTA } Integrator; - double MaxDivertedCourse, MaxCourseAngle, MaxSearchAngle, MaxTrueWindKnots, MaxApparentWindKnots; - double MaxSwellMeters, MaxLatitude, TackingTime, WindVSCurrent; + // The maximum angle the boat can be diverted from the bearing to the destination, + // at each step of the route calculation, in degrees. + // This represents the angle away from Start to End bearing (StartEndBearing). + // The normal setting is 100 degrees, which speeds up calculations. + // If the route needs to go around land, islands or peninsulas, the user can increase the value. + // E.g. the boat may have to go in the opposite direction then back to the destination bearing. + double MaxDivertedCourse; + // The maximum deviation away from the direct route. + double MaxCourseAngle; + // The maximum angle at each step of the route calculation. + double MaxSearchAngle; + // The calculated route will avoid a path where the true wind is above this value in knots. + double MaxTrueWindKnots; + // The calculated route will avoid a path where the gust wind is above this value in knots. + double MaxApparentWindKnots; + + // The calculated route will avoid swells larger than this value in meters. + // If the grib data does not contain swell information, the maximum swell value is ignored. + // If there is no route within the maximum swell value, the route calculation will fail. + double MaxSwellMeters; + // The calculated route will not go beyond this latitude, as an absolute value. + // If the starting or destination position is beyond this latitude, the route calculation will fail. + double MaxLatitude; + // The penalty time to tack the boat, in seconds. + double TackingTime; + // Balance the influence of the wind and the ocean current on the route calculation. + double WindVSCurrent; + // The minimum safety distance to land, in nautical miles. + // The calculated route will avoid land within this distance. double SafetyMarginLand; bool AvoidCycloneTracks; - int CycloneMonths, CycloneDays; + // Avoid cyclone tracks within CycloneMonths months of climatology data. + int CycloneMonths; + // Avoid cyclone tracks within CycloneDays days of climatology data. + int CycloneDays; bool UseGrib; enum ClimatologyDataType {DISABLED, CURRENTS_ONLY, CUMULATIVE_MAP, CUMULATIVE_MINUS_CALMS, MOST_LIKELY, AVERAGE}; enum ClimatologyDataType ClimatologyType; bool AllowDataDeficient; - double WindStrength; // wind speed multiplier - - bool DetectLand, DetectBoundary, Currents, OptimizeTacking, InvertedRegions, Anchoring; + double WindStrength; // wind speed multiplier. 1.0 is 100% of the wind speed in the grib. + + // If true, the route calculation will avoid land, outside the SafetyMarginLand. + bool DetectLand; + // If true, the route calculation will avoid exclusion boundaries. + bool DetectBoundary; + // If true and grib data contains ocean currents, the route calculation will use ocean current data. + bool Currents; + // If true, avoid polar dead zones. + // If false, avoid upwind course (polar angle too low) or downwind no-go zone (polar angle too high). + bool OptimizeTacking; + bool InvertedRegions; + bool Anchoring; + + // Do not go below this minimum True Wind angle at each step of the route calculation. + // The default value is 0 degrees. + double FromDegree; + // Do not go above this maximum True Wind angle at each step of the route calculation. + // The default value is 180 degrees. + double ToDegree; + // The angular resolution at each step of the route calculation, in degrees. + // Lower values provide finer resolution but increase computation time. + // Higher values provide coarser resolution, but faster computation time. + double ByDegrees; - double FromDegree, ToDegree, ByDegrees; /* computed values */ std::list DegreeSteps; - double StartLat, StartLon, EndLat, EndLon; + double StartLat, StartLon; // The latitude and longitude of the starting position. + double EndLat, EndLon; // The latitude and longitude of the destination position. - double StartEndBearing; /* calculated from start and end */ + double StartEndBearing; /* The bearing calculated from start and end */ bool positive_longitudes; /* longitudes are either 0 to 360 or -180 to 180, this means the map cannot cross both 0 and 180 longitude. To fully support this requires a lot more logic and would probably slow the algorithm @@ -356,6 +424,9 @@ struct RouteMapConfiguration { // parameters WR_GribRecordSet *grib; + // The time of the current isochrone step in the route calculation. + // The time starts at StartTime and is incremented for each step until the destination is reached, + // or the route calculation fails. wxDateTime time; bool grib_is_data_deficient, polar_failed, wind_data_failed; bool land_crossing, boundary_crossing; diff --git a/src/Polar.cpp b/src/Polar.cpp index 0e4ed97f..18f94225 100644 --- a/src/Polar.cpp +++ b/src/Polar.cpp @@ -481,7 +481,17 @@ bool Polar::VMGAngle(SailingWindSpeed &ws1, SailingWindSpeed &ws2, float VW, flo return true; } -/* compute boat speed from true wind angle and true wind speed +/** + * Calculate the boat speed (VB) based on the wind angle (W) and wind speed (VW) using the polar data. + * + * @param W The wind angle in degrees. + * @param VW The wind speed in knots. + * @param bound If true, the wind speed must be within the bounds of the polar data. + * For example, if the wind speed is 25 knots and the polar data only goes up to 20 knots, + * the function will return NAN. + * If false, the wind speed is extrapolated if outside the bounds. + * @param optimize_tacking Flag indicating whether to optimize for tacking angles. + * @return The boat speed (VB) in knots. */ double Polar::Speed(double W, double VW, bool bound, bool optimize_tacking) { @@ -498,6 +508,7 @@ double Polar::Speed(double W, double VW, bool bound, bool optimize_tacking) W = 360 - W; if(!optimize_tacking && (W < degree_steps[0] || W > degree_steps[degree_steps.size()-1])) + // Avoid upwind course (polar angle too low) or downwind no-go zone (polar angle too high). return NAN; if(bound && (VW < wind_speeds[0].VW || VW > wind_speeds[wind_speeds.size()-1].VW)) diff --git a/src/RouteMap.cpp b/src/RouteMap.cpp index 63e646db..df94707a 100644 --- a/src/RouteMap.cpp +++ b/src/RouteMap.cpp @@ -117,6 +117,8 @@ static Json::Value RequestGRIB(const wxDateTime &t, const wxString &what, double return error; } +// Return the swell height at the specified lat/long location. +// @return the swell height in meters. 0 if no data is available. static double Swell(RouteMapConfiguration &configuration, double lat, double lon) { WR_GribRecordSet *grib = configuration.grib; @@ -144,6 +146,7 @@ static double Swell(RouteMapConfiguration &configuration, double lat, double lon return height; } +// Return the wind gust speed for the specified lat/long location, in knots. static double Gust(RouteMapConfiguration &configuration, double lat, double lon) { WR_GribRecordSet *grib = configuration.grib; @@ -171,6 +174,7 @@ static double Gust(RouteMapConfiguration &configuration, double lat, double lon) } +// Return the wind speed from the grib at the specified lat/long location, in knots. static bool GribWind(RouteMapConfiguration &configuration, double lat, double lon, double &WG, double &VWG) { @@ -752,6 +756,7 @@ bool Position::Propagate(IsoRouteList &routelist, RouteMapConfiguration &configu return false; if(configuration.WindVSCurrent) { + /* Calculate the wind vector (Wx, Wy) and ocean current vector (Cx, Cy). */ /* these are already computed in OverWater.. could optimize by reusing them */ double Wx = VW*cos(deg2rad(W)), Wy = VW*sin(deg2rad(W)); double Cx = VC*cos(deg2rad(C) + M_PI), Cy = VC*sin(deg2rad(C) + M_PI); @@ -929,7 +934,7 @@ bool Position::Propagate(IsoRouteList &routelist, RouteMapConfiguration &configu double latBorderDown1, lonBorderDown1, latBorderDown2, lonBorderDown2; // Test if land is found within a rectangle with - // dimensiosn (dist, distSecure). Tests borders, plus diag, + // dimensions (dist, distSecure). Tests borders, plus diag, // and middle of each side... // <- dist -> // |-------------------------------| From d63b9084a0dbdece925db6d9660ba8b98560d9bb Mon Sep 17 00:00:00 2001 From: "Sebastien Rosset (serosset)" Date: Mon, 1 Jul 2024 15:32:27 -0700 Subject: [PATCH 2/8] Add code comments --- include/RouteMap.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/RouteMap.h b/include/RouteMap.h index 80d65404..cf777354 100644 --- a/include/RouteMap.h +++ b/include/RouteMap.h @@ -416,7 +416,12 @@ struct RouteMapConfiguration { double StartLat, StartLon; // The latitude and longitude of the starting position. double EndLat, EndLon; // The latitude and longitude of the destination position. - double StartEndBearing; /* The bearing calculated from start and end */ + /* + * The initial bearing from Start position to End position, in degrees, + * taking into consideration the ellipsoidal shape of the Earth. + * A boat sailing the great circle route will not follow the initial bearing. + */ + double StartEndBearing; bool positive_longitudes; /* longitudes are either 0 to 360 or -180 to 180, this means the map cannot cross both 0 and 180 longitude. To fully support this requires a lot more logic and would probably slow the algorithm From 49c95194822da4fd5bc75a69a0a4ea3280050b01 Mon Sep 17 00:00:00 2001 From: "Sebastien Rosset (serosset)" Date: Mon, 1 Jul 2024 15:39:51 -0700 Subject: [PATCH 3/8] Add code comments --- include/RouteMap.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/include/RouteMap.h b/include/RouteMap.h index cf777354..ace4589c 100644 --- a/include/RouteMap.h +++ b/include/RouteMap.h @@ -67,21 +67,24 @@ class RoutePoint { double PropagateToPoint(double dlat, double dlon, RouteMapConfiguration &cf, double &H, int &data_mask, bool end = true); }; +/* + * A RoutePoint that has a time associated with it, along with navigation and weather data. +*/ class PlotData : public RoutePoint { public: - wxDateTime time; - double delta; - double VBG; // Velocity of Boat over ground, in knots. + wxDateTime time; // The time when the boat reaches this position, based on the route calculation. + double delta; // The time in seconds from the previous position to this position. + double VBG; // Velocity of boat over ground, in knots. double BG; // Boat direction over ground. - double VB; // Velocity of Boat over water, in knots. + double VB; // Velocity of boat over water, in knots. double B; // Boat direction over water. double VW; // Velocity of wind over water, in knots. double W; // Wind direction over water. double VWG; // Velocity of wind over ground, in knots. double WG; // Wind direction over ground. - double VC; // Velocity of Current over ground, in knots. - double C; // Sea Current Direction over ground. + double VC; // Velocity of current over ground, in knots. + double C; // Sea current direction over ground. double WVHT; // Swell height, in meters. double VW_GUST; // Gust wind speed, in knots. }; From d3d1978b40fffb1ccdecd8a04c70f7fe267b344e Mon Sep 17 00:00:00 2001 From: "Sebastien Rosset (serosset)" Date: Mon, 1 Jul 2024 15:57:55 -0700 Subject: [PATCH 4/8] Add code comments --- include/RouteMap.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/RouteMap.h b/include/RouteMap.h index ace4589c..fc9ce56d 100644 --- a/include/RouteMap.h +++ b/include/RouteMap.h @@ -140,7 +140,7 @@ class SkipPosition int quadrant; }; -/* a closed loop of isochrone positions */ +/* A closed loop of isochrone positions */ class IsoRoute { public: @@ -420,9 +420,9 @@ struct RouteMapConfiguration { double EndLat, EndLon; // The latitude and longitude of the destination position. /* - * The initial bearing from Start position to End position, in degrees, - * taking into consideration the ellipsoidal shape of the Earth. - * A boat sailing the great circle route will not follow the initial bearing. + * The initial bearing from Start position to End position, following the Great Circle + * route and taking into consideration the ellipsoidal shape of the Earth. + * Note: a boat sailing the great circle route will gradually change the bearing to the destination. */ double StartEndBearing; bool positive_longitudes; /* longitudes are either 0 to 360 or -180 to 180, From cbf33e7414d2c3023cdaf9708950dffebab8dbd4 Mon Sep 17 00:00:00 2001 From: "Sebastien Rosset (serosset)" Date: Mon, 1 Jul 2024 16:11:43 -0700 Subject: [PATCH 5/8] Add code comments --- include/RouteMap.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/RouteMap.h b/include/RouteMap.h index fc9ce56d..5103646b 100644 --- a/include/RouteMap.h +++ b/include/RouteMap.h @@ -371,6 +371,7 @@ struct RouteMapConfiguration { // If the starting or destination position is beyond this latitude, the route calculation will fail. double MaxLatitude; // The penalty time to tack the boat, in seconds. + // The penalty time is added to the route calculation for each tack. double TackingTime; // Balance the influence of the wind and the ocean current on the route calculation. double WindVSCurrent; From 4471f951a301aae25a80be2255f672bd9672ef10 Mon Sep 17 00:00:00 2001 From: "Sebastien Rosset (serosset)" Date: Mon, 1 Jul 2024 16:21:51 -0700 Subject: [PATCH 6/8] Add code comments --- include/RouteMap.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/RouteMap.h b/include/RouteMap.h index 5103646b..5d47ed25 100644 --- a/include/RouteMap.h +++ b/include/RouteMap.h @@ -380,9 +380,8 @@ struct RouteMapConfiguration { double SafetyMarginLand; bool AvoidCycloneTracks; - // Avoid cyclone tracks within CycloneMonths months of climatology data. + // Avoid cyclone tracks within ( 30*CycloneMonths + CycloneDays ) days of climatology data. int CycloneMonths; - // Avoid cyclone tracks within CycloneDays days of climatology data. int CycloneDays; bool UseGrib; From 133168830a6786e01def0fc08b9139c6bf025484 Mon Sep 17 00:00:00 2001 From: "Sebastien Rosset (serosset)" Date: Mon, 1 Jul 2024 16:28:12 -0700 Subject: [PATCH 7/8] Add code comments --- include/RouteMap.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/RouteMap.h b/include/RouteMap.h index 5d47ed25..80783037 100644 --- a/include/RouteMap.h +++ b/include/RouteMap.h @@ -411,6 +411,7 @@ struct RouteMapConfiguration { // The angular resolution at each step of the route calculation, in degrees. // Lower values provide finer resolution but increase computation time. // Higher values provide coarser resolution, but faster computation time. + // The allowed range of resolution is from 0.1 to 60 degrees. double ByDegrees; From ba8e931cf32d45948fbe96b180e08c3d986501a7 Mon Sep 17 00:00:00 2001 From: "Sebastien Rosset (serosset)" Date: Mon, 1 Jul 2024 16:51:15 -0700 Subject: [PATCH 8/8] Add code comments --- include/RouteMap.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/RouteMap.h b/include/RouteMap.h index 80783037..ccb18e00 100644 --- a/include/RouteMap.h +++ b/include/RouteMap.h @@ -412,6 +412,7 @@ struct RouteMapConfiguration { // Lower values provide finer resolution but increase computation time. // Higher values provide coarser resolution, but faster computation time. // The allowed range of resolution is from 0.1 to 60 degrees. + // The default value is 5 degrees. double ByDegrees;