Add IFA/meander/stub antenna templates with SVG and KiCad export.
Progetta now returns a parametric radiator geometry (segments + preview + .kicad_mod) so the layout can be replicated without inventing EM results. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -177,3 +177,79 @@ def test_design_recipe_ready_with_ant_and_stackup():
|
||||
assert recipe.radiator.length_mm_suggest is not None
|
||||
assert recipe.zone is not None
|
||||
assert recipe.zone.bbox_mm is not None
|
||||
assert recipe.geometry is not None
|
||||
assert recipe.geometry.template == "ifa"
|
||||
assert recipe.geometry.fit in ("ok", "scaled")
|
||||
assert len(recipe.geometry.segments) >= 2
|
||||
assert recipe.geometry.svg and "<svg" in recipe.geometry.svg
|
||||
assert recipe.geometry.kicad_mod and '(footprint "' in recipe.geometry.kicad_mod
|
||||
assert "fp_line" in recipe.geometry.kicad_mod
|
||||
|
||||
|
||||
def test_geometry_templates_produce_export():
|
||||
from backend.pinscopex.antenna_geometry import build_geometry
|
||||
|
||||
for tmpl in ("ifa", "meander", "stub"):
|
||||
geo = build_geometry(tmpl, f0_mhz=2440.0, w_mm=0.4, er=4.5)
|
||||
assert geo.fit == "ok"
|
||||
assert geo.total_length_mm is not None and geo.total_length_mm > 10
|
||||
assert geo.length_ideal_mm is not None
|
||||
assert geo.total_length_mm >= geo.length_ideal_mm * 0.9
|
||||
assert geo.svg and "path" in geo.svg
|
||||
assert geo.kicad_mod and '(pad "1"' in geo.kicad_mod
|
||||
assert len(geo.segments) >= 1
|
||||
|
||||
|
||||
def test_geometry_overflow_tiny_zone():
|
||||
from backend.pinscopex.antenna_geometry import build_geometry
|
||||
|
||||
geo = build_geometry(
|
||||
"ifa",
|
||||
f0_mhz=2440.0,
|
||||
w_mm=0.4,
|
||||
er=4.5,
|
||||
zone_bbox_mm=(0.0, 0.0, 2.0, 1.0),
|
||||
feed_xy=(0.0, 0.0),
|
||||
)
|
||||
assert geo.fit == "overflow"
|
||||
assert not geo.segments
|
||||
|
||||
|
||||
def test_design_recipe_meander_template():
|
||||
g = DesignGraph(
|
||||
components={
|
||||
"ANT1": Component(
|
||||
reference="ANT1", value="feed", footprint="",
|
||||
component_type=ComponentType.CONNECTOR,
|
||||
pins={"1": "ANT_FEED"},
|
||||
),
|
||||
},
|
||||
nets={
|
||||
"ANT_FEED": Net(
|
||||
name="ANT_FEED", net_type=NetType.SIGNAL,
|
||||
pins=[PinConnection(component_ref="ANT1", pin_number="1")],
|
||||
),
|
||||
},
|
||||
)
|
||||
layout = LayoutGraph(
|
||||
footprints={
|
||||
"ANT1": LayoutFootprint(
|
||||
reference="ANT1", x=0.0, y=0.0, layer="F.Cu",
|
||||
pads=[LayoutPad(number="1", x=0.0, y=0.0, net="ANT_FEED")],
|
||||
),
|
||||
},
|
||||
nets={"ANT_FEED": 1},
|
||||
stackup=LayoutStackup(
|
||||
copper_layers=["F.Cu", "B.Cu"],
|
||||
dielectrics=[LayoutDielectric(name="FR4", er=4.5, height_mm=0.2)],
|
||||
copper_thickness_mm=0.035,
|
||||
),
|
||||
)
|
||||
recipe = build_design_recipe(
|
||||
g, layout, f0_mhz=2440.0, template="meander",
|
||||
)
|
||||
assert recipe.status == "ready"
|
||||
assert recipe.geometry is not None
|
||||
assert recipe.geometry.template == "meander"
|
||||
assert recipe.geometry.fit == "ok"
|
||||
assert recipe.geometry.kicad_mod is not None
|
||||
|
||||
Reference in New Issue
Block a user