Skip to content

Releases: ZhenxianLi/rota-order

rota-order v0.8.0

Choose a tag to compare

@github-actions github-actions released this 28 Oct 17:13

The project is now rota-order everywhere, and so is the import name. This is a breaking change:

# up to 0.7                      # from 0.8
import rota                      import rota_order as ro
import rota.plot as rp           import rota_order.plot as rp
rota.maps.rpm_order_map(...)     ro.maps.rpm_order_map(...)

Functions, arguments and results are unchanged. Environment variables ROTA_* are now ROTA_ORDER_*, and sidecar files record "rota_order_version".

The plotting gallery now uses realistic synthetic recordings: a four-cylinder engine run-up with upshifts, combustion bursts, a cabin and road noise (synth_scenario("ice_runup")), and an electric drive whose gear whine passes a structural resonance (synth_scenario("ev_runup")). All figures: docs/plotting.md.

Engine run-up, Campbell diagram with the firing orders 2, 4 and 6 — rp.rpm_frequency_map(fmap, **rp.SOUND_PRESSURE, mark_orders=(2, 4, 6))

Campbell diagram

Tacholess speed through the upshifts — rp.rpm_from_ridge(ridge, reference=(t, ice.rpm_mean))

rpm_from_ridge

Electric drive, the whine lowered by 10 dB: before, after and the difference — rp.compare_maps({"original": ev_omap, "order 40 -10 dB": ev_omap_y}, **rp.SOUND_PRESSURE, difference=True, dynamic_range=70)

compare_maps

The whine against the ECMA-418-1 criterion — rp.order_prominence(prom)

order_prominence

Fixed: order maps with min_rpm placed frames below the floor at the floor speed with a wrong shaft angle; they are now left out.

Changes

One name everywhere: rota-order, imported as rota_order.

Changed

  • Breaking: the import name is now rota_order, the same as the project and distribution name rota-order (import rota_order as ro; the recommended short alias). The package rota no longer exists and there is no compatibility package. Replace import rota by import rota_order as ro and rota. by ro. (or from rota. by from rota_order.). Nothing else changes: functions, arguments and results are the same.
  • The project is called rota-order (all lowercase) in the documentation, docstrings and examples.
  • Breaking: environment variables ROTA_SQAT_PATH, ROTA_MATLAB_ENGINE_NAME and ROTA_REF_PART are now ROTA_ORDER_SQAT_PATH, ROTA_ORDER_MATLAB_ENGINE_NAME and ROTA_ORDER_REF_PART (the old names are not read); in the MATLAB reference script the internal variable ROTA_REF_LOG is now ROTA_ORDER_REF_LOG, and the shared MATLAB engine of the CI is named rota_order_ci.
  • Breaking: the key "rota_version" of the provenance and configuration sidecars (synth configurations, OrderTable.config, provenance) is now "rota_order_version"; the old key is not read.
  • rota_order.plot.order_prominence: with rpm_bins= a single speed bin without frames between two prominent bins is now bridged, so a prominent range is shaded as one span (a gap of several bins, or a bin with a point below the criterion, stays open).
  • The plotting gallery (examples/11_plot_gallery.py, docs/plotting.md, docs/images/plot_*.png) uses realistic synthetic recordings (synth_scenario("ice_runup") and "ev_runup") instead of a textbook signal.
  • rota_order.tonal.order_prominence documents that all orders share one frame length (the one the lowest order needs) and how to get the speed resolution of a high order (one call per order).

Fixed

  • rota_order.maps.rpm_order_map(min_rpm=...): frames in which the speed falls below min_rpm were placed at min_rpm with a wrong shaft angle (their order content smeared). They are now left out of the map, so rpm_min >= min_rpm; ValueError if no frame remains. This also removes frames below the default floor (2 % of the maximum speed) near standstill. rota_order.tonal.order_prominence passes min_rpm through and is affected in the same way, and rota_order.metrics.order_residual_energy, which builds order maps with the default floor, may change slightly for records that start near standstill.
  • rota_order.plot.compare_maps: when the changed cells lie below the colour floor of the level panels (e.g. an order next to a dominant low-order rumble), the difference scale collapsed to rounding noise. It is now set by the changed cells near their own row maximum, per difference panel, and a UserWarning suggests a larger dynamic_range when a coherent change lies below the colour floor.

Full list of changes: v0.7.0...v0.8.0

Install: pip install rota-order==0.8.0

rota-order v0.7.0

Choose a tag to compare

@github-actions github-actions released this 28 Oct 10:20

rota.plot draws results in one call, with the unit, the dB reference and the amplitude convention in the labels. The figures below are drawn with these functions on realistic synthetic recordings — a four-cylinder engine run-up with upshifts and an electric drive whose gear whine passes a resonance — taken from the gallery of 0.8.0 (where the package is imported as rota_order; the functions are the same). All fifteen functions: docs/plotting.md.

