2019-07-17 14:15:31 +00:00
"use strict" ;
2019-07-29 13:17:28 +00:00
import haversine from 'haversine-distance' ;
2019-07-30 17:56:45 +00:00
import shuffle _fisher _yates from '../Helpers/FisherYates.mjs' ;
2019-07-30 14:42:37 +00:00
import {
normalise _lat ,
normalise _lng ,
normalise _rssi ,
normalise _gateway _distance ,
} from '../../common/Normalisers.mjs' ;
2019-07-17 14:15:31 +00:00
class DatasetFetcher {
2019-08-02 12:17:17 +00:00
constructor ( { settings , log , GatewayRepo , RSSIRepo , ReadingRepo } ) {
2019-07-17 14:15:31 +00:00
this . settings = settings ;
2019-08-02 12:17:17 +00:00
this . l = log ;
2019-07-29 13:17:28 +00:00
this . repo _gateway = GatewayRepo ;
2019-07-17 14:15:31 +00:00
this . repo _rssi = RSSIRepo ;
2019-07-29 13:17:28 +00:00
this . repo _reading = ReadingRepo ;
2019-07-17 14:15:31 +00:00
}
2019-08-02 13:00:31 +00:00
fetch _all ( gateway _id , extended = false ) {
2019-08-06 11:10:47 +00:00
let result = [ ] ;
// Determine the location of the gateway
2019-07-29 13:17:28 +00:00
let gateway _location = this . repo _gateway . get _by _id ( gateway _id ) ;
2019-08-06 11:10:47 +00:00
// Grab an iterator for the data we want to add
2019-08-02 13:00:31 +00:00
let iterator = gateway _id == null ? this . repo _rssi . iterate ( ) : this . repo _rssi . iterate _gateway ( gateway _id ) ;
2019-08-06 11:10:47 +00:00
// Add the readings where we did get a signal
2019-08-02 13:00:31 +00:00
for ( let rssi of iterator ) {
if ( gateway _id == null )
gateway _location = this . repo _gateway . get _by _id ( rssi . gateway _id ) ;
let item = {
2019-07-30 14:42:37 +00:00
input : {
2019-08-02 12:17:17 +00:00
latitude : rssi . latitude ,
longitude : rssi . longitude ,
distance : haversine ( gateway _location , rssi )
2019-07-30 14:42:37 +00:00
} ,
output : [
2019-08-02 12:17:17 +00:00
rssi . rssi
2019-07-30 14:42:37 +00:00
]
2019-08-02 13:00:31 +00:00
} ;
if ( extended ) {
item . ext = {
gateway : rssi . gateway _id ,
rssi _raw : rssi . rssi
} ;
}
result . push ( item ) ;
2019-07-18 15:34:25 +00:00
}
2019-07-29 13:17:28 +00:00
2019-08-06 11:10:47 +00:00
// Add the readings where we did not get a signal
2019-07-29 13:17:28 +00:00
for ( let reading of this . repo _reading . iterate _unreceived ( ) ) {
2019-08-02 13:00:31 +00:00
let item = {
2019-07-30 14:42:37 +00:00
input : {
2019-08-02 12:17:17 +00:00
latitude : reading . latitude ,
longitude : reading . longitude ,
distance : haversine ( gateway _location , reading )
2019-07-30 14:42:37 +00:00
} ,
2019-08-02 12:17:17 +00:00
output : [ - 150 ]
2019-08-02 13:00:31 +00:00
} ;
if ( extended ) {
item . ext = {
gateway : "(none)" ,
rssi _raw : - 150
} ;
}
result . push ( item ) ;
2019-07-29 13:17:28 +00:00
}
2019-07-30 17:56:45 +00:00
2019-08-06 11:10:47 +00:00
// Zap the false negatives, but only if we're told to
// False neegatives are readings with not signal that are right next to
// a reading with a signal, within a configurable radius.
2019-08-02 12:17:17 +00:00
if ( this . settings . ai . do _zap _false _negatives ) {
2019-08-02 13:04:34 +00:00
let zap _count _before = result . length ,
zap _count = this . zap _false _negatives (
result ,
this . settings . ai . false _negative _zap _radius
) ,
zap _count _after = result . length ;
this . l . log _e ( ` [DatasetFetcher] Zapped ${ zap _count } false negatives with a radius of ${ this . settings . ai . false _negative _zap _radius } m ( ${ zap _count _before } -> ${ zap _count _after } points). ` ) ;
2019-08-02 12:17:17 +00:00
}
2019-08-06 11:10:47 +00:00
// Normalise all the values
2019-08-02 12:17:17 +00:00
for ( let item of result ) {
item . input . latitude = normalise _lat ( item . input . latitude ) ;
item . input . longitude = normalise _lng ( item . input . longitude ) ;
item . input . distance = normalise _gateway _distance ( item . input . distance ) ;
item . output [ 0 ] = normalise _rssi ( item . output [ 0 ] ) ;
}
2019-08-06 11:10:47 +00:00
// Shuffle the dataset
shuffle _fisher _yates ( result ) ;
// Scan the resulting dataset for invalid items
this . scan _for _corruption ( result ) ;
2019-07-29 13:17:28 +00:00
return result ;
}
2019-08-02 12:17:17 +00:00
zap _false _negatives ( readings _raw , max _distance _metres ) {
let items _zapped = 0 ;
for ( let next _item of readings _raw ) {
// Only zap for readings where we got a signal
if ( next _item . output [ 0 ] <= - 150 ) // -150: codename for no signal
continue ;
// console.log(next_item);
// Store a list of items to zap, because changing the length of the
// array while we're iterating it is a recipe for disaster
let items _to _zap = [ ] ;
for ( let comp _item of readings _raw ) {
// Avoid zapping readings where we got a signal
if ( comp _item . output [ 0 ] > - 150 )
continue ;
let distance = haversine (
next _item . input ,
comp _item . input
) ;
if ( isNaN ( distance ) )
throw new Error ( ` Error: Got NaN when checking zapping distance. ` ) ;
if ( distance < max _distance _metres ) {
2019-08-02 13:00:31 +00:00
// console.error(`Zap! (${distance})`);
2019-08-02 12:17:17 +00:00
items _to _zap . push ( comp _item ) ;
}
}
items _zapped += items _to _zap . length ;
for ( let next _item of items _to _zap ) {
readings _raw . splice ( readings _raw . indexOf ( next _item ) , 1 ) ;
}
}
return items _zapped ;
}
2019-08-06 11:10:47 +00:00
scan _for _corruption ( dataset ) {
// Scan the input data to make sure it is't corrupt
for ( let row of dataset ) {
if ( isNaN ( row . output [ 0 ] ) || isNaN ( row . input . latitude ) || isNaN ( row . input . longitude ) || isNaN ( row . input . distance ) ) {
console . error ( row ) ;
throw new Error ( "Error: Found NaN in input data" ) ;
}
if ( typeof row . output [ 0 ] !== "number" || typeof row . input . latitude !== "number" || typeof row . input . longitude !== "number" || typeof row . input . distance !== "number" ) {
console . error ( row ) ;
throw new Error ( "Error: Found item with an invalid type in the input data" ) ;
}
}
this . l . log _e ( ` Scanned ${ dataset . length } rows of data for invalid values. ` ) ;
}
2019-07-17 14:15:31 +00:00
}
2019-07-30 17:56:45 +00:00
2019-07-17 14:15:31 +00:00
export default DatasetFetcher ;