Skip to content

Commit e1e039e

Browse files
author
JonesRobM
committed
Bump with radar eq
1 parent 6c524c7 commit e1e039e

File tree

18 files changed

+879
-15
lines changed

18 files changed

+879
-15
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ wheels/
2222
.hypothesis/
2323
.secrets.baseline
2424

25+
# Coverage
26+
.coverage
27+
htmlcov/
28+
coverage.xml
29+
2530
# IDE
2631
.idea/
2732
.vscode/

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ All notable changes to PhysBound are documented here.
44

55
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [0.2.0] - 2026-02-26
8+
9+
### Added
10+
- **radar_range** tool — monostatic radar range equation with R_max computation and detection range validation
11+
- 2 new validators: `validate_positive_power`, `validate_positive_rcs`
12+
- 2 new hallucination cases: fourth-root power fallacy, drone RCS detection range
13+
- Property-based tests for radar range monotonicity invariants (7 tests)
14+
- Radar range formula reference in `docs/formulas.md`
15+
- MCP integration tests for radar_range tool
16+
717
## [0.1.3] - 2026-02-26
818

919
### Added
@@ -56,6 +66,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Version
5666
- 107 tests covering all engines, validators, server integration, and marketing hallucination cases
5767
- Pre-commit hooks: ruff, detect-secrets, large file checks
5868

69+
[0.2.0]: https://github.com/JonesRobM/physbound/compare/v0.1.3...v0.2.0
5970
[0.1.3]: https://github.com/JonesRobM/physbound/compare/v0.1.2...v0.1.3
6071
[0.1.2]: https://github.com/JonesRobM/physbound/compare/v0.1.1...v0.1.2
6172
[0.1.1]: https://github.com/JonesRobM/physbound/compare/v0.1.0...v0.1.1

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!-- mcp-name: io.github.JonesRobM/physbound -->
2-
<!-- keywords: MCP server, physics validation, RF link budget, Shannon-Hartley, thermal noise, antenna gain, AI hallucination detection, physical layer linter, Friis equation, FSPL, signal processing, telecommunications -->
2+
<!-- keywords: MCP server, physics validation, RF link budget, Shannon-Hartley, thermal noise, antenna gain, AI hallucination detection, physical layer linter, Friis equation, FSPL, signal processing, telecommunications, radar range equation, radar cross section, RCS -->
33

44
<p align="center">
55
<img src="Avatar.png" alt="PhysBound" width="200">
@@ -35,6 +35,8 @@ LLMs routinely hallucinate physics. PhysBound catches it:
3535
| 8 | Shannon-Hartley | "10 MHz LTE at 10 dB SNR supports 1 Gbps" | Shannon limit: **34.6 Mbps** | CAUGHT |
3636
| 9 | Noise Cascade | "Stage order doesn't affect system NF" | LNA first: **1.66 dB** vs mixer first: **8.03 dB** | CAUGHT |
3737
| 10 | Antenna Aperture | "10 cm patch at 900 MHz provides 20 dBi" | Aperture limit: **-3.1 dBi** | CAUGHT |
38+
| 11 | Radar Range | "Doubling TX power doubles radar range" | Range increases by **1.189x** (2^(1/4)), not 2x | CAUGHT |
39+
| 12 | Radar Range | "Drone (0.01 m^2 RCS) at 200 km by 1 kW X-band" | Max range: **2.7 km** | CAUGHT |
3840

3941
*Generated automatically by `pytest tests/test_marketing.py -s`*
4042

@@ -95,6 +97,14 @@ Computes thermal noise power `N = k_B * T * B`, cascades noise figures through m
9597

9698
Returns: Thermal noise in dBm and watts, cascaded noise figure, system noise temperature, and receiver sensitivity.
9799

100+
### `radar_range`
101+
102+
Computes the monostatic radar range equation `R_max = [P_t G^2 lambda^2 sigma / ((4pi)^3 S_min L)]^(1/4)` and validates detection range claims.
103+
104+
**Example:** *"Can a 1 kW X-band radar with 30 dBi gain detect a 0.01 m^2 drone at 200 km?"*
105+
106+
Returns: Maximum detection range, minimum detectable signal, wavelength, and intermediate values. Catches the common fourth-root fallacy where doubling power is incorrectly assumed to double range.
107+
98108
---
99109

