Ejemplo n.º 1
0
RoutePoint *Routeman::FindBestActivatePoint( Route *pR, double lat, double lon, double cog,
        double sog )
{
    if( !pR ) return NULL;

    // Walk thru all the points to find the "best"
    RoutePoint *best_point = NULL;
    double min_time_found = 1e6;

    wxRoutePointListNode *node = ( pR->pRoutePointList )->GetFirst();
    while( node ) {
        RoutePoint *pn = node->GetData();

        double brg, dist;
        DistanceBearingMercator( pn->m_lat, pn->m_lon, lat, lon, &brg, &dist );

        double angle = brg - cog;
        double soa = cos( angle * PI / 180. );

        double time_to_wp = dist / soa;

        if( time_to_wp > 0 ) {
            if( time_to_wp < min_time_found ) {
                min_time_found = time_to_wp;
                best_point = pn;
            }
        }
        node = node->GetNext();
    }
    return best_point;
}
Ejemplo n.º 2
0
/*
 Update a single route segment lengths
 Also, compute total route length by summing segment distances.
 */
void Route::UpdateSegmentDistance( RoutePoint *prp0, RoutePoint *prp, double planspeed )
{
    double slat1 = prp0->m_lat, slon1 = prp0->m_lon;
    double slat2 = prp->m_lat, slon2 = prp->m_lon;

//    Calculate the absolute distance from 1->2

    double dd;
    // why are we using mercator rather than great circle here?? [sean 8-11-2015]
    DistanceBearingMercator( slat1, slon1, slat2, slon2, 0, &dd );

//    And store in Point 2
    prp->m_seg_len = dd;

    m_route_length += dd;

//    If Point1 Description contains VMG, store it for Properties Dialog in Point2
//    If Point1 Description contains ETD, store it in Point1

    if( planspeed > 0. ) {
        double vmg = 0.0;
        wxDateTime etd;

        if( prp0->m_MarkDescription.Find( _T("VMG=") ) != wxNOT_FOUND ) {
            wxString s_vmg = ( prp0->m_MarkDescription.Mid(
                                   prp0->m_MarkDescription.Find( _T("VMG=") ) + 4 ) ).BeforeFirst( ';' );
            if( !s_vmg.ToDouble( &vmg ) ) vmg = planspeed;
        }

        double legspeed = planspeed;
        if( vmg > 0.1 && vmg < 1000. ) legspeed = vmg;
        if( legspeed > 0.1 && legspeed < 1000. ) {
            m_route_time += 3600. * dd / legspeed;
            prp->m_seg_vmg = legspeed;
        }

        prp0->m_seg_etd = wxInvalidDateTime;
        if( prp0->m_MarkDescription.Find( _T("ETD=") ) != wxNOT_FOUND ) {
            wxString s_etd = ( prp0->m_MarkDescription.Mid(
                                   prp0->m_MarkDescription.Find( _T("ETD=") ) + 4 ) ).BeforeFirst( ';' );
            const wxChar *parse_return = etd.ParseDateTime( s_etd );
            if( parse_return ) {
                wxString tz( parse_return );

                if( tz.Find( _T("UT") ) != wxNOT_FOUND ) prp0->m_seg_etd = etd;
                else
                    if( tz.Find( _T("LMT") ) != wxNOT_FOUND ) {
                        prp0->m_seg_etd = etd;
                        long lmt_offset = (long) ( ( prp0->m_lon * 3600. ) / 15. );
                        wxTimeSpan lmt( 0, 0, (int) lmt_offset, 0 );
                        prp0->m_seg_etd -= lmt;
                    } else
                        prp0->m_seg_etd = etd.ToUTC();
            }
        }
    }
}
Ejemplo n.º 3
0
double Track::GetXTE( double fm1Lat, double fm1Lon, double fm2Lat, double fm2Lon, double toLat, double toLon  )
{
    vector2D v, w, p;

    // First we get the cartesian coordinates to the line endpoints, using
    // the current position as origo.

    double brg1, dist1, brg2, dist2;
    DistanceBearingMercator( toLat, toLon, fm1Lat, fm1Lon, &brg1, &dist1 );
    w.x = dist1 * sin( brg1 * PI / 180. );
    w.y = dist1 * cos( brg1 * PI / 180. );

    DistanceBearingMercator( toLat, toLon, fm2Lat, fm2Lon, &brg2, &dist2 );
    v.x = dist2 * sin( brg2 * PI / 180. );
    v.y = dist2 * cos( brg2 * PI / 180. );

    p.x = 0.0; p.y = 0.0;

    const double lengthSquared = _distance2( v, w );
    if ( lengthSquared == 0.0 ) {
        // v == w case
        return _distance( p, v );
    }

    // Consider the line extending the segment, parameterized as v + t (w - v).
    // We find projection of origo onto the line.
    // It falls where t = [(p-v) . (w-v)] / |w-v|^2

    vector2D a = p - v;
    vector2D b = w - v;

    double t = vDotProduct( &a, &b ) / lengthSquared;

    if (t < 0.0) return _distance(p, v);       // Beyond the 'v' end of the segment
    else if (t > 1.0) return _distance(p, w);  // Beyond the 'w' end of the segment
    vector2D projection = v + t * (w - v);     // Projection falls on the segment
    return _distance(p, projection);
}
Ejemplo n.º 4
0
/*
 Update the route segment lengths, storing each segment length in <destination> point.
 Also, compute total route length by summing segment distances.
 */
