-
-
Notifications
You must be signed in to change notification settings - Fork 20
feat: add IEC 61260-1 nominal frequency labels #46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,6 +6,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from __future__ import annotations | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import warnings | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from functools import lru_cache | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import List, Tuple | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import numpy as np | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -14,13 +15,13 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def getansifrequencies( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fraction: float, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| limits: List[float] | None = None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> Tuple[List[float], List[float], List[float]]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> Tuple[List[float], List[float], List[float], List[str]]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Calculate frequencies according to ANSI/IEC standards. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| :param fraction: Bandwidth fraction (e.g., 1, 3). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| :param limits: [f_min, f_max] limits. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| :return: Tuple of (center_freqs, lower_edges, upper_edges). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| :return: Tuple of (center_freqs, lower_edges, upper_edges, nominal_labels). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if limits is None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| limits = [12, 20000] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -40,7 +41,8 @@ def getansifrequencies( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| freq_d = freq / _bandedge(g, fraction) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| freq_u = freq * _bandedge(g, fraction) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return freq.tolist(), freq_d.tolist(), freq_u.tolist() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| labels = [_format_nominal_freq(_nominal_freq_for_band(f, fraction)) for f in freq.tolist()] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return freq.tolist(), freq_d.tolist(), freq_u.tolist(), labels | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _initindex(f: float, fr: float, g: float, b: float) -> int: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -109,17 +111,60 @@ def _deleteouters( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return freq_arr.tolist(), freq_d_arr.tolist(), freq_u_arr.tolist() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _genfreqs(limits: List[float], fraction: float, fs: int) -> Tuple[List[float], List[float], List[float]]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _genfreqs( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| limits: List[float], fraction: float, fs: int | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> Tuple[List[float], List[float], List[float], List[str]]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Determine band frequencies within limits. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| :param limits: [f_min, f_max]. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| :param fraction: Bandwidth fraction. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| :param fs: Sample rate. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| :return: Tuple of center, lower, and upper frequencies. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| :return: Tuple of center, lower, upper frequencies, and nominal labels. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| freq, freq_d, freq_u, labels = getansifrequencies(fraction, limits) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| freq, freq_d, freq_u = _deleteouters(freq, freq_d, freq_u, fs) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # _deleteouters only removes trailing bands above Nyquist, so slice labels | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| labels = labels[: len(freq)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return freq, freq_d, freq_u, labels | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _iec_e3_round(f: float) -> float: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """IEC 61260-1 Annex E.3: 3 sig figs if MSD 1–4, 2 sig figs if MSD 5–9.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if f <= 0: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return f | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exponent = int(np.floor(np.log10(f))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| msd = f / (10.0 ** exponent) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| step = 10.0 ** (exponent - 2) if msd < 5.0 else 10.0 ** (exponent - 1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return round(f / step) * step | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @lru_cache(maxsize=4) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _extended_preferred(frac: int) -> List[float]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Cached expansion of the IEC preferred frequency table across decades.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| base = normalizedfreq(frac) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return [f * (10 ** d) for d in range(-3, 4) for f in base] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _nominal_freq_for_band(exact_freq: float, fraction: float) -> float: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Return IEC 61260-1 nominal frequency (float) for an exact mid-band frequency. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| For standard fractions (1, 3), snaps to the IEC preferred table via | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``normalizedfreq``. For non-standard fractions, falls back to Annex E.3 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| significant-figure rounding (``_iec_e3_round``). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| freq, freq_d, freq_u = getansifrequencies(fraction, limits) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return _deleteouters(freq, freq_d, freq_u, fs) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| frac = round(fraction) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if np.isclose(fraction, frac) and frac in (1, 3): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| extended = _extended_preferred(frac) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return min(extended, key=lambda f: abs(np.log(f / exact_freq))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return _iec_e3_round(exact_freq) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+149
to
+160
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
coderabbitai[bot] marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _format_nominal_freq(f: float) -> str: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Format a nominal frequency as a human-readable label string.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if f >= 1000: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return f"{f / 1000:g}k" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return f"{f:g}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def normalizedfreq(fraction: int) -> List[float]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Treat the 4-tuple return as a versioned API break.
This changes a public unpacking contract. Please pair it with the planned
1.2.0runtime/package version bump before release; otherwise existing callers still on1.1.xwill fail withtoo many values to unpack.🤖 Prompt for AI Agents