100110
## Physics Guarantees
@@ -105,6 +115,7 @@ Every calculation is validated against hard physical limits:
105115
- **Thermal noise floor:** `N = -174 dBm/Hz` at 290K — the IEEE standard reference
106116
- **Shannon limit:** `C = B * log2(1 + SNR)` — no throughput claim exceeds this
107117
- **Aperture limit:** `G_max = eta * (pi * D / lambda)^2` — antenna gain is bounded by physics
118+
- **Radar range equation:** `R_max = [P_t G^2 lambda^2 sigma / ((4pi)^3 S_min)]^(1/4)` — range obeys the fourth-root law
108119

109120
Violations return structured `PhysicalViolationError` responses with LaTeX explanations, not silent failures.
110121

docs/formulas.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,52 @@ S_min is the minimum signal power (in dBm) the receiver can detect.
145145

146146
---
147147

148+
## Monostatic Radar Range Equation
149+
150+
### Maximum Detection Range
151+
152+
```
153+
R_max = [P_t * G^2 * lambda^2 * sigma / ((4*pi)^3 * S_min * L)]^(1/4)
154+
```
155+
156+
- **P_t**: peak transmit power in watts
157+
- **G**: antenna gain (linear, monostatic: same antenna TX/RX)
158+
- **lambda**: wavelength = c / f (meters)
159+
- **sigma**: radar cross section (RCS) in m^2
160+
- **S_min**: minimum detectable signal power in watts
161+
- **L**: total system losses (linear)
162+
163+
### Signal-to-Noise Ratio (SNR Form)
164+
165+
```
166+
SNR = P_t * G^2 * lambda^2 * sigma / ((4*pi)^3 * k_B * T_s * B_n * R^4 * L)
167+
```
168+
169+
- **k_B**: Boltzmann constant
170+
- **T_s**: system noise temperature in Kelvin
171+
- **B_n**: noise bandwidth in Hz
172+
- **R**: range in meters
173+
174+
### Minimum Detectable Signal
175+
176+
```
177+
S_min = k_B * T_s * B_n * SNR_min / N_pulses
178+
```
179+
180+
Where N_pulses provides coherent integration gain.
181+
182+
### Key Physical Insight: The Fourth-Root Law
183+
184+
Range scales as the **fourth root** of power, gain squared, RCS, and wavelength squared:
185+
186+
- Doubling P_t increases R_max by factor of 2^(1/4) = 1.189 (NOT 2x)
187+
- Doubling antenna gain (linear) increases R_max by factor of 2^(1/2) = 1.414
188+
- 10x larger RCS increases R_max by factor of 10^(1/4) = 1.778
189+
190+
Any claimed detection range exceeding R_max for the given parameters is a physics violation.
191+
192+
---
193+
148194
## Input Validation Guards
149195

150196
PhysBound enforces these constraints on all inputs before computation:
@@ -158,3 +204,7 @@ PhysBound enforces these constraints on all inputs before computation:
158204
| SNR > 0 (linear) | Signal must carry energy |
159205
| Noise Figure >= 0 dB | Quantum noise limit |
160206
| Antenna diameter > 0 m | Physical aperture must exist |
207+
| Power > 0 W | Conservation of Energy |
208+
| RCS > 0 m^2 | Physical target must scatter energy |
209+
| Losses >= 0 dB | Passive system cannot create energy |
210+
| Num pulses >= 1 | At least one pulse required |

pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "physbound"
3-
version = "0.1.3"
3+
version = "0.2.0"
44
description = "Physical Layer Linter — validates RF and physics calculations against hard physical limits"
55
readme = "README.md"
66
license = "MIT"
@@ -23,6 +23,9 @@ keywords = [
2323
"ai-hallucination",
2424
"telecommunications",
2525
"friis-equation",
26+
"radar",
27+
"radar-range-equation",
28+
"radar-cross-section",
2629
]
2730
classifiers = [
2831
"Development Status :: 4 - Beta",

server.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
{
22
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
33
"name": "io.github.JonesRobM/physbound",
4-
"description": "Physical Layer Linter — validates RF link budgets, Shannon capacity, and noise floors.",
4+
"description": "Physical Layer Linter — validates RF link budgets, Shannon capacity, noise floors, and radar range equations.",
55
"repository": {
66
"url": "https://github.com/JonesRobM/physbound",
77
"source": "github"
88
},
9-
"version": "0.1.3",
9+
"version": "0.2.0",
1010
"packages": [
1111
{
1212
"registryType": "pypi",
1313
"identifier": "physbound",
14-
"version": "0.1.3",
14+
"version": "0.2.0",
1515
"transport": {
1616
"type": "stdio"
1717
}

src/physbound/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""PhysBound — Physical Layer Linter for AI hallucination detection."""
22

3-
__version__ = "0.1.3"
3+
__version__ = "0.2.0"

src/physbound/engines/radar.py

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
"""Monostatic radar range equation and SNR computation.
2+
3+
Formulas:
4+
R_max = [P_t * G^2 * lambda^2 * sigma / ((4*pi)^3 * S_min * L)]^(1/4)
5+
SNR = P_t * G^2 * lambda^2 * sigma / ((4*pi)^3 * k_B * T_s * B_n * R^4 * L)
6+
S_min = k_B * T_s * B_n * SNR_min / N_pulses
7+
"""
8+
9+
import math
10+
11+
from physbound.engines.constants import BOLTZMANN, SPEED_OF_LIGHT
12+
from physbound.engines.units import db_to_linear
13+
from physbound.errors import PhysicalViolationError
14+
from physbound.validators import (
15+
validate_positive_bandwidth,
16+
validate_positive_frequency,
17+
validate_positive_power,
18+
validate_positive_rcs,
19+
validate_temperature,
20+
)
21+
22+
23+
def compute_radar_range(
24+
peak_power_w: float,
25+
antenna_gain_dbi: float,
26+
frequency_hz: float,
27+
rcs_m2: float,
28+
system_noise_temp_k: float = 290.0,
29+
noise_bandwidth_hz: float = 1e6,
30+
min_snr_db: float = 13.0,
31+
claimed_range_m: float | None = None,
32+
num_pulses: int = 1,
33+
losses_db: float = 0.0,
34+
) -> dict:
35+
"""Compute maximum monostatic radar detection range.
36+
37+
Args:
38+
peak_power_w: Peak transmit power in watts.
39+
antenna_gain_dbi: Antenna gain in dBi (monostatic: same for TX/RX).
40+
frequency_hz: Operating frequency in Hz.
41+
rcs_m2: Radar cross section in m^2.
42+
system_noise_temp_k: System noise temperature in Kelvin.
43+
noise_bandwidth_hz: Receiver noise bandwidth in Hz.
44+
min_snr_db: Minimum required SNR in dB for detection.
45+
claimed_range_m: Optional claimed detection range to validate.
46+
num_pulses: Number of coherently integrated pulses.
47+
losses_db: Total system losses in dB.
48+
49+
Returns:
50+
Dict with max_range_m, max_range_km, wavelength_m,
51+
min_detectable_power_w/dbm, human_readable, latex, warnings.
52+
53+
Raises:
54+
PhysicalViolationError: If inputs violate physics or claimed range
55+
exceeds R_max.
56+
"""
57+
# Validate inputs
58+
validate_positive_power(peak_power_w)
59+
validate_positive_frequency(frequency_hz)
60+
validate_positive_rcs(rcs_m2)
61+
validate_temperature(system_noise_temp_k)
62+
validate_positive_bandwidth(noise_bandwidth_hz)
63+
64+
if num_pulses < 1:
65+
raise PhysicalViolationError(
66+
message=f"Number of pulses must be >= 1, got {num_pulses}",
67+
law_violated="Radar Signal Processing",
68+
latex_explanation=r"$N_{\text{pulses}} \geq 1$ required",
69+
claimed_value=float(num_pulses),
70+
)
71+
if losses_db < 0:
72+
raise PhysicalViolationError(
73+
message=f"System losses must be >= 0 dB, got {losses_db} dB",
74+
law_violated="Conservation of Energy",
75+
latex_explanation=r"$L \geq 0\,\text{dB}$; negative loss implies free energy gain",
76+
claimed_value=losses_db,
77+
unit="dB",
78+
)
79+
80+
# Derived quantities
81+
c = SPEED_OF_LIGHT.magnitude
82+
k_b = BOLTZMANN.magnitude
83+
wavelength_m = c / frequency_hz
84+
gain_linear = db_to_linear(antenna_gain_dbi)
85+
snr_min_linear = db_to_linear(min_snr_db)
86+
losses_linear = db_to_linear(losses_db)
87+
integration_gain = num_pulses
88+
89+
# Minimum detectable signal power
90+
s_min_w = (k_b * system_noise_temp_k * noise_bandwidth_hz * snr_min_linear) / integration_gain
91+
s_min_dbm = 10.0 * math.log10(s_min_w / 1e-3)
92+
93+
# Radar range equation: R_max
94+
numerator = peak_power_w * gain_linear**2 * wavelength_m**2 * rcs_m2
95+
denominator = (4.0 * math.pi) ** 3 * s_min_w * losses_linear
96+
r_max_m = (numerator / denominator) ** 0.25
97+
98+
# Warnings
99+
warnings: list[str] = []
100+
if frequency_hz > 3e11:
101+
warnings.append(
102+
"Frequency > 300 GHz: atmospheric absorption may significantly "
103+
"reduce effective range beyond the free-space model."
104+
)
105+
if num_pulses > 1:
106+
warnings.append(
107+
f"Coherent integration of {num_pulses} pulses assumed "
108+
f"(gain = N). Non-coherent integration yields gain = sqrt(N)."
109+
)
110+
if rcs_m2 > 100:
111+
warnings.append(
112+
"RCS > 100 m^2 is typical only for very large targets (ships, large aircraft)."
113+
)
114+
if rcs_m2 < 1e-4:
115+
warnings.append("RCS < 0.0001 m^2 is at the limit of detectability for most radar systems.")
116+
117+
# Validate claimed range
118+
if claimed_range_m is not None and claimed_range_m > r_max_m:
119+
excess_pct = ((claimed_range_m - r_max_m) / r_max_m) * 100.0
120+
raise PhysicalViolationError(
121+
message=(
122+
f"Claimed detection range {claimed_range_m:.1f} m "
123+
f"({claimed_range_m / 1000:.1f} km) exceeds radar range "
124+
f"equation limit of {r_max_m:.1f} m "
125+
f"({r_max_m / 1000:.1f} km) by {excess_pct:.1f}%"
126+
),
127+
law_violated="Radar Range Equation",
128+
latex_explanation=(
129+
rf"$R_{{\max}} = \left[\frac{{P_t G^2 \lambda^2 \sigma}}"
130+
rf"{{(4\pi)^3 S_{{\min}} L}}\right]^{{1/4}} = "
131+
rf"{r_max_m:.1f}\,\text{{m}}$. "
132+
rf"Claimed ${claimed_range_m:.1f}\,\text{{m}}$ exceeds "
133+
rf"this limit by ${excess_pct:.1f}\%$."
134+
),
135+
computed_limit=r_max_m,
136+
claimed_value=claimed_range_m,
137+
unit="m",
138+
)
139+
140+
# Human-readable output
141+
power_dbm = 10.0 * math.log10(peak_power_w / 1e-3)
142+
human_readable = (
143+
f"Radar Range Equation (Monostatic):\n"
144+
f" Peak Power: {peak_power_w:.1f} W ({power_dbm:.1f} dBm)\n"
145+
f" Antenna Gain: {antenna_gain_dbi:.1f} dBi\n"
146+
f" Frequency: {frequency_hz / 1e9:.3f} GHz "
147+
f"(lambda = {wavelength_m:.4f} m)\n"
148+
f" RCS: {rcs_m2:.4f} m^2\n"
149+
f" System Temp: {system_noise_temp_k:.1f} K\n"
150+
f" Noise BW: {noise_bandwidth_hz / 1e6:.3f} MHz\n"
151+
f" Min SNR: {min_snr_db:.1f} dB\n"
152+
f" Losses: {losses_db:.1f} dB\n"
153+
f" Pulses: {num_pulses}\n"
154+
f" S_min: {s_min_dbm:.2f} dBm ({s_min_w:.3e} W)\n"
155+
f" Max Range: {r_max_m:.1f} m ({r_max_m / 1000:.2f} km)"
156+
)
157+
158+
# LaTeX output
159+
latex = (
160+
rf"$R_{{\max}} = \left[\frac{{P_t G^2 \lambda^2 \sigma}}"
161+
rf"{{(4\pi)^3 S_{{\min}} L}}\right]^{{1/4}} = "
162+
rf"\left[\frac{{{peak_power_w:.1f} \times {gain_linear:.2f}^2 \times "
163+
rf"{wavelength_m:.4f}^2 \times {rcs_m2:.4f}}}"
164+
rf"{{(4\pi)^3 \times {s_min_w:.3e} \times {losses_linear:.2f}}}"
165+
rf"\right]^{{1/4}} = {r_max_m:.1f}\,\text{{m}}$"
166+
)
167+
168+
return {
169+
"max_range_m": r_max_m,
170+
"max_range_km": r_max_m / 1000.0,
171+
"wavelength_m": wavelength_m,
172+
"min_detectable_power_w": s_min_w,
173+
"min_detectable_power_dbm": s_min_dbm,
174+
"antenna_gain_linear": gain_linear,
175+
"snr_min_linear": snr_min_linear,
176+
"integration_gain": integration_gain,
177+
"losses_linear": losses_linear,
178+
"warnings": warnings,
179+
"human_readable": human_readable,
180+
"latex": latex,
181+
}

0 commit comments

Comments
 (0)