@classytic/stage 0.3.0 → 0.4.0
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/_virtual/_rolldown/runtime.mjs +18 -0
- package/dist/atomic/aufbau.d.mts +144 -0
- package/dist/atomic/aufbau.mjs +384 -0
- package/dist/atomic/elements.d.mts +67 -0
- package/dist/atomic/elements.mjs +785 -0
- package/dist/atomic/emission.d.mts +112 -0
- package/dist/atomic/emission.mjs +179 -0
- package/dist/atomic/index.d.mts +8 -0
- package/dist/atomic/index.mjs +9 -0
- package/dist/atomic/periodic.d.mts +82 -0
- package/dist/atomic/periodic.mjs +231 -0
- package/dist/atomic/properties.d.mts +100 -0
- package/dist/atomic/properties.mjs +574 -0
- package/dist/atomic/shells.d.mts +137 -0
- package/dist/atomic/shells.mjs +170 -0
- package/dist/atomic/trends.d.mts +148 -0
- package/dist/atomic/trends.mjs +225 -0
- package/dist/chem/chromatography.d.mts +66 -0
- package/dist/chem/chromatography.mjs +63 -0
- package/dist/chem/complex.d.mts +114 -0
- package/dist/chem/complex.mjs +234 -0
- package/dist/chem/cycle.d.mts +75 -0
- package/dist/chem/cycle.mjs +145 -0
- package/dist/chem/equation.d.mts +86 -0
- package/dist/chem/equation.mjs +168 -0
- package/dist/chem/formula.d.mts +90 -0
- package/dist/chem/formula.mjs +201 -0
- package/dist/chem/functional-groups.d.mts +31 -0
- package/dist/chem/functional-groups.mjs +187 -0
- package/dist/chem/hazard.d.mts +94 -0
- package/dist/chem/hazard.mjs +329 -0
- package/dist/chem/index.d.mts +34 -33
- package/dist/chem/index.mjs +34 -28
- package/dist/chem/ionic.d.mts +98 -0
- package/dist/chem/ionic.mjs +156 -0
- package/dist/chem/layout.d.mts +46 -0
- package/dist/chem/layout.mjs +342 -0
- package/dist/chem/lewis.d.mts +79 -0
- package/dist/chem/lewis.mjs +163 -0
- package/dist/chem/molecular-orbital.d.mts +114 -0
- package/dist/chem/molecular-orbital.mjs +201 -0
- package/dist/chem/molecule.d.mts +154 -0
- package/dist/chem/molecule.mjs +282 -0
- package/dist/chem/nmr.d.mts +153 -0
- package/dist/chem/nmr.mjs +165 -0
- package/dist/chem/polymer.d.mts +65 -0
- package/dist/chem/polymer.mjs +179 -0
- package/dist/chem/redox.d.mts +138 -0
- package/dist/chem/redox.mjs +328 -0
- package/dist/chem/relations.d.mts +183 -0
- package/dist/chem/relations.mjs +299 -0
- package/dist/chem/skeleton.d.mts +51 -0
- package/dist/chem/skeleton.mjs +186 -0
- package/dist/chem/solution.d.mts +116 -0
- package/dist/chem/solution.mjs +186 -0
- package/dist/chem/species.d.mts +47 -0
- package/dist/chem/species.mjs +128 -0
- package/dist/chem/stereo.d.mts +108 -0
- package/dist/chem/stereo.mjs +286 -0
- package/dist/chem/stoichiometry.d.mts +95 -0
- package/dist/chem/stoichiometry.mjs +145 -0
- package/dist/chem/synthesis.d.mts +113 -0
- package/dist/chem/synthesis.mjs +281 -0
- package/dist/chem/vsepr.d.mts +80 -0
- package/dist/chem/vsepr.mjs +247 -0
- package/dist/circuit/ac.d.mts +163 -0
- package/dist/circuit/ac.mjs +452 -0
- package/dist/circuit/dc.d.mts +92 -0
- package/dist/circuit/dc.mjs +333 -0
- package/dist/circuit/index.d.mts +5 -92
- package/dist/circuit/index.mjs +5 -332
- package/dist/circuit/network.d.mts +99 -0
- package/dist/circuit/network.mjs +440 -0
- package/dist/circuit/topology.d.mts +102 -0
- package/dist/circuit/topology.mjs +143 -0
- package/dist/colour/absorption.d.mts +79 -0
- package/dist/colour/absorption.mjs +137 -0
- package/dist/colour/index.d.mts +6 -0
- package/dist/colour/index.mjs +7 -0
- package/dist/colour/name.d.mts +14 -0
- package/dist/colour/name.mjs +76 -0
- package/dist/colour/observer.d.mts +31 -0
- package/dist/colour/observer.mjs +46 -0
- package/dist/colour/spectrum.d.mts +61 -0
- package/dist/colour/spectrum.mjs +108 -0
- package/dist/colour/srgb.d.mts +49 -0
- package/dist/colour/srgb.mjs +108 -0
- package/dist/core/constants.d.mts +138 -0
- package/dist/core/constants.mjs +139 -0
- package/dist/core/index.d.mts +3 -1
- package/dist/core/index.mjs +3 -1
- package/dist/core/vec3.d.mts +94 -0
- package/dist/core/vec3.mjs +123 -0
- package/dist/core/worked.d.mts +35 -0
- package/dist/core/worked.mjs +44 -0
- package/dist/electro/cell.d.mts +102 -0
- package/dist/electro/cell.mjs +170 -0
- package/dist/electro/conductivity.d.mts +157 -0
- package/dist/electro/conductivity.mjs +156 -0
- package/dist/electro/couples.d.mts +114 -0
- package/dist/electro/couples.mjs +334 -0
- package/dist/electro/electrolysis.d.mts +96 -0
- package/dist/electro/electrolysis.mjs +274 -0
- package/dist/electro/faraday.d.mts +150 -0
- package/dist/electro/faraday.mjs +118 -0
- package/dist/electro/index.d.mts +6 -0
- package/dist/electro/index.mjs +7 -0
- package/dist/finance/accounting/adjustments.d.mts +17 -0
- package/dist/finance/accounting/adjustments.mjs +202 -0
- package/dist/finance/accounting/analysis.d.mts +8 -0
- package/dist/finance/accounting/analysis.mjs +205 -0
- package/dist/finance/accounting/clubs.d.mts +21 -0
- package/dist/finance/accounting/clubs.mjs +217 -0
- package/dist/finance/accounting/company-statements.d.mts +7 -0
- package/dist/finance/accounting/company-statements.mjs +150 -0
- package/dist/finance/accounting/harness.d.mts +7 -0
- package/dist/finance/accounting/harness.mjs +45 -0
- package/dist/finance/accounting/index.d.mts +18 -0
- package/dist/finance/accounting/index.mjs +18 -0
- package/dist/finance/accounting/journalize.d.mts +16 -0
- package/dist/finance/accounting/journalize.mjs +128 -0
- package/dist/finance/accounting/manufacturing.d.mts +8 -0
- package/dist/finance/accounting/manufacturing.mjs +236 -0
- package/dist/finance/accounting/partnership.d.mts +7 -0
- package/dist/finance/accounting/partnership.mjs +152 -0
- package/dist/finance/accounting/period-case.d.mts +6 -0
- package/dist/finance/accounting/period-case.mjs +71 -0
- package/dist/finance/accounting/post.d.mts +9 -0
- package/dist/finance/accounting/post.mjs +84 -0
- package/dist/finance/accounting/precision.d.mts +9 -0
- package/dist/finance/accounting/precision.mjs +15 -0
- package/dist/finance/accounting/rules.d.mts +21 -0
- package/dist/finance/accounting/rules.mjs +37 -0
- package/dist/finance/accounting/solve.d.mts +28 -0
- package/dist/finance/accounting/solve.mjs +92 -0
- package/dist/finance/accounting/statements.d.mts +7 -0
- package/dist/finance/accounting/statements.mjs +142 -0
- package/dist/finance/accounting/types.d.mts +649 -0
- package/dist/finance/accounting/validate.d.mts +7 -0
- package/dist/finance/accounting/validate.mjs +67 -0
- package/dist/finance/accounting/verification.d.mts +24 -0
- package/dist/finance/accounting/verification.mjs +386 -0
- package/dist/finance/bizsim.d.mts +21 -39
- package/dist/finance/bizsim.mjs +276 -97
- package/dist/finance/index.d.mts +20 -43
- package/dist/finance/index.mjs +19 -77
- package/dist/finance/investment-math.d.mts +7 -0
- package/dist/finance/investment-math.mjs +18 -0
- package/dist/finance/management-accounting/budgeting.d.mts +7 -0
- package/dist/finance/management-accounting/budgeting.mjs +189 -0
- package/dist/finance/management-accounting/cash-budget.d.mts +7 -0
- package/dist/finance/management-accounting/cash-budget.mjs +154 -0
- package/dist/finance/management-accounting/costing.d.mts +8 -0
- package/dist/finance/management-accounting/costing.mjs +175 -0
- package/dist/finance/management-accounting/decisions.d.mts +8 -0
- package/dist/finance/management-accounting/decisions.mjs +164 -0
- package/dist/finance/management-accounting/index.d.mts +13 -0
- package/dist/finance/management-accounting/index.mjs +13 -0
- package/dist/finance/management-accounting/integrated-budget.d.mts +8 -0
- package/dist/finance/management-accounting/integrated-budget.mjs +67 -0
- package/dist/finance/management-accounting/inventory-costing.d.mts +7 -0
- package/dist/finance/management-accounting/inventory-costing.mjs +173 -0
- package/dist/finance/management-accounting/investment-appraisal.d.mts +7 -0
- package/dist/finance/management-accounting/investment-appraisal.mjs +148 -0
- package/dist/finance/management-accounting/management-case.d.mts +8 -0
- package/dist/finance/management-accounting/management-case.mjs +91 -0
- package/dist/finance/management-accounting/master-budget.d.mts +7 -0
- package/dist/finance/management-accounting/master-budget.mjs +137 -0
- package/dist/finance/management-accounting/production-costing.d.mts +8 -0
- package/dist/finance/management-accounting/production-costing.mjs +214 -0
- package/dist/finance/management-accounting/types.d.mts +644 -0
- package/dist/finance/management-accounting/variances.d.mts +7 -0
- package/dist/finance/management-accounting/variances.mjs +150 -0
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +1 -1
- package/dist/math/analysis.d.mts +61 -0
- package/dist/math/analysis.mjs +187 -0
- package/dist/math/answer-check.d.mts +50 -0
- package/dist/math/answer-check.mjs +106 -0
- package/dist/math/calculus.d.mts +4 -9
- package/dist/math/calculus.mjs +216 -7
- package/dist/math/complex.d.mts +93 -0
- package/dist/math/complex.mjs +239 -0
- package/dist/math/expr.d.mts +23 -0
- package/dist/math/expr.mjs +36 -0
- package/dist/math/index.d.mts +8 -22
- package/dist/math/index.mjs +8 -37
- package/dist/math/latex.mjs +2 -1
- package/dist/math/parse.mjs +1 -1
- package/dist/math/poly.d.mts +55 -0
- package/dist/math/poly.mjs +273 -0
- package/dist/math/rearrange.d.mts +14 -0
- package/dist/math/rearrange.mjs +125 -0
- package/dist/math/solve.d.mts +21 -0
- package/dist/math/solve.mjs +75 -0
- package/dist/networking/addressing.d.mts +26 -0
- package/dist/networking/addressing.mjs +33 -0
- package/dist/networking/cloud/access.d.mts +36 -0
- package/dist/networking/cloud/access.mjs +95 -0
- package/dist/networking/cloud/containers.d.mts +43 -0
- package/dist/networking/cloud/containers.mjs +54 -0
- package/dist/networking/cloud/edge.d.mts +17 -0
- package/dist/networking/cloud/edge.mjs +31 -0
- package/dist/networking/cloud/failure-domains.d.mts +19 -0
- package/dist/networking/cloud/failure-domains.mjs +22 -0
- package/dist/networking/cloud/geo-routing.d.mts +22 -0
- package/dist/networking/cloud/geo-routing.mjs +64 -0
- package/dist/networking/cloud/index.d.mts +18 -0
- package/dist/networking/cloud/index.mjs +19 -0
- package/dist/networking/cloud/inference-reliability.d.mts +30 -0
- package/dist/networking/cloud/inference-reliability.mjs +76 -0
- package/dist/networking/cloud/inference.d.mts +118 -0
- package/dist/networking/cloud/inference.mjs +179 -0
- package/dist/networking/cloud/infrastructure.d.mts +45 -0
- package/dist/networking/cloud/infrastructure.mjs +96 -0
- package/dist/networking/cloud/observability.d.mts +24 -0
- package/dist/networking/cloud/observability.mjs +34 -0
- package/dist/networking/cloud/orchestration.d.mts +32 -0
- package/dist/networking/cloud/orchestration.mjs +38 -0
- package/dist/networking/cloud/recovery.d.mts +21 -0
- package/dist/networking/cloud/recovery.mjs +27 -0
- package/dist/networking/cloud/releases.d.mts +42 -0
- package/dist/networking/cloud/releases.mjs +135 -0
- package/dist/networking/cloud/scaling.d.mts +25 -0
- package/dist/networking/cloud/scaling.mjs +65 -0
- package/dist/networking/cloud/secrets.d.mts +39 -0
- package/dist/networking/cloud/secrets.mjs +68 -0
- package/dist/networking/cloud/server-deployment.d.mts +29 -0
- package/dist/networking/cloud/server-deployment.mjs +72 -0
- package/dist/networking/cloud/storage.d.mts +16 -0
- package/dist/networking/cloud/storage.mjs +58 -0
- package/dist/networking/cloud/vpc.d.mts +18 -0
- package/dist/networking/cloud/vpc.mjs +100 -0
- package/dist/networking/delivery.d.mts +34 -0
- package/dist/networking/delivery.mjs +52 -0
- package/dist/networking/index.d.mts +47 -0
- package/dist/networking/index.mjs +47 -0
- package/dist/networking/ipv6.d.mts +28 -0
- package/dist/networking/ipv6.mjs +17 -0
- package/dist/networking/journeys.d.mts +83 -0
- package/dist/networking/journeys.mjs +161 -0
- package/dist/networking/models/airtime.d.mts +87 -0
- package/dist/networking/models/airtime.mjs +130 -0
- package/dist/networking/models/cellular.d.mts +157 -0
- package/dist/networking/models/cellular.mjs +252 -0
- package/dist/networking/models/encapsulation.d.mts +98 -0
- package/dist/networking/models/encapsulation.mjs +132 -0
- package/dist/networking/models/handover.d.mts +116 -0
- package/dist/networking/models/handover.mjs +209 -0
- package/dist/networking/models/handshake.d.mts +90 -0
- package/dist/networking/models/handshake.mjs +122 -0
- package/dist/networking/models/index.d.mts +14 -0
- package/dist/networking/models/index.mjs +15 -0
- package/dist/networking/models/media.d.mts +82 -0
- package/dist/networking/models/media.mjs +188 -0
- package/dist/networking/models/satellite.d.mts +86 -0
- package/dist/networking/models/satellite.mjs +121 -0
- package/dist/networking/models/spectrum.d.mts +83 -0
- package/dist/networking/models/spectrum.mjs +235 -0
- package/dist/networking/models/switch.d.mts +80 -0
- package/dist/networking/models/switch.mjs +155 -0
- package/dist/networking/models/vlan.d.mts +62 -0
- package/dist/networking/models/vlan.mjs +100 -0
- package/dist/networking/models/vpn.d.mts +66 -0
- package/dist/networking/models/vpn.mjs +125 -0
- package/dist/networking/models/wifi-privacy.d.mts +82 -0
- package/dist/networking/models/wifi-privacy.mjs +130 -0
- package/dist/networking/models/wifi.d.mts +240 -0
- package/dist/networking/models/wifi.mjs +562 -0
- package/dist/networking/nat-traversal.d.mts +24 -0
- package/dist/networking/nat-traversal.mjs +38 -0
- package/dist/networking/protocols.d.mts +17 -0
- package/dist/networking/protocols.mjs +57 -0
- package/dist/networking/queue.d.mts +12 -0
- package/dist/networking/queue.mjs +18 -0
- package/dist/networking/route-learning.d.mts +23 -0
- package/dist/networking/route-learning.mjs +43 -0
- package/dist/networking/routing.d.mts +11 -0
- package/dist/networking/routing.mjs +25 -0
- package/dist/networking/signals.d.mts +26 -0
- package/dist/networking/signals.mjs +35 -0
- package/dist/networking/subnet.d.mts +49 -0
- package/dist/networking/subnet.mjs +89 -0
- package/dist/networking/tcp-reliability.d.mts +24 -0
- package/dist/networking/tcp-reliability.mjs +38 -0
- package/dist/networking/topology/contract.d.mts +123 -0
- package/dist/networking/topology/index.d.mts +4 -0
- package/dist/networking/topology/index.mjs +4 -0
- package/dist/networking/topology/presets.d.mts +7 -0
- package/dist/networking/topology/presets.mjs +302 -0
- package/dist/networking/topology/simulation.d.mts +8 -0
- package/dist/networking/topology/simulation.mjs +179 -0
- package/dist/networking/wireless.d.mts +11 -0
- package/dist/networking/wireless.mjs +17 -0
- package/dist/optics/imaging.d.mts +218 -0
- package/dist/optics/imaging.mjs +276 -0
- package/dist/optics/index.d.mts +7 -0
- package/dist/optics/index.mjs +8 -0
- package/dist/optics/laws.d.mts +70 -0
- package/dist/optics/laws.mjs +195 -0
- package/dist/optics/prism.d.mts +148 -0
- package/dist/optics/prism.mjs +197 -0
- package/dist/optics/refraction.d.mts +201 -0
- package/dist/optics/refraction.mjs +270 -0
- package/dist/optics/wave-optics.d.mts +171 -0
- package/dist/optics/wave-optics.mjs +259 -0
- package/dist/physics/ac/index.d.mts +183 -0
- package/dist/physics/ac/index.mjs +203 -0
- package/dist/physics/dynamics/forces.d.mts +98 -0
- package/dist/physics/dynamics/forces.mjs +153 -0
- package/dist/physics/dynamics/friction.d.mts +76 -0
- package/dist/physics/dynamics/friction.mjs +119 -0
- package/dist/physics/dynamics/system.d.mts +68 -0
- package/dist/physics/dynamics/system.mjs +125 -0
- package/dist/physics/electrostatics/index.d.mts +169 -0
- package/dist/physics/electrostatics/index.mjs +249 -0
- package/dist/physics/energy/centre-of-mass.d.mts +115 -0
- package/dist/physics/energy/centre-of-mass.mjs +180 -0
- package/dist/physics/energy/conservation.d.mts +81 -0
- package/dist/physics/energy/conservation.mjs +129 -0
- package/dist/physics/energy/power.d.mts +90 -0
- package/dist/physics/energy/power.mjs +167 -0
- package/dist/physics/energy/spring.d.mts +73 -0
- package/dist/physics/energy/spring.mjs +129 -0
- package/dist/physics/energy/store.d.mts +63 -0
- package/dist/physics/energy/store.mjs +108 -0
- package/dist/physics/energy/theorem.d.mts +65 -0
- package/dist/physics/energy/theorem.mjs +113 -0
- package/dist/physics/energy/work.d.mts +114 -0
- package/dist/physics/energy/work.mjs +219 -0
- package/dist/physics/gravitation/body.d.mts +85 -0
- package/dist/physics/gravitation/body.mjs +168 -0
- package/dist/physics/gravitation/kepler.d.mts +127 -0
- package/dist/physics/gravitation/kepler.mjs +216 -0
- package/dist/physics/gravitation/law.d.mts +117 -0
- package/dist/physics/gravitation/law.mjs +175 -0
- package/dist/physics/gravitation/orbit.d.mts +119 -0
- package/dist/physics/gravitation/orbit.mjs +202 -0
- package/dist/physics/gravitation/potential.d.mts +114 -0
- package/dist/physics/gravitation/potential.mjs +202 -0
- package/dist/physics/gravitation/variation.d.mts +97 -0
- package/dist/physics/gravitation/variation.mjs +175 -0
- package/dist/physics/index.d.mts +35 -0
- package/dist/physics/index.mjs +36 -0
- package/dist/physics/integral-sweep.d.mts +63 -0
- package/dist/physics/integral-sweep.mjs +126 -0
- package/dist/physics/kinematics/equations.d.mts +47 -0
- package/dist/physics/kinematics/equations.mjs +126 -0
- package/dist/physics/kinematics/free-fall.d.mts +53 -0
- package/dist/physics/kinematics/free-fall.mjs +71 -0
- package/dist/physics/kinematics/given.d.mts +16 -0
- package/dist/physics/kinematics/given.mjs +55 -0
- package/dist/physics/kinematics/roots.d.mts +41 -0
- package/dist/physics/kinematics/roots.mjs +72 -0
- package/dist/physics/kinematics/solve.d.mts +52 -0
- package/dist/physics/kinematics/solve.mjs +172 -0
- package/dist/physics/magnetism/biot-savart.d.mts +161 -0
- package/dist/physics/magnetism/biot-savart.mjs +353 -0
- package/dist/physics/magnetism/flux.d.mts +94 -0
- package/dist/physics/magnetism/flux.mjs +313 -0
- package/dist/physics/magnetism/geomagnetism.d.mts +151 -0
- package/dist/physics/magnetism/geomagnetism.mjs +184 -0
- package/dist/physics/magnetism/index.d.mts +12 -0
- package/dist/physics/magnetism/index.mjs +74 -0
- package/dist/physics/magnetism/lorentz.d.mts +28 -0
- package/dist/physics/magnetism/lorentz.mjs +88 -0
- package/dist/physics/magnetism/magnetic-force.d.mts +102 -0
- package/dist/physics/magnetism/magnetic-force.mjs +169 -0
- package/dist/physics/momentum/collide.d.mts +77 -0
- package/dist/physics/momentum/collide.mjs +115 -0
- package/dist/physics/momentum/impulse.d.mts +89 -0
- package/dist/physics/momentum/impulse.mjs +159 -0
- package/dist/physics/momentum/planar.d.mts +60 -0
- package/dist/physics/momentum/planar.mjs +110 -0
- package/dist/physics/projectile.d.mts +107 -0
- package/dist/physics/projectile.mjs +185 -0
- package/dist/physics/rotation/angular.d.mts +85 -0
- package/dist/physics/rotation/angular.mjs +130 -0
- package/dist/physics/rotation/circular.d.mts +79 -0
- package/dist/physics/rotation/circular.mjs +131 -0
- package/dist/physics/rotation/inertia.d.mts +164 -0
- package/dist/physics/rotation/inertia.mjs +332 -0
- package/dist/physics/rotation/torque.d.mts +65 -0
- package/dist/physics/rotation/torque.mjs +134 -0
- package/dist/physics/vectors.d.mts +64 -0
- package/dist/physics/vectors.mjs +114 -0
- package/dist/quantity/index.d.mts +119 -0
- package/dist/quantity/index.mjs +417 -0
- package/dist/relation/derive.d.mts +74 -0
- package/dist/relation/derive.mjs +73 -0
- package/dist/relation/index.d.mts +56 -0
- package/dist/relation/index.mjs +234 -0
- package/dist/scene/evaluators.mjs +1 -1
- package/dist/sim/thermal.mjs +45 -20
- package/dist/thermo/calorimetry.d.mts +207 -0
- package/dist/thermo/calorimetry.mjs +345 -0
- package/dist/thermo/cycles.d.mts +142 -0
- package/dist/thermo/cycles.mjs +256 -0
- package/dist/thermo/entropy.d.mts +87 -0
- package/dist/thermo/entropy.mjs +140 -0
- package/dist/thermo/expansion.d.mts +130 -0
- package/dist/thermo/expansion.mjs +178 -0
- package/dist/thermo/gas.d.mts +197 -0
- package/dist/thermo/gas.mjs +279 -0
- package/dist/thermo/heat-transfer.d.mts +164 -0
- package/dist/thermo/heat-transfer.mjs +217 -0
- package/dist/thermo/index.d.mts +9 -82
- package/dist/thermo/index.mjs +9 -132
- package/dist/thermo/processes.d.mts +95 -0
- package/dist/thermo/processes.mjs +154 -0
- package/dist/thermo/temperature.d.mts +174 -0
- package/dist/thermo/temperature.mjs +236 -0
- package/dist/waves/damping.d.mts +224 -0
- package/dist/waves/damping.mjs +317 -0
- package/dist/waves/doppler.d.mts +72 -0
- package/dist/waves/doppler.mjs +125 -0
- package/dist/waves/index.d.mts +10 -0
- package/dist/waves/index.mjs +10 -0
- package/dist/waves/pendulum.d.mts +130 -0
- package/dist/waves/pendulum.mjs +211 -0
- package/dist/waves/pitch.d.mts +125 -0
- package/dist/waves/pitch.mjs +158 -0
- package/dist/waves/shm.d.mts +170 -0
- package/dist/waves/shm.mjs +279 -0
- package/dist/waves/springs.d.mts +103 -0
- package/dist/waves/springs.mjs +176 -0
- package/dist/waves/superposition.d.mts +124 -0
- package/dist/waves/superposition.mjs +196 -0
- package/dist/waves/wave.d.mts +154 -0
- package/dist/waves/wave.mjs +178 -0
- package/package.json +74 -2
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
//#region src/networking/models/wifi.ts
|
|
2
|
+
const BANDS = {
|
|
3
|
+
"2.4": {
|
|
4
|
+
id: "2.4",
|
|
5
|
+
label: "2.4 GHz",
|
|
6
|
+
megahertz: 2437,
|
|
7
|
+
channels: 3,
|
|
8
|
+
peakMbps: 150,
|
|
9
|
+
note: "Travels further and through more walls. Only three non-overlapping channels, shared with every neighbour, every microwave oven and every Bluetooth device."
|
|
10
|
+
},
|
|
11
|
+
"5": {
|
|
12
|
+
id: "5",
|
|
13
|
+
label: "5 GHz",
|
|
14
|
+
megahertz: 5180,
|
|
15
|
+
channels: 24,
|
|
16
|
+
peakMbps: 650,
|
|
17
|
+
note: "Far more room and far more speed, and it pays for that with range. A shorter wave is absorbed harder by everything it passes through."
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Loss per wall, in dB. These are the numbers a site survey actually uses, rounded to whole
|
|
22
|
+
* decibels because the spread between real buildings is wider than the rounding.
|
|
23
|
+
*
|
|
24
|
+
* The column to read across is the band. Concrete costs 12 dB at 2.4 GHz and 20 dB at 5 GHz: the
|
|
25
|
+
* SAME wall takes eight extra decibels out of the faster band, which is a further sixfold drop in
|
|
26
|
+
* power. One interior wall is survivable on either band. Two concrete walls is where 5 GHz stops
|
|
27
|
+
* being a choice.
|
|
28
|
+
*/
|
|
29
|
+
const MATERIALS = {
|
|
30
|
+
drywall: {
|
|
31
|
+
id: "drywall",
|
|
32
|
+
label: "Plasterboard partition",
|
|
33
|
+
loss: {
|
|
34
|
+
"2.4": 3,
|
|
35
|
+
"5": 4
|
|
36
|
+
},
|
|
37
|
+
note: "A hollow partition between two rooms. Nearly free to pass through, which is why open-plan flats cover easily."
|
|
38
|
+
},
|
|
39
|
+
wood: {
|
|
40
|
+
id: "wood",
|
|
41
|
+
label: "Wooden door",
|
|
42
|
+
loss: {
|
|
43
|
+
"2.4": 3,
|
|
44
|
+
"5": 5
|
|
45
|
+
},
|
|
46
|
+
note: "Closing an internal door costs real signal. Students rarely believe this until they watch the number move."
|
|
47
|
+
},
|
|
48
|
+
glass: {
|
|
49
|
+
id: "glass",
|
|
50
|
+
label: "Glass",
|
|
51
|
+
loss: {
|
|
52
|
+
"2.4": 3,
|
|
53
|
+
"5": 6
|
|
54
|
+
},
|
|
55
|
+
note: "Plain glass is mild. Coated energy-saving glass behaves closer to brick, which is why signal often dies at a balcony."
|
|
56
|
+
},
|
|
57
|
+
brick: {
|
|
58
|
+
id: "brick",
|
|
59
|
+
label: "Brick wall",
|
|
60
|
+
loss: {
|
|
61
|
+
"2.4": 6,
|
|
62
|
+
"5": 10
|
|
63
|
+
},
|
|
64
|
+
note: "The standard load-bearing wall in most homes here. Two of them between router and phone is the usual cause of a dead bedroom."
|
|
65
|
+
},
|
|
66
|
+
concrete: {
|
|
67
|
+
id: "concrete",
|
|
68
|
+
label: "Reinforced concrete",
|
|
69
|
+
loss: {
|
|
70
|
+
"2.4": 12,
|
|
71
|
+
"5": 20
|
|
72
|
+
},
|
|
73
|
+
note: "Slab, column or shear wall. The steel inside matters as much as the concrete: a conductive mesh reflects what it does not absorb."
|
|
74
|
+
},
|
|
75
|
+
metal: {
|
|
76
|
+
id: "metal",
|
|
77
|
+
label: "Metal / lift shaft",
|
|
78
|
+
loss: {
|
|
79
|
+
"2.4": 30,
|
|
80
|
+
"5": 35
|
|
81
|
+
},
|
|
82
|
+
note: "Effectively a wall a radio cannot cross. Signal reaches the far side by going around it, never through."
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Transmit power of a domestic access point, in dBm. 20 dBm is 100 mW, the legal ceiling in most
|
|
87
|
+
* places and what essentially every home router emits, which is worth knowing because it means
|
|
88
|
+
* "buy a more powerful router" is not available as an answer. The only levers are position,
|
|
89
|
+
* frequency, and a second access point.
|
|
90
|
+
*/
|
|
91
|
+
const TX_POWER_DBM = 20;
|
|
92
|
+
/**
|
|
93
|
+
* How fast power falls with distance indoors.
|
|
94
|
+
*
|
|
95
|
+
* In free space the exponent is 2: pure spreading over a sphere. Indoors, 3.0 is the standard
|
|
96
|
+
* figure for a home or small office, because furniture, floors and people absorb as well as the
|
|
97
|
+
* geometry spreading. The walls are counted separately and explicitly, so this number represents
|
|
98
|
+
* everything that is NOT a wall the learner placed.
|
|
99
|
+
*/
|
|
100
|
+
const PATH_LOSS_EXPONENT = 3;
|
|
101
|
+
/**
|
|
102
|
+
* The floor below which a number stops meaning anything, in dBm.
|
|
103
|
+
*
|
|
104
|
+
* Thermal noise in a 20 MHz channel sits near -101 dBm, so a receiver cannot distinguish a signal
|
|
105
|
+
* from the noise it arrives in much below this. An uncapped model happily reports -114 dBm, which
|
|
106
|
+
* looks precise and is fiction: the honest statement is "below the noise floor", and the lab should
|
|
107
|
+
* say that rather than print a number a real radio could never measure.
|
|
108
|
+
*/
|
|
109
|
+
const NOISE_FLOOR_DBM = -100;
|
|
110
|
+
/**
|
|
111
|
+
* Loss over the first metre, in dB. This is where frequency enters before any wall is involved:
|
|
112
|
+
* about 40 dB at 2.4 GHz and 46 dB at 5 GHz. The faster band starts six decibels behind at the
|
|
113
|
+
* very first metre, having passed through nothing at all.
|
|
114
|
+
*/
|
|
115
|
+
function referenceLossDb(band) {
|
|
116
|
+
return 20 * Math.log10(BANDS[band].megahertz) + 20 * Math.log10(.001) + 32.44;
|
|
117
|
+
}
|
|
118
|
+
/** Do two segments cross? Used to find which walls sit between the router and a point. */
|
|
119
|
+
function segmentsCross(a1, a2, b1, b2) {
|
|
120
|
+
const side = (p, q, r) => (q.x - p.x) * (r.y - p.y) - (q.y - p.y) * (r.x - p.x);
|
|
121
|
+
const d1 = side(b1, b2, a1);
|
|
122
|
+
const d2 = side(b1, b2, a2);
|
|
123
|
+
const d3 = side(a1, a2, b1);
|
|
124
|
+
const d4 = side(a1, a2, b2);
|
|
125
|
+
return (d1 > 0 !== d2 > 0 || d1 === 0 || d2 === 0) && (d3 > 0 !== d4 > 0 || d3 === 0 || d4 === 0) && d1 !== 0 && d2 !== 0 && d3 !== 0 && d4 !== 0;
|
|
126
|
+
}
|
|
127
|
+
/** The walls a straight line from the router to a point passes through. */
|
|
128
|
+
function wallsBetween(plan, from, to) {
|
|
129
|
+
return plan.walls.filter((wall) => segmentsCross(from, to, {
|
|
130
|
+
x: wall.x1,
|
|
131
|
+
y: wall.y1
|
|
132
|
+
}, {
|
|
133
|
+
x: wall.x2,
|
|
134
|
+
y: wall.y2
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Received signal strength at a point, by the log-distance model with explicit wall terms.
|
|
139
|
+
*
|
|
140
|
+
* This treats the signal as travelling in a straight line and ignores reflection, which in a real
|
|
141
|
+
* flat delivers some signal around a corner that this model calls dead. That simplification is
|
|
142
|
+
* deliberate and is the right one for teaching: it keeps the cause of every number visible, and a
|
|
143
|
+
* model that says "a wall costs you this much" is exactly the intuition a learner should leave with.
|
|
144
|
+
*/
|
|
145
|
+
function signalAt(plan, router, point, band) {
|
|
146
|
+
const dx = point.x - router.x;
|
|
147
|
+
const dy = point.y - router.y;
|
|
148
|
+
const distanceM = Math.max(1, Math.hypot(dx, dy));
|
|
149
|
+
const obstacles = wallsBetween(plan, router, point);
|
|
150
|
+
const wallLossDb = obstacles.reduce((sum, wall) => sum + MATERIALS[wall.material].loss[band], 0);
|
|
151
|
+
const spreading = 10 * 3 * Math.log10(distanceM);
|
|
152
|
+
const rssi = 20 - referenceLossDb(band) - spreading - wallLossDb;
|
|
153
|
+
return {
|
|
154
|
+
rssi: Math.max(NOISE_FLOOR_DBM, rssi),
|
|
155
|
+
distanceM,
|
|
156
|
+
obstacles,
|
|
157
|
+
wallLossDb
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* The grade ladder, in dBm.
|
|
162
|
+
*
|
|
163
|
+
* These are the thresholds a real site survey designs against rather than round numbers chosen to
|
|
164
|
+
* look tidy: -67 dBm is the industry target for voice and video, and -70 is where ordinary data
|
|
165
|
+
* starts to struggle. Naming what a learner could DO at each level, rather than calling it "3 bars",
|
|
166
|
+
* is the point. Bars are a made-up scale that differs between handsets; dBm is a measurement.
|
|
167
|
+
*/
|
|
168
|
+
const GRADES = [
|
|
169
|
+
{
|
|
170
|
+
id: "excellent",
|
|
171
|
+
label: "Excellent",
|
|
172
|
+
floor: -50,
|
|
173
|
+
can: "Anything at full speed, right beside the router."
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
id: "good",
|
|
177
|
+
label: "Good",
|
|
178
|
+
floor: -60,
|
|
179
|
+
can: "HD video calls and large downloads, no trouble."
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
id: "usable",
|
|
183
|
+
label: "Usable",
|
|
184
|
+
floor: -67,
|
|
185
|
+
can: "Video works. This is the level a network is designed to reach everywhere it matters."
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
id: "weak",
|
|
189
|
+
label: "Weak",
|
|
190
|
+
floor: -80,
|
|
191
|
+
can: "Messaging and browsing. Calls break up and video stops to buffer."
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
id: "dead",
|
|
195
|
+
label: "No usable signal",
|
|
196
|
+
floor: -Infinity,
|
|
197
|
+
can: "The phone may still show a bar and nothing will load."
|
|
198
|
+
}
|
|
199
|
+
];
|
|
200
|
+
function grade(rssi) {
|
|
201
|
+
return GRADES.find((step) => rssi >= step.floor) ?? GRADES[GRADES.length - 1];
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* A rough throughput for a reading, in Mbps.
|
|
205
|
+
*
|
|
206
|
+
* Real rate selection is a ladder of modulation schemes, and the shape that matters to a learner is
|
|
207
|
+
* that throughput does not fade gently with distance: it holds up, then falls off a cliff as each
|
|
208
|
+
* rung becomes unreadable. Interpolating between the grade thresholds reproduces that staircase
|
|
209
|
+
* feel without pretending to model MCS tables.
|
|
210
|
+
*/
|
|
211
|
+
function throughputMbps(rssi, band) {
|
|
212
|
+
const peak = BANDS[band].peakMbps;
|
|
213
|
+
if (rssi >= -50) return peak;
|
|
214
|
+
if (rssi >= -60) return Math.round(peak * .7);
|
|
215
|
+
if (rssi >= -67) return Math.round(peak * .4);
|
|
216
|
+
if (rssi >= -73) return Math.round(peak * .15);
|
|
217
|
+
if (rssi >= -80) return Math.round(peak * .05);
|
|
218
|
+
return 0;
|
|
219
|
+
}
|
|
220
|
+
/** Sample points inside a room. A 3x3 grid inset from the walls, so a corner is not judged from its centre. */
|
|
221
|
+
function samplePoints(room) {
|
|
222
|
+
const points = [];
|
|
223
|
+
for (let ix = 1; ix <= 3; ix++) for (let iy = 1; iy <= 3; iy++) points.push({
|
|
224
|
+
x: room.x + room.w * ix / 4,
|
|
225
|
+
y: room.y + room.h * iy / 4
|
|
226
|
+
});
|
|
227
|
+
return points;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Grade every room from its WORST corner rather than its centre.
|
|
231
|
+
*
|
|
232
|
+
* Judging a room by its middle is how a coverage plan passes on paper and fails in the building:
|
|
233
|
+
* the desk is against the far wall, not in the centre of the floor. Reporting the worst sample also
|
|
234
|
+
* makes the lab honest when a learner parks the router in the hallway and every room reads "usable"
|
|
235
|
+
* from its midpoint while the bed and the desk do not.
|
|
236
|
+
*/
|
|
237
|
+
function roomVerdicts(plan, router, band) {
|
|
238
|
+
return plan.rooms.map((room) => {
|
|
239
|
+
let worst = null;
|
|
240
|
+
for (const point of samplePoints(room)) {
|
|
241
|
+
const reading = signalAt(plan, router, point, band);
|
|
242
|
+
if (!worst || reading.rssi < worst.rssi) worst = reading;
|
|
243
|
+
}
|
|
244
|
+
const reading = worst;
|
|
245
|
+
const blamed = [...new Set(reading.obstacles.map((wall) => wall.material))].sort((a, b) => MATERIALS[b].loss[band] - MATERIALS[a].loss[band]);
|
|
246
|
+
return {
|
|
247
|
+
room,
|
|
248
|
+
rssi: reading.rssi,
|
|
249
|
+
grade: grade(reading.rssi),
|
|
250
|
+
mbps: throughputMbps(reading.rssi, band),
|
|
251
|
+
blamed
|
|
252
|
+
};
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* RSSI on a regular grid over the whole plan, for the heat map.
|
|
257
|
+
*
|
|
258
|
+
* Returned row-major as `[row][col]` with row 0 at the top, matching how the SVG draws it, so the
|
|
259
|
+
* scene never has to flip an index and quietly mirror the picture.
|
|
260
|
+
*/
|
|
261
|
+
function coverageGrid(plan, router, band, cols, rows) {
|
|
262
|
+
const grid = [];
|
|
263
|
+
for (let row = 0; row < rows; row++) {
|
|
264
|
+
const line = [];
|
|
265
|
+
const y = plan.heightM * (row + .5) / rows;
|
|
266
|
+
for (let col = 0; col < cols; col++) {
|
|
267
|
+
const x = plan.widthM * (col + .5) / cols;
|
|
268
|
+
line.push(signalAt(plan, router, {
|
|
269
|
+
x,
|
|
270
|
+
y
|
|
271
|
+
}, band).rssi);
|
|
272
|
+
}
|
|
273
|
+
grid.push(line);
|
|
274
|
+
}
|
|
275
|
+
return grid;
|
|
276
|
+
}
|
|
277
|
+
/** Share of the rooms that reach at least `usable`, which is the number a learner is trying to raise. */
|
|
278
|
+
function coverageScore(plan, router, band) {
|
|
279
|
+
const verdicts = roomVerdicts(plan, router, band);
|
|
280
|
+
if (!verdicts.length) return 0;
|
|
281
|
+
return verdicts.filter((verdict) => verdict.rssi >= -67).length / verdicts.length;
|
|
282
|
+
}
|
|
283
|
+
/** A run of wall with doorway gaps left open, so a learner can see signal pour through a door. */
|
|
284
|
+
function wallRun(axis, at, spans, material) {
|
|
285
|
+
return spans.map(([from, to]) => axis === "h" ? {
|
|
286
|
+
x1: from,
|
|
287
|
+
y1: at,
|
|
288
|
+
x2: to,
|
|
289
|
+
y2: at,
|
|
290
|
+
material
|
|
291
|
+
} : {
|
|
292
|
+
x1: at,
|
|
293
|
+
y1: from,
|
|
294
|
+
x2: at,
|
|
295
|
+
y2: to,
|
|
296
|
+
material
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Floor plans a learner recognises, which is most of the work.
|
|
301
|
+
*
|
|
302
|
+
* A generic rectangle labelled "room A / room B" teaches the model and none of the intuition. The
|
|
303
|
+
* three-bedroom flat below is the ordinary shape of an apartment in Dhaka or Chattogram: brick
|
|
304
|
+
* partitions, a concrete wet wall around the bathroom, and the router wherever the broadband cable
|
|
305
|
+
* happened to enter, which is almost never where it should be. A learner who fixes THIS plan can
|
|
306
|
+
* walk home and fix theirs, and that transfer is the whole reason to draw a room instead of a graph.
|
|
307
|
+
*/
|
|
308
|
+
const FLOOR_PLANS = [
|
|
309
|
+
{
|
|
310
|
+
id: "flat",
|
|
311
|
+
name: "Three-bedroom flat",
|
|
312
|
+
widthM: 12,
|
|
313
|
+
heightM: 9,
|
|
314
|
+
note: "The cable enters at the drawing room corner, so that is where the router sits. Two brick walls and a concrete wet wall stand between it and the far bedroom.",
|
|
315
|
+
rooms: [
|
|
316
|
+
{
|
|
317
|
+
id: "living",
|
|
318
|
+
name: "Drawing room",
|
|
319
|
+
x: 0,
|
|
320
|
+
y: 0,
|
|
321
|
+
w: 6,
|
|
322
|
+
h: 5
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
id: "kitchen",
|
|
326
|
+
name: "Kitchen",
|
|
327
|
+
x: 6,
|
|
328
|
+
y: 0,
|
|
329
|
+
w: 3,
|
|
330
|
+
h: 5
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
id: "bath",
|
|
334
|
+
name: "Bathroom",
|
|
335
|
+
x: 9,
|
|
336
|
+
y: 0,
|
|
337
|
+
w: 3,
|
|
338
|
+
h: 5
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
id: "bed1",
|
|
342
|
+
name: "Bedroom 1",
|
|
343
|
+
x: 0,
|
|
344
|
+
y: 5,
|
|
345
|
+
w: 4,
|
|
346
|
+
h: 4
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
id: "bed2",
|
|
350
|
+
name: "Bedroom 2",
|
|
351
|
+
x: 4,
|
|
352
|
+
y: 5,
|
|
353
|
+
w: 4,
|
|
354
|
+
h: 4
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
id: "bed3",
|
|
358
|
+
name: "Bedroom 3",
|
|
359
|
+
x: 8,
|
|
360
|
+
y: 5,
|
|
361
|
+
w: 4,
|
|
362
|
+
h: 4
|
|
363
|
+
}
|
|
364
|
+
],
|
|
365
|
+
spots: [
|
|
366
|
+
{
|
|
367
|
+
id: "corner",
|
|
368
|
+
name: "Drawing room corner, where the cable enters",
|
|
369
|
+
x: 1,
|
|
370
|
+
y: 1
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
id: "middle",
|
|
374
|
+
name: "Middle of the drawing room",
|
|
375
|
+
x: 4.6,
|
|
376
|
+
y: 2.4
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
id: "kitchen",
|
|
380
|
+
name: "On the kitchen worktop",
|
|
381
|
+
x: 7.5,
|
|
382
|
+
y: 4.3
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
id: "hall",
|
|
386
|
+
name: "Bedroom 2 wall, beside the door",
|
|
387
|
+
x: 7.6,
|
|
388
|
+
y: 5.4
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
id: "bed1",
|
|
392
|
+
name: "Bedroom 1, next to the bed",
|
|
393
|
+
x: 2,
|
|
394
|
+
y: 7
|
|
395
|
+
}
|
|
396
|
+
],
|
|
397
|
+
walls: [
|
|
398
|
+
...wallRun("h", 5, [
|
|
399
|
+
[0, 3],
|
|
400
|
+
[3.9, 7],
|
|
401
|
+
[7.9, 12]
|
|
402
|
+
], "brick"),
|
|
403
|
+
...wallRun("v", 6, [[0, 3.2], [4.1, 5]], "brick"),
|
|
404
|
+
...wallRun("v", 9, [[0, 5]], "concrete"),
|
|
405
|
+
...wallRun("v", 4, [[5, 8.1]], "brick"),
|
|
406
|
+
...wallRun("v", 8, [[5, 8.1]], "brick")
|
|
407
|
+
]
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
id: "open",
|
|
411
|
+
name: "Open-plan studio",
|
|
412
|
+
widthM: 10,
|
|
413
|
+
heightM: 7,
|
|
414
|
+
note: "Almost nothing in the way. Included so a learner can see what distance alone does, with absorption taken out of the picture.",
|
|
415
|
+
rooms: [
|
|
416
|
+
{
|
|
417
|
+
id: "main",
|
|
418
|
+
name: "Living and sleeping",
|
|
419
|
+
x: 0,
|
|
420
|
+
y: 0,
|
|
421
|
+
w: 7,
|
|
422
|
+
h: 7
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
id: "wash",
|
|
426
|
+
name: "Washroom",
|
|
427
|
+
x: 7,
|
|
428
|
+
y: 0,
|
|
429
|
+
w: 3,
|
|
430
|
+
h: 3
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
id: "balcony",
|
|
434
|
+
name: "Balcony",
|
|
435
|
+
x: 7,
|
|
436
|
+
y: 3,
|
|
437
|
+
w: 3,
|
|
438
|
+
h: 4
|
|
439
|
+
}
|
|
440
|
+
],
|
|
441
|
+
spots: [
|
|
442
|
+
{
|
|
443
|
+
id: "corner",
|
|
444
|
+
name: "Corner by the door",
|
|
445
|
+
x: 1,
|
|
446
|
+
y: 1
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
id: "middle",
|
|
450
|
+
name: "Middle of the room",
|
|
451
|
+
x: 3.5,
|
|
452
|
+
y: 3.5
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
id: "balcony",
|
|
456
|
+
name: "Out on the balcony",
|
|
457
|
+
x: 8.5,
|
|
458
|
+
y: 5
|
|
459
|
+
}
|
|
460
|
+
],
|
|
461
|
+
walls: [
|
|
462
|
+
...wallRun("v", 7, [[0, 2.2]], "brick"),
|
|
463
|
+
...wallRun("h", 3, [[7, 10]], "drywall"),
|
|
464
|
+
...wallRun("v", 7, [[3.4, 7]], "glass")
|
|
465
|
+
]
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
id: "office",
|
|
469
|
+
name: "Small office floor",
|
|
470
|
+
widthM: 16,
|
|
471
|
+
heightM: 10,
|
|
472
|
+
note: "A concrete core and a lift shaft. One access point cannot do this, and discovering that is the point of the plan.",
|
|
473
|
+
rooms: [
|
|
474
|
+
{
|
|
475
|
+
id: "open-desks",
|
|
476
|
+
name: "Open desks",
|
|
477
|
+
x: 0,
|
|
478
|
+
y: 0,
|
|
479
|
+
w: 9,
|
|
480
|
+
h: 10
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
id: "meeting",
|
|
484
|
+
name: "Meeting room",
|
|
485
|
+
x: 9,
|
|
486
|
+
y: 0,
|
|
487
|
+
w: 4,
|
|
488
|
+
h: 5
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
id: "server",
|
|
492
|
+
name: "Server room",
|
|
493
|
+
x: 9,
|
|
494
|
+
y: 5,
|
|
495
|
+
w: 4,
|
|
496
|
+
h: 5
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
id: "lobby",
|
|
500
|
+
name: "Lift lobby",
|
|
501
|
+
x: 13,
|
|
502
|
+
y: 0,
|
|
503
|
+
w: 3,
|
|
504
|
+
h: 10
|
|
505
|
+
}
|
|
506
|
+
],
|
|
507
|
+
spots: [
|
|
508
|
+
{
|
|
509
|
+
id: "desks",
|
|
510
|
+
name: "Above the open desks",
|
|
511
|
+
x: 4.5,
|
|
512
|
+
y: 5
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
id: "core",
|
|
516
|
+
name: "Against the concrete core",
|
|
517
|
+
x: 8.2,
|
|
518
|
+
y: 4.5
|
|
519
|
+
},
|
|
520
|
+
{
|
|
521
|
+
id: "meeting",
|
|
522
|
+
name: "Inside the meeting room",
|
|
523
|
+
x: 11,
|
|
524
|
+
y: 2.5
|
|
525
|
+
}
|
|
526
|
+
],
|
|
527
|
+
walls: [
|
|
528
|
+
...wallRun("v", 9, [[0, 4.1], [5, 10]], "concrete"),
|
|
529
|
+
...wallRun("h", 5, [[9, 13]], "concrete"),
|
|
530
|
+
...wallRun("v", 13, [[0, 4], [5.1, 10]], "metal")
|
|
531
|
+
]
|
|
532
|
+
}
|
|
533
|
+
];
|
|
534
|
+
/**
|
|
535
|
+
* The best spot on the plan, found by trying a coarse grid of positions.
|
|
536
|
+
*
|
|
537
|
+
* Present this only AFTER a learner has committed to a position of their own. Shown first it is an
|
|
538
|
+
* answer key and they will drag toward it without forming a reason; shown second it is feedback on
|
|
539
|
+
* a guess they already had to justify, and the gap between the two is the lesson.
|
|
540
|
+
*/
|
|
541
|
+
function bestPlacement(plan, band, step = 1) {
|
|
542
|
+
let best = {
|
|
543
|
+
x: plan.widthM / 2,
|
|
544
|
+
y: plan.heightM / 2
|
|
545
|
+
};
|
|
546
|
+
let bestScore = -Infinity;
|
|
547
|
+
for (let x = step; x < plan.widthM; x += step) for (let y = step; y < plan.heightM; y += step) {
|
|
548
|
+
const candidate = {
|
|
549
|
+
x,
|
|
550
|
+
y
|
|
551
|
+
};
|
|
552
|
+
const worst = Math.min(...roomVerdicts(plan, candidate, band).map((verdict) => verdict.rssi));
|
|
553
|
+
if (worst > bestScore) {
|
|
554
|
+
bestScore = worst;
|
|
555
|
+
best = candidate;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return best;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
//#endregion
|
|
562
|
+
export { BANDS, FLOOR_PLANS, GRADES, MATERIALS, NOISE_FLOOR_DBM, PATH_LOSS_EXPONENT, TX_POWER_DBM, bestPlacement, coverageGrid, coverageScore, grade, referenceLossDb, roomVerdicts, signalAt, throughputMbps, wallsBetween };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/networking/nat-traversal.d.ts
|
|
2
|
+
type NatBehavior = "endpoint-independent" | "address-dependent" | "symmetric";
|
|
3
|
+
type TraversalStrategy = "host-only" | "stun" | "ice";
|
|
4
|
+
interface NatTraversalOptions {
|
|
5
|
+
leftNat: NatBehavior;
|
|
6
|
+
rightNat: NatBehavior;
|
|
7
|
+
strategy: TraversalStrategy;
|
|
8
|
+
turnAvailable?: boolean;
|
|
9
|
+
}
|
|
10
|
+
interface NatTraversalResult {
|
|
11
|
+
candidates: readonly ("host" | "server-reflexive" | "relay")[];
|
|
12
|
+
selectedPath: "direct" | "mapped-direct" | "relay" | "blocked";
|
|
13
|
+
relayRequired: boolean;
|
|
14
|
+
relativeLatency: 1 | 2;
|
|
15
|
+
explanation: string;
|
|
16
|
+
}
|
|
17
|
+
declare function simulateNatTraversal({
|
|
18
|
+
leftNat,
|
|
19
|
+
rightNat,
|
|
20
|
+
strategy,
|
|
21
|
+
turnAvailable
|
|
22
|
+
}: NatTraversalOptions): NatTraversalResult;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { NatBehavior, NatTraversalOptions, NatTraversalResult, TraversalStrategy, simulateNatTraversal };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/networking/nat-traversal.ts
|
|
2
|
+
function simulateNatTraversal({ leftNat, rightNat, strategy, turnAvailable = true }) {
|
|
3
|
+
if (strategy === "host-only") return {
|
|
4
|
+
candidates: ["host"],
|
|
5
|
+
selectedPath: "blocked",
|
|
6
|
+
relayRequired: false,
|
|
7
|
+
relativeLatency: 1,
|
|
8
|
+
explanation: "Private host addresses are not reachable across the public Internet."
|
|
9
|
+
};
|
|
10
|
+
if (!(leftNat === "symmetric" || rightNat === "symmetric")) return {
|
|
11
|
+
candidates: ["host", "server-reflexive"],
|
|
12
|
+
selectedPath: "mapped-direct",
|
|
13
|
+
relayRequired: false,
|
|
14
|
+
relativeLatency: 1,
|
|
15
|
+
explanation: "STUN reveals usable public mappings, so the peers can check a direct path."
|
|
16
|
+
};
|
|
17
|
+
if (strategy === "ice" && turnAvailable) return {
|
|
18
|
+
candidates: [
|
|
19
|
+
"host",
|
|
20
|
+
"server-reflexive",
|
|
21
|
+
"relay"
|
|
22
|
+
],
|
|
23
|
+
selectedPath: "relay",
|
|
24
|
+
relayRequired: true,
|
|
25
|
+
relativeLatency: 2,
|
|
26
|
+
explanation: "Direct checks fail, so ICE selects the TURN relay candidate."
|
|
27
|
+
};
|
|
28
|
+
return {
|
|
29
|
+
candidates: ["host", "server-reflexive"],
|
|
30
|
+
selectedPath: "blocked",
|
|
31
|
+
relayRequired: true,
|
|
32
|
+
relativeLatency: 1,
|
|
33
|
+
explanation: "The changing mapping defeats the direct candidate and no relay is available."
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
export { simulateNatTraversal };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/networking/protocols.d.ts
|
|
2
|
+
type DnsCache = "cold" | "warm";
|
|
3
|
+
interface DnsHop {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly label: string;
|
|
6
|
+
readonly answer: string;
|
|
7
|
+
}
|
|
8
|
+
declare function dnsHops(hostname?: string, address?: string): readonly DnsHop[];
|
|
9
|
+
declare const DNS_HOPS: readonly DnsHop[];
|
|
10
|
+
declare function dnsPath(cache: DnsCache, hostname?: string, address?: string): readonly DnsHop[];
|
|
11
|
+
declare function dnsLookupMs(cache: DnsCache, hopMs: number): number;
|
|
12
|
+
type CertificateKind = "trusted" | "wrong-name" | "untrusted";
|
|
13
|
+
declare const TLS_STEPS: readonly ["ClientHello", "ServerHello", "Certificate", "Finished", "Application data"];
|
|
14
|
+
declare function certificateAccepted(kind: CertificateKind): boolean;
|
|
15
|
+
declare function visibleOnWire(step: number, accepted: boolean): string;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { CertificateKind, DNS_HOPS, DnsCache, DnsHop, TLS_STEPS, certificateAccepted, dnsHops, dnsLookupMs, dnsPath, visibleOnWire };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
//#region src/networking/protocols.ts
|
|
2
|
+
function dnsHops(hostname = "chat.example", address = "203.0.113.42") {
|
|
3
|
+
const suffix = hostname.includes(".") ? `.${hostname.split(".").at(-1)}` : hostname;
|
|
4
|
+
return [
|
|
5
|
+
{
|
|
6
|
+
id: "device",
|
|
7
|
+
label: "Your device",
|
|
8
|
+
answer: "Ask the recursive resolver"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
id: "resolver",
|
|
12
|
+
label: "Recursive resolver",
|
|
13
|
+
answer: "Check cache, then do the lookup"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: "root",
|
|
17
|
+
label: "Root",
|
|
18
|
+
answer: `Ask the ${suffix} servers`
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: "tld",
|
|
22
|
+
label: suffix,
|
|
23
|
+
answer: `Ask ${hostname}’s authority`
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: "authority",
|
|
27
|
+
label: "Authoritative server",
|
|
28
|
+
answer: address
|
|
29
|
+
}
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
const DNS_HOPS = dnsHops();
|
|
33
|
+
function dnsPath(cache, hostname, address) {
|
|
34
|
+
const hops = dnsHops(hostname, address);
|
|
35
|
+
return cache === "warm" ? hops.slice(0, 2) : hops;
|
|
36
|
+
}
|
|
37
|
+
function dnsLookupMs(cache, hopMs) {
|
|
38
|
+
return cache === "warm" ? Math.max(1, hopMs) : Math.max(1, hopMs) * 4;
|
|
39
|
+
}
|
|
40
|
+
const TLS_STEPS = [
|
|
41
|
+
"ClientHello",
|
|
42
|
+
"ServerHello",
|
|
43
|
+
"Certificate",
|
|
44
|
+
"Finished",
|
|
45
|
+
"Application data"
|
|
46
|
+
];
|
|
47
|
+
function certificateAccepted(kind) {
|
|
48
|
+
return kind === "trusted";
|
|
49
|
+
}
|
|
50
|
+
function visibleOnWire(step, accepted) {
|
|
51
|
+
if (step < 2) return "ClientHello and ServerHello";
|
|
52
|
+
if (!accepted) return "handshake stopped";
|
|
53
|
+
return step < 4 ? "encrypted handshake bytes" : "encrypted application bytes";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
//#endregion
|
|
57
|
+
export { DNS_HOPS, TLS_STEPS, certificateAccepted, dnsHops, dnsLookupMs, dnsPath, visibleOnWire };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/networking/queue.d.ts
|
|
2
|
+
/** Pure finite-buffer queue mechanics. Rates are packets per second. */
|
|
3
|
+
interface QueueResult {
|
|
4
|
+
readonly queued: number;
|
|
5
|
+
readonly dropped: number;
|
|
6
|
+
readonly delayMs: number;
|
|
7
|
+
readonly state: "clear" | "building" | "dropping";
|
|
8
|
+
}
|
|
9
|
+
declare function queueAtTime(arrivalPps: number, servicePps: number, bufferPackets: number, elapsedSeconds: number): QueueResult;
|
|
10
|
+
declare function bottleneckQueue(arrivalPps: number, servicePps: number, bufferPackets: number): QueueResult;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { QueueResult, bottleneckQueue, queueAtTime };
|