Skip to content
This repository has been archived by the owner on Dec 10, 2024. It is now read-only.

Lib: Filters & Signals & Delayed construction #30

Merged
merged 4 commits into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions examples/signal_processing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# This file is part of the faebryk project
# SPDX-License-Identifier: MIT

"""
This file contains a faebryk sample.
"""

import logging

import typer

import faebryk.library._F as F
from faebryk.core.module import Module
from faebryk.core.util import specialize_module
from faebryk.libs.examples.buildutil import apply_design_to_pcb
from faebryk.libs.logging import setup_basic_logging
from faebryk.libs.units import P

logger = logging.getLogger(__name__)


class App(Module):
lowpass: F.Filter

def __preinit__(self) -> None:
# TODO actually do something with the filter

# Parametrize
self.lowpass.cutoff_frequency.merge(200 * P.Hz)
self.lowpass.response.merge(F.Filter.Response.LOWPASS)

# Specialize
special = specialize_module(self.lowpass, F.FilterElectricalLC())

# Construct
special.get_trait(F.has_construction_dependency).construct()


def main():
logger.info("Building app")
app = App()

logger.info("Export")
apply_design_to_pcb(app)


if __name__ == "__main__":
setup_basic_logging()
logger.info("Running experiment")

typer.run(main)
28 changes: 28 additions & 0 deletions src/faebryk/core/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,24 +305,52 @@ def op(a, b):
def __add__(self: "Parameter[PV]", other: "Parameter[PV]"):
return self.arithmetic_op(self, other, lambda a, b: a + b)

@_resolved
def __radd__(self: "Parameter[PV]", other: "Parameter[PV]"):
return self.arithmetic_op(self, other, lambda a, b: b + a)

@_resolved
def __sub__(self: "Parameter[PV]", other: "Parameter[PV]"):
return self.arithmetic_op(self, other, lambda a, b: a - b)

@_resolved
def __rsub__(self: "Parameter[PV]", other: "Parameter[PV]"):
return self.arithmetic_op(self, other, lambda a, b: b - a)

# TODO PV | float
@_resolved
def __mul__(self: "Parameter[PV]", other: "Parameter[PV]"):
return self.arithmetic_op(self, other, lambda a, b: a * b)

@_resolved
def __rmul__(self: "Parameter[PV]", other: "Parameter[PV]"):
return self.arithmetic_op(self, other, lambda a, b: b * a)

# TODO PV | float
@_resolved
def __truediv__(self: "Parameter[PV]", other: "Parameter[PV]"):
return self.arithmetic_op(self, other, lambda a, b: a / b)

@_resolved
def __rtruediv__(self: "Parameter[PV]", other: "Parameter[PV]"):
return self.arithmetic_op(self, other, lambda a, b: b / a)

@_resolved
def __pow__(self: "Parameter[PV]", other: "Parameter[PV]") -> "Parameter[PV]":
return self.arithmetic_op(self, other, lambda a, b: a**b)

@_resolved
def __rpow__(self: "Parameter[PV]", other: "Parameter[PV]") -> "Parameter[PV]":
return self.arithmetic_op(self, other, lambda a, b: b**a)

@_resolved
def __and__(self: "Parameter[PV]", other: "Parameter[PV]") -> "Parameter[PV]":
return self.intersect(self, other)

@_resolved
def __rand__(self: "Parameter[PV]", other: "Parameter[PV]") -> "Parameter[PV]":
return self.intersect(other, self)

def get_most_narrow(self) -> "Parameter[PV]":
out = self.get_narrowing_chain()[-1]

Expand Down
4 changes: 1 addition & 3 deletions src/faebryk/library/ElectricLogic.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from faebryk.libs.library import L


class ElectricLogic(F.Logic):
class ElectricLogic(F.SignalElectrical, F.Logic):
class has_pulls(F.Logic.TraitT):
@abstractmethod
def get_pulls(self) -> tuple[F.Resistor | None, F.Resistor | None]: ...
Expand Down Expand Up @@ -98,8 +98,6 @@ class PushPull(Enum):
OPEN_SOURCE = auto()

