diff options
author | Harald Welte <laforge@gnumonks.org> | 2012-07-18 22:04:09 +0200 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2012-07-18 22:22:47 +0200 |
commit | 47ab210af93a909e5eb8168d1ff58d2401519570 (patch) | |
tree | dad92939b7a5aab639a2aaae367ba3ef191979be /rrlpd | |
parent | a5c8d8068840bbc216c132d7f8b42b3167c43b9f (diff) |
Diffstat (limited to 'rrlpd')
-rw-r--r-- | rrlpd/README | 46 | ||||
-rw-r--r-- | rrlpd/patches_OpenBSC/diff_gsm_04_08 | 27 | ||||
-rw-r--r-- | rrlpd/patches_OpenBSC/diff_rrlp | 289 | ||||
-rw-r--r-- | rrlpd/patches_asn1c/01_fix_per_encoding_dieter.diff | 17 | ||||
-rw-r--r-- | rrlpd/src/Makefile | 41 | ||||
-rw-r--r-- | rrlpd/src/asn1/rrlp.asn | 1055 | ||||
-rw-r--r-- | rrlpd/src/gps.c | 131 | ||||
-rw-r--r-- | rrlpd/src/gps.h | 193 | ||||
-rw-r--r-- | rrlpd/src/main.c | 1298 | ||||
-rw-r--r-- | rrlpd/src/rrlp.c | 864 | ||||
-rw-r--r-- | rrlpd/src/rrlp.h | 65 | ||||
-rw-r--r-- | rrlpd/src/ubx-parse.c | 246 | ||||
-rw-r--r-- | rrlpd/src/ubx-parse.h | 45 | ||||
-rw-r--r-- | rrlpd/src/ubx.c | 96 | ||||
-rw-r--r-- | rrlpd/src/ubx.h | 240 |
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__ */ + |