import rota.plot as rp   # 0.7; pip install "rota-order[plot]==0.7.0"

Campbell diagram with the firing orders — rp.rpm_frequency_map(fmap, **rp.SOUND_PRESSURE, mark_orders=(2, 4, 6))

Campbell diagram

Order map — rp.rpm_order_map(omap, **rp.SOUND_PRESSURE, mark_orders=(2, 4, 6))

Order map

Before and after on one colour scale, with the dB difference — rp.compare_maps({"original": m, "order 40 -10 dB": m_new}, **rp.SOUND_PRESSURE, difference=True, dynamic_range=70)

compare_maps

Order slices against the truth — rp.order_slice({"order 2": s2, "order 4": s4}, **rp.SOUND_PRESSURE, truth={...})

order_slice

Tacholess speed through the upshifts — rp.rpm_from_ridge(ridge, reference=(t, rpm))

rpm_from_ridge

Tone prominence against the ECMA-418-1 criterion — rp.order_prominence(prom)

order_prominence

1/3-octave band levels along rpm — rp.band_levels_vs_rpm(bands, **rp.SOUND_PRESSURE)

band_levels_vs_rpm

Changes

Figures of ROTA results.

Added

  • rota.plot: standard figures of ROTA results on Matplotlib (optional extra plot; import rota does not import Matplotlib, and a call without it raises ImportError with the install command). Fifteen functions, each named like the function whose result it draws: rpm_frequency_map, rpm_order_map, compare_maps, order_slice, frequency_slice, order_spectrum, overall_level, metric_vs_rpm, time_metric, rpm_vs_time, rpm_from_ridge, order_components, order_table, order_prominence, band_levels_vs_rpm. Each takes ax= and returns the Axes it drew on (except compare_maps, which builds its own grid and returns (fig, axes)), never shows the figure and never changes rcParams or the style. Labels carry the unit, the dB reference and the amplitude convention, from an explicit ref/unit (the constant SOUND_PRESSURE for sound pressure in Pa) or the reference a result stores. Line plots take one result, a list or a dict and plot differences with relative_to=; compare_maps shows maps on one colour scale, with their dB difference. orders= selects orders, mark_orders= draws order lines. Maps are drawn against rpm when the speed is essentially monotonic (reversals up to monotonic_tol of the range, 2 % by default) and against time otherwise; channel= selects the channel of multichannel results.
  • Gallery examples/11_plot_gallery.py and page docs/plotting.md, one figure per function with the call that made it.

Changed

  • Examples 01 and 02 draw their figures with rota.plot; their printed numbers are unchanged.
  • Continuous integration installs the plot extra in the main test matrix; the oldest-dependency job runs without Matplotlib.

Full list of changes: v0.6.0...v0.7.0

Install: pip install rota-order==0.7.0

rota-order v0.6.0

Choose a tag to compare

@github-actions github-actions released this 28 Oct 09:06

Optional bridges to psychoacoustic toolboxes.

