// Path: GNSSLogger/pseudorange/src/main/java/com/google/location/lbs/gnss/gps/pseudorange/Ecef2LlaConverter.java
// public static class GeodeticLlaValues {
// 
//   public final double latitudeRadians;
//   public final double longitudeRadians;
//   public final double altitudeMeters;
// 
//   public GeodeticLlaValues(double latitudeRadians,
//       double longitudeRadians, double altitudeMeters) {
//     this.latitudeRadians = latitudeRadians;
//     this.longitudeRadians = longitudeRadians;
//     this.altitudeMeters = altitudeMeters;
//   }
// }
// 
// Path: GNSSLogger/pseudorange/src/main/java/com/google/location/lbs/gnss/gps/pseudorange/EcefToTopocentricConverter.java
// public static class TopocentricAEDValues {
// 
//   public final double elevationRadians;
//   public final double azimuthRadians;
//   public final double distanceMeters;
// 
//   public TopocentricAEDValues(double elevationRadians, double azimuthRadians,
//       double distanceMeters) {
//     this.elevationRadians = elevationRadians;
//     this.azimuthRadians = azimuthRadians;
//     this.distanceMeters = distanceMeters;
//   }
// }


import com.google.location.lbs.gnss.gps.pseudorange.Ecef2LlaConverter.GeodeticLlaValues;
import com.google.location.lbs.gnss.gps.pseudorange.EcefToTopocentricConverter.TopocentricAEDValues;
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.location.lbs.gnss.gps.pseudorange;


/**
 * Calculates the Ionospheric correction of the pseudorange given the {@code userPosition},
 * {@code satellitePosition}, {@code gpsTimeSeconds} and the ionospheric parameters sent by the
 * satellite {@code alpha} and {@code beta}
 *
 * <p>Source: http://www.navipedia.net/index.php/Klobuchar_Ionospheric_Model and
 * http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=4104345 and
 * http://www.ion.org/museum/files/ACF2A4.pdf
 */
public class IonosphericModel {
  /** Center frequency of the L1 band in Hz. */
  public static final double L1_FREQ_HZ = 10.23 * 1e6 * 154;
  /** Center frequency of the L2 band in Hz. */
  public static final double L2_FREQ_HZ = 10.23 * 1e6 * 120;
  /** Center frequency of the L5 band in Hz. */
  public static final double L5_FREQ_HZ = 10.23 * 1e6 * 115;

  private static final double SECONDS_PER_DAY = 86400.0;
  private static final double PERIOD_OF_DELAY_THRESHOLD_SECONDS = 72000.0;
  private static final double IPP_LATITUDE_THRESHOLD_SEMI_CIRCLE = 0.416;
  private static final double DC_TERM = 5.0e-9;
  private static final double NORTH_GEOMAGNETIC_POLE_LONGITUDE_RADIANS = 5.08;
  private static final double GEOMETRIC_LATITUDE_CONSTANT = 0.064;
  private static final int DELAY_PHASE_TIME_CONSTANT_SECONDS = 50400;
  private static final int IONO_0_IDX = 0;
  private static final int IONO_1_IDX = 1;
  private static final int IONO_2_IDX = 2;
  private static final int IONO_3_IDX = 3;

