From 94bd7046e1083090100261cdd6ab5272c9307d50 Mon Sep 17 00:00:00 2001 From: Abse2001 Date: Fri, 7 Aug 2026 15:31:21 +0200 Subject: [PATCH 1/2] Fix asymmetric SOT-343 pads --- src/fn/sot343.ts | 32 +++++++++++++------ src/footprinter.ts | 4 ++- tests/__snapshots__/sot343.snap.svg | 2 +- ...43_c151520_asymmetric_emitter_pad.snap.svg | 1 + .../sot343_pl1.2_pw0.9_p2_w5.2_h5.snap.svg | 2 +- tests/sot343.test.ts | 18 ++++++++++- 6 files changed, 45 insertions(+), 14 deletions(-) create mode 100644 tests/__snapshots__/sot343_c151520_asymmetric_emitter_pad.snap.svg diff --git a/src/fn/sot343.ts b/src/fn/sot343.ts index 1f39ff6ca..e26fcd4a1 100644 --- a/src/fn/sot343.ts +++ b/src/fn/sot343.ts @@ -28,9 +28,12 @@ export const sot343_def = base_def.extend({ num_pins: z.number().default(4), w: z.string().default("3.2mm"), h: z.string().default("2.6mm"), - pl: z.string().default("1.05mm"), - pw: z.string().default("0.45mm"), - p: z.string().default("0.55mm"), + pl: z.string().default("0.7mm"), + pw: z.string().default("0.7mm"), + p: z.string().default("2mm"), + px: z.string().default("1.3mm"), + pin2padwidth: z.string().default("0.9mm"), + pin2centeroffsetx: z.string().default("-0.15mm"), string: z.string().optional(), }) @@ -38,7 +41,7 @@ export const sot343 = ( raw_params: z.input, ): { circuitJson: AnyCircuitElement[]; parameters: any } => { const match = raw_params.string?.match(/^sot343_(\d+)/) - const numPins = match ? Number.parseInt(match[1]!, 4) : 4 + const numPins = match ? Number.parseInt(match[1]!, 10) : 4 const parameters = sot343_def.parse({ ...raw_params, @@ -62,12 +65,14 @@ export const getCcwSot343Coords = (parameters: { h: number pl: number p: number + px: number + pin2centeroffsetx: number }) => { - const { pn, p } = parameters - if (pn === 1) return { x: -p * 1.92, y: -0.65 } - if (pn === 2) return { x: -p * 1.92, y: 0.65 } - if (pn === 3) return { x: p, y: 0.65 } - if (pn === 4) return { x: p, y: -0.65 } + const { pn, p, px, pin2centeroffsetx } = parameters + if (pn === 1) return { x: -px / 2, y: -p / 2 } + if (pn === 2) return { x: px / 2 + pin2centeroffsetx, y: -p / 2 } + if (pn === 3) return { x: px / 2, y: p / 2 } + if (pn === 4) return { x: -px / 2, y: p / 2 } return { x: 0, y: 0 } } @@ -79,6 +84,9 @@ export const sot343_4 = (parameters: z.infer) => { const pl = Number.parseFloat(parameters.pl) const pw = Number.parseFloat(parameters.pw) const p = Number.parseFloat(parameters.p) + const px = Number.parseFloat(parameters.px) + const pin2PadWidth = Number.parseFloat(parameters.pin2padwidth) + const pin2CenterOffsetX = Number.parseFloat(parameters.pin2centeroffsetx) const cornerRadius = Math.min(pl, pw) / 8 let minX = Infinity @@ -94,8 +102,12 @@ export const sot343_4 = (parameters: z.infer) => { h, pl, p, + px, + pin2centeroffsetx: pin2CenterOffsetX, }) - pads.push(rectpad(i + 1, x, y, pl, pw, cornerRadius)) + pads.push( + rectpad(i + 1, x, y, i === 1 ? pin2PadWidth : pl, pw, cornerRadius), + ) if (x < minX) minX = x if (x > maxX) maxX = x diff --git a/src/footprinter.ts b/src/footprinter.ts index 6275af648..ce8214875 100644 --- a/src/footprinter.ts +++ b/src/footprinter.ts @@ -311,7 +311,9 @@ export type Footprinter = { sot: () => FootprinterParamsBuilder<"w" | "h" | "p" | "pl" | "pw"> sot323: () => FootprinterParamsBuilder<"w" | "h" | "p" | "pl" | "pw"> sot89: () => FootprinterParamsBuilder<"w" | "p" | "pl" | "pw" | "h"> - sot343: () => FootprinterParamsBuilder<"w" | "h" | "p" | "pl" | "pw"> + sot343: () => FootprinterParamsBuilder< + "w" | "h" | "p" | "px" | "pl" | "pw" | "pin2padwidth" | "pin2centeroffsetx" + > sod323w: () => FootprinterParamsBuilder<"w" | "h" | "p" | "pl" | "pw"> smc: () => FootprinterParamsBuilder<"w" | "h" | "p" | "pw" | "pl"> minimelf: () => FootprinterParamsBuilder<"w" | "h" | "p" | "pw" | "pl"> diff --git a/tests/__snapshots__/sot343.snap.svg b/tests/__snapshots__/sot343.snap.svg index 18e0a0f43..f28ca108e 100644 --- a/tests/__snapshots__/sot343.snap.svg +++ b/tests/__snapshots__/sot343.snap.svg @@ -1 +1 @@ -{REF} \ No newline at end of file +{REF} \ No newline at end of file diff --git a/tests/__snapshots__/sot343_c151520_asymmetric_emitter_pad.snap.svg b/tests/__snapshots__/sot343_c151520_asymmetric_emitter_pad.snap.svg new file mode 100644 index 000000000..043cf65b4 --- /dev/null +++ b/tests/__snapshots__/sot343_c151520_asymmetric_emitter_pad.snap.svg @@ -0,0 +1 @@ +{REF} \ No newline at end of file diff --git a/tests/__snapshots__/sot343_pl1.2_pw0.9_p2_w5.2_h5.snap.svg b/tests/__snapshots__/sot343_pl1.2_pw0.9_p2_w5.2_h5.snap.svg index b0ada0f2c..b76b30646 100644 --- a/tests/__snapshots__/sot343_pl1.2_pw0.9_p2_w5.2_h5.snap.svg +++ b/tests/__snapshots__/sot343_pl1.2_pw0.9_p2_w5.2_h5.snap.svg @@ -1 +1 @@ -{REF} \ No newline at end of file +{REF} \ No newline at end of file diff --git a/tests/sot343.test.ts b/tests/sot343.test.ts index 881c76296..7652428ef 100644 --- a/tests/sot343.test.ts +++ b/tests/sot343.test.ts @@ -1,4 +1,4 @@ -import { test, expect } from "bun:test" +import { expect, test } from "bun:test" import { convertCircuitJsonToPcbSvg } from "circuit-to-svg" import { fp } from "../src/footprinter" @@ -16,3 +16,19 @@ test("sot343_pl1.2_pw0.9_p2_w5.2_h5", () => { "sot343_pl1.2_pw0.9_p2_w5.2_h5", ) }) + +test("sot343 matches the asymmetric C151520 land pattern", () => { + const circuitJson = fp.string("sot343_rounded0").circuitJson() + const pads = circuitJson.filter((element) => element.type === "pcb_smtpad") + + expect(pads).toHaveLength(4) + expect(pads[0]).toMatchObject({ x: -0.65, y: -1, width: 0.7, height: 0.7 }) + expect(pads[1]).toMatchObject({ x: 0.5, y: -1, width: 0.9, height: 0.7 }) + expect(pads[2]).toMatchObject({ x: 0.65, y: 1, width: 0.7, height: 0.7 }) + expect(pads[3]).toMatchObject({ x: -0.65, y: 1, width: 0.7, height: 0.7 }) + + expect(convertCircuitJsonToPcbSvg(circuitJson)).toMatchSvgSnapshot( + import.meta.path, + "sot343_c151520_asymmetric_emitter_pad", + ) +}) From 5882d6bbcf5ca9e3eceb47edf935b03c175381ad Mon Sep 17 00:00:00 2001 From: Abse2001 Date: Fri, 7 Aug 2026 15:55:31 +0200 Subject: [PATCH 2/2] Preserve standard SOT-343 geometry --- src/fn/index.ts | 1 + src/fn/sot343.ts | 32 ++++-------- src/fn/sot343bfp650.ts | 52 +++++++++++++++++++ src/footprinter.ts | 5 +- tests/__snapshots__/sot343.snap.svg | 2 +- ...43_c151520_asymmetric_emitter_pad.snap.svg | 1 - .../sot343_pl1.2_pw0.9_p2_w5.2_h5.snap.svg | 2 +- .../sot343bfp650_c151520.snap.svg | 1 + tests/sot343.test.ts | 18 +------ tests/sot343bfp650.test.ts | 19 +++++++ 10 files changed, 88 insertions(+), 45 deletions(-) create mode 100644 src/fn/sot343bfp650.ts delete mode 100644 tests/__snapshots__/sot343_c151520_asymmetric_emitter_pad.snap.svg create mode 100644 tests/__snapshots__/sot343bfp650_c151520.snap.svg create mode 100644 tests/sot343bfp650.test.ts diff --git a/src/fn/index.ts b/src/fn/index.ts index b719f9a49..45b960968 100644 --- a/src/fn/index.ts +++ b/src/fn/index.ts @@ -93,6 +93,7 @@ export { smdpinheader } from "./smdpinheader" export { platedhole } from "./platedhole" export { sot } from "./sot" export { sot343 } from "./sot343" +export { sot343bfp650 } from "./sot343bfp650" export { m2host } from "./m2host" export { mountedpcbmodule } from "./mountedpcbmodule" export { to92l } from "./to92l" diff --git a/src/fn/sot343.ts b/src/fn/sot343.ts index e26fcd4a1..1f39ff6ca 100644 --- a/src/fn/sot343.ts +++ b/src/fn/sot343.ts @@ -28,12 +28,9 @@ export const sot343_def = base_def.extend({ num_pins: z.number().default(4), w: z.string().default("3.2mm"), h: z.string().default("2.6mm"), - pl: z.string().default("0.7mm"), - pw: z.string().default("0.7mm"), - p: z.string().default("2mm"), - px: z.string().default("1.3mm"), - pin2padwidth: z.string().default("0.9mm"), - pin2centeroffsetx: z.string().default("-0.15mm"), + pl: z.string().default("1.05mm"), + pw: z.string().default("0.45mm"), + p: z.string().default("0.55mm"), string: z.string().optional(), }) @@ -41,7 +38,7 @@ export const sot343 = ( raw_params: z.input, ): { circuitJson: AnyCircuitElement[]; parameters: any } => { const match = raw_params.string?.match(/^sot343_(\d+)/) - const numPins = match ? Number.parseInt(match[1]!, 10) : 4 + const numPins = match ? Number.parseInt(match[1]!, 4) : 4 const parameters = sot343_def.parse({ ...raw_params, @@ -65,14 +62,12 @@ export const getCcwSot343Coords = (parameters: { h: number pl: number p: number - px: number - pin2centeroffsetx: number }) => { - const { pn, p, px, pin2centeroffsetx } = parameters - if (pn === 1) return { x: -px / 2, y: -p / 2 } - if (pn === 2) return { x: px / 2 + pin2centeroffsetx, y: -p / 2 } - if (pn === 3) return { x: px / 2, y: p / 2 } - if (pn === 4) return { x: -px / 2, y: p / 2 } + const { pn, p } = parameters + if (pn === 1) return { x: -p * 1.92, y: -0.65 } + if (pn === 2) return { x: -p * 1.92, y: 0.65 } + if (pn === 3) return { x: p, y: 0.65 } + if (pn === 4) return { x: p, y: -0.65 } return { x: 0, y: 0 } } @@ -84,9 +79,6 @@ export const sot343_4 = (parameters: z.infer) => { const pl = Number.parseFloat(parameters.pl) const pw = Number.parseFloat(parameters.pw) const p = Number.parseFloat(parameters.p) - const px = Number.parseFloat(parameters.px) - const pin2PadWidth = Number.parseFloat(parameters.pin2padwidth) - const pin2CenterOffsetX = Number.parseFloat(parameters.pin2centeroffsetx) const cornerRadius = Math.min(pl, pw) / 8 let minX = Infinity @@ -102,12 +94,8 @@ export const sot343_4 = (parameters: z.infer) => { h, pl, p, - px, - pin2centeroffsetx: pin2CenterOffsetX, }) - pads.push( - rectpad(i + 1, x, y, i === 1 ? pin2PadWidth : pl, pw, cornerRadius), - ) + pads.push(rectpad(i + 1, x, y, pl, pw, cornerRadius)) if (x < minX) minX = x if (x > maxX) maxX = x diff --git a/src/fn/sot343bfp650.ts b/src/fn/sot343bfp650.ts new file mode 100644 index 000000000..666de7b24 --- /dev/null +++ b/src/fn/sot343bfp650.ts @@ -0,0 +1,52 @@ +import type { AnyCircuitElement, PcbCourtyardRect } from "circuit-json" +import { z } from "zod" +import { rectpad } from "../helpers/rectpad" +import { silkscreenRef } from "../helpers/silkscreenRef" +import { silkscreenpath } from "../helpers/silkscreenpath" +import { base_def } from "../helpers/zod/base_def" + +export const sot343bfp650_def = base_def.extend({ + fn: z.literal("sot343bfp650"), + num_pins: z.literal(4).default(4), +}) + +export const sot343bfp650 = ( + rawParams: z.input, +): { circuitJson: AnyCircuitElement[]; parameters: any } => { + const parameters = sot343bfp650_def.parse(rawParams) + const pads = [ + rectpad(1, -0.65, -1, 0.7, 0.7), + rectpad(2, 0.5, -1, 0.9, 0.7), + rectpad(3, 0.65, 1, 0.7, 0.7), + rectpad(4, -0.65, 1, 0.7, 0.7), + ] + const silkscreen = [ + silkscreenpath([ + { x: -1, y: -0.2 }, + { x: -1, y: 0.2 }, + ]), + silkscreenpath([ + { x: 1, y: 0.2 }, + { x: 1, y: -0.2 }, + ]), + ] + const courtyard: PcbCourtyardRect = { + type: "pcb_courtyard_rect", + pcb_courtyard_rect_id: "", + pcb_component_id: "", + center: { x: 0, y: 0 }, + width: 3, + height: 3.2, + layer: "top", + } + + return { + circuitJson: [ + ...pads, + ...silkscreen, + silkscreenRef(0, 2.1, 0.4), + courtyard, + ], + parameters, + } +} diff --git a/src/footprinter.ts b/src/footprinter.ts index ce8214875..625d84cda 100644 --- a/src/footprinter.ts +++ b/src/footprinter.ts @@ -311,9 +311,8 @@ export type Footprinter = { sot: () => FootprinterParamsBuilder<"w" | "h" | "p" | "pl" | "pw"> sot323: () => FootprinterParamsBuilder<"w" | "h" | "p" | "pl" | "pw"> sot89: () => FootprinterParamsBuilder<"w" | "p" | "pl" | "pw" | "h"> - sot343: () => FootprinterParamsBuilder< - "w" | "h" | "p" | "px" | "pl" | "pw" | "pin2padwidth" | "pin2centeroffsetx" - > + sot343: () => FootprinterParamsBuilder<"w" | "h" | "p" | "pl" | "pw"> + sot343bfp650: () => FootprinterParamsBuilder sod323w: () => FootprinterParamsBuilder<"w" | "h" | "p" | "pl" | "pw"> smc: () => FootprinterParamsBuilder<"w" | "h" | "p" | "pw" | "pl"> minimelf: () => FootprinterParamsBuilder<"w" | "h" | "p" | "pw" | "pl"> diff --git a/tests/__snapshots__/sot343.snap.svg b/tests/__snapshots__/sot343.snap.svg index f28ca108e..18e0a0f43 100644 --- a/tests/__snapshots__/sot343.snap.svg +++ b/tests/__snapshots__/sot343.snap.svg @@ -1 +1 @@ -{REF} \ No newline at end of file +{REF} \ No newline at end of file diff --git a/tests/__snapshots__/sot343_c151520_asymmetric_emitter_pad.snap.svg b/tests/__snapshots__/sot343_c151520_asymmetric_emitter_pad.snap.svg deleted file mode 100644 index 043cf65b4..000000000 --- a/tests/__snapshots__/sot343_c151520_asymmetric_emitter_pad.snap.svg +++ /dev/null @@ -1 +0,0 @@ -{REF} \ No newline at end of file diff --git a/tests/__snapshots__/sot343_pl1.2_pw0.9_p2_w5.2_h5.snap.svg b/tests/__snapshots__/sot343_pl1.2_pw0.9_p2_w5.2_h5.snap.svg index b76b30646..b0ada0f2c 100644 --- a/tests/__snapshots__/sot343_pl1.2_pw0.9_p2_w5.2_h5.snap.svg +++ b/tests/__snapshots__/sot343_pl1.2_pw0.9_p2_w5.2_h5.snap.svg @@ -1 +1 @@ -{REF} \ No newline at end of file +{REF} \ No newline at end of file diff --git a/tests/__snapshots__/sot343bfp650_c151520.snap.svg b/tests/__snapshots__/sot343bfp650_c151520.snap.svg new file mode 100644 index 000000000..fcd68e133 --- /dev/null +++ b/tests/__snapshots__/sot343bfp650_c151520.snap.svg @@ -0,0 +1 @@ +{REF} \ No newline at end of file diff --git a/tests/sot343.test.ts b/tests/sot343.test.ts index 7652428ef..881c76296 100644 --- a/tests/sot343.test.ts +++ b/tests/sot343.test.ts @@ -1,4 +1,4 @@ -import { expect, test } from "bun:test" +import { test, expect } from "bun:test" import { convertCircuitJsonToPcbSvg } from "circuit-to-svg" import { fp } from "../src/footprinter" @@ -16,19 +16,3 @@ test("sot343_pl1.2_pw0.9_p2_w5.2_h5", () => { "sot343_pl1.2_pw0.9_p2_w5.2_h5", ) }) - -test("sot343 matches the asymmetric C151520 land pattern", () => { - const circuitJson = fp.string("sot343_rounded0").circuitJson() - const pads = circuitJson.filter((element) => element.type === "pcb_smtpad") - - expect(pads).toHaveLength(4) - expect(pads[0]).toMatchObject({ x: -0.65, y: -1, width: 0.7, height: 0.7 }) - expect(pads[1]).toMatchObject({ x: 0.5, y: -1, width: 0.9, height: 0.7 }) - expect(pads[2]).toMatchObject({ x: 0.65, y: 1, width: 0.7, height: 0.7 }) - expect(pads[3]).toMatchObject({ x: -0.65, y: 1, width: 0.7, height: 0.7 }) - - expect(convertCircuitJsonToPcbSvg(circuitJson)).toMatchSvgSnapshot( - import.meta.path, - "sot343_c151520_asymmetric_emitter_pad", - ) -}) diff --git a/tests/sot343bfp650.test.ts b/tests/sot343bfp650.test.ts new file mode 100644 index 000000000..4dfbdcfef --- /dev/null +++ b/tests/sot343bfp650.test.ts @@ -0,0 +1,19 @@ +import { expect, test } from "bun:test" +import { convertCircuitJsonToPcbSvg } from "circuit-to-svg" +import { fp } from "../src/footprinter" + +test("sot343bfp650 matches C151520", () => { + const circuitJson = fp.string("sot343bfp650").circuitJson() + const pads = circuitJson.filter((element) => element.type === "pcb_smtpad") + + expect(pads).toHaveLength(4) + expect(pads[0]).toMatchObject({ x: -0.65, y: -1, width: 0.7, height: 0.7 }) + expect(pads[1]).toMatchObject({ x: 0.5, y: -1, width: 0.9, height: 0.7 }) + expect(pads[2]).toMatchObject({ x: 0.65, y: 1, width: 0.7, height: 0.7 }) + expect(pads[3]).toMatchObject({ x: -0.65, y: 1, width: 0.7, height: 0.7 }) + + expect(convertCircuitJsonToPcbSvg(circuitJson)).toMatchSvgSnapshot( + import.meta.path, + "sot343bfp650_c151520", + ) +})