Beispiel #1
0
void BoatPlan::SetSpeedsFromTable(BoatSpeedTable &table)
{
    for(int VWi = 0; VWi < num_wind_speeds; VWi++)
        for(int Wi = 0; Wi <= DEGREE_COUNT/2; Wi++) {
            double VB = table.InterpolateSpeed(wind_speeds[VWi], Wi*DEGREE_STEP);
            Set(Wi, VWi, VB);
            if(Wi != 0)
                Set(DEGREE_COUNT-Wi, VWi, VB);
        }
    CalculateVMG();
    computed = false;
}
Beispiel #2
0
/* instead of traveling in the direction given, allow traveling at angles
   closer to +- 90.  Basically we are trying every two tack angles and combining
   to determine if alternating between these two courses is actually faster than
   the current strategy for the given course, and if so, use it.

   TODO: Make this this work for asymmetrical polars correctly
*/
void BoatPlan::OptimizeTackingSpeed()
{
    for(unsigned int VWi = 0; VWi < wind_speeds.size(); VWi++) {
        CalculateVMG(VWi);
        for(unsigned int Wi = 0; Wi < degree_steps.size(); Wi++) {
            double at = degree_steps[Wi];
            double bt, ct;
            
            if(at > 90 && at < 270) {
                bt = wind_speeds[VWi].VMG.values[SailingVMG::STARBOARD_DOWNWIND];
                ct = wind_speeds[VWi].VMG.values[SailingVMG::PORT_DOWNWIND];
            } else {
                bt = wind_speeds[VWi].VMG.values[SailingVMG::STARBOARD_UPWIND];
                ct = wind_speeds[VWi].VMG.values[SailingVMG::PORT_UPWIND];
            }

            if(isnan(bt) || isnan(ct))
                continue;
            
            SailingWindSpeed::SailingSpeed &a = wind_speeds[VWi].speeds[Wi];
            double b = Speed(bt, wind_speeds[VWi].VW);
            double c = Speed(ct, wind_speeds[VWi].VW);
            
            /* w is the weight between b and c tacks (.5 for equal time on each
               t is the improvement factor]
               bcVB * sin(a) = w*b.VB*sin(b) + (1-w)*c.VB*sin(c)
               bcVB * cos(a) = w*b.VB*cos(b) + (1-w)*c.VB*cos(c) */
            double ar = deg2rad(at);
            double br = deg2rad(bt);
            double cr = deg2rad(ct);
            double sa = sin(ar), ca = cos(ar);
            double sb = sin(br), cb = cos(br);
            double sc = sin(cr), cc = cos(cr);
            double X = ca*sc-sa*cc, Y = sa*cb-ca*sb;
            double d = (X*c + Y*b);
            double w = X*c / d;
            
            if(w > 0 && w < 1) {
                double Z = cb*sc-sb*cc;
                double bcVB = Z*b*c / d;
                if(bcVB > a.VB) {
                    a.VB = bcVB;
                    if(at > 180)
                        a.b = bt, a.c = ct, a.w = w;
                    else
                        a.b = ct, a.c = bt, a.w = (1-w);
                }
            }           
        }
    }
}
Beispiel #3
0
/* instead of traveling in the direction given, allow traveling at angles
   closer to +- 90.  Basically we are trying every two tack angles and combining
   to determine if alternating between these two courses is actually faster than
   the current strategy for the given course, and if so, use it.

   TODO: Make this this work for asymmetrical polars correctly
*/
void BoatPlan::OptimizeTackingSpeed()
{
    CalculateVMG();
    for(int VWi = 0; VWi < num_wind_speeds; VWi++) {
        for(int Wi = 0; Wi <= DEGREE_COUNT; Wi++) {
            int at = Wi, bt, ct;
            
            if(Wi >= DEGREE_COUNT*1/4 && Wi < DEGREE_COUNT*3/4) {
                bt = VMG[VWi].StarboardTackDownWind;
                ct = VMG[VWi].PortTackDownWind;
            } else {
                bt = VMG[VWi].StarboardTackUpWind;
                ct = VMG[VWi].PortTackUpWind;
            }
            
            SailingSpeed &a = speed[VWi][at];
            SailingSpeed &b = speed[VWi][bt];
            SailingSpeed &c = speed[VWi][ct];
            
            /* w is the weight between b and c tacks (.5 for equal time on each
               t is the improvement factor]
               bcVB * sin(a) = w*b.VB*sin(b) + (1-w)*c.VB*sin(c)
               bcVB * cos(a) = w*b.VB*cos(b) + (1-w)*c.VB*cos(c) */
            double ar = deg2rad(at*DEGREE_STEP);
            double br = deg2rad(bt*DEGREE_STEP);
            double cr = deg2rad(ct*DEGREE_STEP);
            double sa = sin(ar), ca = cos(ar);
            double sb = sin(br), cb = cos(br);
            double sc = sin(cr), cc = cos(cr);
            double X = ca*sc-sa*cc, Y = sa*cb-ca*sb;
            double d = (X*c.VB + Y*b.VB);
            double w = X*c.VB / d;
            
            if(w > 0 && w < 1) {
                double Z = cb*sc-sb*cc;
                double bcVB = Z*b.VB*c.VB / d;
                if(bcVB > a.VB) {
                    a.VB = bcVB;
                    if(at > 180)
                        a.b = bt, a.c = ct, a.w = w;
                    else
                        a.b = ct, a.c = bt, a.w = (1-w);
                }
            }           
        }
    }
}
Beispiel #4
0
/* eta is a measure of efficiency of the boat, from .01 for racing boats to .5 for
   heavy cruisers */
void BoatPlan::ComputeBoatSpeeds(Boat &boat)
{
    csvFileName = _("<Computed>");

    for(int VWi = 0; VWi < num_wind_speeds; VWi++)
        for(int Wi = 0; Wi <= DEGREE_COUNT/2; Wi++) {
            double VW = wind_speeds[VWi];
            double W = Wi * DEGREE_STEP;
            double B, VB, A, VA;
            BoatSteadyState(deg2rad(W), VW, B, VB, A, VA, boat);
            Set(Wi, VWi, VB);
            if(W != 0)
                Set(DEGREE_COUNT-Wi, VWi, VB);
        }

    CalculateVMG();
    computed = true;
}
Beispiel #5
0
/* 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;
}
Beispiel #6
0
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;
}