void Polar::RemoveDegreeStep(int index) { degree_steps.erase(degree_steps.begin()+index); for(unsigned int VWi = 0; VWi<wind_speeds.size(); VWi++) wind_speeds[VWi].speeds.erase(wind_speeds[VWi].speeds.begin()+index); UpdateDegreeStepLookup(); }
void Polar::AddDegreeStep(double twa) { unsigned int Wi; for(Wi = 0; Wi < degree_steps.size(); Wi++) if(twa < degree_steps[Wi]) break; degree_steps.insert(degree_steps.begin()+Wi, twa); for(unsigned int VWi = 0; VWi<wind_speeds.size(); VWi++) wind_speeds[VWi].speeds.insert(wind_speeds[VWi].speeds.begin()+Wi, 0); UpdateDegreeStepLookup(); }
/* eta is a measure of efficiency of the boat, from .01 for racing boats to .5 for heavy cruisers */ void BoatPlan::ComputeBoatSpeeds(Boat &boat, PolarMethod method, int speed) { // fileFileName = _T(""); if(/*polarmethod == FROM_FILE ||*/ wind_speeds.size() != num_computed_wind_speeds || degree_steps.size() != computed_degree_count) { wind_speeds.clear(); degree_steps.clear(); for(unsigned int Wi = 0; Wi < computed_degree_count; Wi++) degree_steps.push_back(computed_degree_step*Wi); UpdateDegreeStepLookup(); for(unsigned int VWi = 0; VWi < num_computed_wind_speeds; VWi++) { wind_speeds.push_back(SailingWindSpeed(computed_wind_speeds[VWi])); wind_speeds[VWi].speeds.clear(); for(unsigned int Wi = 0; Wi < computed_degree_count; Wi++) wind_speeds[VWi].speeds.push_back(SailingWindSpeed::SailingSpeed(0, degree_steps[Wi])); } } // for IMF computation double SADR = boat.SailAreaDisplacementRatio(); double lwl_ft = boat.lwl_ft; double hull_speed = boat.HullSpeed(); int VW1i, VW2i; if(speed == -1) // all speeds VW1i = 0, VW2i = wind_speeds.size() - 1; else ClosestVWi(speed, VW1i, VW2i); for(int VWi = VW1i; VWi <= VW2i; VWi++) { for(unsigned int Wi = 0; Wi <= computed_degree_count/2; Wi++) { double VW = wind_speeds[VWi].VW; double W = Wi * computed_degree_step; double B, VB, A, VA; switch(method) { case TRANSFORM: BoatSteadyState(deg2rad(W), VW, B, VB, A, VA, boat); break; case IMF: { if(fabsf(W) < 30) VB = 0; else { double base_speed = 2.62 + .066*SADR + .051*lwl_ft; double b = 1 / sqrt(VW + 3); VB = base_speed*(sin(deg2rad(W)/2) + b*cos(deg2rad(W))) * sqrt(20*VW) / 8; if(VB > hull_speed) VB = hull_speed; } } default: printf("BoatPlan::ComputeBoatSpeeds called with invalid method: %d\n", method); return; } Set(Wi, VWi, VB, W); if(W != 0) // assume symmetric performance Set(computed_degree_count-Wi, VWi, VB, DEGREES-W); } CalculateVMG(VWi); } polarmethod = method; }
bool BoatPlan::Open(const char *filename, wxString &message) { wind_speeds.clear(); degree_steps.clear(); if(filename[0] == 0) return false; int linenum = 0; ZUFILE *f = zu_open(filename, "r"); char line[1024]; double lastentryW = -1; char *token, *saveptr; const char delim[] = ";, \t\r\n"; if(!f) PARSE_ERROR(_("Failed to open.")); // polar file has optional first line which is description for(;;) { if(!zu_gets(f, line, sizeof line)) PARSE_ERROR(_("Failed to read.")); token = strtok_r(line, delim, &saveptr); linenum++; /* chomp invisible bytes */ while(*token < 0) token++; if(!strcasecmp(token, "twa/tws") || !strcasecmp(token, "twa\\tws") || !strcasecmp(token, "twa")) break; if(linenum == 2) PARSE_ERROR(_("Unrecognized format.")); } while((token = strtok_r(NULL, delim, &saveptr))) { wind_speeds.push_back(SailingWindSpeed(strtod(token, 0))); if(wind_speeds.size() > MAX_WINDSPEEDS_IN_TABLE) PARSE_ERROR(_("Too many wind speeds.")); } wind_speed_step = (int)round(wind_speeds.back().VW / wind_speeds.size()); while(zu_gets(f, line, sizeof line)) { linenum++; #if 0 /* strip newline/linefeed */ for(unsigned int i=0; i<strlen(line); i++) if(line[i] == '\r' || line[i] == '\n') line[i] = '\0'; #endif if(!(token = strtok_r(line, delim, &saveptr))) break; double W = strtod(token, 0); if(W < 0 || W > 180) { PARSE_WARNING(_("Wind direction out of range.")); continue; } if(W <= lastentryW) { PARSE_WARNING(_("Wind direction out of order.")); continue; } // add zero speed for all wind speeds going against wind if not specified if(degree_steps.empty() && W > 0) { degree_steps.push_back(0); for(int VWi = 0; VWi < (int)wind_speeds.size(); VWi++) wind_speeds[VWi].speeds.push_back(SailingWindSpeed::SailingSpeed(0, 0)); } degree_steps.push_back(W); lastentryW = W; { for(int VWi = 0; VWi < (int)wind_speeds.size(); VWi++) { double s = 0; if((token = strtok_r(NULL, delim, &saveptr))) s = strtod(token, 0); else PARSE_WARNING(_("Too few tokens.")); wind_speeds[VWi].speeds.push_back (SailingWindSpeed::SailingSpeed(s, W)); } if(strtok_r(NULL, delim, &saveptr)) PARSE_WARNING(_("Too many tokens.")); } } zu_close(f); /* fill in port tack assuming symmetric */ { int Win = degree_steps.size()-1; if(degree_steps[Win] == 180) Win--; for(; Win >= 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]); } } UpdateDegreeStepLookup(); wind_degree_step = degree_steps.size() ? 360 / degree_steps.size() : 0; for(unsigned int VWi = 0; VWi < wind_speeds.size(); VWi++) CalculateVMG(VWi); fileFileName = wxString::FromUTF8(filename); polarmethod = FROM_FILE; return true; failed: wind_speeds.clear(); degree_steps.clear(); zu_close(f); return false; }