protected override async Task <List <Datum> > PollAsync(CancellationToken cancellationToken) { List <Datum> data = new List <Datum>(); Position currentPosition = await GpsReceiver.Get().GetReadingAsync(cancellationToken, false); if (currentPosition == null) { throw new Exception("Failed to get GPS reading."); } else { lock (_previousPositionLocker) { if (_previousPosition == null) { _previousPosition = currentPosition; } else if (currentPosition.Timestamp > _previousPosition.Timestamp) // it has happened (rarely) that positions come in out of order...drop any such positions. { data.Add(new SpeedDatum(currentPosition.Timestamp, _previousPosition, currentPosition)); _previousPosition = currentPosition; } } } return(data); }
protected override IEnumerable <Datum> Poll(CancellationToken cancellationToken) { SpeedDatum datum = null; Position currentPosition = GpsReceiver.Get().GetReading(cancellationToken, false); if (currentPosition == null) { throw new Exception("Failed to get GPS reading."); } else { lock (_previousPositionLocker) { if (_previousPosition == null) { _previousPosition = currentPosition; } else if (currentPosition.Timestamp > _previousPosition.Timestamp) // it has happened (rarely) that positions come in out of order...drop any such positions. { datum = new SpeedDatum(currentPosition.Timestamp, _previousPosition, currentPosition); _previousPosition = currentPosition; } } } if (datum == null) { return(new Datum[] { }); // datum will be null on the first poll and where polls return locations out of order (rare). these should count toward participation. } else { return(new Datum[] { datum }); } }
public void Initialize(Geolocator geolocator) { GpsReceiver.Get().Initialize(geolocator); try { InitializeXamarinInsights(); } catch (Exception ex) { _logger.Log("Failed to initialize Xamarin insights: " + ex.Message, LoggingLevel.Normal, GetType()); } }
protected override async Task StopListeningAsync() { await base.StopListeningAsync(); await GpsReceiver.Get().RemoveListenerAsync(_positionChangedHandler); _previousPosition = null; }
protected override void Initialize() { base.Initialize(); if (!GpsReceiver.Get().Locator.IsGeolocationEnabled) { string error = "Geolocation is not enabled on this device. Cannot start speed probe."; SensusServiceHelper.Get().FlashNotificationAsync(error); throw new Exception(error); } }
protected override IEnumerable <Datum> Poll() { lock (_locker) { Position currentPosition = GpsReceiver.Get().GetReading(); Datum[] data = null; if (_previousPosition == null || currentPosition == null || currentPosition.Timestamp == _previousPosition.Timestamp) { data = new Datum[] { } } ; else { // http://www.movable-type.co.uk/scripts/latlong.html double φ1 = DegreesToRadians(_previousPosition.Latitude); double φ2 = DegreesToRadians(currentPosition.Latitude); double Δφ = DegreesToRadians(currentPosition.Latitude - _previousPosition.Latitude); double Δλ = DegreesToRadians(currentPosition.Longitude - _previousPosition.Longitude); double a = Math.Pow(Math.Sin(Δφ / 2), 2) + Math.Cos(φ1) * Math.Cos(φ2) * Math.Pow(Math.Sin(Δλ / 2), 2); var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a)); var reportedDistKM = 6371 * c; double elapsedHours = new TimeSpan(currentPosition.Timestamp.Ticks - _previousPosition.Timestamp.Ticks).TotalHours; double reportedSpeedKPH = reportedDistKM / elapsedHours; float accuracy = 0; if (_previousPosition.Accuracy >= 0 && currentPosition.Accuracy >= 0) { double maxDistKM = reportedDistKM + _previousPosition.Accuracy / 1000f + currentPosition.Accuracy / 1000f; double maxSpeedKPH = maxDistKM / elapsedHours; accuracy = (float)(maxSpeedKPH - reportedSpeedKPH); } data = new SpeedDatum[] { new SpeedDatum(currentPosition.Timestamp, accuracy, (float)reportedSpeedKPH) }; } if (currentPosition != null) { _previousPosition = currentPosition; } return(data); } }
protected override void Initialize() { base.Initialize(); if (!GpsReceiver.Get().Locator.IsGeolocationEnabled || !GpsReceiver.Get().Locator.SupportsHeading) { // throw standard exception instead of NotSupportedException, since the user might decide to enable GPS in the future // and we'd like the probe to be restarted at that time. string error = "Geolocation / heading are not enabled on this device. Cannot start compass probe."; SensusServiceHelper.Get().FlashNotificationAsync(error); throw new Exception(error); } }
public override async Task LetDeviceSleepAsync() { lock (_keepAwakeLocker) { if (_keepAwakeEnabled) { _keepAwakeEnabled = false; } else { Logger.Log("Attempted to let device sleep, but keep-awake was already disabled.", LoggingLevel.Normal, GetType()); return; } } Logger.Log("Disabling keep-awake by removing GPS listener.", LoggingLevel.Normal, GetType()); await GpsReceiver.Get().RemoveListenerAsync(KeepAwakePositionChanged); }
public override async Task KeepDeviceAwakeAsync() { lock (_keepAwakeLocker) { if (_keepAwakeEnabled) { Logger.Log("Attempted to keep device awake, but keep-awake is already enabled.", LoggingLevel.Normal, GetType()); return; } else { _keepAwakeEnabled = true; } } Logger.Log("Enabling keep-awake by adding GPS listener.", LoggingLevel.Normal, GetType()); await GpsReceiver.Get().AddListenerAsync(KeepAwakePositionChanged, false); }
protected override IEnumerable <Datum> Poll(CancellationToken cancellationToken) { lock (_locker) { Position currentPosition = null; try { currentPosition = GpsReceiver.Get().GetReading(cancellationToken); } catch (Exception ex) { SensusServiceHelper.Get().Logger.Log("Failed to get GPS reading: " + ex.Message, LoggingLevel.Normal, GetType()); } SpeedDatum datum = null; if (currentPosition != null) { if (_previousPosition == null) { _previousPosition = currentPosition; } else if (currentPosition.Timestamp > _previousPosition.Timestamp) // it has happened (rarely) that positions come in out of order...drop any such positions. { datum = new SpeedDatum(currentPosition.Timestamp, _previousPosition, currentPosition); _previousPosition = currentPosition; } } if (datum == null) { return new Datum[] { } } ; else { return new Datum[] { datum } }; } }
public Main() { InitializeComponent(); var earth = CentralBodiesFacet.GetFromContext().Earth; // Load the GPS satellites from the almanac, including transmitters for specific block types, // using the data from the GPSData.txt file. m_gpsConstellation = GetGpsCommunicationsConstellation(); // create the receiver using a standard setup m_receiver = new GpsReceiver { NumberOfChannels = 12, ReceiverSolutionType = GpsReceiverSolutionType.AllInView, NavigationSatellites = m_gpsConstellation }; m_receiver.ReceiverConstraints.Add(new ElevationAngleConstraint(Trig.DegreesToRadians(5.0))); m_location = new PointCartographic(earth, new Cartographic(Trig.DegreesToRadians(-107.494000), Trig.DegreesToRadians(30.228800), 0)); m_orientation = new AxesEastNorthUp(earth, m_location); }
/// <summary> /// Run the specified script. /// </summary> /// <param name="script">Script.</param> /// <param name="previousDatum">Previous datum.</param> /// <param name="currentDatum">Current datum.</param> private void Run(Script script, Datum previousDatum = null, Datum currentDatum = null) { SensusServiceHelper.Get().Logger.Log($"Running \"{Name}\".", LoggingLevel.Normal, GetType()); script.RunTime = DateTimeOffset.UtcNow; // scheduled scripts have their expiration dates set when they're scheduled. scripts triggered by other probes // as well as on-start scripts will not yet have their expiration dates set. so check the script we've been // given and set the expiration date if needed. triggered scripts don't have windows, so the only expiration // condition comes from the maximum age. if (script.ExpirationDate == null && _maxAge.HasValue) { script.ExpirationDate = script.Birthdate + _maxAge.Value; } // script could have already expired (e.g., if user took too long to open notification). if (script.ExpirationDate.HasValue && script.ExpirationDate.Value < DateTime.Now) { SensusServiceHelper.Get().Logger.Log("Script expired before it was run.", LoggingLevel.Normal, GetType()); return; } // do not run a one-shot script if it has already been run if (OneShot && RunTimes.Count > 0) { SensusServiceHelper.Get().Logger.Log("Not running one-shot script multiple times.", LoggingLevel.Normal, GetType()); return; } lock (RunTimes) { // track participation by recording the current time. use this instead of the script's run timestamp, since // the latter is the time of notification on ios rather than the time that the user actually viewed the script. RunTimes.Add(DateTime.Now); RunTimes.RemoveAll(r => r < Probe.Protocol.ParticipationHorizon); } #region submit a separate datum indicating each time the script was run. Task.Run(async() => { // geotag the script-run datum if any of the input groups are also geotagged. if none of the groups are geotagged, then // it wouldn't make sense to gather location data from a user. double?latitude = null; double?longitude = null; DateTimeOffset?locationTimestamp = null; if (script.InputGroups.Any(inputGroup => inputGroup.Geotag)) { try { Position currentPosition = GpsReceiver.Get().GetReading(new CancellationToken(), false); if (currentPosition == null) { throw new Exception("GPS receiver returned null position."); } latitude = currentPosition.Latitude; longitude = currentPosition.Longitude; locationTimestamp = currentPosition.Timestamp; } catch (Exception ex) { SensusServiceHelper.Get().Logger.Log("Failed to get position for script-run datum: " + ex.Message, LoggingLevel.Normal, GetType()); } } await Probe.StoreDatumAsync(new ScriptRunDatum(script.RunTime.Value, Script.Id, Name, script.Id, script.ScheduledRunTime, script.CurrentDatum?.Id, latitude, longitude, locationTimestamp), default(CancellationToken)); }); #endregion // this method can be called with previous / current datum values (e.g., when the script is first triggered). it // can also be called without previous / current datum values (e.g., when triggering on a schedule). if // we have such values, set them on the script. if (previousDatum != null) { script.PreviousDatum = previousDatum; } if (currentDatum != null) { script.CurrentDatum = currentDatum; } SensusServiceHelper.Get().AddScriptToRun(script, RunMode); }
public VideoManager(GpsReceiver gpsRcvr) { gps = gpsRcvr; FpsDisplayer.Elapsed += new System.Timers.ElapsedEventHandler(FpsDisplayer_Tick); FpsDisplayer.Interval = 1000; }
protected sealed override void StopListening() { GpsReceiver.Get().RemoveListener(_positionChangedHandler); _previousPosition = null; }
/// <summary> /// Initializes a new instance of the <see cref="AddProximityTriggerPage"/> class. /// </summary> /// <param name="proximityProbe">Proximity probe to add trigger to.</param> public AddProximityTriggerPage(IPointsOfInterestProximityProbe proximityProbe) { _proximityProbe = proximityProbe; Title = "Add Trigger"; List <PointOfInterest> pointsOfInterest = SensusServiceHelper.Get().PointsOfInterest.Union(_proximityProbe.Protocol.PointsOfInterest).ToList(); if (pointsOfInterest.Count == 0) { Content = new Label { Text = "No points of interest defined. Please define one or more before creating triggers.", FontSize = 20 }; return; } StackLayout contentLayout = new StackLayout { Orientation = StackOrientation.Vertical, VerticalOptions = LayoutOptions.FillAndExpand }; #region point of interest Label pointOfInterestLabel = new Label { Text = "POI:", FontSize = 20 }; Picker pointOfInterestPicker = new Picker { Title = "Select POI", HorizontalOptions = LayoutOptions.FillAndExpand }; foreach (string poiDesc in pointsOfInterest.Select(poi => poi.ToString())) { pointOfInterestPicker.Items.Add(poiDesc); } pointOfInterestPicker.SelectedIndexChanged += (o, e) => { if (pointOfInterestPicker.SelectedIndex < 0) { _pointOfInterestName = _pointOfInterestType = null; } else { PointOfInterest poi = pointsOfInterest[pointOfInterestPicker.SelectedIndex]; _pointOfInterestName = poi.Name; _pointOfInterestType = poi.Type; } }; contentLayout.Children.Add(new StackLayout { Orientation = StackOrientation.Horizontal, HorizontalOptions = LayoutOptions.FillAndExpand, Children = { pointOfInterestLabel, pointOfInterestPicker } }); #endregion #region distance threshold Label distanceThresholdLabel = new Label { Text = "Distance Threshold (Meters):", FontSize = 20 }; Entry distanceThresholdEntry = new Entry { HorizontalOptions = LayoutOptions.FillAndExpand, Keyboard = Keyboard.Numeric }; distanceThresholdEntry.TextChanged += async(o, e) => { if (!double.TryParse(distanceThresholdEntry.Text, out _distanceThresholdMeters)) { _distanceThresholdMeters = -1; } else if (_distanceThresholdMeters < GpsReceiver.Get().MinimumDistanceThreshold) { await SensusServiceHelper.Get().FlashNotificationAsync("Distance threshold must be at least " + GpsReceiver.Get().MinimumDistanceThreshold + "."); } }; contentLayout.Children.Add(new StackLayout { Orientation = StackOrientation.Horizontal, HorizontalOptions = LayoutOptions.FillAndExpand, Children = { distanceThresholdLabel, distanceThresholdEntry } }); #endregion #region threshold direction Label thresholdDirectionLabel = new Label { Text = "Threshold Direction:", FontSize = 20 }; ProximityThresholdDirection[] thresholdDirections = new ProximityThresholdDirection[] { ProximityThresholdDirection.Within, ProximityThresholdDirection.Outside }; Picker thresholdDirectionPicker = new Picker { Title = "Select Threshold Direction", HorizontalOptions = LayoutOptions.FillAndExpand }; foreach (ProximityThresholdDirection thresholdDirection in thresholdDirections) { thresholdDirectionPicker.Items.Add(thresholdDirection.ToString()); } thresholdDirectionPicker.SelectedIndexChanged += (o, e) => { if (thresholdDirectionPicker.SelectedIndex < 0) { _thresholdDirection = ProximityThresholdDirection.Within; } else { _thresholdDirection = thresholdDirections[thresholdDirectionPicker.SelectedIndex]; } }; contentLayout.Children.Add(new StackLayout { Orientation = StackOrientation.Horizontal, HorizontalOptions = LayoutOptions.FillAndExpand, Children = { thresholdDirectionLabel, thresholdDirectionPicker } }); #endregion Button okButton = new Button { Text = "OK", FontSize = 20 }; okButton.Clicked += async(o, e) => { try { _proximityProbe.Triggers.Add(new PointOfInterestProximityTrigger(_pointOfInterestName, _pointOfInterestType, _distanceThresholdMeters, _thresholdDirection)); await Navigation.PopAsync(); } catch (Exception ex) { string message = "Failed to add trigger: " + ex.Message; await SensusServiceHelper.Get().FlashNotificationAsync(message); SensusServiceHelper.Get().Logger.Log(message, LoggingLevel.Normal, GetType()); } }; contentLayout.Children.Add(okButton); Content = new ScrollView { Content = contentLayout }; }
/// <summary> /// Initializes a new instance of the <see cref="PointsOfInterestPage"/> class. /// </summary> /// <param name="pointsOfInterest">Points of interest to display.</param> public PointsOfInterestPage(List <PointOfInterest> pointsOfInterest) { _pointsOfInterest = pointsOfInterest; Title = "Points of Interest"; _pointsOfInterestList = new ListView(); _pointsOfInterestList.ItemTemplate = new DataTemplate(typeof(TextCell)); _pointsOfInterestList.ItemTemplate.SetBinding(TextCell.TextProperty, new Binding(".", stringFormat: "{0}")); _pointsOfInterestList.ItemTapped += async(o, e) => { if (_pointsOfInterestList.SelectedItem == null) { return; } PointOfInterest selectedPointOfInterest = _pointsOfInterestList.SelectedItem as PointOfInterest; string selectedAction = await DisplayActionSheet(selectedPointOfInterest.ToString(), "Cancel", null, "Delete"); if (selectedAction == "Delete") { if (await DisplayAlert("Delete " + selectedPointOfInterest.Name + "?", "This action cannot be undone.", "Delete", "Cancel")) { _pointsOfInterest.Remove(selectedPointOfInterest); _pointsOfInterestList.SelectedItem = null; // reset it manually, since it isn't done automatically. Bind(); } } }; Bind(); Content = _pointsOfInterestList; ToolbarItems.Add(new ToolbarItem(null, "plus.png", () => { SensusServiceHelper.Get().PromptForInputsAsync( "Define Point Of Interest", new Input[] { new SingleLineTextInput("POI Name:", Keyboard.Text) { Required = false }, new SingleLineTextInput("POI Type:", Keyboard.Text) { Required = false }, new SingleLineTextInput("Address:", Keyboard.Text) { Required = false } }, null, true, null, null, null, null, false, inputs => { if (inputs == null) { return; } string name = inputs[0].Value as string; string type = inputs[1].Value as string; string address = inputs[2].Value as string; if (string.IsNullOrWhiteSpace(name) && string.IsNullOrWhiteSpace(type)) { SensusServiceHelper.Get().FlashNotificationAsync("You must enter either a name or type (or both)."); } else { Action <List <Position> > addPOI = new Action <List <Position> >(poiPositions => { SensusContext.Current.MainThreadSynchronizer.ExecuteThreadSafe(async() => { if (poiPositions != null && poiPositions.Count > 0 && await DisplayAlert("Add POI?", "Would you like to add " + poiPositions.Count + " point(s) of interest?", "Yes", "No")) { foreach (Position poiPosition in poiPositions) { _pointsOfInterest.Add(new PointOfInterest(name, type, poiPosition.ToGeolocationPosition())); Bind(); } } }); }); string newPinName = name + (string.IsNullOrWhiteSpace(type) ? "" : " (" + type + ")"); if (string.IsNullOrWhiteSpace(address)) { // cancel existing token source if we have one if (_gpsCancellationTokenSource != null && !_gpsCancellationTokenSource.IsCancellationRequested) { _gpsCancellationTokenSource.Cancel(); } _gpsCancellationTokenSource = new CancellationTokenSource(); Plugin.Geolocator.Abstractions.Position gpsPosition = GpsReceiver.Get().GetReading(_gpsCancellationTokenSource.Token); if (gpsPosition != null) { SensusServiceHelper.Get().GetPositionsFromMapAsync(gpsPosition.ToFormsPosition(), newPinName, addPOI); } } else { SensusServiceHelper.Get().GetPositionsFromMapAsync(address, newPinName, addPOI); } } }); })); Disappearing += (o, e) => { if (_gpsCancellationTokenSource != null && !_gpsCancellationTokenSource.IsCancellationRequested) { _gpsCancellationTokenSource.Cancel(); } }; }
protected sealed override void StartListening() { _previousPosition = null; GpsReceiver.Get().AddListener(_positionChangedHandler, false); }
public void PromptForInputsAsync(Datum triggeringDatum, bool isReprompt, DateTimeOffset firstPromptTimestamp, IEnumerable <InputGroup> inputGroups, CancellationToken?cancellationToken, Action <IEnumerable <InputGroup> > callback) { new Thread(() => { if (inputGroups == null || inputGroups.All(inputGroup => inputGroup == null)) { callback(inputGroups); return; } // only one prompt can run at a time...enforce that here. lock (PROMPT_FOR_INPUTS_LOCKER) { if (PROMPT_FOR_INPUTS_RUNNING) { callback(inputGroups); return; } else { PROMPT_FOR_INPUTS_RUNNING = true; } } InputGroup[] incompleteGroups = inputGroups.Where(inputGroup => !inputGroup.Complete).ToArray(); bool firstPageDisplay = true; for (int incompleteGroupNum = 0; inputGroups != null && incompleteGroupNum < incompleteGroups.Length && !cancellationToken.GetValueOrDefault().IsCancellationRequested; ++incompleteGroupNum) { InputGroup incompleteGroup = incompleteGroups[incompleteGroupNum]; ManualResetEvent responseWait = new ManualResetEvent(false); if (incompleteGroup.Inputs.Count == 1 && incompleteGroup.Inputs[0] is VoiceInput) { VoiceInput promptInput = incompleteGroup.Inputs[0] as VoiceInput; promptInput.RunAsync(triggeringDatum, isReprompt, firstPromptTimestamp, response => { responseWait.Set(); }); } else { BringToForeground(); Device.BeginInvokeOnMainThread(async() => { PromptForInputsPage promptForInputsPage = new PromptForInputsPage(incompleteGroup, incompleteGroupNum + 1, incompleteGroups.Length, result => { if (result == PromptForInputsPage.Result.Cancel || result == PromptForInputsPage.Result.NavigateBackward && incompleteGroupNum == 0) { inputGroups = null; } else if (result == PromptForInputsPage.Result.NavigateBackward) { incompleteGroupNum -= 2; // we're past the first page, so decrement by two so that, after the for-loop post-increment, the previous input group will be shown } responseWait.Set(); }); await App.Current.MainPage.Navigation.PushModalAsync(promptForInputsPage, firstPageDisplay); // animate first page firstPageDisplay = false; }); } responseWait.WaitOne(); } // geotag input groups if the user didn't cancel and we've got input groups with inputs that are complete and lacking locations if (inputGroups != null && incompleteGroups.Any(incompleteGroup => incompleteGroup.Geotag && incompleteGroup.Inputs.Any(input => input.Complete && (input.Latitude == null || input.Longitude == null)))) { SensusServiceHelper.Get().Logger.Log("Geotagging input groups.", LoggingLevel.Normal, GetType()); try { Position currentLocation = GpsReceiver.Get().GetReading(cancellationToken.GetValueOrDefault()); if (currentLocation != null) { foreach (InputGroup incompleteGroup in incompleteGroups) { if (incompleteGroup.Geotag) { foreach (Input input in incompleteGroup.Inputs) { if (input.Complete) { if (input.Latitude == null) { input.Latitude = currentLocation.Latitude; } if (input.Longitude == null) { input.Longitude = currentLocation.Longitude; } } } } } } } catch (Exception ex) { SensusServiceHelper.Get().Logger.Log("Error geotagging input groups: " + ex.Message, LoggingLevel.Normal, GetType()); } } callback(inputGroups); PROMPT_FOR_INPUTS_RUNNING = false; }).Start(); }
protected SensusServiceHelper() { if (SINGLETON != null) { throw new SensusException("Attempted to construct new service helper when singleton already existed."); } _stopped = true; _registeredProtocols = new ObservableCollection <Protocol>(); _runningProtocolIds = new List <string>(); _healthTestCallbackId = null; _idCallback = new Dictionary <string, ScheduledCallback>(); _hasher = new SHA256Managed(); _pointsOfInterest = new List <PointOfInterest>(); if (!Directory.Exists(SHARE_DIRECTORY)) { Directory.CreateDirectory(SHARE_DIRECTORY); } #if DEBUG LoggingLevel loggingLevel = LoggingLevel.Debug; #elif RELEASE LoggingLevel loggingLevel = LoggingLevel.Normal; #else #error "Unrecognized configuration." #endif _logger = new Logger(LOG_PATH, loggingLevel, Console.Error); _logger.Log("Log file started at \"" + LOG_PATH + "\".", LoggingLevel.Normal, GetType()); GpsReceiver.Get().Initialize(Geolocator); // initialize GPS receiver with platform-specific geolocator if (Insights.IsInitialized) { _logger.Log("Xamarin Insights is already initialized.", LoggingLevel.Normal, GetType()); } else if (string.IsNullOrWhiteSpace(XAMARIN_INSIGHTS_APP_KEY)) { _logger.Log("Xamarin Insights API key is empty. Not initializing.", LoggingLevel.Normal, GetType()); // xamarin allows to initialize with a null key, which fails with exception but results in IsInitialized being true. prevent that here. } else { try { _logger.Log("Initializing Xamarin Insights.", LoggingLevel.Normal, GetType()); // wait for startup crash to be logged -- https://insights.xamarin.com/docs Insights.HasPendingCrashReport += (sender, isStartupCrash) => { if (isStartupCrash) { Insights.PurgePendingCrashReports().Wait(); } }; InitializeXamarinInsights(); } catch (Exception ex) { _logger.Log("Failed to initialize Xamarin insights: " + ex.Message, LoggingLevel.Normal, GetType()); } } }
protected sealed override void StartListening() { GpsReceiver.Get().AddListener(_positionChangedHandler, true); }
protected override async Task StartListeningAsync() { await base.StartListeningAsync(); await GpsReceiver.Get().AddListenerAsync(_positionChangedHandler, true); }
/// <summary> /// Method to calculate all the data required for the graphs. /// </summary> private void OnAddReceiverClick(object sender, EventArgs e) { #region CreateAndConfigureReceiver // Let's create the GPSReceiver. The receiver stores many properties and has a defined location. This location // is the point of reference for visibility calculations. receiver = new GpsReceiver(); // add receiver to the tree TreeNode newNode = new TreeNode(Localization.Receiver); rootNode.Nodes.Add(newNode); // Easy reference to Earth Central body used to initialize the ElevationAngleAccessConstraint and // to calculate the Az/El/Range Data. EarthCentralBody earth = CentralBodiesFacet.GetFromContext().Earth; // set the receiver properties based on user selections // The receiver has a receiver FrontEnd that contains the visibility and tracking constraints // Be sure to convert your angles to Radians! double minimumAngle = Trig.DegreesToRadians(Double.Parse(MaskAngle.Text)); receiver.ReceiverConstraints.Clear(); receiver.ReceiverConstraints.Add(new ElevationAngleConstraint(earth, minimumAngle)); receiver.NumberOfChannels = (int)NumberOfChannels.Value; receiver.NoiseModel = new ConstantGpsReceiverNoiseModel(0.8); // The receiver's methods of reducing the number of visible satellites to the limit imposed by the number of channels if (BestNSolType.Checked) { receiver.ReceiverSolutionType = GpsReceiverSolutionType.BestN; } else { receiver.ReceiverSolutionType = GpsReceiverSolutionType.AllInView; } // create a new location for the receiver by using the Cartographic type from AGI.Foundation.Coordinates // again, remember to convert from degrees to Radians! (except the height of course) Cartographic position = new Cartographic(Trig.DegreesToRadians(double.Parse(Longitude.Text)), Trig.DegreesToRadians(double.Parse(Latitude.Text)), double.Parse(ReceiverHeight.Text)); // Now create an antenna for the GPS receiver. We specify the location of the antenna by assigning a // PointCartographic instance to the LocationPoint property. We specify that the antenna should be oriented // according to the typically-used East-North-Up axes by assigning an instance of AxesEastNorthUp to // the OrientationAxes property. While the orientation of the antenna won't affect which satellites are visible // or tracked in this case, it will affect the DOP values. For example, the EDOP value can be found in // DilutionOfPrecision.X, but only if we've configured the antenna to use East-North-Up axes. PointCartographic antennaLocationPoint = new PointCartographic(earth, position); Platform antenna = new Platform { LocationPoint = antennaLocationPoint, OrientationAxes = new AxesEastNorthUp(earth, antennaLocationPoint) }; receiver.Antenna = antenna; #endregion // update the tree to reflect the correct receiver info newNode.Nodes.Add(new TreeNode(string.Format(Localization.FixedMaskAngle + "= {0:0.00} " + Localization.degrees, MaskAngle.Text))); newNode.Nodes.Add(new TreeNode(string.Format(Localization.NumberOfChannels + "= {0}", NumberOfChannels.Value))); newNode.Nodes.Add(new TreeNode(string.Format(Localization.SolutionType + "= {0}", receiver.ReceiverSolutionType == GpsReceiverSolutionType.AllInView ? Localization.AllInView : Localization.BestN))); newNode.Nodes.Add(new TreeNode(string.Format(Localization.Latitude + "= {0:0.000000} " + Localization.degrees, Latitude.Text))); newNode.Nodes.Add(new TreeNode(string.Format(Localization.Longitude + "= {0:0.000000} " + Localization.degrees, Longitude.Text))); newNode.Nodes.Add(new TreeNode(string.Format(Localization.Height + "= {0:0.000} " + Localization.meters, ReceiverHeight.Text))); rootNode.Expand(); newNode.Expand(); #region CalculateDataForGraphs // Now, we'll open the almanac SemAlmanac almanac; using (Stream stream = openAlmanacDialog.OpenFile()) using (StreamReader reader = new StreamReader(stream)) { // Read the SEM almanac from an almanac stream reader. almanac = SemAlmanac.ReadFrom(reader, 1); } // Now create a PlatformCollection to hold GpsSatellite object instances. The SemAlmanac method CreateSatellitesFromRecords returns // just such a collection. We'll use this set of satellites as the set from which we'll try and track. There is a // GpsSatellite object for each satellite specified in the almanac. PlatformCollection gpsSatellites = almanac.CreateSatelliteCollection(); // We provide the receiver with the complete set of gpsSatellites to consider for visibility calculations. // This is usually all SVs defined in the almanac - however you may want to remove SVs that aren't healthy. This can // be done by creating the gpsSatellites collection above using another version of the CreateSatellitesFromRecords method that // takes a SatelliteOutageFileReader. receiver.NavigationSatellites = gpsSatellites; // Optimization opportunity: Add the following code in a thread. This will help for long duration analyses. // Now that we have the receiver and location setup, we need to evaluate all the pertinent data. // using a SatelliteTrackingEvaluator, we can track satellites and using a DOP Evaluator, // we can calculate DOP at a specified time. // The receiver's GetSatelliteTrackingEvaluator method will provide a SatelliteTrackingEvaluator for you. // Similarly, the GetDilutionOfPrecisionEvaluator provides the DOP evaluator. // We create all evaluators in the same EvaluatorGroup for the best performance. EvaluatorGroup group = new EvaluatorGroup(); Evaluator <int[]> satTrackingEvaluator = receiver.GetSatelliteTrackingIndexEvaluator(group); Evaluator <DilutionOfPrecision> dopEvaluator = receiver.GetDilutionOfPrecisionEvaluator(group); // We also need to create an evaluator to compute Azimuth/Elevation for each of the SVs MotionEvaluator <AzimuthElevationRange>[] aerEvaluators = new MotionEvaluator <AzimuthElevationRange> [gpsSatellites.Count]; for (int i = 0; i < gpsSatellites.Count; ++i) { Platform satellite = receiver.NavigationSatellites[i]; VectorTrueDisplacement vector = new VectorTrueDisplacement(antenna.LocationPoint, satellite.LocationPoint); aerEvaluators[i] = earth.GetAzimuthElevationRangeEvaluator(vector, group); } // First we'll initialize the data structures used to plot the data for (int i = 0; i < DOPData.Length; i++) { // PointPairList is defined in the ZedGraph reference DOPData[i] = new PointPairList(); } // We need to know which time standard to use here. If the user has specified that GPS time is to be used // we need to create the JulianDates with the GlobalPositioningSystemTime standard. if (GPSTimeRadio.Checked) { startjd = new JulianDate(StartTime.Value, TimeStandard.GlobalPositioningSystemTime); stopjd = new JulianDate(StopTime.Value, TimeStandard.GlobalPositioningSystemTime); } else { // otherwise, the default time standard is UTC startjd = new JulianDate(StartTime.Value); stopjd = new JulianDate(StopTime.Value); } // Now we''ll create the variables we'll need for iterating through time. // The propagator requires a Duration type be used to specify the timestep. Duration dur = stopjd - startjd; double timestep = Double.Parse(TimeStep.Text); // Initialize the progressbar with appropriate values progressBar1.Maximum = (int)dur.TotalSeconds; progressBar1.Step = (int)timestep; // now we'll iterate through time by adding seconds to the start time JulianDate // creating a new JulianDate 'evaluateTime' each time step. for (double t = 0; t <= dur.TotalSeconds; t += timestep) { JulianDate evaluateTime = startjd.AddSeconds(t); // The string 'trackedSVs' is the start of a string we'll continue to build through this time // iteration. It will contain the info we'll need to put in the DOP graph tooltips for the different // DOP series (VDOP, HDOP, etc.) String trackedSVs = Localization.Tracked + ": "; // The evaluator method GetTrackedSatellites will take the current time and the initial list of satellites and // determine which satellites can be tracked based on the receiver constraints setup earlier. This method // returns a PlatformCollection object as well (though we'll cast each member of the Collection to a GPSSatellite type) int[] trackedSatellites = satTrackingEvaluator.Evaluate(evaluateTime); foreach (int satelliteIndex in trackedSatellites) { Platform satellite = receiver.NavigationSatellites[satelliteIndex]; // Now we have access to a Platform object representing a GPS satellite and calculate the azimuth and elevation // of each. Note that we're just calculating the azimuth and elevation, but could just as easily get the // range as well. AzimuthElevationRange aer = aerEvaluators[satelliteIndex].Evaluate(evaluateTime); // Get the GpsSatelliteExtension attached to the platform. The extension extends a // platform with GPS-specific information. In this case, we need the // satellites PRN. GpsSatelliteExtension extension = satellite.Extensions.GetByType <GpsSatelliteExtension>(); // Create two separate PointPairLists to hold the data stored by Time and Azimuth PointPairList thisTimePointList, thisAzPointList; // Before we can arbitrarily create new PointPair Lists, we have to see if the Data Storage structures already contain a list // for this PRN. // The variables AzElData_TimeBased and AzElData_AzimuthBased are dictionaries that hold the PointPairLists using the PRN // as a key. We use this structure to store a large amount of data for every satellite in a single, easy to access, variable. // if the satellite we're currently looking at already has a list defined in the dictionary, we'll use that one, otherwise // we'll create a new list if (AzElData_TimeBased.ContainsKey(extension.PseudoRandomNumber)) { thisTimePointList = AzElData_TimeBased[extension.PseudoRandomNumber]; AzElData_TimeBased.Remove(extension.PseudoRandomNumber); } else { thisTimePointList = new PointPairList(); } if (AzElData_AzimuthBased.ContainsKey(extension.PseudoRandomNumber)) { thisAzPointList = AzElData_AzimuthBased[extension.PseudoRandomNumber]; AzElData_AzimuthBased.Remove(extension.PseudoRandomNumber); } else { thisAzPointList = new PointPairList(); } // Now to get the actual Azimuth and elevation data // Converting your Radians to degrees here makes the data appear in a more readable format. We also constrain the azimuth // to be within the interval [0, 2*pi] double azimuth = Trig.RadiansToDegrees(Trig.ZeroToTwoPi(aer.Azimuth)); double elevation = Trig.RadiansToDegrees(aer.Elevation); #endregion // now create the point for the Azimuth based data PointPair thisAzPoint = new PointPair(azimuth, elevation); // and add the tooltip (ZedGraph uses the Tag property on a PointPair for the tooltip on that datapoint ) thisAzPoint.Tag = String.Format("PRN {0}, {1}, " + Localization.Az + ": {2:0.000}, " + Localization.El + ": {3:0.000}", extension.PseudoRandomNumber, evaluateTime.ToDateTime().ToString("M/d/yyyy, h:mm tt"), azimuth, elevation); // and finally add this point to the list thisAzPointList.Add(thisAzPoint); // now we'll do the same for the time-based data, instead of adding the Az and El, we'll add the time and El. // Create a new XDate object to store the time for this point double txd = (double)new XDate(evaluateTime.ToDateTime()); // add the time and elevation data to this point PointPair thisTimePoint = new PointPair(txd, Trig.RadiansToDegrees(aer.Elevation)); // Create the tooltip tag thisTimePoint.Tag = String.Format("PRN {0}, {1}, " + Localization.Az + ": {2:0.000}, " + Localization.El + ": {3:0.000}", extension.PseudoRandomNumber, evaluateTime.ToDateTime().ToString("M/d/yyyy, h:mm tt"), azimuth, elevation); // finally add this point to the list thisTimePointList.Add(thisTimePoint); //Now that this data is all calculated, we'll add the point lists to the correct data structures for the GpsSatellite we're working with AzElData_TimeBased.Add(extension.PseudoRandomNumber, thisTimePointList); AzElData_AzimuthBased.Add(extension.PseudoRandomNumber, thisAzPointList); // now update the 'trackedSVs' string to be used for the DOP data tooltip // wee need to do this inside the GpsSatellite loop because we need to get the entire list of tracked SVs for this time step. // we won't use this string until we're out of the loop however. trackedSVs += extension.PseudoRandomNumber.ToString() + ", "; } // now we're out of the GpsSatellite loop, we'll do some string manipulation to get the tooltip for the DOP data. // (gets rid of the last inserted comma) string svs = trackedSVs.Substring(0, trackedSVs.LastIndexOf(' ') - 1); try { // Now we use the evaluator to calculate the DilutionOfPrecision for us for this timestep DilutionOfPrecision dop = dopEvaluator.Evaluate(evaluateTime); // if the dop object throws an exception, there aren't enough tracked satellites to form a navigation solution (typically < 4 tracked) // in that case we leave the data for this time unfilled. The graph will then have empty spots for this time. // Here we create a new PointPair and a new XDate to add to the X-Axis PointPair pp; double txd = (double)new XDate(evaluateTime.ToDateTime()); // add the East DOP value and the time to the PointPair and set the tooltip tag property for this series. pp = new PointPair(txd, dop.X); pp.Tag = String.Format("{0}\n{1} " + Localization.EDOP + ": {2:0.000}", svs, evaluateTime.ToDateTime().ToString("M/d/yyyy, h:mm tt"), dop.X); // add the point to the 0th element of the DOPData structure DOPData[0].Add(pp); // repeat for North DOP pp = new PointPair(txd, dop.Y); pp.Tag = String.Format("{0}\n{1} " + Localization.NDOP + ": {2:0.000}", svs, evaluateTime.ToDateTime().ToString("M/d/yyyy, h:mm tt"), dop.Y); DOPData[1].Add(pp); // repeat for the Vertical DOP pp = new PointPair(txd, dop.Z); pp.Tag = String.Format("{0}\n{1} " + Localization.VDOP + ": {2:0.000}", svs, evaluateTime.ToDateTime().ToString("M/d/yyyy, h:mm tt"), dop.Z); DOPData[2].Add(pp); // repeat for the Horizontal DOP pp = new PointPair(txd, dop.XY); pp.Tag = String.Format("{0}\n{1} " + Localization.HDOP + ": {2:0.000}", svs, evaluateTime.ToDateTime().ToString("M/d/yyyy, h:mm tt"), dop.XY); DOPData[3].Add(pp); // repeat for the Position DOP pp = new PointPair(txd, dop.Position); pp.Tag = String.Format("{0}\n{1} " + Localization.PDOP + ": {2:0.000}", svs, evaluateTime.ToDateTime().ToString("M/d/yyyy, h:mm tt"), dop.Position); DOPData[4].Add(pp); // repeat for the Time DOP pp = new PointPair(txd, dop.Time); pp.Tag = String.Format("{0}\n{1} " + Localization.TDOP + ": {2:0.000}", svs, evaluateTime.ToDateTime().ToString("M/d/yyyy, h:mm tt"), dop.Time); DOPData[5].Add(pp); // repeat for the Geometric DOP pp = new PointPair(txd, dop.Geometric); pp.Tag = String.Format("{0}\n{1} " + Localization.GDOP + ": {2:0.000}", svs, evaluateTime.ToDateTime().ToString("M/d/yyyy, h:mm tt"), dop.Geometric); DOPData[6].Add(pp); // Notice here that the different DOP values (East, North, etc) were denoted by the dop.X, dop.Y etc. This is because the // DOP values could be in any coordinate system. In our case, we're in the ENU coordinate system an X represents East, Y // represents North, Z represents Vertical, XY represents horizontal. You can change the reference frame the DOP is reported in // but you will then have to understand that the dop.X value corresponds to your X-defined axis and so on. } catch { // Do Nothing here - we just won't add the data to the data list } // update the progress bar - we're done with this time step! progressBar1.PerformStep(); } // finally update the graphs UpdateDopGraph(); updateAzElGraph(); // reset the progress bar progressBar1.Value = 0; // and set the appropriate button states SetControlStates(true); }