We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/lamaalrajih/kicad-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""
Utility functions for working with KiCad component values and properties.
"""
import re
from typing import Any, Optional, Tuple, Union, Dict
def extract_voltage_from_regulator(value: str) -> str:
"""Extract output voltage from a voltage regulator part number or description.
Args:
value: Regulator part number or description
Returns:
Extracted voltage as a string or "unknown" if not found
"""
# Common patterns:
# 78xx/79xx series: 7805 = 5V, 7812 = 12V
# LDOs often have voltage in the part number, like LM1117-3.3
# 78xx/79xx series
match = re.search(r'78(\d\d)|79(\d\d)', value, re.IGNORECASE)
if match:
group = match.group(1) or match.group(2)
# Convert code to voltage (e.g., 05 -> 5V, 12 -> 12V)
try:
voltage = int(group)
# For 78xx series, voltage code is directly in volts
if voltage < 50: # Sanity check to prevent weird values
return f"{voltage}V"
except ValueError:
pass
# Look for common voltage indicators in the string
voltage_patterns = [
r'(\d+\.?\d*)V', # 3.3V, 5V, etc.
r'-(\d+\.?\d*)V', # -5V, -12V, etc. (for negative regulators)
r'(\d+\.?\d*)[_-]?V', # 3.3_V, 5-V, etc.
r'[_-](\d+\.?\d*)', # LM1117-3.3, LD1117-3.3, etc.
]
for pattern in voltage_patterns:
match = re.search(pattern, value, re.IGNORECASE)
if match:
try:
voltage = float(match.group(1))
if 0 < voltage < 50: # Sanity check
# Format as integer if it's a whole number
if voltage.is_integer():
return f"{int(voltage)}V"
else:
return f"{voltage}V"
except ValueError:
pass
# Check for common fixed voltage regulators
regulators = {
"LM7805": "5V",
"LM7809": "9V",
"LM7812": "12V",
"LM7905": "-5V",
"LM7912": "-12V",
"LM1117-3.3": "3.3V",
"LM1117-5": "5V",
"LM317": "Adjustable",
"LM337": "Adjustable (Negative)",
"AP1117-3.3": "3.3V",
"AMS1117-3.3": "3.3V",
"L7805": "5V",
"L7812": "12V",
"MCP1700-3.3": "3.3V",
"MCP1700-5.0": "5V"
}
for reg, volt in regulators.items():
if re.search(re.escape(reg), value, re.IGNORECASE):
return volt
return "unknown"
def extract_frequency_from_value(value: str) -> str:
"""Extract frequency information from a component value or description.
Args:
value: Component value or description (e.g., "16MHz", "Crystal 8MHz")
Returns:
Frequency as a string or "unknown" if not found
"""
# Common frequency patterns with various units
frequency_patterns = [
r'(\d+\.?\d*)[\s-]*([kKmMgG]?)[hH][zZ]', # 16MHz, 32.768 kHz, etc.
r'(\d+\.?\d*)[\s-]*([kKmMgG])', # 16M, 32.768k, etc.
]
for pattern in frequency_patterns:
match = re.search(pattern, value, re.IGNORECASE)
if match:
try:
freq = float(match.group(1))
unit = match.group(2).upper() if match.group(2) else ""
# Make sure the frequency is in a reasonable range
if freq > 0:
# Format the output
if unit == "K":
if freq >= 1000:
return f"{freq/1000:.3f}MHz"
else:
return f"{freq:.3f}kHz"
elif unit == "M":
if freq >= 1000:
return f"{freq/1000:.3f}GHz"
else:
return f"{freq:.3f}MHz"
elif unit == "G":
return f"{freq:.3f}GHz"
else: # No unit, need to determine based on value
if freq < 1000:
return f"{freq:.3f}Hz"
elif freq < 1000000:
return f"{freq/1000:.3f}kHz"
elif freq < 1000000000:
return f"{freq/1000000:.3f}MHz"
else:
return f"{freq/1000000000:.3f}GHz"
except ValueError:
pass
# Check for common crystal frequencies
if "32.768" in value or "32768" in value:
return "32.768kHz" # Common RTC crystal
elif "16M" in value or "16MHZ" in value.upper():
return "16MHz" # Common MCU crystal
elif "8M" in value or "8MHZ" in value.upper():
return "8MHz"
elif "20M" in value or "20MHZ" in value.upper():
return "20MHz"
elif "27M" in value or "27MHZ" in value.upper():
return "27MHz"
elif "25M" in value or "25MHZ" in value.upper():
return "25MHz"
return "unknown"
def extract_resistance_value(value: str) -> Tuple[Optional[float], Optional[str]]:
"""Extract resistance value and unit from component value.
Args:
value: Resistance value (e.g., "10k", "4.7k", "100")
Returns:
Tuple of (numeric value, unit) or (None, None) if parsing fails
"""
# Common resistance patterns
# 10k, 4.7k, 100R, 1M, 10, etc.
match = re.search(r'(\d+\.?\d*)([kKmMrRΩ]?)', value)
if match:
try:
resistance = float(match.group(1))
unit = match.group(2).upper() if match.group(2) else "Ω"
# Normalize unit
if unit == "R" or unit == "":
unit = "Ω"
return resistance, unit
except ValueError:
pass
# Handle special case like "4k7" (means 4.7k)
match = re.search(r'(\d+)[kKmM](\d+)', value)
if match:
try:
value1 = int(match.group(1))
value2 = int(match.group(2))
resistance = float(f"{value1}.{value2}")
unit = "k" if "k" in value.lower() else "M" if "m" in value.lower() else "Ω"
return resistance, unit
except ValueError:
pass
return None, None
def extract_capacitance_value(value: str) -> Tuple[Optional[float], Optional[str]]:
"""Extract capacitance value and unit from component value.
Args:
value: Capacitance value (e.g., "10uF", "4.7nF", "100pF")
Returns:
Tuple of (numeric value, unit) or (None, None) if parsing fails
"""
# Common capacitance patterns
# 10uF, 4.7nF, 100pF, etc.
match = re.search(r'(\d+\.?\d*)([pPnNuUμF]+)', value)
if match:
try:
capacitance = float(match.group(1))
unit = match.group(2).lower()
# Normalize unit
if "p" in unit or "pf" in unit:
unit = "pF"
elif "n" in unit or "nf" in unit:
unit = "nF"
elif "u" in unit or "μ" in unit or "uf" in unit or "μf" in unit:
unit = "μF"
else:
unit = "F"
return capacitance, unit
except ValueError:
pass
# Handle special case like "4n7" (means 4.7nF)
match = re.search(r'(\d+)[pPnNuUμ](\d+)', value)
if match:
try:
value1 = int(match.group(1))
value2 = int(match.group(2))
capacitance = float(f"{value1}.{value2}")
if "p" in value.lower():
unit = "pF"
elif "n" in value.lower():
unit = "nF"
elif "u" in value.lower() or "μ" in value:
unit = "μF"
else:
unit = "F"
return capacitance, unit
except ValueError:
pass
return None, None
def extract_inductance_value(value: str) -> Tuple[Optional[float], Optional[str]]:
"""Extract inductance value and unit from component value.
Args:
value: Inductance value (e.g., "10uH", "4.7nH", "100mH")
Returns:
Tuple of (numeric value, unit) or (None, None) if parsing fails
"""
# Common inductance patterns
# 10uH, 4.7nH, 100mH, etc.
match = re.search(r'(\d+\.?\d*)([pPnNuUμmM][hH])', value)
if match:
try:
inductance = float(match.group(1))
unit = match.group(2).lower()
# Normalize unit
if "p" in unit:
unit = "pH"
elif "n" in unit:
unit = "nH"
elif "u" in unit or "μ" in unit:
unit = "μH"
elif "m" in unit:
unit = "mH"
else:
unit = "H"
return inductance, unit
except ValueError:
pass
# Handle special case like "4u7" (means 4.7uH)
match = re.search(r'(\d+)[pPnNuUμmM](\d+)[hH]', value)
if match:
try:
value1 = int(match.group(1))
value2 = int(match.group(2))
inductance = float(f"{value1}.{value2}")
if "p" in value.lower():
unit = "pH"
elif "n" in value.lower():
unit = "nH"
elif "u" in value.lower() or "μ" in value:
unit = "μH"
elif "m" in value.lower():
unit = "mH"
else:
unit = "H"
return inductance, unit
except ValueError:
pass
return None, None
def format_resistance(resistance: float, unit: str) -> str:
"""Format resistance value with appropriate unit.
Args:
resistance: Resistance value
unit: Unit string (Ω, k, M)
Returns:
Formatted resistance string
"""
if unit == "Ω":
return f"{resistance:.0f}Ω" if resistance.is_integer() else f"{resistance}Ω"
elif unit == "k":
return f"{resistance:.0f}kΩ" if resistance.is_integer() else f"{resistance}kΩ"
elif unit == "M":
return f"{resistance:.0f}MΩ" if resistance.is_integer() else f"{resistance}MΩ"
else:
return f"{resistance}{unit}"
def format_capacitance(capacitance: float, unit: str) -> str:
"""Format capacitance value with appropriate unit.
Args:
capacitance: Capacitance value
unit: Unit string (pF, nF, μF, F)
Returns:
Formatted capacitance string
"""
if capacitance.is_integer():
return f"{int(capacitance)}{unit}"
else:
return f"{capacitance}{unit}"
def format_inductance(inductance: float, unit: str) -> str:
"""Format inductance value with appropriate unit.
Args:
inductance: Inductance value
unit: Unit string (pH, nH, μH, mH, H)
Returns:
Formatted inductance string
"""
if inductance.is_integer():
return f"{int(inductance)}{unit}"
else:
return f"{inductance}{unit}"
def normalize_component_value(value: str, component_type: str) -> str:
"""Normalize a component value string based on component type.
Args:
value: Raw component value string
component_type: Type of component (R, C, L, etc.)
Returns:
Normalized value string
"""
if component_type == "R":
resistance, unit = extract_resistance_value(value)
if resistance is not None and unit is not None:
return format_resistance(resistance, unit)
elif component_type == "C":
capacitance, unit = extract_capacitance_value(value)
if capacitance is not None and unit is not None:
return format_capacitance(capacitance, unit)
elif component_type == "L":
inductance, unit = extract_inductance_value(value)
if inductance is not None and unit is not None:
return format_inductance(inductance, unit)
# For other component types or if parsing fails, return the original value
return value
def get_component_type_from_reference(reference: str) -> str:
"""Determine component type from reference designator.
Args:
reference: Component reference (e.g., R1, C2, U3)
Returns:
Component type letter (R, C, L, Q, etc.)
"""
# Extract the alphabetic prefix (component type)
match = re.match(r'^([A-Za-z_]+)', reference)
if match:
return match.group(1)
return ""
def is_power_component(component: Dict[str, Any]) -> bool:
"""Check if a component is likely a power-related component.
Args:
component: Component information dictionary
Returns:
True if the component is power-related, False otherwise
"""
ref = component.get("reference", "")
value = component.get("value", "").upper()
lib_id = component.get("lib_id", "").upper()
# Check reference designator
if ref.startswith(("VR", "PS", "REG")):
return True
# Check for power-related terms in value or library ID
power_terms = ["VCC", "VDD", "GND", "POWER", "PWR", "SUPPLY", "REGULATOR", "LDO"]
if any(term in value or term in lib_id for term in power_terms):
return True
# Check for regulator part numbers
regulator_patterns = [
r"78\d\d", # 7805, 7812, etc.
r"79\d\d", # 7905, 7912, etc.
r"LM\d{3}", # LM317, LM337, etc.
r"LM\d{4}", # LM1117, etc.
r"AMS\d{4}", # AMS1117, etc.
r"MCP\d{4}", # MCP1700, etc.
]
if any(re.search(pattern, value, re.IGNORECASE) for pattern in regulator_patterns):
return True
# Not identified as a power component
return False