Ejemplo n.º 1
0
        /// <summary>
        /// Calculates destination point given distance and bearing from start point.
        /// </summary>
        /// <param name="Bearing">Initial GPS bearing in degrees to the destination point. The valid range of values is [0, 360). See Bearing* constants.</param>
        /// <param name="Distance">Distance in metres between the start point and the destination point.</param>
        /// <returns>GPS location of the destination point.</returns>
        /// <remarks>Formula source: http://www.movable-type.co.uk/scripts/latlong.html .</remarks>
        public GpsLocation GoVector(double Bearing, double Distance)
        {
            double lat1    = (double)Latitude;
            double lon1    = (double)Longitude;
            double lat1Rad = lat1.ToRadians();
            double lon1Rad = lon1.ToRadians();
            double brnRad  = Bearing.ToRadians();

            // Formula:
            // φ2 = asin(sin φ1 ⋅ cos δ + cos φ1 ⋅ sin δ ⋅ cos θ)
            // λ2 = λ1 + atan2(sin θ ⋅ sin δ ⋅ cos φ1, cos δ − sin φ1 ⋅ sin φ2)
            // where φ is latitude, λ is longitude, θ is the bearing(clockwise from north), δ is the angular distance d / R; d being the distance travelled, R the earth’s radius
            double lat2Rad = Math.Asin(Math.Sin(lat1Rad) * Math.Cos(Distance / EarthRadius)
                                       + Math.Cos(lat1Rad) * Math.Sin(Distance / EarthRadius) * Math.Cos(brnRad));
            double lon2Rad = lon1Rad + Math.Atan2(Math.Sin(brnRad) * Math.Sin(Distance / EarthRadius) * Math.Cos(lat1Rad),
                                                  Math.Cos(Distance / EarthRadius) - Math.Sin(lat1Rad) * Math.Sin(lat2Rad));

            double lat2 = lat2Rad.ToDegrees();
            double lon2 = lon2Rad.ToDegrees();

            // Normalize longitude.
            lon2 = (lon2 + 540.0) % 360.0 - 180.0;
            if (lon2 == -180.0)
            {
                lon2 = 180.0;
            }

            GpsLocation res = new GpsLocation((decimal)lat2, (decimal)lon2);

            return(res);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Calculates distance to another location in metres using Haversine formula.
        /// </summary>
        /// <param name="TargetLocation">Target location to which the distance is calculated.</param>
        /// <returns>Distance from this location to the target location in metres.</returns>
        /// <remarks>Formula source: http://www.movable-type.co.uk/scripts/latlong.html .</remarks>
        public double DistanceTo(GpsLocation TargetLocation)
        {
            double lat1 = (double)Latitude;
            double lon1 = (double)Longitude;
            double lat2 = (double)TargetLocation.Latitude;
            double lon2 = (double)TargetLocation.Longitude;

            // Haversine formula:
            // a = sin²(Δφ/2) + cos φ1 ⋅ cos φ2 ⋅ sin²(Δλ/2)
            // c = 2 ⋅ atan2(√a, √(1−a))
            // d = R ⋅ c
            // where φ is latitude, λ is longitude, R is earth’s radius (mean radius = 6,371 km).

            double lat1Rad    = lat1.ToRadians();
            double lat2Rad    = lat2.ToRadians();
            double latDiffRad = (lat2 - lat1).ToRadians();
            double lonDiffRad = (lon2 - lon1).ToRadians();

            double a = Math.Sin(latDiffRad / 2) * Math.Sin(latDiffRad / 2)
                       + Math.Cos(lat1Rad) * Math.Cos(lat2Rad)
                       * Math.Sin(lonDiffRad / 2) * Math.Sin(lonDiffRad / 2);

            double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));

            double res = EarthRadius * c;

            return(res);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Calculates final bearing from the start point to the destination point.
        /// </summary>
        /// <param name="Destination">Destination location.</param>
        /// <returns>Final bearing from the start point to the destination point.</returns>
        /// <remarks>Formula source: http://www.movable-type.co.uk/scripts/latlong.html .</remarks>
        public double FinalBearingTo(GpsLocation Destination)
        {
            // For final bearing, simply take the initial bearing from the end point to the start point and reverse it (using θ = (θ+180) % 360).
            double reverseBrng = Destination.InitialBearingTo(this);

            double res = (reverseBrng + 180.0) % 360.0;

            return(res);
        }
