LoRaWAN-Signal-Mapping/server/train-ai/DatasetFetcher.mjs

135 lines
3.4 KiB
JavaScript

"use strict";
import haversine from 'haversine-distance';
import shuffle_fisher_yates from '../Helpers/FisherYates.mjs';
import {
normalise_lat,
normalise_lng,
normalise_rssi,
normalise_gateway_distance,
} from '../../common/Normalisers.mjs';
class DatasetFetcher {
constructor({ settings, log, GatewayRepo, RSSIRepo, ReadingRepo }) {
this.settings = settings;
this.l = log;
this.repo_gateway = GatewayRepo;
this.repo_rssi = RSSIRepo;
this.repo_reading = ReadingRepo;
}
fetch_all(gateway_id, extended = false) {
let gateway_location = this.repo_gateway.get_by_id(gateway_id);
let iterator = gateway_id == null ? this.repo_rssi.iterate() : this.repo_rssi.iterate_gateway(gateway_id);
let result = [];
for(let rssi of iterator) {
if(gateway_id == null)
gateway_location = this.repo_gateway.get_by_id(rssi.gateway_id);
let item = {
input: {
latitude: rssi.latitude,
longitude: rssi.longitude,
distance: haversine(gateway_location, rssi)
},
output: [
rssi.rssi
]
};
if(extended) {
item.ext = {
gateway: rssi.gateway_id,
rssi_raw: rssi.rssi
};
}
result.push(item);
}
for(let reading of this.repo_reading.iterate_unreceived()) {
let item = {
input: {
latitude: reading.latitude,
longitude: reading.longitude,
distance: haversine(gateway_location, reading)
},
output: [ -150 ]
};
if(extended) {
item.ext = {
gateway: "(none)",
rssi_raw: -150
};
}
result.push(item);
}
shuffle_fisher_yates(result);
if(this.settings.ai.do_zap_false_negatives) {
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).`);
}
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]);
}
return result;
}
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) {
// console.error(`Zap! (${distance})`);
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;
}
}
export default DatasetFetcher;