@smartnet360/svelte-components 0.0.121 → 0.0.123
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apps/antenna-tools/band-config.d.ts +3 -2
- package/dist/apps/antenna-tools/band-config.js +6 -4
- package/dist/apps/antenna-tools/components/AntennaSettingsModal.svelte +5 -2
- package/dist/apps/antenna-tools/components/AntennaSettingsModal.svelte.d.ts +3 -0
- package/dist/apps/antenna-tools/components/AntennaTools.svelte +7 -3
- package/dist/apps/antenna-tools/components/AntennaTools.svelte.d.ts +5 -3
- package/dist/apps/antenna-tools/components/MSIConverter.svelte +56 -14
- package/dist/apps/antenna-tools/components/MSIConverter.svelte.d.ts +3 -0
- package/dist/apps/antenna-tools/types.d.ts +8 -3
- package/dist/apps/antenna-tools/types.js +14 -8
- package/dist/apps/antenna-tools/utils/db-utils.d.ts +3 -2
- package/dist/apps/antenna-tools/utils/db-utils.js +3 -2
- package/dist/apps/antenna-tools/utils/msi-parser.d.ts +15 -1
- package/dist/apps/antenna-tools/utils/msi-parser.js +16 -3
- package/dist/apps/index.d.ts +5 -1
- package/dist/apps/index.js +5 -2
- package/package.json +1 -1
|
@@ -23,15 +23,16 @@ export declare function isFrequencyInStandardBand(frequencyMHz: number): boolean
|
|
|
23
23
|
* Purge antennas - keep only one per band (closest to center frequency)
|
|
24
24
|
*
|
|
25
25
|
* For each antenna name + tilt combination:
|
|
26
|
-
* - For each
|
|
26
|
+
* - For each band, find antennas within the band's frequency range
|
|
27
27
|
* - Keep only the one closest to the center frequency
|
|
28
28
|
* - Update frequency to band name (700, 800, etc.)
|
|
29
29
|
* - Store original frequency in originalFrequency field
|
|
30
30
|
*
|
|
31
31
|
* @param rawAntennas - Antennas parsed from MSI files (with actual MHz frequencies)
|
|
32
|
+
* @param bands - Optional custom band definitions (defaults to STANDARD_BANDS)
|
|
32
33
|
* @returns Purged antennas with band names as frequencies
|
|
33
34
|
*/
|
|
34
|
-
export declare function purgeAntennas(rawAntennas: RawAntenna[]): Antenna[];
|
|
35
|
+
export declare function purgeAntennas(rawAntennas: RawAntenna[], bands?: BandDefinition[]): Antenna[];
|
|
35
36
|
/**
|
|
36
37
|
* Get purge statistics
|
|
37
38
|
*/
|
|
@@ -41,21 +41,23 @@ function groupByNameAndTilt(antennas) {
|
|
|
41
41
|
* Purge antennas - keep only one per band (closest to center frequency)
|
|
42
42
|
*
|
|
43
43
|
* For each antenna name + tilt combination:
|
|
44
|
-
* - For each
|
|
44
|
+
* - For each band, find antennas within the band's frequency range
|
|
45
45
|
* - Keep only the one closest to the center frequency
|
|
46
46
|
* - Update frequency to band name (700, 800, etc.)
|
|
47
47
|
* - Store original frequency in originalFrequency field
|
|
48
48
|
*
|
|
49
49
|
* @param rawAntennas - Antennas parsed from MSI files (with actual MHz frequencies)
|
|
50
|
+
* @param bands - Optional custom band definitions (defaults to STANDARD_BANDS)
|
|
50
51
|
* @returns Purged antennas with band names as frequencies
|
|
51
52
|
*/
|
|
52
|
-
export function purgeAntennas(rawAntennas) {
|
|
53
|
+
export function purgeAntennas(rawAntennas, bands) {
|
|
53
54
|
const purged = [];
|
|
55
|
+
const bandsToUse = bands ?? STANDARD_BANDS;
|
|
54
56
|
// Group by antenna name + tilt
|
|
55
57
|
const groups = groupByNameAndTilt(rawAntennas);
|
|
56
58
|
for (const [_key, group] of groups) {
|
|
57
|
-
// For each
|
|
58
|
-
for (const band of
|
|
59
|
+
// For each band
|
|
60
|
+
for (const band of bandsToUse) {
|
|
59
61
|
// Find antennas in this band's frequency range
|
|
60
62
|
const inBand = group.filter(a => a.frequency >= band.dlMin && a.frequency <= band.dlMax);
|
|
61
63
|
if (inBand.length === 0)
|
|
@@ -6,14 +6,17 @@
|
|
|
6
6
|
import DbNotification from './DbNotification.svelte';
|
|
7
7
|
import DatabaseViewer from './DatabaseViewer.svelte';
|
|
8
8
|
import { exportAntennas, clearAllAntennas } from '../utils/db-utils';
|
|
9
|
+
import type { BandDefinition } from '../types';
|
|
9
10
|
|
|
10
11
|
interface Props {
|
|
11
12
|
show: boolean;
|
|
12
13
|
onClose: () => void;
|
|
13
14
|
onDataRefresh?: () => void;
|
|
15
|
+
/** Custom band definitions for purge (defaults to STANDARD_BANDS) */
|
|
16
|
+
customBands?: BandDefinition[];
|
|
14
17
|
}
|
|
15
18
|
|
|
16
|
-
let { show, onClose, onDataRefresh }: Props = $props();
|
|
19
|
+
let { show, onClose, onDataRefresh, customBands }: Props = $props();
|
|
17
20
|
|
|
18
21
|
let activeTab = $state<'view' | 'import' | 'msi' | 'export' | 'manage'>('view');
|
|
19
22
|
let showClearConfirm = $state(false);
|
|
@@ -147,7 +150,7 @@
|
|
|
147
150
|
<div class="row">
|
|
148
151
|
<div class="col-12">
|
|
149
152
|
<h5 class="mb-3">Import MSI Pattern Files</h5>
|
|
150
|
-
<MSIConverter onDataRefresh={handleDataRefresh} />
|
|
153
|
+
<MSIConverter onDataRefresh={handleDataRefresh} {customBands} />
|
|
151
154
|
</div>
|
|
152
155
|
</div>
|
|
153
156
|
</div>
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import type { BandDefinition } from '../types';
|
|
1
2
|
interface Props {
|
|
2
3
|
show: boolean;
|
|
3
4
|
onClose: () => void;
|
|
4
5
|
onDataRefresh?: () => void;
|
|
6
|
+
/** Custom band definitions for purge (defaults to STANDARD_BANDS) */
|
|
7
|
+
customBands?: BandDefinition[];
|
|
5
8
|
}
|
|
6
9
|
declare const AntennaSettingsModal: import("svelte").Component<Props, {}, "">;
|
|
7
10
|
type AntennaSettingsModal = ReturnType<typeof AntennaSettingsModal>;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<script lang="ts">
|
|
4
4
|
import { onMount } from 'svelte';
|
|
5
5
|
import { loadAntennas } from '../utils/db-utils';
|
|
6
|
-
import type { Antenna, ExternalAntennaInput, ViewMode, PatternType, ChartEngineType, PatternDisplayMode } from '../types';
|
|
6
|
+
import type { Antenna, ExternalAntennaInput, ViewMode, PatternType, ChartEngineType, PatternDisplayMode, BandDefinition } from '../types';
|
|
7
7
|
import AntennaControls from './AntennaControls.svelte';
|
|
8
8
|
import AntennaSettingsModal from './AntennaSettingsModal.svelte';
|
|
9
9
|
import { PolarLineChart, PolarAreaChart } from './chart-engines/index';
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*
|
|
20
20
|
* 2. Standalone Mode (default): User selects antennas from database
|
|
21
21
|
* - Full UI with antenna selection dropdowns
|
|
22
|
-
* -
|
|
22
|
+
* - Database modal for data management
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
interface Props {
|
|
@@ -50,8 +50,10 @@
|
|
|
50
50
|
antenna1Label?: string;
|
|
51
51
|
/** Label for antenna 2 (e.g., cell name) */
|
|
52
52
|
antenna2Label?: string;
|
|
53
|
-
/** Hide the
|
|
53
|
+
/** Hide the database button */
|
|
54
54
|
hideSettings?: boolean;
|
|
55
|
+
/** Custom frequency band definitions for purge (defaults to STANDARD_BANDS with full 3GPP ranges) */
|
|
56
|
+
customBands?: BandDefinition[];
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
let {
|
|
@@ -70,6 +72,7 @@
|
|
|
70
72
|
antenna1Label = undefined,
|
|
71
73
|
antenna2Label = undefined,
|
|
72
74
|
hideSettings = false,
|
|
75
|
+
customBands = undefined,
|
|
73
76
|
}: Props = $props();
|
|
74
77
|
|
|
75
78
|
// === Derived: Operating Mode ===
|
|
@@ -579,6 +582,7 @@
|
|
|
579
582
|
show={showSettingsModal}
|
|
580
583
|
onClose={closeSettings}
|
|
581
584
|
onDataRefresh={handleDataRefresh}
|
|
585
|
+
{customBands}
|
|
582
586
|
/>
|
|
583
587
|
{/if}
|
|
584
588
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExternalAntennaInput, ViewMode } from '../types';
|
|
1
|
+
import type { ExternalAntennaInput, ViewMode, BandDefinition } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* AntennaTools - Main antenna pattern visualization component
|
|
4
4
|
*
|
|
@@ -9,7 +9,7 @@ import type { ExternalAntennaInput, ViewMode } from '../types';
|
|
|
9
9
|
*
|
|
10
10
|
* 2. Standalone Mode (default): User selects antennas from database
|
|
11
11
|
* - Full UI with antenna selection dropdowns
|
|
12
|
-
* -
|
|
12
|
+
* - Database modal for data management
|
|
13
13
|
*/
|
|
14
14
|
interface Props {
|
|
15
15
|
/** External antenna 1 specification (activates external mode) */
|
|
@@ -34,8 +34,10 @@ interface Props {
|
|
|
34
34
|
antenna1Label?: string;
|
|
35
35
|
/** Label for antenna 2 (e.g., cell name) */
|
|
36
36
|
antenna2Label?: string;
|
|
37
|
-
/** Hide the
|
|
37
|
+
/** Hide the database button */
|
|
38
38
|
hideSettings?: boolean;
|
|
39
|
+
/** Custom frequency band definitions for purge (defaults to STANDARD_BANDS with full 3GPP ranges) */
|
|
40
|
+
customBands?: BandDefinition[];
|
|
39
41
|
}
|
|
40
42
|
declare const AntennaTools: import("svelte").Component<Props, {}, "">;
|
|
41
43
|
type AntennaTools = ReturnType<typeof AntennaTools>;
|
|
@@ -1,22 +1,28 @@
|
|
|
1
1
|
<svelte:options runes={true} />
|
|
2
2
|
|
|
3
3
|
<script lang="ts">
|
|
4
|
-
import { parseFolder } from '../utils/msi-parser';
|
|
4
|
+
import { parseFolder, type ParseProgress } from '../utils/msi-parser';
|
|
5
5
|
import { saveAntennasWithPurge, type ImportResult } from '../utils/db-utils';
|
|
6
6
|
import { STANDARD_BANDS } from '../band-config';
|
|
7
|
-
import type { RawAntenna, Antenna } from '../types';
|
|
7
|
+
import type { RawAntenna, Antenna, BandDefinition } from '../types';
|
|
8
8
|
|
|
9
9
|
interface Props {
|
|
10
10
|
onDataRefresh?: () => void;
|
|
11
|
+
/** Custom band definitions for purge (defaults to STANDARD_BANDS) */
|
|
12
|
+
customBands?: BandDefinition[];
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
let { onDataRefresh }: Props = $props();
|
|
15
|
+
let { onDataRefresh, customBands }: Props = $props();
|
|
16
|
+
|
|
17
|
+
// Use custom bands if provided, otherwise use standard bands
|
|
18
|
+
let activeBands = $derived(customBands ?? STANDARD_BANDS);
|
|
14
19
|
|
|
15
20
|
let rawAntennas = $state<RawAntenna[]>([]);
|
|
16
21
|
let isLoading = $state(false);
|
|
17
22
|
let error = $state('');
|
|
18
23
|
let message = $state('');
|
|
19
24
|
let importResult = $state<ImportResult | null>(null);
|
|
25
|
+
let parseProgress = $state<ParseProgress | null>(null);
|
|
20
26
|
|
|
21
27
|
let recursiveScan = $state(true);
|
|
22
28
|
|
|
@@ -32,17 +38,21 @@
|
|
|
32
38
|
error = '';
|
|
33
39
|
message = 'Selecting folder...';
|
|
34
40
|
importResult = null;
|
|
41
|
+
parseProgress = null;
|
|
35
42
|
|
|
36
43
|
// Ask user to select a folder
|
|
37
44
|
const directoryPicker = window.showDirectoryPicker as () => Promise<FileSystemDirectoryHandle>;
|
|
38
45
|
const directoryHandle = await directoryPicker();
|
|
39
46
|
message = recursiveScan ?
|
|
40
|
-
'
|
|
41
|
-
'
|
|
47
|
+
'Scanning folders and parsing files...' :
|
|
48
|
+
'Parsing files (top-level folder only)...';
|
|
42
49
|
|
|
43
|
-
// Parse all MSI/PNT files in the folder, with recursive option
|
|
44
|
-
rawAntennas = await parseFolder(directoryHandle, recursiveScan)
|
|
50
|
+
// Parse all MSI/PNT files in the folder, with recursive option and progress
|
|
51
|
+
rawAntennas = await parseFolder(directoryHandle, recursiveScan, (progress) => {
|
|
52
|
+
parseProgress = progress;
|
|
53
|
+
});
|
|
45
54
|
|
|
55
|
+
parseProgress = null;
|
|
46
56
|
if (rawAntennas.length === 0) {
|
|
47
57
|
message = 'No MSI or PNT files found in the selected folder.';
|
|
48
58
|
} else {
|
|
@@ -75,8 +85,8 @@
|
|
|
75
85
|
isLoading = true;
|
|
76
86
|
message = 'Purging frequencies and saving to database...';
|
|
77
87
|
|
|
78
|
-
// Save with automatic purge
|
|
79
|
-
importResult = await saveAntennasWithPurge(rawAntennas);
|
|
88
|
+
// Save with automatic purge (using custom bands if provided)
|
|
89
|
+
importResult = await saveAntennasWithPurge(rawAntennas, customBands);
|
|
80
90
|
|
|
81
91
|
if (importResult.success) {
|
|
82
92
|
const stats = importResult.purgeStats;
|
|
@@ -122,16 +132,16 @@
|
|
|
122
132
|
<header class="d-flex flex-column flex-lg-row align-items-lg-center justify-content-between gap-3 mb-4">
|
|
123
133
|
<div>
|
|
124
134
|
<h1 class="h3 mb-2">Convert MSI / PNT to JSON</h1>
|
|
125
|
-
<p class="text-muted mb-0">Scan a folder of antenna definitions. Files will be purged to keep only
|
|
135
|
+
<p class="text-muted mb-0">Scan a folder of antenna definitions. Files will be purged to keep only configured bands.</p>
|
|
126
136
|
</div>
|
|
127
137
|
<span class="badge text-bg-success">Local processing</span>
|
|
128
138
|
</header>
|
|
129
139
|
|
|
130
|
-
<!--
|
|
140
|
+
<!-- Bands Info -->
|
|
131
141
|
<div class="alert alert-info border-0 bg-info-subtle mb-4">
|
|
132
|
-
<strong><i class="bi bi-info-circle me-2"></i>Standard Bands:</strong>
|
|
142
|
+
<strong><i class="bi bi-info-circle me-2"></i>{customBands ? 'Custom' : 'Standard'} Bands:</strong>
|
|
133
143
|
<div class="d-flex flex-wrap gap-2 mt-2">
|
|
134
|
-
{#each
|
|
144
|
+
{#each activeBands as band}
|
|
135
145
|
<span class="badge text-bg-primary">{band.name} MHz ({band.dlMin}-{band.dlMax})</span>
|
|
136
146
|
{/each}
|
|
137
147
|
</div>
|
|
@@ -183,7 +193,39 @@
|
|
|
183
193
|
</div>
|
|
184
194
|
{/if}
|
|
185
195
|
|
|
186
|
-
|
|
196
|
+
<!-- Progress indicator during parsing -->
|
|
197
|
+
{#if isLoading && parseProgress}
|
|
198
|
+
<div class="card border-info">
|
|
199
|
+
<div class="card-body">
|
|
200
|
+
<div class="d-flex align-items-center gap-3 mb-3">
|
|
201
|
+
<div class="spinner-border spinner-border-sm text-info" role="status">
|
|
202
|
+
<span class="visually-hidden">Loading...</span>
|
|
203
|
+
</div>
|
|
204
|
+
<span class="text-info fw-semibold">Parsing antenna files...</span>
|
|
205
|
+
</div>
|
|
206
|
+
<div class="row text-center mb-3">
|
|
207
|
+
<div class="col-4">
|
|
208
|
+
<div class="h4 mb-0 text-primary">{parseProgress.directoriesScanned}</div>
|
|
209
|
+
<small class="text-muted">Folders scanned</small>
|
|
210
|
+
</div>
|
|
211
|
+
<div class="col-4">
|
|
212
|
+
<div class="h4 mb-0 text-info">{parseProgress.filesProcessed}</div>
|
|
213
|
+
<small class="text-muted">Files processed</small>
|
|
214
|
+
</div>
|
|
215
|
+
<div class="col-4">
|
|
216
|
+
<div class="h4 mb-0 text-success">{parseProgress.antennaFilesFound}</div>
|
|
217
|
+
<small class="text-muted">Antennas found</small>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
<div class="text-muted small text-truncate">
|
|
221
|
+
<i class="bi bi-file-earmark me-1"></i>
|
|
222
|
+
{parseProgress.currentFile}
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
{/if}
|
|
227
|
+
|
|
228
|
+
{#if message && !error && !parseProgress}
|
|
187
229
|
<div class="alert alert-info border-0 bg-info-subtle text-info-emphasis" role="alert">
|
|
188
230
|
<i class="bi bi-info-circle me-2"></i>
|
|
189
231
|
{message}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import type { BandDefinition } from '../types';
|
|
1
2
|
interface Props {
|
|
2
3
|
onDataRefresh?: () => void;
|
|
4
|
+
/** Custom band definitions for purge (defaults to STANDARD_BANDS) */
|
|
5
|
+
customBands?: BandDefinition[];
|
|
3
6
|
}
|
|
4
7
|
declare const MsiConverter: import("svelte").Component<Props, {}, "">;
|
|
5
8
|
type MsiConverter = ReturnType<typeof MsiConverter>;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Frequency band definition for filtering antennas
|
|
7
7
|
*/
|
|
8
8
|
export interface BandDefinition {
|
|
9
|
-
/** Simple band name: "700", "800", "900", "1800", "2100", "2600" */
|
|
9
|
+
/** Simple band name: "700", "800", "900", "1800", "2100", "2600", "3500" */
|
|
10
10
|
name: string;
|
|
11
11
|
/** Downlink minimum frequency (MHz) */
|
|
12
12
|
dlMin: number;
|
|
@@ -14,8 +14,13 @@ export interface BandDefinition {
|
|
|
14
14
|
dlMax: number;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
* Standard frequency bands
|
|
18
|
-
*
|
|
17
|
+
* Standard frequency bands with FULL downlink ranges per 3GPP standards
|
|
18
|
+
* These are the complete DL frequency ranges, not narrow slices
|
|
19
|
+
*
|
|
20
|
+
* References:
|
|
21
|
+
* - 3GPP TS 36.101 (LTE)
|
|
22
|
+
* - 3GPP TS 38.101 (5G NR)
|
|
23
|
+
* - ETSI standards for GSM
|
|
19
24
|
*/
|
|
20
25
|
export declare const STANDARD_BANDS: BandDefinition[];
|
|
21
26
|
/**
|
|
@@ -3,14 +3,20 @@
|
|
|
3
3
|
* Clean, centralized type definitions for the antenna-tools module
|
|
4
4
|
*/
|
|
5
5
|
/**
|
|
6
|
-
* Standard frequency bands
|
|
7
|
-
*
|
|
6
|
+
* Standard frequency bands with FULL downlink ranges per 3GPP standards
|
|
7
|
+
* These are the complete DL frequency ranges, not narrow slices
|
|
8
|
+
*
|
|
9
|
+
* References:
|
|
10
|
+
* - 3GPP TS 36.101 (LTE)
|
|
11
|
+
* - 3GPP TS 38.101 (5G NR)
|
|
12
|
+
* - ETSI standards for GSM
|
|
8
13
|
*/
|
|
9
14
|
export const STANDARD_BANDS = [
|
|
10
|
-
{ name: '700', dlMin: 758, dlMax:
|
|
11
|
-
{ name: '800', dlMin: 791, dlMax:
|
|
12
|
-
{ name: '900', dlMin:
|
|
13
|
-
{ name: '1800', dlMin: 1805, dlMax: 1880 }, // GSM 1800 / LTE B3
|
|
14
|
-
{ name: '2100', dlMin: 2110, dlMax: 2170 }, // LTE B1
|
|
15
|
-
{ name: '2600', dlMin: 2620, dlMax: 2690 }, // LTE B7
|
|
15
|
+
{ name: '700', dlMin: 758, dlMax: 803 }, // LTE B28 (APT 700) - Full DL: 758-803 MHz
|
|
16
|
+
{ name: '800', dlMin: 791, dlMax: 862 }, // LTE B20 (EU 800) - Full DL: 791-862 MHz
|
|
17
|
+
{ name: '900', dlMin: 925, dlMax: 960 }, // GSM 900 / LTE B8 - Full DL: 925-960 MHz
|
|
18
|
+
{ name: '1800', dlMin: 1805, dlMax: 1880 }, // GSM 1800 / LTE B3 - Full DL: 1805-1880 MHz
|
|
19
|
+
{ name: '2100', dlMin: 2110, dlMax: 2170 }, // LTE B1 / UMTS - Full DL: 2110-2170 MHz
|
|
20
|
+
{ name: '2600', dlMin: 2620, dlMax: 2690 }, // LTE B7 - Full DL: 2620-2690 MHz
|
|
21
|
+
{ name: '3500', dlMin: 3400, dlMax: 3800 }, // 5G NR n78 (C-Band) - Full: 3400-3800 MHz
|
|
16
22
|
];
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Antenna Tools - Database Utilities
|
|
3
3
|
* CRUD operations for antenna data with automatic purge on import
|
|
4
4
|
*/
|
|
5
|
-
import type { Antenna, RawAntenna } from '../types';
|
|
5
|
+
import type { Antenna, RawAntenna, BandDefinition } from '../types';
|
|
6
6
|
import { type PurgeStats } from '../band-config';
|
|
7
7
|
/**
|
|
8
8
|
* Load all antennas from database
|
|
@@ -25,9 +25,10 @@ export interface ImportResult {
|
|
|
25
25
|
* 3. Saves purged antennas to database
|
|
26
26
|
*
|
|
27
27
|
* @param rawAntennas - Antennas parsed from MSI files
|
|
28
|
+
* @param customBands - Optional custom band definitions (defaults to STANDARD_BANDS)
|
|
28
29
|
* @returns Import result with purge statistics
|
|
29
30
|
*/
|
|
30
|
-
export declare function saveAntennasWithPurge(rawAntennas: RawAntenna[]): Promise<ImportResult>;
|
|
31
|
+
export declare function saveAntennasWithPurge(rawAntennas: RawAntenna[], customBands?: BandDefinition[]): Promise<ImportResult>;
|
|
31
32
|
/**
|
|
32
33
|
* Save antennas directly (without purge) - for pre-purged data
|
|
33
34
|
*/
|
|
@@ -52,13 +52,14 @@ export async function loadAntennas() {
|
|
|
52
52
|
* 3. Saves purged antennas to database
|
|
53
53
|
*
|
|
54
54
|
* @param rawAntennas - Antennas parsed from MSI files
|
|
55
|
+
* @param customBands - Optional custom band definitions (defaults to STANDARD_BANDS)
|
|
55
56
|
* @returns Import result with purge statistics
|
|
56
57
|
*/
|
|
57
|
-
export async function saveAntennasWithPurge(rawAntennas) {
|
|
58
|
+
export async function saveAntennasWithPurge(rawAntennas, customBands) {
|
|
58
59
|
try {
|
|
59
60
|
trackDataOperation('import', { inProgress: true, message: 'Purging frequencies...' });
|
|
60
61
|
// Step 1: Purge - keep only one antenna per band
|
|
61
|
-
const purgedAntennas = purgeAntennas(rawAntennas);
|
|
62
|
+
const purgedAntennas = purgeAntennas(rawAntennas, customBands);
|
|
62
63
|
const purgeStats = calculatePurgeStats(rawAntennas.length, purgedAntennas);
|
|
63
64
|
console.log(`[saveAntennasWithPurge] Purge complete: ${purgeStats.totalBefore} → ${purgeStats.totalAfter} antennas (${purgeStats.reductionPercent}% reduction)`);
|
|
64
65
|
trackDataOperation('import', { inProgress: true, message: `Saving ${purgedAntennas.length} antennas...` });
|
|
@@ -12,10 +12,24 @@ import type { RawAntenna } from '../types';
|
|
|
12
12
|
* @returns Parsed antenna with actual MHz frequency
|
|
13
13
|
*/
|
|
14
14
|
export declare function parseMSIFile(file: File): Promise<RawAntenna>;
|
|
15
|
+
/**
|
|
16
|
+
* Progress information during folder parsing
|
|
17
|
+
*/
|
|
18
|
+
export interface ParseProgress {
|
|
19
|
+
/** Current file being processed */
|
|
20
|
+
currentFile: string;
|
|
21
|
+
/** Number of files processed so far */
|
|
22
|
+
filesProcessed: number;
|
|
23
|
+
/** Number of directories scanned */
|
|
24
|
+
directoriesScanned: number;
|
|
25
|
+
/** Number of MSI/PNT files found */
|
|
26
|
+
antennaFilesFound: number;
|
|
27
|
+
}
|
|
15
28
|
/**
|
|
16
29
|
* Parse a folder of MSI files recursively
|
|
17
30
|
* @param directoryHandle - File system directory handle
|
|
18
31
|
* @param recursive - Whether to process subdirectories
|
|
32
|
+
* @param onProgress - Optional callback for progress updates
|
|
19
33
|
* @returns Array of parsed antennas
|
|
20
34
|
*/
|
|
21
|
-
export declare function parseFolder(directoryHandle: FileSystemDirectoryHandle, recursive?: boolean): Promise<RawAntenna[]>;
|
|
35
|
+
export declare function parseFolder(directoryHandle: FileSystemDirectoryHandle, recursive?: boolean, onProgress?: (progress: ParseProgress) => void): Promise<RawAntenna[]>;
|
|
@@ -173,12 +173,16 @@ export async function parseMSIFile(file) {
|
|
|
173
173
|
* Parse a folder of MSI files recursively
|
|
174
174
|
* @param directoryHandle - File system directory handle
|
|
175
175
|
* @param recursive - Whether to process subdirectories
|
|
176
|
+
* @param onProgress - Optional callback for progress updates
|
|
176
177
|
* @returns Array of parsed antennas
|
|
177
178
|
*/
|
|
178
|
-
export async function parseFolder(directoryHandle, recursive = true) {
|
|
179
|
+
export async function parseFolder(directoryHandle, recursive = true, onProgress) {
|
|
179
180
|
const antennas = [];
|
|
181
|
+
let filesProcessed = 0;
|
|
182
|
+
let directoriesScanned = 0;
|
|
180
183
|
// Process directory recursively
|
|
181
184
|
async function processDirectory(dirHandle, path = '') {
|
|
185
|
+
directoriesScanned++;
|
|
182
186
|
const iterator = dirHandle.values();
|
|
183
187
|
for await (const entry of iterator) {
|
|
184
188
|
if (entry.kind === 'file') {
|
|
@@ -188,14 +192,23 @@ export async function parseFolder(directoryHandle, recursive = true) {
|
|
|
188
192
|
const file = await fileHandle.getFile();
|
|
189
193
|
const ext = file.name.split('.').pop()?.toLowerCase();
|
|
190
194
|
if (ext === 'msi' || ext === 'pnt') {
|
|
195
|
+
filesProcessed++;
|
|
196
|
+
const currentPath = path ? `${path}/${file.name}` : file.name;
|
|
197
|
+
// Report progress
|
|
198
|
+
onProgress?.({
|
|
199
|
+
currentFile: currentPath,
|
|
200
|
+
filesProcessed,
|
|
201
|
+
directoriesScanned,
|
|
202
|
+
antennaFilesFound: antennas.length
|
|
203
|
+
});
|
|
191
204
|
try {
|
|
192
205
|
const antenna = await parseMSIFile(file);
|
|
193
206
|
// Add the path info to help identify where the file was found
|
|
194
|
-
antenna.sourcePath =
|
|
207
|
+
antenna.sourcePath = currentPath;
|
|
195
208
|
antennas.push(antenna);
|
|
196
209
|
}
|
|
197
210
|
catch (error) {
|
|
198
|
-
console.error(`Error parsing file ${
|
|
211
|
+
console.error(`Error parsing file ${currentPath}:`, error);
|
|
199
212
|
}
|
|
200
213
|
}
|
|
201
214
|
}
|
package/dist/apps/index.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
export * from './antenna-pattern/index.js';
|
|
2
|
-
export
|
|
2
|
+
export { default as AntennaTools } from './antenna-tools/components/AntennaTools.svelte';
|
|
3
|
+
export { default as AntennaDatabaseModal } from './antenna-tools/components/AntennaSettingsModal.svelte';
|
|
4
|
+
export { default as AntennaDatabaseViewer } from './antenna-tools/components/DatabaseViewer.svelte';
|
|
5
|
+
export type { BandDefinition, Antenna, RawAntenna, ExternalAntennaInput } from './antenna-tools/types.js';
|
|
6
|
+
export { STANDARD_BANDS } from './antenna-tools/types.js';
|
|
3
7
|
export * from './site-check/index.js';
|
package/dist/apps/index.js
CHANGED
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
// Antenna Pattern Analysis App (legacy)
|
|
4
4
|
export * from './antenna-pattern/index.js';
|
|
5
5
|
// Antenna Tools (new version with purge & database viewer)
|
|
6
|
-
//
|
|
7
|
-
export
|
|
6
|
+
// Export only the main component directly to avoid naming conflicts
|
|
7
|
+
export { default as AntennaTools } from './antenna-tools/components/AntennaTools.svelte';
|
|
8
|
+
export { default as AntennaDatabaseModal } from './antenna-tools/components/AntennaSettingsModal.svelte';
|
|
9
|
+
export { default as AntennaDatabaseViewer } from './antenna-tools/components/DatabaseViewer.svelte';
|
|
10
|
+
export { STANDARD_BANDS } from './antenna-tools/types.js';
|
|
8
11
|
// Test app (if needed for demos)
|
|
9
12
|
// export * from './test/index.js';
|
|
10
13
|
export * from './site-check/index.js';
|