Ejemplo n.º 4
0
        public override bool Equals(object obj)
        {
            if (!(obj is GpsLocation))
            {
                return(false);
            }

            GpsLocation val = (GpsLocation)obj;

            return(Latitude.Equals(val.Latitude) && Longitude.Equals(val.Longitude));
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Calculates GPS square from the given centre location using a radius.
        /// </summary>
        /// <param name="Radius">Half of a distance between oposite sides.</param>
        public GpsSquare(GpsLocation Centre, double Radius)
        {
            // We calculate positions of square mid points of top and bottom sides - i.e. points in the center of square sides.
            MidTop    = Centre.GoVector(GpsLocation.BearingNorth, Radius);
            MidBottom = Centre.GoVector(GpsLocation.BearingSouth, Radius);

            // From these mid points, we navigate use West and East bearing to go to the square corners.
            LeftTop     = MidTop.GoVector(GpsLocation.BearingWest, Radius);
            RightTop    = MidTop.GoVector(GpsLocation.BearingEast, Radius);
            LeftBottom  = MidBottom.GoVector(GpsLocation.BearingWest, Radius);
            RightBottom = MidBottom.GoVector(GpsLocation.BearingEast, Radius);
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Basic square constructor.
        /// </summary>
        /// <param name="LeftTop">Location of the left-top corner of the square.</param>
        /// <param name="RightTop">Location of the right-top corner of the square.</param>
        /// <param name="LeftBottom">Location of the left-bottom corner of the square.</param>
        /// <param name="RightBottom">Location of the right-bottom corner of the square.</param>
        public GpsSquare(GpsLocation LeftTop, GpsLocation RightTop, GpsLocation LeftBottom, GpsLocation RightBottom)
        {
            this.LeftTop     = LeftTop;
            this.RightTop    = RightTop;
            this.LeftBottom  = LeftBottom;
            this.RightBottom = RightBottom;

            double bearing  = LeftTop.InitialBearingTo(RightTop);
            double distance = LeftTop.DistanceTo(RightTop) / 2;

            MidTop = LeftTop.GoVector(bearing, distance);

            bearing   = LeftBottom.InitialBearingTo(RightBottom);
            distance  = LeftBottom.DistanceTo(RightBottom) / 2;
            MidBottom = LeftBottom.GoVector(bearing, distance);
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Calculates GPS square from the given centre location using a radius.
        /// </summary>
        /// <param name="Radius">Half of a distance between oposite sides.</param>
        /// <returns></returns>
        public GpsSquare GetSquare(double Radius)
        {
            // We calculate positions of square mid points of top and bottom sides - i.e. points in the center of square sides.
            GpsLocation midTop    = GoVector(BearingNorth, Radius);
            GpsLocation midBottom = GoVector(BearingSouth, Radius);

            // From these mid points, we navigate use West and East bearing to go to the square corners.
            GpsLocation leftTop     = midTop.GoVector(BearingWest, Radius);
            GpsLocation rightTop    = midTop.GoVector(BearingEast, Radius);
            GpsLocation leftBottom  = midBottom.GoVector(BearingWest, Radius);
            GpsLocation rightBottom = midBottom.GoVector(BearingEast, Radius);

            GpsSquare res = new GpsSquare(leftTop, rightTop, leftBottom, rightBottom);

            return(res);
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Calculates initial bearing from the start point to the destination point.
        /// </summary>
        /// <param name="Destination">Destination location.</param>
        /// <returns>Initial bearing from the start point to the destination point.</returns>
        /// <remarks>Formula source: http://www.movable-type.co.uk/scripts/latlong.html .</remarks>
        public double InitialBearingTo(GpsLocation Destination)
        {
            double lat1    = (double)Latitude;
            double lon1    = (double)Longitude;
            double lat2    = (double)Destination.Latitude;
            double lon2    = (double)Destination.Longitude;
            double lat1Rad = lat1.ToRadians();
            double lon1Rad = lon1.ToRadians();
            double lat2Rad = lat2.ToRadians();
            double lon2Rad = lon2.ToRadians();

            // Formula:     θ = atan2(sin Δλ ⋅ cos φ2 , cos φ1 ⋅ sin φ2 − sin φ1 ⋅ cos φ2 ⋅ cos Δλ)
            // where φ1, λ1 is the start point, φ2, λ2 the end point(Δλ is the difference in longitude)

            double y = Math.Sin(lon2Rad - lon1Rad) * Math.Cos(lat2Rad);
            double x = Math.Cos(lat1Rad) * Math.Sin(lat2Rad) -
                       Math.Sin(lat1Rad) * Math.Cos(lat2Rad) * Math.Cos(lon2Rad - lon1Rad);

            double res = Math.Atan2(y, x).ToDegrees();

            // Normalize.
            res = (res + 360.0) % 360.0;
            return(res);
        }
        /// <summary>
        /// Processes update received from LOC server that informs the proximity server about a new neighbor server or a change in existing neighbor server contact information.
        /// </summary>
        /// <param name="UnitOfWork">Unit of work instance.</param>
        /// <param name="ServerId">Network identifier of the neighbor server.</param>
        /// <param name="IpAddress">IP address of the neighbor server.</param>
        /// <param name="Port">Primary interface port of the neighbor server.</param>
        /// <param name="Latitude">GPS location latitude of the neighbor server.</param>
        /// <param name="Longitude">GPS location longitude of the neighbor server.</param>
        /// <param name="NeighborhoodSize">Size of the proximity server's neighborhood at the moment the function is called.</param>
        /// <returns>Information related to how should the caller proceed further, described in AddOrChangeNeighborResult structure.</returns>
        /// <remarks>The caller is responsible for calling this function within a database transaction with NeighborLock and NeighborhoodActionLock locks.</remarks>
        public async Task <AddOrChangeNeighborResult> AddOrChangeNeighborAsync(UnitOfWork UnitOfWork, byte[] ServerId, IPAddress IpAddress, int Port, int Latitude, int Longitude, int NeighborhoodSize)
        {
            log.Trace("(ServerId:'{0}',IpAddress:{1},Port:{2},Latitude:{3},Longitude:{4},NeighborhoodSize:{5})", ServerId.ToHex(), IpAddress, Port, Latitude, Longitude, NeighborhoodSize);

            AddOrChangeNeighborResult res = new AddOrChangeNeighborResult();

            res.NeighborhoodSize = NeighborhoodSize;

            // Data validation.
            bool serverIdValid = ServerId.Length == ProtocolHelper.NetworkIdentifierLength;

            if (!serverIdValid)
            {
                log.Error("Received invalid neighbor server ID '{0}' from LOC server.", ServerId.ToHex());
                res.Error = true;
                log.Trace("(-):*.Error={0},*.SaveDb={1},*.SignalActionProcessor={2},*.NeighborhoodSize={3}", res.Error, res.SaveDb, res.SignalActionProcessor, res.NeighborhoodSize);
                return(res);
            }

            bool portValid = (0 < Port) && (Port <= 65535);

            if (!portValid)
            {
                log.Error("Received invalid neighbor server port '{0}' from LOC server.", Port);
                res.Error = true;
                log.Trace("(-):*.Error={0},*.SaveDb={1},*.SignalActionProcessor={2},*.NeighborhoodSize={3}", res.Error, res.SaveDb, res.SignalActionProcessor, res.NeighborhoodSize);
                return(res);
            }

            IopProtocol.GpsLocation location = new IopProtocol.GpsLocation(Latitude, Longitude);
            if (!location.IsValid())
            {
                log.Error("Received invalid neighbor server location '{0}' from LOC server.", location);
                res.Error = true;
                log.Trace("(-):*.Error={0},*.SaveDb={1},*.SignalActionProcessor={2},*.NeighborhoodSize={3}", res.Error, res.SaveDb, res.SignalActionProcessor, res.NeighborhoodSize);
                return(res);
            }

            // Data processing.
            Neighbor existingNeighbor = (await UnitOfWork.NeighborRepository.GetAsync(n => n.NetworkId == ServerId)).FirstOrDefault();

            if (existingNeighbor == null)
            {
                // New neighbor server.
                if (NeighborhoodSize < Config.Configuration.MaxNeighborhoodSize)
                {
                    // We have not reached the maximal size of the neighborhood yet, the server can be added.
                    log.Trace("New neighbor ID '{0}' detected, IP address {1}, port {2}, latitude {3}, longitude {4}.", ServerId.ToHex(), IpAddress, Port, Latitude, Longitude);

                    // Add neighbor to the database of neighbors.
                    // The neighbor is not initialized, so we will not allow it to send us
                    // any updates. First, we need to contact it and start the neighborhood initialization process.
                    Neighbor neighbor = new Neighbor()
                    {
                        NetworkId         = ServerId,
                        IpAddress         = IpAddress.GetAddressBytes(),
                        PrimaryPort       = Port,
                        NeighborPort      = null,
                        LocationLatitude  = location.Latitude,
                        LocationLongitude = location.Longitude,
                        LastRefreshTime   = DateTime.UtcNow,
                        Initialized       = false,
                        SharedActivities  = 0
                    };
                    await UnitOfWork.NeighborRepository.InsertAsync(neighbor);

                    res.NeighborhoodSize++;

                    // This action will cause our proximity server to contact the new neighbor server and ask it to share its activity database,
                    // i.e. the neighborhood initialization process will be started.
                    // We set a delay depending on the number of neighbors, so that a new server joining a neighborhood is not overwhelmed with requests.
                    int delay = RandomSource.Generator.Next(0, 3 * res.NeighborhoodSize);

                    NeighborhoodAction action = new NeighborhoodAction()
                    {
                        ServerId              = ServerId,
                        Timestamp             = DateTime.UtcNow,
                        Type                  = NeighborhoodActionType.AddNeighbor,
                        ExecuteAfter          = DateTime.UtcNow.AddSeconds(delay),
                        TargetActivityId      = 0,
                        TargetActivityOwnerId = null,
                        AdditionalData        = null,
                    };
                    await UnitOfWork.NeighborhoodActionRepository.InsertAsync(action);

                    res.SignalActionProcessor = true;
                    res.SaveDb = true;
                }
                else
                {
                    log.Error("Unable to add new neighbor ID '{0}', the proximity server reached its neighborhood size limit {1}.", ServerId.ToHex(), Config.Configuration.MaxNeighborhoodSize);
                }
            }
            else
            {
                // This is a neighbor we already know about. Just check that its information is up to date and if not, update it.
                IPAddress existingNeighborIpAddress = new IPAddress(existingNeighbor.IpAddress);
                if (!existingNeighborIpAddress.Equals(IpAddress))
                {
                    log.Trace("Existing neighbor ID '{0}' changed its IP address from {1} to {2}.", ServerId.ToHex(), existingNeighborIpAddress, IpAddress);
                    existingNeighbor.IpAddress = IpAddress.GetAddressBytes();
                }

                if (existingNeighbor.PrimaryPort != Port)
                {
                    // Primary port was change, so we also expect that the neighbors interface port was changed as well.
                    log.Trace("Existing neighbor ID '{0}' changed its primary port from {1} to {2}, invalidating neighbors interface port as well.", ServerId.ToHex(), existingNeighbor.PrimaryPort, Port);
                    existingNeighbor.PrimaryPort  = Port;
                    existingNeighbor.NeighborPort = null;
                }

                if (existingNeighbor.LocationLatitude != location.Latitude)
                {
                    log.Trace("Existing neighbor ID '{0}' changed its latitude from {1} to {2}.", ServerId.ToHex(), existingNeighbor.LocationLatitude, location.Latitude);
                    existingNeighbor.LocationLatitude = Latitude;
                }

                if (existingNeighbor.LocationLongitude != location.Longitude)
                {
                    log.Trace("Existing neighbor ID '{0}' changed its longitude from {1} to {2}.", ServerId.ToHex(), existingNeighbor.LocationLongitude, location.Longitude);
                    existingNeighbor.LocationLongitude = Longitude;
                }

                // We consider a fresh LOC info to be accurate, so we do not want to delete the neighbors received here
                // and hence we update their refresh time.
                existingNeighbor.LastRefreshTime = DateTime.UtcNow;

                UnitOfWork.NeighborRepository.Update(existingNeighbor);
                res.SaveDb = true;
            }

            log.Trace("(-):*.Error={0},*.SaveDb={1},*.SignalActionProcessor={2},*.NeighborhoodSize={3}", res.Error, res.SaveDb, res.SignalActionProcessor, res.NeighborhoodSize);
            return(res);
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Announces server's primary server role interface to the LOC server.
        /// </summary>
        /// <param name="PrimaryPort">Primary port of the server.</param>
        /// <param name="Type">Type of the server.</param>
        /// <param name="Location">Optionally, an empty GpsLocation instance that will be filled with location information received from the LOC server if the function succeeds.</param>
        /// <returns>true if the function succeeds, false otherwise.</returns>
        public async Task <bool> RegisterPrimaryServerRoleAsync(int PrimaryPort, ServiceType Type, IopProtocol.GpsLocation Location = null)
        {
            log.Info("(PrimaryPort:{0})", PrimaryPort);

            bool res = false;

            ServiceInfo serviceInfo = new ServiceInfo()
            {
                Port        = (uint)PrimaryPort,
                Type        = Type,
                ServiceData = ProtocolHelper.ByteArrayToByteString(Crypto.Sha256(((KeysEd25519)config.Settings["Keys"]).PublicKey))
            };

            LocProtocolMessage request = MessageBuilder.CreateRegisterServiceRequest(serviceInfo);

            if (await SendMessageAsync(request))
            {
                LocProtocolMessage response = await ReceiveMessageAsync(shutdownSignaling.ShutdownCancellationTokenSource.Token);

                if (response != null)
                {
                    res = (response.Id == request.Id) &&
                          (response.MessageTypeCase == Message.MessageTypeOneofCase.Response) &&
                          (response.Response.Status == Status.Ok) &&
                          (response.Response.ResponseTypeCase == Response.ResponseTypeOneofCase.LocalService) &&
                          (response.Response.LocalService.LocalServiceResponseTypeCase == LocalServiceResponse.LocalServiceResponseTypeOneofCase.RegisterService);

                    if (res)
                    {
                        if (Location != null)
                        {
                            IopProtocol.GpsLocation location = new IopProtocol.GpsLocation(response.Response.LocalService.RegisterService.Location.Latitude, response.Response.LocalService.RegisterService.Location.Longitude);
                            Location.Latitude  = location.Latitude;
                            Location.Longitude = location.Longitude;
                            if (Location.IsValid())
                            {
                                res = true;
                            }
                            else
                            {
                                log.Error("Registration failed, LOC server provided invalid location information [{0}].", location);
                            }
                        }
                        else
                        {
                            res = true;
                        }

                        if (res)
                        {
                            log.Debug("Primary interface has been registered successfully on LOC server{0}.", Location != null ? string.Format(", server location set is [{0}]", Location) : "");
                        }
                    }
                    else
                    {
                        log.Error("Registration failed, response status is {0}.", response.Response != null ? response.Response.Status.ToString() : "n/a");
                    }
                }
                else
                {
                    log.Error("Invalid message received from LOC server.");
                }
            }
            else
            {
                log.Error("Unable to send register server request to LOC server.");
            }

            log.Info("(-):{0}", res);
            return(res);
        }
Ejemplo n.º 11
0
 /// <summary>
 /// Calculates distance between two locations.
 /// </summary>
 /// <param name="LocationA">First location.</param>
 /// <param name="LocationB">Second location.</param>
 /// <returns>Distance between the two locations in metres.</returns>
 public static double DistanceBetween(GpsLocation LocationA, GpsLocation LocationB)
 {
     return(LocationA.DistanceTo(LocationB));
 }
Ejemplo n.º 12
0
        /// <summary>
        /// Creates a response message to a RegisterServiceRequest message.
        /// </summary>
        /// <param name="Request">RegisterServiceRequest message for which the response is created.</param>
        /// <returns>RegisterServiceResponse message that is ready to be sent.</returns>
        public LocProtocolMessage CreateRegisterServiceResponse(LocProtocolMessage Request, GpsLocation Location)
        {
            RegisterServiceResponse registerServiceResponse = new RegisterServiceResponse();

            registerServiceResponse.Location = new Iop.Locnet.GpsLocation()
            {
                Latitude  = Location.GetLocationTypeLatitude(),
                Longitude = Location.GetLocationTypeLongitude()
            };

            LocProtocolMessage res = CreateLocalServiceOkResponse(Request);

            res.Response.LocalService.RegisterService = registerServiceResponse;

            return(res);
        }