diff --git a/client/src/components/MapView.vue b/client/src/components/MapView.vue index a1710b6..74f9981 100644 --- a/client/src/components/MapView.vue +++ b/client/src/components/MapView.vue @@ -48,16 +48,6 @@ let airTraffic: AirTrafficLayer let aisLayer: AISLayer let range: RangeLayer -const hourTickIndices = computed(() => { - const indices: number[] = [] - let lastHour = -1 - radarFrames.value.forEach((f, i) => { - const h = new Date(f.time * 1000).getHours() - if (h !== lastHour) { indices.push(i); lastHour = h } - }) - return indices -}) - function buildWindSrc(lat: number, lon: number, zoom: number) { return `https://embed.windy.com/embed2.html?lat=${lat.toFixed(4)}&lon=${lon.toFixed(4)}&detailLat=${lat.toFixed(4)}&detailLon=${lon.toFixed(4)}&zoom=${Math.round(zoom)}&level=surface&overlay=wind&product=ecmwf&menu=&message=&marker=&calendar=now&pressure=&type=map&location=coordinates&detail=&metricWind=kt&metricTemp=%C2%B0C&radarRange=-1` } @@ -98,29 +88,15 @@ function seekRadar(index: number) { } } -function onSliderInput(e: Event) { - seekRadar(Number((e.target as HTMLInputElement).value)) -} - -function frameLabel(frame: { time: number }) { - return new Date(frame.time * 1000).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) -} - -function hourLabel(frame: { time: number }) { - return new Date(frame.time * 1000).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false }) -} - -function tickPercent(index: number) { - return (index / (radarFrames.value.length - 1)) * 100 -} - function toggleAT() { if(atActive.value) { aisLayer.hide() airTraffic.hide() + range.hide() } else { aisLayer.show() airTraffic.show() + range.show() } atActive.value = !atActive.value } diff --git a/server/src/ais.mjs b/server/src/ais.mjs index f49409a..2ffea56 100644 --- a/server/src/ais.mjs +++ b/server/src/ais.mjs @@ -1,63 +1,79 @@ -import {cfg} from './config.mjs'; - const CHANNEL_MAP = {0: 'A, B', 1: 'A', 2: 'B'}; const SENDER_TYPE = { - 0: 'Unknown', - 1: 'Base Station', - 2: 'Class A', - 3: 'Class B', - 4: 'SAR Aircraft', - 5: 'Aid to Navigation', - 6: 'Class B CS', - 7: 'Sart/Epirb/MOB' + 0: 'Unknown', 1: 'Base Station', 2: 'Class A', + 3: 'Class B', 4: 'SAR Aircraft', 5: 'Aid to Navigation', + 6: 'Class B CS', 7: 'Sart/Epirb/MOB' }; -function decodeMsgTypes(status_raw) { +function decodeMsgTypes(raw) { const types = []; for (let i = 0; i < 32; i++) { - if (status_raw & (1 << i)) types.push(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() { const {ADSB_URL} = cfg(); - if(!ADSB_URL) return {data: []}; + if (!ADSB_URL) return {data: []}; const r = await fetch(`${ADSB_URL}:9990/api/ships_array.json`); - const {dynamic, static: staticRows} = await r.json(); + const {dynamic, static: staticRows, time} = await r.json(); const staticMap = new Map(staticRows.map(row => [row[0], row])); - const boats = dynamic.map(row => { - const [ - mmsi, lat, lon, distance, bearing, - speed, heading, course, - unknownA, rssi, drift, count, - channelsMaybe, unknown, unknownB, unknownC, receiver, sender, - unknownD, altitude, unknownE, unknownF, unknownG, country, - // Known missing: sender_type, last_signal, in_range, - ] = row; - + return dynamic.map(row => { + const [mmsi, lat, lon, distance, bearing, heading, + cog, speed, status, level, 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, name, name2, name3, - unknown1, unknown2, unknown3, unknown4, unknown5, unknown6, unknown7, unknown8, unknown9, unknown10, unknown11 - ] = s; + 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[sender_type], + mmsi, + type: SENDER_TYPE[mmsi_type], lat, lon, altitude, - heading, drift, speed, - distance, bearing, course, - destination, eta, - rssi, count, receiver, sources, - msg_type: decodeMsgTypes(status_raw), - channels: CHANNEL_MAP[channels] ?? channels, - ship_type, imo, draught, - country, callsign, name: name || name2 || name3, - last_signal, in_range + heading, cog, speed, + distance, bearing, + destination, + level, 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 }; }); - - return boats; }