tscircuit/ti

ti tscircuit library

Version
1.0.186
License
unset
Stars
0

tests/PositionFeedback_DRV5013.test.tsx

import { beforeAll, describe, expect, test } from "bun:test";
import type {
  AnyCircuitElement,
  SchematicBox,
  SchematicComponent,
  SchematicPort,
  SourcePort,
} from "circuit-json";
import { getSourcePortConnectivityMapFromCircuitJson } from "circuit-json-to-connectivity-map";
import { Circuit } from "@tscircuit/core";
import { PositionFeedback_DRV5013 } from "../lib/subcircuits/PositionFeedback_DRV5013.circuit.tsx";

const SOURCE_SCALE = 0.018278145;
const SOURCE_ORIGIN = { x: 185, y: 215 } as const;

const fromSource = (x: number, y: number) => ({
  x: (x - SOURCE_ORIGIN.x) * SOURCE_SCALE,
  y: (y - SOURCE_ORIGIN.y) * SOURCE_SCALE,
});

let circuitJson: AnyCircuitElement[];
type SourceComponent = Extract<AnyCircuitElement, { type: "source_component" }>;
type SourceNet = Extract<AnyCircuitElement, { type: "source_net" }>;
let sourceComponents: SourceComponent[];
let sourcePorts: SourcePort[];
let schematicComponents: SchematicComponent[];
let schematicPorts: SchematicPort[];
let schematicBoxes: SchematicBox[];

beforeAll(async () => {
  const circuit = new Circuit();
  circuit.add(<PositionFeedback_DRV5013 name="positionFeedback" />);
  await circuit.renderUntilSettled();
  circuitJson = circuit.getCircuitJson();
  sourceComponents = circuitJson.filter(
    (element): element is SourceComponent =>
      element.type === "source_component",
  );
  sourcePorts = circuitJson.filter(
    (element): element is SourcePort => element.type === "source_port",
  );
  schematicComponents = circuitJson.filter(
    (element): element is SchematicComponent =>
      element.type === "schematic_component",
  );
  schematicPorts = circuitJson.filter(
    (element): element is SchematicPort => element.type === "schematic_port",
  );
  schematicBoxes = circuitJson.filter(
    (element): element is SchematicBox => element.type === "schematic_box",
  );
});

const component = (name: string) => {
  const found = sourceComponents.find((element) => element.name === name);
  if (!found) throw new Error(`Missing source component ${name}`);
  return found;
};

const port = (componentName: string, pinNumber: number) => {
  const owner = component(componentName);
  const found = sourcePorts.find(
    (element) =>
      element.source_component_id === owner.source_component_id &&
      element.pin_number === pinNumber,
  );
  if (!found) throw new Error(`Missing ${componentName} pin ${pinNumber}`);
  return found;
};

const schematicComponent = (name: string) => {
  const owner = component(name);
  const found = schematicComponents.find(
    (element) => element.source_component_id === owner.source_component_id,
  );
  if (!found) throw new Error(`Missing schematic component ${name}`);
  return found;
};

const schematicPort = (componentName: string, pinNumber: number) => {
  const sourcePort = port(componentName, pinNumber);
  const found = schematicPorts.find(
    (element) => element.source_port_id === sourcePort.source_port_id,
  );
  if (!found)
    throw new Error(`Missing schematic port ${componentName}.${pinNumber}`);
  return found;
};

const capacitance = (name: string) => {
  const found = component(name);
  if (found.ftype !== "simple_capacitor") {
    throw new Error(`${name} is not a capacitor`);
  }
  return found.capacitance;
};

const resistance = (name: string) => {
  const found = component(name);
  if (found.ftype !== "simple_resistor") {
    throw new Error(`${name} is not a resistor`);
  }
  return found.resistance;
};

