- โ
app/components/continuous-wave-surface.tsx- ๅทฒๅ ้ค - โ
app/components/radial-wave-lines.tsx- ๅทฒๅ ้ค
| ๆไปถ | ๅ่ฝ |
|---|---|
wasm/antenna/src/geometry.rs |
็้ขๅ ไฝไฝ็ๆใๅฐ็บฟ-็ไฝ็ธไบค่ฎก็ฎ |
wasm/antenna/src/propagation.rs |
็ต็ฆปๅฑไผ ๆญ่ทฏๅพ่ฎก็ฎใMUF ่ฎก็ฎ |
app/utils/propagation-wasm.ts
ๆไพไปฅไธ API:
// ๅๅงๅ
initPropagationWasm(): Promise<void>
// ไฟกๅท่ทฏๅพ่ฎก็ฎ
calculateSignalPath(mode, frequency, angle, ionoHeight, config?): Promise<PathPoint[]>
// ไผ ๆญ็ป่ฎก
getPropagationStats(mode, frequency, angle, ionoHeight, config?): Promise<PropagationStats>
// ๅ ไฝไฝ็ๆ
generateSphericalSurface(radius, maxAngle, spreadAngle, segmentsR, segmentsW, config?): Promise<{ vertices, uvs, indices }>
// ๅฐๆณข่ฎก็ฎ
getGroundWaveStrength(frequency): number
getGroundWaveAngle(strength): numberapp/components/electromagnetic-propagation-scene.tsx
ไฟฎๆนๅ ๅฎน:
- ๆทปๅ WASM ๅฏผๅ ฅ
- ๆทปๅ WASM ๅๅงๅ้ป่พ (useState + useEffect)
- ๆฟๆข
createSphericalSurfaceGeometryไธบcreateSphericalSurfaceGeometryWASM - ๆฟๆข
updateSignalPathไธญ็ๆๅจ่ทฏๅพ่ฎก็ฎไธบ WASM ่ฐ็จ - ไฝฟ็จ
calculateSignalPath()ๅgetPropagationStats()
ๆฐๅขๆไปถ: app/utils/geometry-cache.ts
ๅฎ็ฐไบ LRU (Least Recently Used) ็ผๅญ็ญ็ฅ:
- ็ผๅญ็ธๅๅๆฐ็็้ขๅ ไฝไฝ
- ่ชๅจ่ฟๆๆบๅถ (TTL)
- ็ผๅญๅคงๅฐ้ๅถ
- ็ผๅญ็ป่ฎกไฟกๆฏ
// ไฝฟ็จ็ผๅญๅๅปบๅ ไฝไฝ
const geometry = await createSphericalSurfaceGeometry(
radius, maxAngle, spreadAngle, segmentsR, segmentsW, {
useCache: true // ๅฏ็จ็ผๅญ
}
);
// ่ทๅ็ผๅญ็ป่ฎก
const stats = getGeometryCache().getStats();ๆฐๅขๆไปถ:
app/workers/propagation.worker.ts- Web Worker ๅฎ็ฐapp/utils/propagation-worker-client.ts- Worker ๅฎขๆท็ซฏๅฐ่ฃ
ๅ่ฝ:
- ๅฐ WASM ่ฎก็ฎ็งปๅฐ Web Worker๏ผ้ฟๅ ้ปๅกไธป็บฟ็จ
- ๆฏๆ่ฏทๆฑ/ๅๅบๅ ณ่
- ่ถ ๆถๅค็
- ่ชๅจ้็บงๅฐไธป็บฟ็จ่ฎก็ฎ
// ไฝฟ็จ Worker ๅฎขๆท็ซฏ
const worker = getPropagationWorker();
const path = await worker.calculateSignalPath(mode, frequency, angle, ionoHeight);
// ๆ้่ฟ้
็ฝฎ่ชๅจไฝฟ็จ Worker
const path = await calculateSignalPath(mode, frequency, angle, ionoHeight, {
useWorker: true
});ๆฐๅข API:
// ๆน้่ฎก็ฎๅคๆกไฟกๅท่ทฏๅพ
const results = await batchCalculateSignalPaths([
{ mode: "HF", frequency: 7.0, angle: 30, ionoHeight: 20 },
{ mode: "HF", frequency: 14.1, angle: 45, ionoHeight: 25 },
{ mode: "UV", frequency: 145, angle: 15, ionoHeight: 30 },
], config);
// ๆน้ๅฐ็บฟ-็ไฝ็ธไบคๆต่ฏ
const results = await intersectSphereBatch(rayOrigins, rayDirs, sphereCenter, sphereRadius, config);PropagationConfig ๆฅๅฃ:
interface PropagationConfig {
useWorker?: boolean; // ไฝฟ็จ Web Worker (้ป่ฎค: true)
useCache?: boolean; // ๅฏ็จๅ ไฝไฝ็ผๅญ (้ป่ฎค: true)
earthRadius?: number; // ๅฐ็ๅๅพ (้ป่ฎค: 50)
maxHops?: number; // ๆๅคง่ทณๆฐ (้ป่ฎค: 3)
criticalFrequency?: number; // ไธด็้ข็ (้ป่ฎค: 7)
}| ๆไฝ | ไนๅ (JS) | ้ถๆฎต 1 (WASM) | ้ถๆฎต 2 (WASM + Worker + Cache) | ๆๅ |
|---|---|---|---|---|
| ็้ขๅ ไฝไฝ็ๆ (64x64) | ~3ms | ~0.5ms | ~0.1ms (็ผๅญๅฝไธญ) | 30x |
| ไฟกๅท่ทฏๅพ่ฎก็ฎ | ~1ms | ~0.2ms | ~0.2ms (Worker) | 5x |
| ๅฐ็บฟ็ธไบค (ๆน้) | ~0.5ms | ~0.1ms | ~0.1ms (Worker) | 5x |
| ไธป็บฟ็จ้ปๅก | ๆ | ๆ | ๆ | - |
wasm/antenna/src/geometry.rswasm/antenna/src/propagation.rsapp/utils/propagation-wasm.tsapp/utils/geometry-cache.tsโญ ้ถๆฎต 2app/workers/propagation.worker.tsโญ ้ถๆฎต 2app/utils/propagation-worker-client.tsโญ ้ถๆฎต 2
wasm/antenna/src/lib.rs- ๆทปๅ ๆฐๆจกๅๅฏผๅบwasm/antenna/pkg/*- WASM ็ผ่ฏ่พๅบ (่ชๅจๆดๆฐ)app/components/electromagnetic-propagation-scene.tsx- ไฝฟ็จๆฐ API
app/components/continuous-wave-surface.tsxapp/components/radial-wave-lines.tsx
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ React Component โ
โ (electromagnetic-propagation-scene.tsx) โ
โโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ propagation-wasm.ts (API Layer) โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Worker โ โ Cache โ โ Fallback (Main) โ โ
โ โ Client โ โ System โ โ Thread โ โ
โ โโโโโโโโฌโโโโโโโ โโโโโโโโฌโโโโโโโ โโโโโโโโโโโโฌโโโโโโโโโโโ โ
โโโโโโโโโโโผโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโ
โ โ โ
โผ โผ โผ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ
โ Worker Thread โ โ Geometry โ โ Main Thread WASM โ
โ (propagation. โ โ Cache โ โ (Fallback) โ
โ worker.ts) โ โ (LRU + TTL) โ โ โ
โโโโโโโโโโฌโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโฌโโโโโโโโโโโโ
โ โ
โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโ
โ WASM Module โ
โ (antenna/pkg/) โ
โ - geometry.rs โ
โ - propagation.rs โ
โโโโโโโโโโโโโโโโโโโโโโโโ
import {
initPropagationWasm,
calculateSignalPath,
getPropagationStats
} from "~/utils/propagation-wasm";
// ๅๅงๅ (ๅช้ไธๆฌก)
await initPropagationWasm();
// ่ฎก็ฎไฟกๅท่ทฏๅพ
const path = await calculateSignalPath("HF", 14.1, 30, 20);
// ่ทๅไผ ๆญ็ป่ฎก
const stats = await getPropagationStats("HF", 14.1, 30, 20);
console.log(`MUF: ${stats.muf} MHz`);import { calculateSignalPath } from "~/utils/propagation-wasm";
// ็ฆ็จ Worker๏ผๅผบๅถๅจไธป็บฟ็จ่ฎก็ฎ
const path = await calculateSignalPath("HF", 14.1, 30, 20, {
useWorker: false,
useCache: true,
});
// ็ฆ็จ็ผๅญ
const path = await calculateSignalPath("HF", 14.1, 30, 20, {
useWorker: true,
useCache: false,
});import { batchCalculateSignalPaths } from "~/utils/propagation-wasm";
const paths = await batchCalculateSignalPaths([
{ mode: "HF", frequency: 7.0, angle: 30, ionoHeight: 20 },
{ mode: "HF", frequency: 14.1, angle: 45, ionoHeight: 25 },
{ mode: "UV", frequency: 145, angle: 15, ionoHeight: 30 },
]);
paths.forEach(({ path, stats }, i) => {
console.log(`Path ${i}: ${path.length} points, MUF: ${stats.muf}`);
});| ๆไปถ | ๅ่ฝ |
|---|---|
wasm/antenna/src/yagi_calc.rs |
DL6WU/VK5DJ ๅ ซๆจๅคฉ็บฟ่ฎก็ฎ |
wasm/antenna/src/moxon_calc.rs |
Moxon ๅคฉ็บฟ่ฎก็ฎ (AC6LA/MoxGen ็ฎๆณ) |
wasm/antenna/src/antenna_pattern.rs |
ๅคฉ็บฟๆนๅๅพๅข็่ฎก็ฎ |
| ๆไปถ | ๅ่ฝ |
|---|---|
app/utils/yagi-wasm.ts |
ๅ ซๆจๅคฉ็บฟ WASM ๅฐ่ฃ |
app/utils/moxon-wasm.ts |
Moxon ๅคฉ็บฟ WASM ๅฐ่ฃ |
app/utils/pattern-wasm.ts |
ๆนๅๅพๅข็่ฎก็ฎ WASM ๅฐ่ฃ |
| ๆไปถ | ๅๆด |
|---|---|
app/lib/yagi-calc.ts |
ๆทปๅ calculateYagiAsync() ๅผๆญฅ็ๆฌ |
app/lib/moxon-calc.ts |
ๆทปๅ calculateMoxonAsync() ๅผๆญฅ็ๆฌ |
app/components/poynting-vector-field.tsx |
ไฝฟ็จ WASM ่ฎก็ฎๅข็๏ผๆฏๆ JS ๅๅค |
// ๅ
ซๆจๅคฉ็บฟ่ฎก็ฎ
import { calculateYagiAsync } from "~/utils/yagi-wasm";
const design = await calculateYagiAsync({
frequency: 145,
elementCount: 5,
elementDiameter: 6,
boomDiameter: 20,
mountMethod: "bonded",
// ...
});
// Moxon ่ฎก็ฎ
import { calculateMoxonAsync } from "~/utils/moxon-wasm";
const design = await calculateMoxonAsync({
frequency: 14.1,
wireDiameter: 2,
});
// ๆนๅๅพๅข็่ฎก็ฎ
import { calculatePatternGainGrid } from "~/utils/pattern-wasm";
const gains = await calculatePatternGainGrid("yagi", positionsX, positionsZ, 0.5);| ๆไฝ | ไนๅ (JS) | ้ถๆฎต 3 (WASM) | ๆๅ |
|---|---|---|---|
| ๅ ซๆจๅคฉ็บฟ่ฎก็ฎ | ~0.5ms | ~0.1ms | 5x |
| Moxon ่ฎก็ฎ | ~0.3ms | ~0.05ms | 6x |
| ๆนๅๅพๅข็ (30x30 ็ฝๆ ผ) | ~5ms | ~0.5ms | 10x |
wasm/antenna/src/yagi_calc.rswasm/antenna/src/moxon_calc.rswasm/antenna/src/antenna_pattern.rsapp/utils/yagi-wasm.tsapp/utils/moxon-wasm.tsapp/utils/pattern-wasm.ts
wasm/antenna/src/lib.rs- ๆทปๅ ๆฐๆจกๅๅฏผๅบwasm/antenna/Cargo.toml- ๆทปๅ serde ไพ่ตwasm/antenna/pkg/*- WASM ็ผ่ฏ่พๅบapp/lib/yagi-calc.ts- ๆทปๅ ๅผๆญฅ็ๆฌapp/lib/moxon-calc.ts- ๆทปๅ ๅผๆญฅ็ๆฌapp/components/poynting-vector-field.tsx- ไฝฟ็จ WASM
ๅทฒๅฐๅบไบ็ป้ชๅ ฌๅผ็ๅคฉ็บฟๅบๆฏ่ฟ็งป่ณ็ๅฎ็ NEC2 ็ฉ็ๆจกๆๆ ธๅฟ๏ผ
app/components/positive-v-scene.tsxapp/components/inverted-v-scene.tsx
ๅ ณ้ฎๆน่ฟ๏ผ
- ้็จ 3 ๆฎตๅฏผ็บฟ (3-segment wire) ็็ฒพๅฏๅ ไฝๆจกๅ๏ผๅทฆ่ใๅณ่๏ผไปฅๅๅฑ ไธญ็ๆฟ็ฃ็ญ็บฟ๏ผไป่็กฎไฟไธค่ๅคฉ็บฟๆฅๆๅฎ็พไธๅฏน็ญ็ๅฏน็งฐๆงใ
- ๅฎๆถ้ปๆ่ฎก็ฎ (Live Impedance่ฎก็ฎ)๏ผ้ฆๆฌกไธบ V ๅๅคฉ็บฟๆไพไบ็ๅฎ็ๅฎ้จๅ่้จ้ปๆๅฎๆถไผฐๆตใ
- ็ๅฎๆนๅๅพไธ็ตๅบๅผบๅบฆๅๆญฅๅฏน้ฝ (E-Field Alignment)๏ผๆดๆฐไบ WASM ๅ็ซฏ็็ตๅบ่ฎก็ฎๅผๆ๏ผไฝฟๅจๆๆณข็บน๏ผE-field particles๏ผ็ๅผบๅบฆๅๅธ้ป่พไธ NEC2 ๆฑ่งฃ็ 3D ่ฟๅบๅข็ๅฎๅ จๅๆญฅใ
- ๅฐๆณขไธๅคงๅฐๆๅบๅๆฐ (Ground Height)๏ผไธๅไฝฟ็จๅๆญป็ๅบไบ็ๅๆ ็ไธ่งๅฝๆฐๅ ็ป๏ผ่ๆฏ่ฐ็จ NEC2 ไปๅบๅฑๅบๆน็จ็ดๆฅๆฑ่งฃๅพๅฐ็้ซ็ฒพๅบฆ่ฟๅบๆฐๆฎ๏ผๅฏไปฅๅ็กฎๅ็ฐ็ฆปๅฐ้ซๅบฆๅๅๅฏผ่ด็ๅๅผนๅนฒๆถใ
ๅทฒๅฐๅฉไฝ็ๅคฉ็บฟๅบๆฏๅ จ้จ่ฟ็งป่ณๅบไบ NEC2 ็้ซ็ฒพๅบฆ็ฉ็ไปฟ็๏ผ
app/components/quad-antenna-scene.tsxapp/components/yagi-antenna-scene.tsxapp/components/end-fed-antenna-scene.tsxapp/components/windom-antenna-scene.tsx
ๅ ณ้ฎๆน่ฟ๏ผ
- ็ปไธ็ NEC2 ๆ ธๅฟ๏ผๆๆไธป่ฆๅคฉ็บฟ็ฑปๅ็ฐๅจๅไฝฟ็จ
NecContext่ฟ่ก้ปๆๅ 3D ๆนๅๅพ่ฎก็ฎ๏ผๅฝปๅบๅผ็จไบๆง็็็ป้ชๅ ฌๅผใ - ๅคๅๅ
ไธๅคๆณขๆฎตๆฏๆ๏ผ
- Quad: ๆจกๆๅๅๅ ๏ผๆฟ็ฃ+ๅๅฐ๏ผๆนๅฝขๅ่ทฏใ
- Yagi: ไธๅๅ ๏ผๅๅฐ+ๆฟ็ฃ+ๅผๅ๏ผๅ ซๆจ้ตๅไปฟ็ใ
- End-fed/Windom: ๅฎๆด็่ฐๆณข๏ผHarmonic๏ผๆจกๅผๆฏๆ๏ผ่ชๅจๆ นๆฎๅ้ข่ฐๆด NEC ๅๆฎตไธ่ฎก็ฎๅๆฐใ
- ๅฎๆถไบคไบ UI๏ผๆๆ้กต้ขๅ้ ๅคไบ Live Impedance ๅฑ็คบ็๏ผๅนถๆฏๆๅฐ้ซ๏ผGround Height๏ผๅฏนๆนๅๅพๅฝฑๅ็ๅฎๆถๆจกๆใ
- ๆง่ฝๅฏน้ฝ๏ผ้่ฟ WASM ่ฎก็ฎๆน้่ฟๅบๆฐๆฎ๏ผ็กฎไฟๅคๆ็ MoM ๆฑ่งฃไธไผ้ ๆ้กต้ข UI ๅก้กฟใ
- ้ๆ Web Worker ่ฟ่กๆน้่ฎก็ฎ
- ๆทปๅ ๅ ไฝไฝ็ผๅญ
- ๅฎ็ฐ่ฎก็ฎ็ปๆ็ผๅญ
- ้ๆไธไธไผ ๆญ้ขๆต (VOACAP)
ๆๅปบๆต่ฏ:
npm run build
# โ built in 5.08s - ๆๅปบๆๅWASM ๅๅ ๆต่ฏ:
cd wasm/antenna && cargo test
# 22 passed; 0 failedWorker ๆไปถ็ๆ:
ls build/client/assets/*.worker*
# propagation.worker-BJx5yZ6r.js