Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor
This commit is contained in:
112
floorplan/v2/FloorplanCeilings.h
Normal file
112
floorplan/v2/FloorplanCeilings.h
Normal file
@@ -0,0 +1,112 @@
|
||||
#ifndef FLOORPLANCEILINGS_H
|
||||
#define FLOORPLANCEILINGS_H
|
||||
|
||||
#include "Floorplan.h"
|
||||
|
||||
namespace Floorplan {
|
||||
|
||||
/**
|
||||
* helper-class for floorplan ceilings
|
||||
* e.g. to determine the number of ceilings between two given positions
|
||||
*/
|
||||
class Ceilings {
|
||||
|
||||
private:
|
||||
|
||||
/** position (height) of all ceilings (in meter) */
|
||||
std::vector<float> ceilingsAtHeight_m;
|
||||
|
||||
public:
|
||||
|
||||
/** empty ctor */
|
||||
Ceilings() {
|
||||
;
|
||||
}
|
||||
|
||||
/** ctor with the map to work with */
|
||||
Ceilings(const IndoorMap* map) {
|
||||
|
||||
// sanity checks
|
||||
Assert::isTrue(map->floors.size() >= 1, "map has no floors?!");
|
||||
|
||||
// get position of all ceilings
|
||||
for (Floorplan::Floor* f : map->floors) {
|
||||
|
||||
const float h1 = f->atHeight;
|
||||
const float h2 = f->atHeight + f->height;
|
||||
|
||||
if (std::find(ceilingsAtHeight_m.begin(), ceilingsAtHeight_m.end(), h1) == ceilingsAtHeight_m.end()) {
|
||||
ceilingsAtHeight_m.push_back(h1);
|
||||
}
|
||||
if (std::find(ceilingsAtHeight_m.begin(), ceilingsAtHeight_m.end(), h2) == ceilingsAtHeight_m.end()) {
|
||||
ceilingsAtHeight_m.push_back(h2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** get the number of ceilings between z1 and z2 */
|
||||
float numCeilingsBetweenFloat(const Point3 pos1, const Point3 pos2) const {
|
||||
|
||||
// sanity checks
|
||||
Assert::isNot0(ceilingsAtHeight_m.size(), "no ceilings available for testing! incorrect map?");
|
||||
|
||||
const float zMin = std::min(pos1.z, pos2.z);
|
||||
const float zMax = std::max(pos1.z, pos2.z);
|
||||
|
||||
float cnt = 0;
|
||||
|
||||
for (const float z : ceilingsAtHeight_m) {
|
||||
if (zMin < z && zMax > z) {
|
||||
const float dmax = zMax - z;
|
||||
cnt += (dmax > 1) ? (1) : (dmax);
|
||||
}
|
||||
}
|
||||
|
||||
return cnt;
|
||||
|
||||
}
|
||||
|
||||
/** get the number of ceilings between z1 and z2 */
|
||||
int numCeilingsBetween(const Point3 pos1, const Point3 pos2) const {
|
||||
|
||||
// sanity checks
|
||||
Assert::isNot0(ceilingsAtHeight_m.size(), "no ceilings available for testing! incorrect map?");
|
||||
|
||||
// find min and max height given the to-be-compared points
|
||||
const float zMin = std::min(pos1.z, pos2.z);
|
||||
const float zMax = std::max(pos1.z, pos2.z);
|
||||
|
||||
#ifdef WITH_ASSERTIONS
|
||||
|
||||
static int numNear = 0;
|
||||
static int numFar = 0;
|
||||
for (const float z : ceilingsAtHeight_m) {
|
||||
const float diff = std::min( std::abs(z-zMin), std::abs(z-zMax) );
|
||||
if (diff < 0.1) {++numNear;} else {++numFar;}
|
||||
}
|
||||
if ((numNear + numFar) > 150000) {
|
||||
Assert::isTrue(numNear < numFar*0.1,
|
||||
"many requests to Floorplan::Ceilings::numCeilingsBetween address nodes (very) near to a ground! \
|
||||
due to rounding issues, determining the number of floors between AP and point-in-question is NOT possible! \
|
||||
expect very wrong outputs! \
|
||||
consider adding the person's height to the questioned positions: p += Point3(0,0,1.3) "
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
int cnt = 0;
|
||||
for (const float z : ceilingsAtHeight_m) {
|
||||
if (zMin < z && zMax > z) {++cnt;}
|
||||
}
|
||||
|
||||
return cnt;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLANCEILINGS_H
|
||||
@@ -7,11 +7,28 @@
|
||||
|
||||
#include "Floorplan.h"
|
||||
|
||||
#include "../../sensors/MACAddress.h"
|
||||
|
||||
/**
|
||||
* helper methods for the floorplan
|
||||
*/
|
||||
class FloorplanHelper {
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/** get the AP for the given MAC [if available] */
|
||||
static std::pair<Floorplan::AccessPoint*, Floorplan::Floor*> getAP(const Floorplan::IndoorMap* map, const MACAddress& mac) {
|
||||
for (Floorplan::Floor* f : map->floors) {
|
||||
for (Floorplan::AccessPoint* ap : f->accesspoints) {
|
||||
if (MACAddress(ap->mac) == mac) {
|
||||
return std::make_pair(ap, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::make_pair(nullptr, nullptr);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/** align all floorplan values to the given grid size. needed for the grid factory */
|
||||
|
||||
@@ -35,11 +35,12 @@ namespace Floorplan {
|
||||
if (res != tinyxml2::XMLError::XML_SUCCESS) {
|
||||
throw Exception(
|
||||
std::string() + "error while loading XML " + file + "\n" +
|
||||
((doc.GetErrorStr1()) ? (doc.GetErrorStr1()) : ("")) + "\n" +
|
||||
((doc.GetErrorStr2()) ? (doc.GetErrorStr2()) : (""))
|
||||
((doc.GetErrorStr1()) ? (doc.GetErrorStr1()) : ("")) + "\n" +
|
||||
((doc.GetErrorStr2()) ? (doc.GetErrorStr2()) : (""))
|
||||
);
|
||||
}
|
||||
return parse(doc);
|
||||
IndoorMap* map = parse(doc);
|
||||
return map;
|
||||
}
|
||||
|
||||
/** read an IndoorMap from the given XMl-string */
|
||||
@@ -51,16 +52,17 @@ namespace Floorplan {
|
||||
if (res != tinyxml2::XMLError::XML_SUCCESS) {
|
||||
throw Exception(
|
||||
std::string() + "error while parsing XML\n" +
|
||||
((doc.GetErrorStr1()) ? (doc.GetErrorStr1()) : ("")) + "\n" +
|
||||
((doc.GetErrorStr2()) ? (doc.GetErrorStr2()) : (""))
|
||||
((doc.GetErrorStr1()) ? (doc.GetErrorStr1()) : ("")) + "\n" +
|
||||
((doc.GetErrorStr2()) ? (doc.GetErrorStr2()) : (""))
|
||||
);
|
||||
}
|
||||
return parse(doc);
|
||||
IndoorMap* map = parse(doc);
|
||||
return map;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
#define FOREACH_NODE(out, in) for( const XMLElem* out = (XMLElem*) in->FirstChild(); out; out = (XMLElem*) out->NextSibling() )
|
||||
#define FOREACH_NODE(out, in) for( const XMLElem* out = in->FirstChildElement(); out; out = out->NextSiblingElement() )
|
||||
|
||||
static void assertNode(const std::string& node, const XMLElem* el) {
|
||||
std::string err = std::string("unexpected node '") + el->Name() + "' expected '" + node + "'";
|
||||
@@ -69,7 +71,7 @@ namespace Floorplan {
|
||||
|
||||
/** parse the complete document */
|
||||
static IndoorMap* parse(tinyxml2::XMLDocument& doc) {
|
||||
return parseMap((XMLElem*)doc.FirstChild());
|
||||
return parseMap(doc.FirstChildElement());
|
||||
}
|
||||
|
||||
/** parse the <map> node */
|
||||
@@ -266,7 +268,9 @@ namespace Floorplan {
|
||||
const XMLElem* sub = n->FirstChildElement();
|
||||
while(sub) {
|
||||
// <entry key="123">abc</entry>
|
||||
elem->add(sub->Attribute("key"), sub->FirstChild()->Value());
|
||||
const std::string key = sub->Attribute("key");
|
||||
const std::string val = sub->GetText();
|
||||
elem->add(key, val);
|
||||
sub = sub->NextSiblingElement();
|
||||
}
|
||||
return elem;
|
||||
@@ -402,7 +406,7 @@ namespace Floorplan {
|
||||
FloorOutline outline;
|
||||
FOREACH_NODE(n, el) {
|
||||
if (std::string("polygon") == n->Name()) {
|
||||
outline.push_back(parseFloorPolygon(n)); // TODO
|
||||
outline.push_back(parseFloorPolygon(n));
|
||||
}
|
||||
}
|
||||
return outline;
|
||||
@@ -427,6 +431,9 @@ namespace Floorplan {
|
||||
poly.points.push_back(p2);
|
||||
}
|
||||
}
|
||||
if (poly.points.size() < 4 || poly.points.size() > 1024) {
|
||||
throw Exception("detected invalid outline-polygon during XML parsing");
|
||||
}
|
||||
return poly;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ struct EarthPos {
|
||||
float height;
|
||||
|
||||
/** ctor with values */
|
||||
EarthPos(const double lat, const double lon, const float height) : lon(lon), lat(lat), height(height) {
|
||||
EarthPos(const double lat, const double lon, const float height) : lat(lat), lon(lon), height(height) {
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define POINT2_H
|
||||
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
/**
|
||||
* 2D Point
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -30,6 +30,9 @@ distribution.
|
||||
# include <stdio.h>
|
||||
# include <stdlib.h>
|
||||
# include <string.h>
|
||||
# if defined(__PS3__)
|
||||
# include <stddef.h>
|
||||
# endif
|
||||
#else
|
||||
# include <cctype>
|
||||
# include <climits>
|
||||
@@ -37,6 +40,7 @@ distribution.
|
||||
# include <cstdlib>
|
||||
# include <cstring>
|
||||
#endif
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
TODO: intern strings instead of allocation.
|
||||
@@ -68,6 +72,8 @@ distribution.
|
||||
# else
|
||||
# define TINYXML2_LIB
|
||||
# endif
|
||||
#elif __GNUC__ >= 4
|
||||
# define TINYXML2_LIB __attribute__((visibility("default")))
|
||||
#else
|
||||
# define TINYXML2_LIB
|
||||
#endif
|
||||
@@ -92,9 +98,9 @@ distribution.
|
||||
/* Versioning, past 1.0.14:
|
||||
http://semver.org/
|
||||
*/
|
||||
static const int TIXML2_MAJOR_VERSION = 3;
|
||||
static const int TIXML2_MAJOR_VERSION = 4;
|
||||
static const int TIXML2_MINOR_VERSION = 0;
|
||||
static const int TIXML2_PATCH_VERSION = 0;
|
||||
static const int TIXML2_PATCH_VERSION = 1;
|
||||
|
||||
namespace tinyxml2
|
||||
{
|
||||
@@ -121,18 +127,20 @@ public:
|
||||
NEEDS_NEWLINE_NORMALIZATION = 0x02,
|
||||
NEEDS_WHITESPACE_COLLAPSING = 0x04,
|
||||
|
||||
TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,
|
||||
TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,
|
||||
TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION,
|
||||
ATTRIBUTE_NAME = 0,
|
||||
ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,
|
||||
ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION,
|
||||
COMMENT = NEEDS_NEWLINE_NORMALIZATION
|
||||
ATTRIBUTE_NAME = 0,
|
||||
ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,
|
||||
ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION,
|
||||
COMMENT = NEEDS_NEWLINE_NORMALIZATION
|
||||
};
|
||||
|
||||
StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {}
|
||||
~StrPair();
|
||||
|
||||
void Set( char* start, char* end, int flags ) {
|
||||
TIXMLASSERT( start );
|
||||
TIXMLASSERT( end );
|
||||
Reset();
|
||||
_start = start;
|
||||
_end = end;
|
||||
@@ -152,13 +160,13 @@ public:
|
||||
|
||||
void SetStr( const char* str, int flags=0 );
|
||||
|
||||
char* ParseText( char* in, const char* endTag, int strFlags );
|
||||
char* ParseText( char* in, const char* endTag, int strFlags, int* curLineNumPtr );
|
||||
char* ParseName( char* in );
|
||||
|
||||
void TransferTo( StrPair* other );
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
void Reset();
|
||||
void CollapseWhitespace();
|
||||
|
||||
enum {
|
||||
@@ -203,7 +211,8 @@ public:
|
||||
void Push( T t ) {
|
||||
TIXMLASSERT( _size < INT_MAX );
|
||||
EnsureCapacity( _size+1 );
|
||||
_mem[_size++] = t;
|
||||
_mem[_size] = t;
|
||||
++_size;
|
||||
}
|
||||
|
||||
T* PushArr( int count ) {
|
||||
@@ -217,7 +226,8 @@ public:
|
||||
|
||||
T Pop() {
|
||||
TIXMLASSERT( _size > 0 );
|
||||
return _mem[--_size];
|
||||
--_size;
|
||||
return _mem[_size];
|
||||
}
|
||||
|
||||
void PopArr( int count ) {
|
||||
@@ -311,7 +321,7 @@ public:
|
||||
/*
|
||||
Template child class to create pools of the correct type.
|
||||
*/
|
||||
template< int SIZE >
|
||||
template< int ITEM_SIZE >
|
||||
class MemPoolT : public MemPool
|
||||
{
|
||||
public:
|
||||
@@ -334,7 +344,7 @@ public:
|
||||
}
|
||||
|
||||
virtual int ItemSize() const {
|
||||
return SIZE;
|
||||
return ITEM_SIZE;
|
||||
}
|
||||
int CurrentAllocs() const {
|
||||
return _currentAllocs;
|
||||
@@ -346,21 +356,23 @@ public:
|
||||
Block* block = new Block();
|
||||
_blockPtrs.Push( block );
|
||||
|
||||
for( int i=0; i<COUNT-1; ++i ) {
|
||||
block->chunk[i].next = &block->chunk[i+1];
|
||||
Item* blockItems = block->items;
|
||||
for( int i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) {
|
||||
blockItems[i].next = &(blockItems[i + 1]);
|
||||
}
|
||||
block->chunk[COUNT-1].next = 0;
|
||||
_root = block->chunk;
|
||||
blockItems[ITEMS_PER_BLOCK - 1].next = 0;
|
||||
_root = blockItems;
|
||||
}
|
||||
void* result = _root;
|
||||
Item* const result = _root;
|
||||
TIXMLASSERT( result != 0 );
|
||||
_root = _root->next;
|
||||
|
||||
++_currentAllocs;
|
||||
if ( _currentAllocs > _maxAllocs ) {
|
||||
_maxAllocs = _currentAllocs;
|
||||
}
|
||||
_nAllocs++;
|
||||
_nUntracked++;
|
||||
++_nAllocs;
|
||||
++_nUntracked;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -369,20 +381,21 @@ public:
|
||||
return;
|
||||
}
|
||||
--_currentAllocs;
|
||||
Chunk* chunk = static_cast<Chunk*>( mem );
|
||||
Item* item = static_cast<Item*>( mem );
|
||||
#ifdef DEBUG
|
||||
memset( chunk, 0xfe, sizeof(Chunk) );
|
||||
memset( item, 0xfe, sizeof( *item ) );
|
||||
#endif
|
||||
chunk->next = _root;
|
||||
_root = chunk;
|
||||
item->next = _root;
|
||||
_root = item;
|
||||
}
|
||||
void Trace( const char* name ) {
|
||||
printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n",
|
||||
name, _maxAllocs, _maxAllocs*SIZE/1024, _currentAllocs, SIZE, _nAllocs, _blockPtrs.Size() );
|
||||
name, _maxAllocs, _maxAllocs * ITEM_SIZE / 1024, _currentAllocs,
|
||||
ITEM_SIZE, _nAllocs, _blockPtrs.Size() );
|
||||
}
|
||||
|
||||
void SetTracked() {
|
||||
_nUntracked--;
|
||||
--_nUntracked;
|
||||
}
|
||||
|
||||
int Untracked() const {
|
||||
@@ -398,21 +411,23 @@ public:
|
||||
// 16k: 5200
|
||||
// 32k: 4300
|
||||
// 64k: 4000 21000
|
||||
enum { COUNT = (4*1024)/SIZE }; // Some compilers do not accept to use COUNT in private part if COUNT is private
|
||||
// Declared public because some compilers do not accept to use ITEMS_PER_BLOCK
|
||||
// in private part if ITEMS_PER_BLOCK is private
|
||||
enum { ITEMS_PER_BLOCK = (4 * 1024) / ITEM_SIZE };
|
||||
|
||||
private:
|
||||
MemPoolT( const MemPoolT& ); // not supported
|
||||
void operator=( const MemPoolT& ); // not supported
|
||||
|
||||
union Chunk {
|
||||
Chunk* next;
|
||||
char mem[SIZE];
|
||||
union Item {
|
||||
Item* next;
|
||||
char itemData[ITEM_SIZE];
|
||||
};
|
||||
struct Block {
|
||||
Chunk chunk[COUNT];
|
||||
Item items[ITEMS_PER_BLOCK];
|
||||
};
|
||||
DynArray< Block*, 10 > _blockPtrs;
|
||||
Chunk* _root;
|
||||
Item* _root;
|
||||
|
||||
int _currentAllocs;
|
||||
int _nAllocs;
|
||||
@@ -485,7 +500,6 @@ public:
|
||||
// WARNING: must match XMLDocument::_errorNames[]
|
||||
enum XMLError {
|
||||
XML_SUCCESS = 0,
|
||||
XML_NO_ERROR = 0,
|
||||
XML_NO_ATTRIBUTE,
|
||||
XML_WRONG_ATTRIBUTE_TYPE,
|
||||
XML_ERROR_FILE_NOT_FOUND,
|
||||
@@ -513,19 +527,23 @@ enum XMLError {
|
||||
/*
|
||||
Utility functionality.
|
||||
*/
|
||||
class XMLUtil
|
||||
class TINYXML2_LIB XMLUtil
|
||||
{
|
||||
public:
|
||||
static const char* SkipWhiteSpace( const char* p ) {
|
||||
static const char* SkipWhiteSpace( const char* p, int* curLineNumPtr ) {
|
||||
TIXMLASSERT( p );
|
||||
|
||||
while( IsWhiteSpace(*p) ) {
|
||||
if (curLineNumPtr && *p == '\n') {
|
||||
++(*curLineNumPtr);
|
||||
}
|
||||
++p;
|
||||
}
|
||||
TIXMLASSERT( p );
|
||||
return p;
|
||||
}
|
||||
static char* SkipWhiteSpace( char* p ) {
|
||||
return const_cast<char*>( SkipWhiteSpace( const_cast<const char*>(p) ) );
|
||||
static char* SkipWhiteSpace( char* p, int* curLineNumPtr ) {
|
||||
return const_cast<char*>( SkipWhiteSpace( const_cast<const char*>(p), curLineNumPtr ) );
|
||||
}
|
||||
|
||||
// Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't
|
||||
@@ -556,6 +574,9 @@ public:
|
||||
if ( p == q ) {
|
||||
return true;
|
||||
}
|
||||
TIXMLASSERT( p );
|
||||
TIXMLASSERT( q );
|
||||
TIXMLASSERT( nChar >= 0 );
|
||||
return strncmp( p, q, nChar ) == 0;
|
||||
}
|
||||
|
||||
@@ -575,6 +596,7 @@ public:
|
||||
static void ToStr( bool v, char* buffer, int bufferSize );
|
||||
static void ToStr( float v, char* buffer, int bufferSize );
|
||||
static void ToStr( double v, char* buffer, int bufferSize );
|
||||
static void ToStr(int64_t v, char* buffer, int bufferSize);
|
||||
|
||||
// converts strings to primitive types
|
||||
static bool ToInt( const char* str, int* value );
|
||||
@@ -582,6 +604,18 @@ public:
|
||||
static bool ToBool( const char* str, bool* value );
|
||||
static bool ToFloat( const char* str, float* value );
|
||||
static bool ToDouble( const char* str, double* value );
|
||||
static bool ToInt64(const char* str, int64_t* value);
|
||||
|
||||
// Changes what is serialized for a boolean value.
|
||||
// Default to "true" and "false". Shouldn't be changed
|
||||
// unless you have a special testing or compatibility need.
|
||||
// Be careful: static, global, & not thread safe.
|
||||
// Be sure to set static const memory as parameters.
|
||||
static void SetBoolSerialization(const char* writeTrue, const char* writeFalse);
|
||||
|
||||
private:
|
||||
static const char* writeBoolTrue;
|
||||
static const char* writeBoolFalse;
|
||||
};
|
||||
|
||||
|
||||
@@ -687,6 +721,9 @@ public:
|
||||
*/
|
||||
void SetValue( const char* val, bool staticMem=false );
|
||||
|
||||
/// Gets the line number the node is in, if the document was parsed from a file.
|
||||
int GetLineNum() const { return _parseLineNum; }
|
||||
|
||||
/// Get the parent of this node on the DOM.
|
||||
const XMLNode* Parent() const {
|
||||
return _parent;
|
||||
@@ -852,15 +889,30 @@ public:
|
||||
*/
|
||||
virtual bool Accept( XMLVisitor* visitor ) const = 0;
|
||||
|
||||
/**
|
||||
Set user data into the XMLNode. TinyXML-2 in
|
||||
no way processes or interprets user data.
|
||||
It is initially 0.
|
||||
*/
|
||||
void SetUserData(void* userData) { _userData = userData; }
|
||||
|
||||
/**
|
||||
Get user data set into the XMLNode. TinyXML-2 in
|
||||
no way processes or interprets user data.
|
||||
It is initially 0.
|
||||
*/
|
||||
void* GetUserData() const { return _userData; }
|
||||
|
||||
protected:
|
||||
XMLNode( XMLDocument* );
|
||||
virtual ~XMLNode();
|
||||
|
||||
virtual char* ParseDeep( char*, StrPair* );
|
||||
virtual char* ParseDeep( char*, StrPair*, int* );
|
||||
|
||||
XMLDocument* _document;
|
||||
XMLNode* _parent;
|
||||
mutable StrPair _value;
|
||||
int _parseLineNum;
|
||||
|
||||
XMLNode* _firstChild;
|
||||
XMLNode* _lastChild;
|
||||
@@ -868,11 +920,14 @@ protected:
|
||||
XMLNode* _prev;
|
||||
XMLNode* _next;
|
||||
|
||||
void* _userData;
|
||||
|
||||
private:
|
||||
MemPool* _memPool;
|
||||
void Unlink( XMLNode* child );
|
||||
static void DeleteNode( XMLNode* node );
|
||||
void InsertChildPreamble( XMLNode* insertThis ) const;
|
||||
const XMLElement* ToElementWithName( const char* name ) const;
|
||||
|
||||
XMLNode( const XMLNode& ); // not supported
|
||||
XMLNode& operator=( const XMLNode& ); // not supported
|
||||
@@ -893,7 +948,6 @@ private:
|
||||
*/
|
||||
class TINYXML2_LIB XMLText : public XMLNode
|
||||
{
|
||||
friend class XMLBase;
|
||||
friend class XMLDocument;
|
||||
public:
|
||||
virtual bool Accept( XMLVisitor* visitor ) const;
|
||||
@@ -921,7 +975,7 @@ protected:
|
||||
XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {}
|
||||
virtual ~XMLText() {}
|
||||
|
||||
char* ParseDeep( char*, StrPair* endTag );
|
||||
char* ParseDeep( char*, StrPair* endTag, int* curLineNumPtr );
|
||||
|
||||
private:
|
||||
bool _isCData;
|
||||
@@ -952,7 +1006,7 @@ protected:
|
||||
XMLComment( XMLDocument* doc );
|
||||
virtual ~XMLComment();
|
||||
|
||||
char* ParseDeep( char*, StrPair* endTag );
|
||||
char* ParseDeep( char*, StrPair* endTag, int* curLineNumPtr);
|
||||
|
||||
private:
|
||||
XMLComment( const XMLComment& ); // not supported
|
||||
@@ -991,7 +1045,7 @@ protected:
|
||||
XMLDeclaration( XMLDocument* doc );
|
||||
virtual ~XMLDeclaration();
|
||||
|
||||
char* ParseDeep( char*, StrPair* endTag );
|
||||
char* ParseDeep( char*, StrPair* endTag, int* curLineNumPtr );
|
||||
|
||||
private:
|
||||
XMLDeclaration( const XMLDeclaration& ); // not supported
|
||||
@@ -1026,7 +1080,7 @@ protected:
|
||||
XMLUnknown( XMLDocument* doc );
|
||||
virtual ~XMLUnknown();
|
||||
|
||||
char* ParseDeep( char*, StrPair* endTag );
|
||||
char* ParseDeep( char*, StrPair* endTag, int* curLineNumPtr );
|
||||
|
||||
private:
|
||||
XMLUnknown( const XMLUnknown& ); // not supported
|
||||
@@ -1051,6 +1105,9 @@ public:
|
||||
/// The value of the attribute.
|
||||
const char* Value() const;
|
||||
|
||||
/// Gets the line number the attribute is in, if the document was parsed from a file.
|
||||
int GetLineNum() const { return _parseLineNum; }
|
||||
|
||||
/// The next attribute in the list.
|
||||
const XMLAttribute* Next() const {
|
||||
return _next;
|
||||
@@ -1060,11 +1117,18 @@ public:
|
||||
If the value isn't an integer, 0 will be returned. There is no error checking;
|
||||
use QueryIntValue() if you need error checking.
|
||||
*/
|
||||
int IntValue() const {
|
||||
int i=0;
|
||||
QueryIntValue( &i );
|
||||
return i;
|
||||
}
|
||||
int IntValue() const {
|
||||
int i = 0;
|
||||
QueryIntValue(&i);
|
||||
return i;
|
||||
}
|
||||
|
||||
int64_t Int64Value() const {
|
||||
int64_t i = 0;
|
||||
QueryInt64Value(&i);
|
||||
return i;
|
||||
}
|
||||
|
||||
/// Query as an unsigned integer. See IntValue()
|
||||
unsigned UnsignedValue() const {
|
||||
unsigned i=0;
|
||||
@@ -1091,13 +1155,15 @@ public:
|
||||
}
|
||||
|
||||
/** QueryIntValue interprets the attribute as an integer, and returns the value
|
||||
in the provided parameter. The function will return XML_NO_ERROR on success,
|
||||
in the provided parameter. The function will return XML_SUCCESS on success,
|
||||
and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful.
|
||||
*/
|
||||
XMLError QueryIntValue( int* value ) const;
|
||||
/// See QueryIntValue
|
||||
XMLError QueryUnsignedValue( unsigned int* value ) const;
|
||||
/// See QueryIntValue
|
||||
/// See QueryIntValue
|
||||
XMLError QueryInt64Value(int64_t* value) const;
|
||||
/// See QueryIntValue
|
||||
XMLError QueryBoolValue( bool* value ) const;
|
||||
/// See QueryIntValue
|
||||
XMLError QueryDoubleValue( double* value ) const;
|
||||
@@ -1110,7 +1176,9 @@ public:
|
||||
void SetAttribute( int value );
|
||||
/// Set the attribute to value.
|
||||
void SetAttribute( unsigned value );
|
||||
/// Set the attribute to value.
|
||||
/// Set the attribute to value.
|
||||
void SetAttribute(int64_t value);
|
||||
/// Set the attribute to value.
|
||||
void SetAttribute( bool value );
|
||||
/// Set the attribute to value.
|
||||
void SetAttribute( double value );
|
||||
@@ -1120,17 +1188,18 @@ public:
|
||||
private:
|
||||
enum { BUF_SIZE = 200 };
|
||||
|
||||
XMLAttribute() : _next( 0 ), _memPool( 0 ) {}
|
||||
XMLAttribute() : _parseLineNum( 0 ), _next( 0 ), _memPool( 0 ) {}
|
||||
virtual ~XMLAttribute() {}
|
||||
|
||||
XMLAttribute( const XMLAttribute& ); // not supported
|
||||
void operator=( const XMLAttribute& ); // not supported
|
||||
void SetName( const char* name );
|
||||
|
||||
char* ParseDeep( char* p, bool processEntities );
|
||||
char* ParseDeep( char* p, bool processEntities, int* curLineNumPtr );
|
||||
|
||||
mutable StrPair _name;
|
||||
mutable StrPair _value;
|
||||
int _parseLineNum;
|
||||
XMLAttribute* _next;
|
||||
MemPool* _memPool;
|
||||
};
|
||||
@@ -1142,7 +1211,6 @@ private:
|
||||
*/
|
||||
class TINYXML2_LIB XMLElement : public XMLNode
|
||||
{
|
||||
friend class XMLBase;
|
||||
friend class XMLDocument;
|
||||
public:
|
||||
/// Get the name of an element (which is the Value() of the node.)
|
||||
@@ -1188,42 +1256,25 @@ public:
|
||||
const char* Attribute( const char* name, const char* value=0 ) const;
|
||||
|
||||
/** Given an attribute name, IntAttribute() returns the value
|
||||
of the attribute interpreted as an integer. 0 will be
|
||||
returned if there is an error. For a method with error
|
||||
checking, see QueryIntAttribute()
|
||||
of the attribute interpreted as an integer. The default
|
||||
value will be returned if the attribute isn't present,
|
||||
or if there is an error. (For a method with error
|
||||
checking, see QueryIntAttribute()).
|
||||
*/
|
||||
int IntAttribute( const char* name ) const {
|
||||
int i=0;
|
||||
QueryIntAttribute( name, &i );
|
||||
return i;
|
||||
}
|
||||
int IntAttribute(const char* name, int defaultValue = 0) const;
|
||||
/// See IntAttribute()
|
||||
unsigned UnsignedAttribute( const char* name ) const {
|
||||
unsigned i=0;
|
||||
QueryUnsignedAttribute( name, &i );
|
||||
return i;
|
||||
}
|
||||
unsigned UnsignedAttribute(const char* name, unsigned defaultValue = 0) const;
|
||||
/// See IntAttribute()
|
||||
int64_t Int64Attribute(const char* name, int64_t defaultValue = 0) const;
|
||||
/// See IntAttribute()
|
||||
bool BoolAttribute(const char* name, bool defaultValue = false) const;
|
||||
/// See IntAttribute()
|
||||
bool BoolAttribute( const char* name ) const {
|
||||
bool b=false;
|
||||
QueryBoolAttribute( name, &b );
|
||||
return b;
|
||||
}
|
||||
double DoubleAttribute(const char* name, double defaultValue = 0) const;
|
||||
/// See IntAttribute()
|
||||
double DoubleAttribute( const char* name ) const {
|
||||
double d=0;
|
||||
QueryDoubleAttribute( name, &d );
|
||||
return d;
|
||||
}
|
||||
/// See IntAttribute()
|
||||
float FloatAttribute( const char* name ) const {
|
||||
float f=0;
|
||||
QueryFloatAttribute( name, &f );
|
||||
return f;
|
||||
}
|
||||
float FloatAttribute(const char* name, float defaultValue = 0) const;
|
||||
|
||||
/** Given an attribute name, QueryIntAttribute() returns
|
||||
XML_NO_ERROR, XML_WRONG_ATTRIBUTE_TYPE if the conversion
|
||||
XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion
|
||||
can't be performed, or XML_NO_ATTRIBUTE if the attribute
|
||||
doesn't exist. If successful, the result of the conversion
|
||||
will be written to 'value'. If not successful, nothing will
|
||||
@@ -1242,7 +1293,8 @@ public:
|
||||
}
|
||||
return a->QueryIntValue( value );
|
||||
}
|
||||
/// See QueryIntAttribute()
|
||||
|
||||
/// See QueryIntAttribute()
|
||||
XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const {
|
||||
const XMLAttribute* a = FindAttribute( name );
|
||||
if ( !a ) {
|
||||
@@ -1250,7 +1302,17 @@ public:
|
||||
}
|
||||
return a->QueryUnsignedValue( value );
|
||||
}
|
||||
/// See QueryIntAttribute()
|
||||
|
||||
/// See QueryIntAttribute()
|
||||
XMLError QueryInt64Attribute(const char* name, int64_t* value) const {
|
||||
const XMLAttribute* a = FindAttribute(name);
|
||||
if (!a) {
|
||||
return XML_NO_ATTRIBUTE;
|
||||
}
|
||||
return a->QueryInt64Value(value);
|
||||
}
|
||||
|
||||
/// See QueryIntAttribute()
|
||||
XMLError QueryBoolAttribute( const char* name, bool* value ) const {
|
||||
const XMLAttribute* a = FindAttribute( name );
|
||||
if ( !a ) {
|
||||
@@ -1277,7 +1339,7 @@ public:
|
||||
|
||||
|
||||
/** Given an attribute name, QueryAttribute() returns
|
||||
XML_NO_ERROR, XML_WRONG_ATTRIBUTE_TYPE if the conversion
|
||||
XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion
|
||||
can't be performed, or XML_NO_ATTRIBUTE if the attribute
|
||||
doesn't exist. It is overloaded for the primitive types,
|
||||
and is a generally more convenient replacement of
|
||||
@@ -1301,6 +1363,10 @@ public:
|
||||
return QueryUnsignedAttribute( name, value );
|
||||
}
|
||||
|
||||
int QueryAttribute(const char* name, int64_t* value) const {
|
||||
return QueryInt64Attribute(name, value);
|
||||
}
|
||||
|
||||
int QueryAttribute( const char* name, bool* value ) const {
|
||||
return QueryBoolAttribute( name, value );
|
||||
}
|
||||
@@ -1328,7 +1394,14 @@ public:
|
||||
XMLAttribute* a = FindOrCreateAttribute( name );
|
||||
a->SetAttribute( value );
|
||||
}
|
||||
/// Sets the named attribute to value.
|
||||
|
||||
/// Sets the named attribute to value.
|
||||
void SetAttribute(const char* name, int64_t value) {
|
||||
XMLAttribute* a = FindOrCreateAttribute(name);
|
||||
a->SetAttribute(value);
|
||||
}
|
||||
|
||||
/// Sets the named attribute to value.
|
||||
void SetAttribute( const char* name, bool value ) {
|
||||
XMLAttribute* a = FindOrCreateAttribute( name );
|
||||
a->SetAttribute( value );
|
||||
@@ -1425,7 +1498,9 @@ public:
|
||||
void SetText( int value );
|
||||
/// Convenience method for setting text inside an element. See SetText() for important limitations.
|
||||
void SetText( unsigned value );
|
||||
/// Convenience method for setting text inside an element. See SetText() for important limitations.
|
||||
/// Convenience method for setting text inside an element. See SetText() for important limitations.
|
||||
void SetText(int64_t value);
|
||||
/// Convenience method for setting text inside an element. See SetText() for important limitations.
|
||||
void SetText( bool value );
|
||||
/// Convenience method for setting text inside an element. See SetText() for important limitations.
|
||||
void SetText( double value );
|
||||
@@ -1461,13 +1536,28 @@ public:
|
||||
XMLError QueryIntText( int* ival ) const;
|
||||
/// See QueryIntText()
|
||||
XMLError QueryUnsignedText( unsigned* uval ) const;
|
||||
/// See QueryIntText()
|
||||
/// See QueryIntText()
|
||||
XMLError QueryInt64Text(int64_t* uval) const;
|
||||
/// See QueryIntText()
|
||||
XMLError QueryBoolText( bool* bval ) const;
|
||||
/// See QueryIntText()
|
||||
XMLError QueryDoubleText( double* dval ) const;
|
||||
/// See QueryIntText()
|
||||
XMLError QueryFloatText( float* fval ) const;
|
||||
|
||||
int IntText(int defaultValue = 0) const;
|
||||
|
||||
/// See QueryIntText()
|
||||
unsigned UnsignedText(unsigned defaultValue = 0) const;
|
||||
/// See QueryIntText()
|
||||
int64_t Int64Text(int64_t defaultValue = 0) const;
|
||||
/// See QueryIntText()
|
||||
bool BoolText(bool defaultValue = false) const;
|
||||
/// See QueryIntText()
|
||||
double DoubleText(double defaultValue = 0) const;
|
||||
/// See QueryIntText()
|
||||
float FloatText(float defaultValue = 0) const;
|
||||
|
||||
// internal:
|
||||
enum {
|
||||
OPEN, // <foo>
|
||||
@@ -1481,7 +1571,7 @@ public:
|
||||
virtual bool ShallowEqual( const XMLNode* compare ) const;
|
||||
|
||||
protected:
|
||||
char* ParseDeep( char* p, StrPair* endTag );
|
||||
char* ParseDeep( char* p, StrPair* endTag, int* curLineNumPtr );
|
||||
|
||||
private:
|
||||
XMLElement( XMLDocument* doc );
|
||||
@@ -1494,8 +1584,9 @@ private:
|
||||
}
|
||||
XMLAttribute* FindOrCreateAttribute( const char* name );
|
||||
//void LinkAttribute( XMLAttribute* attrib );
|
||||
char* ParseAttributes( char* p );
|
||||
char* ParseAttributes( char* p, int* curLineNumPtr );
|
||||
static void DeleteAttribute( XMLAttribute* attribute );
|
||||
XMLAttribute* CreateAttribute();
|
||||
|
||||
enum { BUF_SIZE = 200 };
|
||||
int _closingType;
|
||||
@@ -1522,7 +1613,7 @@ class TINYXML2_LIB XMLDocument : public XMLNode
|
||||
friend class XMLElement;
|
||||
public:
|
||||
/// constructor
|
||||
XMLDocument( bool processEntities = true, Whitespace = PRESERVE_WHITESPACE );
|
||||
XMLDocument( bool processEntities = true, Whitespace whitespaceMode = PRESERVE_WHITESPACE );
|
||||
~XMLDocument();
|
||||
|
||||
virtual XMLDocument* ToDocument() {
|
||||
@@ -1536,7 +1627,7 @@ public:
|
||||
|
||||
/**
|
||||
Parse an XML file from a character string.
|
||||
Returns XML_NO_ERROR (0) on success, or
|
||||
Returns XML_SUCCESS (0) on success, or
|
||||
an errorID.
|
||||
|
||||
You may optionally pass in the 'nBytes', which is
|
||||
@@ -1548,7 +1639,7 @@ public:
|
||||
|
||||
/**
|
||||
Load an XML file from disk.
|
||||
Returns XML_NO_ERROR (0) on success, or
|
||||
Returns XML_SUCCESS (0) on success, or
|
||||
an errorID.
|
||||
*/
|
||||
XMLError LoadFile( const char* filename );
|
||||
@@ -1561,14 +1652,14 @@ public:
|
||||
not text in order for TinyXML-2 to correctly
|
||||
do newline normalization.
|
||||
|
||||
Returns XML_NO_ERROR (0) on success, or
|
||||
Returns XML_SUCCESS (0) on success, or
|
||||
an errorID.
|
||||
*/
|
||||
XMLError LoadFile( FILE* );
|
||||
|
||||
/**
|
||||
Save the XML file to disk.
|
||||
Returns XML_NO_ERROR (0) on success, or
|
||||
Returns XML_SUCCESS (0) on success, or
|
||||
an errorID.
|
||||
*/
|
||||
XMLError SaveFile( const char* filename, bool compact = false );
|
||||
@@ -1577,7 +1668,7 @@ public:
|
||||
Save the XML file to disk. You are responsible
|
||||
for providing and closing the FILE*.
|
||||
|
||||
Returns XML_NO_ERROR (0) on success, or
|
||||
Returns XML_SUCCESS (0) on success, or
|
||||
an errorID.
|
||||
*/
|
||||
XMLError SaveFile( FILE* fp, bool compact = false );
|
||||
@@ -1586,7 +1677,7 @@ public:
|
||||
return _processEntities;
|
||||
}
|
||||
Whitespace WhitespaceMode() const {
|
||||
return _whitespace;
|
||||
return _whitespaceMode;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1671,25 +1762,35 @@ public:
|
||||
*/
|
||||
void DeleteNode( XMLNode* node );
|
||||
|
||||
void SetError( XMLError error, const char* str1, const char* str2 );
|
||||
void SetError( XMLError error, const char* str1, const char* str2, int lineNum );
|
||||
|
||||
void ClearError() {
|
||||
SetError(XML_SUCCESS, 0, 0, 0);
|
||||
}
|
||||
|
||||
/// Return true if there was an error parsing the document.
|
||||
bool Error() const {
|
||||
return _errorID != XML_NO_ERROR;
|
||||
return _errorID != XML_SUCCESS;
|
||||
}
|
||||
/// Return the errorID.
|
||||
XMLError ErrorID() const {
|
||||
return _errorID;
|
||||
}
|
||||
const char* ErrorName() const;
|
||||
static const char* ErrorIDToName(XMLError errorID);
|
||||
|
||||
/// Return a possibly helpful diagnostic location or string.
|
||||
const char* GetErrorStr1() const {
|
||||
return _errorStr1;
|
||||
return _errorStr1.GetStr();
|
||||
}
|
||||
/// Return a possibly helpful secondary diagnostic location or string.
|
||||
const char* GetErrorStr2() const {
|
||||
return _errorStr2;
|
||||
return _errorStr2.GetStr();
|
||||
}
|
||||
/// Return the line where the error occured, or zero if unknown.
|
||||
int GetErrorLineNum() const
|
||||
{
|
||||
return _errorLineNum;
|
||||
}
|
||||
/// If there is an error, print it to stdout.
|
||||
void PrintError() const;
|
||||
@@ -1711,13 +1812,15 @@ private:
|
||||
XMLDocument( const XMLDocument& ); // not supported
|
||||
void operator=( const XMLDocument& ); // not supported
|
||||
|
||||
bool _writeBOM;
|
||||
bool _processEntities;
|
||||
XMLError _errorID;
|
||||
Whitespace _whitespace;
|
||||
const char* _errorStr1;
|
||||
const char* _errorStr2;
|
||||
char* _charBuffer;
|
||||
bool _writeBOM;
|
||||
bool _processEntities;
|
||||
XMLError _errorID;
|
||||
Whitespace _whitespaceMode;
|
||||
mutable StrPair _errorStr1;
|
||||
mutable StrPair _errorStr2;
|
||||
int _errorLineNum;
|
||||
char* _charBuffer;
|
||||
int _parseCurLineNum;
|
||||
|
||||
MemPoolT< sizeof(XMLElement) > _elementPool;
|
||||
MemPoolT< sizeof(XMLAttribute) > _attributePool;
|
||||
@@ -1727,8 +1830,21 @@ private:
|
||||
static const char* _errorNames[XML_ERROR_COUNT];
|
||||
|
||||
void Parse();
|
||||
|
||||
template<class NodeType, int PoolElementSize>
|
||||
NodeType* CreateUnlinkedNode( MemPoolT<PoolElementSize>& pool );
|
||||
};
|
||||
|
||||
template<class NodeType, int PoolElementSize>
|
||||
inline NodeType* XMLDocument::CreateUnlinkedNode( MemPoolT<PoolElementSize>& pool )
|
||||
{
|
||||
TIXMLASSERT( sizeof( NodeType ) == PoolElementSize );
|
||||
TIXMLASSERT( sizeof( NodeType ) == pool.ItemSize() );
|
||||
NodeType* returnNode = new (pool.Alloc()) NodeType( this );
|
||||
TIXMLASSERT( returnNode );
|
||||
returnNode->_memPool = &pool;
|
||||
return returnNode;
|
||||
}
|
||||
|
||||
/**
|
||||
A XMLHandle is a class that wraps a node pointer with null checks; this is
|
||||
@@ -1845,19 +1961,19 @@ public:
|
||||
}
|
||||
/// Safe cast to XMLElement. This can return null.
|
||||
XMLElement* ToElement() {
|
||||
return ( ( _node == 0 ) ? 0 : _node->ToElement() );
|
||||
return ( _node ? _node->ToElement() : 0 );
|
||||
}
|
||||
/// Safe cast to XMLText. This can return null.
|
||||
XMLText* ToText() {
|
||||
return ( ( _node == 0 ) ? 0 : _node->ToText() );
|
||||
return ( _node ? _node->ToText() : 0 );
|
||||
}
|
||||
/// Safe cast to XMLUnknown. This can return null.
|
||||
XMLUnknown* ToUnknown() {
|
||||
return ( ( _node == 0 ) ? 0 : _node->ToUnknown() );
|
||||
return ( _node ? _node->ToUnknown() : 0 );
|
||||
}
|
||||
/// Safe cast to XMLDeclaration. This can return null.
|
||||
XMLDeclaration* ToDeclaration() {
|
||||
return ( ( _node == 0 ) ? 0 : _node->ToDeclaration() );
|
||||
return ( _node ? _node->ToDeclaration() : 0 );
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -1917,16 +2033,16 @@ public:
|
||||
return _node;
|
||||
}
|
||||
const XMLElement* ToElement() const {
|
||||
return ( ( _node == 0 ) ? 0 : _node->ToElement() );
|
||||
return ( _node ? _node->ToElement() : 0 );
|
||||
}
|
||||
const XMLText* ToText() const {
|
||||
return ( ( _node == 0 ) ? 0 : _node->ToText() );
|
||||
return ( _node ? _node->ToText() : 0 );
|
||||
}
|
||||
const XMLUnknown* ToUnknown() const {
|
||||
return ( ( _node == 0 ) ? 0 : _node->ToUnknown() );
|
||||
return ( _node ? _node->ToUnknown() : 0 );
|
||||
}
|
||||
const XMLDeclaration* ToDeclaration() const {
|
||||
return ( ( _node == 0 ) ? 0 : _node->ToDeclaration() );
|
||||
return ( _node ? _node->ToDeclaration() : 0 );
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -1998,7 +2114,8 @@ public:
|
||||
void PushAttribute( const char* name, const char* value );
|
||||
void PushAttribute( const char* name, int value );
|
||||
void PushAttribute( const char* name, unsigned value );
|
||||
void PushAttribute( const char* name, bool value );
|
||||
void PushAttribute(const char* name, int64_t value);
|
||||
void PushAttribute( const char* name, bool value );
|
||||
void PushAttribute( const char* name, double value );
|
||||
/// If streaming, close the Element.
|
||||
virtual void CloseElement( bool compactMode=false );
|
||||
@@ -2009,7 +2126,9 @@ public:
|
||||
void PushText( int value );
|
||||
/// Add a text node from an unsigned.
|
||||
void PushText( unsigned value );
|
||||
/// Add a text node from a bool.
|
||||
/// Add a text node from an unsigned.
|
||||
void PushText(int64_t value);
|
||||
/// Add a text node from a bool.
|
||||
void PushText( bool value );
|
||||
/// Add a text node from a float.
|
||||
void PushText( float value );
|
||||
|
||||
2
main.cpp
2
main.cpp
@@ -29,7 +29,7 @@ int main(int argc, char** argv) {
|
||||
//::testing::GTEST_FLAG(filter) = "*WiFiOptimizer*";
|
||||
|
||||
|
||||
::testing::GTEST_FLAG(filter) = "*NormalN*";
|
||||
::testing::GTEST_FLAG(filter) = "*FloorplanCeilings*";
|
||||
//::testing::GTEST_FLAG(filter) = "*Barometer*";
|
||||
//::testing::GTEST_FLAG(filter) = "*GridWalk2RelPressure*";
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ private:
|
||||
} fields;
|
||||
|
||||
/** store as 64-bit integer */
|
||||
uint64_t mac;
|
||||
uint64_t mac = 0;
|
||||
|
||||
public:
|
||||
|
||||
@@ -61,8 +61,8 @@ public:
|
||||
fields.h5 = hexWordToInt(str[ 0], str[ 1]);
|
||||
fields.h4 = hexWordToInt(str[ 2], str[ 3]);
|
||||
fields.h3 = hexWordToInt(str[ 4], str[ 5]);
|
||||
fields.h2 = hexWordToInt(str[ 6], str[7]);
|
||||
fields.h1 = hexWordToInt(str[8], str[9]);
|
||||
fields.h2 = hexWordToInt(str[ 6], str[ 7]);
|
||||
fields.h1 = hexWordToInt(str[ 8], str[ 9]);
|
||||
fields.h0 = hexWordToInt(str[10], str[11]);
|
||||
}
|
||||
else{
|
||||
@@ -89,7 +89,9 @@ public:
|
||||
|
||||
/** get the mac address as a long-int value */
|
||||
uint64_t asLong() const {
|
||||
return mac;
|
||||
// even if we initialized all 8 bytes with zero
|
||||
// we mask the 2 unsued bytes to be absolutely safe
|
||||
return mac & 0x0000FFFFFFFFFFFF;
|
||||
}
|
||||
|
||||
/** equal? */
|
||||
@@ -105,13 +107,19 @@ public:
|
||||
private:
|
||||
|
||||
/** convert the given hex char [0-F] to an integer [0-15] */
|
||||
static uint8_t hexCharToInt(const char _hex) {
|
||||
|
||||
// to upper case
|
||||
const char hex = (_hex >= 'a') ? (_hex - ('a' - 'A')) : (_hex);
|
||||
static uint8_t hexCharToInt(const char hex) {
|
||||
|
||||
// convert
|
||||
return (hex - '0' < 10) ? (hex - '0') : (hex - 'A' + 10);
|
||||
if (hex >= '0' && hex <= '9') { // digits
|
||||
return (hex - '0');
|
||||
} else if (hex >= 'a' && hex <= 'f') { // lower case
|
||||
return (hex - 'a' + 10);
|
||||
} else if (hex >= 'A' && hex <= 'F') { // upper case
|
||||
return (hex - 'A' + 10);
|
||||
}
|
||||
|
||||
// found an invalid character
|
||||
throw Exception("invalid character within MAC address");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include "../../math/Stats.h"
|
||||
|
||||
#include "../../misc/Debug.h"
|
||||
|
||||
#include "WiFiMeasurements.h"
|
||||
|
||||
class VAPGrouper {
|
||||
@@ -40,6 +42,8 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
static constexpr const char* name = "VAPGrp";
|
||||
|
||||
/** the mode to use for grouping VAPs */
|
||||
const Mode mode;
|
||||
|
||||
@@ -85,6 +89,9 @@ public:
|
||||
|
||||
}
|
||||
|
||||
Log::add(name, "grouped " + std::to_string(original.entries.size()) + " measurements into " + std::to_string(result.entries.size()), true);
|
||||
|
||||
|
||||
// done
|
||||
return result;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define WIFIMODELLOGDISTCEILING_H
|
||||
|
||||
#include "../../../floorplan/v2/Floorplan.h"
|
||||
#include "../../../floorplan/v2/FloorplanCeilings.h"
|
||||
|
||||
#include "../../../Assertions.h"
|
||||
#include "WiFiModel.h"
|
||||
@@ -36,21 +37,18 @@ private:
|
||||
/** map of all APs (and their parameters) known to the model */
|
||||
std::unordered_map<MACAddress, APEntry> accessPoints;
|
||||
|
||||
/** position (height) of all ceilings (in meter) */
|
||||
std::vector<float> ceilingsAtHeight_m;
|
||||
// /** position (height) of all ceilings (in meter) */
|
||||
// std::vector<float> ceilingsAtHeight_m;
|
||||
Floorplan::Ceilings ceilings;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor with floorplan (needed for ceiling position) */
|
||||
WiFiModelLogDistCeiling(const Floorplan::IndoorMap* map) {
|
||||
WiFiModelLogDistCeiling(const Floorplan::IndoorMap* map) : ceilings(map) {
|
||||
|
||||
// sanity checks
|
||||
Assert::isTrue(map->floors.size() >= 1, "map has no floors?!");
|
||||
|
||||
// position of all ceilings
|
||||
for (Floorplan::Floor* f : map->floors) {
|
||||
ceilingsAtHeight_m.push_back(f->atHeight);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -123,74 +121,13 @@ public:
|
||||
const float rssiLOS = LogDistanceModel::distanceToRssi(params.txp, params.exp, distance_m);
|
||||
|
||||
// WAF loss (params.waf is a negative value!) -> WAF loss is also a negative value
|
||||
const float wafLoss = params.waf * numCeilingsBetween(position_m, params.position_m);
|
||||
const float wafLoss = params.waf * ceilings.numCeilingsBetween(position_m, params.position_m);
|
||||
|
||||
// combine
|
||||
return rssiLOS + wafLoss;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
FRIEND_TEST(LogDistanceCeilingModel, numCeilings);
|
||||
FRIEND_TEST(LogDistanceCeilingModel, numCeilingsFloat);
|
||||
|
||||
|
||||
/** get the number of ceilings between z1 and z2 */
|
||||
float numCeilingsBetweenFloat(const Point3 pos1, const Point3 pos2) const {
|
||||
|
||||
|
||||
const float zMin = std::min(pos1.z, pos2.z);
|
||||
const float zMax = std::max(pos1.z, pos2.z);
|
||||
|
||||
float cnt = 0;
|
||||
|
||||
for (const float z : ceilingsAtHeight_m) {
|
||||
if (zMin < z && zMax > z) {
|
||||
const float dmax = zMax - z;
|
||||
cnt += (dmax > 1) ? (1) : (dmax);
|
||||
}
|
||||
}
|
||||
|
||||
return cnt;
|
||||
|
||||
}
|
||||
|
||||
/** get the number of ceilings between z1 and z2 */
|
||||
int numCeilingsBetween(const Point3 pos1, const Point3 pos2) const {
|
||||
|
||||
int cnt = 0;
|
||||
const float zMin = std::min(pos1.z, pos2.z);
|
||||
const float zMax = std::max(pos1.z, pos2.z);
|
||||
|
||||
#ifdef WITH_ASSERTIONS
|
||||
|
||||
static int numNear = 0;
|
||||
static int numFar = 0;
|
||||
for (const float z : ceilingsAtHeight_m) {
|
||||
const float diff = std::min( std::abs(z-zMin), std::abs(z-zMax) );
|
||||
if (diff < 0.1) {++numNear;} else {++numFar;}
|
||||
}
|
||||
if ((numNear + numFar) > 150000) {
|
||||
Assert::isTrue(numNear < numFar*0.1,
|
||||
"many requests to the WiFiModel address nodes (very) near to a ground! \
|
||||
due to rounding issues, determining the number of floors between AP and point-in-question is NOT possible! \
|
||||
expect very wrong outputs! \
|
||||
consider adding the person's height to the questioned positions: p += Point3(0,0,1.3) "
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (const float z : ceilingsAtHeight_m) {
|
||||
if (zMin < z && zMax > z) {++cnt;}
|
||||
}
|
||||
|
||||
return cnt;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
#include <unordered_map>
|
||||
|
||||
/**
|
||||
* denotes a wifi fingerprint
|
||||
* known position and several measurements conducted at this position
|
||||
* denotes a wifi fingerprint:
|
||||
* known position and 1-n measurements [wifi-scan on all channels] conducted at this position
|
||||
*
|
||||
* as several measurements were conducted, each AP is usually contained more than once!
|
||||
* if more than one measurement is conducted, each AP is contained more than once!
|
||||
*/
|
||||
struct WiFiFingerprint {
|
||||
|
||||
@@ -19,7 +19,7 @@ struct WiFiFingerprint {
|
||||
/** real-world-position that was measured */
|
||||
Point3 pos_m;
|
||||
|
||||
/** measurements (APs) at the given location */
|
||||
/** seen APs at the given location */
|
||||
WiFiMeasurements measurements;
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ struct WiFiFingerprint {
|
||||
|
||||
|
||||
|
||||
/** as each AP is contained more than once (scanned more than once), group them by MAC and use the average RSSI */
|
||||
/** as each AP might be contained more than once (scanned more than once), group them by MAC and use the average RSSI */
|
||||
WiFiMeasurements average() {
|
||||
|
||||
// group scans by MAC (all measurements for one AP)
|
||||
|
||||
125
sensors/radio/setup/WiFiFingerprints.h
Normal file
125
sensors/radio/setup/WiFiFingerprints.h
Normal file
@@ -0,0 +1,125 @@
|
||||
#ifndef WIFIFINGERPRINTS_H
|
||||
#define WIFIFINGERPRINTS_H
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "WiFiFingerprint.h"
|
||||
|
||||
|
||||
/**
|
||||
* helper class to load and save fingerprints.
|
||||
* fingerprints are wifi-measurements given at some known location
|
||||
* those can be used to e.g. optimize access-point models etc.
|
||||
*/
|
||||
class WiFiFingerprints {
|
||||
|
||||
private:
|
||||
|
||||
/** the file to save the calibration model to */
|
||||
std::string file;
|
||||
|
||||
/** all fingerprints (position -> measurements) within the model */
|
||||
std::vector<WiFiFingerprint> fingerprints;
|
||||
|
||||
public:
|
||||
|
||||
WiFiFingerprints(const std::string& file) : file(file) {
|
||||
load();
|
||||
}
|
||||
|
||||
const std::vector<WiFiFingerprint>& getFingerprints() const {
|
||||
return fingerprints;
|
||||
}
|
||||
|
||||
/** get all fingerprints that measured exactly the given mac [no VAP grouping] */
|
||||
const std::vector<WiFiFingerprint> getFingerprintsFor(const MACAddress& mac) const {
|
||||
|
||||
std::vector<WiFiFingerprint> res;
|
||||
|
||||
// process each fingerprint location
|
||||
for (const WiFiFingerprint& _fp : fingerprints) {
|
||||
|
||||
// start with an empty copy
|
||||
WiFiFingerprint fp = _fp; fp.measurements.entries.clear();
|
||||
|
||||
// only add the measurements that belong to the requested mac
|
||||
for (const WiFiMeasurement& _m : _fp.measurements.entries) {
|
||||
if (_m.getAP().getMAC() == mac) {
|
||||
fp.measurements.entries.push_back(_m);
|
||||
}
|
||||
}
|
||||
if (fp.measurements.entries.size() > 0) { // got some measurements for this AP?
|
||||
res.push_back(fp);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
/** deserialize the model */
|
||||
void load() {
|
||||
|
||||
// open and check
|
||||
std::ifstream inp(file.c_str());
|
||||
if (inp.bad() || inp.eof() || ! inp.is_open()) { return; }
|
||||
|
||||
// read all entries
|
||||
while (!inp.eof()) {
|
||||
|
||||
// each section starts with [fingerprint]
|
||||
std::string section;
|
||||
inp >> section;
|
||||
if (inp.eof()) {break;}
|
||||
if (section != "[fingerprint]") {throw Exception("error!");}
|
||||
|
||||
// deserialize it
|
||||
WiFiFingerprint wfp;
|
||||
wfp.read(inp);
|
||||
if (wfp.measurements.entries.empty()) {continue;}
|
||||
fingerprints.push_back(wfp);
|
||||
|
||||
}
|
||||
|
||||
inp.close();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** serialize the model */
|
||||
void save() {
|
||||
|
||||
// open and check
|
||||
std::ofstream out(file.c_str());
|
||||
if (out.bad()) {throw Exception("error while opening " + file + " for write");}
|
||||
|
||||
// write all entries
|
||||
for (const WiFiFingerprint& wfp : fingerprints) {
|
||||
out << "[fingerprint]\n";
|
||||
wfp.write(out);
|
||||
}
|
||||
|
||||
// done
|
||||
out.close();
|
||||
|
||||
}
|
||||
|
||||
/** get the fingerprint for the given location. if no fingerprint exists, an empty one is created! */
|
||||
WiFiFingerprint& getFingerprint(const Point3 pos_m) {
|
||||
|
||||
// try to find an existing one
|
||||
for (WiFiFingerprint& wfp : fingerprints) {
|
||||
// get within range of floating-point rounding issues
|
||||
if (wfp.pos_m.getDistance(pos_m) < 0.01) {return wfp;}
|
||||
}
|
||||
|
||||
// create a new one and return its reference
|
||||
WiFiFingerprint wfp(pos_m);
|
||||
fingerprints.push_back(wfp);
|
||||
return fingerprints.back();
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // WIFIFINGERPRINTS_H
|
||||
@@ -1,247 +1,61 @@
|
||||
#ifndef OPTIMIZER_H
|
||||
#define OPTIMIZER_H
|
||||
|
||||
#include "../../../floorplan/v2/Floorplan.h"
|
||||
#include "../../../floorplan/v2/FloorplanHelper.h"
|
||||
#ifndef WIFIOPTIMIZER_H
|
||||
#define WIFIOPTIMIZER_H
|
||||
|
||||
#include "WiFiFingerprints.h"
|
||||
#include "WiFiOptimizerStructs.h"
|
||||
#include "../VAPGrouper.h"
|
||||
#include "../../../geo/BBox3.h"
|
||||
#include "../../../misc/Debug.h"
|
||||
|
||||
#include "WiFiFingerprint.h"
|
||||
#include "../model/WiFiModel.h"
|
||||
#include "../model/WiFiModelLogDistCeiling.h"
|
||||
#include <unordered_map>
|
||||
|
||||
#include <KLib/math/optimization/NumOptAlgoDownhillSimplex.h>
|
||||
#include <KLib/math/optimization/NumOptAlgoGenetic.h>
|
||||
#include <KLib/math/optimization/NumOptAlgoRangeRandom.h>
|
||||
namespace WiFiOptimizer {
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
/** base-class for all WiFiOptimizers */
|
||||
class Base {
|
||||
|
||||
struct WiFiOptimizer {
|
||||
protected:
|
||||
|
||||
private:
|
||||
/** each MAC-Adress has several position->rssi entries */
|
||||
std::unordered_map<MACAddress, std::vector<RSSIatPosition>> apMap;
|
||||
|
||||
/** combine one RSSI measurement with the position the signal was measured at */
|
||||
struct RSSIatPosition {
|
||||
/** how to handle virtual access points [group, not-group, ..] */
|
||||
const VAPGrouper vg;
|
||||
|
||||
/** real-world position (in meter) */
|
||||
const Point3 pos_m;
|
||||
|
||||
/** measured signal strength (for one AP) */
|
||||
const float rssi;
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
RSSIatPosition(const Point3 pos_m, const float rssi) : pos_m(pos_m), rssi(rssi) {;}
|
||||
Base(const VAPGrouper vg) : vg(vg) {;}
|
||||
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
struct APParams {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float txp;
|
||||
float exp;
|
||||
float waf;
|
||||
Point3 getPos() const {return Point3(x,y,z);}
|
||||
APParams() {;}
|
||||
APParams(float x, float y, float z, float txp, float exp, float waf) : x(x), y(y), z(z), txp(txp), exp(exp), waf(waf) {;}
|
||||
std::string asString() {
|
||||
std::stringstream ss;
|
||||
ss << "Pos:" << getPos().asString() << " TXP:" << txp << " EXP:" << exp << " WAF:" << waf;
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
/** add MAC-info to params */
|
||||
struct APParamsMAC {
|
||||
MACAddress mac;
|
||||
APParams params;
|
||||
APParamsMAC(const MACAddress mac, const APParams& params) : mac(mac), params(params) {;}
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Floorplan::IndoorMap* map;
|
||||
const VAPGrouper vg;
|
||||
|
||||
/** each MAC-Adress has several position->rssi entries */
|
||||
std::unordered_map<MACAddress, std::vector<RSSIatPosition>> apMap;
|
||||
|
||||
const char* name = "WiFiOptimizer";
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
WiFiOptimizer(Floorplan::IndoorMap* map, const VAPGrouper& vg) : map(map), vg(vg) {
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
/** add a new fingerprint to the optimizers data-source */
|
||||
void addFingerprint(const WiFiFingerprint& fp) {
|
||||
|
||||
// group the fingerprint's measurements by VAP (if configured)
|
||||
const WiFiMeasurements measurements = vg.group(fp.measurements);
|
||||
|
||||
// add each available AP to its slot (lookup map)
|
||||
for (const WiFiMeasurement& m : measurements.entries) {
|
||||
const RSSIatPosition rap(fp.pos_m, m.getRSSI());
|
||||
apMap[m.getAP().getMAC()].push_back(rap);
|
||||
/** get a list of all to-be-optimized access-points (given by their mac-address) */
|
||||
virtual std::vector<MACAddress> getAllMACs() const {
|
||||
std::vector<MACAddress> res;
|
||||
for (const auto& it : apMap) {res.push_back(it.first);}
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
/** add a new fingerprint to the optimizer as data-source */
|
||||
virtual void addFingerprint(const WiFiFingerprint& fp) {
|
||||
|
||||
/** get a list of all to-be-optimized access-points (given by their mac-address) */
|
||||
std::vector<MACAddress> getAllMACs() const {
|
||||
std::vector<MACAddress> res;
|
||||
for (const auto& it : apMap) {res.push_back(it.first);}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** optimize all known APs */
|
||||
std::vector<APParamsMAC> optimizeAll() const {
|
||||
|
||||
// sanity chekc
|
||||
Assert::isFalse(getAllMACs().empty(), "no APs found for optimization!");
|
||||
|
||||
float errSum = 0;
|
||||
std::vector<APParamsMAC> res;
|
||||
for (const MACAddress& mac : getAllMACs()) {
|
||||
float err;
|
||||
const APParams params = optimize(mac, err);
|
||||
res.push_back(APParamsMAC(mac, params));
|
||||
errSum += err;
|
||||
}
|
||||
|
||||
const float avgErr = errSum / getAllMACs().size();
|
||||
Log::add(name, "average AP error is: " + std::to_string(avgErr) + " dB");
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
/** optimize the given AP */
|
||||
APParams optimize(const MACAddress& mac, float& errResult) const {
|
||||
|
||||
// starting parameters do not matter for the current optimizer!
|
||||
APParams params(0,0,0, -40, 2.5, -4.0);
|
||||
constexpr float hugeError = 1e10;
|
||||
|
||||
// get all position->rssi measurements for this AP to compare them with the corresponding model estimations
|
||||
const std::vector<RSSIatPosition>& entries = apMap.find(mac)->second;
|
||||
|
||||
|
||||
// signal-strength-prediction-model...
|
||||
WiFiModelLogDistCeiling model(map);
|
||||
|
||||
auto func = [&] (const float* data) {
|
||||
|
||||
const APParams* params = (APParams*) data;
|
||||
|
||||
// some sanity checks
|
||||
if (params->waf > 0) {return hugeError;}
|
||||
|
||||
if (params->txp < -50) {return hugeError;}
|
||||
if (params->txp > -30) {return hugeError;}
|
||||
|
||||
if (params->exp > 4) {return hugeError;}
|
||||
if (params->exp < 1) {return hugeError;}
|
||||
|
||||
// current position guess for the AP;
|
||||
const Point3 apPos_m = params->getPos();
|
||||
|
||||
// add the AP [described by the current guess] to the signal-strength-prediction model
|
||||
model.clear();
|
||||
model.addAP(mac, WiFiModelLogDistCeiling::APEntry(apPos_m, params->txp, params->exp, params->waf));
|
||||
|
||||
float err = 0;
|
||||
int cnt = 0;
|
||||
|
||||
// process each measurement
|
||||
for (const RSSIatPosition& reading : entries) {
|
||||
|
||||
// get the model-estimation for the fingerprint's position
|
||||
const float rssiModel = model.getRSSI(mac, reading.pos_m);
|
||||
|
||||
// difference between estimation and measurement
|
||||
const float diff = std::abs(rssiModel - reading.rssi);
|
||||
|
||||
// adjust the error
|
||||
err += diff*diff;
|
||||
++cnt;
|
||||
|
||||
// max distance penality
|
||||
// [unlikely to get a reading for this AP here!]
|
||||
if (apPos_m.getDistance(reading.pos_m) > 150) {err += 999999;}
|
||||
// group the fingerprint's measurements by VAP (if configured)
|
||||
const WiFiMeasurements measurements = vg.group(fp.measurements);
|
||||
|
||||
// add each available AP to its slot (lookup map)
|
||||
for (const WiFiMeasurement& m : measurements.entries) {
|
||||
const RSSIatPosition rap(fp.pos_m, m.getRSSI());
|
||||
apMap[m.getAP().getMAC()].push_back(rap);
|
||||
}
|
||||
|
||||
err /= cnt;
|
||||
err = std::sqrt(err);
|
||||
}
|
||||
|
||||
if (params->txp < -50) {err += 999999;}
|
||||
if (params->txp > -35) {err += 999999;}
|
||||
/** add new fingerprints to the optimizer as data-source */
|
||||
virtual void addFingerprints(const WiFiFingerprints& fps) {
|
||||
for (const WiFiFingerprint& fp : fps.getFingerprints()) {
|
||||
addFingerprint(fp);
|
||||
}
|
||||
}
|
||||
|
||||
if (params->exp > 3.5) {err += 999999;}
|
||||
if (params->exp < 1.0) {err += 999999;}
|
||||
|
||||
return err;
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
const BBox3 mapBBox = FloorplanHelper::getBBox(map);
|
||||
}
|
||||
|
||||
using LeOpt = K::NumOptAlgoRangeRandom<float>;
|
||||
const std::vector<LeOpt::MinMax> valRegion = {
|
||||
LeOpt::MinMax(mapBBox.getMin().x - 20, mapBBox.getMax().x + 20), // x
|
||||
LeOpt::MinMax(mapBBox.getMin().y - 20, mapBBox.getMax().y + 20), // y
|
||||
LeOpt::MinMax(mapBBox.getMin().z - 5, mapBBox.getMax().z + 5), // z
|
||||
LeOpt::MinMax(-50,-30), // txp
|
||||
LeOpt::MinMax(1,3), // exp
|
||||
LeOpt::MinMax(-10,-4), // waf
|
||||
};
|
||||
|
||||
|
||||
// log
|
||||
Log::add(name, "optimizing parameters for AP " + mac.asString() + " by using " + std::to_string(entries.size()) + " fingerprints", false);
|
||||
Log::tick();
|
||||
|
||||
LeOpt opt(valRegion);
|
||||
opt.setPopulationSize(500); // USE MORE FOR PRODUCTION
|
||||
opt.setNumIerations(150);
|
||||
opt.calculateOptimum(func, (float*) ¶ms);
|
||||
|
||||
// using LeOpt = K::NumOptAlgoGenetic<float>;
|
||||
// LeOpt opt(6);
|
||||
// opt.setPopulationSize(750);
|
||||
// opt.setMaxIterations(50);
|
||||
// opt.setElitism(0.05f);
|
||||
// opt.setMutation(0.75f);
|
||||
// //opt.setValRange({0.5, 0.5, 0.5, 0.1, 0.1, 0.1});
|
||||
// opt.setValRegion(valRegion);
|
||||
|
||||
// K::NumOptAlgoDownhillSimplex<float, 6> opt;
|
||||
// opt.setMaxIterations(100);
|
||||
// opt.setNumRestarts(10);
|
||||
|
||||
opt.calculateOptimum(func, (float*) ¶ms);
|
||||
errResult = func((float*)¶ms);
|
||||
|
||||
Log::tock();
|
||||
Log::add(name, mac.asString() + ": " + params.asString() + " @ " + std::to_string(errResult) +" dB err");
|
||||
|
||||
return params;
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // OPTIMIZER_H
|
||||
#endif // WIFIOPTIMIZER_H
|
||||
|
||||
341
sensors/radio/setup/WiFiOptimizerLogDistCeiling.h
Normal file
341
sensors/radio/setup/WiFiOptimizerLogDistCeiling.h
Normal file
@@ -0,0 +1,341 @@
|
||||
#ifndef WIFI_OPTIMIZER_LOG_DIST_CEILING_H
|
||||
#define WIFI_OPTIMIZER_LOG_DIST_CEILING_H
|
||||
|
||||
#include "../../../floorplan/v2/Floorplan.h"
|
||||
#include "../../../floorplan/v2/FloorplanHelper.h"
|
||||
|
||||
#include "../../../geo/BBox3.h"
|
||||
#include "../../../misc/Debug.h"
|
||||
|
||||
#include "WiFiFingerprint.h"
|
||||
#include "WiFiFingerprints.h"
|
||||
|
||||
#include "../model/WiFiModel.h"
|
||||
#include "../model/WiFiModelLogDistCeiling.h"
|
||||
|
||||
#include <KLib/math/optimization/NumOptAlgoDownhillSimplex.h>
|
||||
#include <KLib/math/optimization/NumOptAlgoGenetic.h>
|
||||
#include <KLib/math/optimization/NumOptAlgoRangeRandom.h>
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include "WiFiOptimizer.h"
|
||||
#include "WiFiOptimizerStructs.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace WiFiOptimizer {
|
||||
|
||||
/**
|
||||
* optimize access-point parameters,
|
||||
* given several fingerprints using the log-dist-ceiling model
|
||||
*/
|
||||
struct LogDistCeiling : public Base {
|
||||
|
||||
public:
|
||||
|
||||
enum class Mode {
|
||||
FAST,
|
||||
MEDIUM,
|
||||
QUALITY,
|
||||
};
|
||||
|
||||
/**
|
||||
* resulting optimization stats for one AP
|
||||
*/
|
||||
struct Stats {
|
||||
|
||||
/** average model<->scan error after optimzing */
|
||||
float error_db;
|
||||
|
||||
/** number of fingerprints [= locations] that were used for optimzing */
|
||||
int usedFingerprins;
|
||||
|
||||
/** resulting model<->scan error after optimzing for each individual fingerprints [= location] */
|
||||
std::vector<ErrorAtPosition> errors;
|
||||
|
||||
/** get the location where the model estimation reaches the highest negative value [model estimation too low] */
|
||||
ErrorAtPosition getEstErrorMaxNeg() const {
|
||||
auto cmpErrAtPos = [] (const ErrorAtPosition& a, const ErrorAtPosition& b) {return a.getError_db() < b.getError_db();};
|
||||
return *std::min_element(errors.begin(), errors.end(), cmpErrAtPos);
|
||||
}
|
||||
|
||||
/** get the location where the model estimation reaches the highest positive value [model estimation too high] */
|
||||
ErrorAtPosition getEstErrorMaxPos() const {
|
||||
auto cmpErrAtPos = [] (const ErrorAtPosition& a, const ErrorAtPosition& b) {return a.getError_db() < b.getError_db();};
|
||||
return *std::max_element(errors.begin(), errors.end(), cmpErrAtPos);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/** parameters for one AP when using the LogDistCeiling model */
|
||||
struct APParams {
|
||||
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
|
||||
float txp;
|
||||
float exp;
|
||||
float waf;
|
||||
|
||||
Point3 getPos() const {return Point3(x,y,z);}
|
||||
|
||||
/** ctor */
|
||||
APParams() {;}
|
||||
|
||||
/** ctor */
|
||||
APParams(float x, float y, float z, float txp, float exp, float waf) : x(x), y(y), z(z), txp(txp), exp(exp), waf(waf) {;}
|
||||
|
||||
std::string asString() const {
|
||||
std::stringstream ss;
|
||||
ss << "Pos:" << getPos().asString() << " TXP:" << txp << " EXP:" << exp << " WAF:" << waf;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/** add MAC-info to params */
|
||||
struct APParamsMAC {
|
||||
MACAddress mac;
|
||||
APParams params;
|
||||
APParamsMAC(const MACAddress mac, const APParams& params) : mac(mac), params(params) {;}
|
||||
};
|
||||
|
||||
class APParamsList {
|
||||
|
||||
std::vector<APParamsMAC> lst;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
APParamsList(const std::vector<APParamsMAC>& lst) : lst(lst) {
|
||||
|
||||
}
|
||||
|
||||
/** get the list */
|
||||
const std::vector<APParamsMAC>& get() const {
|
||||
return lst;
|
||||
}
|
||||
|
||||
/** get params for the given mac [if known, otherwise nullptr] */
|
||||
const APParamsMAC* get (const MACAddress& mac) const {
|
||||
for (const APParamsMAC& ap : lst) {
|
||||
if (ap.mac == mac) {return ≈}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
using APFilter = std::function<bool(const int numFingerprints, const MACAddress& mac)>;
|
||||
|
||||
const APFilter NONE = [] (const int numFingerprints, const MACAddress& mac) {
|
||||
(void) numFingerprints; (void) mac;
|
||||
return false;
|
||||
};
|
||||
|
||||
const APFilter MIN_8_FPS = [] (const int numFingerprints, const MACAddress& mac) {
|
||||
(void) mac;
|
||||
return numFingerprints < 8;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Mode mode = Mode::QUALITY;
|
||||
|
||||
Floorplan::IndoorMap* map;
|
||||
|
||||
const char* name = "WiFiOptLDC";
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
LogDistCeiling(Floorplan::IndoorMap* map, const VAPGrouper& vg, const Mode mode = Mode::QUALITY) : Base(vg), map(map), mode(mode) {
|
||||
;
|
||||
}
|
||||
|
||||
/** ctor */
|
||||
LogDistCeiling(Floorplan::IndoorMap* map, const VAPGrouper& vg, const WiFiFingerprints& fps, const Mode mode = Mode::QUALITY) : Base(vg), map(map), mode(mode) {
|
||||
addFingerprints(fps);
|
||||
}
|
||||
|
||||
/** optimize all known APs */
|
||||
APParamsList optimizeAll(APFilter filter) const {
|
||||
|
||||
// sanity check
|
||||
Assert::isFalse(getAllMACs().empty(), "no APs found for optimization! call addFingerprint() first!");
|
||||
|
||||
float errSum = 0; int errCnt = 0;
|
||||
std::vector<APParamsMAC> res;
|
||||
for (const MACAddress& mac : getAllMACs()) {
|
||||
Stats stats;
|
||||
const APParams params = optimize(mac, stats);
|
||||
if (!filter(stats.usedFingerprins, mac)) {
|
||||
res.push_back(APParamsMAC(mac, params));
|
||||
errSum += stats.error_db;
|
||||
++errCnt;
|
||||
} else {
|
||||
std::cout << "ignored due to filter!" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
const float avgErr = errSum / errCnt;
|
||||
Log::add(name, "average AP error is: " + std::to_string(avgErr) + " dB");
|
||||
|
||||
// done
|
||||
return APParamsList(res);
|
||||
|
||||
}
|
||||
|
||||
/** optimize the given AP */
|
||||
APParams optimize(const MACAddress& mac, Stats& res) const {
|
||||
|
||||
// starting parameters do not matter for the current optimizer!
|
||||
APParams params(0,0,0, -40, 2.5, -4.0);
|
||||
|
||||
// get all position->rssi measurements for this AP to compare them with the corresponding model estimations
|
||||
const std::vector<RSSIatPosition>& entries = apMap.find(mac)->second;
|
||||
|
||||
// log
|
||||
Log::add(name, "optimizing parameters for AP " + mac.asString() + " by using " + std::to_string(entries.size()) + " fingerprints", false);
|
||||
Log::tick();
|
||||
|
||||
// get the map's size
|
||||
const BBox3 mapBBox = FloorplanHelper::getBBox(map);
|
||||
|
||||
using LeOpt = K::NumOptAlgoRangeRandom<float>;
|
||||
const std::vector<LeOpt::MinMax> valRegion = {
|
||||
LeOpt::MinMax(mapBBox.getMin().x - 20, mapBBox.getMax().x + 20), // x
|
||||
LeOpt::MinMax(mapBBox.getMin().y - 20, mapBBox.getMax().y + 20), // y
|
||||
LeOpt::MinMax(mapBBox.getMin().z - 5, mapBBox.getMax().z + 5), // z
|
||||
LeOpt::MinMax(-50, -30), // txp
|
||||
LeOpt::MinMax(1, 5), // exp
|
||||
LeOpt::MinMax(-15, -0), // waf
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
LeOpt opt(valRegion);
|
||||
|
||||
switch(mode) {
|
||||
case Mode::FAST:
|
||||
opt.setPopulationSize(100);
|
||||
opt.setNumIerations(50);
|
||||
break;
|
||||
case Mode::MEDIUM:
|
||||
opt.setPopulationSize(200);
|
||||
opt.setNumIerations(100);
|
||||
break;
|
||||
case Mode::QUALITY:
|
||||
opt.setPopulationSize(500);
|
||||
opt.setNumIerations(150);
|
||||
break;
|
||||
}
|
||||
|
||||
// error function
|
||||
auto func = [&] (const float* params) {
|
||||
return getErrorLogDistCeiling(mac, entries, params, nullptr);
|
||||
};
|
||||
|
||||
opt.calculateOptimum(func, (float*) ¶ms);
|
||||
|
||||
// using LeOpt = K::NumOptAlgoGenetic<float>;
|
||||
// LeOpt opt(6);
|
||||
// opt.setPopulationSize(750);
|
||||
// opt.setMaxIterations(50);
|
||||
// opt.setElitism(0.05f);
|
||||
// opt.setMutation(0.75f);
|
||||
// //opt.setValRange({0.5, 0.5, 0.5, 0.1, 0.1, 0.1});
|
||||
// opt.setValRegion(valRegion);
|
||||
|
||||
// K::NumOptAlgoDownhillSimplex<float, 6> opt;
|
||||
// opt.setMaxIterations(100);
|
||||
// opt.setNumRestarts(10);
|
||||
|
||||
opt.calculateOptimum(func, (float*) ¶ms);
|
||||
res.error_db = getErrorLogDistCeiling(mac, entries, (float*)¶ms, &res);
|
||||
res.usedFingerprins = entries.size();
|
||||
|
||||
Log::tock();
|
||||
Log::add(name, mac.asString() + ": " + params.asString() + " @ " + std::to_string(res.error_db) +" dB err");
|
||||
|
||||
return params;
|
||||
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
float getErrorLogDistCeiling(const MACAddress& mac, const std::vector<RSSIatPosition>& entries, const float* data, Stats* stats = nullptr) const {
|
||||
|
||||
constexpr float hugeError = 1e10;
|
||||
const APParams* params = (APParams*) data;
|
||||
|
||||
// some sanity checks
|
||||
if (params->waf > 0) {return hugeError;}
|
||||
|
||||
if (params->txp < -50) {return hugeError;}
|
||||
if (params->txp > -30) {return hugeError;}
|
||||
|
||||
if (params->exp > 4) {return hugeError;}
|
||||
if (params->exp < 1) {return hugeError;}
|
||||
|
||||
// current position guess for the AP;
|
||||
const Point3 apPos_m = params->getPos();
|
||||
|
||||
// add the AP [described by the current guess] to the signal-strength-prediction model
|
||||
// signal-strength-prediction-model...
|
||||
WiFiModelLogDistCeiling model(map);
|
||||
model.addAP(mac, WiFiModelLogDistCeiling::APEntry(apPos_m, params->txp, params->exp, params->waf));
|
||||
|
||||
float err = 0;
|
||||
int cnt = 0;
|
||||
|
||||
// process each measurement
|
||||
for (const RSSIatPosition& reading : entries) {
|
||||
|
||||
// get the model-estimation for the fingerprint's position
|
||||
const float rssiModel = model.getRSSI(mac, reading.pos_m);
|
||||
|
||||
// difference between estimation and measurement
|
||||
const float diff = std::abs(rssiModel - reading.rssi);
|
||||
|
||||
// add error to stats object?
|
||||
if (stats) {
|
||||
stats->errors.push_back(ErrorAtPosition(reading.pos_m, reading.rssi, rssiModel));
|
||||
}
|
||||
|
||||
// adjust the error
|
||||
err += diff*diff;
|
||||
++cnt;
|
||||
|
||||
// max distance penality
|
||||
// [unlikely to get a reading for this AP here!]
|
||||
if (apPos_m.getDistance(reading.pos_m) > 150) {err += 999999;}
|
||||
|
||||
}
|
||||
|
||||
err /= cnt;
|
||||
err = std::sqrt(err);
|
||||
|
||||
if (params->txp < -50) {err += 999999;}
|
||||
if (params->txp > -35) {err += 999999;}
|
||||
|
||||
if (params->exp > 3.5) {err += 999999;}
|
||||
if (params->exp < 1.0) {err += 999999;}
|
||||
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // WIFI_OPTIMIZER_LOG_DIST_CEILING_H
|
||||
55
sensors/radio/setup/WiFiOptimizerStructs.h
Normal file
55
sensors/radio/setup/WiFiOptimizerStructs.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#ifndef WIFIOPTIMIZERSTRUCTS_H
|
||||
#define WIFIOPTIMIZERSTRUCTS_H
|
||||
|
||||
#include "../../../geo/Point3.h"
|
||||
|
||||
namespace WiFiOptimizer {
|
||||
|
||||
/**
|
||||
* one entry that is used during optimization:
|
||||
* combine one RSSI measurement with the position the signal was measured at
|
||||
*/
|
||||
struct RSSIatPosition {
|
||||
|
||||
/** real-world position (in meter) */
|
||||
const Point3 pos_m;
|
||||
|
||||
/** measured signal strength (for one AP) */
|
||||
const float rssi;
|
||||
|
||||
/** ctor */
|
||||
RSSIatPosition(const Point3 pos_m, const float rssi) : pos_m(pos_m), rssi(rssi) {;}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* for statistics
|
||||
* after optimiziaton
|
||||
*
|
||||
* denotes the difference [error] between one fingerprinted rssi
|
||||
* at location (x,y,z) and the model estimation for this location
|
||||
*/
|
||||
struct ErrorAtPosition {
|
||||
|
||||
/** real-world position (in meter) */
|
||||
const Point3 pos_m;
|
||||
|
||||
/** measured signal strength (for one AP) */
|
||||
const float scan_rssi;
|
||||
|
||||
/** final model's prediction */
|
||||
const float model_rssi;
|
||||
|
||||
/** ctor */
|
||||
ErrorAtPosition(const Point3 pos_m, const float scan_rssi, const float model_rssi) : pos_m(pos_m), scan_rssi(scan_rssi), model_rssi(model_rssi) {;}
|
||||
|
||||
/** get the difference [error] between model-estimated-rssi and fingerprinted-rssi */
|
||||
float getError_db() const {
|
||||
return model_rssi - scan_rssi;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // WIFIOPTIMIZERSTRUCTS_H
|
||||
85
tests/floorplan/TestFloorplanCeilings.cpp
Normal file
85
tests/floorplan/TestFloorplanCeilings.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#ifdef WITH_TESTS
|
||||
|
||||
#include "../Tests.h"
|
||||
#include "../../floorplan/v2/FloorplanCeilings.h"
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
|
||||
TEST(FloorplanCeilings, numCeilings) {
|
||||
|
||||
// dummy floorplan
|
||||
Floorplan::Floor* f0 = new Floorplan::Floor(); f0->atHeight = 0;
|
||||
Floorplan::Floor* f1 = new Floorplan::Floor(); f1->atHeight = 3;
|
||||
Floorplan::Floor* f2 = new Floorplan::Floor(); f2->atHeight = 7;
|
||||
|
||||
Floorplan::IndoorMap map;
|
||||
map.floors.push_back(f0);
|
||||
map.floors.push_back(f1);
|
||||
map.floors.push_back(f2);
|
||||
|
||||
Floorplan::Ceilings model(&map);
|
||||
|
||||
ASSERT_EQ(0, model.numCeilingsBetween(Point3(0,0,-1), Point3(0,0,0)) );
|
||||
ASSERT_EQ(0, model.numCeilingsBetween(Point3(0,0,0), Point3(0,0,-1)) );
|
||||
|
||||
ASSERT_EQ(0, model.numCeilingsBetween(Point3(0,0,0), Point3(0,0,1)) );
|
||||
ASSERT_EQ(0, model.numCeilingsBetween(Point3(0,0,1), Point3(0,0,0)) );
|
||||
|
||||
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,-0.01), Point3(0,0,+0.01)) );
|
||||
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,+0.01), Point3(0,0,-0.01)) );
|
||||
|
||||
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,2.99), Point3(0,0,3.01)) );
|
||||
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,3.01), Point3(0,0,2.99)) );
|
||||
|
||||
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,6.99), Point3(0,0,7.01)) );
|
||||
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,7.01), Point3(0,0,6.99)) );
|
||||
|
||||
ASSERT_EQ(0, model.numCeilingsBetween(Point3(0,0,7.00), Point3(0,0,99)) );
|
||||
|
||||
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,0), Point3(0,0,7)) );
|
||||
ASSERT_EQ(3, model.numCeilingsBetween(Point3(0,0,-1), Point3(0,0,8)) );
|
||||
|
||||
}
|
||||
|
||||
TEST(FloorplanCeilings, numCeilingsFloat) {
|
||||
|
||||
// dummy floorplan
|
||||
Floorplan::Floor* f0 = new Floorplan::Floor(); f0->atHeight = 0;
|
||||
Floorplan::Floor* f1 = new Floorplan::Floor(); f1->atHeight = 3;
|
||||
Floorplan::Floor* f2 = new Floorplan::Floor(); f2->atHeight = 7;
|
||||
|
||||
Floorplan::IndoorMap map;
|
||||
map.floors.push_back(f0);
|
||||
map.floors.push_back(f1);
|
||||
map.floors.push_back(f2);
|
||||
|
||||
Floorplan::Ceilings model(&map);
|
||||
|
||||
const float d = 0.01;
|
||||
|
||||
ASSERT_NEAR(0, model.numCeilingsBetweenFloat(Point3(0,0,-1), Point3(0,0,0)), d );
|
||||
ASSERT_NEAR(0, model.numCeilingsBetweenFloat(Point3(0,0,0), Point3(0,0,-1)), d );
|
||||
|
||||
ASSERT_NEAR(0, model.numCeilingsBetweenFloat(Point3(0,0,0), Point3(0,0,1)), d );
|
||||
ASSERT_NEAR(0, model.numCeilingsBetweenFloat(Point3(0,0,1), Point3(0,0,0)), d );
|
||||
|
||||
ASSERT_NEAR(0.5, model.numCeilingsBetweenFloat(Point3(0,0,-0.01), Point3(0,0,+0.50)), d );
|
||||
ASSERT_NEAR(0.5, model.numCeilingsBetweenFloat(Point3(0,0,+0.50), Point3(0,0,-0.01)), d );
|
||||
|
||||
ASSERT_NEAR(0.2, model.numCeilingsBetweenFloat(Point3(0,0,2.99), Point3(0,0,3.20)), d );
|
||||
ASSERT_NEAR(0.2, model.numCeilingsBetweenFloat(Point3(0,0,3.20), Point3(0,0,2.99)), d );
|
||||
|
||||
ASSERT_NEAR(1.0, model.numCeilingsBetweenFloat(Point3(0,0,6.99), Point3(0,0,8.33)), d );
|
||||
ASSERT_NEAR(1.0, model.numCeilingsBetweenFloat(Point3(0,0,8.33), Point3(0,0,6.99)), d );
|
||||
ASSERT_NEAR(2.0, model.numCeilingsBetweenFloat(Point3(0,0,0.00), Point3(0,0,8.33)), d );
|
||||
ASSERT_NEAR(2.0, model.numCeilingsBetweenFloat(Point3(0,0,8.33), Point3(0,0,0.00)), d );
|
||||
|
||||
ASSERT_NEAR(0, model.numCeilingsBetweenFloat(Point3(0,0,7.00), Point3(0,0,99)), d );
|
||||
|
||||
ASSERT_NEAR(1, model.numCeilingsBetweenFloat(Point3(0,0,0), Point3(0,0,7)), d );
|
||||
ASSERT_NEAR(3, model.numCeilingsBetweenFloat(Point3(0,0,-1), Point3(0,0,8)), d );
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -20,11 +20,59 @@ TEST(MAC, caseInsensitive) {
|
||||
|
||||
}
|
||||
|
||||
TEST(MAC, invalidLength) {
|
||||
|
||||
ASSERT_THROW(MACAddress("12:34:56:78:9A:B"), Exception);
|
||||
ASSERT_THROW(MACAddress("2:34:56:78:9A:BC"), Exception);
|
||||
|
||||
ASSERT_THROW(MACAddress("12:34:56:78:9A:"), Exception);
|
||||
ASSERT_THROW(MACAddress("12:34:56:78:9A"), Exception);
|
||||
ASSERT_THROW(MACAddress("12:34:56:78:9A:11:"), Exception);
|
||||
ASSERT_THROW(MACAddress("12:34:56:78:9A:11:11"), Exception);
|
||||
|
||||
|
||||
}
|
||||
|
||||
TEST(MAC, invalidChars) {
|
||||
|
||||
ASSERT_THROW(MACAddress("g2:34:56:78:9A:BC"), Exception);
|
||||
ASSERT_THROW(MACAddress("a2:34:56:78:9A:BG"), Exception);
|
||||
ASSERT_THROW(MACAddress("a2:34:5!:78:9A:BC"), Exception);
|
||||
ASSERT_THROW(MACAddress("a2:34:51:78:?A:BC"), Exception);
|
||||
|
||||
}
|
||||
|
||||
TEST(MAC, ends) {
|
||||
|
||||
MACAddress mac1("12:34:56:78:9A:BC");
|
||||
MACAddress mac2("12:34:56:78:9a:bc");
|
||||
MACAddress mac3("12:34:56:78:9a:bd");
|
||||
MACAddress mac4("02:34:56:78:9a:bc");
|
||||
MACAddress mac5("13:34:56:78:9a:bc");
|
||||
MACAddress mac6("12:34:56:78:9a:cc");
|
||||
|
||||
ASSERT_EQ(mac1, mac2); ASSERT_EQ(mac1.asString(), mac2.asString());
|
||||
|
||||
ASSERT_NE(mac1, mac3); ASSERT_NE(mac1.asString(), mac3.asString());
|
||||
ASSERT_NE(mac2, mac3); ASSERT_NE(mac2.asString(), mac3.asString());
|
||||
|
||||
ASSERT_NE(mac1, mac4); ASSERT_NE(mac1.asString(), mac4.asString());
|
||||
ASSERT_NE(mac2, mac4); ASSERT_NE(mac2.asString(), mac4.asString());
|
||||
|
||||
ASSERT_NE(mac1, mac5); ASSERT_NE(mac1.asString(), mac5.asString());
|
||||
ASSERT_NE(mac2, mac5); ASSERT_NE(mac2.asString(), mac5.asString());
|
||||
|
||||
ASSERT_NE(mac1, mac6); ASSERT_NE(mac1.asString(), mac6.asString());
|
||||
ASSERT_NE(mac2, mac6); ASSERT_NE(mac2.asString(), mac6.asString());
|
||||
|
||||
}
|
||||
|
||||
TEST(MAC, convertLong) {
|
||||
|
||||
MACAddress mac1("12:34:56:78:9A:BC");
|
||||
MACAddress mac2 = MACAddress( mac1.asLong() );
|
||||
ASSERT_EQ(mac1, mac2);
|
||||
ASSERT_EQ(mac1.asString(), mac2.asString());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -41,81 +41,6 @@ TEST(LogDistanceCeilingModel, calc) {
|
||||
|
||||
}
|
||||
|
||||
TEST(LogDistanceCeilingModel, numCeilings) {
|
||||
|
||||
// dummy floorplan
|
||||
Floorplan::Floor* f0 = new Floorplan::Floor(); f0->atHeight = 0;
|
||||
Floorplan::Floor* f1 = new Floorplan::Floor(); f1->atHeight = 3;
|
||||
Floorplan::Floor* f2 = new Floorplan::Floor(); f2->atHeight = 7;
|
||||
|
||||
Floorplan::IndoorMap map;
|
||||
map.floors.push_back(f0);
|
||||
map.floors.push_back(f1);
|
||||
map.floors.push_back(f2);
|
||||
|
||||
WiFiModelLogDistCeiling model(&map);
|
||||
|
||||
ASSERT_EQ(0, model.numCeilingsBetween(Point3(0,0,-1), Point3(0,0,0)) );
|
||||
ASSERT_EQ(0, model.numCeilingsBetween(Point3(0,0,0), Point3(0,0,-1)) );
|
||||
|
||||
ASSERT_EQ(0, model.numCeilingsBetween(Point3(0,0,0), Point3(0,0,1)) );
|
||||
ASSERT_EQ(0, model.numCeilingsBetween(Point3(0,0,1), Point3(0,0,0)) );
|
||||
|
||||
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,-0.01), Point3(0,0,+0.01)) );
|
||||
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,+0.01), Point3(0,0,-0.01)) );
|
||||
|
||||
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,2.99), Point3(0,0,3.01)) );
|
||||
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,3.01), Point3(0,0,2.99)) );
|
||||
|
||||
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,6.99), Point3(0,0,7.01)) );
|
||||
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,7.01), Point3(0,0,6.99)) );
|
||||
|
||||
ASSERT_EQ(0, model.numCeilingsBetween(Point3(0,0,7.00), Point3(0,0,99)) );
|
||||
|
||||
ASSERT_EQ(1, model.numCeilingsBetween(Point3(0,0,0), Point3(0,0,7)) );
|
||||
ASSERT_EQ(3, model.numCeilingsBetween(Point3(0,0,-1), Point3(0,0,8)) );
|
||||
|
||||
}
|
||||
|
||||
TEST(LogDistanceCeilingModel, numCeilingsFloat) {
|
||||
|
||||
// dummy floorplan
|
||||
Floorplan::Floor* f0 = new Floorplan::Floor(); f0->atHeight = 0;
|
||||
Floorplan::Floor* f1 = new Floorplan::Floor(); f1->atHeight = 3;
|
||||
Floorplan::Floor* f2 = new Floorplan::Floor(); f2->atHeight = 7;
|
||||
|
||||
Floorplan::IndoorMap map;
|
||||
map.floors.push_back(f0);
|
||||
map.floors.push_back(f1);
|
||||
map.floors.push_back(f2);
|
||||
|
||||
WiFiModelLogDistCeiling model(&map);
|
||||
|
||||
const float d = 0.01;
|
||||
|
||||
ASSERT_NEAR(0, model.numCeilingsBetweenFloat(Point3(0,0,-1), Point3(0,0,0)), d );
|
||||
ASSERT_NEAR(0, model.numCeilingsBetweenFloat(Point3(0,0,0), Point3(0,0,-1)), d );
|
||||
|
||||
ASSERT_NEAR(0, model.numCeilingsBetweenFloat(Point3(0,0,0), Point3(0,0,1)), d );
|
||||
ASSERT_NEAR(0, model.numCeilingsBetweenFloat(Point3(0,0,1), Point3(0,0,0)), d );
|
||||
|
||||
ASSERT_NEAR(0.5, model.numCeilingsBetweenFloat(Point3(0,0,-0.01), Point3(0,0,+0.50)), d );
|
||||
ASSERT_NEAR(0.5, model.numCeilingsBetweenFloat(Point3(0,0,+0.50), Point3(0,0,-0.01)), d );
|
||||
|
||||
ASSERT_NEAR(0.2, model.numCeilingsBetweenFloat(Point3(0,0,2.99), Point3(0,0,3.20)), d );
|
||||
ASSERT_NEAR(0.2, model.numCeilingsBetweenFloat(Point3(0,0,3.20), Point3(0,0,2.99)), d );
|
||||
|
||||
ASSERT_NEAR(1.0, model.numCeilingsBetweenFloat(Point3(0,0,6.99), Point3(0,0,8.33)), d );
|
||||
ASSERT_NEAR(1.0, model.numCeilingsBetweenFloat(Point3(0,0,8.33), Point3(0,0,6.99)), d );
|
||||
ASSERT_NEAR(2.0, model.numCeilingsBetweenFloat(Point3(0,0,0.00), Point3(0,0,8.33)), d );
|
||||
ASSERT_NEAR(2.0, model.numCeilingsBetweenFloat(Point3(0,0,8.33), Point3(0,0,0.00)), d );
|
||||
|
||||
ASSERT_NEAR(0, model.numCeilingsBetweenFloat(Point3(0,0,7.00), Point3(0,0,99)), d );
|
||||
|
||||
ASSERT_NEAR(1, model.numCeilingsBetweenFloat(Point3(0,0,0), Point3(0,0,7)), d );
|
||||
ASSERT_NEAR(3, model.numCeilingsBetweenFloat(Point3(0,0,-1), Point3(0,0,8)), d );
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifdef WITH_TESTS
|
||||
|
||||
#include "../../Tests.h"
|
||||
#include "../../../sensors/radio/setup/WiFiOptimizer.h"
|
||||
#include "../../../sensors/radio/setup/WiFiOptimizerLogDistCeiling.h"
|
||||
#include "../../../sensors/radio/setup/WiFiFingerprint.h"
|
||||
#include "../../../misc/Debug.h"
|
||||
|
||||
@@ -55,23 +55,23 @@ TEST(WiFiOptimizer, optimize) {
|
||||
|
||||
}
|
||||
|
||||
WiFiOptimizer opt(&map, vg);
|
||||
WiFiOptimizer::LogDistCeiling opt(&map, vg);
|
||||
for (const WiFiFingerprint& fp : fingerprints) {
|
||||
opt.addFingerprint(fp);
|
||||
}
|
||||
|
||||
ASSERT_EQ(2, opt.getAllMACs().size());
|
||||
float errRes;
|
||||
WiFiOptimizer::LogDistCeiling::Stats errRes;
|
||||
|
||||
const WiFiOptimizer::APParams params1 = opt.optimize(mac1, errRes);
|
||||
ASSERT_TRUE(errRes < 0.1);
|
||||
const WiFiOptimizer::LogDistCeiling::APParams params1 = opt.optimize(mac1, errRes);
|
||||
ASSERT_TRUE(errRes.error_db < 0.1);
|
||||
ASSERT_NEAR(0, pos1.getDistance(params1.getPos()), 0.4); // apx position estimation
|
||||
ASSERT_NEAR(-40, params1.txp, 1.0);
|
||||
ASSERT_NEAR(2, params1.exp, 0.1);
|
||||
ASSERT_NEAR(-4, params1.waf, 0.5);
|
||||
|
||||
const WiFiOptimizer::APParams params2 = opt.optimize(mac2, errRes);
|
||||
ASSERT_TRUE(errRes < 0.1);
|
||||
const WiFiOptimizer::LogDistCeiling::APParams params2 = opt.optimize(mac2, errRes);
|
||||
ASSERT_TRUE(errRes.error_db < 0.1);
|
||||
ASSERT_NEAR(0, pos2.getDistance(params2.getPos()), 0.4); // apx position estimation
|
||||
ASSERT_NEAR(-40, params1.txp, 1.0);
|
||||
ASSERT_NEAR(2, params2.exp, 0.1);
|
||||
|
||||
Reference in New Issue
Block a user