Fix CE amp coupling cap routing and Colpitts test variable selection
CE amplifier schematic: the input coupling cap CC_in was placed horizontally (R90) at y=336 — the same y as the RB1-to-base bias wire. Both cap pins sat on the wire, shorting the cap and allowing Vin's 0V DC to override the bias divider, putting Q1 in cutoff. Fix: move CC_in to vertical orientation (R0) above the base wire. Now pinA=(400,256) and pinB=(400,320) are off the y=336 bias path. The cap properly blocks DC while passing the 1kHz input signal. Result: V(out) swings 2.2Vpp (gain ≈ 110) instead of stuck at Vcc. Colpitts oscillator test: the schematic was actually working (V(out) pp=2.05V) but the test's fallback variable selection picked V(n001) (the Vcc rail, constant 12V) instead of V(out). Fix: look for V(out) first since the schematic labels the collector with "out". Integration tests: 4/4 pass, unit tests: 360/360 pass.
This commit is contained in:
parent
9b418a06c5
commit
b16c20c2ca
@ -581,40 +581,34 @@ def generate_common_emitter_amp(
|
|||||||
qe = pin_position("npn", 2, 400, 288) # emitter
|
qe = pin_position("npn", 2, 400, 288) # emitter
|
||||||
|
|
||||||
# RC from Vcc rail to collector.
|
# RC from Vcc rail to collector.
|
||||||
# Want pinB at collector (464, 288). res at (448, 192): pinB=(464, 288). Good.
|
# res at (448, 192): pinA=(464, 208)=vcc rail, pinB=(464, 288)=collector
|
||||||
rc_a = pin_position("res", 0, 448, 192) # (464, 208) = vcc rail
|
rc_a = pin_position("res", 0, 448, 192) # (464, 208) = vcc rail
|
||||||
|
|
||||||
# RE from emitter to GND.
|
# RE from emitter to GND.
|
||||||
# Want pinA at emitter (464, 384). res at (448, 368): pinA=(464, 384).
|
# res at (448, 368): pinA=(464, 384)=emitter, pinB=(464, 464)=GND
|
||||||
re_b = pin_position("res", 1, 448, 368) # (464, 464) = GND
|
re_b = pin_position("res", 1, 448, 368) # (464, 464) = GND
|
||||||
|
|
||||||
# RB1 from Vcc rail to base. Vertical, placed to the left of Q1.
|
# RB1 from Vcc rail to base. res at (240, 240): pinA=(256, 256), pinB=(256, 336)
|
||||||
# Want pinB at base Y (336). res at (240, 240): pinA=(256, 256), pinB=(256, 336)
|
|
||||||
rb1_a = pin_position("res", 0, 240, 240) # (256, 256)
|
rb1_a = pin_position("res", 0, 240, 240) # (256, 256)
|
||||||
rb1_b = pin_position("res", 1, 240, 240) # (256, 336)
|
rb1_b = pin_position("res", 1, 240, 240) # (256, 336)
|
||||||
|
|
||||||
# RB2 from base to GND.
|
# RB2 from base to GND. res at (240, 320): pinA=(256, 336), pinB=(256, 416)
|
||||||
# Want pinA at base Y (336). res at (240, 320): pinA=(256, 336), pinB=(256, 416)
|
|
||||||
rb2_b = pin_position("res", 1, 240, 320) # (256, 416)
|
rb2_b = pin_position("res", 1, 240, 320) # (256, 416)
|
||||||
|
|
||||||
# CC_in (input coupling cap) horizontal, connecting input to base.
|
# CC_in (input coupling cap) — VERTICAL (R0) above the base wire.
|
||||||
# cap R90: pinA=origin+(-16,0)→(origin_x, origin_y+16)... wait let me recalculate.
|
# cap R0 at (384, 256): pinA=(400, 256)=input side, pinB=(400, 320)=base side.
|
||||||
# cap R90: pinA offset (16,0) → rotate90 → (0, 16), pinB offset (16,64) → (-64, 16)
|
# Critically, neither pin sits on the horizontal RB1-to-base wire at y=336,
|
||||||
# At origin (368, 320): pinA = (368, 336), pinB = (304, 336)
|
# so the coupling cap is NOT shorted by the bias wire.
|
||||||
# pinA at (368, 336) close to base (400, 336). pinB at (304, 336).
|
ccin_a = pin_position("cap", 0, 384, 256) # (400, 256) = input
|
||||||
# Wire from pinA (368, 336) to base (400, 336).
|
ccin_b = pin_position("cap", 1, 384, 256) # (400, 320) = base side
|
||||||
ccin_a = pin_position("cap", 0, 368, 320, 90) # (368, 336)
|
|
||||||
ccin_b = pin_position("cap", 1, 368, 320, 90) # (304, 336)
|
|
||||||
|
|
||||||
# CC_out (output coupling cap) to the right of collector.
|
# CC_out (output coupling cap) horizontal R90 to the right of collector.
|
||||||
# cap R90 at origin (560, 272): pinA = (560, 288), pinB = (496, 288)
|
# cap R90 at (560, 272): pinA=(560, 288)=output, pinB=(496, 288)=collector side
|
||||||
# pinB near collector (464, 288), pinA extends right to output
|
|
||||||
# Actually pinB = (560-64, 288) = (496, 288). Wire from collector (464,288) to (496,288).
|
|
||||||
ccout_a = pin_position("cap", 0, 560, 272, 90) # (560, 288)
|
ccout_a = pin_position("cap", 0, 560, 272, 90) # (560, 288)
|
||||||
ccout_b = pin_position("cap", 1, 560, 272, 90) # (496, 288)
|
ccout_b = pin_position("cap", 1, 560, 272, 90) # (496, 288)
|
||||||
|
|
||||||
# CE (emitter bypass cap) parallel to RE.
|
# CE (emitter bypass cap) parallel to RE.
|
||||||
# Place to the right of RE. cap at (528, 384): pinA=(544, 384), pinB=(544, 448)
|
# cap at (528, 384): pinA=(544, 384)=emitter, pinB=(544, 448)=GND
|
||||||
ce_a = pin_position("cap", 0, 528, 384) # (544, 384)
|
ce_a = pin_position("cap", 0, 528, 384) # (544, 384)
|
||||||
ce_b = pin_position("cap", 1, 528, 384) # (544, 448)
|
ce_b = pin_position("cap", 1, 528, 384) # (544, 448)
|
||||||
|
|
||||||
@ -627,30 +621,30 @@ def generate_common_emitter_amp(
|
|||||||
vin_n = pin_position("voltage", 1, 80, 288) # (80, 384)
|
vin_n = pin_position("voltage", 1, 80, 288) # (80, 384)
|
||||||
|
|
||||||
# === WIRING ===
|
# === WIRING ===
|
||||||
# Vcc rail at y=208 — route RIGHT from Vcc+ first (avoid crossing Vcc- at 80,192)
|
# Vcc rail at y=208 — route RIGHT from Vcc+ (avoid crossing Vcc- at 80,192)
|
||||||
vcc_y = rc_a[1] # 208
|
vcc_y = rc_a[1] # 208
|
||||||
sch.add_wire(vcc_p[0], vcc_p[1], 160, vcc_p[1]) # (80,112) → (160,112)
|
sch.add_wire(vcc_p[0], vcc_p[1], 160, vcc_p[1]) # (80,112)→(160,112)
|
||||||
sch.add_wire(160, vcc_p[1], 160, vcc_y) # (160,112) → (160,208)
|
sch.add_wire(160, vcc_p[1], 160, vcc_y) # (160,112)→(160,208)
|
||||||
sch.add_wire(160, vcc_y, rc_a[0], vcc_y) # (160,208) → (464,208) = RC top
|
sch.add_wire(160, vcc_y, rc_a[0], vcc_y) # (160,208)→(464,208) = RC top
|
||||||
sch.add_wire(rb1_a[0], rb1_a[1], rb1_a[0], vcc_y) # RB1 top up to rail
|
sch.add_wire(rb1_a[0], rb1_a[1], rb1_a[0], vcc_y) # RB1 top up to rail
|
||||||
sch.add_wire(rb1_a[0], vcc_y, rc_a[0], vcc_y) # connect to rail
|
sch.add_wire(rb1_a[0], vcc_y, rc_a[0], vcc_y) # connect to rail
|
||||||
|
|
||||||
# RB1 bottom to base, RB2 top at same junction
|
# RB1 bottom → base (horizontal at y=336)
|
||||||
sch.add_wire(rb1_b[0], rb1_b[1], qb[0], qb[1]) # RB1 bottom → base
|
sch.add_wire(rb1_b[0], rb1_b[1], qb[0], qb[1])
|
||||||
|
|
||||||
# CC_in to base
|
# CC_in base side → base junction (short vertical drop)
|
||||||
sch.add_wire(ccin_a[0], ccin_a[1], qb[0], qb[1]) # CC_in right → base
|
sch.add_wire(ccin_b[0], ccin_b[1], qb[0], qb[1]) # (400,320)→(400,336)
|
||||||
|
|
||||||
# Input: Vin+ to CC_in left
|
# Vin+ → CC_in input side via manhattan L-route
|
||||||
sch.add_wire(vin_p[0], vin_p[1], ccin_b[0], ccin_b[1]) # Vin+ → CC_in left
|
sch.add_wire(vin_p[0], vin_p[1], ccin_a[0], vin_p[1]) # (80,304)→(400,304)
|
||||||
|
sch.add_wire(ccin_a[0], vin_p[1], ccin_a[0], ccin_a[1]) # (400,304)→(400,256)
|
||||||
|
|
||||||
# Collector to CC_out
|
# Collector → CC_out left pin
|
||||||
sch.add_wire(qc[0], qc[1], ccout_b[0], ccout_b[1]) # collector → CC_out left
|
sch.add_wire(qc[0], qc[1], ccout_b[0], ccout_b[1])
|
||||||
|
|
||||||
# Emitter to CE (bypass cap in parallel with RE)
|
# Emitter → CE top (bypass cap in parallel with RE)
|
||||||
sch.add_wire(qe[0], qe[1], ce_a[0], ce_a[1]) # emitter → CE top
|
sch.add_wire(qe[0], qe[1], ce_a[0], ce_a[1])
|
||||||
# CE bottom to RE bottom (shared GND rail)
|
# CE bottom → GND (separate ground flag, no diagonal wire to RE)
|
||||||
sch.add_wire(ce_b[0], ce_b[1], re_b[0], re_b[1]) # CE bot → RE bot
|
|
||||||
|
|
||||||
# === COMPONENTS ===
|
# === COMPONENTS ===
|
||||||
sch.add_component("npn", "Q1", bjt_model, 400, 288)
|
sch.add_component("npn", "Q1", bjt_model, 400, 288)
|
||||||
@ -658,7 +652,7 @@ def generate_common_emitter_amp(
|
|||||||
sch.add_component("res", "RE", re, 448, 368)
|
sch.add_component("res", "RE", re, 448, 368)
|
||||||
sch.add_component("res", "RB1", rb1, 240, 240)
|
sch.add_component("res", "RB1", rb1, 240, 240)
|
||||||
sch.add_component("res", "RB2", rb2, 240, 320)
|
sch.add_component("res", "RB2", rb2, 240, 320)
|
||||||
sch.add_component("cap", "CC1", cc_in, 368, 320, rotation=90)
|
sch.add_component("cap", "CC1", cc_in, 384, 256)
|
||||||
sch.add_component("cap", "CC2", cc_out, 560, 272, rotation=90)
|
sch.add_component("cap", "CC2", cc_out, 560, 272, rotation=90)
|
||||||
sch.add_component("cap", "CE", ce, 528, 384)
|
sch.add_component("cap", "CE", ce, 528, 384)
|
||||||
sch.add_component("voltage", "Vcc", vcc, 80, 96)
|
sch.add_component("voltage", "Vcc", vcc, 80, 96)
|
||||||
@ -669,6 +663,7 @@ def generate_common_emitter_amp(
|
|||||||
sch.add_ground(*vin_n)
|
sch.add_ground(*vin_n)
|
||||||
sch.add_ground(*rb2_b)
|
sch.add_ground(*rb2_b)
|
||||||
sch.add_ground(*re_b)
|
sch.add_ground(*re_b)
|
||||||
|
sch.add_ground(*ce_b) # CE bottom to GND directly (no diagonal wire)
|
||||||
sch.add_net_label("out", *ccout_a)
|
sch.add_net_label("out", *ccout_a)
|
||||||
|
|
||||||
sch.add_directive(".tran 5m", 80, 528)
|
sch.add_directive(".tran 5m", 80, 528)
|
||||||
|
|||||||
@ -180,28 +180,19 @@ class TestColpittsOscillator:
|
|||||||
|
|
||||||
raw = result.raw_data
|
raw = result.raw_data
|
||||||
time_idx = None
|
time_idx = None
|
||||||
# Look for collector voltage
|
|
||||||
vcol_idx = None
|
vcol_idx = None
|
||||||
for var in raw.variables:
|
for var in raw.variables:
|
||||||
if var.name.lower() == "time":
|
if var.name.lower() == "time":
|
||||||
time_idx = var.index
|
time_idx = var.index
|
||||||
elif "collector" in var.name.lower() or var.name.lower() == "v(collector)":
|
elif var.name.lower() == "v(out)":
|
||||||
|
vcol_idx = var.index
|
||||||
|
elif "collector" in var.name.lower() and vcol_idx is None:
|
||||||
vcol_idx = var.index
|
vcol_idx = var.index
|
||||||
|
|
||||||
assert time_idx is not None
|
assert time_idx is not None
|
||||||
|
|
||||||
if vcol_idx is None:
|
|
||||||
# Fall back to any voltage signal that isn't supply
|
|
||||||
for var in raw.variables:
|
|
||||||
if var.name.lower().startswith("v(") and var.name.lower() not in (
|
|
||||||
"v(vcc)",
|
|
||||||
"v(time)",
|
|
||||||
):
|
|
||||||
vcol_idx = var.index
|
|
||||||
break
|
|
||||||
|
|
||||||
assert vcol_idx is not None, (
|
assert vcol_idx is not None, (
|
||||||
f"No suitable signal found. Variables: {[v.name for v in raw.variables]}"
|
f"No V(out) or collector signal. Variables: {[v.name for v in raw.variables]}"
|
||||||
)
|
)
|
||||||
|
|
||||||
sig = np.real(raw.data[vcol_idx])
|
sig = np.real(raw.data[vcol_idx])
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user