Home | History | Annotate | Download | only in suplClient
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.location.cts.suplClient;
     18 
     19 import android.location.cts.asn1.supl2.rrlp_components.IonosphericModel;
     20 import android.location.cts.asn1.supl2.rrlp_components.NavModelElement;
     21 import android.location.cts.asn1.supl2.rrlp_components.NavigationModel;
     22 import android.location.cts.asn1.supl2.rrlp_components.SatStatus;
     23 import android.location.cts.asn1.supl2.rrlp_components.UncompressedEphemeris;
     24 import android.location.cts.asn1.supl2.rrlp_messages.PDU;
     25 import android.location.cts.asn1.supl2.supl_pos.PosPayLoad;
     26 import android.location.cts.asn1.supl2.ulp.ULP_PDU;
     27 import android.location.cts.asn1.supl2.ulp.UlpMessage;
     28 import android.location.cts.asn1.supl2.ulp_components.SessionID;
     29 import android.location.cts.nano.Ephemeris.GpsEphemerisProto;
     30 import android.location.cts.nano.Ephemeris.GpsNavMessageProto;
     31 import android.location.cts.nano.Ephemeris.IonosphericModelProto;
     32 import java.io.IOException;
     33 import java.net.UnknownHostException;
     34 import java.util.ArrayList;
     35 import java.util.concurrent.TimeUnit;
     36 
     37 /**
     38  * A class that applies the SUPL protocol call flow to obtain GPS assistance data over a TCP
     39  * connection.
     40  *
     41  * <p>A rough location of the receiver has to be known in advance which is passed to the method
     42  * #generateNavMessage to obtain a GpsNavMessageProto containing the GPS assistance
     43  * data.
     44  *
     45  * <p>The SUPL protocol flaw is made over a TCP socket to a server specified by SUPL_SERVER_NAME
     46  * at port SUPL_SERVER_PORT.
     47  */
     48 public class SuplRrlpController {
     49   // Details of the following constants can be found in hte IS-GPS-200F which can be found at:
     50   // http://www.navcen.uscg.gov/pdf/is-gps-200f.pdf
     51   private static final double NAVIGATION_TGD_SCALE_FACTOR = Math.pow(2, -31);
     52   private static final double NAVIGATION_TOC_SCALE_FACTOR = Math.pow(2, 4);
     53   private static final double NAVIGATION_AF2_SCALE_FACTOR = Math.pow(2, -55);
     54   private static final double NAVIGATION_AF1_SCALE_FACTOR = Math.pow(2, -43);
     55   private static final double NAVIGATION_AF0_SCALE_FACTOR = Math.pow(2, -31);
     56   private static final double NAVIGATION_CRS_SCALE_FACTOR = Math.pow(2, -5);
     57   private static final double NAVIGATION_DELTA_N_SCALE_FACTOR = Math.pow(2, -43) * Math.PI;
     58   private static final double NAVIGATION_M0_SCALE_FACTOR = Math.pow(2, -31) * Math.PI;
     59   private static final double NAVIGATION_CUC_SCALE_FACTOR = Math.pow(2, -29);
     60   private static final double NAVIGATION_E_SCALE_FACTOR = Math.pow(2, -33);
     61   private static final double NAVIGATION_CUS_SCALE_FACTOR = Math.pow(2, -29);
     62   private static final double NAVIGATION_A_POWER_HALF_SCALE_FACTOR = Math.pow(2, -19);
     63   private static final double NAVIGATION_TOE_SCALE_FACTOR = Math.pow(2, 4);
     64   private static final double NAVIGATION_CIC_SCALE_FACTOR = Math.pow(2, -29);
     65   private static final double NAVIGATION_OMEGA0_SCALE_FACTOR = Math.pow(2, -31) * Math.PI;
     66   private static final double NAVIGATION_CIS_SCALE_FACTOR = Math.pow(2, -29);
     67   private static final double NAVIGATION_I0_SCALE_FACTOR = Math.pow(2, -31) * Math.PI;
     68   private static final double NAVIGATION_CRC_SCALE_FACTOR = Math.pow(2, -5);
     69   private static final double NAVIGATION_W_SCALE_FACTOR = Math.pow(2, -31) * Math.PI;
     70   private static final double NAVIGATION_OMEGA_A_DOT_SCALE_FACTOR = Math.pow(2, -43) * Math.PI;
     71   private static final double NAVIGATION_I_DOT_SCALE_FACTOR = Math.pow(2, -43) * Math.PI;
     72   private static final double IONOSPHERIC_ALFA_0_SCALE_FACTOR = Math.pow(2, -30);
     73   private static final double IONOSPHERIC_ALFA_1_SCALE_FACTOR = Math.pow(2, -27);
     74   private static final double IONOSPHERIC_ALFA_2_SCALE_FACTOR = Math.pow(2, -24);
     75   private static final double IONOSPHERIC_ALFA_3_SCALE_FACTOR = Math.pow(2, -24);
     76   private static final double IONOSPHERIC_BETA_0_SCALE_FACTOR = Math.pow(2, 11);
     77   private static final double IONOSPHERIC_BETA_1_SCALE_FACTOR = Math.pow(2, 14);
     78   private static final double IONOSPHERIC_BETA_2_SCALE_FACTOR = Math.pow(2, 16);
     79   private static final double IONOSPHERIC_BETA_3_SCALE_FACTOR = Math.pow(2, 16);
     80 
     81   // 3657 is the number of days between the unix epoch and GPS epoch as the GPS epoch started on
     82   // Jan 6, 1980
     83   private static final long GPS_EPOCH_AS_UNIX_EPOCH_MS = TimeUnit.DAYS.toMillis(3657);
     84   // A GPS Cycle is 1024 weeks, or 7168 days
     85   private static final long GPS_CYCLE_MS = TimeUnit.DAYS.toMillis(7168);
     86   private static final int GPS_CYCLE_WEEKS = 1024;
     87 
     88   private final String suplServerName;
     89   private final int suplServerPort;
     90 
     91   public SuplRrlpController(String suplServerName, int suplServerPort) {
     92     this.suplServerName = suplServerName;
     93     this.suplServerPort = suplServerPort;
     94   }
     95 
     96   /**
     97    * Applies the SUPL protocol call flaw to obtain the assistance data and store the result in a
     98    * GpsNavMessageProto
     99    */
    100   public GpsNavMessageProto generateNavMessage(long latE7, long lngE7)
    101       throws UnknownHostException, IOException {
    102     // Establishes a TCP socket that is used to send and receive SUPL messages
    103     SuplTcpClient tcpClient = new SuplTcpClient(suplServerName, suplServerPort);
    104 
    105     // Send a SUPL START message from the client to server
    106     byte[] suplStartMessage = SuplRrlpMessagesGenerator.generateSuplStartLocalLocationMessage(null);
    107     tcpClient.sendSuplRequest(suplStartMessage);
    108     // Receive a SUPL RESPONSE from the server and obtain the Session ID send by the server
    109     byte[] response = tcpClient.getSuplResponse();
    110     if (response == null) {
    111       return new GpsNavMessageProto();
    112     }
    113     ULP_PDU decodedMessage = ULP_PDU.fromPerUnaligned(response);
    114 
    115     if (!decodedMessage.getMessage().isMsSUPLRESPONSE()) {
    116       return new GpsNavMessageProto();
    117     }
    118     SessionID sessionId = decodedMessage.getSessionID();
    119 
    120     // Send a SUPL POS INIT message from the client to the server requesting GPS assistance data
    121     // for the location specified by the given latitude and longitude
    122     byte[] suplPosInitMessage = SuplRrlpMessagesGenerator
    123         .generateSuplPositionInitLocalLocationMessage(sessionId, latE7, lngE7);
    124     tcpClient.sendSuplRequest(suplPosInitMessage);
    125 
    126     // Receive a SUPL POS message from the server containing all the assitance data requested
    127     response = tcpClient.getSuplResponse();
    128     if (response == null) {
    129       return new GpsNavMessageProto();
    130     }
    131     decodedMessage = ULP_PDU.fromPerUnaligned(response);
    132 
    133     if (!decodedMessage.getMessage().isMsSUPLPOS()) {
    134       return new GpsNavMessageProto();
    135     }
    136     // build a NavMessageProto out of the received decoded payload from the SUPL server
    137     GpsNavMessageProto navMessageProto = buildNavMessageProto(decodedMessage);
    138 
    139     tcpClient.closeSocket();
    140 
    141     return navMessageProto;
    142   }
    143 
    144   /** Fills GpsNavMessageProto with the assistance data obtained in ULP_PDU */
    145   private GpsNavMessageProto buildNavMessageProto(ULP_PDU decodedMessage) {
    146     UlpMessage message = decodedMessage.getMessage();
    147 
    148     PosPayLoad.rrlpPayloadType rrlpPayload =
    149         message.getMsSUPLPOS().getPosPayLoad().getRrlpPayload();
    150     PDU pdu = PDU.fromPerUnaligned(rrlpPayload.getValue());
    151     IonosphericModel ionoModel = pdu.getComponent().getAssistanceData().getGps_AssistData()
    152         .getControlHeader().getIonosphericModel();
    153     NavigationModel navModel = pdu.getComponent().getAssistanceData().getGps_AssistData()
    154         .getControlHeader().getNavigationModel();
    155     int gpsWeek = pdu.getComponent().getAssistanceData().getGps_AssistData().getControlHeader()
    156         .getReferenceTime().getGpsTime().getGpsWeek().getInteger().intValue();
    157     gpsWeek = getGpsWeekWithRollover(gpsWeek);
    158     Iterable<NavModelElement> navModelElements = navModel.getNavModelList().getValues();
    159 
    160     GpsNavMessageProto gpsNavMessageProto = new GpsNavMessageProto();
    161     gpsNavMessageProto.rpcStatus = GpsNavMessageProto.UNKNOWN_RPC_STATUS;
    162 
    163     // Set Iono Model.
    164     IonosphericModelProto ionosphericModelProto = new IonosphericModelProto();
    165     double[] alpha = new double[4];
    166     alpha[0] = ionoModel.getAlfa0().getInteger().byteValue() * IONOSPHERIC_ALFA_0_SCALE_FACTOR;
    167     alpha[1] = ionoModel.getAlfa1().getInteger().byteValue() * IONOSPHERIC_ALFA_1_SCALE_FACTOR;
    168     alpha[2] = ionoModel.getAlfa2().getInteger().byteValue() * IONOSPHERIC_ALFA_2_SCALE_FACTOR;
    169     alpha[3] = ionoModel.getAlfa3().getInteger().byteValue() * IONOSPHERIC_ALFA_3_SCALE_FACTOR;
    170     ionosphericModelProto.alpha = alpha;
    171 
    172     double[] beta = new double[4];
    173     beta[0] = ionoModel.getBeta0().getInteger().byteValue() * IONOSPHERIC_BETA_0_SCALE_FACTOR;
    174     beta[1] = ionoModel.getBeta1().getInteger().byteValue() * IONOSPHERIC_BETA_1_SCALE_FACTOR;
    175     beta[2] = ionoModel.getBeta2().getInteger().byteValue() * IONOSPHERIC_BETA_2_SCALE_FACTOR;
    176     beta[3] = ionoModel.getBeta3().getInteger().byteValue() * IONOSPHERIC_BETA_3_SCALE_FACTOR;
    177     ionosphericModelProto.beta = beta;
    178 
    179     gpsNavMessageProto.iono = ionosphericModelProto;
    180 
    181     ArrayList<GpsEphemerisProto> ephemerisList = new ArrayList<>();
    182     for (NavModelElement navModelElement : navModelElements) {
    183       int satID = navModelElement.getSatelliteID().getInteger().intValue();
    184       SatStatus satStatus = navModelElement.getSatStatus();
    185       UncompressedEphemeris ephemeris = satStatus.getNewSatelliteAndModelUC();
    186 
    187       GpsEphemerisProto gpsEphemerisProto = new GpsEphemerisProto();
    188       toSingleEphemeris(satID, gpsWeek, ephemeris, gpsEphemerisProto);
    189       ephemerisList.add(gpsEphemerisProto);
    190     }
    191 
    192     gpsNavMessageProto.ephemerids =
    193         ephemerisList.toArray(new GpsEphemerisProto[ephemerisList.size()]);
    194     gpsNavMessageProto.rpcStatus = GpsNavMessageProto.SUCCESS;
    195 
    196     return gpsNavMessageProto;
    197   }
    198 
    199   /**
    200    * Calculates the GPS week with rollovers. A rollover happens every 1024 weeks, beginning from GPS
    201    * epoch (January 6, 1980).
    202    *
    203    * @param gpsWeek The modulo-1024 GPS week.
    204    *
    205    * @return The absolute GPS week.
    206    */
    207   private int getGpsWeekWithRollover(int gpsWeek) {
    208     long nowMs = System.currentTimeMillis();
    209     long elapsedTimeFromGpsEpochMs = nowMs - GPS_EPOCH_AS_UNIX_EPOCH_MS;
    210     long rolloverCycles = elapsedTimeFromGpsEpochMs / GPS_CYCLE_MS;
    211     int rolloverWeeks = (int) rolloverCycles * GPS_CYCLE_WEEKS;
    212     return gpsWeek + rolloverWeeks;
    213   }
    214 
    215   /**
    216    * Fills GpsEphemerisProto with the assistance data obtained in UncompressedEphemeris for the
    217    * given satellite id.
    218    */
    219   private void toSingleEphemeris(
    220       int satId, int gpsWeek, UncompressedEphemeris ephemeris,
    221       GpsEphemerisProto gpsEphemerisProto) {
    222 
    223     gpsEphemerisProto.prn = satId + 1;
    224     gpsEphemerisProto.week = gpsWeek;
    225     gpsEphemerisProto.l2Code = ephemeris.getEphemCodeOnL2().getInteger().intValue();
    226     gpsEphemerisProto.l2Flag = ephemeris.getEphemL2Pflag().getInteger().intValue();
    227     gpsEphemerisProto.svHealth = ephemeris.getEphemSVhealth().getInteger().intValue();
    228 
    229     gpsEphemerisProto.iode = ephemeris.getEphemIODC().getInteger().intValue();
    230     gpsEphemerisProto.iodc = ephemeris.getEphemIODC().getInteger().intValue();
    231     gpsEphemerisProto.toc = ephemeris.getEphemToc().getInteger().intValue() * NAVIGATION_TOC_SCALE_FACTOR;
    232     gpsEphemerisProto.toe = ephemeris.getEphemToe().getInteger().intValue() * NAVIGATION_TOE_SCALE_FACTOR;
    233     gpsEphemerisProto.af0 = ephemeris.getEphemAF0().getInteger().intValue() * NAVIGATION_AF0_SCALE_FACTOR;
    234     gpsEphemerisProto.af1 = ephemeris.getEphemAF1().getInteger().shortValue() * NAVIGATION_AF1_SCALE_FACTOR;
    235     gpsEphemerisProto.af2 = ephemeris.getEphemAF2().getInteger().byteValue() * NAVIGATION_AF2_SCALE_FACTOR;
    236     gpsEphemerisProto.tgd = ephemeris.getEphemTgd().getInteger().byteValue() * NAVIGATION_TGD_SCALE_FACTOR;
    237     gpsEphemerisProto.rootOfA = ephemeris.getEphemAPowerHalf().getInteger().longValue()
    238         * NAVIGATION_A_POWER_HALF_SCALE_FACTOR;
    239 
    240     gpsEphemerisProto.e = ephemeris.getEphemE().getInteger().longValue() * NAVIGATION_E_SCALE_FACTOR;
    241     gpsEphemerisProto.i0 = ephemeris.getEphemI0().getInteger().intValue() * NAVIGATION_I0_SCALE_FACTOR;
    242     gpsEphemerisProto.iDot = ephemeris.getEphemIDot().getInteger().intValue() * NAVIGATION_I_DOT_SCALE_FACTOR;
    243     gpsEphemerisProto.omega = ephemeris.getEphemW().getInteger().intValue() * NAVIGATION_W_SCALE_FACTOR;
    244     gpsEphemerisProto.omega0 = ephemeris.getEphemOmegaA0().getInteger().intValue() * NAVIGATION_OMEGA0_SCALE_FACTOR;
    245     gpsEphemerisProto.omegaDot = ephemeris.getEphemOmegaADot().getInteger().intValue()
    246         * NAVIGATION_OMEGA_A_DOT_SCALE_FACTOR;
    247     gpsEphemerisProto.m0 = ephemeris.getEphemM0().getInteger().intValue() * NAVIGATION_M0_SCALE_FACTOR;
    248     gpsEphemerisProto.deltaN = ephemeris.getEphemDeltaN().getInteger().shortValue() * NAVIGATION_DELTA_N_SCALE_FACTOR;
    249     gpsEphemerisProto.crc = ephemeris.getEphemCrc().getInteger().shortValue() * NAVIGATION_CRC_SCALE_FACTOR;
    250     gpsEphemerisProto.crs = ephemeris.getEphemCrs().getInteger().shortValue() * NAVIGATION_CRS_SCALE_FACTOR;
    251     gpsEphemerisProto.cuc = ephemeris.getEphemCuc().getInteger().shortValue() * NAVIGATION_CUC_SCALE_FACTOR;
    252     gpsEphemerisProto.cus = ephemeris.getEphemCus().getInteger().shortValue() * NAVIGATION_CUS_SCALE_FACTOR;
    253     gpsEphemerisProto.cic = ephemeris.getEphemCic().getInteger().shortValue() * NAVIGATION_CIC_SCALE_FACTOR;
    254     gpsEphemerisProto.cis = ephemeris.getEphemCis().getInteger().shortValue() * NAVIGATION_CIS_SCALE_FACTOR;
    255 
    256   }
    257 
    258 }
    259