From 47152001058a49a8f680a0991397ca9807e09e69 Mon Sep 17 00:00:00 2001 From: Sean D'Epagnier Date: Sun, 25 Mar 2018 22:05:43 -0400 Subject: [PATCH] support sparse polars with automatic interpolation --- VERSION.cmake | 6 +- WeatherRouting.fbp | 97 +++++++++++++-- src/BoatDialog.cpp | 26 +++-- src/EditPolarDialog.cpp | 17 ++- src/Polar.cpp | 246 ++++++++++++++++++++++++++++----------- src/Polar.h | 3 + src/RouteMap.cpp | 3 - src/WeatherRoutingUI.cpp | 4 + src/WeatherRoutingUI.h | 1 + 9 files changed, 312 insertions(+), 91 deletions(-) diff --git a/VERSION.cmake b/VERSION.cmake index 682ea020..53c1f35b 100644 --- a/VERSION.cmake +++ b/VERSION.cmake @@ -1,4 +1,4 @@ SET(PLUGIN_VERSION_MAJOR "1") -SET(PLUGIN_VERSION_MINOR "11") -SET(PLUGIN_VERSION_PATCH "2") -SET(PLUGIN_VERSION_DATE "2018-03-13") +SET(PLUGIN_VERSION_MINOR "12") +SET(PLUGIN_VERSION_PATCH "0") +SET(PLUGIN_VERSION_DATE "2018-03-25") diff --git a/WeatherRouting.fbp b/WeatherRouting.fbp index f979c248..076a7ee5 100644 --- a/WeatherRouting.fbp +++ b/WeatherRouting.fbp @@ -25032,7 +25032,7 @@ - + 0 wxAUI_MGR_DEFAULT @@ -25094,7 +25094,7 @@ - + 1 wxBOTH @@ -25106,11 +25106,11 @@ none 0 0 - + 5 wxEXPAND | wxALL 1 - + 1 1 1 @@ -25188,11 +25188,11 @@ - + Grid 1 - + 1 1 1 @@ -25266,7 +25266,7 @@ - + 1 wxBOTH 0 @@ -25421,6 +25421,89 @@ + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Leave any cell blank to automatically interpolate from nearby values. Use a value of 0.0 to specify invalid (cannot be used) View the polar plot in the boat dialog while editing the polar. + + 0 + + + 0 + + 1 + m_staticText1351 + 1 + + + protected + 1 + + Resizable + 1 + + + ; forward_declare + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/BoatDialog.cpp b/src/BoatDialog.cpp index f9025acc..76aab7c2 100644 --- a/src/BoatDialog.cpp +++ b/src/BoatDialog.cpp @@ -341,6 +341,11 @@ void BoatDialog::OnPaintPlot(wxPaintEvent& event) break; } + if(isnan(VB)) { + lastvalid = false; + continue; + } + double a; switch(selection) { @@ -389,6 +394,11 @@ void BoatDialog::OnPaintPlot(wxPaintEvent& event) break; } + if(isnan(VB)) { + lastvalid = false; + continue; + } + #if 0 double a; @@ -514,9 +524,8 @@ void BoatDialog::OnPaintCrossOverChart(wxPaintEvent& event) bool full = m_cbFullPlot->GetValue(); double scale; int xc = full ? w / 2 : 0; - if(polar) { + if(polar) scale = wxMin(full ? w/2 : w, h/2) / 40.0; - } for(double VW = 0; VW < 40; VW += 10) { if(polar) { @@ -861,6 +870,7 @@ void BoatDialog::OnAddPolar( wxCommandEvent& event ) for(unsigned int i=0; iSetItemState(m_Boat.Polars.size()-1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); generate = true; - } else { - wxMessageDialog md(this, message, - _("OpenCPN Weather Routing Plugin"), - wxICON_ERROR | wxOK ); + } + + if(!message.IsEmpty()) { + wxMessageDialog md(this, message, _("OpenCPN Weather Routing Plugin"), + success ? wxICON_WARNING : wxICON_ERROR | wxOK ); md.ShowModal(); } skip:; diff --git a/src/EditPolarDialog.cpp b/src/EditPolarDialog.cpp index 4947205f..cfee4d89 100644 --- a/src/EditPolarDialog.cpp +++ b/src/EditPolarDialog.cpp @@ -62,9 +62,14 @@ void EditPolarDialog::SetPolarIndex(int i) void EditPolarDialog::OnPolarGridChanged( wxGridEvent& event ) { + wxString str = m_gPolar->GetCellValue(event.GetRow(), event.GetCol()); + if(str == "0") + str = ""; double VB; - m_gPolar->GetCellValue(event.GetRow(), event.GetCol()).ToDouble(&VB); - GetPolar()->wind_speeds[event.GetCol()].speeds[event.GetRow()] = VB; + if(!str.ToDouble(&VB)) + VB = NAN; + GetPolar()->wind_speeds[event.GetCol()].orig_speeds[event.GetRow()] = VB; + GetPolar()->UpdateSpeeds(); m_BoatDialog->Refresh(); } @@ -205,9 +210,11 @@ void EditPolarDialog::RebuildGrid() m_gPolar->SetColLabelValue ( i, wxString::Format(_T("%4.1f"), GetPolar()->wind_speeds[i].VW)); - for(unsigned int j = 0; jdegree_steps.size(); j++) - m_gPolar->SetCellValue - (j, i, wxString::Format(_T("%4.1f"), GetPolar()->wind_speeds[i].speeds[j])); + for(unsigned int j = 0; jdegree_steps.size(); j++) { + double v = GetPolar()->wind_speeds[i].orig_speeds[j]; + wxString str = isnan(v) ? "" : v==0 ? "0.0" : wxString::Format(_T("%4.1f"), v); + m_gPolar->SetCellValue(j, i, str); + } } m_BoatDialog->Refresh(); diff --git a/src/Polar.cpp b/src/Polar.cpp index a0fa5ca0..656618b5 100644 --- a/src/Polar.cpp +++ b/src/Polar.cpp @@ -24,6 +24,7 @@ */ #include +#include #include #include @@ -99,6 +100,8 @@ double interp_value(double x, double x1, double x2, double y1, double y2) y = m*(x-x1)+y1, m = (y2 - y1)/(x2 - x1) y = (y2 - y1)*(x - x1)/(x2 - x1) + y1 */ + if(x == x1) return y1; // handle partial nan edge case + if(x == x2) return y2; return x2 - x1 ? (y2 - y1)*(x - x1)/(x2 - x1) + y1 : y1; } @@ -179,11 +182,33 @@ Polar::Polar() m_crossoverpercentage = 0; } +static char *strtok_polar(const char *line, char **saveptr) +{ + const char delim[] = " ;,\t\r\n"; + if(line) + *saveptr = (char*)line; + + char *start = *saveptr, *c = start; + while(*c == ' ') c++; // chomp spaces + while(*c) { + for(unsigned int i=0; i<(sizeof delim) / (sizeof *delim); i++) + if(*c == delim[i]) { + if(*c == '\r' || *c == '\n') + c[1] = 0; + *c = 0; + *saveptr = c+1; + return start; + } + c++; + } + return NULL; +} + #define MAX_WINDSPEEDS_IN_TABLE 200 -#define MESSAGE(S) (S + wxString(_T("\n")) + wxString::FromUTF8(filename) \ - + (linenum > 0 ? (_(" line ") + wxString::Format(_T("%d"), linenum)) : _T(""))) -#define PARSE_WARNING(S) do { if(message.empty()) message = MESSAGE(S); } while (0) -#define PARSE_ERROR(S) do { message = _("Boat polar failed") + wxString(_T("\n")) \ +#define MESSAGE(S) (wxString::FromUTF8(wxFileName(filename).GetFullName()) \ + + (linenum > 0 ? (_(" line ") + wxString::Format("%d", linenum)) : "") + ": " + S + "\n") +#define PARSE_WARNING(S) do { if(message.size() < 512) message += MESSAGE(S); } while (0) +#define PARSE_ERROR(S) do { message = _("Boat polar failed") + wxString("\n") \ + MESSAGE(S); goto failed; } while (0) bool Polar::Open(const wxString &filename, wxString &message) { @@ -198,7 +223,8 @@ bool Polar::Open(const wxString &filename, wxString &message) char line[1024]; double lastentryW = -1; char *token, *saveptr; - const char delim[] = ";, \t\r\n"; + double lastspeed = -1; + bool warn_zeros = false; if(!f) PARSE_ERROR(_("Failed to open.")); @@ -207,11 +233,12 @@ bool Polar::Open(const wxString &filename, wxString &message) for(;;) { if(!zu_gets(f, line, sizeof line)) PARSE_ERROR(_("Failed to read.")); - - token = strtok_r(line, delim, &saveptr); - assert(token != 0); linenum++; + token = strtok_polar(line, &saveptr); + if(!token) + PARSE_ERROR(_("Invalid data.")); + /* chomp invisible bytes */ while(*token < 0) token++; @@ -223,24 +250,22 @@ bool Polar::Open(const wxString &filename, wxString &message) if(linenum == 2) PARSE_ERROR(_("Unrecognized format.")); } - - while((token = strtok_r(NULL, delim, &saveptr))) { - wind_speeds.push_back(SailingWindSpeed(strtod(token, 0))); + + while((token = strtok_polar(NULL, &saveptr))) { + double speed = strtod(token, 0); + if(speed > lastspeed) + wind_speeds.push_back(SailingWindSpeed(speed)); + else + PARSE_ERROR(_("Invalid wind speeds. Wind speeds must be increasing.")); if(wind_speeds.size() > MAX_WINDSPEEDS_IN_TABLE) PARSE_ERROR(_("Too many wind speeds.")); + lastspeed = speed; } while(zu_gets(f, line, sizeof line)) { linenum++; -#if 0 - /* strip newline/linefeed */ - for(unsigned int i=0; i 0) { - std::vector speeds; - speeds.push_back(SailingWindSpeed(0)); - for(unsigned int i=0; i= 0; Win--) { - if(degree_steps[Win] == 0) - break; - - degree_steps.push_back(DEGREES - degree_steps[Win]); - - for(unsigned int VWi = 0; VWi < wind_speeds.size(); VWi++) - wind_speeds[VWi].speeds.push_back(wind_speeds[VWi].speeds[Win]); - } - } -#endif - - UpdateDegreeStepLookup(); - - for(unsigned int VWi = 0; VWi < wind_speeds.size(); VWi++) - CalculateVMG(VWi); + UpdateSpeeds(); FileName = wxString::FromUTF8(filename); return true; @@ -357,7 +362,12 @@ bool Polar::Save(const wxString &filename) break; fprintf(f, "%.5g", degree_steps[Wi]); for(unsigned int VWi = vwi0; VWi=0; i0--) + if(!isnan(wind_speeds[i0].speeds[j])) + for(unsigned int i1=i+1; i1=0; j0--) + if(!isnan(wind_speeds[i].speeds[j0])) + for(unsigned int j1=j+1; j1 A1 || c == 24) + wind_speeds[i].speeds[j] = interp_value(W, W0, W1, VB0, VB1); + else + wind_speeds[i].speeds[j] = VB; + + ret = true; + goto next; + } + next:; + } + } + return ret; +} + +void Polar::UpdateSpeeds() +{ + // interpolate wind speeds + for(unsigned int i=0; i 0) { + std::vector speeds; + speeds.push_back(SailingWindSpeed(0)); + for(unsigned int i=0; i VB2) + if(VB1 > VB2) maxW = (W1 + maxW) / 2; - else + if(VB1 < VB2) maxW = (W2 + maxW) / 2; step /= 2; @@ -945,8 +1055,9 @@ void Polar::AddDegreeStep(double twa) degree_steps.insert(degree_steps.begin()+Wi, twa); for(unsigned int VWi = 0; VWi speeds; // by degree_count + std::vector orig_speeds; // by degree_count, from polar file SailingVMG VMG; }; // num_wind_speeds bool VMGAngle(SailingWindSpeed &ws1, SailingWindSpeed &ws2, float VW, float &W); diff --git a/src/RouteMap.cpp b/src/RouteMap.cpp index c31f51a0..9a021a46 100644 --- a/src/RouteMap.cpp +++ b/src/RouteMap.cpp @@ -584,9 +584,6 @@ static inline bool ComputeBoatSpeed /* compound boatspeed with current */ OverGround(B, VB, C, VC, BG, VBG); - if(!VBG) // no speed - return false; - /* distance over ground */ dist = VBG * timeseconds / 3600.0; return true; diff --git a/src/WeatherRoutingUI.cpp b/src/WeatherRoutingUI.cpp index 194112c5..b6bd4a38 100644 --- a/src/WeatherRoutingUI.cpp +++ b/src/WeatherRoutingUI.cpp @@ -2809,6 +2809,10 @@ EditPolarDialogBase::EditPolarDialogBase( wxWindow* parent, wxWindowID id, const fgSizer93->Add( m_gPolar, 0, wxALL|wxEXPAND, 5 ); + m_staticText1351 = new wxStaticText( m_panel19, wxID_ANY, _("Leave any cell blank to automatically interpolate from nearby values.\n View the polar plot in the boat dialog while editing the polar."), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText1351->Wrap( -1 ); + fgSizer93->Add( m_staticText1351, 0, wxALL, 5 ); + m_panel19->SetSizer( fgSizer93 ); m_panel19->Layout(); diff --git a/src/WeatherRoutingUI.h b/src/WeatherRoutingUI.h index 67d0da6a..d380b4a1 100644 --- a/src/WeatherRoutingUI.h +++ b/src/WeatherRoutingUI.h @@ -696,6 +696,7 @@ class EditPolarDialogBase : public wxDialog wxNotebook* m_notebook6; wxPanel* m_panel19; wxGrid* m_gPolar; + wxStaticText* m_staticText1351; wxPanel* m_panel20; wxTextCtrl* m_tTrueWindAngle; wxListBox* m_lTrueWindAngles;