  /**
   * Calculates the Ionospheric correction of the pseudorange in seconds using the Klobuchar
   * Ionospheric model.
   */
  public static double ionoKlobucharCorrectionSeconds(
      double[] userPositionECEFMeters,
      double[] satellitePositionECEFMeters,
      double gpsTOWSeconds,
      double[] alpha,
      double[] beta,
      double frequencyHz) {

    TopocentricAEDValues elevationAndAzimuthRadians = EcefToTopocentricConverter
        .calculateElAzDistBetween2Points(userPositionECEFMeters, satellitePositionECEFMeters);
    double elevationSemiCircle = elevationAndAzimuthRadians.elevationRadians / Math.PI;
    double azimuthSemiCircle = elevationAndAzimuthRadians.azimuthRadians / Math.PI;
    GeodeticLlaValues latLngAlt = Ecef2LlaConverter.convertECEFToLLACloseForm(
        userPositionECEFMeters[0], userPositionECEFMeters[1], userPositionECEFMeters[2]);
    double latitudeUSemiCircle = latLngAlt.latitudeRadians / Math.PI;
    double longitudeUSemiCircle = latLngAlt.longitudeRadians / Math.PI;

    // earth's centered angle (semi-circles)
    double earthCentredAngleSemiCircle = 0.0137 / (elevationSemiCircle + 0.11) - 0.022;

    // latitude of the Ionospheric Pierce Point (IPP) (semi-circles)
// Path: GNSSLogger/pseudorange/src/main/java/com/google/location/lbs/gnss/gps/pseudorange/Ecef2LlaConverter.java
// public static class GeodeticLlaValues {
// 
//   public final double latitudeRadians;
//   public final double longitudeRadians;
//   public final double altitudeMeters;
// 
//   public GeodeticLlaValues(double latitudeRadians,
//       double longitudeRadians, double altitudeMeters) {
//     this.latitudeRadians = latitudeRadians;
//     this.longitudeRadians = longitudeRadians;
//     this.altitudeMeters = altitudeMeters;
//   }
// }
// 
// Path: GNSSLogger/pseudorange/src/main/java/com/google/location/lbs/gnss/gps/pseudorange/EcefToTopocentricConverter.java
// public static class TopocentricAEDValues {
// 
//   public final double elevationRadians;
//   public final double azimuthRadians;
//   public final double distanceMeters;
// 
//   public TopocentricAEDValues(double elevationRadians, double azimuthRadians,
//       double distanceMeters) {
//     this.elevationRadians = elevationRadians;
//     this.azimuthRadians = azimuthRadians;
//     this.distanceMeters = distanceMeters;
//   }
// }


// Path: GNSSLogger/pseudorange/src/main/java/com/google/location/lbs/gnss/gps/pseudorange/IonosphericModel.java
import com.google.location.lbs.gnss.gps.pseudorange.Ecef2LlaConverter.GeodeticLlaValues;
import com.google.location.lbs.gnss.gps.pseudorange.EcefToTopocentricConverter.TopocentricAEDValues;

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.location.lbs.gnss.gps.pseudorange;


/**
 * Calculates the Ionospheric correction of the pseudorange given the {@code userPosition},
 * {@code satellitePosition}, {@code gpsTimeSeconds} and the ionospheric parameters sent by the
 * satellite {@code alpha} and {@code beta}
 *
 * <p>Source: http://www.navipedia.net/index.php/Klobuchar_Ionospheric_Model and
 * http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=4104345 and
 * http://www.ion.org/museum/files/ACF2A4.pdf
 */
public class IonosphericModel {
  /** Center frequency of the L1 band in Hz. */
  public static final double L1_FREQ_HZ = 10.23 * 1e6 * 154;
  /** Center frequency of the L2 band in Hz. */
  public static final double L2_FREQ_HZ = 10.23 * 1e6 * 120;
  /** Center frequency of the L5 band in Hz. */
  public static final double L5_FREQ_HZ = 10.23 * 1e6 * 115;

  private static final double SECONDS_PER_DAY = 86400.0;
  private static final double PERIOD_OF_DELAY_THRESHOLD_SECONDS = 72000.0;
  private static final double IPP_LATITUDE_THRESHOLD_SEMI_CIRCLE = 0.416;
  private static final double DC_TERM = 5.0e-9;
  private static final double NORTH_GEOMAGNETIC_POLE_LONGITUDE_RADIANS = 5.08;
  private static final double GEOMETRIC_LATITUDE_CONSTANT = 0.064;
  private static final int DELAY_PHASE_TIME_CONSTANT_SECONDS = 50400;
  private static final int IONO_0_IDX = 0;
  private static final int IONO_1_IDX = 1;
  private static final int IONO_2_IDX = 2;
  private static final int IONO_3_IDX = 3;

  /**
   * Calculates the Ionospheric correction of the pseudorange in seconds using the Klobuchar
   * Ionospheric model.
   */
  public static double ionoKlobucharCorrectionSeconds(
      double[] userPositionECEFMeters,
      double[] satellitePositionECEFMeters,
      double gpsTOWSeconds,
      double[] alpha,
      double[] beta,
      double frequencyHz) {

    TopocentricAEDValues elevationAndAzimuthRadians = EcefToTopocentricConverter
        .calculateElAzDistBetween2Points(userPositionECEFMeters, satellitePositionECEFMeters);
    double elevationSemiCircle = elevationAndAzimuthRadians.elevationRadians / Math.PI;
    double azimuthSemiCircle = elevationAndAzimuthRadians.azimuthRadians / Math.PI;
    GeodeticLlaValues latLngAlt = Ecef2LlaConverter.convertECEFToLLACloseForm(
        userPositionECEFMeters[0], userPositionECEFMeters[1], userPositionECEFMeters[2]);
    double latitudeUSemiCircle = latLngAlt.latitudeRadians / Math.PI;
    double longitudeUSemiCircle = latLngAlt.longitudeRadians / Math.PI;

    // earth's centered angle (semi-circles)
    double earthCentredAngleSemiCircle = 0.0137 / (elevationSemiCircle + 0.11) - 0.022;

    // latitude of the Ionospheric Pierce Point (IPP) (semi-circles)
    double latitudeISemiCircle =