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