Skip to content

Commit

Permalink
derive framrate from clock now upon start.
Browse files Browse the repository at this point in the history
  • Loading branch information
mgineer85 committed Oct 22, 2024
1 parent d562673 commit ac57399
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 31 deletions.
8 changes: 4 additions & 4 deletions node/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@

from .config import appconfig
from .services.camera_service import Picamera2Service
from .services.gpio_primary import GpioPrimaryService
from .services.gpio_secondary import GpioSecondaryService
from .services.gpio_primary_clockwork import GpioPrimaryClockworkService
from .services.gpio_secondary_node import GpioSecondaryNodeService
from .services.synced_camera import SyncedCameraService

logger = logging.getLogger(__name__)

# container
gpio_primary_service = GpioPrimaryService(config=appconfig.primary_gpio)
gpio_primary_service = GpioPrimaryClockworkService(config=appconfig.primary_gpio)
picamera2_service = Picamera2Service(config=appconfig.picamera2)
gpio_secondary_service = GpioSecondaryService(config=appconfig.secondary_gpio)
gpio_secondary_service = GpioSecondaryNodeService(config=appconfig.secondary_gpio)
synced_camera_service = SyncedCameraService(picamera2_service, gpio_secondary_service)


Expand Down
6 changes: 3 additions & 3 deletions node/config/appconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from pydantic import PrivateAttr
from pydantic_settings import BaseSettings, SettingsConfigDict

from .models import ConfigGpioPrimary, ConfigGpioSecondary, ConfigPicamera2
from .models import ConfigGpioPrimaryClockwork, ConfigGpioSecondaryNode, ConfigPicamera2


class AppConfig(BaseSettings):
Expand All @@ -23,8 +23,8 @@ class AppConfig(BaseSettings):
_processed_at: datetime = PrivateAttr(default_factory=datetime.now) # private attributes

# groups -> setting items
primary_gpio: ConfigGpioPrimary = ConfigGpioPrimary()
secondary_gpio: ConfigGpioSecondary = ConfigGpioSecondary()
primary_gpio: ConfigGpioPrimaryClockwork = ConfigGpioPrimaryClockwork()
secondary_gpio: ConfigGpioSecondaryNode = ConfigGpioSecondaryNode()
picamera2: ConfigPicamera2 = ConfigPicamera2()

model_config = SettingsConfigDict(
Expand Down
4 changes: 2 additions & 2 deletions node/config/models.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
from pydantic import BaseModel, Field


class ConfigGpioPrimary(BaseModel):
class ConfigGpioPrimaryClockwork(BaseModel):
enable_primary_gpio: bool = Field(default=False)
# clock_out_pin_name: str = Field(default="GPIO18") # replaced by sysfs, need update
trigger_out_pin_name: str = Field(default="GPIO17")
ext_trigger_in_pin_name: str = Field(default="GPIO4")
FPS_NOMINAL: int = Field(default=10)


class ConfigGpioSecondary(BaseModel):
class ConfigGpioSecondaryNode(BaseModel):
chip: str = Field(default="/dev/gpiochip0")
clock_in_pin_name: str = Field(default="GPIO14")
trigger_in_pin_name: str = Field(default="GPIO15")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from gpiozero import Button as ZeroButton
from gpiozero import DigitalOutputDevice

from ..config.models import ConfigGpioPrimary
from ..config.models import ConfigGpioPrimaryClockwork

logger = logging.getLogger(__name__)

Expand All @@ -25,10 +25,10 @@ def _fire_held(self):
self._fire_events(self.pin_factory.ticks(), False)


class GpioPrimaryService:
def __init__(self, config: ConfigGpioPrimary):
class GpioPrimaryClockworkService:
def __init__(self, config: ConfigGpioPrimaryClockwork):
# init arguments
self._config: ConfigGpioPrimary = config
self._config: ConfigGpioPrimaryClockwork = config

# define private props
self._trigger_out: DigitalOutputDevice = None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

import gpiod

from ..config.models import ConfigGpioSecondary
from ..config.models import ConfigGpioSecondaryNode

logger = logging.getLogger(__name__)


class GpioSecondaryService:
def __init__(self, config: ConfigGpioSecondary):
class GpioSecondaryNodeService:
def __init__(self, config: ConfigGpioSecondaryNode):
# init with arguments
self._config: ConfigGpioSecondary = config
self._config: ConfigGpioSecondaryNode = config

# private props
self._gpiod_chip = None
Expand Down Expand Up @@ -45,18 +45,24 @@ def clock_signal_valid(self) -> bool:
return False
return (time.monotonic_ns() - self._clock_in_timestamp_ns) < TIMEOUT_CLOCK_SIGNAL_INVALID

def get_nominal_framerate(self) -> int:
return 10 # TODO: implement derive from clock

def _derive_framerate_from_clock(self) -> float:
"""calc the framerate derived by monitoring the clock signal for some time.
def derive_nominal_framerate_from_clock(self) -> int:
"""calc the framerate derived by monitoring the clock signal for 11 ticks (means 10 intervals).
needs to set the nominal frame duration running freely while no adjustments are made to sync up
Returns:
float: _description_
int: _description_
"""
pass
# TODO: implement.
try:
first_timestamp_ns = self.wait_for_clock_signal(timeout=1)
for _ in range(10):
last_timestamp_ns = self.wait_for_clock_signal(timeout=1)
except TimeoutError as exc:
raise RuntimeError("no clock, cannot derive nominal framerate!") from exc
else:
duration_10_ticks_s = (last_timestamp_ns - first_timestamp_ns) * 1.0e-9
fps = round(duration_10_ticks_s * 10.0)

return fps

def _on_clock_in(self):
with self._clock_in_condition:
Expand Down
14 changes: 8 additions & 6 deletions node/services/synced_camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@
from threading import Thread

from .camera_service import Picamera2Service
from .gpio_secondary import GpioSecondaryService
from .gpio_secondary_node import GpioSecondaryNodeService

logger = logging.getLogger(__name__)

FPS_NOMINAL = 10


class SyncedCameraService:
def __init__(self, camera_service: Picamera2Service, gpio_service: GpioSecondaryService):
def __init__(self, camera_service: Picamera2Service, gpio_service: GpioSecondaryNodeService):
# init the arguments
self._camera_service: Picamera2Service = camera_service
self._gpio_service: GpioSecondaryService = gpio_service
self._gpio_service: GpioSecondaryNodeService = gpio_service

# define private props
self._sync_thread: Thread = None
Expand All @@ -26,7 +24,11 @@ def start(self):
self._wait_for_clock()
print("got it, continue starting...")

self._camera_service.start(nominal_framerate=FPS_NOMINAL)
print("deriving nominal framerate from clock signal, counting 10 ticks...")
derived_fps = self._gpio_service.derive_nominal_framerate_from_clock()
print(f"got it, derived {derived_fps}fps...")

self._camera_service.start(nominal_framerate=derived_fps)

self._sync_thread = Thread(name="_sync_thread", target=self._sync_fun, args=(), daemon=True)
self._sync_thread.start()
Expand Down

0 comments on commit ac57399

Please sign in to comment.