void Route::UpdateSegmentDistances( double planspeed )
{
    wxPoint rpt, rptn;
    float slat1, slon1, slat2, slon2;

    double route_len = 0.0;
    double route_time = 0.0;

    wxRoutePointListNode *node = pRoutePointList->GetFirst();

    if( node ) {
        RoutePoint *prp0 = node->GetData();
        slat1 = prp0->m_lat;
        slon1 = prp0->m_lon;

        node = node->GetNext();

        while( node ) {
            RoutePoint *prp = node->GetData();
            slat2 = prp->m_lat;
            slon2 = prp->m_lon;

//    Calculate the absolute distance from 1->2

            double brg, dd;
            DistanceBearingMercator( slat1, slon1, slat2, slon2, &brg, &dd );

//    And store in Point 2
            prp->m_seg_len = dd;

            route_len += dd;

            slat1 = slat2;
            slon1 = slon2;

//    If Point1 Description contains VMG, store it for Properties Dialog in Point2
//    If Point1 Description contains ETD, store it in Point1

            if( planspeed > 0. ) {
                double vmg = 0.0;
                wxDateTime etd;

                if( prp0->m_MarkDescription.Find( _T("VMG=") ) != wxNOT_FOUND ) {
                    wxString s_vmg = ( prp0->m_MarkDescription.Mid(
                            prp0->m_MarkDescription.Find( _T("VMG=") ) + 4 ) ).BeforeFirst( ';' );
                    if( !s_vmg.ToDouble( &vmg ) ) vmg = planspeed;
                }

                double legspeed = planspeed;
                if( vmg > 0.1 && vmg < 1000. ) legspeed = vmg;
                if( legspeed > 0.1 && legspeed < 1000. ) {
                    route_time += dd / legspeed;
                    prp->m_seg_vmg = legspeed;
                }

                prp0->m_seg_etd = wxInvalidDateTime;
                if( prp0->m_MarkDescription.Find( _T("ETD=") ) != wxNOT_FOUND ) {
                    wxString s_etd = ( prp0->m_MarkDescription.Mid(
                            prp0->m_MarkDescription.Find( _T("ETD=") ) + 4 ) ).BeforeFirst( ';' );
                    const wxChar *parse_return = etd.ParseDateTime( s_etd );
                    if( parse_return ) {
                        wxString tz( parse_return );

                        if( tz.Find( _T("UT") ) != wxNOT_FOUND ) prp0->m_seg_etd = etd;
                        else
                            if( tz.Find( _T("LMT") ) != wxNOT_FOUND ) {
                                prp0->m_seg_etd = etd;
                                long lmt_offset = (long) ( ( prp0->m_lon * 3600. ) / 15. );
                                wxTimeSpan lmt( 0, 0, (int) lmt_offset, 0 );
                                prp0->m_seg_etd -= lmt;
                            } else
                                prp0->m_seg_etd = etd.ToUTC();
                    }
                }
            }

            prp0 = prp;

            node = node->GetNext();
        }
    }

    m_route_length = route_len;
    m_route_time = route_time * 3600.;
}
Ejemplo n.º 5
0
wxString OCPNTrackListCtrl::OnGetItemText( long item, long column ) const
{
    wxString ret;

    if( item != g_prev_item ) {
        if( g_prev_point_index == ( item - 1 ) ) {
            if( !g_prev_point_node ) return wxEmptyString;
            g_prev_point = g_this_point;
            g_this_point_node = g_prev_point_node->GetNext();
            if( g_this_point_node )
                g_this_point = g_this_point_node->GetData();
            else
                g_this_point = NULL;
        } else {
            wxRoutePointListNode *node = m_pRoute->pRoutePointList->GetFirst();
            if( node ) {
                if( item > 0 ) {
                    int i = 0;
                    while( node && ( i < ( item - 1 ) ) ) {
                        node = node->GetNext();
                        i++;
                    }
                    g_prev_point_node = node;
                    if( ! node )  return wxEmptyString;
                    g_prev_point = g_prev_point_node->GetData();

                    g_this_point_node = g_prev_point_node->GetNext();
                    if( g_this_point_node )
                        g_this_point = g_this_point_node->GetData();
                    else
                        g_this_point = NULL;
                } else {
                    g_prev_point_node = NULL;
                    g_prev_point = NULL;

                    g_this_point_node = node;
                    if( g_this_point_node )
                        g_this_point = g_this_point_node->GetData();
                    else
                        g_this_point = NULL;
                }
            } else {
                g_prev_point_node = NULL;
                g_prev_point = NULL;
                g_this_point_node = NULL;
                g_this_point = NULL;
            }
        }

        //    Update for next time
        g_prev_point_node = g_this_point_node;
        g_prev_point_index = item;

        g_prev_item = item;
    }

    if( ! g_this_point )
        return wxEmptyString;

    switch( column )
    {
        case 0:
            if( item == 0 )
                ret = _T("---");
            else
                ret.Printf( _T("%ld"), item );
            break;

        case 1:
            double slat, slon;
            if( item == 0 )
            {
                slat = gLat;
                slon = gLon;
            }
            else
            {
                slat = g_prev_point->m_lat;
                slon = g_prev_point->m_lon;
            }

            DistanceBearingMercator( g_this_point->m_lat, g_this_point->m_lon, slat, slon, &gt_brg, &gt_leg_dist );

            ret.Printf( _T("%6.2f ") + getUsrDistanceUnit(), toUsrDistance( gt_leg_dist ) );
            break;

        case 2:
            ret.Printf( _T("%03.0f \u00B0T"), gt_brg );
            break;

        case 3:
            ret = toSDMM( 1, g_this_point->m_lat, 1 );
            break;

        case 4:
            ret = toSDMM( 2, g_this_point->m_lon, 1 );
            break;

        case 5:
            {
                wxDateTime timestamp = g_this_point->GetCreateTime();
                if( timestamp.IsValid() )
                    ret = timestamp2s( timestamp, m_tz_selection, m_LMT_Offset, TIMESTAMP_FORMAT );
                else
                    ret = _T("----");
            }
            break;

        case 6:
            if( ( item > 0 ) && g_this_point->GetCreateTime().IsValid()
                    && g_prev_point->GetCreateTime().IsValid() )
            {
                double speed = 0.;
                double seconds =
                        g_this_point->GetCreateTime().Subtract( g_prev_point->GetCreateTime() ).GetSeconds().ToDouble();

                if( seconds > 0. )
                    speed = gt_leg_dist / seconds * 3600;

                ret.Printf( _T("%5.2f"), toUsrSpeed( speed ) );
            } else
                ret = _("--");
            break;

        default:
            break;
    }

    return ret;
}
Ejemplo n.º 6
0
bool Routeman::UpdateProgress()
{
    bool bret_val = false;

    if( pActiveRoute ) {
//      Update bearing, range, and crosstrack error

//  Bearing is calculated as Mercator Sailing, i.e. a  cartographic "bearing"
        double north, east;
        toSM( pActivePoint->m_lat, pActivePoint->m_lon, gLat, gLon, &east, &north );
        double a = atan( north / east );
        if( fabs( pActivePoint->m_lon - gLon ) < 180. ) {
            if( pActivePoint->m_lon > gLon ) CurrentBrgToActivePoint = 90. - ( a * 180 / PI );
            else
                CurrentBrgToActivePoint = 270. - ( a * 180 / PI );
        } else {
            if( pActivePoint->m_lon > gLon ) CurrentBrgToActivePoint = 270. - ( a * 180 / PI );
            else
                CurrentBrgToActivePoint = 90. - ( a * 180 / PI );
        }

//      Calculate range using Great Circle Formula

        double d5 = DistGreatCircle( gLat, gLon, pActivePoint->m_lat, pActivePoint->m_lon );
        CurrentRngToActivePoint = d5;

//      Get the XTE vector, normal to current segment
        vector2D va, vb, vn;

        double brg1, dist1, brg2, dist2;
        DistanceBearingMercator( pActivePoint->m_lat, pActivePoint->m_lon,
                                 pActiveRouteSegmentBeginPoint->m_lat, pActiveRouteSegmentBeginPoint->m_lon, &brg1,
                                 &dist1 );
        vb.x = dist1 * sin( brg1 * PI / 180. );
        vb.y = dist1 * cos( brg1 * PI / 180. );

        DistanceBearingMercator( pActivePoint->m_lat, pActivePoint->m_lon, gLat, gLon, &brg2,
                                 &dist2 );
        va.x = dist2 * sin( brg2 * PI / 180. );
        va.y = dist2 * cos( brg2 * PI / 180. );

        double sdelta = vGetLengthOfNormal( &va, &vb, &vn );             // NM
        CurrentXTEToActivePoint = sdelta;

//    Calculate the distance to the arrival line, which is perpendicular to the current route segment
//    Taking advantage of the calculated normal from current position to route segment vn
        vector2D vToArriveNormal;
        vSubtractVectors( &va, &vn, &vToArriveNormal );

        CurrentRangeToActiveNormalCrossing = vVectorMagnitude( &vToArriveNormal );

//          Compute current segment course
//          Using simple Mercater projection
        double x1, y1, x2, y2;
        toSM( pActiveRouteSegmentBeginPoint->m_lat, pActiveRouteSegmentBeginPoint->m_lon,
              pActiveRouteSegmentBeginPoint->m_lat, pActiveRouteSegmentBeginPoint->m_lon, &x1,
              &y1 );

        toSM( pActivePoint->m_lat, pActivePoint->m_lon, pActiveRouteSegmentBeginPoint->m_lat,
              pActiveRouteSegmentBeginPoint->m_lon, &x2, &y2 );

        double e1 = atan2( ( x2 - x1 ), ( y2 - y1 ) );
        CurrentSegmentCourse = e1 * 180 / PI;
        if( CurrentSegmentCourse < 0 ) CurrentSegmentCourse += 360;

        //      Compute XTE direction
        double h = atan( vn.y / vn.x );
        if( vn.x > 0 ) CourseToRouteSegment = 90. - ( h * 180 / PI );
        else
            CourseToRouteSegment = 270. - ( h * 180 / PI );

        h = CurrentBrgToActivePoint - CourseToRouteSegment;
        if( h < 0 ) h = h + 360;

        if( h > 180 ) XTEDir = 1;
        else
            XTEDir = -1;

//      Determine Arrival

        bool bDidArrival = false;

        if( CurrentRangeToActiveNormalCrossing <= pActiveRoute->GetRouteArrivalRadius() ) {
            m_bArrival = true;
            UpdateAutopilot();

            bDidArrival = true;

            if( !ActivateNextPoint( pActiveRoute, false ) )            // at the end?
            {
                Route *pthis_route = pActiveRoute;
                DeactivateRoute( true );                  // this is an arrival
                if( pthis_route->m_bDeleteOnArrival ) {
                    pConfig->DeleteConfigRoute( pthis_route );
                    DeleteRoute( pthis_route );
                    if( pRoutePropDialog ) {
                        pRoutePropDialog->SetRouteAndUpdate( NULL );
                        pRoutePropDialog->UpdateProperties();
                    }
                    if( pRouteManagerDialog ) pRouteManagerDialog->UpdateRouteListCtrl();

                }
            }

        }

        if( !bDidArrival )                                        // Only once on arrival
            UpdateAutopilot();

        bret_val = true;                                        // a route is active
    }

    m_bDataValid = true;

    return bret_val;
}
Ejemplo n.º 7
0
Route *Track::RouteFromTrack( wxGenericProgressDialog *pprog )
{

    Route *route = new Route();

    TrackPoint *pWP_src = TrackPoints.front();
    size_t prpnodeX;
    RoutePoint *pWP_dst, *pWP_prev;
    TrackPoint *prp_OK = NULL;  // last routepoint known not to exceed xte limit, if not yet added

    wxString icon = _T("xmblue");
    if( g_TrackDeltaDistance >= 0.1 ) icon = _T("diamond");

    int next_ic = 0;
    int back_ic = 0;
    int nPoints = TrackPoints.size();
    bool isProminent = true;
    double delta_dist = 0.;
    double delta_hdg, xte;
    double leg_speed = 0.1;

    if( pRoutePropDialog )
        leg_speed = pRoutePropDialog->m_planspeed;
    else
        leg_speed = g_PlanSpeed;

// add first point

    pWP_dst = new RoutePoint( pWP_src->m_lat, pWP_src->m_lon, icon, _T ( "" ), wxEmptyString );
    route->AddPoint( pWP_dst );

    pWP_dst->m_bShowName = false;
    
    pSelect->AddSelectableRoutePoint( pWP_dst->m_lat, pWP_dst->m_lon, pWP_dst );
    pWP_prev = pWP_dst;
// add intermediate points as needed

    for(size_t i = 1; i < TrackPoints.size();) {
        TrackPoint *prp = TrackPoints[i];
        prpnodeX = i;
        pWP_dst->m_lat = pWP_prev->m_lat;
        pWP_dst->m_lon = pWP_prev->m_lon;
        pWP_prev = pWP_dst;

        delta_dist = 0.0;
        delta_hdg = 0.0;
        back_ic = next_ic;

        DistanceBearingMercator( prp->m_lat, prp->m_lon, pWP_prev->m_lat, pWP_prev->m_lon, &delta_hdg,
                &delta_dist );

        if( ( delta_dist > ( leg_speed * 6.0 ) ) && !prp_OK ) {
            int delta_inserts = floor( delta_dist / ( leg_speed * 4.0 ) );
            delta_dist = delta_dist / ( delta_inserts + 1 );
            double tlat = 0.0;
            double tlon = 0.0;

            while( delta_inserts-- ) {
                ll_gc_ll( pWP_prev->m_lat, pWP_prev->m_lon, delta_hdg, delta_dist, &tlat, &tlon );
                pWP_dst = new RoutePoint( tlat, tlon, icon, _T ( "" ), wxEmptyString );
                route->AddPoint( pWP_dst );
                pWP_dst->m_bShowName = false;
                pSelect->AddSelectableRoutePoint( pWP_dst->m_lat, pWP_dst->m_lon, pWP_dst );

                pSelect->AddSelectableRouteSegment( pWP_prev->m_lat, pWP_prev->m_lon, pWP_dst->m_lat,
                        pWP_dst->m_lon, pWP_prev, pWP_dst, route );

                pWP_prev = pWP_dst;
            }
            prpnodeX = i;
            pWP_dst = pWP_prev;
            next_ic = 0;
            delta_dist = 0.0;
            back_ic = next_ic;
            prp_OK = prp;
            isProminent = true;
        } else {
            isProminent = false;
            if( delta_dist >= ( leg_speed * 4.0 ) ) isProminent = true;
            if( !prp_OK ) prp_OK = prp;
        }
        while( prpnodeX < TrackPoints.size() ) {

            TrackPoint *prpX = TrackPoints[prpnodeX];
//            TrackPoint src(pWP_prev->m_lat, pWP_prev->m_lon);
            xte = GetXTE( pWP_src, prpX, prp );
            if( isProminent || ( xte > g_TrackDeltaDistance ) ) {

                pWP_dst = new RoutePoint( prp_OK->m_lat, prp_OK->m_lon, icon, _T ( "" ),
                        wxEmptyString );

                route->AddPoint( pWP_dst );
                pWP_dst->m_bShowName = false;

                pSelect->AddSelectableRoutePoint( pWP_dst->m_lat, pWP_dst->m_lon, pWP_dst );

                pSelect->AddSelectableRouteSegment( pWP_prev->m_lat, pWP_prev->m_lon, pWP_dst->m_lat,
                        pWP_dst->m_lon, pWP_prev, pWP_dst, route );

                pWP_prev = pWP_dst;
                next_ic = 0;
                prpnodeX = TrackPoints.size();
                prp_OK = NULL;
            }

            if( prpnodeX != TrackPoints.size()) prpnodeX--;
            if( back_ic-- <= 0 ) {
                prpnodeX = TrackPoints.size();
            }
        }

        if( prp_OK ) {
            prp_OK = prp;
        }

        DistanceBearingMercator( prp->m_lat, prp->m_lon, pWP_prev->m_lat, pWP_prev->m_lon, NULL,
                &delta_dist );

        if( !( ( delta_dist > ( g_TrackDeltaDistance ) ) && !prp_OK ) ) {
            i++;
            next_ic++;
        }
        if( pprog ) pprog->Update( ( i * 100 ) / nPoints );
    }

// add last point, if needed
    if( delta_dist >= g_TrackDeltaDistance ) {
        pWP_dst = new RoutePoint( TrackPoints.back()->m_lat,
                                  TrackPoints.back()->m_lon,
                                  icon, _T ( "" ), wxEmptyString );
        route->AddPoint( pWP_dst );

        pWP_dst->m_bShowName = false;

        pSelect->AddSelectableRoutePoint( pWP_dst->m_lat, pWP_dst->m_lon, pWP_dst );

        pSelect->AddSelectableRouteSegment( pWP_prev->m_lat, pWP_prev->m_lon, pWP_dst->m_lat,
                pWP_dst->m_lon, pWP_prev, pWP_dst, route );
    }
    route->m_RouteNameString = m_TrackNameString;
    route->m_RouteStartString = m_TrackStartString;
    route->m_RouteEndString = m_TrackEndString;
    route->m_bDeleteOnArrival = false;

    return route;
}
Ejemplo n.º 8
0
bool Routeman::UpdateAutopilot()
{
    //Send all known Autopilot messages upstream
    
    //RMB
        {

            m_NMEA0183.TalkerID = _T("EC");

            SENTENCE snt;
            m_NMEA0183.Rmb.IsDataValid = NTrue;
            m_NMEA0183.Rmb.CrossTrackError = CurrentXTEToActivePoint;

            if( XTEDir < 0 ) m_NMEA0183.Rmb.DirectionToSteer = Left;
            else
                m_NMEA0183.Rmb.DirectionToSteer = Right;

            m_NMEA0183.Rmb.To = pActivePoint->GetName().Truncate( 6 );
            m_NMEA0183.Rmb.From = pActiveRouteSegmentBeginPoint->GetName().Truncate( 6 );

            if( pActivePoint->m_lat < 0. ) m_NMEA0183.Rmb.DestinationPosition.Latitude.Set(
                    -pActivePoint->m_lat, _T("S") );
            else
                m_NMEA0183.Rmb.DestinationPosition.Latitude.Set( pActivePoint->m_lat, _T("N") );

            if( pActivePoint->m_lon < 0. ) m_NMEA0183.Rmb.DestinationPosition.Longitude.Set(
                    -pActivePoint->m_lon, _T("W") );
            else
                m_NMEA0183.Rmb.DestinationPosition.Longitude.Set( pActivePoint->m_lon, _T("E") );

            m_NMEA0183.Rmb.RangeToDestinationNauticalMiles = CurrentRngToActivePoint;
            m_NMEA0183.Rmb.BearingToDestinationDegreesTrue = CurrentBrgToActivePoint;
            m_NMEA0183.Rmb.DestinationClosingVelocityKnots = gSog;

            if( m_bArrival ) m_NMEA0183.Rmb.IsArrivalCircleEntered = NTrue;
            else
                m_NMEA0183.Rmb.IsArrivalCircleEntered = NFalse;

            m_NMEA0183.Rmb.Write( snt );

            g_pMUX->SendNMEAMessage( snt.Sentence );
        }

        // RMC
        {

            m_NMEA0183.TalkerID = _T("EC");

            SENTENCE snt;
            m_NMEA0183.Rmc.IsDataValid = NTrue;

            if( gLat < 0. ) m_NMEA0183.Rmc.Position.Latitude.Set( -gLat, _T("S") );
            else
                m_NMEA0183.Rmc.Position.Latitude.Set( gLat, _T("N") );

            if( gLon < 0. ) m_NMEA0183.Rmc.Position.Longitude.Set( -gLon, _T("W") );
            else
                m_NMEA0183.Rmc.Position.Longitude.Set( gLon, _T("E") );

            m_NMEA0183.Rmc.SpeedOverGroundKnots = gSog;
            m_NMEA0183.Rmc.TrackMadeGoodDegreesTrue = gCog;

            if( !wxIsNaN(gVar) ) {
                if( gVar < 0. ) {
                    m_NMEA0183.Rmc.MagneticVariation = -gVar;
                    m_NMEA0183.Rmc.MagneticVariationDirection = West;
                } else {
                    m_NMEA0183.Rmc.MagneticVariation = gVar;
                    m_NMEA0183.Rmc.MagneticVariationDirection = East;
                }
            } else
                m_NMEA0183.Rmc.MagneticVariation = 361.; // A signal to NMEA converter, gVAR is unknown

            wxDateTime now = wxDateTime::Now();
            wxDateTime utc = now.ToUTC();
            wxString time = utc.Format( _T("%H%M%S") );
            m_NMEA0183.Rmc.UTCTime = time;

            wxString date = utc.Format( _T("%d%m%y") );
            m_NMEA0183.Rmc.Date = date;

            m_NMEA0183.Rmc.Write( snt );

            g_pMUX->SendNMEAMessage( snt.Sentence );
        }

        // APB
        {
            m_NMEA0183.TalkerID = _T("EC");
            
            SENTENCE snt;
             
            m_NMEA0183.Apb.IsLoranBlinkOK = NTrue;
            m_NMEA0183.Apb.IsLoranCCycleLockOK = NTrue;
            
            m_NMEA0183.Apb.CrossTrackErrorMagnitude = CurrentXTEToActivePoint;
            
            if( XTEDir < 0 ) m_NMEA0183.Apb.DirectionToSteer = Left;
            else
                m_NMEA0183.Apb.DirectionToSteer = Right;
            
            m_NMEA0183.Apb.CrossTrackUnits = _T("N");

            if( m_bArrival )
                m_NMEA0183.Apb.IsArrivalCircleEntered = NTrue;
            else
                m_NMEA0183.Apb.IsArrivalCircleEntered = NFalse;
 
            //  We never pass the perpendicular, since we declare arrival before reaching this point
            m_NMEA0183.Apb.IsPerpendicular = NFalse;
            
            double brg1, dist1;
            DistanceBearingMercator( pActivePoint->m_lat, pActivePoint->m_lon,
                                     pActiveRouteSegmentBeginPoint->m_lat, pActiveRouteSegmentBeginPoint->m_lon,
                                     &brg1,
                                     &dist1 );
            
            m_NMEA0183.Apb.BearingOriginToDestination = brg1;
            m_NMEA0183.Apb.BearingOriginToDestinationUnits = _("T");

            m_NMEA0183.Apb.To = pActivePoint->GetName().Truncate( 6 );
            
            m_NMEA0183.Apb.BearingPresentPositionToDestination = CurrentBrgToActivePoint;
            m_NMEA0183.Apb.BearingPresentPositionToDestinationUnits = _("T");
            
            m_NMEA0183.Apb.To = pActivePoint->GetName().Truncate( 6 );

            m_NMEA0183.Apb.HeadingToSteer = CurrentBrgToActivePoint;
            m_NMEA0183.Apb.HeadingToSteerUnits = _("T");
            
            m_NMEA0183.Apb.Write( snt );
            g_pMUX->SendNMEAMessage( snt.Sentence );
        }
        
        
    return true;
}