Write AI layer, but it's untested.

master
Starbeamrainbowlabs 4 years ago
parent f0694de02c
commit 61edaf6fa3
  1. 7
      client_src/js/ClientConfig.mjs
  2. 83
      client_src/js/LayerAI.mjs
  3. 0
      common/Math.mjs
  4. 23
      package-lock.json
  5. 2
      package.json
  6. 3
      rollup.config.js
  7. 8
      server/train-ai/AITrainer.mjs
  8. 2
      server/train-ai/DatasetFetcher.mjs

@ -12,5 +12,10 @@ export default {
border: 0.1,
// The resolution of the coverage map
step: 0.005
step: 0.005,
colour_scale: {
min: "#ffffff",
max: "#2cb42c"
}
};

@ -3,10 +3,12 @@
import path from 'path';
import L from 'leaflet';
import tf from '@tensorflow/tfjs';
import * as tf from '@tensorflow/tfjs';
import chroma from 'chroma-js';
import GetFromUrl from './Helpers/GetFromUrl.mjs';
import Config from './ClientConfig.mjs';
import { normalise } from '../../common/Math.mjs';
class LayerAI {
get gateway_bounds() {
@ -37,21 +39,28 @@ class LayerAI {
);
console.log(index);
for(let gateway of this.index) {
let gateway_data = {
// TODO: Swap this out for the real thing - probably a GeoJSON layer or something
// This is just a placeholder
layer: L.layerGroup([
]),
ai: await tf.loadModel(`${window.location.href}/${path.dirname(Config.ai_index_file)}/${gateway.id}`)
}
this.gateways.set(gateway.id, gateway_data);
for(let gateway of this.index.index) {
this.gateways.set(
gateway.id,
await tf.LayersModel.loadModel(`${window.location.href}/${path.dirname(Config.ai_index_file)}/${gateway.id}`)
);
}
this.layer = this.generate_layer();
this.layer.addTo(this.map);
}
generate_layer() {
return L.geoJSON(this.render_map(), {
style: (feature) => { return {
fillColor: feature.properties.colour,
fillOpacity: 0.4
} }
});
}
async render_map() {
render_map() {
// FUTURE: Do this in a web worker?
let map_bounds = this.gateway_bounds;
map_bounds.north += Config.border;
map_bounds.south -= Config.border;
@ -59,7 +68,55 @@ class LayerAI {
map_bounds.east += Config.border;
map_bounds.west -= Config.border;
let coverage = [],
colour_scale = chroma.scale(
Config.colour_scale.min,
Config.colour_scale.max
).domain(
this.index.properties.rssi_min,
this.index.properties.rssi_max
);
for(let lat = map_bounds.west; lat < map_bounds.east; lat += Config.step) {
for(let lng = map_bounds.south; lng < map_bounds.north; lng += Config.step) {
let max_predicted_rssi = -Infinity;
for(let [, ai] of this.gateways) {
max_predicted_rssi = Math.max(
max_predicted_rssi,
ai.predict(
tf.tensor1d([ lat, lng ])
)
);
}
max_predicted_rssi = normalise(max_predicted_rssi,
{ min: 0, max: 1 },
{
min: this.index.properties.rssi_min,
max: this.index.properties.rssi_max
}
);
coverage.push({
type: "Feature",
geometry: {
type: "Polygon",
coordinates: [
[lat, lng],
[lat + Config.step, lng],
[lat + Config.step, lng + Config.step],
[lat, lng + Config.step]
]
},
properties: {
colour: colour_scale(max_predicted_rssi)
}
})
}
}
return coverage;
}
}

23
package-lock.json generated

@ -93,6 +93,12 @@
"@types/integer": "*"
}
},
"@types/chroma-js": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@types/chroma-js/-/chroma-js-1.4.1.tgz",
"integrity": "sha512-i9hUiO3bwgmzZUDwBuR65WqsBQ/nwN+H2fKX0bykXCdd8cFQEuIj8vI7FXjyb2f5z5h+pv76I/uakikKSgaqTA==",
"dev": true
},
"@types/estree": {
"version": "0.0.39",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
@ -492,9 +498,9 @@
}
},
"caniuse-lite": {
"version": "1.0.30000984",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000984.tgz",
"integrity": "sha512-n5tKOjMaZ1fksIpQbjERuqCyfgec/m9pferkFQbLmWtqLUdmt12hNhjSwsmPdqeiG2NkITOQhr1VYIwWSAceiA==",
"version": "1.0.30000985",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000985.tgz",
"integrity": "sha512-1ngiwkgqAYPG0JSSUp3PUDGPKKY59EK7NrGGX+VOxaKCNzRbNc7uXMny+c3VJfZxtoK3wSImTvG9T9sXiTw2+w==",
"dev": true
},
"chalk": {
@ -512,6 +518,11 @@
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g=="
},
"chroma-js": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.0.4.tgz",
"integrity": "sha512-gk71qOrSdBTLbsd0DIUO3QjZL8tTvMwpG1EoXYScy7rI4rcO4EyYH6zGuvCgUDumKumqg0pt6Ua+vWnMJsTYhw=="
},
"class-utils": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
@ -1041,9 +1052,9 @@
}
},
"electron-to-chromium": {
"version": "1.3.194",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.194.tgz",
"integrity": "sha512-w0LHR2YD9Ex1o+Sz4IN2hYzCB8vaFtMNW+yJcBf6SZlVqgFahkne/4rGVJdk4fPF98Gch9snY7PiabOh+vqHNg==",
"version": "1.3.199",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.199.tgz",
"integrity": "sha512-gachlDdHSK47s0N2e58GH9HMC6Z4ip0SfmYUa5iEbE50AKaOUXysaJnXMfKj0xB245jWbYcyFSH+th3rqsF8hA==",
"dev": true
},
"emojis-list": {

@ -26,6 +26,7 @@
"async-mqtt": "^2.3.0",
"awilix": "^4.2.2",
"better-sqlite3": "^5.4.0",
"chroma-js": "^2.0.4",
"debug": "^4.1.1",
"leaflet": "^1.5.1",
"path": "^0.12.7",
@ -36,6 +37,7 @@
},
"devDependencies": {
"@types/better-sqlite3": "^5.4.0",
"@types/chroma-js": "^1.4.1",
"@types/leaflet": "^1.4.6",
"rollup": "^1.17.0",
"rollup-plugin-commonjs": "^10.0.1",

@ -34,6 +34,9 @@ let plugins = [
// not all files you want to resolve are .js files
extensions: ['.mjs', '.js', '.jsx', '.json'], // Default: [ '.mjs', '.js', '.json', '.node' ]
// Use the polyfills we've installed as dependencies instead of the Node.js natives, since those aren't available in the browser
preferBuiltins: false
}),
// json({

@ -65,7 +65,13 @@ class AITrainer {
this.settings.ai.output_directory,
"index.json"
),
JSON.stringify(index)
JSON.stringify({
properties: {
rssi_min: this.settings.ai.rssi_min,
rssi_max: this.settings.ai.rssi_max
},
index
})
);
}

@ -1,6 +1,6 @@
"use strict";
import { normalise, clamp } from '../Helpers/Math.mjs';
import { normalise, clamp } from '../../common/Math.mjs';
class DatasetFetcher {
constructor({ settings, RSSIRepo }) {

Loading…
Cancel
Save