laforge
/
openbts-osmo
Archived
1
0
Fork 0
This repository has been archived on 2022-03-30. You can view files and clone it, but cannot push or open issues or pull requests.
openbts-osmo/public-trunk/Control/RRLPQueryController.cpp

491 lines
18 KiB
C++

#include <cstdio> // sprintf
#include "GSMLogicalChannel.h"
#include "GSML3RRMessages.h"
#include "RRLPQueryController.h"
#ifdef RRLP_TEST_HACK
// TEMP TEMP TEMP - so it gets to stdout
#define RRLP_LOG_DEBUG LOG(ERROR)
#define RRLP_LOG_NOTICE LOG(ERROR)
#define RRLP_LOG_INFO LOG(ERROR)
#else
#define RRLP_LOG_DEBUG LOG(DEBUG)
#define RRLP_LOG_NOTICE LOG(NOTICE)
#define RRLP_LOG_INFO LOG(INFO)
#endif
// TODO: doing this namespace thing because I don't remember the right way.
namespace GSM {
namespace RRLP {
////////////////////////////////////////////////////////////////////////////////
// RRLPQueryManager
// Helper - should go in the class probably
long getNumWithDefault(const std::string& key, long default_value) {
if (gConfig.defines(key))
return gConfig.getNum(key);
return default_value;
}
RRLPQueryManager::RRLPQueryManager(unsigned accuracy)
: m_accuracy(accuracy)
, m_numQueries(0)
, m_numPositionResponses(0)
{
// override from gConfig
m_accuracy = getNumWithDefault("RRLP.Accuracy", m_accuracy);
}
RRLPQueryManager* RRLPQueryManager::instance()
{
static RRLPQueryManager* instance = 0;
if (instance == 0) {
instance = new RRLPQueryManager(60 /*accuracy*/);
}
return instance;
}
PositionResult RRLPQueryManager::doTransaction(L3MobileIdentity mobID, LogicalChannel* chan
, unsigned accuracy)
{
if (accuracy == 0)
accuracy = m_accuracy;
if (m_data.count(mobID) == 0) {
// first time
m_data.insert(std::pair<L3MobileIdentity, RRLP_MS_Data>(mobID, RRLP_MS_Data()));
}
RRLP_MS_Data& data = m_data[mobID];
data.lastSentReferenceNumber = 1 + (data.lastSentReferenceNumber % 7);
RRLPQueryController QC(chan, data.lastSentReferenceNumber, accuracy);
return record(QC.doTransaction());
}
PositionResult RRLPQueryManager::doTransaction(L3MobileIdentity mobID, LogicalChannel* chan
, BitVector& rrlp_position_request)
{
// we don't store anything in this case. maybe TODO?
RRLPQueryController QC(chan, rrlp_position_request);
return record(QC.doTransaction());
}
PositionResult RRLPQueryManager::record(PositionResult res)
{
m_numQueries ++;
if (res.mValid) m_numPositionResponses++;
return res;
}
////////////////////////////////////////////////////////////////////////////////
// RRLPQueryController
// Create an RRLP Query with certain configurable params.
// requests one set of location, GPS, msBased only, within 2 seconds (2**1).
// referenceNumber - number between 1 and 7 (0 is illegal)
// accuracy - 60 is about 400 meters, generally logarithmic scale. (TODO - add reference)
void buildRRLPQueryFromParams(BitVector& out, unsigned referenceNumber, unsigned int accuracy)
{
if (referenceNumber > 7 || referenceNumber == 0) {
LOG(ERROR) << "referenceNumber = " << referenceNumber;
referenceNumber = 1;
}
if (accuracy < 10) {
LOG(ERROR) << "accuracy = " << accuracy;
accuracy = 60;
}
// 101 0 000 0 00000 0 01 0111100 01 001 1 000
out.resize(32);
out.fillField(0, 1, 32);
size_t wp = 0;
out.writeField(wp, referenceNumber, 3);
// extension bit - leave as 0
wp ++;
// a msrPositionReq, encoded as 0 in 3 bits (choice)
wp += 3;
// no extension
wp += 1;
// no optional components
wp += 5;
// PositionInstruct | no extension
wp += 1;
// PositionInstruct | msBased methodType
out.writeField(wp, 1, 2);
// Accuracy - 7 bits
out.writeField(wp, accuracy, 7);
// PositionMethod - GPS
out.writeField(wp, 1, 2);
// Measure Response time - actually 2^x where we put down x
out.writeField(wp, 1, 3);
// use one or multiple sets for response - we say 1
out.writeField(wp, 1, 1);
// The left 3 bits are fillers to octet boundary
LOG(DEBUG) << "buildRRLPQueryFromParams: ref " << referenceNumber << ", acc "
<< accuracy << ";built " << out;
}
void RRLPQueryController::init(unsigned reference)
{
if (reference > 7 || reference == 0) {
LOG(ERROR) << "Programming error: reference must be in the range [1,7]";
}
m_reference = reference;
m_timeout = 1000; // |- These are defaults, overridable
m_retries = 5; // |
m_retrans_on_ack = false;
m_continue = true;
// initialize assistance data
m_assistance_data.resize(2*8);
m_assistance_data.fillField(0, 0, 16);
// reference number in high 3 bits
// component number in zeros are for no optional components, no extensions.
m_assistance_data.fillField(0, m_reference, 3);
// set component type to ASSISTANCE_DATA (bits 4,5,6)
m_assistance_data.fillField(4, ASSISTANCE_DATA, 3);
}
RRLPQueryController::RRLPQueryController(LogicalChannel* chan,
unsigned int referenceNumber, unsigned int accuracy)
: m_chan(chan)
{
init(referenceNumber);
buildRRLPQueryFromParams(m_rrlp_position_request, m_reference, accuracy);
LOG(DEBUG) << "Reference is " << referenceNumber << ", Assistance is " << m_assistance_data;
}
RRLPQueryController::RRLPQueryController(LogicalChannel* chan,
const BitVector rrlp_position_request)
: m_chan(chan)
, m_rrlp_position_request(rrlp_position_request)
{
init(m_rrlp_position_request.peekField(0, 3));
}
void RRLPQueryController::setReferenceNumber(unsigned reference)
{
m_reference = reference;
m_assistance_data.fillField(0, m_reference, 3);
m_rrlp_position_request.fillField(0, m_reference, 3);
}
void RRLPQueryController::sendRequest()
{
RRLP_LOG_INFO << "sending request " << m_rrlp_position_request;
m_chan->send(L3ApplicationInformation(m_rrlp_position_request));
}
void RRLPQueryController::sendAssistanceData()
{
RRLP_LOG_INFO << "sent assistance data " << m_assistance_data;
m_chan->send(L3ApplicationInformation(m_assistance_data));
}
void RRLPQueryController::parseLocationInfo(size_t &pos, BitVector& rrlp)
{
// Reference Frame
unsigned refFrame = rrlp.readField(pos, 16);
if (refFrame != 65535) {
// TODO - shouldn't be INFO
RRLP_LOG_INFO << "refFrame != 65535";
}
// TOW (Time of week) OPTIONAL
unsigned TOW_Optional = rrlp.readField(pos, 1);
unsigned TOW = 0;
if (TOW_Optional)
TOW = rrlp.readField(pos, 24);
else
RRLP_LOG_NOTICE << "unexpected non existance of TOW field in MsrPositionRsp";
// 1 fixType - (0 is 2D, 1 is 3D) 1
unsigned fixType = rrlp.readField(pos, 1);
// 5 size of Ext-GeographicalInformation 0..20 (5 bits) 01101
unsigned ExtGeographicalInformation_Size = rrlp.readField(pos, 5);
// 4 Kind of Ext-GeographicalInformation. 9 is 1001
unsigned ExtGeographicalInformation_Kind = rrlp.readField(pos, 4);
// 4 0000
pos += 4; // Spare bits
// The following fields should be there regardless, but still better safe then sorry
if (rrlp.size() - pos < 48) {
LOG(ERROR) << "unexpectedly small MsrPositionRsp, missing Lat/Lon";
return;
}
// 1 Latitude Sign 0
unsigned LatSign = rrlp.readField(pos, 1);
// 23 Latitude 01101011011010011100011
unsigned Lat23 = rrlp.readField(pos, 23);
// 24 Longitude 101010001111001001010011
unsigned Lon24 = rrlp.readField(pos, 24);
// Finally, store this
m_pr.mValid = true;
m_pr.mPos.mLat = (1.0 - LatSign*2.0) * Lat23 * 90.0 / (1 << 23);
m_pr.mPos.mLon = Lon24 * 360.0 / (1 << 24);
// Now we actually branch according to the kind - but we don't actually care for this right now.
// so just log this stuff.
RRLP_LOG_INFO << "unparsed " << (rrlp.size() - pos) << " bits (following the Lat/Lon) of a " << ExtGeographicalInformation_Kind;
// 1 Altitude Direction 1
// 15 Altitude 000000000010001
// 1 Spare 0
// 7 Uncertainty semi-major 0000110
// 1 Spare 0
// 7 Uncertainty semi-minor 0000110
// 8 Orientatin of major axis 00110010
// 1 Spare 0
// 7 Uncertainty Altitude 0001000
// 1 Spare 0
// 7 Confidence 1000100
// 2 Padding (to make length a multiple of 8) 00
}
/**
Implemente decoding of a Measurement Position Response.
conservative - decode what we know, complain but don't interfer
if we don't.
what we want to know:
parsing a packet with a LocationInfo
parsing a packet that says not enough satellites
parsing a packet that says in need of assistance data
we only do something with the first: record the location in m_pr
TODO - this should follow the structure of OpenBTS code, namely this
code should be in some sort of Message Heirarchy for RRLP (minimal,
but with a base class etc.) and this function should be the decoder
for a MsrPositionRsp class instance.
TODO: The input is a L3Frame basically, but I'm using BitVector type for easy tester
- actually not sure if I need to.
*/
void RRLPQueryController::parseMsrPositionResponse(BitVector& rrlp)
{
// Example:
// 0,24 Header, not interesting (checked by caller) 000001100011100000000000
// 24,8 Length of rest of packet 00010110
size_t pos = 32; // start by skipping the APDU header and the TV header for the PDU
// Reference Number
pos += 3;
// PDU Extension bit
unsigned PDU_Extension= rrlp.readField(pos, 1);
RRLP_LOG_DEBUG << rrlp.segment(0, 32);
RRLP_LOG_DEBUG << rrlp.segment(32, rrlp.size() - 32);
if (PDU_Extension) {
RRLP_LOG_NOTICE << "Not handling an extended PDU";
return;
}
// Component Selection
if (rrlp.readField(pos, 3) != MSR_POSITION_RSP) {
LOG(ERROR) << "parseMsrPositionResponse called with a non MsrPositionRsp packet";
return;
}
// MsrPositionRsp Extension bit
unsigned MsrPositionRsp_Extension = rrlp.readField(pos, 1);
if (MsrPositionRsp_Extension) {
RRLP_LOG_NOTICE << "Not handling an extended MsrPositionRsp";
return;
}
// MsrPositionRsp Optional bits
unsigned MultipleSets_Optional = rrlp.readField(pos, 1);
unsigned ReferenceIdentity_Optional = rrlp.readField(pos, 1);
unsigned OTD_MeasureInfo_Optional = rrlp.readField(pos, 1);
unsigned LocationInfo_Optional = rrlp.readField(pos, 1);
unsigned GPS_MeasureInfo_Optional = rrlp.readField(pos, 1);
unsigned LocationError_Optional = rrlp.readField(pos, 1);
unsigned ExtensionContainer_Optional = rrlp.readField(pos, 1);
// The ones before the handled components are catastrophic, since they take up
// a yet unknown number of bits (because we didn't read the spec and implement it).
// The ones after are not so bad, since they won't bother us, we'll just ignore them.
if (MultipleSets_Optional || ReferenceIdentity_Optional || OTD_MeasureInfo_Optional) {
LOG(ERROR) << "UNIMPLEMENTED optional component in MsrPositionResponse, packet unparsed (Multi, Ref, OTD): "
<< MultipleSets_Optional << ", " << ReferenceIdentity_Optional << ", " << OTD_MeasureInfo_Optional << ": at " << pos;
return;
}
if (GPS_MeasureInfo_Optional || ExtensionContainer_Optional || LocationError_Optional) {
LOG(ERROR) << "UNIMPLEMENTED optional component in MsrPositionResponse, information thrown (GPSMeasure, Extension, Error): "
<< GPS_MeasureInfo_Optional << ", " << ExtensionContainer_Optional << ", " << LocationError_Optional;
}
// TODO: basically parsing is pretty simple - for any optional bit, BY ORDER, you parse the packet with a delegate function.
// right now just LocationInfo is parsed.
if (LocationInfo_Optional)
parseLocationInfo(pos, rrlp);
// 0,3 Reference Number 001
// don't care right now (also, handled by caller)
// 3,1 RRLP-Component Extension bit 0
// 4,3 RRLP-Component Selection 001
// Should be 1 for MsrPositionResponse (checked by caller)
// 7,1 MsrPosition-Rsp Extension bit 0
// 8,1 MultipleSets exists 0
// 9,1 ReferenceIdentify exists 0
// 10,1 OTD-MeasureInfo exists 0
// 11,1 LocationInfo exists 1
// 12,1 GPS-MeasureInfo exists 0
// 13,1 LocationError exists 0
// 14,1 ExtensionContainer exists 0
// 15,16 refFrame (0..65535) 1111111111111111
// 31,1 gpsTOW existance 1
// 32,24 gpsTOW (0..14399999 hence 24 bits) 010010101111101111011110
// 56,1 fixType - (0 is 2D, 1 is 3D) 1
// 57,5 size of Ext-GeographicalInformation 0..20 (5 bits) 01101
// 62,4 Kind of Ext-GeographicalInformation. 9 is 1001
// 66,4 0000
// 70,1 Latitude Sign 0
// 71,23 Latitude 01101011011010011100011
// 94,24 Longitude 101010001111001001010011
// 118,1 Altitude Direction 1
// 119,15 Altitude 000000000010001
// 134,1 Spare 0
// 135,7 Uncertainty semi-major 0000110
// 142,1 Spare 0
// 143,7 Uncertainty semi-minor 0000110
// 150,8 Orientatin of major axis 00110010
// 158,1 Spare 0
// 159,7 Uncertainty Altitude 0001000
// 166,1 Spare 0
// 167,7 Confidence 1000100
// 174,2 Padding (to make length a multiple of 8) 00
}
/** Return: True if successful, False otherwise (means retry or fail completly)
*/
bool RRLPQueryController::parseSingleAPDU(L3Frame* resp)
{
// TODO - this is basically parsing the RRLP - refactor
bool component_extension = resp->peekField(32+3, 1) == 1;
unsigned rrlp_component = (component_extension ? -1 :
resp->peekField(32+3+1, 3)); // 32 = 24 APDU + 8 TV Length,
// 3 = Reference Number, 1 Extension for Component.
// 3 bits for the Component enum (in case of no extension)
if (rrlp_component == ASSISTANCE_DATA_ACK) {
LOG(INFO) << "Unexpected Assistance Data Acknowledge.";
if (m_retrans_on_ack)
sendRequest();
return true;
}
if (rrlp_component != MSR_POSITION_RSP) {
LOG(INFO) << "Unexpected RRLP response, component " << rrlp_component;
return true;
}
parseMsrPositionResponse(*resp);
return false;
}
const char* RR_MTI_To_String(unsigned MTI)
{
switch (MTI) {
case 18: return "RR STATUS";
};
static char buf[1024];
sprintf(buf, "unknown %d", MTI);
return buf;
}
/** Return: True if successful, False otherwise (means retry or fail completly)
*/
bool RRLPQueryController::doSingleReceive()
{
// Receive an L3 frame with a timeout. Timeout is RRLP max + 1 second.
L3Frame* resp = m_chan->recv(m_timeout);
if (!resp) {
LOG(INFO) << "timed out on recieve";
return true;
}
bool retry = false;
// TODO: <rant> This is really ugly. Basically we just want to parse the response. But
// this is complicated by the fact that some phones (G1..) return unexpected
// messages. This is technically allowed (they can say anything on the channel),
// so I think the solution should be systematic.
// </rant>
// Retry (up to limit of 3) Wehn:
// * We get GPRS Suspension Requests for some reason from the G1
// * We get an RRLP Assistance Data Ack even though we haven't sent any
const unsigned PD_RR = 6;
const unsigned MTI_RR_GPRS_SUSPENSION_REQUEST = 52;
const unsigned MTI_RR_STATUS = 18;
const unsigned MTI_RR_APDU = 56;
unsigned cause = 0; // only used in case of MTI_RR_STATUS
if (resp) {
LOG(INFO) << "received " << *resp;
if (resp->primitive() == DATA && resp->PD() == PD_RR) {
// Is an RR
switch (resp->MTI()) {
case MTI_RR_GPRS_SUSPENSION_REQUEST:
LOG(INFO) <<
"received GPRS SUSPENSION REQUEST, rereceiving";
retry = true;
break;
case MTI_RR_APDU:
retry = parseSingleAPDU(resp);
break;
case MTI_RR_STATUS:
cause = resp->peekField(16, 8);
switch (cause) {
case 97:
LOG(INFO) << "MS says: message not implemented";
m_continue = false;
break;
case 98:
LOG(INFO) << "MS says: message type not compatible with protocol state";
m_continue = false;
break;
default:
// never saw this.
LOG(INFO) <<
"unknown RR_STATUS response, cause = "
<< cause;
retry = true;
}
break;
default:
LOG(INFO) << "received unexpected RR Message "
<< RR_MTI_To_String(resp->MTI());
}
} else {
// Not RR
LOG(INFO) << "didn't receive a RR message, or primitive != DATA";
retry = true;
}
}
delete resp;
return retry;
}
PositionResult RRLPQueryController::doTransaction()
{
// optional parameters, defaults used otherwise (see constructor)
if (gConfig.defines("RRLP.Retries"))
m_retries = gConfig.getNum("RRLP.Retries");
if (gConfig.defines("RRLP.Timeout"))
m_timeout = gConfig.getNum("RRLP.Timeout");
LOG(INFO) << "doTransaction: Assistance " << m_assistance_data
<< ", Query " << m_rrlp_position_request
<< ", " << m_retries << "x" << m_timeout;
m_pr.mValid = false;
sendAssistanceData();
m_retrans_on_ack = false; // don't retrans after the assistance data.
doSingleReceive();
if (!m_continue) return m_pr; // stopped short on account of not implemented probably.
m_retrans_on_ack = true; // now we do want retrans. might wake up the damn thing.
if (m_pr.mValid) return m_pr; // shouldn't happen, but I don't want to throw it if I get it.
sendRequest();
while (m_continue && m_retries-- > 0 && doSingleReceive()) {};
return m_pr;
}
////////////////////////////////////////////////////////////////////////////////
// Testing Constructor - parse a message
static BitVector g_testing_vec_char;
RRLPQueryController::RRLPQueryController(BitVector& pdu)
: m_rrlp_position_request(g_testing_vec_char)
{
parseMsrPositionResponse(pdu);
}
}; // namespace RRLP
}; // namespace GSM