Skip to content

Helpers for NTP server sync#142

Merged
t-sasatani merged 8 commits into
mainfrom
feat-ntp
Nov 27, 2025
Merged

Helpers for NTP server sync#142
t-sasatani merged 8 commits into
mainfrom
feat-ntp

Conversation

@t-sasatani
Copy link
Copy Markdown
Collaborator

@t-sasatani t-sasatani commented Nov 26, 2025

This pull request adds a module that checks whether the system is synchronized with an optional NTP server. This doesn't perform the actual sync because it's OS-dependent and requires sudo. Any additions or suggestions are welcome. For instance, I have no idea how to test this.

  • Users can define the NTP server address and the allowed time offset in runtime configurations for various CLI commands (currently integrated only with the stream command).
  • The prompt_ntp_sync method measures the offset between the system clock and the specified NTP server. This method can be integrated with CLI commands.
  • The NTP check can be bypassed through interactive prompts. This feature is needed because NTP servers may not be connected.

Example

Add something like below to the CLI command

    if config.runtime.ntp_server is not None:
        prompt_ntp_sync(
            config.runtime.ntp_server, max_offset_seconds=config.runtime.ntp_max_offset_seconds
        )

If the specified NTP server exists and the time is synced, it proceeds:

[25-11-26T10:37:05] INFO     [mio.ntp] Checking time sync with NTP server: mio-ntp.local                       ntp.py:78
[25-11-26T10:37:22] INFO     [mio.ntp] Resolved mio-ntp.local to 192.168.10.2                                  ntp.py:33
                    INFO     [mio.ntp] Time is synchronized with NTP server mio-ntp.local (offset: 0.000s)     ntp.py:89

And if not, a query shows up:

[25-11-26T10:38:40] INFO     [mio.ntp] Checking time sync with NTP server: mio-ntp.local                       ntp.py:78
[25-11-26T10:38:57] WARNING  [mio.ntp] Could not resolve hostname mio-ntp.local to IP address.                 ntp.py:36
[25-11-26T10:38:59] WARNING  [mio.ntp] Could not query NTP server mio-ntp.local.                               ntp.py:82
System time may not be synchronized. Proceed anyway? [y/N]:

📚 Documentation preview 📚: https://miniscope-io--142.org.readthedocs.build/en/142/

@t-sasatani t-sasatani changed the title CLI helpers for NTP server sync Helpers for NTP server sync Nov 26, 2025
@coveralls
Copy link
Copy Markdown
Collaborator

coveralls commented Nov 26, 2025

Pull Request Test Coverage Report for Build 19747502406

Details

  • 18 of 49 (36.73%) changed or added relevant lines in 3 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage decreased (-0.8%) to 79.09%

Changes Missing Coverage Covered Lines Changed/Added Lines %
mio/cli/stream.py 4 5 80.0%
mio/ntp.py 12 42 28.57%
Totals Coverage Status
Change from base Build 19316009037: -0.8%
Covered Lines: 1982
Relevant Lines: 2506

💛 - Coveralls

@t-sasatani
Copy link
Copy Markdown
Collaborator Author

t-sasatani commented Nov 26, 2025

Gets under 1 ms of offset on Mac and around 50 ms on Windows, right after manually syncing with the specified NTP server. Unsure why Windows has such a large offset.

@t-sasatani
Copy link
Copy Markdown
Collaborator Author

Seems like installing a real NTP client on Windows may solve this. Will try out later.

Comment thread pyproject.toml Outdated
"scikit-video>=1.1.11",
"scipy>=1.13.0",
"ntplib>=0.4.0",
"pillow>=11.0.0,<11.3.0",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is pillow added here? I don't see it used

Copy link
Copy Markdown
Collaborator Author

@t-sasatani t-sasatani Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, don't need it in this PR. Got mixed up while working on this PR (#144) at the same time, trying a bunch of camera-detection stuff that works on Windows too (which I gave up for now).

I think I ended up not needing pillow for this, too, so I will remove it.

@sneakers-the-rat
Copy link
Copy Markdown
Collaborator

Can we make this optional? As in the dependencies for NTP are optional. Seems a bit out of scope for general use.

@t-sasatani
Copy link
Copy Markdown
Collaborator Author

t-sasatani commented Nov 26, 2025

Can we make this optional? As in the dependencies for NTP are optional. Seems a bit out of scope for general use.

I don't mind at all if that's better, but is it worth adding a bit of if statements for that? It only checks the NTP server when there's a host/IP in the runtime config, so it should work the same without it. Also, it's not a very heavy package like matplotlib.

@t-sasatani
Copy link
Copy Markdown
Collaborator Author

And I guess syncing is pretty generic. But that said, either way works great for me.

@sneakers-the-rat
Copy link
Copy Markdown
Collaborator

I think it could be pretty simple: make the NTP dependency in an optional dependency group "ntp", and then in that module do like

try:
    import ntplib
except ImportError as e:
    raise ImportError("attempted to import ntp module, but NTP dependencies not installed. Install mio like `pip install mio[ntp]`") from e

and then where its used in streamdaq like

if {ntp is configured}:
    from mio.ntp import ...
    # rest of usage

@t-sasatani
Copy link
Copy Markdown
Collaborator Author

t-sasatani commented Nov 26, 2025

Yes, that's exactly the few lines I was thinking about (tho not if). It doesn't hurt either way, so I will do it if it looks better.

@t-sasatani
Copy link
Copy Markdown
Collaborator Author

t-sasatani commented Nov 27, 2025

The native Windows time system wasn't accurate, but using Meinberg NTP on Windows keeps this synced within a few milliseconds as well.

@t-sasatani
Copy link
Copy Markdown
Collaborator Author

Ok now made NTP dependencies optional

@sneakers-the-rat
Copy link
Copy Markdown
Collaborator

maybe a changelog entry? But otherwise lgtm

@sneakers-the-rat
Copy link
Copy Markdown
Collaborator

Also forgot you were syncing windows, maybe that explains why sync couldn't get down into the microsecond range? I remember with the raspis i pretty reliably could get them down there

@t-sasatani
Copy link
Copy Markdown
Collaborator Author

t-sasatani commented Nov 27, 2025

Thanks, I'll add a changelog before merging.

Also forgot you were syncing windows, maybe that explains why sync couldn't get down into the microsecond range? I remember with the raspis i pretty reliably could get them down there

Yes, the Mac/Raspi sync offset is under a millisecond right after a manual sync. So we can probably get tighter synchronization by increasing the polling frequency. Windows shows more randomness even after manual syncing with a proper NTP client, but it is always under 10 ms, so it's enough for syncing 20-30 FPS imaging systems.

@t-sasatani t-sasatani merged commit 4b30a0d into main Nov 27, 2025
20 checks passed
@t-sasatani t-sasatani deleted the feat-ntp branch November 27, 2025 21:08
@t-sasatani t-sasatani restored the feat-ntp branch November 27, 2025 21:10
@sneakers-the-rat
Copy link
Copy Markdown
Collaborator

Windows as an OS has bad timing properties generally, the OS loves to preempt stuff in a way that can't be prevented. maybe that goes in the docs

@t-sasatani t-sasatani deleted the feat-ntp branch November 27, 2025 23:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants