fixed some potential issues with MAC addresses
added corresponding test-cases switched to newer version of tinyxml due to some issues adjusted affected code-parts accordingly for better re-use, moved ceiling-calculation to a new class some minor fixes new helper methods worked on wifi-opt
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 "Floorplan.h"
|
||||||
|
|
||||||
|
#include "../../sensors/MACAddress.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* helper methods for the floorplan
|
* helper methods for the floorplan
|
||||||
*/
|
*/
|
||||||
class FloorplanHelper {
|
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:
|
public:
|
||||||
|
|
||||||
/** align all floorplan values to the given grid size. needed for the grid factory */
|
/** 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) {
|
if (res != tinyxml2::XMLError::XML_SUCCESS) {
|
||||||
throw Exception(
|
throw Exception(
|
||||||
std::string() + "error while loading XML " + file + "\n" +
|
std::string() + "error while loading XML " + file + "\n" +
|
||||||
((doc.GetErrorStr1()) ? (doc.GetErrorStr1()) : ("")) + "\n" +
|
((doc.GetErrorStr1()) ? (doc.GetErrorStr1()) : ("")) + "\n" +
|
||||||
((doc.GetErrorStr2()) ? (doc.GetErrorStr2()) : (""))
|
((doc.GetErrorStr2()) ? (doc.GetErrorStr2()) : (""))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return parse(doc);
|
IndoorMap* map = parse(doc);
|
||||||
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** read an IndoorMap from the given XMl-string */
|
/** read an IndoorMap from the given XMl-string */
|
||||||
@@ -51,16 +52,17 @@ namespace Floorplan {
|
|||||||
if (res != tinyxml2::XMLError::XML_SUCCESS) {
|
if (res != tinyxml2::XMLError::XML_SUCCESS) {
|
||||||
throw Exception(
|
throw Exception(
|
||||||
std::string() + "error while parsing XML\n" +
|
std::string() + "error while parsing XML\n" +
|
||||||
((doc.GetErrorStr1()) ? (doc.GetErrorStr1()) : ("")) + "\n" +
|
((doc.GetErrorStr1()) ? (doc.GetErrorStr1()) : ("")) + "\n" +
|
||||||
((doc.GetErrorStr2()) ? (doc.GetErrorStr2()) : (""))
|
((doc.GetErrorStr2()) ? (doc.GetErrorStr2()) : (""))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return parse(doc);
|
IndoorMap* map = parse(doc);
|
||||||
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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) {
|
static void assertNode(const std::string& node, const XMLElem* el) {
|
||||||
std::string err = std::string("unexpected node '") + el->Name() + "' expected '" + node + "'";
|
std::string err = std::string("unexpected node '") + el->Name() + "' expected '" + node + "'";
|
||||||
@@ -69,7 +71,7 @@ namespace Floorplan {
|
|||||||
|
|
||||||
/** parse the complete document */
|
/** parse the complete document */
|
||||||
static IndoorMap* parse(tinyxml2::XMLDocument& doc) {
|
static IndoorMap* parse(tinyxml2::XMLDocument& doc) {
|
||||||
return parseMap((XMLElem*)doc.FirstChild());
|
return parseMap(doc.FirstChildElement());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** parse the <map> node */
|
/** parse the <map> node */
|
||||||
@@ -266,7 +268,9 @@ namespace Floorplan {
|
|||||||
const XMLElem* sub = n->FirstChildElement();
|
const XMLElem* sub = n->FirstChildElement();
|
||||||
while(sub) {
|
while(sub) {
|
||||||
// <entry key="123">abc</entry>
|
// <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();
|
sub = sub->NextSiblingElement();
|
||||||
}
|
}
|
||||||
return elem;
|
return elem;
|
||||||
@@ -402,7 +406,7 @@ namespace Floorplan {
|
|||||||
FloorOutline outline;
|
FloorOutline outline;
|
||||||
FOREACH_NODE(n, el) {
|
FOREACH_NODE(n, el) {
|
||||||
if (std::string("polygon") == n->Name()) {
|
if (std::string("polygon") == n->Name()) {
|
||||||
outline.push_back(parseFloorPolygon(n)); // TODO
|
outline.push_back(parseFloorPolygon(n));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return outline;
|
return outline;
|
||||||
@@ -427,6 +431,9 @@ namespace Floorplan {
|
|||||||
poly.points.push_back(p2);
|
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;
|
return poly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ struct EarthPos {
|
|||||||
float height;
|
float height;
|
||||||
|
|
||||||
/** ctor with values */
|
/** 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
|
#define POINT2_H
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 2D Point
|
* 2D Point
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -30,6 +30,9 @@ distribution.
|
|||||||
# include <stdio.h>
|
# include <stdio.h>
|
||||||
# include <stdlib.h>
|
# include <stdlib.h>
|
||||||
# include <string.h>
|
# include <string.h>
|
||||||
|
# if defined(__PS3__)
|
||||||
|
# include <stddef.h>
|
||||||
|
# endif
|
||||||
#else
|
#else
|
||||||
# include <cctype>
|
# include <cctype>
|
||||||
# include <climits>
|
# include <climits>
|
||||||
@@ -37,6 +40,7 @@ distribution.
|
|||||||
# include <cstdlib>
|
# include <cstdlib>
|
||||||
# include <cstring>
|
# include <cstring>
|
||||||
#endif
|
#endif
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: intern strings instead of allocation.
|
TODO: intern strings instead of allocation.
|
||||||
@@ -68,6 +72,8 @@ distribution.
|
|||||||
# else
|
# else
|
||||||
# define TINYXML2_LIB
|
# define TINYXML2_LIB
|
||||||
# endif
|
# endif
|
||||||
|
#elif __GNUC__ >= 4
|
||||||
|
# define TINYXML2_LIB __attribute__((visibility("default")))
|
||||||
#else
|
#else
|
||||||
# define TINYXML2_LIB
|
# define TINYXML2_LIB
|
||||||
#endif
|
#endif
|
||||||
@@ -92,9 +98,9 @@ distribution.
|
|||||||
/* Versioning, past 1.0.14:
|
/* Versioning, past 1.0.14:
|
||||||
http://semver.org/
|
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_MINOR_VERSION = 0;
|
||||||
static const int TIXML2_PATCH_VERSION = 0;
|
static const int TIXML2_PATCH_VERSION = 1;
|
||||||
|
|
||||||
namespace tinyxml2
|
namespace tinyxml2
|
||||||
{
|
{
|
||||||
@@ -121,18 +127,20 @@ public:
|
|||||||
NEEDS_NEWLINE_NORMALIZATION = 0x02,
|
NEEDS_NEWLINE_NORMALIZATION = 0x02,
|
||||||
NEEDS_WHITESPACE_COLLAPSING = 0x04,
|
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,
|
TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION,
|
||||||
ATTRIBUTE_NAME = 0,
|
ATTRIBUTE_NAME = 0,
|
||||||
ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,
|
ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,
|
||||||
ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION,
|
ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION,
|
||||||
COMMENT = NEEDS_NEWLINE_NORMALIZATION
|
COMMENT = NEEDS_NEWLINE_NORMALIZATION
|
||||||
};
|
};
|
||||||
|
|
||||||
StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {}
|
StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {}
|
||||||
~StrPair();
|
~StrPair();
|
||||||
|
|
||||||
void Set( char* start, char* end, int flags ) {
|
void Set( char* start, char* end, int flags ) {
|
||||||
|
TIXMLASSERT( start );
|
||||||
|
TIXMLASSERT( end );
|
||||||
Reset();
|
Reset();
|
||||||
_start = start;
|
_start = start;
|
||||||
_end = end;
|
_end = end;
|
||||||
@@ -152,13 +160,13 @@ public:
|
|||||||
|
|
||||||
void SetStr( const char* str, int flags=0 );
|
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 );
|
char* ParseName( char* in );
|
||||||
|
|
||||||
void TransferTo( StrPair* other );
|
void TransferTo( StrPair* other );
|
||||||
|
void Reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Reset();
|
|
||||||
void CollapseWhitespace();
|
void CollapseWhitespace();
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@@ -203,7 +211,8 @@ public:
|
|||||||
void Push( T t ) {
|
void Push( T t ) {
|
||||||
TIXMLASSERT( _size < INT_MAX );
|
TIXMLASSERT( _size < INT_MAX );
|
||||||
EnsureCapacity( _size+1 );
|
EnsureCapacity( _size+1 );
|
||||||
_mem[_size++] = t;
|
_mem[_size] = t;
|
||||||
|
++_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
T* PushArr( int count ) {
|
T* PushArr( int count ) {
|
||||||
@@ -217,7 +226,8 @@ public:
|
|||||||
|
|
||||||
T Pop() {
|
T Pop() {
|
||||||
TIXMLASSERT( _size > 0 );
|
TIXMLASSERT( _size > 0 );
|
||||||
return _mem[--_size];
|
--_size;
|
||||||
|
return _mem[_size];
|
||||||
}
|
}
|
||||||
|
|
||||||
void PopArr( int count ) {
|
void PopArr( int count ) {
|
||||||
@@ -311,7 +321,7 @@ public:
|
|||||||
/*
|
/*
|
||||||
Template child class to create pools of the correct type.
|
Template child class to create pools of the correct type.
|
||||||
*/
|
*/
|
||||||
template< int SIZE >
|
template< int ITEM_SIZE >
|
||||||
class MemPoolT : public MemPool
|
class MemPoolT : public MemPool
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -334,7 +344,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual int ItemSize() const {
|
virtual int ItemSize() const {
|
||||||
return SIZE;
|
return ITEM_SIZE;
|
||||||
}
|
}
|
||||||
int CurrentAllocs() const {
|
int CurrentAllocs() const {
|
||||||
return _currentAllocs;
|
return _currentAllocs;
|
||||||
@@ -346,21 +356,23 @@ public:
|
|||||||
Block* block = new Block();
|
Block* block = new Block();
|
||||||
_blockPtrs.Push( block );
|
_blockPtrs.Push( block );
|
||||||
|
|
||||||
for( int i=0; i<COUNT-1; ++i ) {
|
Item* blockItems = block->items;
|
||||||
block->chunk[i].next = &block->chunk[i+1];
|
for( int i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) {
|
||||||
|
blockItems[i].next = &(blockItems[i + 1]);
|
||||||
}
|
}
|
||||||
block->chunk[COUNT-1].next = 0;
|
blockItems[ITEMS_PER_BLOCK - 1].next = 0;
|
||||||
_root = block->chunk;
|
_root = blockItems;
|
||||||
}
|
}
|
||||||
void* result = _root;
|
Item* const result = _root;
|
||||||
|
TIXMLASSERT( result != 0 );
|
||||||
_root = _root->next;
|
_root = _root->next;
|
||||||
|
|
||||||
++_currentAllocs;
|
++_currentAllocs;
|
||||||
if ( _currentAllocs > _maxAllocs ) {
|
if ( _currentAllocs > _maxAllocs ) {
|
||||||
_maxAllocs = _currentAllocs;
|
_maxAllocs = _currentAllocs;
|
||||||
}
|
}
|
||||||
_nAllocs++;
|
++_nAllocs;
|
||||||
_nUntracked++;
|
++_nUntracked;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,20 +381,21 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
--_currentAllocs;
|
--_currentAllocs;
|
||||||
Chunk* chunk = static_cast<Chunk*>( mem );
|
Item* item = static_cast<Item*>( mem );
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
memset( chunk, 0xfe, sizeof(Chunk) );
|
memset( item, 0xfe, sizeof( *item ) );
|
||||||
#endif
|
#endif
|
||||||
chunk->next = _root;
|
item->next = _root;
|
||||||
_root = chunk;
|
_root = item;
|
||||||
}
|
}
|
||||||
void Trace( const char* name ) {
|
void Trace( const char* name ) {
|
||||||
printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n",
|
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() {
|
void SetTracked() {
|
||||||
_nUntracked--;
|
--_nUntracked;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Untracked() const {
|
int Untracked() const {
|
||||||
@@ -398,21 +411,23 @@ public:
|
|||||||
// 16k: 5200
|
// 16k: 5200
|
||||||
// 32k: 4300
|
// 32k: 4300
|
||||||
// 64k: 4000 21000
|
// 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:
|
private:
|
||||||
MemPoolT( const MemPoolT& ); // not supported
|
MemPoolT( const MemPoolT& ); // not supported
|
||||||
void operator=( const MemPoolT& ); // not supported
|
void operator=( const MemPoolT& ); // not supported
|
||||||
|
|
||||||
union Chunk {
|
union Item {
|
||||||
Chunk* next;
|
Item* next;
|
||||||
char mem[SIZE];
|
char itemData[ITEM_SIZE];
|
||||||
};
|
};
|
||||||
struct Block {
|
struct Block {
|
||||||
Chunk chunk[COUNT];
|
Item items[ITEMS_PER_BLOCK];
|
||||||
};
|
};
|
||||||
DynArray< Block*, 10 > _blockPtrs;
|
DynArray< Block*, 10 > _blockPtrs;
|
||||||
Chunk* _root;
|
Item* _root;
|
||||||
|
|
||||||
int _currentAllocs;
|
int _currentAllocs;
|
||||||
int _nAllocs;
|
int _nAllocs;
|
||||||
@@ -485,7 +500,6 @@ public:
|
|||||||
// WARNING: must match XMLDocument::_errorNames[]
|
// WARNING: must match XMLDocument::_errorNames[]
|
||||||
enum XMLError {
|
enum XMLError {
|
||||||
XML_SUCCESS = 0,
|
XML_SUCCESS = 0,
|
||||||
XML_NO_ERROR = 0,
|
|
||||||
XML_NO_ATTRIBUTE,
|
XML_NO_ATTRIBUTE,
|
||||||
XML_WRONG_ATTRIBUTE_TYPE,
|
XML_WRONG_ATTRIBUTE_TYPE,
|
||||||
XML_ERROR_FILE_NOT_FOUND,
|
XML_ERROR_FILE_NOT_FOUND,
|
||||||
@@ -513,19 +527,23 @@ enum XMLError {
|
|||||||
/*
|
/*
|
||||||
Utility functionality.
|
Utility functionality.
|
||||||
*/
|
*/
|
||||||
class XMLUtil
|
class TINYXML2_LIB XMLUtil
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const char* SkipWhiteSpace( const char* p ) {
|
static const char* SkipWhiteSpace( const char* p, int* curLineNumPtr ) {
|
||||||
TIXMLASSERT( p );
|
TIXMLASSERT( p );
|
||||||
|
|
||||||
while( IsWhiteSpace(*p) ) {
|
while( IsWhiteSpace(*p) ) {
|
||||||
|
if (curLineNumPtr && *p == '\n') {
|
||||||
|
++(*curLineNumPtr);
|
||||||
|
}
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
TIXMLASSERT( p );
|
TIXMLASSERT( p );
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
static char* SkipWhiteSpace( char* p ) {
|
static char* SkipWhiteSpace( char* p, int* curLineNumPtr ) {
|
||||||
return const_cast<char*>( SkipWhiteSpace( const_cast<const char*>(p) ) );
|
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
|
// 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 ) {
|
if ( p == q ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
TIXMLASSERT( p );
|
||||||
|
TIXMLASSERT( q );
|
||||||
|
TIXMLASSERT( nChar >= 0 );
|
||||||
return strncmp( p, q, 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( bool v, char* buffer, int bufferSize );
|
||||||
static void ToStr( float 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( double v, char* buffer, int bufferSize );
|
||||||
|
static void ToStr(int64_t v, char* buffer, int bufferSize);
|
||||||
|
|
||||||
// converts strings to primitive types
|
// converts strings to primitive types
|
||||||
static bool ToInt( const char* str, int* value );
|
static bool ToInt( const char* str, int* value );
|
||||||
@@ -582,6 +604,18 @@ public:
|
|||||||
static bool ToBool( const char* str, bool* value );
|
static bool ToBool( const char* str, bool* value );
|
||||||
static bool ToFloat( const char* str, float* value );
|
static bool ToFloat( const char* str, float* value );
|
||||||
static bool ToDouble( const char* str, double* 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 );
|
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.
|
/// Get the parent of this node on the DOM.
|
||||||
const XMLNode* Parent() const {
|
const XMLNode* Parent() const {
|
||||||
return _parent;
|
return _parent;
|
||||||
@@ -852,15 +889,30 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual bool Accept( XMLVisitor* visitor ) const = 0;
|
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:
|
protected:
|
||||||
XMLNode( XMLDocument* );
|
XMLNode( XMLDocument* );
|
||||||
virtual ~XMLNode();
|
virtual ~XMLNode();
|
||||||
|
|
||||||
virtual char* ParseDeep( char*, StrPair* );
|
virtual char* ParseDeep( char*, StrPair*, int* );
|
||||||
|
|
||||||
XMLDocument* _document;
|
XMLDocument* _document;
|
||||||
XMLNode* _parent;
|
XMLNode* _parent;
|
||||||
mutable StrPair _value;
|
mutable StrPair _value;
|
||||||
|
int _parseLineNum;
|
||||||
|
|
||||||
XMLNode* _firstChild;
|
XMLNode* _firstChild;
|
||||||
XMLNode* _lastChild;
|
XMLNode* _lastChild;
|
||||||
@@ -868,11 +920,14 @@ protected:
|
|||||||
XMLNode* _prev;
|
XMLNode* _prev;
|
||||||
XMLNode* _next;
|
XMLNode* _next;
|
||||||
|
|
||||||
|
void* _userData;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MemPool* _memPool;
|
MemPool* _memPool;
|
||||||
void Unlink( XMLNode* child );
|
void Unlink( XMLNode* child );
|
||||||
static void DeleteNode( XMLNode* node );
|
static void DeleteNode( XMLNode* node );
|
||||||
void InsertChildPreamble( XMLNode* insertThis ) const;
|
void InsertChildPreamble( XMLNode* insertThis ) const;
|
||||||
|
const XMLElement* ToElementWithName( const char* name ) const;
|
||||||
|
|
||||||
XMLNode( const XMLNode& ); // not supported
|
XMLNode( const XMLNode& ); // not supported
|
||||||
XMLNode& operator=( const XMLNode& ); // not supported
|
XMLNode& operator=( const XMLNode& ); // not supported
|
||||||
@@ -893,7 +948,6 @@ private:
|
|||||||
*/
|
*/
|
||||||
class TINYXML2_LIB XMLText : public XMLNode
|
class TINYXML2_LIB XMLText : public XMLNode
|
||||||
{
|
{
|
||||||
friend class XMLBase;
|
|
||||||
friend class XMLDocument;
|
friend class XMLDocument;
|
||||||
public:
|
public:
|
||||||
virtual bool Accept( XMLVisitor* visitor ) const;
|
virtual bool Accept( XMLVisitor* visitor ) const;
|
||||||
@@ -921,7 +975,7 @@ protected:
|
|||||||
XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {}
|
XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {}
|
||||||
virtual ~XMLText() {}
|
virtual ~XMLText() {}
|
||||||
|
|
||||||
char* ParseDeep( char*, StrPair* endTag );
|
char* ParseDeep( char*, StrPair* endTag, int* curLineNumPtr );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _isCData;
|
bool _isCData;
|
||||||
@@ -952,7 +1006,7 @@ protected:
|
|||||||
XMLComment( XMLDocument* doc );
|
XMLComment( XMLDocument* doc );
|
||||||
virtual ~XMLComment();
|
virtual ~XMLComment();
|
||||||
|
|
||||||
char* ParseDeep( char*, StrPair* endTag );
|
char* ParseDeep( char*, StrPair* endTag, int* curLineNumPtr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
XMLComment( const XMLComment& ); // not supported
|
XMLComment( const XMLComment& ); // not supported
|
||||||
@@ -991,7 +1045,7 @@ protected:
|
|||||||
XMLDeclaration( XMLDocument* doc );
|
XMLDeclaration( XMLDocument* doc );
|
||||||
virtual ~XMLDeclaration();
|
virtual ~XMLDeclaration();
|
||||||
|
|
||||||
char* ParseDeep( char*, StrPair* endTag );
|
char* ParseDeep( char*, StrPair* endTag, int* curLineNumPtr );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
XMLDeclaration( const XMLDeclaration& ); // not supported
|
XMLDeclaration( const XMLDeclaration& ); // not supported
|
||||||
@@ -1026,7 +1080,7 @@ protected:
|
|||||||
XMLUnknown( XMLDocument* doc );
|
XMLUnknown( XMLDocument* doc );
|
||||||
virtual ~XMLUnknown();
|
virtual ~XMLUnknown();
|
||||||
|
|
||||||
char* ParseDeep( char*, StrPair* endTag );
|
char* ParseDeep( char*, StrPair* endTag, int* curLineNumPtr );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
XMLUnknown( const XMLUnknown& ); // not supported
|
XMLUnknown( const XMLUnknown& ); // not supported
|
||||||
@@ -1051,6 +1105,9 @@ public:
|
|||||||
/// The value of the attribute.
|
/// The value of the attribute.
|
||||||
const char* Value() const;
|
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.
|
/// The next attribute in the list.
|
||||||
const XMLAttribute* Next() const {
|
const XMLAttribute* Next() const {
|
||||||
return _next;
|
return _next;
|
||||||
@@ -1060,11 +1117,18 @@ public:
|
|||||||
If the value isn't an integer, 0 will be returned. There is no error checking;
|
If the value isn't an integer, 0 will be returned. There is no error checking;
|
||||||
use QueryIntValue() if you need error checking.
|
use QueryIntValue() if you need error checking.
|
||||||
*/
|
*/
|
||||||
int IntValue() const {
|
int IntValue() const {
|
||||||
int i=0;
|
int i = 0;
|
||||||
QueryIntValue( &i );
|
QueryIntValue(&i);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t Int64Value() const {
|
||||||
|
int64_t i = 0;
|
||||||
|
QueryInt64Value(&i);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
/// Query as an unsigned integer. See IntValue()
|
/// Query as an unsigned integer. See IntValue()
|
||||||
unsigned UnsignedValue() const {
|
unsigned UnsignedValue() const {
|
||||||
unsigned i=0;
|
unsigned i=0;
|
||||||
@@ -1091,13 +1155,15 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** QueryIntValue interprets the attribute as an integer, and returns the value
|
/** 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.
|
and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful.
|
||||||
*/
|
*/
|
||||||
XMLError QueryIntValue( int* value ) const;
|
XMLError QueryIntValue( int* value ) const;
|
||||||
/// See QueryIntValue
|
/// See QueryIntValue
|
||||||
XMLError QueryUnsignedValue( unsigned int* value ) const;
|
XMLError QueryUnsignedValue( unsigned int* value ) const;
|
||||||
/// See QueryIntValue
|
/// See QueryIntValue
|
||||||
|
XMLError QueryInt64Value(int64_t* value) const;
|
||||||
|
/// See QueryIntValue
|
||||||
XMLError QueryBoolValue( bool* value ) const;
|
XMLError QueryBoolValue( bool* value ) const;
|
||||||
/// See QueryIntValue
|
/// See QueryIntValue
|
||||||
XMLError QueryDoubleValue( double* value ) const;
|
XMLError QueryDoubleValue( double* value ) const;
|
||||||
@@ -1110,7 +1176,9 @@ public:
|
|||||||
void SetAttribute( int value );
|
void SetAttribute( int value );
|
||||||
/// Set the attribute to value.
|
/// Set the attribute to value.
|
||||||
void SetAttribute( unsigned 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 );
|
void SetAttribute( bool value );
|
||||||
/// Set the attribute to value.
|
/// Set the attribute to value.
|
||||||
void SetAttribute( double value );
|
void SetAttribute( double value );
|
||||||
@@ -1120,17 +1188,18 @@ public:
|
|||||||
private:
|
private:
|
||||||
enum { BUF_SIZE = 200 };
|
enum { BUF_SIZE = 200 };
|
||||||
|
|
||||||
XMLAttribute() : _next( 0 ), _memPool( 0 ) {}
|
XMLAttribute() : _parseLineNum( 0 ), _next( 0 ), _memPool( 0 ) {}
|
||||||
virtual ~XMLAttribute() {}
|
virtual ~XMLAttribute() {}
|
||||||
|
|
||||||
XMLAttribute( const XMLAttribute& ); // not supported
|
XMLAttribute( const XMLAttribute& ); // not supported
|
||||||
void operator=( const XMLAttribute& ); // not supported
|
void operator=( const XMLAttribute& ); // not supported
|
||||||
void SetName( const char* name );
|
void SetName( const char* name );
|
||||||
|
|
||||||
char* ParseDeep( char* p, bool processEntities );
|
char* ParseDeep( char* p, bool processEntities, int* curLineNumPtr );
|
||||||
|
|
||||||
mutable StrPair _name;
|
mutable StrPair _name;
|
||||||
mutable StrPair _value;
|
mutable StrPair _value;
|
||||||
|
int _parseLineNum;
|
||||||
XMLAttribute* _next;
|
XMLAttribute* _next;
|
||||||
MemPool* _memPool;
|
MemPool* _memPool;
|
||||||
};
|
};
|
||||||
@@ -1142,7 +1211,6 @@ private:
|
|||||||
*/
|
*/
|
||||||
class TINYXML2_LIB XMLElement : public XMLNode
|
class TINYXML2_LIB XMLElement : public XMLNode
|
||||||
{
|
{
|
||||||
friend class XMLBase;
|
|
||||||
friend class XMLDocument;
|
friend class XMLDocument;
|
||||||
public:
|
public:
|
||||||
/// Get the name of an element (which is the Value() of the node.)
|
/// 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;
|
const char* Attribute( const char* name, const char* value=0 ) const;
|
||||||
|
|
||||||
/** Given an attribute name, IntAttribute() returns the value
|
/** Given an attribute name, IntAttribute() returns the value
|
||||||
of the attribute interpreted as an integer. 0 will be
|
of the attribute interpreted as an integer. The default
|
||||||
returned if there is an error. For a method with error
|
value will be returned if the attribute isn't present,
|
||||||
checking, see QueryIntAttribute()
|
or if there is an error. (For a method with error
|
||||||
|
checking, see QueryIntAttribute()).
|
||||||
*/
|
*/
|
||||||
int IntAttribute( const char* name ) const {
|
int IntAttribute(const char* name, int defaultValue = 0) const;
|
||||||
int i=0;
|
|
||||||
QueryIntAttribute( name, &i );
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
/// See IntAttribute()
|
/// See IntAttribute()
|
||||||
unsigned UnsignedAttribute( const char* name ) const {
|
unsigned UnsignedAttribute(const char* name, unsigned defaultValue = 0) const;
|
||||||
unsigned i=0;
|
/// See IntAttribute()
|
||||||
QueryUnsignedAttribute( name, &i );
|
int64_t Int64Attribute(const char* name, int64_t defaultValue = 0) const;
|
||||||
return i;
|
/// See IntAttribute()
|
||||||
}
|
bool BoolAttribute(const char* name, bool defaultValue = false) const;
|
||||||
/// See IntAttribute()
|
/// See IntAttribute()
|
||||||
bool BoolAttribute( const char* name ) const {
|
double DoubleAttribute(const char* name, double defaultValue = 0) const;
|
||||||
bool b=false;
|
|
||||||
QueryBoolAttribute( name, &b );
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
/// See IntAttribute()
|
/// See IntAttribute()
|
||||||
double DoubleAttribute( const char* name ) const {
|
float FloatAttribute(const char* name, float defaultValue = 0) 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Given an attribute name, QueryIntAttribute() returns
|
/** 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
|
can't be performed, or XML_NO_ATTRIBUTE if the attribute
|
||||||
doesn't exist. If successful, the result of the conversion
|
doesn't exist. If successful, the result of the conversion
|
||||||
will be written to 'value'. If not successful, nothing will
|
will be written to 'value'. If not successful, nothing will
|
||||||
@@ -1242,7 +1293,8 @@ public:
|
|||||||
}
|
}
|
||||||
return a->QueryIntValue( value );
|
return a->QueryIntValue( value );
|
||||||
}
|
}
|
||||||
/// See QueryIntAttribute()
|
|
||||||
|
/// See QueryIntAttribute()
|
||||||
XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const {
|
XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const {
|
||||||
const XMLAttribute* a = FindAttribute( name );
|
const XMLAttribute* a = FindAttribute( name );
|
||||||
if ( !a ) {
|
if ( !a ) {
|
||||||
@@ -1250,7 +1302,17 @@ public:
|
|||||||
}
|
}
|
||||||
return a->QueryUnsignedValue( value );
|
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 {
|
XMLError QueryBoolAttribute( const char* name, bool* value ) const {
|
||||||
const XMLAttribute* a = FindAttribute( name );
|
const XMLAttribute* a = FindAttribute( name );
|
||||||
if ( !a ) {
|
if ( !a ) {
|
||||||
@@ -1277,7 +1339,7 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
/** Given an attribute name, QueryAttribute() returns
|
/** 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
|
can't be performed, or XML_NO_ATTRIBUTE if the attribute
|
||||||
doesn't exist. It is overloaded for the primitive types,
|
doesn't exist. It is overloaded for the primitive types,
|
||||||
and is a generally more convenient replacement of
|
and is a generally more convenient replacement of
|
||||||
@@ -1301,6 +1363,10 @@ public:
|
|||||||
return QueryUnsignedAttribute( name, value );
|
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 {
|
int QueryAttribute( const char* name, bool* value ) const {
|
||||||
return QueryBoolAttribute( name, value );
|
return QueryBoolAttribute( name, value );
|
||||||
}
|
}
|
||||||
@@ -1328,7 +1394,14 @@ public:
|
|||||||
XMLAttribute* a = FindOrCreateAttribute( name );
|
XMLAttribute* a = FindOrCreateAttribute( name );
|
||||||
a->SetAttribute( value );
|
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 ) {
|
void SetAttribute( const char* name, bool value ) {
|
||||||
XMLAttribute* a = FindOrCreateAttribute( name );
|
XMLAttribute* a = FindOrCreateAttribute( name );
|
||||||
a->SetAttribute( value );
|
a->SetAttribute( value );
|
||||||
@@ -1425,7 +1498,9 @@ public:
|
|||||||
void SetText( int value );
|
void SetText( int 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( unsigned value );
|
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 );
|
void SetText( bool 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( double value );
|
void SetText( double value );
|
||||||
@@ -1461,13 +1536,28 @@ public:
|
|||||||
XMLError QueryIntText( int* ival ) const;
|
XMLError QueryIntText( int* ival ) const;
|
||||||
/// See QueryIntText()
|
/// See QueryIntText()
|
||||||
XMLError QueryUnsignedText( unsigned* uval ) const;
|
XMLError QueryUnsignedText( unsigned* uval ) const;
|
||||||
/// See QueryIntText()
|
/// See QueryIntText()
|
||||||
|
XMLError QueryInt64Text(int64_t* uval) const;
|
||||||
|
/// See QueryIntText()
|
||||||
XMLError QueryBoolText( bool* bval ) const;
|
XMLError QueryBoolText( bool* bval ) const;
|
||||||
/// See QueryIntText()
|
/// See QueryIntText()
|
||||||
XMLError QueryDoubleText( double* dval ) const;
|
XMLError QueryDoubleText( double* dval ) const;
|
||||||
/// See QueryIntText()
|
/// See QueryIntText()
|
||||||
XMLError QueryFloatText( float* fval ) const;
|
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:
|
// internal:
|
||||||
enum {
|
enum {
|
||||||
OPEN, // <foo>
|
OPEN, // <foo>
|
||||||
@@ -1481,7 +1571,7 @@ public:
|
|||||||
virtual bool ShallowEqual( const XMLNode* compare ) const;
|
virtual bool ShallowEqual( const XMLNode* compare ) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
char* ParseDeep( char* p, StrPair* endTag );
|
char* ParseDeep( char* p, StrPair* endTag, int* curLineNumPtr );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
XMLElement( XMLDocument* doc );
|
XMLElement( XMLDocument* doc );
|
||||||
@@ -1494,8 +1584,9 @@ private:
|
|||||||
}
|
}
|
||||||
XMLAttribute* FindOrCreateAttribute( const char* name );
|
XMLAttribute* FindOrCreateAttribute( const char* name );
|
||||||
//void LinkAttribute( XMLAttribute* attrib );
|
//void LinkAttribute( XMLAttribute* attrib );
|
||||||
char* ParseAttributes( char* p );
|
char* ParseAttributes( char* p, int* curLineNumPtr );
|
||||||
static void DeleteAttribute( XMLAttribute* attribute );
|
static void DeleteAttribute( XMLAttribute* attribute );
|
||||||
|
XMLAttribute* CreateAttribute();
|
||||||
|
|
||||||
enum { BUF_SIZE = 200 };
|
enum { BUF_SIZE = 200 };
|
||||||
int _closingType;
|
int _closingType;
|
||||||
@@ -1522,7 +1613,7 @@ class TINYXML2_LIB XMLDocument : public XMLNode
|
|||||||
friend class XMLElement;
|
friend class XMLElement;
|
||||||
public:
|
public:
|
||||||
/// constructor
|
/// constructor
|
||||||
XMLDocument( bool processEntities = true, Whitespace = PRESERVE_WHITESPACE );
|
XMLDocument( bool processEntities = true, Whitespace whitespaceMode = PRESERVE_WHITESPACE );
|
||||||
~XMLDocument();
|
~XMLDocument();
|
||||||
|
|
||||||
virtual XMLDocument* ToDocument() {
|
virtual XMLDocument* ToDocument() {
|
||||||
@@ -1536,7 +1627,7 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
Parse an XML file from a character string.
|
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.
|
an errorID.
|
||||||
|
|
||||||
You may optionally pass in the 'nBytes', which is
|
You may optionally pass in the 'nBytes', which is
|
||||||
@@ -1548,7 +1639,7 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
Load an XML file from disk.
|
Load an XML file from disk.
|
||||||
Returns XML_NO_ERROR (0) on success, or
|
Returns XML_SUCCESS (0) on success, or
|
||||||
an errorID.
|
an errorID.
|
||||||
*/
|
*/
|
||||||
XMLError LoadFile( const char* filename );
|
XMLError LoadFile( const char* filename );
|
||||||
@@ -1561,14 +1652,14 @@ public:
|
|||||||
not text in order for TinyXML-2 to correctly
|
not text in order for TinyXML-2 to correctly
|
||||||
do newline normalization.
|
do newline normalization.
|
||||||
|
|
||||||
Returns XML_NO_ERROR (0) on success, or
|
Returns XML_SUCCESS (0) on success, or
|
||||||
an errorID.
|
an errorID.
|
||||||
*/
|
*/
|
||||||
XMLError LoadFile( FILE* );
|
XMLError LoadFile( FILE* );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Save the XML file to disk.
|
Save the XML file to disk.
|
||||||
Returns XML_NO_ERROR (0) on success, or
|
Returns XML_SUCCESS (0) on success, or
|
||||||
an errorID.
|
an errorID.
|
||||||
*/
|
*/
|
||||||
XMLError SaveFile( const char* filename, bool compact = false );
|
XMLError SaveFile( const char* filename, bool compact = false );
|
||||||
@@ -1577,7 +1668,7 @@ public:
|
|||||||
Save the XML file to disk. You are responsible
|
Save the XML file to disk. You are responsible
|
||||||
for providing and closing the FILE*.
|
for providing and closing the FILE*.
|
||||||
|
|
||||||
Returns XML_NO_ERROR (0) on success, or
|
Returns XML_SUCCESS (0) on success, or
|
||||||
an errorID.
|
an errorID.
|
||||||
*/
|
*/
|
||||||
XMLError SaveFile( FILE* fp, bool compact = false );
|
XMLError SaveFile( FILE* fp, bool compact = false );
|
||||||
@@ -1586,7 +1677,7 @@ public:
|
|||||||
return _processEntities;
|
return _processEntities;
|
||||||
}
|
}
|
||||||
Whitespace WhitespaceMode() const {
|
Whitespace WhitespaceMode() const {
|
||||||
return _whitespace;
|
return _whitespaceMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1671,25 +1762,35 @@ public:
|
|||||||
*/
|
*/
|
||||||
void DeleteNode( XMLNode* node );
|
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.
|
/// Return true if there was an error parsing the document.
|
||||||
bool Error() const {
|
bool Error() const {
|
||||||
return _errorID != XML_NO_ERROR;
|
return _errorID != XML_SUCCESS;
|
||||||
}
|
}
|
||||||
/// Return the errorID.
|
/// Return the errorID.
|
||||||
XMLError ErrorID() const {
|
XMLError ErrorID() const {
|
||||||
return _errorID;
|
return _errorID;
|
||||||
}
|
}
|
||||||
const char* ErrorName() const;
|
const char* ErrorName() const;
|
||||||
|
static const char* ErrorIDToName(XMLError errorID);
|
||||||
|
|
||||||
/// Return a possibly helpful diagnostic location or string.
|
/// Return a possibly helpful diagnostic location or string.
|
||||||
const char* GetErrorStr1() const {
|
const char* GetErrorStr1() const {
|
||||||
return _errorStr1;
|
return _errorStr1.GetStr();
|
||||||
}
|
}
|
||||||
/// Return a possibly helpful secondary diagnostic location or string.
|
/// Return a possibly helpful secondary diagnostic location or string.
|
||||||
const char* GetErrorStr2() const {
|
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.
|
/// If there is an error, print it to stdout.
|
||||||
void PrintError() const;
|
void PrintError() const;
|
||||||
@@ -1711,13 +1812,15 @@ private:
|
|||||||
XMLDocument( const XMLDocument& ); // not supported
|
XMLDocument( const XMLDocument& ); // not supported
|
||||||
void operator=( const XMLDocument& ); // not supported
|
void operator=( const XMLDocument& ); // not supported
|
||||||
|
|
||||||
bool _writeBOM;
|
bool _writeBOM;
|
||||||
bool _processEntities;
|
bool _processEntities;
|
||||||
XMLError _errorID;
|
XMLError _errorID;
|
||||||
Whitespace _whitespace;
|
Whitespace _whitespaceMode;
|
||||||
const char* _errorStr1;
|
mutable StrPair _errorStr1;
|
||||||
const char* _errorStr2;
|
mutable StrPair _errorStr2;
|
||||||
char* _charBuffer;
|
int _errorLineNum;
|
||||||
|
char* _charBuffer;
|
||||||
|
int _parseCurLineNum;
|
||||||
|
|
||||||
MemPoolT< sizeof(XMLElement) > _elementPool;
|
MemPoolT< sizeof(XMLElement) > _elementPool;
|
||||||
MemPoolT< sizeof(XMLAttribute) > _attributePool;
|
MemPoolT< sizeof(XMLAttribute) > _attributePool;
|
||||||
@@ -1727,8 +1830,21 @@ private:
|
|||||||
static const char* _errorNames[XML_ERROR_COUNT];
|
static const char* _errorNames[XML_ERROR_COUNT];
|
||||||
|
|
||||||
void Parse();
|
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
|
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.
|
/// Safe cast to XMLElement. This can return null.
|
||||||
XMLElement* ToElement() {
|
XMLElement* ToElement() {
|
||||||
return ( ( _node == 0 ) ? 0 : _node->ToElement() );
|
return ( _node ? _node->ToElement() : 0 );
|
||||||
}
|
}
|
||||||
/// Safe cast to XMLText. This can return null.
|
/// Safe cast to XMLText. This can return null.
|
||||||
XMLText* ToText() {
|
XMLText* ToText() {
|
||||||
return ( ( _node == 0 ) ? 0 : _node->ToText() );
|
return ( _node ? _node->ToText() : 0 );
|
||||||
}
|
}
|
||||||
/// Safe cast to XMLUnknown. This can return null.
|
/// Safe cast to XMLUnknown. This can return null.
|
||||||
XMLUnknown* ToUnknown() {
|
XMLUnknown* ToUnknown() {
|
||||||
return ( ( _node == 0 ) ? 0 : _node->ToUnknown() );
|
return ( _node ? _node->ToUnknown() : 0 );
|
||||||
}
|
}
|
||||||
/// Safe cast to XMLDeclaration. This can return null.
|
/// Safe cast to XMLDeclaration. This can return null.
|
||||||
XMLDeclaration* ToDeclaration() {
|
XMLDeclaration* ToDeclaration() {
|
||||||
return ( ( _node == 0 ) ? 0 : _node->ToDeclaration() );
|
return ( _node ? _node->ToDeclaration() : 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -1917,16 +2033,16 @@ public:
|
|||||||
return _node;
|
return _node;
|
||||||
}
|
}
|
||||||
const XMLElement* ToElement() const {
|
const XMLElement* ToElement() const {
|
||||||
return ( ( _node == 0 ) ? 0 : _node->ToElement() );
|
return ( _node ? _node->ToElement() : 0 );
|
||||||
}
|
}
|
||||||
const XMLText* ToText() const {
|
const XMLText* ToText() const {
|
||||||
return ( ( _node == 0 ) ? 0 : _node->ToText() );
|
return ( _node ? _node->ToText() : 0 );
|
||||||
}
|
}
|
||||||
const XMLUnknown* ToUnknown() const {
|
const XMLUnknown* ToUnknown() const {
|
||||||
return ( ( _node == 0 ) ? 0 : _node->ToUnknown() );
|
return ( _node ? _node->ToUnknown() : 0 );
|
||||||
}
|
}
|
||||||
const XMLDeclaration* ToDeclaration() const {
|
const XMLDeclaration* ToDeclaration() const {
|
||||||
return ( ( _node == 0 ) ? 0 : _node->ToDeclaration() );
|
return ( _node ? _node->ToDeclaration() : 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -1998,7 +2114,8 @@ public:
|
|||||||
void PushAttribute( const char* name, const char* value );
|
void PushAttribute( const char* name, const char* value );
|
||||||
void PushAttribute( const char* name, int value );
|
void PushAttribute( const char* name, int value );
|
||||||
void PushAttribute( const char* name, unsigned 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 );
|
void PushAttribute( const char* name, double value );
|
||||||
/// If streaming, close the Element.
|
/// If streaming, close the Element.
|
||||||
virtual void CloseElement( bool compactMode=false );
|
virtual void CloseElement( bool compactMode=false );
|
||||||
@@ -2009,7 +2126,9 @@ public:
|
|||||||
void PushText( int value );
|
void PushText( int value );
|
||||||
/// Add a text node from an unsigned.
|
/// Add a text node from an unsigned.
|
||||||
void PushText( unsigned value );
|
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 );
|
void PushText( bool value );
|
||||||
/// Add a text node from a float.
|
/// Add a text node from a float.
|
||||||
void PushText( float value );
|
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) = "*WiFiOptimizer*";
|
||||||
|
|
||||||
|
|
||||||
::testing::GTEST_FLAG(filter) = "*NormalN*";
|
::testing::GTEST_FLAG(filter) = "*FloorplanCeilings*";
|
||||||
//::testing::GTEST_FLAG(filter) = "*Barometer*";
|
//::testing::GTEST_FLAG(filter) = "*Barometer*";
|
||||||
//::testing::GTEST_FLAG(filter) = "*GridWalk2RelPressure*";
|
//::testing::GTEST_FLAG(filter) = "*GridWalk2RelPressure*";
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ private:
|
|||||||
} fields;
|
} fields;
|
||||||
|
|
||||||
/** store as 64-bit integer */
|
/** store as 64-bit integer */
|
||||||
uint64_t mac;
|
uint64_t mac = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -61,8 +61,8 @@ public:
|
|||||||
fields.h5 = hexWordToInt(str[ 0], str[ 1]);
|
fields.h5 = hexWordToInt(str[ 0], str[ 1]);
|
||||||
fields.h4 = hexWordToInt(str[ 2], str[ 3]);
|
fields.h4 = hexWordToInt(str[ 2], str[ 3]);
|
||||||
fields.h3 = hexWordToInt(str[ 4], str[ 5]);
|
fields.h3 = hexWordToInt(str[ 4], str[ 5]);
|
||||||
fields.h2 = hexWordToInt(str[ 6], str[7]);
|
fields.h2 = hexWordToInt(str[ 6], str[ 7]);
|
||||||
fields.h1 = hexWordToInt(str[8], str[9]);
|
fields.h1 = hexWordToInt(str[ 8], str[ 9]);
|
||||||
fields.h0 = hexWordToInt(str[10], str[11]);
|
fields.h0 = hexWordToInt(str[10], str[11]);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
@@ -89,7 +89,9 @@ public:
|
|||||||
|
|
||||||
/** get the mac address as a long-int value */
|
/** get the mac address as a long-int value */
|
||||||
uint64_t asLong() const {
|
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? */
|
/** equal? */
|
||||||
@@ -105,13 +107,19 @@ public:
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
/** convert the given hex char [0-F] to an integer [0-15] */
|
/** convert the given hex char [0-F] to an integer [0-15] */
|
||||||
static uint8_t hexCharToInt(const char _hex) {
|
static uint8_t hexCharToInt(const char hex) {
|
||||||
|
|
||||||
// to upper case
|
|
||||||
const char hex = (_hex >= 'a') ? (_hex - ('a' - 'A')) : (_hex);
|
|
||||||
|
|
||||||
// convert
|
// 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 "../../math/Stats.h"
|
||||||
|
|
||||||
|
#include "../../misc/Debug.h"
|
||||||
|
|
||||||
#include "WiFiMeasurements.h"
|
#include "WiFiMeasurements.h"
|
||||||
|
|
||||||
class VAPGrouper {
|
class VAPGrouper {
|
||||||
@@ -40,6 +42,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
static constexpr const char* name = "VAPGrp";
|
||||||
|
|
||||||
/** the mode to use for grouping VAPs */
|
/** the mode to use for grouping VAPs */
|
||||||
const Mode mode;
|
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
|
// done
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#define WIFIMODELLOGDISTCEILING_H
|
#define WIFIMODELLOGDISTCEILING_H
|
||||||
|
|
||||||
#include "../../../floorplan/v2/Floorplan.h"
|
#include "../../../floorplan/v2/Floorplan.h"
|
||||||
|
#include "../../../floorplan/v2/FloorplanCeilings.h"
|
||||||
|
|
||||||
#include "../../../Assertions.h"
|
#include "../../../Assertions.h"
|
||||||
#include "WiFiModel.h"
|
#include "WiFiModel.h"
|
||||||
@@ -36,21 +37,18 @@ private:
|
|||||||
/** map of all APs (and their parameters) known to the model */
|
/** map of all APs (and their parameters) known to the model */
|
||||||
std::unordered_map<MACAddress, APEntry> accessPoints;
|
std::unordered_map<MACAddress, APEntry> accessPoints;
|
||||||
|
|
||||||
/** position (height) of all ceilings (in meter) */
|
// /** position (height) of all ceilings (in meter) */
|
||||||
std::vector<float> ceilingsAtHeight_m;
|
// std::vector<float> ceilingsAtHeight_m;
|
||||||
|
Floorplan::Ceilings ceilings;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/** ctor with floorplan (needed for ceiling position) */
|
/** ctor with floorplan (needed for ceiling position) */
|
||||||
WiFiModelLogDistCeiling(const Floorplan::IndoorMap* map) {
|
WiFiModelLogDistCeiling(const Floorplan::IndoorMap* map) : ceilings(map) {
|
||||||
|
|
||||||
// sanity checks
|
// sanity checks
|
||||||
Assert::isTrue(map->floors.size() >= 1, "map has no floors?!");
|
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);
|
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
|
// 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
|
// combine
|
||||||
return rssiLOS + wafLoss;
|
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>
|
#include <unordered_map>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* denotes a wifi fingerprint
|
* denotes a wifi fingerprint:
|
||||||
* known position and several measurements conducted at this position
|
* 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 {
|
struct WiFiFingerprint {
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ struct WiFiFingerprint {
|
|||||||
/** real-world-position that was measured */
|
/** real-world-position that was measured */
|
||||||
Point3 pos_m;
|
Point3 pos_m;
|
||||||
|
|
||||||
/** measurements (APs) at the given location */
|
/** seen APs at the given location */
|
||||||
WiFiMeasurements measurements;
|
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() {
|
WiFiMeasurements average() {
|
||||||
|
|
||||||
// group scans by MAC (all measurements for one AP)
|
// 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
|
#ifndef WIFIOPTIMIZER_H
|
||||||
#define OPTIMIZER_H
|
#define WIFIOPTIMIZER_H
|
||||||
|
|
||||||
#include "../../../floorplan/v2/Floorplan.h"
|
|
||||||
#include "../../../floorplan/v2/FloorplanHelper.h"
|
|
||||||
|
|
||||||
|
#include "WiFiFingerprints.h"
|
||||||
|
#include "WiFiOptimizerStructs.h"
|
||||||
#include "../VAPGrouper.h"
|
#include "../VAPGrouper.h"
|
||||||
#include "../../../geo/BBox3.h"
|
|
||||||
#include "../../../misc/Debug.h"
|
|
||||||
|
|
||||||
#include "WiFiFingerprint.h"
|
#include <unordered_map>
|
||||||
#include "../model/WiFiModel.h"
|
|
||||||
#include "../model/WiFiModelLogDistCeiling.h"
|
|
||||||
|
|
||||||
#include <KLib/math/optimization/NumOptAlgoDownhillSimplex.h>
|
namespace WiFiOptimizer {
|
||||||
#include <KLib/math/optimization/NumOptAlgoGenetic.h>
|
|
||||||
#include <KLib/math/optimization/NumOptAlgoRangeRandom.h>
|
|
||||||
|
|
||||||
#include <string>
|
/** base-class for all WiFiOptimizers */
|
||||||
#include <sstream>
|
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 */
|
/** how to handle virtual access points [group, not-group, ..] */
|
||||||
struct RSSIatPosition {
|
const VAPGrouper vg;
|
||||||
|
|
||||||
/** real-world position (in meter) */
|
public:
|
||||||
const Point3 pos_m;
|
|
||||||
|
|
||||||
/** measured signal strength (for one AP) */
|
|
||||||
const float rssi;
|
|
||||||
|
|
||||||
/** ctor */
|
/** ctor */
|
||||||
RSSIatPosition(const Point3 pos_m, const float rssi) : pos_m(pos_m), rssi(rssi) {;}
|
Base(const VAPGrouper vg) : vg(vg) {;}
|
||||||
|
|
||||||
};
|
/** get a list of all to-be-optimized access-points (given by their mac-address) */
|
||||||
|
virtual std::vector<MACAddress> getAllMACs() const {
|
||||||
public:
|
std::vector<MACAddress> res;
|
||||||
|
for (const auto& it : apMap) {res.push_back(it.first);}
|
||||||
struct APParams {
|
return res;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
/** 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) */
|
// group the fingerprint's measurements by VAP (if configured)
|
||||||
std::vector<MACAddress> getAllMACs() const {
|
const WiFiMeasurements measurements = vg.group(fp.measurements);
|
||||||
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;}
|
|
||||||
|
|
||||||
|
// 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;}
|
/** add new fingerprints to the optimizer as data-source */
|
||||||
if (params->txp > -35) {err += 999999;}
|
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>;
|
#endif // WIFIOPTIMIZER_H
|
||||||
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
|
|
||||||
|
|||||||
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) {
|
TEST(MAC, convertLong) {
|
||||||
|
|
||||||
MACAddress mac1("12:34:56:78:9A:BC");
|
MACAddress mac1("12:34:56:78:9A:BC");
|
||||||
MACAddress mac2 = MACAddress( mac1.asLong() );
|
MACAddress mac2 = MACAddress( mac1.asLong() );
|
||||||
ASSERT_EQ(mac1, mac2);
|
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
|
#endif
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifdef WITH_TESTS
|
#ifdef WITH_TESTS
|
||||||
|
|
||||||
#include "../../Tests.h"
|
#include "../../Tests.h"
|
||||||
#include "../../../sensors/radio/setup/WiFiOptimizer.h"
|
#include "../../../sensors/radio/setup/WiFiOptimizerLogDistCeiling.h"
|
||||||
#include "../../../sensors/radio/setup/WiFiFingerprint.h"
|
#include "../../../sensors/radio/setup/WiFiFingerprint.h"
|
||||||
#include "../../../misc/Debug.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) {
|
for (const WiFiFingerprint& fp : fingerprints) {
|
||||||
opt.addFingerprint(fp);
|
opt.addFingerprint(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT_EQ(2, opt.getAllMACs().size());
|
ASSERT_EQ(2, opt.getAllMACs().size());
|
||||||
float errRes;
|
WiFiOptimizer::LogDistCeiling::Stats errRes;
|
||||||
|
|
||||||
const WiFiOptimizer::APParams params1 = opt.optimize(mac1, errRes);
|
const WiFiOptimizer::LogDistCeiling::APParams params1 = opt.optimize(mac1, errRes);
|
||||||
ASSERT_TRUE(errRes < 0.1);
|
ASSERT_TRUE(errRes.error_db < 0.1);
|
||||||
ASSERT_NEAR(0, pos1.getDistance(params1.getPos()), 0.4); // apx position estimation
|
ASSERT_NEAR(0, pos1.getDistance(params1.getPos()), 0.4); // apx position estimation
|
||||||
ASSERT_NEAR(-40, params1.txp, 1.0);
|
ASSERT_NEAR(-40, params1.txp, 1.0);
|
||||||
ASSERT_NEAR(2, params1.exp, 0.1);
|
ASSERT_NEAR(2, params1.exp, 0.1);
|
||||||
ASSERT_NEAR(-4, params1.waf, 0.5);
|
ASSERT_NEAR(-4, params1.waf, 0.5);
|
||||||
|
|
||||||
const WiFiOptimizer::APParams params2 = opt.optimize(mac2, errRes);
|
const WiFiOptimizer::LogDistCeiling::APParams params2 = opt.optimize(mac2, errRes);
|
||||||
ASSERT_TRUE(errRes < 0.1);
|
ASSERT_TRUE(errRes.error_db < 0.1);
|
||||||
ASSERT_NEAR(0, pos2.getDistance(params2.getPos()), 0.4); // apx position estimation
|
ASSERT_NEAR(0, pos2.getDistance(params2.getPos()), 0.4); // apx position estimation
|
||||||
ASSERT_NEAR(-40, params1.txp, 1.0);
|
ASSERT_NEAR(-40, params1.txp, 1.0);
|
||||||
ASSERT_NEAR(2, params2.exp, 0.1);
|
ASSERT_NEAR(2, params2.exp, 0.1);
|
||||||
|
|||||||
Reference in New Issue
Block a user