Files
weather-station/server/src/ais.mjs
2026-07-03 18:54:40 -04:00

95 lines
2.8 KiB
JavaScript

import {cfg} from './config.mjs';
const CHANNEL_MAP = {0: 'A, B', 1: 'A', 2: 'B'};
const SENDER_TYPE = {
0: 'Other', 1: 'Unknown', 2: 'Cargo', 3: 'Class B',
4: 'Passenger', 5: 'Special', 6: 'Tanker', 7: 'High Speed',
8: 'Fishing', 9: 'Aircraft', 10: 'Helicopter', 11: 'Base Station',
12: 'AtoN', 13: 'SART/EPIRB'
};
let aisCache = null;
let aisCacheTs = 0;
const AIS_TTL = 1000;
function decodeMsgTypes(raw) {
const types = [];
for (let i = 0; i < 32; i++) {
if (raw & (1 << i)) types.push(i);
}
return types;
}
function decodeFlags(flags) {
return {
validated: flags & 3,
validated2: (flags & 3) == 2 ? -1 : flags & 3,
repeat: (flags >> 2) & 3,
virtual_aid: (flags >> 4) & 1,
approx: (flags >> 5) & 1,
channels: (flags >> 6) & 15,
cs_unit: (flags >> 10) & 3,
raim: (flags >> 12) & 3,
dte: (flags >> 14) & 3,
assigned: (flags >> 16) & 3,
display: (flags >> 18) & 3,
dsc: (flags >> 20) & 3,
band: (flags >> 22) & 3,
msg22: (flags >> 24) & 3,
off_position: (flags >> 26) & 3,
maneuver: (flags >> 28) & 3,
};
}
export async function getAIS() {
if (aisCache && Date.now() - aisCacheTs < AIS_TTL) return aisCache;
const {ADSB_URL} = cfg();
if (!ADSB_URL) return {data: []};
const r = await fetch(`${ADSB_URL}:9990/api/ships_array.json`);
const {dynamic, static: staticRows, time} = await r.json();
const staticMap = new Map(staticRows.map(row => [row[0], row]));
aisCache = dynamic.map(row => {
const [mmsi, lat, lon, distance, bearing, heading,
cog, speed, status, rrsi, ppm, count, msg_type,
last_signal, last_group, group_mask, flags, altitude,
received_stations, mmsi_type, shipclass, country] = row;
const s = staticMap.get(mmsi) ?? [];
const [_mmsi, shipname, callsign, destination, shiptype,
imo, to_bow, to_stern, to_port, to_starboard, draught,
eta_month, eta_day, eta_hour, eta_minute] = s;
const f = decodeFlags(flags);
return {
mmsi,
type: SENDER_TYPE[shipclass],
lat, lon, altitude,
heading, cog, speed,
distance, bearing,
destination,
rrsi, ppm, count,
msg_type: decodeMsgTypes(msg_type),
channels: CHANNEL_MAP[f.channels] ?? f.channels,
...f,
shiptype, imo, draught,
country, callsign,
shipname,
to_bow, to_stern, to_port, to_starboard,
eta_month, eta_day, eta_hour, eta_minute,
last_signal: last_signal ? time - last_signal : null,
last_group, group_mask,
received_stations, shipclass,
status
};
});
aisCacheTs = Date.now();
return aisCache;
}
export function getAISImage(mmsi) {
return fetch(`https://www.marinetraffic.com/getAssetDefaultPhoto/?photo_size=800&asset_type_id=0&asset_id=${mmsi}`,
{headers: {'User-Agent': 'Mozilla/5.0', 'Referer': 'https://www.marinetraffic.com/'}}).then(resp => resp.blob());
}