summaryrefslogtreecommitdiffstats
path: root/rrlpd
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2012-07-18 22:04:09 +0200
committerHarald Welte <laforge@gnumonks.org>2012-07-18 22:22:47 +0200
commit47ab210af93a909e5eb8168d1ff58d2401519570 (patch)
treedad92939b7a5aab639a2aaae367ba3ef191979be /rrlpd
parenta5c8d8068840bbc216c132d7f8b42b3167c43b9f (diff)
import Dieter's rrlpd based on Sylvain's code from August 10, 2011HEADmaster
Diffstat (limited to 'rrlpd')
-rw-r--r--rrlpd/README46
-rw-r--r--rrlpd/patches_OpenBSC/diff_gsm_04_0827
-rw-r--r--rrlpd/patches_OpenBSC/diff_rrlp289
-rw-r--r--rrlpd/patches_asn1c/01_fix_per_encoding_dieter.diff17
-rw-r--r--rrlpd/src/Makefile41
-rw-r--r--rrlpd/src/asn1/rrlp.asn1055
-rw-r--r--rrlpd/src/gps.c131
-rw-r--r--rrlpd/src/gps.h193
-rw-r--r--rrlpd/src/main.c1298
-rw-r--r--rrlpd/src/rrlp.c864
-rw-r--r--rrlpd/src/rrlp.h65
-rw-r--r--rrlpd/src/ubx-parse.c246
-rw-r--r--rrlpd/src/ubx-parse.h45
-rw-r--r--rrlpd/src/ubx.c96
-rw-r--r--rrlpd/src/ubx.h240
15 files changed, 4653 insertions, 0 deletions
diff --git a/rrlpd/README b/rrlpd/README
new file mode 100644
index 0000000..d49748c
--- /dev/null
+++ b/rrlpd/README
@@ -0,0 +1,46 @@
+RRLP Server
+------------
+
+- Adjust ASN1C paths in the makefile (maybe adjust the makefile too)
+
+- ** VERY IMPORTANT **: apply ASN1C patch, otherwise invalid PDUs will
+ be generated
+
+- Patches for OpenBSC are "TODO"
+
+- Adjust IP Address of RRLP Server in OpenBSC rrlp.c (TODO: ajust code to
+ get this setting from config file)
+
+- Requires a u-Blox GPS receiver. The receiver is supposed to be connected
+ over its USB port. Some changes are problably required if the reciver is
+ connected through its UART port (see "#define GPS_USB ..." in main.c)
+
+- To work properly, the GPS receiver should already have a GPS fix
+
+- Enable RRLP in OpenBSC config file ("rrlp mode ms-based")
+
+- How to run: (Parameter: interface IP Address where to listen,
+ GPS receiver port), e.g.:
+
+ ./rrlp-serv 192.168.1.1 /dev/ttyS0
+
+
+Issues:
+
+ - "Work in Progress": code not yet properly organized and cleaned up
+
+ - very verbose output for debugging/testing
+
+ - rrlp.c: find out if data channel is slow (SDCCH) so that long assistance
+ data will not be sent
+
+ - send an RRLP request not just when paging a phone
+
+ - paging: sometimes no RRLP response is reveicved !?
+
+ - Location update: response of the phone got lost, channel is closed too
+ early !?
+
+ - GPS reference time: do we need an offset so that the time is correct when
+ the phone receives it ?
+
diff --git a/rrlpd/patches_OpenBSC/diff_gsm_04_08 b/rrlpd/patches_OpenBSC/diff_gsm_04_08
new file mode 100644
index 0000000..4ccd003
--- /dev/null
+++ b/rrlpd/patches_OpenBSC/diff_gsm_04_08
@@ -0,0 +1,27 @@
+--- libmsc/gsm_04_08.c Mon Jul 18 11:19:21 2011
++++ r:libmsc/gsm_04_08.c Tue Aug 09 14:34:30 2011
+@@ -1136,16 +1135,24 @@
+ static int gsm48_rx_rr_app_info(struct gsm_subscriber_connection *conn, struct msgb *msg)
+ {
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ u_int8_t apdu_id_flags;
+ u_int8_t apdu_len;
+ u_int8_t *apdu_data;
+
+ apdu_id_flags = gh->data[0];
+ apdu_len = gh->data[1];
+ apdu_data = gh->data+2;
+
+ DEBUGP(DNM, "RX APPLICATION INFO id/flags=0x%02x apdu_len=%u apdu=%s",
+ apdu_id_flags, apdu_len, osmo_hexdump(apdu_data, apdu_len));
++
++#if 1 /* RRLP Server */
++ if(apdu_id_flags == 0x00) { /* RRLP */
++ extern int handle_rrlp(struct gsm_subscriber_connection *conn, uint8_t *data, int len);
++
++ handle_rrlp(conn, apdu_data, apdu_len);
++ }
++#endif
+
+ return db_apdu_blob_store(conn->subscr, apdu_id_flags, apdu_len, apdu_data);
+ }
diff --git a/rrlpd/patches_OpenBSC/diff_rrlp b/rrlpd/patches_OpenBSC/diff_rrlp
new file mode 100644
index 0000000..6de751a
--- /dev/null
+++ b/rrlpd/patches_OpenBSC/diff_rrlp
@@ -0,0 +1,289 @@
+--- libmsc/rrlp.c Mon Jul 18 11:19:21 2011
++++ r:libmsc/rrlp.c Wed Aug 10 07:34:26 2011
+@@ -20,37 +20,317 @@
+ */
+
+
+ #include <sys/types.h>
+
+ #include <openbsc/gsm_04_08.h>
+ #include <openbsc/signal.h>
+ #include <openbsc/gsm_subscriber.h>
+ #include <openbsc/chan_alloc.h>
+
++/* ----------------------------------------------- */
++
++/* TODO: move in a separate file ? */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <errno.h>
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++
++#define RRLP_SERV_PORT 7890
++#define RRLP_SERV_IP "192.168.5.250" /* TODO: from config file */
++
++#define MAX_RRLP_DATA 256
++
++/* Server cmds */
++
++#define RRLP_CMD_MS_DATA 1 /* data from MS */
++#define RRLP_CMD_MS_DATA_SLOW 2 /* data from MS, slow channel */
++
++/* Server response */
++
++#define RRLP_RSP_ASSIST_DATA 1 /* assitance data, send to MS */
++#define RRLP_RSP_RRLP_ERROR 2 /* RRLP error */
++#define RRLP_RSP_RRLP_POSITION 3 /* RRLP position */
++#define RRLP_RSP_ERROR 4 /* something went wrong */
++
++/* TODO: adjust error messages, use logging */
++
++static int rrlp_serv_cmd(struct gsm_subscriber_connection *conn,
++ uint8_t cmd, uint8_t *data, int len_data,
++ uint8_t *cmd_reply, uint8_t *reply, int *len_reply)
++{
++ static int fd = -1;
++ static struct sockaddr_in sa;
++ int len;
++ uint8_t buf[2 + 1 + 8 + MAX_RRLP_DATA]; /* len, cmd, subscriber ID, data */
++ int len_pkt, offs;
++ int rc;
++ long long unsigned int id;
++ struct sockaddr_in from;
++ int from_len;
++ fd_set readset;
++ struct timeval tv;
++
++ if(len_data > MAX_RRLP_DATA) {
++ fprintf(stderr, "len_data > MAX_RRLP_DATA: %d\n", len_data);
++ return -1;
++ }
++ if(fd == -1) {
++ fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
++ if(fd < 0) {
++ fprintf(stderr, "socket() failed: (%d) %s\n", fd, strerror(errno));
++ return -1;
++ }
++
++ sa.sin_family = AF_INET;
++ sa.sin_port = htons(RRLP_SERV_PORT);
++ if(inet_aton(RRLP_SERV_IP, &sa.sin_addr) != 1) {
++ fprintf(stderr, "inet_aton() failed: %s\n", strerror(errno));
++ close(fd);
++ fd = -1;
++ return -1;
++ }
++
++ rc = connect(fd, (struct sockaddr *)&sa, sizeof(sa));
++ if(rc < 0) {
++ fprintf(stderr, "connect() failed: (%d) %s\n", rc, strerror(errno));
++ close(fd);
++ fd = -1;
++ return -1;
++ }
++ }
++
++ /* we are now connected */
++
++ id = conn->subscr->id;
++
++ /* build cmd packet */
++
++ len_pkt = 2 + 1 + 8 + len_data;
++ buf[0] = len_pkt & 0xFF;
++ buf[1] = (len_pkt >> 8) & 0xFF;
++
++ buf[2] = cmd;
++
++ buf[3] = id & 0xFF;
++ buf[4] = (id >> 8) & 0xFF;
++ buf[5] = (id >> 16) & 0xFF;
++ buf[6] = (id >> 24) & 0xFF;
++ buf[7] = (id >> 32) & 0xFF;
++ buf[8] = (id >> 40) & 0xFF;
++ buf[9] = (id >> 48) & 0xFF;
++ buf[10] = (id >> 56) & 0xFF;
++ /* data */
++ memcpy(&buf[11], data, len_data);
++
++ /* send cmd */
++
++ len = sendto(fd, buf, len_pkt, 0, (struct sockaddr*)&sa, sizeof(sa));
++ if(len < 0) {
++ fprintf(stderr, "sendto() failed: (%d) %s\n", len, strerror(errno));
++ close(fd);
++ fd = -1;
++ return -1;
++ }
++ if(len != len_pkt) {
++ fprintf(stderr, "sendto: len != len_pkt: %d %d\n", len, len_pkt);
++ close(fd);
++ fd = -1;
++ return -1;
++ }
++
++ /* wait at most 500 ms for a reply */
++
++ FD_ZERO(&readset);
++ FD_SET(fd, &readset);
++ tv.tv_sec = 0;
++ tv.tv_usec = 500 * 1000;
++
++ /* this creates another UDP socket on Cygwin !? */
++ rc = select(fd + 1, &readset, NULL, NULL, &tv);
++ if(rc < 0) {
++ fprintf(stderr, "select() failed: (%d) %s\n", rc, strerror(errno));
++ close(fd);
++ fd = -1;
++ return -1;
++ }
++
++ if(!FD_ISSET(fd, &readset)) {
++ fprintf(stderr, "timeout select()\n");
++ close(fd);
++ fd = -1;
++ return -1;
++ }
++
++ /* read packet */
++ from_len = sizeof(from);
++ len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr*)&from, &from_len);
++ if(len < 0) {
++ fprintf(stderr, "recvfrom() failed: (%d) %s\n", len, strerror(errno));
++ close(fd);
++ fd = -1;
++ return -1;
++ }
++ if(len < 2) {
++ fprintf(stderr, "len < 2: %d\n", len);
++ close(fd);
++ fd = -1;
++ return -1;
++ }
++ len_pkt = buf[0] + (buf[1] << 8);
++ if(len_pkt < 2 + 1) {
++ fprintf(stderr, "len_pkt < 2 + 1: %d\n", len_pkt);
++ close(fd);
++ fd = -1;
++ return -1;
++ }
++ if(len != len_pkt) {
++ fprintf(stderr, "recvfrom: len != len_pkt: %d %d\n", len, len_pkt);
++ close(fd);
++ fd = -1;
++ return -1;
++ }
++ len_pkt -= 2;
++ offs = 2;
++
++#if 0 /* dump packet */
++ {
++ int i;
++ for(i = 0; i < len_pkt; i++)
++ printf("%02X ", buf[offs + i]);
++ printf("\n");
++ }
++#endif
++
++ /* process packet */
++
++ *len_reply = len_pkt - 1;
++ *cmd_reply = buf[offs];
++ memcpy(reply, &buf[offs + 1], *len_reply);
++
++ return 0;
++}
++
++/* ----------------------------------------------- */
++
++static int send_rrlp_req(struct gsm_subscriber_connection *conn);
++
++/* TODO: adjust error messages, use logging */
++
++int handle_rrlp(struct gsm_subscriber_connection *conn, uint8_t *data, int len)
++{
++ struct gsm_network *net = conn->bts->network;
++ int rc;
++ uint8_t cmd_reply;
++ uint8_t reply[MAX_RRLP_DATA];
++ int len_reply;
++ uint8_t cmd;
++
++ if (net->rrlp.mode == RRLP_MODE_NONE)
++ return 0;
++
++ if(len > MAX_RRLP_DATA) {
++ fprintf(stderr, "too many data for handle_rrlp (%d)\n", len);
++ return -1;
++ }
++
++ /* TODO: decide if channel is slow (SDCCH), for slow channels
++ only short assistance data should be sent */
++
++ if(1)
++ cmd = RRLP_CMD_MS_DATA;
++ else
++ cmd = RRLP_CMD_MS_DATA_SLOW;
++
++ rc = rrlp_serv_cmd(conn, cmd, data, len, &cmd_reply, reply, &len_reply);
++ if(rc != 0) {
++ fprintf(stderr, "rrlp_serv_cmd failed (%d)\n", rc);
++ return rc;
++ }
++
++ if(cmd_reply == RRLP_RSP_ERROR) {
++ printf("RRLP Server error (general): %s\n", reply);
++ return 0;
++ }
++
++ if(cmd_reply == RRLP_RSP_RRLP_ERROR) {
++ printf("RRLP Server error (RRLP): %s\n", reply);
++ return 0;
++ }
++
++ if(cmd_reply == RRLP_RSP_RRLP_POSITION) {
++ long latitude;
++ long longitude;
++ long altitude;
++
++ if(len_reply != 12) {
++ fprintf(stderr, "invalid RRLP position length (%d)\n", len_reply);
++ return -1;
++ }
++
++ latitude = reply[0] + (reply[1] << 8) + (reply[2] << 16) + (reply[3] << 24);
++ longitude = reply[4] + (reply[5] << 8) + (reply[6] << 16) + (reply[7] << 24);
++ altitude = reply[8] + (reply[9] << 8) + (reply[10] << 16) + (reply[11] << 24);
++
++ /* TODO: do something useful with the position */
++
++ printf("RRLP Server position: ");
++ printf("latitude = %f ", ((double)latitude * 90.0) / 0x800000L);
++ printf("longitude = %f ", ((double)longitude * 360.0) / 0x1000000L);
++ printf("altitude = %ld\n", altitude);
++
++ return 0;
++ }
++
++ if(cmd_reply == RRLP_RSP_ASSIST_DATA) {
++ printf("Assistance data, len %d\n", len_reply);
++
++ /*
++ If there are assistance data, send them. If there are no more,
++ repeat the measurement request
++ */
++
++ if(len_reply)
++ return gsm48_send_rr_app_info(conn, 0x00, len_reply, reply);
++ else
++ send_rrlp_req(conn);
++ }
++
++ return 0;
++}
++
diff --git a/rrlpd/patches_asn1c/01_fix_per_encoding_dieter.diff b/rrlpd/patches_asn1c/01_fix_per_encoding_dieter.diff
new file mode 100644
index 0000000..a09c201
--- /dev/null
+++ b/rrlpd/patches_asn1c/01_fix_per_encoding_dieter.diff
@@ -0,0 +1,17 @@
+Index: skeletons/per_support.c
+===================================================================
+--- skeletons/per_support.c (revision 1407)
++++ skeletons/per_support.c (working copy)
+@@ -336,7 +336,12 @@
+ buf[3] = bits;
+ else {
+ ASN_DEBUG("->[PER out split %d]", obits);
++#if 1 // Dieter
++ po->nboff -= obits; // undo incrementation from a few lines above
++ per_put_few_bits(po, bits >> (obits - 24), 24); // shift according to the rest of the bits
++#else
+ per_put_few_bits(po, bits >> 8, 24);
++#endif
+ per_put_few_bits(po, bits, obits - 24);
+ ASN_DEBUG("<-[PER out split %d]", obits);
+ }
diff --git a/rrlpd/src/Makefile b/rrlpd/src/Makefile
new file mode 100644
index 0000000..774a19d
--- /dev/null
+++ b/rrlpd/src/Makefile
@@ -0,0 +1,41 @@
+# !! adjust as needed !!
+ASN1C=/usr/local/bin/asn1c
+ASN1_INCLUDE=/usr/src/asn1c/skeletons
+
+CC=gcc
+# -DEMIT_ASN_DEBUG=1 ??
+CFLAGS=-I$(ASN1_INCLUDE) -I./asn1_gen -O3 -Wall
+
+ASN1_FILES=$(wildcard asn1/*.asn)
+
+all: rrlp-serv
+
+rrlp-serv: librrlp-asn1.a main.o rrlp.o ubx.o ubx-parse.o gps.o
+ $(CC) $(CFLAGS) -o $@ main.o rrlp.o ubx.o ubx-parse.o gps.o -L. -lrrlp-asn1
+
+#
+# ASN1 file autogeneration (need recursive makefile call)
+#
+
+ASN1_SOURCES = $(wildcard asn1_gen/*.c)
+ASN1_OBJECTS = $(ASN1_SOURCES:.c=.o)
+
+# -fnative-types ??
+
+librrlp-asn1.a: $(ASN1_FILES)
+ mkdir -p asn1_gen && \
+ cd asn1_gen && \
+ $(ASN1C) -fskeletons-copy -gen-PER $(addprefix ../,$^) && \
+ rm converter-sample.c Makefile.am.sample && \
+ $(ASN1C) -gen-PER $(addprefix ../,$^)
+ @$(MAKE) librrlp-asn1.a.submake
+
+librrlp-asn1.a.submake: $(ASN1_OBJECTS)
+ $(AR) rcs librrlp-asn1.a $^
+
+.PHONY: librrlp-asn1.a.submake
+
+clean:
+ rm -Rf asn1_gen
+ rm -f *.o rrlp-serv rrlp-serv.exe test-clnt test-clnt.exe
+
diff --git a/rrlpd/src/asn1/rrlp.asn b/rrlpd/src/asn1/rrlp.asn
new file mode 100644
index 0000000..f7f5535
--- /dev/null
+++ b/rrlpd/src/asn1/rrlp.asn
@@ -0,0 +1,1055 @@
+RRLP-Messages
+-- { RRLP-messages }
+
+DEFINITIONS AUTOMATIC TAGS ::=
+
+BEGIN
+
+--IMPORTS
+
+-- MsrPosition-Req, MsrPosition-Rsp, AssistanceData,
+-- ProtocolError
+
+--FROM
+-- RRLP-Components
+-- { RRLP-Components }
+--;
+
+--IMPORTS
+-- Ext-GeographicalInformation
+--FROM
+-- MAP-LCS-DataTypes {
+-- ccitt identified-organization (4) etsi (0) mobileDomain (0)
+-- gsm-Network (1) modules (3) map-LCS-DataTypes (25) version5 (5)}
+
+-- ExtensionContainer
+--FROM MAP-ExtensionDataTypes {
+-- ccitt identified-organization (4) etsi (0) mobileDomain (0)
+-- gsm-Network (1) modules (3) map-ExtensionDataTypes (21) version4 (4)}
+-- ;
+-- local import
+-- maxExt-GeographicalInformation INTEGER ::= 20
+-- Ext-GeographicalInformation ::= OCTET STRING (SIZE (1..20))
+-- ExtensionContainer ::= OCTET STRING
+
+--ExtensionContainer ::= SEQUENCE {
+-- privateExtensionList [0] IMPLICIT PrivateExtensionList OPTIONAL,
+-- pcsExtensions [1] IMPLICIT PcsExtensions OPTIONAL,
+-- ... }
+
+--PrivateExtensionList ::= SEQUENCE OF PrivateExtension
+
+--PrivateExtension ::= SEQUENCE {
+-- extId OBJECT IDENTIFIER, MAP-EXTENSION .&extensionId
+-- extType ANY OPTIONAL { @extId }
+--}
+
+--PcsExtensions ::= SEQUENCE {
+-- ...
+-- }
+
+
+PDU ::= SEQUENCE {
+ referenceNumber INTEGER (0..7),
+ component RRLP-Component
+}
+
+RRLP-Component ::= CHOICE {
+ msrPositionReq MsrPosition-Req,
+ msrPositionRsp MsrPosition-Rsp,
+ assistanceData AssistanceData,
+ assistanceDataAck NULL,
+ protocolError ProtocolError,
+ ...
+}
+
+--RRLP-Components
+-- { RRLP-Components }
+-- ETSI TS 144 031 V6.8.0
+--DEFINITIONS AUTOMATIC TAGS ::=
+
+--BEGIN
+
+--IMPORTS
+
+-- Ext-GeographicalInformation
+--FROM
+-- MAP-LCS-DataTypes {
+-- ccitt identified-organization (4) etsi (0) mobileDomain (0)
+-- gsm-Network (1) modules (3) map-LCS-DataTypes (25) version5 (5)}
+
+-- ExtensionContainer
+--FROM MAP-ExtensionDataTypes {
+-- ccitt identified-organization (4) etsi (0) mobileDomain (0)
+-- gsm-Network (1) modules (3) map-ExtensionDataTypes (21) version4 (4)}
+-- ;
+-- Add here other ASN.1 definitions presented below
+-- in chapters 4 and 5.
+
+-- add this definition to RRLP-Components module
+-- Measurement Position request component
+
+MsrPosition-Req ::= SEQUENCE {
+ positionInstruct PositionInstruct,
+ referenceAssistData ReferenceAssistData OPTIONAL,
+ msrAssistData MsrAssistData OPTIONAL,
+ systemInfoAssistData SystemInfoAssistData OPTIONAL,
+ gps-AssistData GPS-AssistData OPTIONAL,
+ extensionContainer ExtensionContainer OPTIONAL,
+ ...,
+-- Release 98 extension element
+ rel98-MsrPosition-Req-extension Rel98-MsrPosition-Req-Extension OPTIONAL,
+-- Release 5 extension element
+ rel5-MsrPosition-Req-extension Rel5-MsrPosition-Req-Extension OPTIONAL
+}
+
+-- add this defintion to RRLP-Components module
+-- Measurement Position response component
+
+MsrPosition-Rsp ::= SEQUENCE {
+ multipleSets MultipleSets OPTIONAL,
+ referenceIdentity ReferenceIdentity OPTIONAL,
+ otd-MeasureInfo OTD-MeasureInfo OPTIONAL,
+ locationInfo LocationInfo OPTIONAL,
+ gps-MeasureInfo GPS-MeasureInfo OPTIONAL,
+ locationError LocationError OPTIONAL,
+ extensionContainer ExtensionContainer OPTIONAL,
+ ...,
+-- Release extension here
+ rel-98-MsrPosition-Rsp-Extension Rel-98-MsrPosition-Rsp-Extension OPTIONAL,
+ rel-5-MsrPosition-Rsp-Extension Rel-5-MsrPosition-Rsp-Extension OPTIONAL
+-- rel-5-MsrPosition-Rsp-Extension and other possible future extensions
+-- are the only information elements that may be included in the 2nd
+-- MsrPosition-Rsp component when RRLP pseudo-segmentation is used
+}
+
+-- add this defintion to RRLP-Components module
+-- Assistance Data component
+
+AssistanceData ::= SEQUENCE {
+ referenceAssistData ReferenceAssistData OPTIONAL,
+ msrAssistData MsrAssistData OPTIONAL,
+ systemInfoAssistData SystemInfoAssistData OPTIONAL,
+ gps-AssistData GPS-AssistData OPTIONAL,
+ moreAssDataToBeSent MoreAssDataToBeSent OPTIONAL, -- If not present, interpret as only
+-- Assistance Data component used to
+-- deliver entire set of assistance
+-- data.
+ extensionContainer ExtensionContainer OPTIONAL,
+ ...,
+-- Release extension here
+ rel98-AssistanceData-Extension Rel98-AssistanceData-Extension OPTIONAL,
+ rel5-AssistanceData-Extension Rel5-AssistanceData-Extension OPTIONAL
+}
+
+-- add this defintion to RRLP-Components module
+-- Protocol Error component
+ ProtocolError ::= SEQUENCE {
+ errorCause ErrorCodes,
+ extensionContainer ExtensionContainer OPTIONAL,
+ ...,
+-- Release extensions here
+ rel-5-ProtocolError-Extension Rel-5-ProtocolError-Extension OPTIONAL
+}
+
+PositionInstruct ::= SEQUENCE {
+-- Method type
+ methodType MethodType,
+ positionMethod PositionMethod,
+ measureResponseTime MeasureResponseTime,
+ useMultipleSets UseMultipleSets,
+ environmentCharacter EnvironmentCharacter OPTIONAL
+}
+
+--
+MethodType ::= CHOICE {
+ msAssisted AccuracyOpt, -- accuracy is optional
+ msBased Accuracy, -- accuracy is mandatory
+ msBasedPref Accuracy, -- accuracy is mandatory
+ msAssistedPref Accuracy -- accuracy is mandatory
+}
+
+-- Accuracy of the location estimation
+AccuracyOpt ::= SEQUENCE {
+ accuracy Accuracy OPTIONAL
+}
+
+-- The values of this field are defined in 3GPP TS 23.032 (Uncertainty code)
+Accuracy ::= INTEGER (0..127)
+
+-- Position Method
+PositionMethod ::= ENUMERATED {
+ eotd (0),
+ gps (1),
+ gpsOrEOTD (2)
+}
+
+-- Measurement request response time
+MeasureResponseTime ::= INTEGER (0..7)
+
+-- useMultiple Sets, FFS!
+UseMultipleSets ::= ENUMERATED {
+ multipleSets (0), -- multiple sets are allowed
+ oneSet (1) -- sending of multiple is not allowed
+}
+
+-- Environment characterization
+EnvironmentCharacter ::= ENUMERATED {
+ badArea (0), -- bad urban or suburban, heavy multipath and NLOS
+ notBadArea (1), -- light multipath and NLOS
+ mixedArea (2), -- not defined or mixed environment
+ ...
+}
+-- E-OTD reference BTS for Assitance data IE
+ReferenceAssistData ::= SEQUENCE {
+ bcchCarrier BCCHCarrier, -- BCCH carrier
+ bsic BSIC, -- BSIC
+ timeSlotScheme TimeSlotScheme, -- Timeslot scheme
+ btsPosition BTSPosition OPTIONAL
+}
+
+-- ellipsoid point and
+-- ellipsoid point with altitude and uncertainty ellipsoid shapes are supported
+BTSPosition ::= Ext-GeographicalInformation
+
+-- RF channel number of BCCH
+BCCHCarrier ::= INTEGER (0..1023)
+
+-- Base station Identity Code
+BSIC ::= INTEGER (0..63)
+
+-- Timeslot scheme
+TimeSlotScheme ::= ENUMERATED {
+ equalLength (0),
+ variousLength (1)
+}
+
+-- Time slot (modulo)
+ModuloTimeSlot ::= INTEGER (0..3)
+
+-- E-OTD measurement assistance data IE
+-- The total number of neighbors in this element (MsrAssistData)
+-- and in SystemInfoAssistData element (presented neighbors
+-- can be at a maximum 15!)
+MsrAssistData ::= SEQUENCE {
+ msrAssistList SeqOfMsrAssistBTS
+}
+
+SeqOfMsrAssistBTS ::= SEQUENCE (SIZE(1..15)) OF MsrAssistBTS
+
+MsrAssistBTS ::= SEQUENCE {
+ bcchCarrier BCCHCarrier, -- BCCH carrier
+ bsic BSIC, -- BSIC
+ multiFrameOffset MultiFrameOffset, -- multiframe offset
+ timeSlotScheme TimeSlotScheme, -- Timeslot scheme
+ roughRTD RoughRTD, -- rough RTD value
+-- Location Calculation Assistance data is moved here
+ calcAssistanceBTS CalcAssistanceBTS OPTIONAL
+}
+-- Multiframe offset
+MultiFrameOffset ::= INTEGER (0..51)
+-- The Multiframe Offset value 51 shall not be encoded by the transmitting entity and
+-- shall be treated by the receiving entity as 0.
+-- Rough RTD value between one base station and reference BTS
+
+RoughRTD ::= INTEGER (0..1250)
+-- The RoughRTD value 1250 shall not be encoded by the transmitting entity and shall
+-- be treated by the receiving entity as 0.
+-- E-OTD Measurement assistance data for system information List IE
+-- The total number of base stations in this element (SystemInfoAssistData
+-- presented neighbors) and in MsrAssistData element can be at a maximum 15.
+
+SystemInfoAssistData ::= SEQUENCE {
+ systemInfoAssistList SeqOfSystemInfoAssistBTS
+}
+
+SeqOfSystemInfoAssistBTS::= SEQUENCE (SIZE(1..32)) OF SystemInfoAssistBTS
+-- whether n.th is present or not ?
+
+SystemInfoAssistBTS ::= CHOICE {
+ notPresent NULL,
+ present AssistBTSData
+}
+
+-- Actual assistance data for system information base station
+AssistBTSData ::= SEQUENCE {
+ bsic BSIC, -- BSIC
+ multiFrameOffset MultiFrameOffset, -- multiframe offset
+ timeSlotScheme TimeSlotScheme, -- Timeslot scheme
+ roughRTD RoughRTD, -- rough RTD value
+-- Location Calculation Assistance data
+ calcAssistanceBTS CalcAssistanceBTS OPTIONAL
+}
+
+-- E-OTD Location calculation assistance data,
+-- CalcAssistanceBTS element is optional not subfields
+CalcAssistanceBTS ::= SEQUENCE {
+ fineRTD FineRTD, -- fine RTD value between base stations
+ referenceWGS84 ReferenceWGS84 -- reference coordinates
+}
+
+-- Coordinates of neighbour BTS, WGS-84 ellipsoid
+ReferenceWGS84 ::= SEQUENCE {
+ relativeNorth RelDistance, -- relative distance (south negative)
+ relativeEast RelDistance, -- relative distance (west negative)
+-- Relative Altitude is not always known
+ relativeAlt RelativeAlt OPTIONAL -- relative altitude
+}
+
+-- Fine RTD value between this BTS and the reference BTS
+FineRTD ::= INTEGER (0..255)
+
+-- Relative north/east distance
+RelDistance ::= INTEGER (-200000..200000)
+
+-- Relative altitude
+
+RelativeAlt ::= INTEGER (-4000..4000)
+-- Measure position response IEs
+-- Reference Identity
+-- Multiple sets
+
+MultipleSets ::= SEQUENCE {
+ -- number of reference sets
+ nbrOfSets INTEGER (2..3),
+ -- This field actually tells the number of reference BTSs
+ nbrOfReferenceBTSs INTEGER (1..3),
+ -- This field is conditional and included optionally only if
+ -- nbrOfSets is 3 and number of reference BTSs is 2.
+ referenceRelation ReferenceRelation OPTIONAL
+}
+
+-- Relation between refence BTSs and sets
+ReferenceRelation ::= ENUMERATED {
+ secondBTSThirdSet (0), -- 1st BTS related to 1st and 2nd sets
+ secondBTSSecondSet (1), -- 1st BTS related to 1st and 3rd sets
+ firstBTSFirstSet (2) -- 1st BTS related to 1st set
+}
+
+-- Reference BTS Identity, this element contains number of
+-- BTSs told nbrOfReferenceBTSs field in Multiple sets element)
+
+ReferenceIdentity ::= SEQUENCE {
+ -- Reference BTS list
+ refBTSList SeqOfReferenceIdentityType
+}
+SeqOfReferenceIdentityType ::= SEQUENCE (SIZE(1..3)) OF ReferenceIdentityType
+
+-- Cell identity
+ReferenceIdentityType ::= CHOICE {
+ bsicAndCarrier BSICAndCarrier, -- BSIC and Carrier
+ ci CellID, -- Cell ID, LAC not needed
+ requestIndex RequestIndex, -- Index to Requested Neighbor List
+ systemInfoIndex SystemInfoIndex, -- Index to System info list, this type of ref. identity
+ -- shall not be used by the MS unless it has received
+ -- the SystemInfoAssistData from the SMLC for this cell.
+ ciAndLAC CellIDAndLAC -- CI and LAC
+}
+
+BSICAndCarrier ::= SEQUENCE {
+ carrier BCCHCarrier,
+ bsic BSIC
+}
+
+RequestIndex ::= INTEGER (1..16)
+
+SystemInfoIndex ::= INTEGER (1..32)
+
+CellIDAndLAC ::= SEQUENCE {
+ referenceLAC LAC, -- Location area code
+ referenceCI CellID -- Cell identity
+}
+
+CellID ::= INTEGER (0..65535)
+
+LAC ::= INTEGER (0..65535)
+
+-- OTD-MeasureInfo
+
+OTD-MeasureInfo ::= SEQUENCE {
+ -- Measurement info elements, OTD-MsrElement is repeated number of times
+ -- told in nbrOfReferenceBTSs in MultipleSets, default value is 1
+ otdMsrFirstSets OTD-MsrElementFirst,
+ -- if more than one sets are present this element is repeated
+ -- NumberOfSets - 1 (-1 = first set)
+ otdMsrRestSets SeqOfOTD-MsrElementRest OPTIONAL
+}
+
+SeqOfOTD-MsrElementRest ::= SEQUENCE (SIZE(1..2)) OF OTD-MsrElementRest
+
+-- OTD measurent information for 1 set
+OTD-MsrElementFirst ::= SEQUENCE {
+ refFrameNumber INTEGER (0..42431), -- Frame number modulo 42432
+ referenceTimeSlot ModuloTimeSlot,
+ toaMeasurementsOfRef TOA-MeasurementsOfRef OPTIONAL,
+ stdResolution StdResolution,
+ taCorrection INTEGER (0..960) OPTIONAL, -- TA correction
+-- measured neighbors in OTD measurements
+ otd-FirstSetMsrs SeqOfOTD-FirstSetMsrs OPTIONAL
+}
+
+SeqOfOTD-FirstSetMsrs ::= SEQUENCE (SIZE(1..10)) OF OTD-FirstSetMsrs
+
+-- OTD measurent information 2 and 3 sets if exist
+OTD-MsrElementRest ::= SEQUENCE {
+ refFrameNumber INTEGER (0..42431), -- Frame number modulo 42432
+ referenceTimeSlot ModuloTimeSlot,
+ toaMeasurementsOfRef TOA-MeasurementsOfRef OPTIONAL,
+ stdResolution StdResolution,
+ taCorrection INTEGER (0..960) OPTIONAL, -- TA correction
+ -- measured neighbors in OTD measurements
+ otd-MsrsOfOtherSets SeqOfOTD-MsrsOfOtherSets OPTIONAL
+}
+
+SeqOfOTD-MsrsOfOtherSets ::= SEQUENCE (SIZE(1..10)) OF OTD-MsrsOfOtherSets
+
+-- Standard deviation of the TOA measurements from the reference BTS
+TOA-MeasurementsOfRef ::= SEQUENCE {
+ refQuality RefQuality,
+ numOfMeasurements NumOfMeasurements
+}
+
+RefQuality ::= INTEGER (0..31) -- St Dev of TOA of reference as defined in annex
+
+NumOfMeasurements ::= INTEGER (0..7) -- No. of measurements for RefQuality as defined in annex
+
+StdResolution ::= INTEGER (0..3) -- Values of resolution are defined in annex
+
+OTD-FirstSetMsrs ::= OTD-MeasurementWithID
+
+-- Neighbour info in OTD measurements 0-10 times in TD measurement info
+OTD-MsrsOfOtherSets ::= CHOICE {
+ identityNotPresent OTD-Measurement,
+ identityPresent OTD-MeasurementWithID
+}
+
+-- For this OTD measurement identity is same as the identity of BTS
+-- in the first set with same sequence number
+OTD-Measurement ::= SEQUENCE {
+ nborTimeSlot ModuloTimeSlot,
+ eotdQuality EOTDQuality,
+ otdValue OTDValue
+}
+
+-- This measurement contains the BTS identity and measurement
+OTD-MeasurementWithID ::=SEQUENCE {
+ neighborIdentity NeighborIdentity,
+ nborTimeSlot ModuloTimeSlot,
+ eotdQuality EOTDQuality,
+ otdValue OTDValue
+}
+
+EOTDQuality ::= SEQUENCE {
+ nbrOfMeasurements INTEGER (0..7),
+ stdOfEOTD INTEGER (0..31)
+}
+
+NeighborIdentity ::= CHOICE {
+ bsicAndCarrier BSICAndCarrier, -- BSIC and Carrier
+ ci CellID, -- Cell ID, LAC not needed
+ multiFrameCarrier MultiFrameCarrier, -- MultiFrameOffest and BSIC
+ requestIndex RequestIndex, -- Index to Requested Neighbor List
+ systemInfoIndex SystemInfoIndex, -- Index to System info list, this type of neighbour
+ -- identity shall not be used by the MS unless it has
+ -- received the SystemInfoAssistData from the SMLC for
+ -- this cell.
+ ciAndLAC CellIDAndLAC -- CI and LAC
+}
+
+-- Multiframe and carrier
+MultiFrameCarrier ::= SEQUENCE {
+ bcchCarrier BCCHCarrier,
+ multiFrameOffset MultiFrameOffset
+}
+
+-- OTD measurement value for neighbour
+OTDValue ::= INTEGER (0..39999)
+
+-- Location information IE
+LocationInfo ::= SEQUENCE {
+ refFrame INTEGER (0..65535), -- Reference Frame number
+ -- If refFrame is within (42432..65535), it shall be ignored by the receiver
+ -- in that case the MS should provide GPS TOW if available
+ gpsTOW INTEGER (0..14399999) OPTIONAL, -- GPS TOW
+ fixType FixType,
+ -- Note that applicable range for refFrame is 0 - 42431
+ -- Possible shapes carried in posEstimate are
+ -- ellipsoid point,
+ -- ellipsoid point with uncertainty circle
+ -- ellipsoid point with uncertainty ellipse
+ -- ellipsoid point with altitude and uncertainty ellipsoid
+ posEstimate Ext-GeographicalInformation
+}
+
+FixType ::= INTEGER {
+ twoDFix (0),
+ threeDFix (1)
+} (0..1)
+
+-- GPS-Measurement information
+GPS-MeasureInfo ::= SEQUENCE {
+ -- Measurement info elements
+ -- user has to make sure that in this element is number of elements
+ -- defined in reference BTS identity
+ gpsMsrSetList SeqOfGPS-MsrSetElement
+}
+
+SeqOfGPS-MsrSetElement ::= SEQUENCE (SIZE(1..3)) OF GPS-MsrSetElement
+
+-- OTD measurent information 1-3 times in message
+GPS-MsrSetElement ::= SEQUENCE {
+ refFrame INTEGER (0..65535) OPTIONAL, -- Reference Frame number
+ gpsTOW GPSTOW24b,
+ -- GPS TOW
+ -- Note that applicable range for refFrame is 0 - 42431
+ --N_SAT can be read from number of elements of gps-msrList
+ gps-msrList SeqOfGPS-MsrElement
+}
+
+-- 24 bit presentation for GPSTOW
+GPSTOW24b ::= INTEGER (0..14399999)
+
+-- measured elements in measurement parameters field
+SeqOfGPS-MsrElement ::= SEQUENCE (SIZE(1..16)) OF GPS-MsrElement
+
+GPS-MsrElement ::= SEQUENCE {
+ satelliteID SatelliteID, -- Satellite identifier
+ cNo INTEGER (0..63), -- carrier noise ratio
+ doppler INTEGER (-32768..32767), -- doppler, mulltiply by 0.2
+ wholeChips INTEGER (0..1022), -- whole value of the code phase measurement
+ fracChips INTEGER (0..1024), -- fractional value of the code phase measurement
+ -- a value of 1024 shall not be encoded by the sender
+ -- the receiver shall consider a value of 1024 to be
+ -- invalid data
+ mpathIndic MpathIndic, -- multipath indicator
+ pseuRangeRMSErr INTEGER (0..63) -- index
+}
+
+-- Multipath indicator
+MpathIndic ::= ENUMERATED {
+ notMeasured (0),
+ low (1),
+ medium (2),
+ high (3)
+}
+
+-- Location error IE
+LocationError ::= SEQUENCE {
+ locErrorReason LocErrorReason,
+ additionalAssistanceData AdditionalAssistanceData OPTIONAL,
+ ...
+}
+
+LocErrorReason ::= ENUMERATED {
+ unDefined (0),
+ notEnoughBTSs (1),
+ notEnoughSats (2),
+ eotdLocCalAssDataMissing (3),
+ eotdAssDataMissing (4),
+ gpsLocCalAssDataMissing (5),
+ gpsAssDataMissing (6),
+ methodNotSupported (7),
+ notProcessed (8),
+ refBTSForGPSNotServingBTS (9),
+ refBTSForEOTDNotServingBTS (10),
+ ...
+}
+
+-- exception handling:
+-- an unrecognized value shall be treated the same as value 0
+-- defines additional assistance data needed for any new location attempt
+-- MS shall retain any assistance data already received
+AdditionalAssistanceData ::= SEQUENCE {
+ gpsAssistanceData GPSAssistanceData OPTIONAL,
+ extensionContainer ExtensionContainer OPTIONAL,
+...
+}
+
+GPSAssistanceData ::= OCTET STRING (SIZE (1..maxGPSAssistanceData))
+-- GPSAssistanceData has identical structure and encoding to octets 3 to n of the
+-- GPS Assistance Data IE in 3GPP TS 49.031
+maxGPSAssistanceData INTEGER ::= 40
+-- Protocol Error Causes
+ErrorCodes ::= ENUMERATED {
+ unDefined (0),
+ missingComponet (1),
+ incorrectData (2),
+ missingIEorComponentElement (3),
+ messageTooShort (4),
+ unknowReferenceNumber (5),
+ ...
+}
+
+-- exception handling:
+-- an unrecognized value shall be treated the same as value 0
+-- GPS assistance data IE
+GPS-AssistData ::= SEQUENCE {
+ controlHeader ControlHeader
+}
+
+-- More Assistance Data To Be Sent IE
+-- More Assistance Data Components On the Way indication for delivery of an entire set of assistance
+-- data in multiple Assistance Data components.
+MoreAssDataToBeSent ::= ENUMERATED {
+ noMoreMessages (0), -- This is the only or last Assistance Data message used to deliver
+ -- the entire set of assistance data.
+ moreMessagesOnTheWay (1) -- The SMLC will send more Assistance Data messages or a final RRLP
+ -- Measure Position Request message to deliver the
+ -- the entire set of assistance data.
+}
+
+-- Control header of the GPS assistance data
+ControlHeader ::= SEQUENCE {
+ -- Field type Present information
+ referenceTime ReferenceTime OPTIONAL,
+ refLocation RefLocation OPTIONAL,
+ dgpsCorrections DGPSCorrections OPTIONAL,
+ navigationModel NavigationModel OPTIONAL,
+ ionosphericModel IonosphericModel OPTIONAL,
+ utcModel UTCModel OPTIONAL,
+ almanac Almanac OPTIONAL,
+ acquisAssist AcquisAssist OPTIONAL,
+ realTimeIntegrity SeqOf-BadSatelliteSet OPTIONAL
+}
+
+ReferenceTime ::= SEQUENCE {
+ gpsTime GPSTime,
+ gsmTime GSMTime OPTIONAL,
+ gpsTowAssist GPSTOWAssist OPTIONAL
+}
+
+-- GPS Time includes week number and time-of-week (TOW)
+GPSTime ::= SEQUENCE {
+ gpsTOW23b GPSTOW23b,
+ gpsWeek GPSWeek
+}
+
+-- GPSTOW, range 0-604799.92, resolution 0.08 sec, 23-bit presentation
+GPSTOW23b ::= INTEGER (0..7559999)
+
+-- GPS week number
+GPSWeek ::= INTEGER (0..1023)
+-- GPSTOWAssist consists of TLM message, Anti-spoof flag, Alert flag, and 2 reserved bits in TLM Word
+-- for each visible satellite.
+-- N_SAT can be read from number of elements in GPSTOWAssist
+
+GPSTOWAssist ::= SEQUENCE (SIZE(1..12)) OF GPSTOWAssistElement
+
+GPSTOWAssistElement ::= SEQUENCE {
+ satelliteID SatelliteID,
+ tlmWord TLMWord,
+ antiSpoof AntiSpoofFlag,
+ alert AlertFlag,
+ tlmRsvdBits TLMReservedBits
+}
+
+-- TLM Word, 14 bits
+TLMWord ::= INTEGER (0..16383)
+
+-- Anti-Spoof flag
+AntiSpoofFlag ::= INTEGER (0..1)
+
+-- Alert flag
+AlertFlag ::= INTEGER (0..1)
+
+-- Reserved bits in TLM word, MSB occurs earlier in TLM Word transmitted by satellite
+TLMReservedBits ::= INTEGER (0..3)
+
+GSMTime ::= SEQUENCE {
+ bcchCarrier BCCHCarrier, -- BCCH carrier
+ bsic BSIC, -- BSIC
+ frameNumber FrameNumber,
+ timeSlot TimeSlot,
+ bitNumber BitNumber
+}
+
+-- Frame number
+FrameNumber ::= INTEGER (0..2097151)
+
+-- Time slot number
+TimeSlot ::= INTEGER (0..7)
+
+-- Bit number
+BitNumber ::= INTEGER (0..156)
+
+-- Reference Location IE
+RefLocation ::= SEQUENCE {
+ threeDLocation Ext-GeographicalInformation
+}
+
+-- DGPS Corrections IE
+DGPSCorrections ::= SEQUENCE {
+ gpsTOW INTEGER (0..604799), -- DGPS reference time
+ status INTEGER (0..7),
+-- N_SAT can be read from number of elements of satList
+ satList SeqOfSatElement
+}
+SeqOfSatElement ::= SEQUENCE (SIZE (1..16)) OF SatElement
+
+-- number of correction for satellites
+SatElement ::= SEQUENCE {
+ satelliteID SatelliteID,
+--- Sequence number for ephemeris
+ iode INTEGER (0..239),
+-- User Differential Range Error
+ udre INTEGER (0..3),
+-- Pseudo Range Correction, range is
+-- -655.04 - +655.04,
+ pseudoRangeCor INTEGER (-2047..2047),
+-- Pseudo Range Rate Correction, range is
+-- -4.064 - +4.064,
+ rangeRateCor INTEGER (-127..127),
+-- Delta Pseudo Range Correction 2
+ deltaPseudoRangeCor2 INTEGER (-127..127), -- This IE shall be ignored by the receiver and
+-- set to zero by the sender
+-- Delta Pseudo Range Correction 2
+ deltaRangeRateCor2 INTEGER (-7..7), -- This IE shall be ignored by the receiver and
+-- set to zero by the sender
+-- Delta Pseudo Range Correction 3
+ deltaPseudoRangeCor3 INTEGER (-127..127), -- This IE shall be ignored by the receiver and
+-- set to zero by the sender
+-- Delta Pseudo Range Correction 3
+ deltaRangeRateCor3 INTEGER (-7..7) -- This IE shall be ignored by the receiver and
+-- set to zero by the sender
+}
+
+SatelliteID ::= INTEGER (0..63) -- identifies satellite
+
+-- Navigation Model IE
+NavigationModel ::= SEQUENCE {
+ navModelList SeqOfNavModelElement
+}
+
+-- navigation model satellite list
+SeqOfNavModelElement ::= SEQUENCE (SIZE(1..16)) OF NavModelElement
+
+NavModelElement ::= SEQUENCE {
+ satelliteID SatelliteID,
+ satStatus SatStatus -- satellite status
+}
+
+-- the Status of the navigation model
+SatStatus ::= CHOICE {
+-- New satellite, new Navigation Model
+ newSatelliteAndModelUC UncompressedEphemeris,
+-- Existing satellite, Existing Navigation Model
+ oldSatelliteAndModel NULL,
+-- Existing satellite, new Navigation Model
+ newNaviModelUC UncompressedEphemeris,
+ ...
+}
+
+-- Uncompressed satellite emhemeris and clock corrections
+UncompressedEphemeris ::= SEQUENCE {
+ ephemCodeOnL2 INTEGER (0..3),
+ ephemURA INTEGER (0..15),
+ ephemSVhealth INTEGER (0..63),
+ ephemIODC INTEGER (0..1023),
+ ephemL2Pflag INTEGER (0..1),
+ ephemSF1Rsvd EphemerisSubframe1Reserved,
+ ephemTgd INTEGER (-128..127),
+ ephemToc INTEGER (0..37799),
+ ephemAF2 INTEGER (-128..127),
+ ephemAF1 INTEGER (-32768..32767),
+ ephemAF0 INTEGER (-2097152..2097151),
+ ephemCrs INTEGER (-32768..32767),
+ ephemDeltaN INTEGER (-32768..32767),
+ ephemM0 INTEGER (-2147483648..2147483647),
+ ephemCuc INTEGER (-32768..32767),
+ ephemE INTEGER (0..4294967295),
+ ephemCus INTEGER (-32768..32767),
+ ephemAPowerHalf INTEGER (0..4294967295),
+ ephemToe INTEGER (0..37799),
+ ephemFitFlag INTEGER (0..1),
+ ephemAODA INTEGER (0..31),
+ ephemCic INTEGER (-32768..32767),
+ ephemOmegaA0 INTEGER (-2147483648..2147483647),
+ ephemCis INTEGER (-32768..32767),
+ ephemI0 INTEGER (-2147483648..2147483647),
+ ephemCrc INTEGER (-32768..32767),
+ ephemW INTEGER (-2147483648..2147483647),
+ ephemOmegaADot INTEGER (-8388608..8388607),
+ ephemIDot INTEGER (-8192..8191)
+}
+
+-- Reserved bits in subframe 1 of navigation message
+EphemerisSubframe1Reserved ::= SEQUENCE {
+ reserved1 INTEGER (0..8388607), -- 23-bit field
+ reserved2 INTEGER (0..16777215), -- 24-bit field
+ reserved3 INTEGER (0..16777215), -- 24-bit field
+ reserved4 INTEGER (0..65535) -- 16-bit field
+}
+
+-- Ionospheric Model IE
+IonosphericModel ::= SEQUENCE {
+ alfa0 INTEGER (-128..127),
+ alfa1 INTEGER (-128..127),
+ alfa2 INTEGER (-128..127),
+ alfa3 INTEGER (-128..127),
+ beta0 INTEGER (-128..127),
+ beta1 INTEGER (-128..127),
+ beta2 INTEGER (-128..127),
+ beta3 INTEGER (-128..127)
+}
+
+-- Universal Time Coordinate Model
+UTCModel ::= SEQUENCE {
+ utcA1 INTEGER (-8388608..8388607),
+ utcA0 INTEGER (-2147483648..2147483647),
+ utcTot INTEGER (0..255),
+ utcWNt INTEGER (0..255),
+ utcDeltaTls INTEGER (-128..127),
+ utcWNlsf INTEGER (0..255),
+ utcDN INTEGER (-128..127),
+ utcDeltaTlsf INTEGER (-128..127)
+}
+
+-- Almanac, Long term model
+-- NOTE: These are parameters are subset of the ephemeris
+-- NOTE: But with reduced resolution and accuracy
+Almanac ::= SEQUENCE {
+ alamanacWNa INTEGER (0..255), -- Once per message
+-- navigation model satellite list.
+-- The size of almanacList is actually Nums_Sats_Total field
+ almanacList SeqOfAlmanacElement
+}
+
+SeqOfAlmanacElement ::= SEQUENCE (SIZE(1..64)) OF AlmanacElement
+
+-- Almanac info once per satellite
+AlmanacElement ::= SEQUENCE {
+ satelliteID SatelliteID,
+ almanacE INTEGER (0..65535),
+ alamanacToa INTEGER (0..255),
+ almanacKsii INTEGER (-32768..32767),
+ almanacOmegaDot INTEGER (-32768..32767),
+ almanacSVhealth INTEGER (0..255),
+ almanacAPowerHalf INTEGER (0..16777215),
+ almanacOmega0 INTEGER (-8388608..8388607),
+ almanacW INTEGER (-8388608..8388607),
+ almanacM0 INTEGER (-8388608..8388607),
+ almanacAF0 INTEGER (-1024..1023),
+ almanacAF1 INTEGER (-1024..1023)
+}
+
+-- Acquisition Assistance
+AcquisAssist ::= SEQUENCE {
+-- Number of Satellites can be read from acquistList
+ timeRelation TimeRelation,
+-- Acquisition assistance list
+-- The size of Number of Satellites is actually Number of Satellites field
+ acquisList SeqOfAcquisElement
+}
+
+SeqOfAcquisElement ::= SEQUENCE (SIZE(1..16)) OF AcquisElement
+
+-- the relationship between GPS time and air-interface timing
+TimeRelation ::= SEQUENCE {
+--
+ gpsTOW GPSTOW23b, -- 23b presentation
+ gsmTime GSMTime OPTIONAL
+}
+
+-- data occuring per number of satellites
+AcquisElement ::= SEQUENCE {
+ svid SatelliteID,
+-- Doppler 0th order term,
+-- -5120.0 - 5117.5 Hz (= -2048 - 2047 with 2.5 Hz resolution)
+ doppler0 INTEGER (-2048..2047),
+ addionalDoppler AddionalDopplerFields OPTIONAL,
+ codePhase INTEGER (0..1022), -- Code Phase
+ intCodePhase INTEGER (0..19), -- Integer Code Phase
+ gpsBitNumber INTEGER (0..3), -- GPS bit number
+ codePhaseSearchWindow INTEGER (0..15), -- Code Phase Search Window
+ addionalAngle AddionalAngleFields OPTIONAL
+}
+
+AddionalDopplerFields ::= SEQUENCE {
+-- Doppler 1st order term, -1.0 - +0.5 Hz/sec
+-- (= -42 + (0 to 63) with 1/42 Hz/sec. resolution)
+ doppler1 INTEGER (0..63),
+ dopplerUncertainty INTEGER (0..7)
+-- a sender shall not encode any DopplerUncertainty value in the range 5 to 7
+-- a receiver shall ignore any value between 5 and 7.
+}
+
+AddionalAngleFields ::= SEQUENCE {
+-- azimuth angle, 0 - 348.75 deg (= 0 - 31 with 11.25 deg resolution)
+ azimuth INTEGER (0..31),
+-- elevation angle, 0 - 78.75 deg (= 0 - 7 with 11.25 deg resolution)
+ elevation INTEGER (0..7)
+}
+
+-- Real-Time Integrity
+-- number of bad satellites can be read from this element
+SeqOf-BadSatelliteSet ::= SEQUENCE (SIZE(1..16)) OF SatelliteID
+
+-- Extension Elements
+-- Release 98 Extensions here
+Rel98-MsrPosition-Req-Extension ::= SEQUENCE {
+ rel98-Ext-ExpOTD Rel98-Ext-ExpOTD OPTIONAL, -- ExpectedOTD extension
+ ...,
+ gpsTimeAssistanceMeasurementRequest NULL OPTIONAL,
+ gpsReferenceTimeUncertainty GPSReferenceTimeUncertainty OPTIONAL
+-- Further R98 extensions here
+}
+
+Rel98-AssistanceData-Extension ::= SEQUENCE {
+ rel98-Ext-ExpOTD Rel98-Ext-ExpOTD OPTIONAL, -- ExpectedOTD extension
+ ...,
+ gpsTimeAssistanceMeasurementRequest NULL OPTIONAL,
+ gpsReferenceTimeUncertainty GPSReferenceTimeUncertainty OPTIONAL
+-- Further R98 extensions here
+
+}
+-- Release 98 ExpOTD extension
+Rel98-Ext-ExpOTD ::= SEQUENCE {
+-- If MsrAssistBTS is included in message, msrAssistData-R98-ExpOTD shall be included.
+ msrAssistData-R98-ExpOTD MsrAssistData-R98-ExpOTD OPTIONAL,
+-- If SystemInfoAssistaData is included in message, systemInfoAssistData-R98-ExpOTD shall be
+-- included.
+ systemInfoAssistData-R98-ExpOTD SystemInfoAssistData-R98-ExpOTD OPTIONAL
+}
+
+-- MsrAssistData R98 extension
+MsrAssistData-R98-ExpOTD ::= SEQUENCE {
+ msrAssistList-R98-ExpOTD SeqOfMsrAssistBTS-R98-ExpOTD
+}
+
+-- Indexes in SeqOfMsrAssistBTS-R98-ExpOTD refer to SeqOfMsrAssistBTS
+-- If the index exceeds the SegOfMsrAssistBTS range or if there is other
+-- inconsistencies between the BTS indices, the MS shall apply protocol
+-- error cause incorrectData
+SeqOfMsrAssistBTS-R98-ExpOTD ::= SEQUENCE (SIZE(1..15)) OF MsrAssistBTS-R98-ExpOTD
+
+-- This element completes MsrAssistBTS IE
+MsrAssistBTS-R98-ExpOTD ::= SEQUENCE {
+ expectedOTD ExpectedOTD,
+ expOTDUncertainty ExpOTDUncertainty
+}
+
+-- SystemInfoAssistData R98 extension
+SystemInfoAssistData-R98-ExpOTD ::= SEQUENCE {
+ systemInfoAssistListR98-ExpOTD SeqOfSystemInfoAssistBTS-R98-ExpOTD
+}
+
+-- SeqOfSystemInfoAssistBTS-R98-ExpOTD index refer to SeqOfSystemInfoAssistBTS
+-- If the index exceeds the SegOfSystemInfoAssistBTS range or if there is other
+-- inconsistencies between the BTS indices, the MS shall apply protocol
+-- error cause incorrectData
+
+SeqOfSystemInfoAssistBTS-R98-ExpOTD ::= SEQUENCE (SIZE(1..32)) OF SystemInfoAssistBTS-R98-ExpOTD
+
+-- whether n.th is present or not ?
+SystemInfoAssistBTS-R98-ExpOTD ::= CHOICE {
+ notPresent NULL,
+ present AssistBTSData-R98-ExpOTD
+}
+
+-- This element completes AssistBTSData IE
+AssistBTSData-R98-ExpOTD ::= SEQUENCE {
+ expectedOTD ExpectedOTD,
+ expOTDuncertainty ExpOTDUncertainty -- Uncertainty of expected OTD
+}
+
+-- Expected OTD value between nbor base station and reference BTS
+-- at MS's current estimated location.
+ExpectedOTD ::= INTEGER (0..1250)
+
+-- The ExpectedOTD value 1250 shall not be encoded by the transmitting entity and
+-- shall be treated by the receiving entity as 0.
+-- Uncertainty of Exptected OTD in bits
+ExpOTDUncertainty ::= INTEGER(0..7)
+
+-- Release 98 extensions
+GPSReferenceTimeUncertainty ::= INTEGER (0 .. 127) -- Coding according to Annex
+
+GPSTimeAssistanceMeasurements ::= SEQUENCE {
+ referenceFrameMSB INTEGER (0 .. 63), -- MSB of frame number
+ gpsTowSubms INTEGER (0 .. 9999) OPTIONAL, -- in units of 100ns, for MS based AGPS
+ deltaTow INTEGER (0 .. 127) OPTIONAL, -- for MS assisted AGPS
+ gpsReferenceTimeUncertainty GPSReferenceTimeUncertainty OPTIONAL
+}
+
+Rel-98-MsrPosition-Rsp-Extension ::= SEQUENCE {
+-- First extension to Release 98
+ rel-98-Ext-MeasureInfo SEQUENCE {
+ otd-MeasureInfo-R98-Ext OTD-MeasureInfo-R98-Ext OPTIONAL
+ },
+ ...,
+ timeAssistanceMeasurements GPSTimeAssistanceMeasurements OPTIONAL
+-- Further R98 extensions here
+}
+
+-- This is an addition to OTD-MeasureInfo element defined in original message,
+-- If OTD-MeasureInfo is absent, or if one or more OTD-MsrElementRest are present
+-- OTD-MeasureInfo-R98-Ext shall be absent.
+-- OTD-MeasureInfo-R98-Ext
+OTD-MeasureInfo-R98-Ext ::= SEQUENCE {
+-- Measurement info elements
+ otdMsrFirstSets-R98-Ext OTD-MsrElementFirst-R98-Ext
+}
+
+-- OTD measurement information Ext for the first set only
+OTD-MsrElementFirst-R98-Ext ::= SEQUENCE {
+-- additional measured neighbors in OTD measurements
+ otd-FirstSetMsrs-R98-Ext SeqOfOTD-FirstSetMsrs-R98-Ext OPTIONAL
+}
+
+SeqOfOTD-FirstSetMsrs-R98-Ext ::= SEQUENCE (SIZE(1..5)) OF OTD-FirstSetMsrs
+
+Rel-5-MsrPosition-Rsp-Extension ::= SEQUENCE {
+ extended-reference Extended-reference OPTIONAL,
+-- The extended-reference shall be included by the MS if and only if previously
+-- received from the SMLC in a Measure Position Request. When included, the value sent
+-- by the MS shall equal the value received from the SMLC.
+-- extension to Release 5, for RRLP pseudo-segmentation here
+ otd-MeasureInfo-5-Ext OTD-MeasureInfo-5-Ext OPTIONAL,
+ ulPseudoSegInd UlPseudoSegInd OPTIONAL, -- Included when uplink RRLP
+-- Pseudo-segmentation is used, not included when no uplink pseudo-segmentation is used
+ ...
+-- Possibly more extensions for Release 5 here later
+}
+
+Extended-reference ::= SEQUENCE {
+ smlc-code INTEGER (0..63),
+ transaction-ID INTEGER (0..262143)
+}
+
+OTD-MeasureInfo-5-Ext ::= SeqOfOTD-MsrElementRest
+-- if more than one measurement sets are present this element is repeated
+-- NumberOfSets - 1 (-1 = first set) combined in OTD-MeasureInfo-5-Ext and
+-- OTD-MeasureInfo (e.g. if NumberOfSets is 3, then one otdMsrRestSets may
+-- be sent in OTD-MeasureInfo-5-Ext and one in OTD-MeasureInfo)
+-- First part of Uplink RRLP Pseudo-segmentation indication, possibly more may be defined
+-- in the future for segmentation with more than two segments.
+UlPseudoSegInd ::= ENUMERATED {
+ firstOfMany (0),
+ secondOfMany(1)
+}
+
+Rel5-MsrPosition-Req-Extension ::= SEQUENCE {
+ extended-reference Extended-reference,
+ ...
+-- Possibly more extensions for Release 5 here later
+}
+
+Rel5-AssistanceData-Extension ::= SEQUENCE {
+ extended-reference Extended-reference,
+ ...
+-- Possibly more extensions for Release 5 here later
+}
+
+Rel-5-ProtocolError-Extension::= SEQUENCE {
+ extended-reference Extended-reference OPTIONAL,
+-- The extended-reference shall be included by the MS if and only if previously
+-- received from the SMLC.
+-- When included, the value sent by the MS shall equal the value received from the SMLC.
+ ...
+-- Possibly more extensions for Release 5 here later
+}
+
+Ext-GeographicalInformation ::= OCTET STRING (SIZE (1..20))
+ExtensionContainer ::= OCTET STRING
+
+END \ No newline at end of file
diff --git a/rrlpd/src/gps.c b/rrlpd/src/gps.c
new file mode 100644
index 0000000..e8b6848
--- /dev/null
+++ b/rrlpd/src/gps.c
@@ -0,0 +1,131 @@
+/*
+ * gps.c
+ *
+ * A few utility functions to deal with low level GPS data
+ *
+ *
+ * Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+
+#include "gps.h"
+
+
+#define GET_FIELD_U(w, nb, pos) (((w) >> (pos)) & ((1<<(nb))-1))
+#define GET_FIELD_S(w, nb, pos) (((int)((w) << (32-(nb)-(pos)))) >> (32-(nb)))
+
+/*
+ * Unpacks GPS Subframe 1,2,3 payloads (3 * 8 words)
+ *
+ * Note: eph->sv_id is not filled here since not present in those subframes
+ *
+ * (no parity bit checking is done, only the lower 24 bits of each word
+ * are used)
+ */
+int
+gps_unpack_sf123(uint32_t *sf, struct gps_ephemeris_sv *eph)
+{
+ uint32_t *sf1 = &sf[0];
+ uint32_t *sf2 = &sf[8];
+ uint32_t *sf3 = &sf[16];
+
+ int iode1, iode2;
+
+ eph->week_no = GET_FIELD_U(sf1[0], 10, 14);
+ eph->code_on_l2 = GET_FIELD_U(sf1[0], 2, 12);
+ eph->sv_ura = GET_FIELD_U(sf1[0], 4, 8);
+ eph->sv_health = GET_FIELD_U(sf1[0], 6, 2);
+ eph->l2_p_flag = GET_FIELD_U(sf1[1], 1, 23);
+ eph->t_gd = GET_FIELD_S(sf1[4], 8, 0);
+ eph->iodc = (GET_FIELD_U(sf1[0], 2, 0) << 8) | \
+ GET_FIELD_U(sf1[5], 8, 16);
+ eph->t_oc = GET_FIELD_U(sf1[5], 16, 0);
+ eph->a_f2 = GET_FIELD_S(sf1[6], 8, 16);
+ eph->a_f1 = GET_FIELD_S(sf1[6], 16, 0);
+ eph->a_f0 = GET_FIELD_S(sf1[7], 22, 2);
+
+ iode1 = GET_FIELD_U(sf2[0], 8, 16);
+ eph->c_rs = GET_FIELD_S(sf2[0], 16, 0);
+ eph->delta_n = GET_FIELD_S(sf2[1], 16, 8);
+ eph->m_0 = (GET_FIELD_S(sf2[1], 8, 0) << 24) | \
+ GET_FIELD_U(sf2[2], 24, 0);
+ eph->c_uc = GET_FIELD_S(sf2[3], 16, 8);
+ eph->e = (GET_FIELD_U(sf2[3], 8, 0) << 24) | \
+ GET_FIELD_U(sf2[4], 24, 0);
+ eph->c_us = GET_FIELD_S(sf2[5], 16, 8);
+ eph->a_powhalf = (GET_FIELD_U(sf2[5], 8, 0) << 24) | \
+ GET_FIELD_U(sf2[6], 24, 0);
+ eph->t_oe = GET_FIELD_U(sf2[7], 16, 8);
+ eph->fit_flag = GET_FIELD_U(sf2[7], 1, 7);
+
+ eph->c_ic = GET_FIELD_S(sf3[0], 16, 8);
+ eph->omega_0 = (GET_FIELD_S(sf3[0], 8, 0) << 24) | \
+ GET_FIELD_U(sf3[1], 24, 0);
+ eph->c_is = GET_FIELD_S(sf3[2], 16, 8);
+ eph->i_0 = (GET_FIELD_S(sf3[2], 8, 0) << 24) | \
+ GET_FIELD_U(sf3[3], 24, 0);
+ eph->c_rc = GET_FIELD_S(sf3[4], 16, 8);
+ eph->w = (GET_FIELD_S(sf3[4], 8, 0) << 24) | \
+ GET_FIELD_U(sf3[5], 24, 0);
+ eph->omega_dot = GET_FIELD_S(sf3[6], 24, 0);
+ iode2 = GET_FIELD_U(sf3[7], 8, 16);
+ eph->idot = GET_FIELD_S(sf3[7], 14, 2);
+
+ eph->_rsvd1 = GET_FIELD_U(sf1[1], 23, 0);
+ eph->_rsvd2 = GET_FIELD_U(sf1[2], 24, 0);
+ eph->_rsvd3 = GET_FIELD_U(sf1[3], 24, 0);
+ eph->_rsvd4 = GET_FIELD_U(sf1[4], 16, 8);
+ eph->aodo = GET_FIELD_U(sf2[7], 5, 2);
+
+ /* Check & cross-validate iodc[7:0], iode1, iode2 */
+ if ((iode1 != iode2) || (iode1 != (eph->iodc & 0xff))) {
+ fprintf(stderr, "gps_unpack_sf123 failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Unpacks GPS Subframe 4 or 5 Almanac pages payload (8 words)
+ *
+ * (no parity bit checking is done, only the lower 24 bits of each word
+ * are used)
+ */
+int
+gps_unpack_sf45_almanac(uint32_t *sf, struct gps_almanac_sv *alm)
+{
+ /* this is the page ID but not the satellite ID, its wrong in subframe 5 */
+ alm->sv_id = GET_FIELD_U(sf[0], 6, 16);
+
+ alm->e = GET_FIELD_U(sf[0], 16, 0);
+ alm->t_oa = GET_FIELD_U(sf[1], 8, 16);
+ alm->ksii = GET_FIELD_S(sf[1], 16, 0);
+ alm->omega_dot = GET_FIELD_S(sf[2], 16, 8);
+ alm->sv_health = GET_FIELD_U(sf[2], 8, 0);
+ alm->a_powhalf = GET_FIELD_U(sf[3], 24, 0);
+ alm->omega_0 = GET_FIELD_S(sf[4], 24, 0);
+ alm->w = GET_FIELD_S(sf[5], 24, 0);
+ alm->m_0 = GET_FIELD_S(sf[6], 24, 0);
+ alm->a_f0 = (GET_FIELD_S(sf[7], 8, 16) << 3) | \
+ GET_FIELD_U(sf[7], 3, 2);
+ alm->a_f1 = GET_FIELD_S(sf[7], 11, 5);
+
+ return 0;
+}
+
diff --git a/rrlpd/src/gps.h b/rrlpd/src/gps.h
new file mode 100644
index 0000000..424fb7a
--- /dev/null
+++ b/rrlpd/src/gps.h
@@ -0,0 +1,193 @@
+/*
+ * gps.h
+ *
+ * Header to deal with low level GPS data
+ *
+ *
+ * Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GPS_H__
+#define __GPS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <time.h>
+
+
+#define MAX_SV 64
+
+
+/* Ionosperic model data */
+struct gps_ionosphere_model {
+ /* #bits Scale factor Effective Units */
+ /* (LSB) range */
+
+ int alpha_0; /* s 8 2^-30 seconds */
+ int alpha_1; /* s 8 2^-27 s / semi-circles */
+ int alpha_2; /* s 8 2^-24 s / (semi-circles)^2 */
+ int alpha_3; /* s 8 2^-24 s / (semi-circles)^3 */
+ int beta_0; /* s 8 2^11 seconds */
+ int beta_1; /* s 8 2^14 s / semi-circles */
+ int beta_2; /* s 8 2^16 s / (semi-circles)^2 */
+ int beta_3; /* s 8 2^16 s / (semi-circles)^3 */
+};
+
+
+/* UTC model data */
+struct gps_utc_model {
+ /* #bits Scale factor Effective Units */
+ /* (LSB) range */
+
+ int a0; /* s 32 2^-30 seconds */
+ int a1; /* s 24 2^-50 seconds / seconds */
+ int delta_t_ls; /* s 8 1 seconds */
+ int t_ot; /* u 8 2^12 602,112 seconds */
+ int wn_t; /* u 8 1 weeks */
+ int wn_lsf; /* u 8 1 weeks */
+ int dn; /* u 8 1 7 days */
+ int delta_t_lsf;/* s 8 1 seconds */
+};
+
+
+/* Almanach data */
+struct gps_almanac_sv {
+ int sv_id;
+ int sv_health;
+
+ /* #bits Scale factor Effective Units */
+ /* (LSB) range */
+
+ int e; /* u 16 2^-21 */
+ int t_oa; /* u 8 2^12 602,112 seconds */
+ int ksii; /* s 16 2^-19 semi-circles */
+ int omega_dot; /* s 16 2^-38 semi-circles / s */
+ int a_powhalf; /* u 24 2^-11 meters */
+ int omega_0; /* s 24 2^-23 semi-circles */
+ int w; /* s 24 2^-23 semi-circles */
+ int m_0; /* s 24 2^-23 semi-circles */
+ int a_f0; /* s 11 2^-20 seconds */
+ int a_f1; /* s 11 2^-38 seconds / seconds */
+};
+
+struct gps_almanac {
+ int wna;
+ int n_sv;
+ struct gps_almanac_sv svs[MAX_SV];
+};
+
+
+/* Ephemeris data */
+struct gps_ephemeris_sv {
+ int sv_id;
+
+ /* #bits Scale factor Effective Units */
+ /* (LSB) range */
+
+ int code_on_l2; /* u 2 1 / */
+ int week_no; /* u 10 1 week */
+ int l2_p_flag; /* u 1 1 / */
+ int sv_ura; /* u 4 / / */
+ int sv_health; /* u 6 / / */
+ int t_gd; /* s 8 2^-31 seconds */
+ int iodc; /* u 10 / / */
+ int t_oc; /* u 16 2^4 604,784 seconds */
+ int a_f2; /* s 8 2^-55 sec / sec^2 */
+ int a_f1; /* s 16 2^-43 sec / sec */
+ int a_f0; /* s 22 2^-31 seconds */
+
+ int c_rs; /* s 16 2^-5 meters */
+ int delta_n; /* s 16 2^-43 semi-circles / s */
+ int m_0; /* s 32 2^-31 semi-circles */
+ int c_uc; /* s 16 2^-29 radians */
+ unsigned int e; /* u 32 2^-33 0.03 / */
+ int c_us; /* s 16 2^-29 radians */
+ unsigned int a_powhalf; /* u 32 2^-19 meters^(1/2) */
+ int t_oe; /* u 16 2^4 604,784 seconds */
+ int fit_flag; /* u 1 / / */
+
+ int c_ic; /* s 16 2^-29 radians */
+ int omega_0; /* s 32 2^-31 semi-circles */
+ int c_is; /* s 16 2^-29 radians */
+ int i_0; /* s 32 2^-31 semi-circles */
+ int c_rc; /* s 16 2^-5 meters */
+ int w; /* s 32 2^-31 semi-circles */
+ int omega_dot; /* s 24 2^-43 semi-circles / s */
+ int idot; /* s 14 2^-43 semi-circles / s */
+
+ int _rsvd1; /* 23 bits */
+ int _rsvd2; /* 24 bits */
+ int _rsvd3; /* 24 bits */
+ int _rsvd4; /* 16 bits */
+ int aodo; /* 8 bits Not sure it needs to be here ... */
+};
+
+struct gps_ephemeris {
+ int n_sv;
+ struct gps_ephemeris_sv svs[MAX_SV];
+};
+
+
+/* Reference position */
+struct gps_ref_pos { /* WSG84 ellipsoid */
+ double latitude; /* deg */
+ double longitude; /* deg */
+ double altitude; /* m above ellipsoid */
+};
+
+
+/* Reference time */
+struct gps_ref_time {
+ int wn; /* GPS week number */
+ double tow; /* in seconds */
+ time_t when; /* time in seconds when time was set */
+};
+
+
+/* All assist data */
+#define GPS_FIELD_IONOSPHERE (1<<0)
+#define GPS_FIELD_UTC (1<<1)
+#define GPS_FIELD_ALMANAC (1<<2)
+#define GPS_FIELD_EPHEMERIS (1<<3)
+#define GPS_FIELD_REFPOS (1<<4)
+#define GPS_FIELD_REFTIME (1<<5)
+#define GPS_FIELD_REALTIME_INT (1<<6)
+
+struct gps_assist_data {
+ int fields;
+ struct gps_ionosphere_model ionosphere;
+ struct gps_utc_model utc;
+ struct gps_almanac almanac;
+ struct gps_ephemeris ephemeris;
+ struct gps_ref_pos ref_pos;
+ struct gps_ref_time ref_time;
+};
+
+
+/* GPS Subframe utility methods (see gps.c for details) */
+int gps_unpack_sf123(uint32_t *sf, struct gps_ephemeris_sv *eph);
+int gps_unpack_sf45_almanac(uint32_t *sf, struct gps_almanac_sv *alm);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GPS_H__ */
+
diff --git a/rrlpd/src/main.c b/rrlpd/src/main.c
new file mode 100644
index 0000000..eae2d0c
--- /dev/null
+++ b/rrlpd/src/main.c
@@ -0,0 +1,1298 @@
+/* (C) 2011 by Dieter Spaar <spaar@mirider.augusta.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+
+ The idea behind the RRLP server is as follows:
+
+ - Every RRLP related response from the MS is sent to the RRLP server
+
+ - If it is a position, the decoded position is returned
+
+ - If there is an error, an error message is returned
+
+ - If assistance data are requested, a set of PDUs with the required
+ data is generated. A set of PDUs is used to not exceed the maximum
+ size for a RRLP message.
+
+ - If an assitance data ACK is received, the next assitance data PDU
+ is sent (no data are returned if it is the final message).
+
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <fcntl.h>
+
+#include <PDU.h>
+
+#include "gps.h"
+#include "ubx.h"
+#include "ubx-parse.h"
+#include "rrlp.h"
+
+#define DEBUG 1
+#if DEBUG
+ #define printd(x, args ...) printf(x, ## args)
+#else
+ #define printd(x, args ...)
+#endif
+
+#define DEBUG_GPS 1
+#if DEBUG_GPD
+ #define prints(x, args ...) printf(x, ## args)
+#else
+ #define prints(x, args ...)
+#endif
+
+//#define PDU RRLP_Component
+
+/* Convert "Type" defined by -DPDU into "asn_DEF_Type" */
+#define ASN_DEF_PDU(t) asn_DEF_ ## t
+#define DEF_PDU_Type(t) ASN_DEF_PDU(t)
+#define PDU_Type DEF_PDU_Type(PDU)
+
+/* TODO */
+
+#define RRLP_SERV_PORT 7890
+
+#define MAX_RRLP_DATA 256
+
+/* Server cmds */
+
+#define RRLP_CMD_MS_DATA 1 /* data from MS */
+#define RRLP_CMD_MS_DATA_SLOW 2 /* data from MS, slow channel */
+
+/* Server response */
+
+#define RRLP_RSP_ASSIST_DATA 1 /* assitance data, send to MS */
+#define RRLP_RSP_RRLP_ERROR 2 /* RRLP error */
+#define RRLP_RSP_RRLP_POSITION 3 /* RRLP position */
+#define RRLP_RSP_ERROR 4 /* something went wrong */
+
+
+#define SUBSC_ID_NONE 0
+
+#define N_MAX_AD_RECORDS 10
+#define N_MAX_AD_RECORDS_SLOW 1 /* maximum number of messages used on slow channel */
+
+enum rrlp_state { RRLP_NONE = 0, RRLP_ASSIST, RRLP_ERROR, RRLP_POSITION, RRLP_WAIT_POSITION };
+
+/* record for the state of a subscriber */
+
+struct rrlp_sub_state {
+ long long unsigned int id; /* subscriber ID from OpenBSC */
+ time_t time_first; /* time when record was used the first time */
+
+ int reference_num; /* RRLP reference number */
+
+ enum rrlp_state state;
+
+ int error; /* error */
+
+ /* a large assistance data APDU is split in smaller parts */
+ int cur_assist_record; /* current assistance data record */
+ uint8_t assist_data_len[N_MAX_AD_RECORDS]; /* length of a record */
+ uint8_t assist_data[N_MAX_AD_RECORDS][MAX_RRLP_DATA]; /* record */
+
+ /* GPS position */
+ long latitude; /* */
+ long longitude; /* */
+ long altitude; /* in meter */
+};
+
+void free_rrlp_subscriber(struct rrlp_sub_state *rrlp_sub_state);
+
+/* static allocation, might change that for a large number of queries at once */
+
+#define N_MAX_RRLP_STATES 3000
+
+struct rrlp_sub_state g_rrlp_states[N_MAX_RRLP_STATES];
+
+/* assistance data from the GPS receiver */
+
+struct gps_assist_data g_gps;
+
+/*
+ According to TS 23.032
+
+ So far only "Ellipsoid point with altitude and uncertainty Ellipsoid"
+ seems to be sent from the MS.
+*/
+
+int decode_pos_estimate(struct rrlp_sub_state *rrlp_sub_state, uint8_t *data, int len)
+{
+ long latitude;
+ long longitude;
+ long altitude;
+
+ if(len < 1) {
+ fprintf(stderr, "decode_pos_estimate: len too short (%d)\n", len);
+ return -1;
+ }
+
+ /* type of shape */
+ switch((data[0] & 0xF0) >> 4) {
+ case 0x00:
+ printd("Ellipsoid point\n");
+ break;
+
+ case 0x01:
+ printd("Ellipsoid point with uncertainty Circle\n");
+ break;
+
+ case 0x03:
+ printd("Ellipsoid point with uncertainty Ellipse\n");
+ break;
+
+ case 0x05:
+ printd("Polygon\n");
+ break;
+
+ case 0x08:
+ printd("Ellipsoid point with altitude\n");
+ break;
+
+ case 0x09:
+ printd("Ellipsoid point with altitude and uncertainty Ellipsoid\n");
+ if(len != 14) {
+ fprintf(stderr, "decode_pos_estimate: invalid len (%d)\n", len);
+ return -1;
+ }
+ /* Deggrees of latitude */
+ latitude = ((data[1] & 0x7F) << 16) + (data[2] << 8) + data[3];
+ if(data[1] & 0x80)
+ latitude = -latitude;
+ printd("latitude = %f\n", ((double)latitude * 90.0) / 0x800000L);
+ /* Deggrees of longitude */
+ longitude = (data[4] << 16) + (data[5] << 8) + data[6];
+ if(data[4] & 0x80)
+ longitude |= 0xFF000000;
+ printd("longitude = %f\n", ((double)longitude * 360.0) / 0x1000000L);
+ /* Altitude */
+ altitude = ((data[7] & 0x7F) << 8) + data[8];
+ if(data[7] & 0x80)
+ altitude = -altitude;
+ printd("altitude = %ld\n", altitude);
+ /* Uncertainty semi-major */
+ /* Uncertainty semi-minor */
+ /* Orientation of major axis */
+ /* Uncertainty Altitude */
+ /* Confidence */
+
+ rrlp_sub_state->latitude = latitude;
+ rrlp_sub_state->longitude = longitude;
+ rrlp_sub_state->altitude = altitude;
+
+ return 0;
+ break;
+
+ case 0x0A:
+ printd("Ellipsoid Arc\n");
+ break;
+
+ default:
+ printd("Unknown shape type: %d\n", (data[0] & 0xF0) >> 4);
+ break;
+ }
+
+ fprintf(stderr, "cannot decode shape type (%d)\n", (data[0] & 0xF0) >> 4);
+
+ return -1;
+}
+
+int decode_rrlp_data(struct rrlp_sub_state *rrlp_sub_state, int max_reply_msgs,
+ uint8_t *data, int len,
+ uint8_t *cmd_reply, uint8_t *reply, int *len_reply)
+{
+ static asn_TYPE_descriptor_t *pduType = &PDU_Type;
+ void *structure; /* Decoded structure */
+ asn_codec_ctx_t *opt_codec_ctx = 0;
+ asn_dec_rval_t rval;
+ int rc;
+
+ structure = 0;
+
+ rval = uper_decode(opt_codec_ctx, pduType, (void **)&structure, data, len, 0, 0);
+
+ if(!structure) {
+ if(errno) {
+ fprintf(stderr, "Error: errno = %d\n", errno);
+ return -1;
+ } else {
+ /* EOF */
+ fprintf(stderr, "EOF\n");
+ return -1;
+ }
+ } else {
+ /* dump PDU */
+ asn_fprint(stdout, pduType, structure);
+
+ long l;
+ PDU_t *pdu = (PDU_t*)structure;
+
+ printd("\n");
+ printd("-- referenceNumber = %ld\n", pdu->referenceNumber);
+
+ rrlp_sub_state->reference_num = pdu->referenceNumber;
+
+ /* CHOICE */
+ switch(pdu->component.present) {
+
+ case RRLP_Component_PR_msrPositionReq:
+ printd("-- msrPositionReq\n");
+
+ *cmd_reply = RRLP_RSP_ERROR;
+ strcpy(reply, "APU unsupported: msrPositionReq");
+ *len_reply = strlen(reply) + 1;
+ break;
+
+ case RRLP_Component_PR_msrPositionRsp:
+ printd("-- msrPositionRsp\n");
+
+ /* "MsrPosition-Rsp": process either "locationInfo" or "locationError" */
+
+ /* SEQUENCE with OPTIONAL components */
+ MsrPosition_Rsp_t *msrPositionRsp = &pdu->component.choice.msrPositionRsp;
+
+ if(msrPositionRsp->multipleSets) {
+ printd("---- multipleSets\n");
+ }
+
+ if(msrPositionRsp->referenceIdentity) {
+ printd("---- referenceIdentity\n");
+ }
+
+ if(msrPositionRsp->otd_MeasureInfo) {
+ printd("---- otd_MeasureInfo\n");
+ }
+
+ if(msrPositionRsp->locationInfo) {
+ printd("---- locationInfo\n");
+ printd("------ refFrame = %ld\n", msrPositionRsp->locationInfo->refFrame);
+ if(msrPositionRsp->locationInfo->gpsTOW) {
+ printd("------ gpsTOW = %ld\n", *msrPositionRsp->locationInfo->gpsTOW);
+ /* in ms */
+ }
+ printd("---- fixType = %ld\n", msrPositionRsp->locationInfo->fixType);
+ printd("---- posEstimate\n");
+ int n = msrPositionRsp->locationInfo->posEstimate.size;
+ uint8_t *buf = msrPositionRsp->locationInfo->posEstimate.buf;
+ #if 1 /* dump data */
+ int i;
+ for(i = 0; i < n; i++)
+ printd("%02X ", buf[i]);
+ printd("\n");
+ #endif
+ /* TODO */
+ if(decode_pos_estimate(rrlp_sub_state, buf, n) != 0) {
+ fprintf(stderr, "decode_pos_estimate failed\n");
+
+ *cmd_reply = RRLP_RSP_ERROR;
+ strcpy(reply, "decode_pos_estimate failed");
+ *len_reply = strlen(reply) + 1;
+ } else {
+ *cmd_reply = RRLP_RSP_RRLP_POSITION;
+
+ rrlp_sub_state->state = RRLP_POSITION;
+
+ reply[0] = rrlp_sub_state->latitude & 0xFF;
+ reply[1] = (rrlp_sub_state->latitude >> 8) & 0xFF;
+ reply[2] = (rrlp_sub_state->latitude >> 16) & 0xFF;
+ reply[3] = (rrlp_sub_state->latitude >> 24) & 0xFF;
+
+ reply[4] = rrlp_sub_state->longitude & 0xFF;
+ reply[5] = (rrlp_sub_state->longitude >> 8) & 0xFF;
+ reply[6] = (rrlp_sub_state->longitude >> 16) & 0xFF;
+ reply[7] = (rrlp_sub_state->longitude >> 24) & 0xFF;
+
+ reply[ 8] = rrlp_sub_state->altitude & 0xFF;
+ reply[ 9] = (rrlp_sub_state->altitude >> 8) & 0xFF;
+ reply[10] = (rrlp_sub_state->altitude >> 16) & 0xFF;
+ reply[11] = (rrlp_sub_state->altitude >> 24) & 0xFF;
+
+ *len_reply = 3 * 4;
+ }
+ /* we got a location, ignore anything else */
+ goto done;
+ }
+
+ if(msrPositionRsp->gps_MeasureInfo) {
+ printd("---- gps_MeasureInfo\n");
+ }
+
+ if(msrPositionRsp->locationError) {
+ printd("---- locationError\n");
+ if(asn_INTEGER2long(&msrPositionRsp->locationError->locErrorReason, &l) == 0) {
+ asn_INTEGER_specifics_t *specs = (asn_INTEGER_specifics_t *)asn_DEF_LocErrorReason.specifics;
+ const asn_INTEGER_enum_map_t *el;
+ el = INTEGER_map_value2enum(specs, l);
+ if(el) {
+ printd("---- locErrorReason = %ld (%s)\n", l, el->enum_name);
+ sprintf(reply, "locationError %ld (%s)", l, el->enum_name);
+ } else {
+ printd("---- locErrorReason = %ld\n", l);
+ sprintf(reply, "locationError %ld", l);
+ }
+ } else {
+ printd("---- locErrorReason: decode error\n");
+ strcpy(reply, "locationError ??");
+ l = 9999;
+ }
+ if(msrPositionRsp->locationError->additionalAssistanceData) {
+ printd("------ additionalAssistanceData\n");
+ if(msrPositionRsp->locationError->additionalAssistanceData->gpsAssistanceData) {
+ printd("------ gpsAssistanceData\n");
+ int n = msrPositionRsp->locationError->additionalAssistanceData->gpsAssistanceData->size;
+ uint8_t *buf = msrPositionRsp->locationError->additionalAssistanceData->gpsAssistanceData->buf;
+ struct rrlp_assist_req ar;
+ #if 1 /* dump data */
+ int i;
+ for(i = 0; i < n; i++)
+ printd("%02X ", buf[i]);
+ printd("\n");
+ #endif
+ /* check state */
+ if(rrlp_sub_state->state == RRLP_ASSIST) {
+ fprintf(stderr, "assistanceData in invalid state\n");
+
+ *cmd_reply = RRLP_RSP_ERROR;
+ strcpy(reply, "assistanceData in invalid state");
+ *len_reply = strlen(reply) + 1;
+ goto done;
+ }
+ if(rrlp_sub_state->state == RRLP_WAIT_POSITION) {
+ fprintf(stderr, "assistanceData already sent\n");
+
+ *cmd_reply = RRLP_RSP_ERROR;
+ strcpy(reply, "assistanceData already sent");
+ *len_reply = strlen(reply) + 1;
+ goto done;
+ }
+ /* decode the requested assistance data */
+ rc = rrlp_decode_assistance_request(&ar, buf, n);
+ if(rc != 0) {
+ fprintf(stderr, "rrlp_decode_assistance_request failed: %d\n", rc);
+
+ *cmd_reply = RRLP_RSP_ERROR;
+ sprintf(reply, "rrlp_decode_assistance_request failed: %d", rc);
+ *len_reply = strlen(reply) + 1;
+ } else if(l == LocErrorReason_gpsAssDataMissing) {
+ void *pdus[N_MAX_AD_RECORDS];
+ int lens[N_MAX_AD_RECORDS];
+ int i;
+ int error = 0;
+ int ref_num;
+
+ /* take a new reference number for the assistance data */
+ ref_num = rrlp_sub_state->reference_num;
+ ref_num++;
+ if(ref_num > 7) /* 0..7 */
+ ref_num = 7;
+ if(ref_num == 0) /* 0 is special */
+ ref_num = 1;
+
+ /* generate PDUs */
+ rc = rrlp_gps_assist_pdus(ref_num, &g_gps, &ar, pdus, lens, N_MAX_AD_RECORDS);
+ if(rc < 0) {
+ fprintf(stderr, "rrlp_gps_assist_pdus: %d\n", rc);
+
+ *cmd_reply = RRLP_RSP_ERROR;
+ sprintf(reply, "rrlp_gps_assist_pdus failed: %d", rc);
+ *len_reply = strlen(reply) + 1;
+ goto done;
+ }
+
+ /* clear messages */
+ for(i = 0; i < N_MAX_AD_RECORDS; i++) {
+ rrlp_sub_state->assist_data_len[i] = 0;
+ memset(rrlp_sub_state->assist_data[i], 0, MAX_RRLP_DATA);
+ }
+
+ /* check if maximum number of messages exceeded */
+ if(rc > max_reply_msgs) {
+ for(i = 0; i < rc; i++) {
+ if(pdus[i])
+ asn_DEF_PDU.free_struct(&asn_DEF_PDU, pdus[i], 0);
+ }
+
+ fprintf(stderr, "too many assitance messages: (%d > %d)\n", rc, max_reply_msgs);
+
+ *cmd_reply = RRLP_RSP_ERROR;
+ sprintf(reply, "too many assitance messages: (%d > %d)\n", rc, max_reply_msgs);
+ *len_reply = strlen(reply) + 1;
+ goto done;
+ }
+
+ /* copy PDUs to subscriber state */
+ for(i = 0; i < rc; i++) {
+ printd("%p %d\n", pdus[i], lens[i]);
+ if(pdus[i] == NULL) {
+ fprintf(stderr, "pdus[%d] == NULL\n", i);
+ sprintf(reply, "pdus[%d] == NULL\n", i);
+ error = -1;
+ }
+ #if 0 /* Debug */
+ {
+ int rv, fd;
+ char filename[50];
+ sprintf(filename, "pdu%02d.uper", i);
+ fd = open(filename, O_TRUNC | O_CREAT | O_RDWR, S_IREAD | S_IWRITE);
+ if (fd < 0)
+ printf("File error\n");
+ rv = write(fd, pdus[i], lens[i]);
+ if(rv != lens[i])
+ printf("Write error\n");
+ close(fd);
+ }
+ #endif
+ #if 0 /* Debug */
+ int j;
+ for(j = 0; j < lens[i]; j++)
+ printf("%02X ", ((uint8_t*)pdus[i])[j]);
+ printf("\n");
+ #endif
+
+ if(lens[i] > MAX_RRLP_DATA) {
+ fprintf(stderr, "lens[%d] > MAX_RRLP_DATA (%d)\n", i, lens[i]);
+ sprintf(reply, "lens[%d] > MAX_RRLP_DATA (%d)\n", i, lens[i]);
+ error = -2;
+ }
+
+ if(error == 0) {
+ rrlp_sub_state->assist_data_len[i] = lens[i];
+ memcpy(rrlp_sub_state->assist_data[i], pdus[i], lens[i]);
+
+ }
+
+ if(pdus[i])
+ free(pdus[i]);
+ }
+
+ if(error) {
+ *cmd_reply = RRLP_RSP_ERROR;
+ *len_reply = strlen(reply) + 1;
+ goto done;
+ }
+
+ *cmd_reply = RRLP_RSP_ASSIST_DATA;
+
+ rrlp_sub_state->state = RRLP_ASSIST;
+ rrlp_sub_state->cur_assist_record = 0;
+
+ /* send first assistent data block */
+ i = rrlp_sub_state->cur_assist_record;
+ memcpy(reply, rrlp_sub_state->assist_data[i], rrlp_sub_state->assist_data_len[i]);
+ *len_reply = rrlp_sub_state->assist_data_len[i];
+ /* advance if not already at the end */
+ if(rrlp_sub_state->assist_data_len[i])
+ rrlp_sub_state->cur_assist_record++;
+
+ /* we got assistance data, ignore anything else */
+ goto done;
+ }
+ }
+ }
+ rrlp_sub_state->state = RRLP_ERROR;
+
+ *cmd_reply = RRLP_RSP_RRLP_ERROR;
+ *len_reply = strlen(reply) + 1;
+ /* we got an error, ignore anything else */
+ goto done;
+ }
+
+ if(msrPositionRsp->extensionContainer) {
+ printd("---- extensionContainer\n");
+ }
+
+ /* no "locationInfo" or "locationError" */
+ *cmd_reply = RRLP_RSP_ERROR;
+ strcpy(reply, "unexpected MsrPosition-Rsp component");
+ *len_reply = strlen(reply) + 1;
+ break;
+
+ case RRLP_Component_PR_assistanceData:
+ printd("-- assistanceData\n");
+
+ *cmd_reply = RRLP_RSP_ERROR;
+ strcpy(reply, "APDU unsupported: assistanceData");
+ *len_reply = strlen(reply) + 1;
+ break;
+
+ case RRLP_Component_PR_assistanceDataAck:
+ printd("-- assistanceDataAck\n");
+
+ /* check state */
+ if(rrlp_sub_state->state != RRLP_ASSIST) {
+ *cmd_reply = RRLP_RSP_ERROR;
+ fprintf(stderr, "assistanceData Ack in invalid state\n");
+ strcpy(reply, "assistanceData Ack in invalid state");
+ *len_reply = strlen(reply) + 1;
+ goto done;
+ }
+
+ /* send next assitance data reply or done */
+
+ *cmd_reply = RRLP_RSP_ASSIST_DATA;
+
+ int i = rrlp_sub_state->cur_assist_record;
+ if(i < N_MAX_AD_RECORDS) {
+ memcpy(reply, rrlp_sub_state->assist_data[i], rrlp_sub_state->assist_data_len[i]);
+ *len_reply = rrlp_sub_state->assist_data_len[i];
+ /* advance if not already at the end */
+ if(rrlp_sub_state->assist_data_len[i])
+ rrlp_sub_state->cur_assist_record++;
+ else {
+ rrlp_sub_state->state = RRLP_NONE;
+ }
+ } else {
+ *len_reply = 0;
+ rrlp_sub_state->state = RRLP_WAIT_POSITION;
+ }
+
+ break;
+
+ case RRLP_Component_PR_protocolError:
+ printd("-- protocolError\n");
+ ProtocolError_t *protocolError = &pdu->component.choice.protocolError;
+ if(asn_INTEGER2long(&protocolError->errorCause, &l) == 0) {
+ asn_INTEGER_specifics_t *specs = (asn_INTEGER_specifics_t *)asn_DEF_ErrorCodes.specifics;
+ const asn_INTEGER_enum_map_t *el;
+ el = INTEGER_map_value2enum(specs, l);
+ if(el) {
+ printd("---- errorCause = %ld (%s)\n", l, el->enum_name);
+ sprintf(reply, "protocolError %ld (%s)", l, el->enum_name);
+ } else {
+ printd("---- errorCause = %ld\n", l);
+ sprintf(reply, "protocolError %ld", l);
+ }
+ } else {
+ printd("---- errorCause: decode error\n");
+ strcpy(reply, "protocolError ??");
+ }
+
+ rrlp_sub_state->state = RRLP_ERROR;
+
+ *cmd_reply = RRLP_RSP_RRLP_ERROR;
+ *len_reply = strlen(reply) + 1;
+ break;
+
+ default:
+ printd("-- unknown %d\n", pdu->component.present);
+
+ *cmd_reply = RRLP_RSP_ERROR;
+ sprintf(reply, "unknown PDU conponent %d", pdu->component.present);
+ *len_reply = strlen(reply) + 1;
+ break;
+ }
+
+done:
+ /* free structure */
+
+ asn_DEF_PDU.free_struct(&asn_DEF_PDU, (void*)structure, 0);
+ }
+
+ return 0;
+}
+
+/***********************************************************************/
+
+static int ubx_parse(struct gps_assist_data *gps, uint8_t *data, int len)
+{
+ int rv = 0, i;
+
+ /* Parse each message */
+ for(i = 0; i < len; ) {
+ rv = ubx_msg_dispatch(ubx_parse_dt, data + i, len - i, gps);
+ if (rv < 0)
+ i++; /* Invalid message: try one byte later */
+ else
+ i += rv;
+ }
+
+ return (rv >= 0) ? 0 : rv;
+}
+
+/***********************************************************************/
+
+/* taken from OsmocomBB */
+
+static int serial_init(const char *serial_port, speed_t baudrate)
+{
+ int rc, serial_fd;
+ struct termios tio;
+
+ serial_fd = open(serial_port, O_RDWR | O_NOCTTY | O_NDELAY);
+ if (serial_fd < 0) {
+ perror("cannot open serial port");
+ return serial_fd;
+ }
+
+ //fcntl(serial_fd, F_SETFL, 0);
+
+ /* Configure serial interface */
+ rc = tcgetattr(serial_fd, &tio);
+ if (rc < 0) {
+ close(serial_fd);
+ perror("tcgetattr()");
+ return rc;
+ }
+ rc = cfsetispeed(&tio, baudrate);
+ if (rc < 0) {
+ close(serial_fd);
+ perror("cfsetispeed()");
+ return rc;
+ }
+ rc = cfsetospeed(&tio, baudrate);
+ if (rc < 0) {
+ close(serial_fd);
+ perror("cfsetospeed()");
+ return rc;
+ }
+ tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
+ tio.c_cflag |= (CREAD | CLOCAL | CS8);
+ tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+ tio.c_iflag |= (INPCK | ISTRIP);
+ tio.c_iflag &= ~(ISTRIP | IXON | IXOFF | IGNBRK | INLCR | ICRNL | IGNCR);
+ tio.c_oflag &= ~(OPOST | ONLCR);
+ rc = tcsetattr(serial_fd, TCSANOW, &tio);
+ if (rc < 0) {
+ close(serial_fd);
+ perror("tcsetattr()");
+ return rc;
+ }
+
+ return serial_fd;
+}
+
+#define GPS_USB 1 /* set to 1 if uBlox GPS receiver is connected to USB */
+
+#if GPS_USB
+ #define GPS_BAUDRATE B115200
+#else
+ #define GPS_BAUDRATE B9600
+#endif
+
+/*
+
+USB: switch back to NMEA output
+
+"$PUBX,41,3,0003,0002,9600,0*17\r\n"
+
+*/
+
+static int set_ubx_protocol(int fd)
+{
+ int len;
+ /* Set Protocol and baudrate (switch to UBX output, still allow NMEA input) */
+#if GPS_USB /* if uBlox is connected through USB */
+ char upx_chg_mode[] = "$PUBX,41,3,0003,0001,115200,0*1C\r\n";
+#else /* if uBlox is connected through the serial port */
+ char upx_chg_mode[] = "$PUBX,41,1,0003,0001,9600,0*16\r\n";
+#endif
+
+ len = write(fd, upx_chg_mode, sizeof(upx_chg_mode) - 1);
+ if(len != sizeof(upx_chg_mode) - 1) {
+ perror("write()");
+ return len;
+ }
+
+ /* a short delay */
+
+ sleep(1);
+
+ /* flush the input buffer (there is probably are more elegant solution) */
+
+ for(;;) {
+ fd_set readset;
+ struct timeval tv;
+ int rc;
+ int len;
+ uint8_t buf[256];
+
+ /* check for data */
+
+ FD_ZERO(&readset);
+ FD_SET(fd, &readset);
+ tv.tv_sec = 0;
+ tv.tv_usec = 300 * 1000;
+
+ rc = select(fd + 1, &readset, NULL, NULL, &tv);
+ if(rc < 0) {
+ if(errno != EINTR) {
+ fprintf(stderr, "select() failed: (%d) %s\n", rc, strerror(errno));
+ break;
+ }
+ }
+ if(!FD_ISSET(fd, &readset))
+ break;
+
+ /* read data */
+
+ len = read(fd, buf, sizeof(buf));
+ if(len <= 0) {
+ fprintf(stderr, "serial read() failed: (%d) %s\n", len, strerror(errno));
+ break;
+ }
+
+ }
+
+ return 0;
+}
+
+static int ubx_get_data(int fd)
+{
+ int len;
+ char upx_msg1[] = "\xb5\x62\x01\x02\x00\x00\x03\x0a"; /* NAV-POSLLH */
+ char upx_msg2[] = "\xb5\x62\x0b\x10\x00\x00\x1b\x5c"; /* AID-DATA */
+ char upx_msg3[] = "\xB5\x62\x06\x01\x03\x00\x01\x20\x01\x2C\x83"; /* CFG-MSG, send NAV-TIMEGPS every second */
+
+ len = write(fd, upx_msg1, sizeof(upx_msg1) - 1);
+ if(len != sizeof(upx_msg1) - 1) {
+ perror("write()");
+ return len;
+ }
+
+ len = write(fd, upx_msg2, sizeof(upx_msg2) - 1);
+ if(len != sizeof(upx_msg2) - 1) {
+ perror("write()");
+ return len;
+ }
+
+#if 1
+ len = write(fd, upx_msg3, sizeof(upx_msg3) - 1);
+ if(len != sizeof(upx_msg3) - 1) {
+ perror("write()");
+ return len;
+ }
+#endif
+
+ return 0;
+}
+
+static int ubx_get_time(int fd)
+{
+ int len;
+ char upx_msg1[] = "\xb5\x62\x01\x20\x00\x00\x21\x64"; /* NAV-TIMEGPS */
+ //char upx_msg1[] = "\xb5\x62\x01\x22\x00\x00\x23\x6A"; /* NAV-CLOCK */
+ fd_set readset;
+ struct timeval tv;
+ int rc;
+ uint8_t buf[256];
+ struct gps_assist_data gps;
+
+ len = write(fd, upx_msg1, sizeof(upx_msg1) - 1);
+ if(len != sizeof(upx_msg1) - 1) {
+ perror("write()");
+ return len;
+ }
+
+ /* wait for reply (does it really take nearly a second ?) */
+
+ FD_ZERO(&readset);
+ FD_SET(fd, &readset);
+ tv.tv_sec = 0;
+ tv.tv_usec = 1000 * 1000;
+
+ rc = select(fd + 1, &readset, NULL, NULL, &tv);
+ if(rc < 0) {
+ if(errno != EINTR) {
+ fprintf(stderr, "select() failed: (%d) %s\n", rc, strerror(errno));
+ return -1;
+ }
+ }
+ if(!FD_ISSET(fd, &readset)) {
+ fprintf(stderr, "timeout waiting for GPS time\n");
+ return -2;
+ }
+
+ /* read data */
+
+ len = read(fd, buf, sizeof(buf));
+ if(len <= 0) {
+ fprintf(stderr, "serial read() failed: (%d) %s\n", len, strerror(errno));
+ return -1;
+ }
+
+ /* parse data */
+
+ memset(&gps, 0, sizeof(gps));
+ if(ubx_parse(&gps, buf, len) != 0) {
+ fprintf(stderr, "ubx_parse failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/***********************************************************************/
+
+void init_rrlp_states(void)
+{
+ int i;
+ memset(&g_rrlp_states, 0, sizeof(g_rrlp_states));
+
+ for(i = 0; i < N_MAX_RRLP_STATES; i++) {
+ g_rrlp_states[i].id = SUBSC_ID_NONE;
+ g_rrlp_states[i].state = RRLP_NONE;
+ }
+}
+
+/* find record with this ID or get an unused one */
+
+struct rrlp_sub_state *get_rrlp_subscriber(long long unsigned int id)
+{
+ int i;
+
+ /* search for this ID */
+ for(i = 0; i < N_MAX_RRLP_STATES; i++) {
+ if(g_rrlp_states[i].id == id)
+ return &g_rrlp_states[i];
+ }
+
+ /* search for an unused record */
+ for(i = 0; i < N_MAX_RRLP_STATES; i++) {
+ if(g_rrlp_states[i].id == SUBSC_ID_NONE) {
+ g_rrlp_states[i].id = id;
+ g_rrlp_states[i].time_first = time(NULL);
+ return &g_rrlp_states[i];
+ }
+ }
+
+ return NULL;
+}
+
+void free_rrlp_subscriber(struct rrlp_sub_state *rrlp_sub_state)
+{
+ memset(rrlp_sub_state, 0, sizeof(struct rrlp_sub_state));
+ rrlp_sub_state->id = SUBSC_ID_NONE;
+ rrlp_sub_state->state = RRLP_NONE;
+}
+
+/* find expired records and free them */
+
+#define SUBS_RECORD_LIFETIME 300 /* in seconds */
+
+void free_expired_rrlp_subscribers(void)
+{
+ int i;
+
+ /* search for this ID */
+ for(i = 0; i < N_MAX_RRLP_STATES; i++) {
+ if(g_rrlp_states[i].id != SUBSC_ID_NONE && time(NULL) - g_rrlp_states[i].time_first > SUBS_RECORD_LIFETIME) {
+ free_rrlp_subscriber(&g_rrlp_states[i]);
+ }
+ }
+}
+
+#define GPS_POLL_TIME 100 /* in seconds */
+
+#define MAX(a, b) ((a > b) ? a : b)
+
+int rrlp_serv(int fd, int fd_serial)
+{
+ fd_set readset;
+ struct timeval tv;
+ int rc;
+ int len;
+ uint8_t buf[2 + 1 + 8 + MAX_RRLP_DATA]; /* len, cmd, subscriber ID, data */
+ uint8_t buf_reply[MAX_RRLP_DATA];
+ uint8_t buf_serial[1024 * 8]; /* should be large enough for the complete data set */
+ int len_pkt, offs, len_reply, len_data;
+ uint8_t cmd, cmd_reply;
+ long long unsigned int id;
+ struct sockaddr_in from;
+ int from_len;
+ time_t last_gps_query = 0;
+ time_t last_subs_expiration = time(NULL);
+ struct rrlp_sub_state *rrlp_sub_state;
+
+ memset(&g_gps, 0, sizeof(g_gps));
+
+ init_rrlp_states();
+
+ for(;;) {
+
+ /* new GPS poll ? */
+
+ if(time(NULL) - last_gps_query > GPS_POLL_TIME) {
+ printd("GPS poll\n");
+ ubx_get_data(fd_serial);
+ last_gps_query = time(NULL);
+ }
+
+ /* any data from RRLP client or GPS Receicer ? */
+
+ FD_ZERO(&readset);
+ FD_SET(fd, &readset);
+ if(fd_serial != -1)
+ FD_SET(fd_serial, &readset);
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ /* this creates another UDP socket on Cygwin !? */
+#if 0
+ rc = select(sizeof(readset) * 8, &readset, NULL, NULL, &tv);
+#else
+ rc = select(MAX(fd, fd_serial) + 1, &readset, NULL, NULL, &tv);
+#endif
+ if(rc < 0) {
+ if(errno == EINTR)
+ continue;
+ fprintf(stderr, "select() failed: (%d) %s\n", rc, strerror(errno));
+ return -1;
+ }
+
+ /* data from RRLP client ? */
+
+ if(FD_ISSET(fd, &readset)) {
+
+ /* read packet */
+ from_len = sizeof(from);
+ len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr*)&from, &from_len);
+ if(len < 0) {
+ fprintf(stderr, "recvfrom() failed: (%d) %s\n", len, strerror(errno));
+ return -1;
+ }
+ if(len < 2) {
+ fprintf(stderr, "len < 2: %d\n", len);
+ return -1;
+ }
+ len_pkt = buf[0] + (buf[1] << 8);
+ if(len_pkt < 2 + 1 + 8) {
+ fprintf(stderr, "len_pkt < 2 + 8 + 1: %d\n", len_pkt);
+ return -1;
+ }
+ if(len != len_pkt) {
+ /* TODO: we might have received more than one packet */
+ fprintf(stderr, "recvfrom: len != len_pkt: %d %d\n", len, len_pkt);
+ return -1;
+ }
+ len_pkt -= 2;
+ offs = 2;
+
+#if 0 /* dump packet */
+ {
+ int i;
+ for(i = 0; i < len_pkt; i++)
+ printd("%02X ", buf[offs + i]);
+ printd("\n");
+ }
+#endif
+
+ cmd = buf[offs + 0];
+ /* get subscriber ID */
+ id = buf[offs + 1];
+ id += ((long long unsigned int)buf[offs + 2] << 8);
+ id += ((long long unsigned int)buf[offs + 3] << 16);
+ id += ((long long unsigned int)buf[offs + 4] << 24);
+ id += ((long long unsigned int)buf[offs + 5] << 32);
+ id += ((long long unsigned int)buf[offs + 6] << 40);
+ id += ((long long unsigned int)buf[offs + 7] << 48);
+ id += ((long long unsigned int)buf[offs + 8] << 56);
+
+ printd("cmd = 0x%02X ID = %llu\n", cmd, id);
+
+ len_data = len_pkt - (1 + 8);
+ offs += (1 + 8);
+
+#if 1 /* dump data */
+ {
+ int i;
+ for(i = 0; i < len_data; i++)
+ printd("%02X ", buf[offs + i]);
+ printd("\n");
+ }
+#endif
+
+ /* process packet */
+
+ len_reply = 0;
+ cmd_reply = 0x00;
+
+ /* search subscriber record with this ID or allocate a new one */
+
+ rrlp_sub_state = get_rrlp_subscriber(id);
+ if(rrlp_sub_state == NULL) {
+ fprintf(stderr, "cannot get subscriber reccord\n");
+
+ cmd_reply = RRLP_RSP_ERROR;
+ strcpy(buf_reply, "cannot get subscriber reccord");
+ len_reply = strlen(buf_reply) + 1;
+ } else {
+ if(cmd == RRLP_CMD_MS_DATA) {
+ if(decode_rrlp_data(rrlp_sub_state, N_MAX_AD_RECORDS, &buf[offs], len_data, &cmd_reply, buf_reply, &len_reply) != 0) {
+ cmd_reply = RRLP_RSP_ERROR;
+ strcpy(buf_reply, "decode_rrlp_data failed");
+ len_reply = strlen(buf_reply) + 1;
+ }
+ } else if(cmd == RRLP_CMD_MS_DATA_SLOW) {
+ if(decode_rrlp_data(rrlp_sub_state, N_MAX_AD_RECORDS_SLOW, &buf[offs], len_data, &cmd_reply, buf_reply, &len_reply) != 0) {
+ cmd_reply = RRLP_RSP_ERROR;
+ strcpy(buf_reply, "decode_rrlp_data failed");
+ len_reply = strlen(buf_reply) + 1;
+ }
+ } else {
+ cmd_reply = RRLP_RSP_ERROR;
+ strcpy(buf_reply, "invalid command");
+ len_reply = strlen(buf_reply) + 1;
+ }
+ }
+
+ /* send reply, build packet */
+ len_pkt = 2 + 1 + len_reply;
+ buf[0] = len_pkt & 0xFF;
+ buf[1] = (len_pkt >> 8) & 0xFF;
+ buf[2] = cmd_reply; /* cmd */
+ /* data */
+ memcpy(&buf[3], buf_reply, len_reply);
+
+ len = sendto(fd, buf, len_pkt, 0, (struct sockaddr*)&from, sizeof(from));
+ if(len < 0) {
+ fprintf(stderr, "sendto() failed: (%d) %s\n", len, strerror(errno));
+ return -1;
+ }
+ if(len != len_pkt) {
+ fprintf(stderr, "sendto: len != len_pkt: %d %d\n", len, len_pkt);
+ return -1;
+ }
+ }
+
+ /* data from the GPS receiver ? */
+
+ if(fd_serial != -1 && FD_ISSET(fd_serial, &readset)) {
+
+ int buf_offset = 0;
+ int total_len = 0;
+ struct gps_assist_data gps;
+
+ for(;;) {
+ len = read(fd_serial, buf_serial + buf_offset, sizeof(buf_serial) - buf_offset);
+ if(len <= 0) {
+ fprintf(stderr, "serial read() failed: (%d) %s\n", len, strerror(errno));
+ break;
+ }
+ prints("serial data: %d\n", len);
+
+ total_len += len;
+
+ buf_offset += len;
+ if(buf_offset >= sizeof(buf_serial)) {
+ fprintf(stderr, "serial buffer full\n");
+ break;
+ }
+
+ /* check for more data */
+
+ FD_ZERO(&readset);
+ FD_SET(fd_serial, &readset);
+ tv.tv_sec = 0;
+ tv.tv_usec = 300 * 1000;
+
+ rc = select(fd_serial + 1, &readset, NULL, NULL, &tv);
+ if(rc < 0) {
+ if(errno != EINTR) {
+ fprintf(stderr, "select() failed: (%d) %s\n", rc, strerror(errno));
+ break;
+ }
+ }
+ if(!FD_ISSET(fd_serial, &readset))
+ break;
+ }
+
+ prints("total_len: %d\n", total_len);
+
+#if 0 /* dump hex data */
+ {
+ int i;
+ for(i = 0; i < total_len; i++)
+ prints("%02X ", buf_serial[i]);
+ prints("\n");
+ }
+#endif
+#if 0 /* dump ASCII data */
+ {
+ int i;
+ for(i = 0; i < total_len; i++)
+ prints("%c", buf_serial[i]);
+ }
+#endif
+
+ memset(&gps, 0, sizeof(gps));
+ if(ubx_parse(&gps, buf_serial, total_len) != 0) {
+ fprintf(stderr, "ubx_parse failed\n");
+ }
+ else {
+ if(gps.fields == GPS_FIELD_REFTIME) {
+ g_gps.ref_time = gps.ref_time;
+ }
+ else if (gps.fields != 0) {
+ g_gps = gps;
+ }
+ }
+
+
+ #if 0 /* for time testing */
+ close(fd_serial);
+ fd_serial = -1;
+ #endif
+ }
+
+ /* expire subscribers ? */
+
+ if(time(NULL) - last_subs_expiration > SUBS_RECORD_LIFETIME) {
+ printd("Subscriber expiration\n");
+ free_expired_rrlp_subscribers();
+ last_subs_expiration = time(NULL);
+ }
+
+#if 0 /* for testing only */
+ ubx_get_time(fd_serial);
+#endif
+ }
+
+ return 0;
+}
+
+static int fd = -1;
+static int fd_serial = -1;
+
+static void signal_handler(int signal)
+{
+ int rc;
+
+ fprintf(stdout, "signal %u received\n", signal);
+
+ switch (signal) {
+ case SIGINT:
+ if(fd != -1) {
+ printd("close\n");
+ rc = close(fd);
+ if(rc != 0) {
+ fprintf(stderr, "close() failed: (%d) %s\n", rc, strerror(errno));
+ }
+ }
+ if(fd_serial != -1) {
+ printd("close serial\n");
+ rc = close(fd_serial);
+ if(rc != 0) {
+ fprintf(stderr, "close() failed: (%d) %s\n", rc, strerror(errno));
+ }
+ }
+ exit(0);
+ break;
+ default:
+ break;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ struct sockaddr_in sa;
+ int on;
+
+ printf("RRLP Server\n");
+
+ if(argc != 3) {
+ fprintf(stderr, "usage: rrlp-serv <IP address of the network interface to use> <GPS serial device>.\n");
+ return -1;
+ }
+
+ fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if(fd < 0) {
+ fprintf(stderr, "socket() failed: (%d) %s\n", fd, strerror(errno));
+ return -1;
+ }
+
+ /* This is for Cygwin/Windows, for Linux take the interface name */
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(RRLP_SERV_PORT);
+ sa.sin_addr.s_addr = INADDR_ANY;
+ if(inet_aton(argv[1], &sa.sin_addr) != 1) {
+ fprintf(stderr, "inet_aton() failed: %s\n", strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ if(sa.sin_addr.s_addr == INADDR_NONE)
+ {
+ fprintf(stderr, "Invalid local address\n");
+ close(fd);
+ return -1;
+ }
+
+ on = 1;
+ rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ if(rc != 0) {
+ fprintf(stderr, "setsockopt() failed: (%d) %s\n", rc, strerror(errno));
+ }
+
+ rc = bind(fd, (struct sockaddr *)&sa, sizeof(sa));
+ if(rc < 0) {
+ fprintf(stderr, "bind() failed: (%d) %s\n", rc, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ /* serial port for GPS receiver */
+
+ fd_serial= serial_init(argv[2], GPS_BAUDRATE);
+ if(fd_serial < 0) {
+ fprintf(stderr, "serial_init failed: (%d) %s\n", fd_serial, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ set_ubx_protocol(fd_serial);
+
+ signal(SIGINT, &signal_handler);
+ signal(SIGABRT, &signal_handler);
+ signal(SIGUSR1, &signal_handler);
+
+ printf("Waiting for incoming data....\n");
+
+ rrlp_serv(fd, fd_serial);
+
+ rc = close(fd);
+ if(rc != 0) {
+ fprintf(stderr, "close() failed: (%d) %s\n", rc, strerror(errno));
+ }
+ fd = -1;
+
+ rc = close(fd_serial);
+ if(rc != 0) {
+ fprintf(stderr, "close() serial failed: (%d) %s\n", rc, strerror(errno));
+ }
+ fd_serial = -1;
+
+ return 0;
+}
diff --git a/rrlpd/src/rrlp.c b/rrlpd/src/rrlp.c
new file mode 100644
index 0000000..541c0a0
--- /dev/null
+++ b/rrlpd/src/rrlp.c
@@ -0,0 +1,864 @@
+/*
+ * rrlp.c
+ *
+ * RRLP implementation
+ *
+ *
+ * Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <errno.h>
+#include <math.h>
+
+#include "gps.h"
+#include "rrlp.h"
+
+#include <PDU.h>
+#include <GPS-AssistData.h>
+#include <NavigationModel.h>
+#include <IonosphericModel.h>
+#include <UTCModel.h>
+#include <Almanac.h>
+#include <RefLocation.h>
+#include <ReferenceTime.h>
+
+
+/* ------------------------------------------------------------------------ */
+/* RRLP Assistance request decoding */
+/* ---------------------------------------------------------------------{{{ */
+/* Decode and validate the assistance data request messages.
+ * See section 10.10 of
+ * . ETSI TS 149 031 V8.1.0 (2009-01)
+ * . 3GPP TS 49.031 version 8.1.0 Release 8
+ */
+
+/* Packed structure from 49.031 spec (RGA = Request GPS Assistance) */
+
+#define RRLP_RGA0_ALMANAC (1<<0)
+#define RRLP_RGA0_UTC_MODEL (1<<1)
+#define RRLP_RGA0_IONO_MODEL (1<<2)
+#define RRLP_RGA0_NAV_MODEL (1<<3)
+#define RRLP_RGA0_DGPS (1<<4)
+#define RRLP_RGA0_REF_LOC (1<<5)
+#define RRLP_RGA0_REF_TIME (1<<6)
+#define RRLP_RGA0_ACQ_ASSIST (1<<7)
+
+#define RRLP_RGA1_REALTIME_INT (1<<0)
+#define RRLP_RGA1_EPH_EXT (1<<1)
+#define RRLP_RGA1_EPH_EXT_CHECK (1<<2)
+
+struct rrlp_rga_hdr {
+ uint8_t items0;
+ uint8_t items1;
+} __attribute__((packed));
+
+struct rrlp_rga_eph_sv {
+ uint8_t sv_id; /* [7:6] reserved, [5:0] sv_id */
+ uint8_t iode; /* latest eph in the MS memory in hours */
+} __attribute__((packed));
+
+struct rrlp_rga_eph {
+ uint8_t wn_hi; /* [7:6] = wn[9:8] */
+ uint8_t wn_lo; /* wn[7:0] */
+ uint8_t toe; /* latest eph in the MS memory in hours */
+ uint8_t nsat_tmtoe; /* [7:4] nstat, [3:0] T-Toe limit */
+ struct rrlp_rga_eph_sv svs[0];
+} __attribute__((packed));
+
+struct rrlp_rga_eph_ext {
+ uint8_t validity; /* in 4 hours units */
+} __attribute__((packed));
+
+struct rrlp_rga_eph_ext_check {
+ /* weeks are in gps week modulo 4 */
+ uint8_t wn_begin_end; /* [7:4] begin, [3:0] end */
+ uint8_t tow_begin;
+ uint8_t tow_end;
+} __attribute__((packed));
+
+
+/* Parsing function */
+
+int
+rrlp_decode_assistance_request(
+ struct rrlp_assist_req *ar,
+ void *req, int req_len)
+{
+ struct rrlp_rga_hdr *hdr = NULL;
+ struct rrlp_rga_eph *eph = NULL;
+ struct rrlp_rga_eph_ext *eph_ext = NULL;
+ struct rrlp_rga_eph_ext_check *eph_ext_check = NULL;
+ int p = 0;
+ int rc = 0;
+
+ /* Reset */
+ ar->req_elems = 0;
+ ar->eph_svs = 0;
+
+ /* Parse message */
+ hdr = req;
+ p += sizeof(struct rrlp_rga_hdr);
+ if (p > req_len)
+ return -1;
+
+ if (hdr->items0 & RRLP_RGA0_NAV_MODEL) {
+ printf("NAV_MODEL\n");
+ eph = req + p;
+ p += sizeof(struct rrlp_rga_eph);
+ if (p > req_len)
+ return -1;
+ p += (eph->nsat_tmtoe >> 4) * sizeof(struct rrlp_rga_eph_sv);
+ if (p > req_len)
+ return -1;
+
+ printf(" GPS week = %d\n", (eph->wn_hi << 8) + eph->wn_lo);
+ printf(" TOE = %d\n", eph->toe);
+ printf(" T-TOE limit = %d\n", eph->nsat_tmtoe & 0x0F);
+
+ int i;
+ for(i = 0; i < (eph->nsat_tmtoe >> 4); i++) {
+ printf(" %2d: sv_id = %2d (%d) IODE = %3d\n", i, eph->svs[i].sv_id & 0x3F, eph->svs[i].sv_id >> 6, eph->svs[i].iode);
+ if(eph->svs[i].sv_id >> 6) {
+ /* most certainly invalid data or have to be interpreted differently */
+ rc = -3;
+ }
+ }
+ }
+
+ if (hdr->items1 & RRLP_RGA1_EPH_EXT) {
+ printf("EPH_EXT\n");
+ eph_ext = req + p;
+ p += sizeof(struct rrlp_rga_eph_ext);
+ if (p > req_len)
+ return -1;
+ }
+
+ if (hdr->items1 & RRLP_RGA1_EPH_EXT_CHECK) {
+ printf("EPH_EXT_CHECK\n");
+ eph_ext_check = req + p;
+ p += sizeof(struct rrlp_rga_eph_ext_check);
+ if (p > req_len)
+ return -1;
+ }
+
+ if (p != req_len && (p != 2 || req_len != 6)) { /* P==2 && req_len == 6 might happen */
+ fprintf(stderr, "p != req_len (%d %d)\n", p, req_len);
+ return -2; /* not all bytes consumed ??? */
+ }
+
+ /* Print a warning for unsupported requests */
+ if ((eph_ext != NULL) ||
+ (eph_ext_check != NULL) ||
+ (hdr->items0 & (RRLP_RGA0_DGPS | RRLP_RGA0_ACQ_ASSIST)) ||
+ #if 0
+ (hdr->items1 & RRLP_RGA1_REALTIME_INT)) {
+ #else
+ 0) {
+ #endif
+ fprintf(stderr, "[w] Unsupported assistance data requested, ignored ...\n");
+
+ if(hdr->items0 & RRLP_RGA0_DGPS)
+ printf("Unsupported assistance data requested: RRLP_RGA0_DGPS\n");
+ if(hdr->items0 & RRLP_RGA0_ACQ_ASSIST)
+ printf("Unsupported assistance data requested: RRLP_RGA0_ACQ_ASSIST\n");
+ if(hdr->items1 & RRLP_RGA1_REALTIME_INT)
+ printf("Unsupported assistance data requested: RRLP_RGA1_REALTIME_INT\n");
+ }
+
+ /* Copy the request */
+ if (hdr->items0 & RRLP_RGA0_ALMANAC) {
+ printf("ALMANAC\n");
+ ar->req_elems |= RRLP_AR_ALMANAC;
+ }
+
+ if (hdr->items0 & RRLP_RGA0_UTC_MODEL) {
+ printf("UTC_MODEL\n");
+ ar->req_elems |= RRLP_AR_UTC_MODEL;
+ }
+
+ if (hdr->items0 & RRLP_RGA0_IONO_MODEL) {
+ printf("IONO_MODEL\n");
+ ar->req_elems |= RRLP_AR_IONO_MODEL;
+ }
+
+ if (hdr->items0 & RRLP_RGA0_REF_LOC) {
+ printf("REF_LOC\n");
+ ar->req_elems |= RRLP_AR_REF_LOC;
+ }
+
+ if (hdr->items0 & RRLP_RGA0_REF_TIME) {
+ printf("REF_TIME\n");
+ ar->req_elems |= RRLP_AR_REF_TIME;
+ }
+
+ if (hdr->items1 & RRLP_RGA1_REALTIME_INT) {
+ printf("REALTIME_INTEGRITY\n");
+ ar->req_elems |= RRLP_AR_REALTIME_INT;
+ }
+
+ if (hdr->items0 & RRLP_RGA0_NAV_MODEL) {
+ printf("NAV_MODEL\n");
+ int i, n_svs = eph->nsat_tmtoe >> 4;
+ ar->req_elems |= RRLP_AR_EPHEMERIS;
+ if(n_svs == 0) {
+ ar->eph_svs = 0xFFFFFFFFFFFFFFFFULL;
+ }
+ else {
+ for (i=0; i<n_svs; i++)
+ ar->eph_svs |= (1ULL << (eph->svs[i].sv_id - 1)); /* Dieter: CHECK */
+ }
+ }
+
+ return rc;
+}
+
+/* }}} */
+
+
+/* ------------------------------------------------------------------------ */
+/* RRLP elements fill */
+/* ---------------------------------------------------------------------{{{ */
+
+ /* Helpers */
+
+static void
+_ts_23_032_store_latitude(double lat, uint8_t *b)
+{
+ uint32_t x;
+ x = (uint32_t) floor(fabs(lat/90.0) * ((double)(1<<23)));
+ if (x >= (1<<23))
+ x = (1<<23) - 1;
+ if (lat < 0.0)
+ x |= (1<<23);
+ b[0] = (x >> 16) & 0xff;
+ b[1] = (x >> 8) & 0xff;
+ b[2] = x & 0xff;
+}
+
+static void
+_ts_23_032_store_longitude(double lon, uint8_t *b)
+{
+ int32_t x;
+ x = floor((lon/360.0) * ((double)(1<<24)));
+ if (x >= (1<<23))
+ x = 0x007fffff;
+ else if (x < -(1<<23))
+ x = 0x00800000;
+ b[0] = (x >> 16) & 0xff;
+ b[1] = (x >> 8) & 0xff;
+ b[2] = x & 0xff;
+}
+
+static void
+_ts_23_032_store_altitude(double alt, uint8_t *b)
+{
+ int alt_i = (int)fabs(alt);
+ b[0] = ((alt_i >> 8) & 0x7f) | (alt<0.0 ? 0x80 : 0x00);
+ b[1] = alt_i & 0xff;
+}
+
+
+ /* Fill methods */
+
+static void
+_rrlp_fill_navigation_model_element(
+ struct NavModelElement *rrlp_nme,
+ struct gps_ephemeris_sv *gps_eph_sv)
+{
+ struct UncompressedEphemeris *rrlp_eph;
+
+ #if 1
+ rrlp_nme->satStatus.present = SatStatus_PR_newSatelliteAndModelUC;
+ rrlp_eph = &rrlp_nme->satStatus.choice.newSatelliteAndModelUC;
+ #else /* does this make any difference fro the MS !? */
+ rrlp_nme->satStatus.present = SatStatus_PR_newNaviModelUC;
+ rrlp_eph = &rrlp_nme->satStatus.choice.newNaviModelUC;
+ #endif
+
+ rrlp_nme->satelliteID = gps_eph_sv->sv_id - 1; /* Dieter: satellite ID is zero based */
+
+
+ rrlp_eph->ephemCodeOnL2 = gps_eph_sv->code_on_l2;
+ rrlp_eph->ephemURA = gps_eph_sv->sv_ura;
+ rrlp_eph->ephemSVhealth = gps_eph_sv->sv_health;
+ rrlp_eph->ephemIODC = gps_eph_sv->iodc;
+ rrlp_eph->ephemL2Pflag = gps_eph_sv->l2_p_flag;
+ rrlp_eph->ephemTgd = gps_eph_sv->t_gd;
+ rrlp_eph->ephemToc = gps_eph_sv->t_oc;
+ rrlp_eph->ephemAF2 = gps_eph_sv->a_f2;
+ rrlp_eph->ephemAF1 = gps_eph_sv->a_f1;
+ rrlp_eph->ephemAF0 = gps_eph_sv->a_f0;
+ rrlp_eph->ephemCrs = gps_eph_sv->c_rs;
+ rrlp_eph->ephemDeltaN = gps_eph_sv->delta_n;
+ rrlp_eph->ephemM0 = gps_eph_sv->m_0;
+ rrlp_eph->ephemCuc = gps_eph_sv->c_uc;
+ rrlp_eph->ephemE = gps_eph_sv->e;
+ rrlp_eph->ephemCus = gps_eph_sv->c_us;
+ rrlp_eph->ephemAPowerHalf = gps_eph_sv->a_powhalf;
+ rrlp_eph->ephemToe = gps_eph_sv->t_oe;
+ rrlp_eph->ephemFitFlag = gps_eph_sv->fit_flag;
+ rrlp_eph->ephemAODA = gps_eph_sv->aodo;
+ rrlp_eph->ephemCic = gps_eph_sv->c_ic;
+ rrlp_eph->ephemOmegaA0 = gps_eph_sv->omega_0;
+ rrlp_eph->ephemCis = gps_eph_sv->c_is;
+ rrlp_eph->ephemI0 = gps_eph_sv->i_0;
+ rrlp_eph->ephemCrc = gps_eph_sv->c_rc;
+ rrlp_eph->ephemW = gps_eph_sv->w;
+ rrlp_eph->ephemOmegaADot = gps_eph_sv->omega_dot;
+ rrlp_eph->ephemIDot = gps_eph_sv->idot;
+
+ rrlp_eph->ephemSF1Rsvd.reserved1 = gps_eph_sv->_rsvd1;
+ rrlp_eph->ephemSF1Rsvd.reserved2 = gps_eph_sv->_rsvd2;
+ rrlp_eph->ephemSF1Rsvd.reserved3 = gps_eph_sv->_rsvd3;
+ rrlp_eph->ephemSF1Rsvd.reserved4 = gps_eph_sv->_rsvd4;
+}
+
+static void
+_rrlp_fill_almanac_element(
+ struct AlmanacElement *rrlp_ae,
+ struct gps_almanac_sv *gps_alm_sv)
+{
+ rrlp_ae->satelliteID = gps_alm_sv->sv_id - 1; /* Dieter: satellite ID is zero based */
+
+ rrlp_ae->almanacE = gps_alm_sv->e;
+ rrlp_ae->alamanacToa = gps_alm_sv->t_oa;
+ rrlp_ae->almanacKsii = gps_alm_sv->ksii;
+ rrlp_ae->almanacOmegaDot = gps_alm_sv->omega_dot;
+ rrlp_ae->almanacSVhealth = gps_alm_sv->sv_health;
+ rrlp_ae->almanacAPowerHalf = gps_alm_sv->a_powhalf;
+ rrlp_ae->almanacOmega0 = gps_alm_sv->omega_0;
+ rrlp_ae->almanacW = gps_alm_sv->w;
+ rrlp_ae->almanacM0 = gps_alm_sv->m_0;
+ rrlp_ae->almanacAF0 = gps_alm_sv->a_f0;
+ rrlp_ae->almanacAF1 = gps_alm_sv->a_f1;
+
+}
+
+static void
+_rrlp_fill_ionospheric_model(
+ struct IonosphericModel *rrlp_iono,
+ struct gps_ionosphere_model *gps_iono)
+{
+ rrlp_iono->alfa0 = gps_iono->alpha_0;
+ rrlp_iono->alfa1 = gps_iono->alpha_1;
+ rrlp_iono->alfa2 = gps_iono->alpha_2;
+ rrlp_iono->alfa3 = gps_iono->alpha_3;
+ rrlp_iono->beta0 = gps_iono->beta_0;
+ rrlp_iono->beta1 = gps_iono->beta_1;
+ rrlp_iono->beta2 = gps_iono->beta_2;
+ rrlp_iono->beta3 = gps_iono->beta_3;
+}
+
+static void
+_rrlp_fill_utc_model(
+ struct UTCModel *rrlp_utc,
+ struct gps_utc_model *gps_utc)
+{
+ rrlp_utc->utcA1 = gps_utc->a1;
+ rrlp_utc->utcA0 = gps_utc->a0;
+ rrlp_utc->utcTot = gps_utc->t_ot;
+ rrlp_utc->utcWNt = gps_utc->wn_t & 0xff;
+ rrlp_utc->utcDeltaTls = gps_utc->delta_t_ls;
+ rrlp_utc->utcWNlsf = gps_utc->wn_lsf & 0xff;
+ rrlp_utc->utcDN = gps_utc->dn;
+ rrlp_utc->utcDeltaTlsf = gps_utc->delta_t_lsf;
+
+ printf("UTC: 0x%X\n", (unsigned)rrlp_utc->utcA1);
+ printf("UTC: 0x%X\n", (unsigned)rrlp_utc->utcA0);
+ printf("UTC: 0x%X\n", (unsigned)rrlp_utc->utcTot);
+ printf("UTC: 0x%X\n", (unsigned)rrlp_utc->utcWNt);
+ printf("UTC: 0x%X\n", (unsigned)rrlp_utc->utcDeltaTls);
+ printf("UTC: 0x%X\n", (unsigned)rrlp_utc->utcWNlsf);
+ printf("UTC: 0x%X\n", (unsigned)rrlp_utc->utcDN);
+ printf("UTC: 0x%X\n", (unsigned)rrlp_utc->utcDeltaTlsf);
+}
+
+/* }}} */
+
+
+/* ------------------------------------------------------------------------ */
+/* RRLP Assistance PDU Generation */
+/* ---------------------------------------------------------------------{{{ */
+
+struct PDU *
+_rrlp_create_gps_assist_pdu(int refnum, struct GPS_AssistData **o_gps_ad, int iLast)
+{
+ struct PDU *pdu;
+ struct GPS_AssistData *gps_ad;
+ MoreAssDataToBeSent_t *moreAssDataToBeSent;
+
+ pdu = calloc(1, sizeof(*pdu));
+ if (!pdu)
+ return NULL;
+
+ gps_ad = calloc(1, sizeof(*gps_ad));
+ if (!gps_ad) {
+ free(pdu);
+ return NULL;
+ }
+
+ moreAssDataToBeSent = calloc(1, sizeof(*moreAssDataToBeSent));
+ if (!moreAssDataToBeSent) {
+ free(gps_ad);
+ free(pdu);
+ return NULL;
+ }
+ /* last message or more messages ? */
+ if(iLast) {
+ if(asn_long2INTEGER(moreAssDataToBeSent, MoreAssDataToBeSent_noMoreMessages) != 0)
+ fprintf(stderr, "encode error\n");
+ } else {
+ if(asn_long2INTEGER(moreAssDataToBeSent, MoreAssDataToBeSent_moreMessagesOnTheWay) != 0)
+ fprintf(stderr, "encode error\n");
+ }
+
+ if (o_gps_ad)
+ *o_gps_ad = gps_ad;
+
+ pdu->referenceNumber = refnum;
+ pdu->component.present = RRLP_Component_PR_assistanceData;
+ pdu->component.choice.assistanceData.gps_AssistData = gps_ad;
+ pdu->component.choice.assistanceData.moreAssDataToBeSent = moreAssDataToBeSent;
+
+ return pdu;
+}
+
+static int
+_rrlp_add_ionospheric_model(
+ struct GPS_AssistData *rrlp_gps_ad,
+ struct gps_assist_data *gps_ad)
+{
+ struct IonosphericModel *rrlp_iono;
+
+ if (!(gps_ad->fields & GPS_FIELD_IONOSPHERE))
+ return -EINVAL;
+
+ rrlp_iono = calloc(1, sizeof(*rrlp_iono));
+ if (!rrlp_iono)
+ return -ENOMEM;
+ rrlp_gps_ad->controlHeader.ionosphericModel = rrlp_iono;
+
+ _rrlp_fill_ionospheric_model(rrlp_iono, &gps_ad->ionosphere);
+
+ return 0;
+}
+
+static int
+_rrlp_add_utc_model(
+ struct GPS_AssistData *rrlp_gps_ad,
+ struct gps_assist_data *gps_ad)
+{
+ struct UTCModel *rrlp_utc;
+
+ if (!(gps_ad->fields & GPS_FIELD_UTC))
+ return -EINVAL;
+
+ rrlp_utc = calloc(1, sizeof(*rrlp_utc));
+ if (!rrlp_utc)
+ return -ENOMEM;
+ rrlp_gps_ad->controlHeader.utcModel = rrlp_utc;
+
+ _rrlp_fill_utc_model(rrlp_utc, &gps_ad->utc);
+
+ return 0;
+}
+
+static int
+_rrlp_add_reference_location(
+ struct GPS_AssistData *rrlp_gps_ad,
+ struct gps_assist_data *gps_ad)
+{
+#if 0 /* old */
+ struct RefLocation *rrlp_refloc;
+
+ /* FIXME: Check if info is in gps_ad */
+
+ rrlp_refloc = calloc(1, sizeof(*rrlp_refloc));
+ if (!rrlp_refloc)
+ return -ENOMEM;
+ rrlp_gps_ad->controlHeader.refLocation = rrlp_refloc;
+
+ /* FIXME */
+ {
+ uint8_t gps_loc[] = {
+ 0x80, /* Ellipsoid Point with altitude */
+ #if 0
+ 0x48, 0x0f, 0x93, /* 50.667778 N */
+ 0x03, 0x47, 0x87, /* 4.611667 E */
+ 0x00, 0x72, /* 114m */
+ #else // Dieter
+ 0x44, 0xEF, 0xEB,
+ 0x09, 0x33, 0xAD,
+ 0x01, 0xEC,
+ #endif
+ };
+ uint8_t *b = malloc(sizeof(gps_loc));
+ memcpy(b, gps_loc, sizeof(gps_loc));
+ rrlp_refloc->threeDLocation.buf = b;
+ rrlp_refloc->threeDLocation.size = sizeof(gps_loc);
+ }
+
+ return 0;
+
+#else /* new */
+
+ struct RefLocation *rrlp_refloc;
+ uint8_t *b;
+
+ if (!(gps_ad->fields & GPS_FIELD_REFPOS))
+ return -EINVAL;
+
+ rrlp_refloc = calloc(1, sizeof(*rrlp_refloc));
+ if (!rrlp_refloc)
+ return -ENOMEM;
+ rrlp_gps_ad->controlHeader.refLocation = rrlp_refloc;
+
+ b = malloc(9);
+
+ b[0] = 0x80; /* Ellipsoid Point with altitude */
+ _ts_23_032_store_latitude(gps_ad->ref_pos.latitude, &b[1]);
+ _ts_23_032_store_longitude(gps_ad->ref_pos.longitude, &b[4]);
+ _ts_23_032_store_altitude(gps_ad->ref_pos.altitude, &b[7]);
+
+ rrlp_refloc->threeDLocation.buf = b;
+ rrlp_refloc->threeDLocation.size = 9;
+
+ return 0;
+#endif
+}
+
+static int
+_rrlp_add_reference_time(
+ struct GPS_AssistData *rrlp_gps_ad,
+ struct gps_assist_data *gps_ad)
+{
+#if 0 /* old */
+ struct ReferenceTime *rrlp_reftime;
+
+ /* FIXME: Check if info is in gps_ad */
+
+ rrlp_reftime = calloc(1, sizeof(*rrlp_reftime));
+ if (!rrlp_reftime)
+ return -ENOMEM;
+ rrlp_gps_ad->controlHeader.referenceTime = rrlp_reftime;
+
+ /* FIXME */
+// rrlp_reftime.gpsTime.gpsTOW23b = g_gps_tow / 80; /* 23 bits */
+// rrlp_reftime.gpsTime.gpsWeek = g_gps_week & 0x3ff; /* 10 bits */
+
+ #if 1 // Dieter
+ printf("Time %d\n", (int)time(NULL));
+ //rrlp_reftime->gpsTime.gpsTOW23b = (time(NULL) - 1261643144) + 375961;
+ rrlp_reftime->gpsTime.gpsTOW23b = (time(NULL) - 1281949530) + 119148;
+ printf("GPS TOW %d\n", (int)rrlp_reftime->gpsTime.gpsTOW23b);
+ rrlp_reftime->gpsTime.gpsTOW23b = (uint32_t)((double)rrlp_reftime->gpsTime.gpsTOW23b * 12.5 + 0.5) & 0x7FFFFF;
+ //rrlp_reftime->gpsTime.gpsWeek = 1563 & 0x3FF; // KW52 2009
+ //rrlp_reftime->gpsTime.gpsWeek = 1565 & 0x3FF; // KW1 2010
+ rrlp_reftime->gpsTime.gpsWeek = 1597 & 0x3FF; // KW33 2010
+ #endif
+
+ return 0;
+#else /* new */
+ struct ReferenceTime *rrlp_reftime;
+ double tow;
+
+ if (!(gps_ad->fields & GPS_FIELD_REFTIME))
+ return -EINVAL;
+
+ rrlp_reftime = calloc(1, sizeof(*rrlp_reftime));
+ if (!rrlp_reftime)
+ return -ENOMEM;
+ rrlp_gps_ad->controlHeader.referenceTime = rrlp_reftime;
+
+ /* TODO: adjust offset so that MS receives the correct time ? */
+ #define MY_OFFSET 0
+
+ tow = gps_ad->ref_time.tow + (time(NULL) - gps_ad->ref_time.when) + MY_OFFSET;
+ printf("TOW = %f\n", tow);
+
+ rrlp_reftime->gpsTime.gpsWeek = gps_ad->ref_time.wn & 0x3ff; /* 10b */
+ rrlp_reftime->gpsTime.gpsTOW23b =
+ ((int)floor(tow / 0.08)) & 0x7fffff; /* 23b */
+
+ return 0;
+#endif
+}
+
+static int
+_rrlp_add_realtime_integrity(
+ struct GPS_AssistData *rrlp_gps_ad,
+ struct gps_assist_data *gps_ad)
+{
+ struct SeqOf_BadSatelliteSet *rrlp_realtime_integrity;
+
+#if 0 /* not yet */
+ if (!(gps_ad->fields & GPS_FIELD_REALTIME_INT))
+ return -EINVAL;
+#endif
+
+ rrlp_realtime_integrity = calloc(1, sizeof(*rrlp_realtime_integrity));
+ if (!rrlp_realtime_integrity)
+ return -ENOMEM;
+
+ rrlp_gps_ad->controlHeader.realTimeIntegrity = rrlp_realtime_integrity;
+
+#if 1 /* TODO */
+
+ SatelliteID_t *sa_id;
+
+ sa_id = calloc(1, sizeof(*sa_id));
+
+ *sa_id = 63;
+
+ ASN_SEQUENCE_ADD(&rrlp_realtime_integrity->list, sa_id);
+#endif
+
+ return 0;
+}
+
+static int
+_rrlp_add_almanac(
+ struct GPS_AssistData *rrlp_gps_ad,
+ struct gps_assist_data *gps_ad, int *start, int count)
+{
+ int i;
+ struct Almanac *rrlp_alm;
+ struct gps_almanac *gps_alm = &gps_ad->almanac;
+
+ if (!(gps_ad->fields & GPS_FIELD_ALMANAC))
+ return -EINVAL;
+
+ rrlp_alm = calloc(1, sizeof(*rrlp_alm));
+ if (!rrlp_alm)
+ return -ENOMEM;
+ rrlp_gps_ad->controlHeader.almanac = rrlp_alm;
+
+ rrlp_alm->alamanacWNa = gps_alm->wna;
+ if (count == -1)
+ count = gps_alm->n_sv - *start;
+ for (i=*start; (i<*start+count) && (i<gps_alm->n_sv); i++) {
+ struct AlmanacElement *ae;
+ ae = calloc(1, sizeof(*ae));
+ if (!ae)
+ return -ENOMEM;
+ _rrlp_fill_almanac_element(ae, &gps_alm->svs[i]);
+ ASN_SEQUENCE_ADD(&rrlp_alm->almanacList.list, ae);
+ }
+
+ *start = i;
+
+ return i < gps_alm->n_sv;
+}
+
+static int
+_rrlp_add_ephemeris(
+ struct GPS_AssistData *rrlp_gps_ad,
+ struct gps_assist_data *gps_ad, int *start, int count, uint64_t mask)
+{
+ int i, j;
+ struct NavigationModel *rrlp_nav;
+ struct gps_ephemeris *gps_eph = &gps_ad->ephemeris;
+
+ if (!(gps_ad->fields & GPS_FIELD_EPHEMERIS))
+ return -EINVAL;
+
+ rrlp_nav = calloc(1, sizeof(*rrlp_nav));
+ if (!rrlp_nav)
+ return -ENOMEM;
+ rrlp_gps_ad->controlHeader.navigationModel = rrlp_nav;
+
+ if (count == -1)
+ count = gps_eph->n_sv - *start;
+ for (i=*start,j=0; (j<count) && (i<gps_eph->n_sv); i++) {
+ if (!(mask & (1ULL<<(gps_eph->svs[i].sv_id-1)))) /* Dieter: CHECK */
+ continue;
+ struct NavModelElement *nme;
+ nme = calloc(1, sizeof(*nme));
+ if (!nme)
+ return -ENOMEM;
+ _rrlp_fill_navigation_model_element(nme, &gps_eph->svs[i]);
+ ASN_SEQUENCE_ADD(&rrlp_nav->navModelList.list, nme);
+ j++;
+ }
+
+ *start = i;
+
+ return i < gps_eph->n_sv;
+}
+
+
+#define MAX_PDUS 64
+
+int
+rrlp_gps_assist_pdus(int refnum,
+ struct gps_assist_data *gps_ad, struct rrlp_assist_req *req,
+ void **o_pdu, int *o_len, int o_max_pdus)
+{
+ struct PDU *lst_pdu[MAX_PDUS];
+ int lst_cnt = 0;
+
+ struct PDU *rrlp_pdu = NULL;
+ struct GPS_AssistData *rrlp_gps_ad = NULL;
+ uint32_t re = req->req_elems;
+ int i, rv = 0;
+
+ /* IonosphericModel, UTCModel, RefLocation, ReferenceTime */
+ if (re & (RRLP_AR_IONO_MODEL |
+ RRLP_AR_UTC_MODEL |
+ RRLP_AR_REF_TIME |
+ RRLP_AR_REF_LOC |
+ RRLP_AR_REALTIME_INT))
+ {
+ int pdu_has_data = 0;
+
+ rrlp_pdu = _rrlp_create_gps_assist_pdu(refnum, &rrlp_gps_ad, 0);
+ if (!rrlp_pdu) {
+ rv = -ENOMEM;
+ goto error;
+ }
+
+#if 1 /* enable/disable component */
+ if (re & RRLP_AR_IONO_MODEL) {
+ printf("+ IONO_MODEL\n");
+ if (!_rrlp_add_ionospheric_model(rrlp_gps_ad, gps_ad))
+ pdu_has_data = 1;
+ }
+#endif
+#if 1 /* enable/disable component */
+ if (re & RRLP_AR_UTC_MODEL) {
+ printf("+ UTC_MODEL\n");
+ if (!_rrlp_add_utc_model(rrlp_gps_ad, gps_ad))
+ pdu_has_data = 1;
+ }
+#endif
+#if 1 /* enable/disable component */
+ if (re & RRLP_AR_REF_TIME) {
+ printf("+ REF_TIME\n");
+ if (!_rrlp_add_reference_time(rrlp_gps_ad, gps_ad))
+ pdu_has_data = 1;
+ }
+#endif
+#if 1 /* enable/disable component */
+ if (re & RRLP_AR_REF_LOC) {
+ printf("+ REF_LOC\n");
+ if (!_rrlp_add_reference_location(rrlp_gps_ad, gps_ad))
+ pdu_has_data = 1;
+ }
+#endif
+#if 0 /* enable/disable component (skip this as it is for now a dummy list only) */
+ if (re & RRLP_AR_REALTIME_INT) {
+ printf("+ REALTIME_INTEGRITY\n");
+ if (!_rrlp_add_realtime_integrity(rrlp_gps_ad, gps_ad))
+ pdu_has_data = 1;
+ }
+#endif
+
+ if (pdu_has_data) {
+ lst_pdu[lst_cnt++] = rrlp_pdu;
+ rrlp_pdu = NULL;
+ }
+ }
+#if 1 /* enable/disable component */
+ /* Almanac */
+ if (re & RRLP_AR_ALMANAC) {
+ i = 0;
+ do {
+ if (!(gps_ad->fields & GPS_FIELD_ALMANAC))
+ break;
+
+ printf("+ ALMANAC\n");
+
+ if (!rrlp_pdu) {
+ rrlp_pdu = _rrlp_create_gps_assist_pdu(refnum, &rrlp_gps_ad, 0);
+ if (!rrlp_pdu) {
+ rv = -ENOMEM;
+ goto error;
+ }
+ }
+
+ /* adjust count so that messages fit in a single PDU */
+ rv = _rrlp_add_almanac(rrlp_gps_ad, gps_ad, &i, 10);
+ if (rv < 0)
+ goto error;
+
+ lst_pdu[lst_cnt++] = rrlp_pdu;
+ rrlp_pdu = NULL;
+ } while (rv);
+ }
+#endif
+#if 1 /* enable/disable component */
+ /* Ephemeris */
+ if (re & RRLP_AR_EPHEMERIS) {
+ i = 0;
+ do {
+ if (!(gps_ad->fields & GPS_FIELD_EPHEMERIS))
+ break;
+
+ printf("+ EPHEMERIS\n");
+
+ if (!rrlp_pdu) {
+ rrlp_pdu = _rrlp_create_gps_assist_pdu(refnum, &rrlp_gps_ad, 0);
+ if (!rrlp_pdu) {
+ rv = -ENOMEM;
+ goto error;
+ }
+ }
+#if 1 /* two sets in one PDS to be not larger than maximum message size */
+ rv = _rrlp_add_ephemeris(rrlp_gps_ad, gps_ad, &i, 2, req->eph_svs);
+#elif 0 /* three sets in one PDU, too large */
+ rv = _rrlp_add_ephemeris(rrlp_gps_ad, gps_ad, &i, 3, req->eph_svs);
+#elif 0 /* all sets in one PDU, too large */
+ rv = _rrlp_add_ephemeris(rrlp_gps_ad, gps_ad, &i, -1, req->eph_svs);
+#endif
+ lst_pdu[lst_cnt++] = rrlp_pdu;
+ rrlp_pdu = NULL;
+
+ } while (rv);
+ }
+#endif
+
+ /* set last message flag */
+ if(asn_long2INTEGER(lst_pdu[lst_cnt - 1]->component.choice.assistanceData.moreAssDataToBeSent, MoreAssDataToBeSent_noMoreMessages) != 0)
+ fprintf(stderr, "encode error\n");
+
+ /* Serialize & Release all PDUs */
+ for (i=0; i<lst_cnt && i<o_max_pdus; i++) {
+ /* Debugging, dump PDU */
+ asn_fprint(stdout, &asn_DEF_PDU, lst_pdu[i]);
+ rv = uper_encode_to_new_buffer(&asn_DEF_PDU, NULL, lst_pdu[i], &o_pdu[i]);
+ if (rv < 0) {
+ printf("uper_encode_to_new_buffer error %d (%d)\n", rv, i);
+ goto error;
+ }
+ o_len[i] = rv;
+ }
+
+ return lst_cnt;
+
+ /* Release ASN.1 objects */
+error:
+ if (rrlp_pdu)
+ asn_DEF_PDU.free_struct(&asn_DEF_PDU, (void*)rrlp_pdu, 0);
+
+ for (i=0; i<lst_cnt; i++)
+ asn_DEF_PDU.free_struct(&asn_DEF_PDU, lst_pdu[i], 0);
+
+ return rv;
+}
+
+/* }}} */
+
diff --git a/rrlpd/src/rrlp.h b/rrlpd/src/rrlp.h
new file mode 100644
index 0000000..5026c3c
--- /dev/null
+++ b/rrlpd/src/rrlp.h
@@ -0,0 +1,65 @@
+/*
+ * rrlp.h
+ *
+ * RRLP Header
+ *
+ *
+ * Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __RRLP_H__
+#define __RRLP_H__
+
+#include <stdint.h>
+
+#include "gps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Our internal simplified structure for requests */
+
+#define RRLP_AR_REF_LOC (1<<0)
+#define RRLP_AR_REF_TIME (1<<1)
+#define RRLP_AR_UTC_MODEL (1<<2)
+#define RRLP_AR_IONO_MODEL (1<<3)
+#define RRLP_AR_ALMANAC (1<<4)
+#define RRLP_AR_EPHEMERIS (1<<5)
+#define RRLP_AR_REALTIME_INT (1<<6)
+
+struct rrlp_assist_req {
+ uint32_t req_elems;
+ uint64_t eph_svs;
+};
+
+
+/* Methods */
+int rrlp_decode_assistance_request(struct rrlp_assist_req *ar,
+ void *req, int req_len);
+
+int rrlp_gps_assist_pdus(int refnum,
+ struct gps_assist_data *gps_ad, struct rrlp_assist_req *req,
+ void **o_pdu, int *o_len, int o_max_pdus);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __RRLP_H__ */
+
diff --git a/rrlpd/src/ubx-parse.c b/rrlpd/src/ubx-parse.c
new file mode 100644
index 0000000..0f6e816
--- /dev/null
+++ b/rrlpd/src/ubx-parse.c
@@ -0,0 +1,246 @@
+/*
+ * ubx-parse.c
+ *
+ * Implementation of parsing code converting UBX messages to GPS assist
+ * data
+ *
+ *
+ * Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+
+#include "gps.h"
+#include "ubx.h"
+#include "ubx-parse.h"
+
+#define DEBUG 1
+#if DEBUG
+ #define printd(x, args ...) printf(x, ## args)
+#else
+ #define printd(x, args ...)
+#endif
+
+#define DEBUG1 0
+#if DEBUG1
+ #define printd1(x, args ...) printf(x, ## args)
+#else
+ #define printd1(x, args ...)
+#endif
+
+/* Helpers */
+
+static int
+float_to_fixedpoint(float f, int sf)
+{
+ if (sf < 0) {
+ while (sf++ < 0)
+ f *= 2.0f;
+ } else {
+ while (sf-- > 0)
+ f *= 0.5f;
+ }
+
+ return (int)f;
+}
+
+static inline int
+double_to_fixedpoint(double d, int sf)
+{
+ if (sf < 0) {
+ while (sf++ < 0)
+ d *= 2.0;
+ } else {
+ while (sf-- > 0)
+ d *= 0.5;
+ }
+
+ return (int)d;
+}
+
+
+/* UBX message parsing to fill gps assist data */
+
+static void
+_ubx_msg_parse_nav_posllh(struct ubx_hdr *hdr, void *pl, int pl_len, void *ud)
+{
+ struct ubx_nav_posllh *nav_posllh = pl;
+ struct gps_assist_data *gps = ud;
+
+ printd("[.] NAV_POSLLH\n");
+
+ gps->fields |= GPS_FIELD_REFPOS;
+
+ gps->ref_pos.latitude = (double)(nav_posllh->lat) * 1e-7;
+ gps->ref_pos.longitude = (double)(nav_posllh->lon) * 1e-7;
+ gps->ref_pos.altitude = (double)(nav_posllh->height) * 1e-3;
+
+ printd(" TOW %lu\n", nav_posllh->itow);
+ printd(" latitude %f\n", gps->ref_pos.latitude);
+ printd(" longitude %f\n", gps->ref_pos.longitude);
+ printd(" altitude %f\n", gps->ref_pos.altitude);
+}
+
+static void
+_ubx_msg_parse_aid_ini(struct ubx_hdr *hdr, void *pl, int pl_len, void *ud)
+{
+ struct ubx_aid_ini *aid_ini = pl;
+ struct gps_assist_data *gps = ud;
+
+ printd("[.] AID_INI\n");
+
+ /* Extract info for "Reference Time" */
+ gps->fields |= GPS_FIELD_REFTIME;
+
+ gps->ref_time.wn = aid_ini->wn;
+ gps->ref_time.tow = (double)aid_ini->tow * 1e-3;
+ gps->ref_time.when = time(NULL);
+
+ printd(" WN %d\n", gps->ref_time.wn);
+ printd(" TOW %ld\n", aid_ini->tow);
+
+ if((aid_ini->flags & 0x03) != 0x03) { /* time and pos valid ? */
+ fprintf(stderr, "Postion and/or time not valid (0x%lx)", aid_ini->flags);
+ }
+
+ // FIXME: We could extract ref position as well but we need it in
+ // WGS84 geodetic coordinates and it's provided as ecef, so
+ // we need a lot of math ...
+}
+
+static void
+_ubx_msg_parse_aid_hui(struct ubx_hdr *hdr, void *pl, int pl_len, void *ud)
+{
+ struct ubx_aid_hui *aid_hui = pl;
+ struct gps_assist_data *gps = ud;
+
+ printd("[.] AID_HUI\n");
+
+ if (aid_hui->flags & 0x2) { /* UTC parameters valid */
+ struct gps_utc_model *utc = &gps->utc;
+
+ printd(" UTC\n");
+
+ gps->fields |= GPS_FIELD_UTC;
+
+ utc->a0 = double_to_fixedpoint(aid_hui->utc_a0, -30);
+ utc->a1 = double_to_fixedpoint(aid_hui->utc_a1, -50);
+ utc->delta_t_ls = aid_hui->utc_ls;
+ utc->t_ot = aid_hui->utc_tot >> 12;
+ utc->wn_t = aid_hui->utc_wnt;
+ utc->wn_lsf = aid_hui->utc_wnf;
+ utc->dn = aid_hui->utc_dn;
+ utc->delta_t_lsf = aid_hui->utc_lsf;
+ }
+
+ if (aid_hui->flags & 0x04) { /* Klobuchar parameters valid */
+ struct gps_ionosphere_model *iono = &gps->ionosphere;
+
+ printd(" IONOSPHERE\n");
+
+ gps->fields |= GPS_FIELD_IONOSPHERE;
+
+ iono->alpha_0 = float_to_fixedpoint(aid_hui->klob_a0, -30);
+ iono->alpha_1 = float_to_fixedpoint(aid_hui->klob_a1, -27);
+ iono->alpha_2 = float_to_fixedpoint(aid_hui->klob_a2, -24);
+ iono->alpha_3 = float_to_fixedpoint(aid_hui->klob_a3, -24);
+ iono->beta_0 = float_to_fixedpoint(aid_hui->klob_b0, 11);
+ iono->beta_1 = float_to_fixedpoint(aid_hui->klob_b1, 14);
+ iono->beta_2 = float_to_fixedpoint(aid_hui->klob_b2, 16);
+ iono->beta_3 = float_to_fixedpoint(aid_hui->klob_b3, 16);
+ }
+}
+
+static void
+_ubx_msg_parse_aid_alm(struct ubx_hdr *hdr, void *pl, int pl_len, void *ud)
+{
+ struct ubx_aid_alm *aid_alm = pl;
+ struct gps_assist_data *gps = ud;
+
+ if(pl_len == 8) /* length if not available */
+ return;
+
+ if(pl_len != sizeof(struct ubx_aid_alm)) {
+ fprintf(stderr, "pl_len != sizeof(struct ubx_aid_alm) (%d)\n", pl_len);
+ return;
+ }
+
+ printd("[.] AID_ALM %2ld - %ld (nsv = %d)\n", aid_alm->sv_id, aid_alm->gps_week, gps->almanac.n_sv);
+
+ if (aid_alm->gps_week) {
+ int i = gps->almanac.n_sv++;
+ gps->fields |= GPS_FIELD_ALMANAC;
+ gps->almanac.wna = aid_alm->gps_week & 0xff;
+ gps_unpack_sf45_almanac(aid_alm->alm_words, &gps->almanac.svs[i]);
+ /* set satellite ID this way, otherwise it will be wrong */
+ gps->almanac.svs[i].sv_id = aid_alm->sv_id;
+ }
+}
+
+static void
+_ubx_msg_parse_aid_eph(struct ubx_hdr *hdr, void *pl, int pl_len, void *ud)
+{
+ struct ubx_aid_eph *aid_eph = pl;
+ struct gps_assist_data *gps = ud;
+
+ if(pl_len == 8) /* length if not available */
+ return;
+
+ if(pl_len != sizeof(struct ubx_aid_eph)) {
+ fprintf(stderr, "pl_len != sizeof(struct ubx_aid_eph) (%d)\n", pl_len);
+ return;
+ }
+
+ printd("[.] AID_EPH %2ld - %s (nsv = %d)\n", aid_eph->sv_id, aid_eph->present ? "present" : "", gps->ephemeris.n_sv);
+
+ if (aid_eph->present) {
+ int i = gps->ephemeris.n_sv++;
+ gps->fields |= GPS_FIELD_EPHEMERIS;
+ gps->ephemeris.svs[i].sv_id = aid_eph->sv_id;
+ gps_unpack_sf123(aid_eph->eph_words, &gps->ephemeris.svs[i]);
+ }
+}
+
+
+static void
+_ubx_msg_parse_nav_timegps(struct ubx_hdr *hdr, void *pl, int pl_len, void *ud)
+{
+ struct ubx_nav_timegps *nav_timegps = pl;
+ struct gps_assist_data *gps = ud;
+
+ printd1("[.] NAV_TIMEGPS\n");
+
+ /* Extract info for "Reference Time" */
+ gps->fields |= GPS_FIELD_REFTIME;
+
+ gps->ref_time.wn = nav_timegps->week;
+ gps->ref_time.tow = (double)nav_timegps->itow * 1e-3;
+ gps->ref_time.when = time(NULL);
+
+ printd1(" WN %d\n", nav_timegps->week);
+ printd1(" TOW %ld\n", nav_timegps->itow);
+}
+
+/* Dispatch table */
+struct ubx_dispatch_entry ubx_parse_dt[] = {
+ UBX_DISPATCH(NAV, POSLLH, _ubx_msg_parse_nav_posllh),
+ UBX_DISPATCH(AID, INI, _ubx_msg_parse_aid_ini),
+ UBX_DISPATCH(AID, HUI, _ubx_msg_parse_aid_hui),
+ UBX_DISPATCH(AID, ALM, _ubx_msg_parse_aid_alm),
+ UBX_DISPATCH(AID, EPH, _ubx_msg_parse_aid_eph),
+ UBX_DISPATCH(NAV, TIMEGPS, _ubx_msg_parse_nav_timegps),
+};
+
diff --git a/rrlpd/src/ubx-parse.h b/rrlpd/src/ubx-parse.h
new file mode 100644
index 0000000..621475d
--- /dev/null
+++ b/rrlpd/src/ubx-parse.h
@@ -0,0 +1,45 @@
+/*
+ * ubx-parse.h
+ *
+ * Header for parsing code converting UBX messages to GPS assist data
+ *
+ *
+ * Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UBX_PARSE_H__
+#define __UBX_PARSE_H__
+
+
+#include "gps.h"
+#include "ubx.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Dispatch table */
+extern struct ubx_dispatch_entry ubx_parse_dt[];
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __UBX_PARSE_H__ */
+
diff --git a/rrlpd/src/ubx.c b/rrlpd/src/ubx.c
new file mode 100644
index 0000000..29baa61
--- /dev/null
+++ b/rrlpd/src/ubx.c
@@ -0,0 +1,96 @@
+/*
+ * ubx.c
+ *
+ * Implementation of generic UBX helpers
+ *
+ *
+ * Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include "ubx.h"
+
+
+static void
+ubx_checksum(uint8_t *data, int len, uint8_t *cksum)
+{
+ int i;
+ uint8_t ck0 = 0, ck1 = 0;
+ for (i=0; i<len; i++) {
+ ck0 += data[i];
+ ck1 += ck0;
+ }
+ cksum[0] = ck0;
+ cksum[1] = ck1;
+}
+
+
+static ubx_msg_handler_t
+ubx_find_handler(struct ubx_dispatch_entry *dt, uint8_t msg_class, uint8_t msg_id)
+{
+ while (dt->handler) {
+ if ((dt->msg_class == msg_class) && (dt->msg_id == msg_id))
+ return dt->handler;
+ dt++;
+ }
+ return NULL;
+}
+
+
+int
+ubx_msg_dispatch(struct ubx_dispatch_entry *dt,
+ void *msg, int len, void *userdata)
+{
+ struct ubx_hdr *hdr = msg;
+ uint8_t cksum[2], *cksum_ptr;
+ ubx_msg_handler_t h;
+
+ if(len < 2) {
+ fprintf(stderr, "[!] Length too small (%d)\n", len);
+ return -1;
+ }
+
+ if ((hdr->sync[0] != UBX_SYNC0) || (hdr->sync[1] != UBX_SYNC1)) {
+ fprintf(stderr, "[!] Invalid sync bytes\n");
+ return -1;
+ }
+
+ if(len < sizeof(struct ubx_hdr)) {
+ fprintf(stderr, "[!] Length too small for UBX header (%d)\n", len);
+ return -1;
+ }
+
+ if(len < sizeof(struct ubx_hdr) + hdr->payload_len - 2) {
+ fprintf(stderr, "[!] Length too small for UBX header and payload (%d)\n", len);
+ return -1;
+ }
+
+ ubx_checksum(msg + 2, sizeof(struct ubx_hdr) + hdr->payload_len - 2, cksum);
+ cksum_ptr = msg + (sizeof(struct ubx_hdr) + hdr->payload_len);
+ if ((cksum_ptr[0] != cksum[0]) || (cksum_ptr[1] != cksum[1])) {
+ fprintf(stderr, "[!] Invalid checksum\n");
+ return -1;
+ }
+
+ h = ubx_find_handler(dt, hdr->msg_class, hdr->msg_id);
+ if (h)
+ h(hdr, msg + sizeof(struct ubx_hdr), hdr->payload_len, userdata);
+
+ return sizeof(struct ubx_hdr) + hdr->payload_len + 2;
+}
+
diff --git a/rrlpd/src/ubx.h b/rrlpd/src/ubx.h
new file mode 100644
index 0000000..13ae5fa
--- /dev/null
+++ b/rrlpd/src/ubx.h
@@ -0,0 +1,240 @@
+/*
+ * ubx.h
+ *
+ * Header for UBX related stuff
+ *
+ *
+ * Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UBX_H__
+#define __UBX_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/* Constants used in UBX */
+
+ /* Sync bytes (two first bytes of each message) */
+#define UBX_SYNC0 0xb5
+#define UBX_SYNC1 0x62
+
+ /* UBX messages classes */
+#define UBX_CLASS_NAV 0x01
+#define UBX_CLASS_RXM 0x02
+#define UBX_CLASS_INF 0x04
+#define UBX_CLASS_ACK 0x05
+#define UBX_CLASS_CFG 0x06
+#define UBX_CLASS_UPD 0x09
+#define UBX_CLASS_MON 0x0a
+#define UBX_CLASS_AID 0x0b
+#define UBX_CLASS_TIM 0x0d
+
+ /* UBX messages type ID (by class) */
+#define UBX_NAV_POSECEF 0x01
+#define UBX_NAV_POSLLH 0x02
+#define UBX_NAV_STATUS 0x03
+#define UBX_NAV_DOP 0x04
+#define UBX_NAV_SOL 0x06
+#define UBX_NAV_POSUTM 0x08
+#define UBX_NAV_VELECEF 0x11
+#define UBX_NAV_VELNED 0x12
+#define UBX_NAV_TIMEGPS 0x20
+#define UBX_NAV_TIMEUTC 0x21
+#define UBX_NAV_CLOCK 0x22
+#define UBX_NAV_SVINFO 0x30
+#define UBX_NAV_DGPS 0x31
+#define UBX_NAV_SBAS 0x32
+#define UBX_NAV_EKFSTATUS 0x40
+
+#define UBX_RXM_RAW 0x10
+#define UBX_RXM_SFRB 0x11
+#define UBX_RXM_SVSI 0x20
+#define UBX_RXM_SVSI_GPS 0x20
+#define UBX_RXM_ALM 0x30
+#define UBX_RXM_EPH 0x31
+#define UBX_RXM_POSREQ 0x40
+
+#define UBX_INF_ERROR 0x00
+#define UBX_INF_WARNING 0x01
+#define UBX_INF_NOTICE 0x02
+#define UBX_INF_TEST 0x03
+#define UBX_INF_DEBUG 0x04
+#define UBX_INF_USER 0x07
+
+#define UBX_ACK_NAK 0x00
+#define UBX_ACK_ACK 0x01
+
+#define UBX_CFG_PRT 0x00
+#define UBX_CFG_USB 0x1b
+#define UBX_CFG_MSG 0x01
+#define UBX_CFG_NMEA 0x17
+#define UBX_CFG_RATE 0x08
+#define UBX_CFG_CFG 0x09
+#define UBX_CFG_TP 0x07
+#define UBX_CFG_NAV2 0x1a
+#define UBX_CFG_DAT 0x06
+#define UBX_CFG_INF 0x02
+#define UBX_CFG_RST 0x04
+#define UBX_CFG_RXM 0x11
+#define UBX_CFG_ANT 0x13
+#define UBX_CFG_FXN 0x0e
+#define UBX_CFG_SBAS 0x16
+#define UBX_CFG_LIC 0x80
+#define UBX_CFG_TM 0x10
+#define UBX_CFG_TM2 0x19
+#define UBX_CFG_TMODE 0x1d
+#define UBX_CFG_EKF 0x12
+
+#define UBX_UPD_DOWNL 0x01
+#define UBX_UPD_UPLOAD 0x02
+#define UBX_UPD_EXEC 0x03
+#define UBX_UPD_MEMCPY 0x04
+
+#define UBX_MON_SCHD 0x01
+#define UBX_MON_IO 0x02
+#define UBX_MON_IPC 0x03
+#define UBX_MON_VER 0x04
+#define UBX_MON_EXCEPT 0x05
+#define UBX_MON_MSGPP 0x06
+#define UBX_MON_RXBUF 0x07
+#define UBX_MON_TXBUF 0x08
+#define UBX_MON_HW 0x09
+#define UBX_MON_USB 0x0a
+
+#define UBX_AID_REQ 0x00
+#define UBX_AID_INI 0x01
+#define UBX_AID_HUI 0x02
+#define UBX_AID_DATA 0x10
+#define UBX_AID_ALM 0x30
+#define UBX_AID_EPH 0x31
+
+#define UBX_TIM_TP 0x01
+#define UBX_TIM_TM 0x02
+#define UBX_TIM_TM2 0x03
+#define UBX_TIM_SVIN 0x04
+
+
+/* Header */
+struct ubx_hdr {
+ uint8_t sync[2];
+ uint8_t msg_class;
+ uint8_t msg_id;
+ uint16_t payload_len;
+} __attribute__((packed));
+
+
+/* Payload formats (some of them) */
+struct ubx_nav_posllh {
+ uint32_t itow; /* ms */
+ int32_t lon; /* scaling 1e-7 */
+ int32_t lat; /* scaling 1e-7 */
+ int32_t height;/* mm */
+ int32_t hsl; /* mm */
+ uint32_t hacc; /* mm */
+ uint32_t vacc; /* mm */
+} __attribute__((packed));
+
+struct ubx_aid_ini {
+ int32_t x;
+ int32_t y;
+ int32_t z;
+ uint32_t posacc;
+ uint16_t tm_cfg;
+ uint16_t wn;
+ uint32_t tow; /* ms */
+ int32_t tow_ns;
+ uint32_t tacc_ms;
+ uint32_t tacc_ns;
+ int32_t clkd;
+ uint32_t clkdacc;
+ uint32_t flags;
+} __attribute__((packed));
+
+struct ubx_aid_hui {
+ uint32_t health;
+ double utc_a1;
+ double utc_a0;
+ int32_t utc_tot;
+ int16_t utc_wnt;
+ int16_t utc_ls;
+ int16_t utc_wnf;
+ int16_t utc_dn;
+ int16_t utc_lsf;
+ int16_t utc_spare;
+ float klob_a0;
+ float klob_a1;
+ float klob_a2;
+ float klob_a3;
+ float klob_b0;
+ float klob_b1;
+ float klob_b2;
+ float klob_b3;
+ uint32_t flags;
+} __attribute__((packed));
+
+struct ubx_aid_alm {
+ uint32_t sv_id;
+ uint32_t gps_week;
+ uint32_t alm_words[8]; /* Present only if 'gps_week' != 0 */
+} __attribute__((packed));
+
+struct ubx_aid_eph {
+ uint32_t sv_id;
+ uint32_t present;
+ uint32_t eph_words[24]; /* Present only if 'present' != 0 */
+} __attribute__((packed));
+
+struct ubx_nav_timegps {
+ uint32_t itow; /* ms */
+ int32_t ftow; /* ns */
+ int16_t week;
+ uint8_t leaps;
+ uint8_t valid;
+ uint32_t tacc; /* ns */
+} __attribute__((packed));
+
+/* Message handler */
+typedef void (*ubx_msg_handler_t)(
+ struct ubx_hdr *hdr, void *payload, int payload_len, void *userdata);
+
+struct ubx_dispatch_entry {
+ uint8_t msg_class;
+ uint8_t msg_id;
+ ubx_msg_handler_t handler;
+};
+
+#define UBX_DISPATCH(kls,id,hdl) { \
+ .msg_class = UBX_CLASS_ ## kls , \
+ .msg_id = UBX_ ## kls ## _ ## id, \
+ .handler = (hdl), \
+}
+
+
+/* Methods */
+int ubx_msg_dispatch(struct ubx_dispatch_entry *dt,
+ void *msg, int len, void *userdata);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __UBX_H__ */
+