Source code for jitxlib.landpatterns.ipc

from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
from typing import TYPE_CHECKING, ClassVar, Literal, Self
import math

from jitx.context import Context
from jitx.toleranced import Toleranced
from jitx._structural import Structurable

if TYPE_CHECKING:
    from .leads.fillets import LeadFillets


[docs] class DensityLevel(Enum): """Density Level This enum specifies the density level for footprints on a PCB. Higher density levels indicate less space between landpatterns. These are defined in IPC-7351. """ A = "A" """Density Level A Maximum land protrusion """ B = "B" """Density Level B Median (nominal) land protrusion """ C = "C" """Density Level C Minimum land protrusion """
[docs] @dataclass class IPCRequirements: """IPC Formula Results from Section 3 of IPC 7351B This class contains the results of the IPC formula for computing the pad size. TODO: Add Diagram here """ Zmax: float """Maximum overall length""" Gmin: float """Minimum distance between pads""" Xmin: float """Minimum pad width"""
[docs] def pad_size(self) -> tuple[float, float]: """Compute the pad dimensions from this IPC result Returns: The pad dimensions as (y, x)""" return (0.5 * (self.Zmax - self.Gmin), self.Xmin)
[docs] def compute_ipc( leadSpan: Toleranced, leadLength: Toleranced, leadWidth: Toleranced, fillets: LeadFillets, ) -> IPCRequirements: """Compute Pad Geometry According to IPC Rules Args: leadSpan: edge-of-lead to edge-of-lead distance for an IC package. leadLength: length of the exposed contact in the same dimension as `leadSpan` leadWidth: width of the exposed contact in the dimension orthogonal to `leadSpan` fillets: Specifications for the solder fillets created when soldering the lead to the land-pattern pad. These parameters define extra spacing around the lead dimension to form these fillets. Returns: IPCResult containing the results of the IPC formula """ Jt = fillets.toe Jh = fillets.heel Js = fillets.side Lmax = leadSpan.max_value Lmin = leadSpan.min_value Wmin = leadWidth.min_value Tmin = leadLength.min_value Smax = Lmax - 2.0 * Tmin C_L = leadSpan.range() C_W = leadWidth.range() C_T = leadLength.range() C_S = math.sqrt(C_L * C_L + C_T * C_T) # the distance from edge of land to edge of land on the exterior of the land pattern Zmax = Lmin + 2.0 * Jt + C_L # the distance from edge of land to edge of land on the interior of the land pattern Gmin = Smax - 2.0 * Jh - C_S # the size of the land in the dimension orthogonal to Z and G. Xmin = Wmin + 2.0 * Js + C_W return IPCRequirements(Zmax, Gmin, Xmin)
[docs] @dataclass(frozen=True) class DensityLevelContext(Context): _global_default: ClassVar[DensityLevelContext] density_level: DensityLevel
[docs] @classmethod def get(cls) -> DensityLevelContext: # do not construct a new object here, it'll taint caching mechanisms. return super().get() or DensityLevelContext._global_default
DensityLevelContext._global_default = DensityLevelContext(DensityLevel.C) # make use of contexts, so much be instantiated in context.
[docs] class DensityLevelMixin(Structurable): __density_level: DensityLevel | None = None __density_level_ctx: DensityLevel if not TYPE_CHECKING: def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.__density_level_ctx = DensityLevelContext.get().density_level
[docs] def density_level( self, density_level: DensityLevel | Literal["A"] | Literal["B"] | Literal["C"] ) -> Self: if isinstance(density_level, str): density_level = DensityLevel[density_level] self.__density_level = density_level return self
@property def _density_level(self) -> DensityLevel: return self.__density_level or self.__density_level_ctx