push_pull: F.TBD[PushPull]
reference: F.ElectricPower
signal: F.Electrical

@L.rt_field
def single_electric_reference(self):
Expand Down
23 changes: 23 additions & 0 deletions src/faebryk/library/Filter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# This file is part of the faebryk project
# SPDX-License-Identifier: MIT

from enum import Enum, auto

import faebryk.library._F as F
from faebryk.core.module import Module


class Filter(Module):
class Response(Enum):
LOWPASS = auto()
HIGHPASS = auto()
BANDPASS = auto()
BANDSTOP = auto()
OTHER = auto()

cutoff_frequency: F.TBD[float]
order: F.TBD[int]
response: F.TBD[Response]

in_: F.Signal
out: F.Signal
57 changes: 57 additions & 0 deletions src/faebryk/library/FilterElectricalLC.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# This file is part of the faebryk project
# SPDX-License-Identifier: MIT

import math

import faebryk.library._F as F
from faebryk.libs.library import L
from faebryk.libs.units import P


class FilterElectricalLC(F.Filter):
in_: F.SignalElectrical
out: F.SignalElectrical
capacitor: F.Capacitor
inductor: F.Inductor

def __preinit__(self) -> None: ...

@L.rt_field
def construction_dependency(self):
class _(F.has_construction_dependency.impl()):
def _construct(_self):
if F.Constant(F.Filter.Response.LOWPASS).is_subset_of(self.response):
self.response.merge(F.Filter.Response.LOWPASS)

# TODO other orders
self.order.merge(2)

L = self.inductor.inductance
C = self.capacitor.capacitance
fc = self.cutoff_frequency

# TODO requires parameter constraint solving implemented
# fc.merge(1 / (2 * math.pi * math.sqrt(C * L)))

# instead assume fc being the driving param
realistic_C = F.Range(1 * P.pF, 1 * P.mF)
L.merge(1 / ((2 * math.pi * fc) ** 2 * realistic_C))
C.merge(1 / ((2 * math.pi * fc) ** 2 * L))

# TODO consider splitting C / L in a typical way

# low pass
self.in_.signal.connect_via(
(self.inductor, self.capacitor),
self.in_.reference.lv,
)

self.in_.signal.connect_via(self.inductor, self.out.signal)
return

if isinstance(self.response, F.Constant):
raise F.has_construction_dependency.NotConstructableEver()

raise F.has_construction_dependency.NotConstructableYet()

return _()
3 changes: 1 addition & 2 deletions src/faebryk/library/Logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
# SPDX-License-Identifier: MIT

import faebryk.library._F as F
from faebryk.core.moduleinterface import ModuleInterface
from faebryk.libs.library import L


class Logic(ModuleInterface):
class Logic(F.Signal):
state = L.f_field(F.Range)(False, True)

def set(self, on: bool):
Expand Down
7 changes: 7 additions & 0 deletions src/faebryk/library/Signal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# This file is part of the faebryk project
# SPDX-License-Identifier: MIT

from faebryk.core.moduleinterface import ModuleInterface


class Signal(ModuleInterface): ...
11 changes: 11 additions & 0 deletions src/faebryk/library/SignalElectrical.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# This file is part of the faebryk project
# SPDX-License-Identifier: MIT

import faebryk.library._F as F


