kirr has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmocom-bb/+/40049?usp=email )
Change subject: trx_toolkit/clck_gen: Split it into clck_gen and _clck_gen modules ......................................................................
trx_toolkit/clck_gen: Split it into clck_gen and _clck_gen modules
clck_gen will remain at Python while _clck_gen will be later converted to Cython for speed to avoid py-related overhead. This patch does only plain code movement as a preparatory step for that.
Change-Id: If90f5b6718d6a24eda6221e52dc4626197f57356 --- A src/target/trx_toolkit/_clck_gen.py M src/target/trx_toolkit/clck_gen.py 2 files changed, 112 insertions(+), 91 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmocom-bb refs/changes/49/40049/1
diff --git a/src/target/trx_toolkit/_clck_gen.py b/src/target/trx_toolkit/_clck_gen.py new file mode 100644 index 0000000..9d9fd15 --- /dev/null +++ b/src/target/trx_toolkit/_clck_gen.py @@ -0,0 +1,111 @@ +# TRX Toolkit +# Simple TDMA frame clock generator +# +# (C) 2017-2019 by Vadim Yanitskiy axilirator@gmail.com +# +# All Rights Reserved +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +import logging as log +import time +import os +import sys + +from gsm_shared import * + + +ns = 1e-9 +us = 1e-6 + + +class CLCKGen: + # GSM TDMA definitions + SEC_DELAY_US = 1000 * 1000 + GSM_FRAME_US = 4615.0 + + def __init__(self, clck_links, clck_start = 0, ind_period = 102): + self.clck_links = clck_links + self.ind_period = ind_period + self.clck_start = clck_start + + # Calculate counter time + self.ctr_interval = self.GSM_FRAME_US + self.ctr_interval /= self.SEC_DELAY_US + self._t_tick = int(self.ctr_interval // ns) + + # Initialize timer fd + self._timerfd = os.timerfd_create(time.CLOCK_MONOTONIC) + + # (Optional) clock consumer + self.clck_handler = None + + def __del__(self): + os.close(self._timerfd) + + @property + def running(self): + t_next, _ = os.timerfd_gettime_ns(self._timerfd) + return (t_next != 0) + + def start(self): + # (Re)set the clock counter + self.clck_src = self.clck_start + + # start timer fd + os.timerfd_settime_ns(self._timerfd, initial=self._t_tick, interval=self._t_tick) + + def stop(self): + # stop timer fd + os.timerfd_settime_ns(self._timerfd, initial=0, interval=0) + + # tick must be called periodically by CLCKGen user. + # + # It waits for the next GSM frame to happen and emits corresponding clock indication at that time. + # It also runs attached .clck_handler if there is one. + # + # It is possible to use .tick in both blocking and non-blocking ways: + # + # - without extra care .tick will block waiting for the next GSM frame as explained above, + # - client code can also poll/select on ._timerfd to wait for GSM frame. + # After ._timerfd becomes ready it is guaranteed that the next .tick call will not block. + def tick(self): + # run .send_clck_ind() every .ctr_interval + # NOTE timerfd is careful not to accumulate timing error when organizing the clock loop + + _ = os.read(self._timerfd, 8) + assert len(_) == 8, len(_) + ticks = int.from_bytes(_, byteorder=sys.byteorder) + assert ticks > 0, ticks + if ticks > 1: + log.warning("CLCKGen: time overrun by %dus; resetting the clock" % ((ticks-1)*self._t_tick * ns // us)) + # (the kernel does clock reset by itself) + + self.send_clck_ind() + + def send_clck_ind(self): + # We don't need to send so often + if self.clck_src % self.ind_period == 0: + # Create UDP payload + payload = "IND CLOCK %u\0" % self.clck_src + + # Send indication to all UDP links + for link in self.clck_links: + link.send(payload) + + # Debug print + log.debug(payload.rstrip("\0")) + + if self.clck_handler is not None: + self.clck_handler(self.clck_src) + + # Increase frame count (modular arithmetic) + self.clck_src = (self.clck_src + 1) % GSM_HYPERFRAME diff --git a/src/target/trx_toolkit/clck_gen.py b/src/target/trx_toolkit/clck_gen.py index a223572..c40446b 100755 --- a/src/target/trx_toolkit/clck_gen.py +++ b/src/target/trx_toolkit/clck_gen.py @@ -21,103 +21,13 @@ APP_CR_HOLDERS = [("2017-2019", "Vadim Yanitskiy axilirator@gmail.com")]
import logging as log -import time import signal -import os -import sys
+from _clck_gen import CLCKGen from app_common import ApplicationBase from udp_link import UDPLink -from gsm_shared import *
-ns = 1e-9 -us = 1e-6 - - -class CLCKGen: - # GSM TDMA definitions - SEC_DELAY_US = 1000 * 1000 - GSM_FRAME_US = 4615.0 - - def __init__(self, clck_links, clck_start = 0, ind_period = 102): - self.clck_links = clck_links - self.ind_period = ind_period - self.clck_start = clck_start - - # Calculate counter time - self.ctr_interval = self.GSM_FRAME_US - self.ctr_interval /= self.SEC_DELAY_US - self._t_tick = int(self.ctr_interval // ns) - - # Initialize timer fd - self._timerfd = os.timerfd_create(time.CLOCK_MONOTONIC) - - # (Optional) clock consumer - self.clck_handler = None - - def __del__(self): - os.close(self._timerfd) - - @property - def running(self): - t_next, _ = os.timerfd_gettime_ns(self._timerfd) - return (t_next != 0) - - def start(self): - # (Re)set the clock counter - self.clck_src = self.clck_start - - # start timer fd - os.timerfd_settime_ns(self._timerfd, initial=self._t_tick, interval=self._t_tick) - - def stop(self): - # stop timer fd - os.timerfd_settime_ns(self._timerfd, initial=0, interval=0) - - # tick must be called periodically by CLCKGen user. - # - # It waits for the next GSM frame to happen and emits corresponding clock indication at that time. - # It also runs attached .clck_handler if there is one. - # - # It is possible to use .tick in both blocking and non-blocking ways: - # - # - without extra care .tick will block waiting for the next GSM frame as explained above, - # - client code can also poll/select on ._timerfd to wait for GSM frame. - # After ._timerfd becomes ready it is guaranteed that the next .tick call will not block. - def tick(self): - # run .send_clck_ind() every .ctr_interval - # NOTE timerfd is careful not to accumulate timing error when organizing the clock loop - - _ = os.read(self._timerfd, 8) - assert len(_) == 8, len(_) - ticks = int.from_bytes(_, byteorder=sys.byteorder) - assert ticks > 0, ticks - if ticks > 1: - log.warning("CLCKGen: time overrun by %dus; resetting the clock" % ((ticks-1)*self._t_tick * ns // us)) - # (the kernel does clock reset by itself) - - self.send_clck_ind() - - def send_clck_ind(self): - # We don't need to send so often - if self.clck_src % self.ind_period == 0: - # Create UDP payload - payload = "IND CLOCK %u\0" % self.clck_src - - # Send indication to all UDP links - for link in self.clck_links: - link.send(payload) - - # Debug print - log.debug(payload.rstrip("\0")) - - if self.clck_handler is not None: - self.clck_handler(self.clck_src) - - # Increase frame count (modular arithmetic) - self.clck_src = (self.clck_src + 1) % GSM_HYPERFRAME - # Just a wrapper for independent usage class Application(ApplicationBase): def __init__(self):