1. 16 Dec, 2024 5 commits
    • Kirill Smelkov's avatar
      kpi: Establish data model for DRB.IPLatDl and DRB.UEActive · 3a35162b
      Kirill Smelkov authored
      3GPP says that this values are averages over collected samples and over
      observed time period. But if we are to follow 3GPP as-is there is a problem how
      to aggregate two Measurements corresponding to two different periods into one
      bigger Mesurement that corresponds to combined time period.
      
      -> Solve that by introducing "statistical profile" types and use them for DRB.IPLatDl and DRB.UEActive;
      -> Teach Calc.aggregate to aggregate such statistical profiles via corresponding math.
      
      See individual patches for details.
      
      /reviewed-by @paul.graydon
      /reviewed-on kirr/xlte!9
      3a35162b
    • Kirill Smelkov's avatar
      kpi: Robustify logic in _newscalar · 6d2694d8
      Kirill Smelkov authored
      _newscalar creates a scalar value of type typ with specified dtype.
      Inside it works via creating dtype that will have its .type = typ and
      that's how NumPy knows to instantiate scalars of such dtype via
      specified typ. But at the end _newscalar was checking only that .dtype
      of creatged scalar == original dtype, while we can insist that
      scalar.dtype _is_ the created dtype which itself should be == to
      originally passed dtype.
      
      No change in behaviour, only make the logic more robust and explicit.
      6d2694d8
    • Kirill Smelkov's avatar
      kpi: Explain what .QCI and .CAUSE suffixes mean inside Measurement definition · ca4300ed
      Kirill Smelkov authored
      Cosmetical change done when rereading Measurement description afresh.
      ca4300ed
    • Kirill Smelkov's avatar
      kpi: Establish data model for DRB.UEActive · f47d68ea
      Kirill Smelkov authored
      Similarly to DRB.IPLatDl 3GPP says to use average number of active UEs
      during a period for DRB.UEActive. Which means that we'll also need to
      use statistical profile for this value to be able to aggregate
      measurements. But contrary to DRB.IPLatDl, DRB.UEActive should be
      statistical profile with time-based sampling because the averaging
      happened over time, not over arbitrary general samples.
      
      -> Introduce StatT type, that is similar to Stat, to represent such
         time-based profiling and use it to define DRB.UEActive; Teach
         Calc.aggregate to handle aggregation of such time-based statistical
         profiles via
      
      	    a₁⋅δt₁ + a₂·δt₂
      	A = ───────────────
      	       δt₁ + δt₂
      
         formula.
      
      This patch is partly based on the following patch by Paul Graydon:
      paul.graydon/xlte@eb9f1fa9
      f47d68ea
    • Kirill Smelkov's avatar
      kpi: Establish data model for DRB.IPLatDl.QCI · 7cd9cb91
      Kirill Smelkov authored
      Since the beginning - since dc1d5481 (kpi: Start of the package)
      DRB.IPLatDl.QCI was introduced only in commented form with the following
      remark:
      
              # XXX mean is not good for our model
              # TODO mean -> total + npkt?
              #('DRB.IPLatDl.QCI',                Ttime),     # s         4.4.5.1  32.450:6.3.2   NOTE not ms
      
      The problem here is that if we introduce DRB.IPLatDl.QCI as just Ttime for
      average latency, and we have two measurements m1 and m2 with such
      DRB.IPLatDl, there is no way to know what DRB.IPLatDl should be
      for aggregated measurement - in the aggregated measurement the latency
      should be the mean time - averaged for combined periods of m1+m2, over
      samples of all transmision bursts. And knowing something already averaged in
      period1 and period2 we can compute the average for aggregated measurement
      only if we know both initial averages _and_ the number of samples in each
      period. That's what the "TODO mean -> total + npkt?" comment was about.
      
      Besides DRB.IPLatDl there are many other values that 3GPP say to be
      mean. For example UE.Active and other values. So there is a need to
      uniformly represent such averages somehow and that there is a way to
      also aggregate the averages for combined measurements.
      
      -> Introduce Stat type, that represents results of statistical profiling
         and use it for DRB.IPLatDl.QCI; Teach Calc.aggregate to handle
         aggregation of such statistical profiles via
      
                  a₁⋅n₁ + a₂·n₂
              A = ─────────────
                     n₁ + n₂
      
         formula.
      7cd9cb91
  2. 13 Dec, 2024 3 commits
    • Kirill Smelkov's avatar
      kpi: Rework ΣMeasurement to define itself via dtypes instead of types · 205616f2
      Kirill Smelkov authored
      ΣMeasurement used to extract types of fields of Measurement to define
      same fields of itself. However with upcoming Stat and StatT "generic"
      types, they can be used with several underlying dtypes, e.g.
      Stat(dtype=int32) and Stat(dtype=float64), so using just a type class
      when copying definition of a field will loose information.
      
      -> Fix that by always using dtype of a field as definitive source of
         what it is.
      
      NOTE that dtype has .type attribute which refers to attached type class
      that represents scalars of that dtype.
      205616f2
    • Kirill Smelkov's avatar
      kpi: Calc.aggregate: Add basic test · 89f29e90
      Kirill Smelkov authored
      As marked as TODO in bf96c767 add basic test for Calc.aggregate .
      89f29e90
    • Kirill Smelkov's avatar
      kpi: Rename Calc.sum -> Calc.aggregate · 43250935
      Kirill Smelkov authored
      In bf96c767 (kpi: Add way to compute aggregated counters + showcase
      this) I added Calc.sum with the idea that aggregating measurements is
      summing their values. That is indeed true for cumulative counters, but
      to aggregate e.g. average values of something, one needs to use
      different formula than simple summation. For this reason it is generally
      more correct to name the aggregation method as "aggregate" instead of
      "sum".
      
      -> Do this renaming.
      43250935
  3. 14 Nov, 2024 1 commit
    • Kirill Smelkov's avatar
      amari.{kpi,drb}: Fix multicell handling · 7164e99c
      Kirill Smelkov authored
      1) Fix amari.kpi to handle stats messages in enb.xlog that come with multiple cells.
         Previously such messages were leading to the following errors on KPI calculator place (e.g. Wendelin):
      
              xlte.amari.kpi.LogError: t1731059787.321: stats describes 2 cells;  but only single-cell configurations are supported
      
      2) Fix amari.drb to generate x.drb_stats messages when an UE is associated to multiple cells due to e.g. Carrier Aggregation.
         Previously CA was leading to
      
              raise RuntimeError(("ue #%s belongs to %d cells;  "+
                  "but only single-cell configurations are supported") % (ue_id, len(ju(['cells']))))
      
         error on eNB side.
      
      + minor fixes and enhancements done along the way.
      
      Please see individual patches for details.
      
      An example enb.xlog for eNB with 2 cells and an UE Carrier-Aggregated to both cells is here:
      
      https://lab.nexedi.com/kirr/misc/-/blob/6a04cf3/lte/20241111-2cell.xlog
      
      And here is how it looks when visualized via kpidemo.py :
      
      https://lab.nexedi.com/kirr/misc/-/blob/6a04cf3/lte/20241111-2cell.png
      
      Kirill
      
      /cc @lu.xu
      /reviewed-by @paul.graydon
      /reviewed-on kirr/xlte!7
      7164e99c
  4. 11 Nov, 2024 12 commits
    • Kirill Smelkov's avatar
      kpi: Fix pyflakes · 10837c10
      Kirill Smelkov authored
      ./kpi.py:658:9: local variable 'τ' is assigned to but never used
      
      My mistake from 2824f50d (kpi: Calc: Add support for E-UTRAN IP Throughput KPI)
      10837c10
    • Kirill Smelkov's avatar
      amari.{drb,kpi}: Cosmetics · c18aac68
      Kirill Smelkov authored
      Add explanatory comments and fix typos noticed while working on
      drb/multicell topic.
      c18aac68
    • Kirill Smelkov's avatar
      amari.drb: Add __repr__ to _Utx and _UCtx · 54b4ac47
      Kirill Smelkov authored
      It helps debugging and testing to be able to see UE transmission states
      in human-readable form.
      54b4ac47
    • Kirill Smelkov's avatar
      amari.drb: Teach Sampler to be multicell-aware · b1adc068
      Kirill Smelkov authored
      Since 2a016d48 (Draft support for E-UTRAN IP Throughput KPI) there was a
      hardcoded limitation that x.drb_stats generation works with 1-cell
      configurations only. However we do use multicell eNB configurations and
      on such configurations `xamari xlog x.drb_stats` was failing on eNB
      side with
      
          raise RuntimeError(("ue #%s belongs to %d cells;  "+
              "but only single-cell configurations are supported") % (ue_id, len(ju(['cells']))))
      
      because an UE might be associated with multiple cells of one eNB
      due to e.g. Carrier Aggregation.
      
      Now, after we did preparatory amari.drb refactoring and taught BitSync
      to be multicell-aware, we can finally remove the limitation and correct
      the Sampler to handle UEs associated with multiple cells at the same
      time.
      
      The handling is mostly straightforward: for every frame the Sampler needs
      to estimate time of active transmission and amount of transmitted bytes.
      The transmitted amount is Σcell(tx_bytes) and we estimate transmission
      time on different cells C₁ and C₂ as
      
          tx_time ∈ [max(t₁,t₂), min(t₁+t₂, δt/tti)]
      
      In other words when transmission to/from UE on C₁ and C₂ is going in
      parallel in one frame, the time it takes to transmit current part of UEs
      data
      
          1) cannot be less than time spent on each cell
             (equals to the maximum if transmission on Ci was fully performed
              in the same subframes where transmission on Cj was active)
      
          2) cannot be more than the sum of time spent transmitting on each cell
             (equals to the sum if transmission on C₁ and C₂ was performed in
              distinct subframes of the frame), and
      
          3) cannot be more than the whole frame
             (t₁+t₂ could become more than that if there is some overlap in
              subframes where C₁ and C₂ transmissions were done)
      
      The patch implements this logic when updating data flows of an UE.
      
      Amari.drb now works for multicell configurations.
      
      There are one-cell unit tests for Sampler already added in 2a016d48 and
      now I manually verified that `xamari xlog x.drb_stats` works ok with
      multicell eNB configurations.
      
      Unittests for Sampler in multicell environment are left as TODO.
      b1adc068
    • Kirill Smelkov's avatar
      amari.drb: Rework _BitSync to be multicell-aware · 80d82d4e
      Kirill Smelkov authored
      To be able to handle multicell configurations rework _BitSync to handle
      UEs that might be associated with multiple cells at the same time, e.g.
      in the case of Carrier Aggregation.
      
      For this BitSync interface is reworked correspondingly, then it uses
      tx_bytes total->percell splitter, we added in the previous patch, then
      it uses BitSync1 helpers, which each work on the stream of one
      particular cell, and then the result is combined back into multicell _Utx.
      
      The internals of new BitSync1 are very similar to original old BitSync implementation.
      The new BitSync, that wraps many BitSync1s, is new.
      
      The Sampler still accepts UEs with one cell only.
      In the next patch we will update the Sampler to handle multicell
      configurations as well.
      80d82d4e
    • Kirill Smelkov's avatar
      amari.drb: Add _CTXBytesSplitter to split total tx_bytes into per-cell parts · 9cd06cb9
      Kirill Smelkov authored
      To be able to compute E-UTRAN IP Throughput amari.drb uses Sampler which
      detects and extracts separate transmission samples from 100Hz log of
      ue_get[stats]. The Sampler, in turn uses help from _BitSync for correct
      operations because Amarisoft LTEENB updates counters for dl_total_bytes
      and dl_tx at different times, and _BitSync synchronizes streams of those
      updates in time. _BitSync itself works by correlating amount of
      transmitted data (tx_bytes) and transport blocks (#tx) and shifting some
      amount of #tx to previous frame based on the correlation. See d102ffaa
      (amari.drb: Start of the package) for details.
      
      This works ok for configurations with 1 cell, but does not work out of the box
      for multicell configurations because #tx is per-cell value, while e.g.
      dl_total_bytes is reported by LTEENB only as a value aggregated over all cells.
      That's why original implementation in d102ffaa had an assert that the
      number of cells an UE is associated with is 1.
      
      -> Implement custom filter that splits overall tx_bytes into per-cell
         parts via heuristic based on reported per-cell bitrates to workaround
         that: the more cell bitrate is the more is the part of tx_bytes that
         gets associated to this cell.
      
      In basic implementation the heuristic would be to divide tx_bytes as
      
      	tx_bytes(cell) = tx_bytes·β/Σcells(β)		; β is bitrate of a cell
      
      but given that for every frame _BitSync works by computing things based
      on neighbour frame as well we do it as
      
      	tx_bytes(cell) = tx_bytes·(β₁+β₂)/Σcells(β₁+β₂)
      
      This should make the heuristic a bit more stable.
      
      This patch comes with tx_bytes splitter filter itself only. In the next
      patch we will use _CTXBytesSplitter to implement multicell-awareness for
      _BitSync.
      9cd06cb9
    • Kirill Smelkov's avatar
      amari.drb: Start tracking current bitrate in per-cell UE transmission state · 91967123
      Kirill Smelkov authored
      We will need to know current cell DL/UL bitrate for multicell BitSync in
      the next patches.
      91967123
    • Kirill Smelkov's avatar
      amari.drb: Split _Utx into global and per-cell parts · 26a82c6e
      Kirill Smelkov authored
      Continue preparatory steps to support multicell configurations and for
      that split the class that tracks UE transmission state into global part
      (_Utx) and per-cell parts (_UCtx).
      
      Everywhere else in the code we still assert that the number of cells an UE
      attached to is 1, so no support for multicell yet - only preparatory
      non-functional changes currently.
      26a82c6e
    • Kirill Smelkov's avatar
      amari.drb: Move tracking of #tx and #retx into _Utx · bd57acbb
      Kirill Smelkov authored
      Similarly to previous patch this is preparatory non-functional change to
      support multicell configurations.
      
      Previously the number of transmitted transport blocks was passed around
      as separate argument but with multiple cells each cell will have its own
      information about how many TB were transmitted/received and we will need
      to maintain those .tx and .retx in per-cell data structure.
      
      Start preparing to that by moving .tx and .retx to be tracked in UE
      transmission state instead of being passed around separately.
      
      No support for multicell yet - only preparatory non-functional changes currently.
      bd57acbb
    • Kirill Smelkov's avatar
      amari.drb: Move handling of xl_use_avg and rank from _QCI_Flow to higher level at _UE · beeb9dea
      Kirill Smelkov authored
      Since 2a016d48 (Draft support for E-UTRAN IP Throughput KPI) there was a
      hardcoded limitation that x.drb_stats generation works with 1-cell
      configurations only. However we do use multicell eNB configurations and
      on such configurations `xamari xlog x.drb_stats` was failing on eNB
      side with
      
          raise RuntimeError(("ue #%s belongs to %d cells;  "+
              "but only single-cell configurations are supported") % (ue_id, len(ju(['cells']))))
      
      Start fixing that.
      
      As the first preparatory step to support multiple cells with x.drb_stats
      reorganize amari/drb.py code a bit: _QCI_Flow works at whole QCI
      transmission level which can aggregate and cover multiple cells, but
      xl_use_avg and rank are per cell things and they can be different for
      different cells.
      
      -> Start preparing to handle them in per-cell way by adjusting the code
         to take those values into account at higher level in computation stack
         where we still have cell context.
      
      No support for multicell yet - only preparatory non-functional changes
      currently.
      beeb9dea
    • Kirill Smelkov's avatar
      amari.kpi: Fix LogMeasure to handle multiple x.drb_stats messages in one period · 1b9d4e1a
      Kirill Smelkov authored
      When computing Measurements amari.kpi derives period from stats messages
      - for example 60s if `xamari xlog` was running with stats/60s. But
      inside that period there could be multiple x.drb_stats messages - for
      example if xlog was running with `stats/60s x.drb_stats/10s`.
      However the code was handling only the last such x.drb_stats instead of
      accumulating counters from there during the period.
      
      As the result, if. e.g. during the period there were 90B/9s and 2B/0.2s
      x.drb_stats events, only the latter was accounted giving wrong data for
      transmitted amount and transmission time.
      
      -> Fix it.
      1b9d4e1a
    • Kirill Smelkov's avatar
      amari.kpi: Teach LogMeasure to handle stats messages with multiple cells · 51c01b14
      Kirill Smelkov authored
      Starting from 71087f67 (amari.kpi: New package with driver for Amarisoft
      LTE stack to retrieve KPI-related measurements from logs) a limitation
      was hardcoded that only 1-cell configurations are supported. However we
      do use multicell eNB configurations and computing E-RAB Accessibility
      KPI was failing on them as e.g.:
      
          xlte.amari.kpi.LogError: t1731059787.321: stats describes 2 cells;  but only single-cell configurations are supported
      
      -> Remove that limitation and handle stats covering multiple cells.
      51c01b14
  5. 27 May, 2024 2 commits
    • Kirill Smelkov's avatar
      amari.xlog: Add support for password-based authentication · c5e92b6a
      Kirill Smelkov authored
      Add new --password option and wire it to go to amari.connect(password=...)
      
      Support for password-based authentication in amari.connect was just
      added in the previous patch.
      
      We need to extend filtering of logged fields on service attach a bit
      since now the first service message can be both 'ready' and
      'authenticate', and besides e.g. 'message' we don't want to log what was
      the 'challenge'.
      
      /reported-and-tested-by @lu.xu
      /reviewed-on kirr/xlte!6
      c5e92b6a
    • Kirill Smelkov's avatar
      amari: Conn: add support for password-based authentication · 5eb80f9a
      Kirill Smelkov authored
      Sometimes Amarisoft services are setup to require custom
      challenge/response authentication upon connecting to their WebSocket.
      In 61ad9032 (amari: Add functionality to interoperate with an Amarisoft
      LTE service via WebSocket) I've put TODO for that but now we start to
      need it.
      
      -> Implement corresponding support for password-based authentication.
      
      In the next patch we will teach XLog to use it.
      
      See https://tech-academy.amarisoft.com/lteenb.doc#Startup for details of
      the authentication handshake protocol.
      
      /reported-and-tested-by @lu.xu
      /reviewed-on kirr/xlte!6
      5eb80f9a
  6. 29 Dec, 2023 1 commit
    • Kirill Smelkov's avatar
      nrarfcn: Fix behaviour on invalid input parameters · 8e606c64
      Kirill Smelkov authored
      Contrary to earfcn, where band can be automatically deduced from earfcn
      number because 4G bands never overlap, most functions in nrarfcn accept
      as input parameters both nr_arfcn and band, because 5G bands can and do
      overlap. As the result it is possible to invoke e.g. dl2ul with
      dl_nr_arfcn being outside of downlink spectrum of specified band.
      
      However in b8065120 I've made a thinko and handled such situation with
      simple assert which does not lead to useful error feedback from a user
      perspective, for example:
      
          In [2]: xnrarfcn.dl2ul(10000, 1)
          ---------------------------------------------------------------------------
          AssertionError                            Traceback (most recent call last)
          Cell In[2], line 1
          ----> 1 n.dl2ul(10000, 1)
      
          File ~/src/wendelin/xlte/nrarfcn.py:85, in dl2ul(dl_nr_arfcn, band)
               83 if dl_lo == 'N/A':
               84     raise AssertionError('band%r does not have downlink spectrum' % band)
          ---> 85 assert dl_lo <= dl_nr_arfcn <= dl_hi
               86 ul_lo, ul_hi = nr.get_nrarfcn_range(band, 'ul')
               87 if ul_lo == 'N/A':
      
          AssertionError:
      
      The issue here is that asserts can be used to only verify internal
      invariants, and that reported error does not provide details about which
      nrarfcn and band were used in the query.
      
      -> Fix this by providing details in the error reported to incorrect
      module usage, and by consistently raising ValueError for "invalid
      parameters" cases.
      
      The reported error for above example now becomes
      
          ValueError: band1: NR-ARFCN=10000 is outside of downlink spectrum
      8e606c64
  7. 05 Dec, 2023 1 commit
  8. 25 Oct, 2023 1 commit
    • Kirill Smelkov's avatar
      earfcn: New package to do computations with LTE bands, frequencies and EARFCN numbers · 6cb9d37f
      Kirill Smelkov authored
      Do a package which provides calculations like EARFCN -> frequency,
      EARFCN -> band info, and to convert DL/UL EARFCN in between each other.
      
      I was hoping to find something ready on the net, but could find only
      pypi.org/project/nrarfcn for 5G, while for LTE everything I found
      was of lesser quality and capability.
      
      -> So do it myself.
      
      See package documentation for API details.
      6cb9d37f
  9. 25 Jul, 2023 2 commits
    • Kirill Smelkov's avatar
      amari.xlog: Add support for arbitrary query options · bcfd82dd
      Kirill Smelkov authored
      Before this patch we were supporting only boolean option flags - with, for
      example, stats[rf] meaning stats query with {"rf": True} arguments. Now we add
      support for arbitrary types, so that it is possible to specify e.g. integer or
      string query options, as well as some boolean flag set to false.
      
      This should be good for generality.
      
      For backward compatibility the old way to implicitly specify "on" flags is
      continued to be supported.
      bcfd82dd
    • Kirill Smelkov's avatar
      amari.xlog: Test and polish str(LogSpec) · 70b4b71c
      Kirill Smelkov authored
      If the parsed period was '60s' we were printing it back as '60.0s' on str.
      Fix it by using %g insted of %s.
      70b4b71c
  10. 27 Apr, 2023 1 commit
  11. 20 Apr, 2023 1 commit
    • Kirill Smelkov's avatar
      kpi: Add way to compute aggregated counters + showcase this · bf96c767
      Kirill Smelkov authored
      - add Calc.cum to aggregate Measurements.
      
      - add ΣMeasurement type to represent result of this. It is very similar
        to Measurement, but every field comes accompanied with information
        about how much time there was no data for that field. In other words
        it is not all or nothing for NA in the result. For example a field
        might be present 90% of the time and NA only 10% of the time. We want to
        preserver knowledge about that 90% of valid values in the result. And we
        also want to know how much time there was no data.
      
      - amend kpidemo.py and kpidemo.ipynb to demonstrate this.
      bf96c767
  12. 18 Apr, 2023 3 commits
  13. 17 Apr, 2023 1 commit
    • Kirill Smelkov's avatar
      amari.xlog: Implement log rotation · a2c3afaa
      Kirill Smelkov authored
      Rotate output enb.xlog ourselves at sync points so that nothing is lost
      in the output (hello `logrotate copytruncate`) and so that we can emit
      pre- and post- logrotate syncs.
      
      Reuse logging's RotatingFileHandler and TimedRotatingFileHandler to
      implement actual rotation, but carefully wrap them in our writer
      classes so that we emit exactly the output we prepared explicitly
      without any headers prepended by logging, and that we explicitly control
      when rotation happens.
      
      /proposed-for-review-at !5
      a2c3afaa
  14. 28 Mar, 2023 1 commit
  15. 27 Mar, 2023 3 commits
  16. 22 Mar, 2023 2 commits
    • Kirill Smelkov's avatar
      amari.xlog: Sync, reverse reading, timestamps for eNB < 2022-12-01 · 67466ae5
      Kirill Smelkov authored
      Rework XLog protocol to come with periodic sync events that come from time to
      time so that xlog stream becomes self-synchronizing. Sync events should be
      useful for Wendelin to start reading xlog stream from any point, and to verify
      that the stream is ok by matching its content vs messages schedule coming in
      the syncs.
      
      Teach xlog.Reader to read streams in reverse order from end to start. This
      should be useful to look at tail of a log without reading it in full from the
      start.
      
      Teach xlog.Reader to reconstruct messages timestamps for xlog streams produced
      with Amarisoft releases < 2022-12-01. There messages do not have .utc field
      added in https://support.amarisoft.com/issues/21934 and come with only .time
      field that represent internal eNB time using clock originating at eNB startup.
      We combine message.time and δ(utc, enb.time) from sync to build message.timestamp .
      
      See individual patches for details and
      kirr/xlte!3 for preliminary discussion.
      
      /reviewed-by @xavier_thompson
      /reviewed-on kirr/xlte!4
      67466ae5
    • Kirill Smelkov's avatar
      amari.xlog: attach,sync += information about on-service time · 0c772eb4
      Kirill Smelkov authored
      We currently emit information about local time in events, and
      information about on-service time in messages. Events don't have
      information about on-service time and messages don't carry information
      about local time. That is mostly ok, since primary xlog setup is to run
      on the same machine, where eNB runs because on-service .utc correlates
      with .time in events.
      
      However for eNB < 2022-12-01 on-service time includes only .time field
      without .utc field with .time representing "time passed since when eNB
      was started". This way for enb.xlog streams generated on older systems
      it is not possible for xlog.Reader to know the absolute timestamps of
      read messages.
      
      To fix this we amend "attach" and "sync" events to carry both local and
      on-service times. This way xlog.Reader, after seeing e.g. "sync" with
      .time and only .srv_time without .srv_utc, should be able to
      correlate local and on-service clocks and to approximate srv_utc as
      
      	srv_utc' = srv_time' + (time - srv_time)
      
      where time and srv_time correspond to last synchronization, and
      srv_time' is what xlog.Reader retrieves for a further-read message in
      question.
      
      See kirr/xlte!3 for related discussion.
      0c772eb4