Repository navigation
Releases: ZhenxianLi/rota-order
Release list
rota-order v0.8.0
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))
Tacholess speed through the upshifts — rp.rpm_from_ridge(ridge, reference=(t, ice.rpm_mean))
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)
The whine against the ECMA-418-1 criterion — rp.order_prominence(prom)
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 namerota-order(import rota_order as ro; the recommended short alias). The packagerotano longer exists and there is no compatibility package. Replaceimport rotabyimport rota_order as roandrota.byro.(orfrom rota.byfrom 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_NAMEandROTA_REF_PARTare nowROTA_ORDER_SQAT_PATH,ROTA_ORDER_MATLAB_ENGINE_NAMEandROTA_ORDER_REF_PART(the old names are not read); in the MATLAB reference script the internal variableROTA_REF_LOGis nowROTA_ORDER_REF_LOG, and the shared MATLAB engine of the CI is namedrota_order_ci. - Breaking: the key
"rota_version"of the provenance and configuration sidecars (synthconfigurations,OrderTable.config,provenance) is now"rota_order_version"; the old key is not read. rota_order.plot.order_prominence: withrpm_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_prominencedocuments 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 belowmin_rpmwere placed atmin_rpmwith a wrong shaft angle (their order content smeared). They are now left out of the map, sorpm_min >= min_rpm;ValueErrorif no frame remains. This also removes frames below the default floor (2 % of the maximum speed) near standstill.rota_order.tonal.order_prominencepassesmin_rpmthrough and is affected in the same way, androta_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 aUserWarningsuggests a largerdynamic_rangewhen 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
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))
Order map — rp.rpm_order_map(omap, **rp.SOUND_PRESSURE, mark_orders=(2, 4, 6))
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)
Order slices against the truth — rp.order_slice({"order 2": s2, "order 4": s4}, **rp.SOUND_PRESSURE, truth={...})
Tacholess speed through the upshifts — rp.rpm_from_ridge(ridge, reference=(t, rpm))
Tone prominence against the ECMA-418-1 criterion — rp.order_prominence(prom)
1/3-octave band levels along rpm — rp.band_levels_vs_rpm(bands, **rp.SOUND_PRESSURE)
Changes
Figures of ROTA results.
Added
rota.plot: standard figures of ROTA results on Matplotlib (optional extraplot;import rotadoes not import Matplotlib, and a call without it raisesImportErrorwith 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 takesax=and returns the Axes it drew on (exceptcompare_maps, which builds its own grid and returns(fig, axes)), never shows the figure and never changesrcParamsor the style. Labels carry the unit, the dB reference and the amplitude convention, from an explicitref/unit(the constantSOUND_PRESSUREfor sound pressure in Pa) or the reference a result stores. Line plots take one result, a list or a dict and plot differences withrelative_to=;compare_mapsshows 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 tomonotonic_tolof the range, 2 % by default) and against time otherwise;channel=selects the channel of multichannel results.- Gallery
examples/11_plot_gallery.pyand pagedocs/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
plotextra 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
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;ImportErrorwith 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 MatplotlibrcParamsit changes on import restored. The functions return aTimeMetricresult container whose methodvs_rpm(rpm, t_rpm, **kwargs)bins the result along rpm withmetric_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 theValueErrorwhen every frame after the start-up exclusion is at or below the threshold (threshold=Nonemeans none), and fillsname,unitandsourceof the binned result from the metric. For full control, callrota.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 unlessmatlab.engineandROTA_SQAT_PATHare available (hosted runners cannot license an engine started from Python; thesqat-engineworkflow is a manual probe).- Optional dependency extras
mosqitoandsottek-hearing-model, named like the toolbox (no extra for SQAT:matlabenginemust 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.MetricVsRpmgains the optional fieldsname,unitandsource(default: empty strings; filled byTimeMetric.vs_rpmfrom the metric, left empty bymetric_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 staysrota. - Examples 07, 08 and 10 use
rota.bridges: MoSQITo and the Sottek hearing model are called throughrota.bridges.mosqitoandrota.bridges.sottek_hearing_model, and the results are binned withTimeMetric.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'stnr_ecma_stdirectly (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 extrasmosqitoandsottek-hearing-model, andexamples/09_sqat_bridge/README.mdshowsrota.bridges.sqatwith a licensed local MATLAB (CI keepsrun_sqat.m, since a MATLAB engine started from Python is not licensed on hosted runners). - Roadmap:
rota.plotmoves to v0.7. - Releases are published by continuous integration: a commit
Release X.Y.Zonmainruns the tests, uploads to PyPI, tagsvX.Y.Zand 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
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
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.
confidencerecalibrated so that it separates good tracks from bad ones.- Warnings only when a failure is likely; new
qualitysummary andRidgeQualityWarning. - 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 byexamples/05andexamples/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
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.






