/* compute boat speed from given power level, true wind angle and true wind speed */ double BoatPlan::Speed(double W, double VW) { W = positive_degrees(W); if(VW < 0 || VW > MAX_KNOTS) return NAN; double W1 = floor(W/DEGREE_STEP)*DEGREE_STEP; double W2 = ceil(W/DEGREE_STEP)*DEGREE_STEP; /* get headings nearby each other */ while(W1 - W2 > 180) W2 += 360; while(W2 - W1 > 180) W1 += 360; int W1i = positive_degrees(W1)/DEGREE_STEP; int W2i = positive_degrees(W2)/DEGREE_STEP; int VW1i = ClosestVWi(VW), VW2i = VW1i+1; double VW1 = wind_speeds[VW1i], VW2 = wind_speeds[VW2i]; double VB11 = speed[VW1i][W1i].VB; double VB12 = speed[VW1i][W2i].VB; double VB21 = speed[VW2i][W1i].VB; double VB22 = speed[VW2i][W2i].VB; double VB1 = interp_value(VW, VW1, VW2, VB11, VB21); double VB2 = interp_value(VW, VW1, VW2, VB12, VB22); double VB = interp_value(W, W1, W2, VB1, VB2); return VB; }
SailingVMG BoatPlan::GetVMGTrueWind(double VW) { int VW1i, VW2i; ClosestVWi(VW, VW1i, VW2i); SailingWindSpeed &ws1 = wind_speeds[VW1i], &ws2 = wind_speeds[VW2i]; double VW1 = ws1.VW, VW2 = ws2.VW; SailingVMG vmg, vmg1 = ws1.VMG, vmg2 = ws2.VMG; for(int i=0; i<4; i++) vmg.values[i] = interp_value(VW, VW1, VW2, vmg1.values[i], vmg2.values[i]); return vmg; }
/* compute boat speed from true wind angle and true wind speed */ double Polar::Speed(double W, double VW, bool bound) { if(VW < 0) return NAN; if(!degree_steps.size() || !wind_speeds.size()) return NAN; W = positive_degrees(W); // assume symmetric if(W > 180) W = 360 - W; if(W < degree_steps[0] || W > degree_steps[degree_steps.size()-1]) return NAN; if(bound) if(VW < wind_speeds[0].VW || VW > wind_speeds[wind_speeds.size()-1].VW) return NAN; unsigned int W1i = degree_step_index[(int)floor(W)]; unsigned int W2i = degree_steps.size() == 1 ? 0 : W1i+1; double W1 = degree_steps[W1i], W2 = degree_steps[W2i]; int VW1i, VW2i; ClosestVWi(VW, VW1i, VW2i); if(VW1i == (int)wind_speeds.size() - 1) VW2i = VW1i; else VW2i = VW1i + 1; SailingWindSpeed &ws1 = wind_speeds[VW1i], &ws2 = wind_speeds[VW2i]; double VW1 = ws1.VW, VW2 = ws2.VW; double VB11 = ws1.speeds[W1i], VB12 = ws1.speeds[W2i]; double VB21 = ws2.speeds[W1i], VB22 = ws2.speeds[W2i]; double VB1 = interp_value(VW, VW1, VW2, VB11, VB21); double VB2 = interp_value(VW, VW1, VW2, VB12, VB22); double VB = interp_value(W, W1, W2, VB1, VB2); if(VB < 0) // with faulty polars, extrapolation, sometimes results in negative boat speed return 0; return VB; }
SailingVMG BoatPlan::GetVMG(double VW) { int VW2i = ClosestVWi(VW), VW1i = VW2i > 0 ? VW2i - 1 : 0; SailingVMG vmg, vmg1 = VMG[VW1i], vmg2 = VMG[VW2i]; double VW1 = wind_speeds[VW1i], VW2 = wind_speeds[VW2i]; vmg.PortTackUpWind = DEGREE_STEP * interp_value (VW, VW1, VW2, vmg1.PortTackUpWind, vmg2.PortTackUpWind); vmg.StarboardTackUpWind = DEGREE_STEP * interp_value (VW, VW1, VW2, vmg1.StarboardTackUpWind, vmg2.StarboardTackUpWind); vmg.PortTackDownWind = DEGREE_STEP * interp_value (VW, VW1, VW2, vmg1.PortTackDownWind, vmg2.PortTackDownWind); vmg.StarboardTackDownWind = DEGREE_STEP * interp_value (VW, VW1, VW2, vmg1.StarboardTackDownWind, vmg2.StarboardTackDownWind); return vmg; }
/* compute boat speed from true wind angle and true wind speed Maybe this should use rectangular coordinate interpolation when the speed is outside the VMG zone. */ double BoatPlan::Speed(double W, double VW) { if(VW < 0) return NAN; W = positive_degrees(W); unsigned int W1i = degree_step_index[(int)floor(W)], W2i; double W1 = degree_steps[W1i], W2; if(W1i == degree_steps.size()-1) { W2i = 0; W2 = 360 + degree_steps[0]; } else { W2i = W1i+1; W2 = degree_steps[W2i]; } int VW1i, VW2i; ClosestVWi(VW, VW1i, VW2i); if(VW1i == wind_speeds.size() - 1) VW2i = VW1i; else VW2i = VW1i + 1; SailingWindSpeed &ws1 = wind_speeds[VW1i], &ws2 = wind_speeds[VW2i]; double VW1 = ws1.VW, VW2 = ws2.VW; double VB11 = ws1.speeds[W1i].VB, VB12 = ws1.speeds[W2i].VB; double VB21 = ws2.speeds[W1i].VB, VB22 = ws2.speeds[W2i].VB; double VB1 = interp_value(VW, VW1, VW2, VB11, VB21); double VB2 = interp_value(VW, VW1, VW2, VB12, VB22); double VB = interp_value(W, W1, W2, VB1, VB2); if(VB < 0) // with faulty polars, extrapolation, sometimes results in negative boat speed return 0; return VB; }
/* 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; }