class SignalElectrical(F.Signal):
# line is a better name, but for compatibility with Logic we use signal
# might change in future
signal: F.Electrical
reference: F.ElectricPower
11 changes: 8 additions & 3 deletions src/faebryk/library/_F.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
from faebryk.library.is_esphome_bus import is_esphome_bus
from faebryk.library.has_single_electric_reference import has_single_electric_reference
from faebryk.library.Power import Power
from faebryk.library.Signal import Signal
from faebryk.library.has_construction_dependency import has_construction_dependency
from faebryk.library.has_footprint import has_footprint
from faebryk.library.Mechanical import Mechanical
from faebryk.library.has_overriden_name import has_overriden_name
Expand All @@ -45,11 +47,12 @@
from faebryk.library.is_representable_by_single_value import is_representable_by_single_value
from faebryk.library.ANY import ANY
from faebryk.library.Electrical import Electrical
from faebryk.library.Logic import Logic
from faebryk.library.Set import Set
from faebryk.library.has_esphome_config_defined import has_esphome_config_defined
from faebryk.library.is_esphome_bus_defined import is_esphome_bus_defined
from faebryk.library.has_single_electric_reference_defined import has_single_electric_reference_defined
from faebryk.library.Filter import Filter
from faebryk.library.Logic import Logic
from faebryk.library.Footprint import Footprint
from faebryk.library.has_overriden_name_defined import has_overriden_name_defined
from faebryk.library.has_linked_pad_defined import has_linked_pad_defined
Expand Down Expand Up @@ -137,13 +140,16 @@
from faebryk.library.Battery import Battery
from faebryk.library.Comparator import Comparator
from faebryk.library.Crystal_Oscillator import Crystal_Oscillator
from faebryk.library.ElectricLogic import ElectricLogic
from faebryk.library.Fan import Fan
from faebryk.library.LED import LED
from faebryk.library.OpAmp import OpAmp
from faebryk.library.RS485_Bus_Protection import RS485_Bus_Protection
from faebryk.library.SignalElectrical import SignalElectrical
from faebryk.library.USB_Type_C_Receptacle_16_pin import USB_Type_C_Receptacle_16_pin
from faebryk.library.ButtonCell import ButtonCell
from faebryk.library.PoweredLED import PoweredLED
from faebryk.library.ElectricLogic import ElectricLogic
from faebryk.library.FilterElectricalLC import FilterElectricalLC
from faebryk.library.ElectricLogicGate import ElectricLogicGate
from faebryk.library.GenericBusProtection import GenericBusProtection
from faebryk.library.I2C import I2C
Expand All @@ -161,7 +167,6 @@
from faebryk.library.XL_3528RGBW_WS2812B import XL_3528RGBW_WS2812B
from faebryk.library.can_switch_power import can_switch_power
from faebryk.library.pf_74AHCT2G125 import pf_74AHCT2G125
from faebryk.library.PoweredLED import PoweredLED
from faebryk.library.ElectricLogicGates import ElectricLogicGates
from faebryk.library.Logic74xx import Logic74xx
from faebryk.library.BH1750FVI_TR import BH1750FVI_TR
Expand Down
35 changes: 35 additions & 0 deletions src/faebryk/library/has_construction_dependency.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# This file is part of the faebryk project
# SPDX-License-Identifier: MIT

from abc import abstractmethod

from faebryk.core.trait import Trait


# TODO this is still very early phase
# need to write somewhere the auto construct logic
# either trigger it by param merge or at pick time or so
class has_construction_dependency(Trait):
class NotConstructable(Exception): ...

class NotConstructableYet(NotConstructable): ...

class NotConstructableEver(NotConstructable): ...

def __preinit__(self) -> None:
self.executed = False

def construct(self):
if self.executed:
return
self._construct()
self._fullfill()

@abstractmethod
def _construct(self): ...

def _fullfill(self):
self.executed = True

def is_implemented(self):
return not self.executed
2 changes: 1 addition & 1 deletion src/faebryk/library/has_descriptive_properties_defined.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def get_properties(self) -> dict[str, str]:
return self.properties

def handle_duplicate(self, other: TraitImpl, node: Node) -> bool:
if not isinstance(other, F.has_descriptive_properties_defined):
if not isinstance(other, has_descriptive_properties_defined):
self.properties.update(other.get_properties())
return super().handle_duplicate(other, node)

Expand Down
2 changes: 1 addition & 1 deletion src/faebryk/library/has_multi_picker.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def add_pickers_by_type[T](
for i, k in enumerate(picker_types):
v = lookup[k]
module.add(
F.has_multi_picker(
has_multi_picker(
# most specific first
prio + i,
picker_factory(v),
Expand Down