describe("TIDA-01389 position feedback extraction", () => {
  test("preserves source designators, values, device MPNs, and centers", () => {
    const expected = {
      U6: { xy: [180, 280], mpn: "DRV5013ADQDBZRQ1" },
      U5: { xy: [180, 140], mpn: "DRV5013ADQDBZRQ1" },
      C13: { xy: [60, 275] },
      C14: { xy: [60, 135] },
      R14: { xy: [270, 310] },
      R15: { xy: [270, 170] },
      J1: { xy: [150, 475] },
      J2: { xy: [190, 475] },
      J4: { xy: [200, 610] },
      R9: { xy: [100, 530] },
    } as const;

    for (const [name, source] of Object.entries(expected)) {
      const sourceComponent = component(name);
      const center = schematicComponent(name).center;
      const [x, y] = source.xy;
      const transformed = fromSource(x, y);
      if ("mpn" in source) {
        expect(sourceComponent.manufacturer_part_number).toBe(source.mpn);
      }
      expect(center.x).toBeCloseTo(transformed.x, 5);
      expect(center.y).toBeCloseTo(transformed.y, 5);
    }

    expect(capacitance("C13")).toBe(0.1e-6);
    expect(capacitance("C14")).toBe(0.1e-6);
    expect(resistance("R14")).toBe(10_000);
    expect(resistance("R15")).toBe(10_000);
    expect(resistance("R9")).toBe(0);

    for (const pullup of ["R14", "R15"]) {
      const centerY = schematicComponent(pullup).center.y;
      expect(schematicPort(pullup, 1).center.y).toBeLessThan(centerY);
      expect(schematicPort(pullup, 2).center.y).toBeGreaterThan(centerY);
    }
  });

  test("uses the data-sheet DBZ pin numbers and names", () => {
    for (const sensor of ["U5", "U6"]) {
      expect(port(sensor, 1).port_hints).toContain("VCC");
      expect(port(sensor, 2).port_hints).toContain("OUT");
      expect(port(sensor, 3).port_hints).toContain("GND");
    }

    for (const name of ["U5", "U6"]) {
      const center = schematicComponent(name).center;
      expect(schematicPort(name, 1).center.x).toBeLessThan(center.x);
      expect(schematicPort(name, 2).center.x).toBeGreaterThan(center.x);
      expect(schematicPort(name, 3).center.x).toBeGreaterThan(center.x);
      expect(schematicPort(name, 2).center.y).toBeGreaterThan(
        schematicPort(name, 3).center.y,
      );
    }

    expect(schematicPort("J1", 1).center.x).toBeLessThan(
      schematicComponent("J1").center.x,
    );
    expect(schematicPort("J2", 1).center.x).toBeGreaterThan(
      schematicComponent("J2").center.x,
    );
    expect(schematicPort("J4", 1).center.x).toBeLessThan(
      schematicComponent("J4").center.x,
    );
    expect(schematicPort("J4", 2).center.y).toBeGreaterThan(
      schematicPort("J4", 1).center.y,
    );
  });

  test("renders both source sections with dashed clearance boundaries", () => {
    expect(schematicBoxes).toHaveLength(2);
    for (const box of schematicBoxes) {
      expect(box.is_dashed).toBeTrue();
    }
  });

  test("uses an inline V_BAT label and native trace-to-ground connections", () => {
    for (const [name, label] of [["J4_VBAT", "V_BAT"]] as const) {
      const sourceTrace = circuitJson.find(
        (element) => element.type === "source_trace" && element.name === name,
      );
      expect(sourceTrace).toBeDefined();
      if (!sourceTrace || sourceTrace.type !== "source_trace") continue;

      expect(sourceTrace.connected_source_port_ids).toHaveLength(2);
      expect(
        circuitJson.some(
          (element) =>
            element.type === "schematic_text" &&
            element.text === label &&
            element.source_trace_id === sourceTrace.source_trace_id,
        ),
      ).toBeTrue();
    }

    for (const name of [
      "U6_GND",
      "C13_GND",
      "U5_GND",
      "C14_GND",
      "J2_GND",
      "J4_GND",
    ]) {
      const sourceTrace = circuitJson.find(
        (element) => element.type === "source_trace" && element.name === name,
      );
      expect(sourceTrace).toBeDefined();
      if (!sourceTrace || sourceTrace.type !== "source_trace") continue;

      expect(sourceTrace.connected_source_port_ids).toHaveLength(1);
      expect(sourceTrace.connected_source_net_ids).toHaveLength(1);
      const groundNet = circuitJson.find(
        (element) =>
          element.type === "source_net" &&
          element.source_net_id === sourceTrace.connected_source_net_ids[0],
      );
      expect(groundNet).toMatchObject({ name: "GND", is_ground: true });
    }

    expect(
      circuitJson.filter(
        (element) =>
          element.type === "schematic_net_label" && element.text === "GND",
      ),
    ).toHaveLength(5);
    expect(
      circuitJson.some(
        (element) =>
          element.type === "schematic_net_label" && element.text === "V_BAT",
      ),
    ).toBeFalse();
  });

  test("connects both Hall outputs and both supply rails end to end", () => {
    const connectivity =
      getSourcePortConnectivityMapFromCircuitJson(circuitJson);
    const areConnected = (pins: ReadonlyArray<readonly [string, number]>) =>
      connectivity.areAllIdsConnected(
        pins.map(([name, pinNumber]) => port(name, pinNumber).source_port_id),
      );

    expect(
      areConnected([
        ["U6", 2],
        ["R14", 1],
        ["J2", 6],
      ]),
    ).toBeTrue();
    expect(
      areConnected([
        ["J4", 1],
        ["J2", 1],
      ]),
    ).toBeTrue();

    const vBatNet = circuitJson.find(
      (element): element is SourceNet =>
        element.type === "source_net" && element.name === "V_BAT",
    );
    expect(vBatNet).toBeDefined();
    expect(
      connectivity.areAllIdsConnected([
        port("J4", 2).source_port_id,
        vBatNet!.source_net_id,
      ]),
    ).toBeTrue();
    expect(
      areConnected([
        ["U5", 2],
        ["R15", 1],
        ["J2", 7],
      ]),
    ).toBeTrue();
    expect(
      areConnected([
        ["U6", 1],
        ["U5", 1],
        ["C13", 1],
        ["C14", 1],
        ["R14", 2],
        ["R15", 2],
        ["R9", 2],
      ]),
    ).toBeTrue();
    expect(
      areConnected([
        ["R9", 1],
        ["J1", 1],
      ]),
    ).toBeTrue();
    expect(
      areConnected([
        ["U6", 3],
        ["U5", 3],
        ["C13", 2],
        ["C14", 2],
        ["J2", 1],
      ]),
    ).toBeTrue();

    const outputPorts = sourcePorts
      .filter(
        (element) =>
          element.source_component_id == null &&
          (element.name === "HALL_1" || element.name === "HALL_2"),
      )
      .map((element) => element.name);
    expect(outputPorts).toEqual(["HALL_1", "HALL_2", "HALL_1", "HALL_2"]);
  });

  test("renders without circuit errors", () => {
    const errors = circuitJson.filter((element) =>
      element.type.endsWith("_error"),
    );
    expect(errors).toEqual([]);
  });
});