153 lines
4.0 KiB
TypeScript
153 lines
4.0 KiB
TypeScript
import { inject, injectable } from "tsyringe";
|
|
import { Op } from 'sequelize';
|
|
import { Location } from "../models/location";
|
|
import { LocationRepository } from "../repositories/locationRepository";
|
|
import { WifiLocationService } from "./wifiLocationService";
|
|
|
|
interface CreateLocationParams {
|
|
lp_ttn_end_device_uplinks_id: string;
|
|
wifi?: Coordinates;
|
|
gnss?: Coordinates;
|
|
ttn_gw?: Coordinates;
|
|
gnss_timestamp?: Date;
|
|
}
|
|
|
|
interface CreateLocationTriangulationParams {
|
|
lp_ttn_end_device_uplinks_id: string;
|
|
wifi: {
|
|
mac: string;
|
|
rssi: number;
|
|
}[];
|
|
ttn_gw: LocationSignal[];
|
|
gnss?: Coordinates;
|
|
gnss_timestamp?: Date;
|
|
}
|
|
|
|
interface LocationSignal extends Coordinates {
|
|
rssi: number;
|
|
}
|
|
|
|
interface Coordinates {
|
|
latitude: number;
|
|
longitude: number;
|
|
}
|
|
|
|
interface UpdateTtnGatewayReceptionParams {
|
|
ttn_gateway_reception_id: string;
|
|
gateway_id?: string;
|
|
eui?: string;
|
|
rssi?: number;
|
|
latitude?: number;
|
|
longitude?: number;
|
|
altitude?: number;
|
|
}
|
|
|
|
@injectable()
|
|
export class LocationService {
|
|
constructor(
|
|
@inject(LocationRepository)
|
|
private repository: LocationRepository,
|
|
@inject(WifiLocationService)
|
|
private wifiLocationService: WifiLocationService
|
|
) { }
|
|
|
|
public async getAllLocations() {
|
|
return this.repository.findAll();
|
|
}
|
|
|
|
public async getAllGnssLocations() {
|
|
return this.repository.findAll({
|
|
where: {
|
|
gnss_latitude: { [Op.ne]: null },
|
|
gnss_longitude: { [Op.ne]: null }
|
|
}
|
|
});
|
|
}
|
|
|
|
public async getLocationById(id: string) {
|
|
return this.repository.findById(id);
|
|
}
|
|
|
|
public async createLocation(data: CreateLocationParams) {
|
|
return this.repository.create({
|
|
lp_ttn_end_device_uplinks_id: data.lp_ttn_end_device_uplinks_id,
|
|
wifi_latitude: data.wifi?.latitude,
|
|
wifi_longitude: data.wifi?.longitude,
|
|
ttn_gw_latitude: data.ttn_gw?.latitude,
|
|
ttn_gw_longitude: data.ttn_gw?.longitude,
|
|
gnss_latitude: data.gnss?.latitude,
|
|
gnss_longitude: data.gnss?.longitude,
|
|
gnss_location_at_utc: data.gnss_timestamp,
|
|
});
|
|
}
|
|
|
|
public async createLocationFromTriangulation(
|
|
data: CreateLocationTriangulationParams
|
|
) {
|
|
const wifi_location = this.calculateVirtualLocation(
|
|
await this.enrichWifiObjectWithLocation(data.wifi)
|
|
);
|
|
const gateway_location = this.calculateVirtualLocation(data.ttn_gw);
|
|
|
|
return this.createLocation({
|
|
lp_ttn_end_device_uplinks_id: data.lp_ttn_end_device_uplinks_id,
|
|
wifi: wifi_location,
|
|
ttn_gw: gateway_location,
|
|
gnss: data.gnss,
|
|
gnss_timestamp: data.gnss_timestamp,
|
|
});
|
|
}
|
|
|
|
public async updateLocation(id: string, data: Partial<Location>) {
|
|
return this.repository.update(id, data);
|
|
}
|
|
|
|
public async deleteLocation(id: string) {
|
|
return this.repository.delete(id);
|
|
}
|
|
|
|
private async enrichWifiObjectWithLocation(
|
|
wifis: CreateLocationTriangulationParams["wifi"]
|
|
) {
|
|
const enrichedWifi = await Promise.all(
|
|
wifis.map(async (wifi) => {
|
|
const location = await this.wifiLocationService.getWifiLocationByMac(
|
|
wifi.mac
|
|
);
|
|
|
|
return location?.latitude !== undefined &&
|
|
location.longitude !== undefined
|
|
? {
|
|
rssi: wifi.rssi,
|
|
latitude: location.latitude,
|
|
longitude: location.longitude,
|
|
}
|
|
: null;
|
|
})
|
|
);
|
|
|
|
return enrichedWifi.filter((wifi) => wifi !== null);
|
|
}
|
|
|
|
private calculateVirtualLocation(locations: LocationSignal[]) {
|
|
if (locations.length === 0) return undefined;
|
|
|
|
const { totalWeight, weightedLatitude, weightedLongitude } =
|
|
locations.reduce(
|
|
(acc, { latitude, longitude, rssi }) => {
|
|
const weight = 1 / Math.abs(rssi);
|
|
acc.totalWeight += weight;
|
|
acc.weightedLatitude += latitude * weight;
|
|
acc.weightedLongitude += longitude * weight;
|
|
return acc;
|
|
},
|
|
{ totalWeight: 0, weightedLatitude: 0, weightedLongitude: 0 }
|
|
);
|
|
|
|
return {
|
|
latitude: weightedLatitude / totalWeight,
|
|
longitude: weightedLongitude / totalWeight,
|
|
};
|
|
}
|
|
}
|