Added

  • rota.bridges: optional thin wrappers for open-source psychoacoustic toolboxes, imported lazily inside the functions (never vendored, never required; ImportError with the install command when missing). A bridge module is named exactly like the toolbox it wraps. rota.bridges.mosqito: ISO 532-1:2017 time-varying loudness, DIN 45692:2009 sharpness (optionally from a loudness result) and Daniel and Weber roughness. rota.bridges.sottek_hearing_model: ECMA-418-2:2025 (4th edition) tonality, loudness (optionally from a tonality result) and roughness of the Sottek hearing model, with the Matplotlib rcParams it changes on import restored. The functions return a TimeMetric result container whose method vs_rpm(rpm, t_rpm, **kwargs) bins the result along rpm with metric_vs_rpm, so that the quantity and the toolbox are visible in one line (bridges.mosqito.loudness(x, fs).vs_rpm(rpm, t_rpm)): it uses the recommended start-up exclusion, aggregation and threshold of the metric (keyword arguments override them), holds the last speed for frames that lie after the last speed sample by no more than the speed-sample interval plus the frame step of the metric (the Sottek hearing model puts its last tonality and loudness frame at the signal length rounded up to a multiple of 256 samples at 48 kHz, up to 5.33 ms after the last sample), warns about the frames it cannot assign a speed (outside the speed record, or where the speed is NaN; only frames that would otherwise be used are counted), says so in the ValueError when every frame after the start-up exclusion is at or below the threshold (threshold=None means none), and fills name, unit and source of the binned result from the metric. For full control, call rota.slices.metric_vs_rpm(m.values, m.t, rpm, t_rpm, ...) yourself.
  • rota.bridges.sqat: optional wrappers that call SQAT v2.0 (loudness, sharpness, roughness, tonality) in a local, licensed MATLAB through the MATLAB Engine API for Python; tested with a stub engine, the tests against a real MATLAB are skipped unless matlab.engine and ROTA_SQAT_PATH are available (hosted runners cannot license an engine started from Python; the sqat-engine workflow is a manual probe).
  • Optional dependency extras mosqito and sottek-hearing-model, named like the toolbox (no extra for SQAT: matlabengine must match the user's MATLAB release).
  • Each wrapper states the licence of its toolbox and the citation its authors ask for; the README lists them.

Changed

  • rota.slices.MetricVsRpm gains the optional fields name, unit and source (default: empty strings; filled by TimeMetric.vs_rpm from the metric, left empty by metric_vs_rpm). Backward compatible: construction by keyword and attribute access are unchanged.
  • The repository is renamed rota-order (github.com/ZhenxianLi/rota-order), the same name as the PyPI package; links are updated and the old address redirects. The import name stays rota.
  • Examples 07, 08 and 10 use rota.bridges: MoSQITo and the Sottek hearing model are called through rota.bridges.mosqito and rota.bridges.sottek_hearing_model, and the results are binned with TimeMetric.vs_rpm, which replaces the resampling to 48 kHz, start-up exclusions, aggregations, tonality threshold, last-frame speed hold, silencing of MoSQITo and restoring of Matplotlib's settings that the examples did by hand; example 07 still calls MoSQITo's tnr_ecma_st directly (not wrapped: ROTA has its own tone-to-noise ratio). The printed numbers, figures and audio files of the examples are unchanged. The install notes of the examples use the extras mosqito and sottek-hearing-model, and examples/09_sqat_bridge/README.md shows rota.bridges.sqat with a licensed local MATLAB (CI keeps run_sqat.m, since a MATLAB engine started from Python is not licensed on hosted runners).
  • Roadmap: rota.plot moves to v0.7.
  • Releases are published by continuous integration: a commit Release X.Y.Z on main runs the tests, uploads to PyPI, tags vX.Y.Z and creates the GitHub release with the CHANGELOG section as notes (.github/workflows/publish.yml; see CONTRIBUTING.md).

Full list of changes: v0.5.0...v0.6.0

Install: pip install rota-order==0.6.0

rota-order v0.5.0

Choose a tag to compare

@ZhenxianLi ZhenxianLi released this 27 Oct 21:17

Application layer and bridges to open-source psychoacoustic toolboxes.

Highlights

  • rota.tonal (new):
    • A/C weighting (IEC 61672-1).
    • Calibrated levels and resampling to 48 kHz.
    • Octave and 1/3-octave filterbank (IEC 61260-1), with band levels along rpm.
    • Tone-to-noise ratio and prominence ratio after ECMA-418-1 (3rd edition, 2024), for stationary spectra (tone_prominence) and for orders along a run-up in the angle domain (order_prominence).
  • Application layer:
    • frequency_slice, overall_level.
    • metric_vs_rpm: bins any time-varying metric, for example loudness from a psychoacoustic toolbox, along rpm.
    • frequency_to_order, order_share.
    • rpm binning of maps (bin_map).
  • Signal alignment: align_signals (GCC-PHAT delay), detect_onsets.
  • Psychoacoustic bridges: examples 07–09 link ROTA to MoSQITo, sottek-hearing-model (ECMA-418-2) and SQAT (MATLAB, run in CI). The toolboxes are optional, external dependencies; ROTA itself still depends only on NumPy and SciPy. Open-source implementations of psychoacoustic metrics are catalogued in LISQM.
  • Worked analysis (docs/worked_example.md): an electric-drive run-up with a gear whine passing a resonance.
    • Speed and overview.
    • Diagnosis of the prominent order with ECMA-418-1.
    • Extraction and a what-if reduction of that order.
    • Its effect on loudness, sharpness and ECMA-418-2 tonality, with audio before and after.
    • Every finding checked against the truth built into the synthetic signal.

rota.plot moves to v0.6. Full list of changes: CHANGELOG.md.

Install: pip install rota-order==0.5.0

rota-order v0.4.0

Choose a tag to compare

@ZhenxianLi ZhenxianLi released this 27 Oct 14:22

Realistic test signals, stress tests and a black-box reference comparison.

Install

pip install --upgrade rota-order

Highlights

Realistic synthetic scenarios (rota.synth.synth_scenario)

  • ice_runup: four-stroke four-cylinder engine run-up with gear shifts, angular speed ripple, combustion bursts, a modal transfer path with cabin boom, a cabin impulse response, speed-dependent road noise and a tachometer.
  • ev_runup: electric drive with motor orders, gear mesh and whine, a dithered inverter carrier with sidebands, and fixed high-frequency tones.
  • steady_cruise: either powertrain at constant speed.
  • Optional recorder impairments (clipping, quantisation, self-noise, clock drift) and two-channel receivers.
  • Every stem, the true speed and angle, and the order truth are returned. Defaults are illustrative values typical of passenger cars, rounded.
  • The building blocks are public too (drive_rpm, angle_impulses, carrier_components, modal_path, room_impulse_response, speed_noise, measurement_chain, ...).

Stress tests (validation/stress.md)

  • ROTA measured on these scenarios: speed estimation, order levels, extraction and removal, recorder impairments. Results and recommended settings are written down, including what does not work.

More robust tacholess speed (rpm_from_ridge)

  • Automatic analysis window chosen from the sweep rate.
  • Channels averaged in power.
  • confidence recalibrated so that it separates good tracks from bad ones.
  • Warnings only when a failure is likely; new quality summary and RidgeQualityWarning.
  • Recommended comb by machine type documented (four-stroke N-cylinder: (N/2, N, 3N/2)).

Black-box comparison with MATLAB in CI

  • MATLAB's documented public functions, called with explicit parameters, compared with ROTA and the analytic truth on physical quantities.
  • No discrepancy was found where ROTA is wrong; where the two disagree, the truth sides with ROTA.
  • The MATLAB output stays in the workflow artifact and is not published in the repository.

Listen

  • Synthetic WAV examples in docs/audio/: engine run-up (original, orders removed, orders only, apparent speed +15 %) and electric drive (original, motor orders removed, whine only), generated by examples/05 and examples/06.

Notes

  • Pre-alpha: signatures may still change before 1.0.
  • The application layer and psychoacoustic bridges move to v0.5.
  • Full list of changes: CHANGELOG.md.

rota-order v0.3.0

Choose a tag to compare

@ZhenxianLi ZhenxianLi released this 26 Oct 10:41

First public release of ROTA (Rotational Order Tracking & Analysis): order tracking and order analysis of noise and vibration signals from rotating machinery, with order-based sound resynthesis. Python, NumPy and SciPy only.

Install

pip install rota-order

The distribution is called rota-order; the import name is rota. Python 3.10–3.13, NumPy ≥ 1.23, SciPy ≥ 1.9.

What's included

Speed reference (rota.speed)

  • Tachometer pulses to rpm (sub-sample edges, missing and spurious pulses, jitter smoothing)
  • Tacholess speed estimation: order comb + dynamic programming, with confidence and search-boundary flags
  • Shaft angle from rpm, control-signal cleanup and resampling, vehicle speed to rpm, steady-segment selection, virtual rpm

Computed order tracking (rota.angle, rota.maps, rota.slices)

  • Angular resampling with angle-domain anti-aliasing, its inverse, and time warping to a reference speed
  • Campbell (rpm–frequency) and rpm–order maps; order slices and order spectra with explicit peak/RMS convention

Order extraction (rota.extract)

  • Second-generation Vold-Kalman filter, joint over orders and shafts (crossing orders, close twin shafts)
  • Phase-locked tracked harmonics over windows of fixed length in revolutions
  • Order removal, broadband residual, and a resolvability check that recommends windows or bandwidths

Resynthesis (rota.resynth)

  • Order tables (orders × rpm, explicit peak/RMS), additive synthesis offline and in blocks for real-time use
  • Broadband synthesis from a spectral envelope without frame-rate pumping
  • Order editing (gain, mute, solo, relabel), speed remapping, order-plus-residual recombination with explicit gains, steady listening excerpts, JSON provenance sidecars

Test signals and metrics (rota.synth, rota.metrics)

  • Run-up generator that stores the analytic truth of every component; tone-plus-noise stimuli
  • Speed-track error, energy-weighted waveform similarity, residual order energy, window/pumping and envelope checks

Validation

Every public function is tested against analytic ground truth generated by rota.synth (1209 tests). Validation cases T1–T6 and T8–T14 meet the design tolerances, typically with a wide margin. For example:

  • Stationary order amplitude: ≤ 0.08 dB against a tolerance of 0.2 dB.
  • Tachometer speed: about 0.01 % median error against a tolerance of 0.1 %.
  • Order removal: −46 to −56 dB residual against a tolerance of −20 dB.

Details: validation/README.md. A black-box comparison with other implementations has not been run yet.

Notes

  • Pre-alpha: function signatures may still change before 1.0.
  • Versions 0.1 and 0.2 were development milestones and were not released separately; see CHANGELOG.md.
  • Four example scripts with figures are in examples/.
  • Licence: GPL-3.0-or-later.