11import type { WindowConfig , StationCoverage } from '../types/coverage.js' ;
2+ import type { TransportType } from '../types/events.js' ;
23
3- /** Per-station sector data stored in one time slice */
4+ /** Per-station+transport sector data stored in one time slice */
45interface StationSliceData {
5- /** Max distance per 10-degree sector (36 sectors) */
66 sectorMaxDist : Float64Array ;
7- /** Message count per sector */
87 sectorMsgCount : Uint32Array ;
9- /** Sum of signal levels per sector (for averaging) */
108 sectorLevelSum : Float64Array ;
11- /** Total messages (with and without position) */
129 totalMessages : number ;
13- /** Messages that had target position */
1410 messagesWithPosition : number ;
15- /** Sum of signal levels for overall average */
1611 totalLevelSum : number ;
17- /** Count of messages with signal level data */
1812 levelCount : number ;
19- /** Count of messages with errors */
2013 errorCount : number ;
14+ transportType : TransportType ;
2115}
2216
23- function createEmptySliceData ( ) : StationSliceData {
17+ function createEmptySliceData ( transportType : TransportType ) : StationSliceData {
2418 return {
2519 sectorMaxDist : new Float64Array ( 36 ) ,
2620 sectorMsgCount : new Uint32Array ( 36 ) ,
@@ -30,15 +24,21 @@ function createEmptySliceData(): StationSliceData {
3024 totalLevelSum : 0 ,
3125 levelCount : 0 ,
3226 errorCount : 0 ,
27+ transportType,
3328 } ;
3429}
3530
36- type Slice = Map < number , StationSliceData > ; // stationId -> data
31+ /** Composite key: stationId + transportType */
32+ function sliceKey ( stationId : number , transportType : TransportType ) : string {
33+ return `${ stationId } :${ transportType } ` ;
34+ }
35+
36+ type Slice = Map < string , StationSliceData > ;
3737
3838/**
3939 * Sliding window for per-station bearing sector data.
40- * Each slice stores sector data for all stations active in that period.
41- * Aggregation across slices computes the window-scoped StationCoverage .
40+ * Data is keyed by stationId+transportType so coverage can be
41+ * filtered by transport type at query time .
4242 */
4343export class StationSlidingWindow {
4444 readonly config : WindowConfig ;
@@ -61,20 +61,22 @@ export class StationSlidingWindow {
6161 this . slices [ this . currentIndex ] . clear ( ) ;
6262 }
6363
64- /** Record a reception event for a station in the current slice */
64+ /** Record a reception event */
6565 record (
6666 stationId : number ,
6767 sector : number ,
6868 distance : number ,
6969 signalLevel : number | null ,
7070 hasPosition : boolean ,
7171 hasError : boolean ,
72+ transportType : TransportType = 'aircraft' ,
7273 ) : void {
74+ const key = sliceKey ( stationId , transportType ) ;
7375 const slice = this . slices [ this . currentIndex ] ;
74- let data = slice . get ( stationId ) ;
76+ let data = slice . get ( key ) ;
7577 if ( ! data ) {
76- data = createEmptySliceData ( ) ;
77- slice . set ( stationId , data ) ;
78+ data = createEmptySliceData ( transportType ) ;
79+ slice . set ( key , data ) ;
7880 }
7981
8082 data . totalMessages ++ ;
@@ -100,8 +102,11 @@ export class StationSlidingWindow {
100102 }
101103 }
102104
103- /** Aggregate all slices into per-station coverage for the full window */
104- getStationCoverage ( stationId : number ) : StationCoverage | null {
105+ /**
106+ * Aggregate all slices into per-station coverage.
107+ * @param transportFilter - if set, only include data from matching transport types
108+ */
109+ getStationCoverage ( stationId : number , transportFilter ?: TransportType ) : StationCoverage | null {
105110 const result : StationCoverage = {
106111 stationId,
107112 bearingSectors : new Float64Array ( 36 ) ,
@@ -122,32 +127,32 @@ export class StationSlidingWindow {
122127 const sectorLevelSums = new Float64Array ( 36 ) ;
123128
124129 for ( const slice of this . slices ) {
125- const data = slice . get ( stationId ) ;
126- if ( ! data ) continue ;
127-
128- result . totalMessages += data . totalMessages ;
129- result . messagesWithPosition += data . messagesWithPosition ;
130- totalLevelSum += data . totalLevelSum ;
131- levelCount += data . levelCount ;
132- errorCount += data . errorCount ;
133-
134- for ( let i = 0 ; i < 36 ; i ++ ) {
135- // Max distance across all slices per sector
136- if ( data . sectorMaxDist [ i ] > result . bearingSectors [ i ] ) {
137- result . bearingSectors [ i ] = data . sectorMaxDist [ i ] ;
138- }
139- result . sectorMessageCounts [ i ] += data . sectorMsgCount [ i ] ;
140- sectorLevelSums [ i ] += data . sectorLevelSum [ i ] ;
141-
142- if ( data . sectorMaxDist [ i ] > result . maxDistance ) {
143- result . maxDistance = data . sectorMaxDist [ i ] ;
130+ // Check all matching keys for this station
131+ for ( const [ key , data ] of slice ) {
132+ if ( ! key . startsWith ( `${ stationId } :` ) ) continue ;
133+ if ( transportFilter && data . transportType !== transportFilter ) continue ;
134+
135+ result . totalMessages += data . totalMessages ;
136+ result . messagesWithPosition += data . messagesWithPosition ;
137+ totalLevelSum += data . totalLevelSum ;
138+ levelCount += data . levelCount ;
139+ errorCount += data . errorCount ;
140+
141+ for ( let i = 0 ; i < 36 ; i ++ ) {
142+ if ( data . sectorMaxDist [ i ] > result . bearingSectors [ i ] ) {
143+ result . bearingSectors [ i ] = data . sectorMaxDist [ i ] ;
144+ }
145+ result . sectorMessageCounts [ i ] += data . sectorMsgCount [ i ] ;
146+ sectorLevelSums [ i ] += data . sectorLevelSum [ i ] ;
147+ if ( data . sectorMaxDist [ i ] > result . maxDistance ) {
148+ result . maxDistance = data . sectorMaxDist [ i ] ;
149+ }
144150 }
145151 }
146152 }
147153
148154 if ( result . totalMessages === 0 ) return null ;
149155
150- // Compute averages
151156 result . avgLevel = levelCount > 0 ? totalLevelSum / levelCount : 0 ;
152157 result . errorRate = result . totalMessages > 0 ? errorCount / result . totalMessages : 0 ;
153158
@@ -161,12 +166,14 @@ export class StationSlidingWindow {
161166 return result ;
162167 }
163168
164- /** Get all station IDs that have data in this window */
165- activeStationIds ( ) : Set < number > {
169+ /** Get all unique station IDs, optionally filtered by transport type */
170+ activeStationIds ( transportFilter ?: TransportType ) : Set < number > {
166171 const ids = new Set < number > ( ) ;
167172 for ( const slice of this . slices ) {
168- for ( const id of slice . keys ( ) ) {
169- ids . add ( id ) ;
173+ for ( const [ key , data ] of slice ) {
174+ if ( transportFilter && data . transportType !== transportFilter ) continue ;
175+ const stationId = parseInt ( key . split ( ':' ) [ 0 ] , 10 ) ;
176+ ids . add ( stationId ) ;
170177 }
171178 }
172179 return ids ;
0 commit comments