visualdynamics 0.1.0a74__tar.gz → 0.1.0a76__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {visualdynamics-0.1.0a74/src/visualdynamics.egg-info → visualdynamics-0.1.0a76}/PKG-INFO +3 -2
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/pyproject.toml +13 -2
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/__init__.py +1 -1
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/compatibility.py +2 -2
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/author.py +22 -11
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/channel_table.py +44 -1
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/classical_pulse.py +11 -25
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/compliance.py +197 -31
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/data.py +63 -17
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/drive_prediction.py +131 -114
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/half_power.py +9 -2
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/kurtosis.py +10 -2
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/matrix_checks.py +55 -28
- visualdynamics-0.1.0a76/src/visualdynamics/core/matrix_comparison.py +1181 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/modal_response.py +6 -52
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/overload.py +13 -0
- visualdynamics-0.1.0a76/src/visualdynamics/core/prediction.py +998 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/replication_segments.py +5 -5
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/report.py +175 -48
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/resample.py +2 -1
- visualdynamics-0.1.0a76/src/visualdynamics/core/sensor_catalog.py +166 -0
- visualdynamics-0.1.0a76/src/visualdynamics/core/sensors.py +242 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/shock_synthesis.py +3 -8
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/sine_distortion.py +5 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/sine_tracking.py +5 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/solids.py +65 -3
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/srs.py +41 -22
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/stroke.py +20 -5
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/sweep_rate.py +1 -11
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/time_statistics.py +3 -3
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/transform.py +3 -2
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/icons.py +36 -1
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/main_window.py +484 -37
- visualdynamics-0.1.0a76/src/visualdynamics/gui/matrix_comparison_panel.py +125 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/modal_response_panel.py +3 -3
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/object_tables.py +29 -2
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/opengl.py +54 -1
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/panes.py +50 -7
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/report_editor.py +55 -2
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/resample_panel.py +15 -5
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/sine_panel.py +1 -8
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/tables.py +8 -1
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/native.py +73 -5
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/__init__.py +71 -0
- visualdynamics-0.1.0a76/src/visualdynamics/plot/matrix_comparison.py +333 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/mixed_mode.py +3 -8
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/project.py +310 -34
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/report/__init__.py +106 -14
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/report/features.py +10 -7
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/report/plot_conventions.py +25 -4
- visualdynamics-0.1.0a76/src/visualdynamics/report/prediction.py +291 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/report/skeleton.py +15 -6
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/viz/animate.py +83 -12
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/viz/geometry.py +15 -6
- visualdynamics-0.1.0a76/src/visualdynamics/viz/sensors.py +413 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76/src/visualdynamics.egg-info}/PKG-INFO +3 -2
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics.egg-info/SOURCES.txt +8 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics.egg-info/requires.txt +1 -1
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/LICENSE +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/README.md +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/setup.cfg +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/__main__.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/__init__.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/amplitude_spectrum.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/averaging.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/breakpoints.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/calibration.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/chance.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/channel_health.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/correlate.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/detect.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/dwell.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/entities.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/fem.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/filters.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/geometry.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/hexmesh.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/instrumentation.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/matches.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/mates.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/merge.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/mesh.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/mixed_mode.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/modal_fit.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/octave.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/photos.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/progress.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/replication.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/replication_fit.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/report_caption.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/rigid.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/rms_check.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/shaker.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/shapes.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/shells.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/shock_check.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/shocks.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/signals.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/signature.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/sine.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/sine_frequency.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/sine_profile.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/snr.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/sparse_solve.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/tables.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/tetmesh.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/tone_readout.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/transmissibility.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/truncate.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/unit_choices.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/validate.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/wavelet.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/decimate.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/deform.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/demo/__init__.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/demo/barc.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/demo/drone.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/demo/frame_wing.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/demo/plate.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/demo/shapes.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/__init__.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/amplitude_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/app_icon.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/ask.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/author_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/averaging_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/chance_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/confidence_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/console.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/disclaimer.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/distortion_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/drawer_tab.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/drive_prediction_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/dwell_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/editors.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/filter_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/frequency_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/glyphs.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/icons/drawer-tab-shape.svg +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/icons/drawer-tab.svg +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/instrumentation_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/levels_readout.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/mate_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/matrix_check_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/merge_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/mesh_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/mesh_solids_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/message_bar.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/mixed_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/modes_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/movie.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/octave_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/overload_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/pattern_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/preferences.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/profile_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/project_tree.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/pulse_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/record_grid.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/replication_fit_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/report_features_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/rms_check_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/run_reports.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/segments_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/settings_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/shock_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/signal_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/signature_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/srs_tolerance_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/stage_drag.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/statistics_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/stream_window.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/stroke_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/sweep_rate_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/text_import.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/tone_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/toolbars.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/tooltips.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/transmissibility_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/truncate_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/updater.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/wavelet_panel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/gui/windows.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/__init__.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/adf.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/delimited.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_objects.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/LICENSE +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/activity_result.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/channel_table.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/data.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/data_physics_random_control_parameters.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/data_physics_shock_response_synthesis_control_parameters.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/data_physics_sine_control_parameters.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/geometry.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/global_analysis_attributes.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/global_test_attributes.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/matrix.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/mechanical_control_parameters.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/modal_test_parameters.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/mode.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/parameter_set.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/point_cloud.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/rattlesnake_random_control_parameters.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/rattlesnake_sysid_parameters.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/rattlesnake_transient_control_parameters.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/response_spectrum.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/scalar.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/unknown.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/vector.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/escdf_specifications/vibration_channel_table.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/excel.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/exodus.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/exporters.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/femap.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/matlab.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/nastran.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/notes.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/photo_files.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/punch.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/rattlesnake.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/rattlesnake_sine.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/rattlesnake_specification.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/report_template.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/sdynpy_data.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/sdynpy_npz.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/sdynpy_shapes.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/sep005.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/sniffing.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/stated_units.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/step.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/stl.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/threemf.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/io/unv.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/names.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/amplitude_spectrum.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/averaging.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/bands.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/bars.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/breakpoints.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/chance.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/classical_pulse.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/drive_prediction.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/dwell.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/filtering.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/half_power.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/log_axis.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/matrix_checks.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/overload.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/replication_fit.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/replication_segments.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/report_caption.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/resample.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/rms_check.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/scalogram.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/shock_check.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/shock_synthesis.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/shocks.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/signature.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/sine_distortion.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/sine_frequency.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/sine_profile.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/sine_tracking.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/srs_tolerance.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/stroke.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/sweep_rate.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/text_import.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/time_statistics.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/tone_readout.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/tracking_filter.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/transmissibility.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/plot/truncation.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/py.typed +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/report/calibration.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/report/markdown.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/report/page.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/rotate.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/theme.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/units.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/update.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/viz/__init__.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/viz/_quiet.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/viz/banded.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/viz/mac_bars.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/viz/marks.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/viz/paired.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/viz/pick.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/viz/rigid.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/viz/scalogram.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/viz/sinespec.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/viz/waterfall.py +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics.egg-info/dependency_links.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics.egg-info/entry_points.txt +0 -0
- {visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: visualdynamics
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.0a76
|
|
4
4
|
Summary: Structural Dynamics Visualization: a units-aware structural dynamics analysis toolset
|
|
5
5
|
Author-email: Brandon Zwink <bzwink@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -16,6 +16,7 @@ Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
20
|
Classifier: Operating System :: OS Independent
|
|
20
21
|
Classifier: Environment :: X11 Applications :: Qt
|
|
21
22
|
Requires-Python: >=3.12
|
|
@@ -29,7 +30,7 @@ Requires-Dist: h5py
|
|
|
29
30
|
Requires-Dist: netCDF4
|
|
30
31
|
Requires-Dist: pint
|
|
31
32
|
Requires-Dist: pyvista
|
|
32
|
-
Requires-Dist: pyvistaqt
|
|
33
|
+
Requires-Dist: pyvistaqt>=0.13
|
|
33
34
|
Requires-Dist: vtk
|
|
34
35
|
Requires-Dist: PySide6
|
|
35
36
|
Requires-Dist: shiboken6
|
|
@@ -33,6 +33,7 @@ classifiers = [
|
|
|
33
33
|
"Programming Language :: Python :: 3",
|
|
34
34
|
"Programming Language :: Python :: 3.12",
|
|
35
35
|
"Programming Language :: Python :: 3.13",
|
|
36
|
+
"Programming Language :: Python :: 3.14",
|
|
36
37
|
"Operating System :: OS Independent",
|
|
37
38
|
"Environment :: X11 Applications :: Qt",
|
|
38
39
|
]
|
|
@@ -70,7 +71,13 @@ dependencies = [
|
|
|
70
71
|
"netCDF4",
|
|
71
72
|
"pint",
|
|
72
73
|
"pyvista",
|
|
73
|
-
|
|
74
|
+
# 0.13 made the 3-D view a QOpenGLWidget that Qt composes with the
|
|
75
|
+
# rest of the window, and the window is built for that: Qt Quick
|
|
76
|
+
# draws with OpenGL and every context has the views' format from
|
|
77
|
+
# the start (gui.opengl.one_graphics_system). A Mac still on 0.11.4
|
|
78
|
+
# crashed generating a report under that setup (2026-10-27); the
|
|
79
|
+
# floor has pip upgrade such an environment rather than leave it.
|
|
80
|
+
"pyvistaqt>=0.13",
|
|
74
81
|
# BSD-3-Clause. pyvista brings it, but this package imports
|
|
75
82
|
# `vtkmodules` directly — the stage's marks, the drag picking, the
|
|
76
83
|
# scene's axes — and what is imported is declared here rather than
|
|
@@ -189,10 +196,14 @@ line-length = 100
|
|
|
189
196
|
# — forty-six seconds, which was most of the suite's wall clock and none
|
|
190
197
|
# of it about the package. The rules that *are* about the package are
|
|
191
198
|
# stated on a three-quad strip in test_fem.py instead. Run the demo's own
|
|
192
|
-
# suite with `pytest -m slow` (CI does, in a
|
|
199
|
+
# suite with `pytest -m slow` (CI does, in a job of its own).
|
|
200
|
+
# `guide` marks the slow tests that are minutes of meshing each, the
|
|
201
|
+
# guide's tetrahedra table: CI runs them on main and weekly, not on a
|
|
202
|
+
# pull request (2026-10-28), and `-m slow` here still runs them.
|
|
193
203
|
addopts = '-n auto -m "not slow"'
|
|
194
204
|
markers = [
|
|
195
205
|
"slow: the demonstration model, built for the website and the docs — run on demand rather than on every edit",
|
|
206
|
+
"guide: the guide's mesh table, minutes of meshing a shape — checked on main and weekly, not on every pull request",
|
|
196
207
|
]
|
|
197
208
|
filterwarnings = [
|
|
198
209
|
# netCDF4's published wheel is built against an older numpy C API; the
|
|
@@ -97,7 +97,7 @@ from .units import (
|
|
|
97
97
|
#: alpha, said in the version itself so the dialog, the About text, the
|
|
98
98
|
#: installers and latest.json all say the same thing, and the first
|
|
99
99
|
#: non-alpha release is offered as an upgrade (Brandon, 2026-09-11)
|
|
100
|
-
__version__ = '0.1.
|
|
100
|
+
__version__ = '0.1.0a76'
|
|
101
101
|
|
|
102
102
|
|
|
103
103
|
def launch_gui(*paths: str, in_process: bool | None = None) -> Any:
|
|
@@ -22,8 +22,8 @@ A data object whose DOFs are all modal coordinates (`M1` ... `Mn`, see
|
|
|
22
22
|
the one it was transformed or computed through, and fits wherever there
|
|
23
23
|
is a set with at least as many modes as it names: a set in its object
|
|
24
24
|
group when it is in one, any set in the project otherwise. A time
|
|
25
|
-
history can hold both kinds of record, forces with the modal
|
|
26
|
-
computed from them
|
|
25
|
+
history can hold both kinds of record, forces merged with the modal
|
|
26
|
+
responses computed from them: its physical records answer to the
|
|
27
27
|
geometry and its modal records to the shape sets, each by its own rule.
|
|
28
28
|
|
|
29
29
|
Not checked here:
|
|
@@ -1907,11 +1907,19 @@ def read_at_lines(old_f: np.ndarray, values: np.ndarray,
|
|
|
1907
1907
|
"""A specification record read at new lines.
|
|
1908
1908
|
|
|
1909
1909
|
At its own lines the record keeps its own values. Between them it
|
|
1910
|
-
is interpolated the way a specification is read, the
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1910
|
+
is interpolated the way a specification is read, so a line the
|
|
1911
|
+
sheet added does not change what a channel the sheet does not hold
|
|
1912
|
+
means: a cross term by `compliance.cross_term_at` (the magnitude
|
|
1913
|
+
log-log between points above zero, straight through a zero, the
|
|
1914
|
+
phase linearly), a real record by its magnitude log-log over the
|
|
1915
|
+
points above zero. Outside its range it takes its end values.
|
|
1914
1916
|
"""
|
|
1917
|
+
# A cross term was read here by its own rule, bridging a zero at a
|
|
1918
|
+
# breakpoint as a gap, and the drive prediction read the sheet's
|
|
1919
|
+
# rule while the matrix comparison read the specification's: the
|
|
1920
|
+
# same cross term gave two coherences (2026-10-28). One reader now.
|
|
1921
|
+
from .compliance import cross_term_at
|
|
1922
|
+
|
|
1915
1923
|
values = np.asarray(values)
|
|
1916
1924
|
exact = np.isin(lines, old_f)
|
|
1917
1925
|
if exact.all():
|
|
@@ -1923,16 +1931,19 @@ def read_at_lines(old_f: np.ndarray, values: np.ndarray,
|
|
|
1923
1931
|
out = np.full(len(lines), np.nan, dtype=values.dtype)
|
|
1924
1932
|
out[exact] = values[np.searchsorted(old_f, lines[exact])]
|
|
1925
1933
|
new = ~exact
|
|
1934
|
+
if np.iscomplexobj(values):
|
|
1935
|
+
written = np.isfinite(values)
|
|
1936
|
+
read = cross_term_at(old_f[written], values[written], lines[new])
|
|
1937
|
+
# the end values outside the range, as the real branch holds them
|
|
1938
|
+
below, above = lines[new] < old_f[0], lines[new] > old_f[-1]
|
|
1939
|
+
read[below], read[above] = values[0], values[-1]
|
|
1940
|
+
out[new] = read
|
|
1941
|
+
return out
|
|
1926
1942
|
if positive.sum() >= 2:
|
|
1927
1943
|
size = np.exp(np.interp(log_new[new], log_old[positive],
|
|
1928
1944
|
np.log(magnitude[positive])))
|
|
1929
1945
|
else:
|
|
1930
1946
|
size = np.interp(lines[new], old_f, np.nan_to_num(magnitude))
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
np.unwrap(np.angle(values)))
|
|
1934
|
-
out[new] = size * np.exp(1j * phase)
|
|
1935
|
-
else:
|
|
1936
|
-
out[new] = size * np.sign(np.interp(lines[new], old_f,
|
|
1937
|
-
np.nan_to_num(values)))
|
|
1947
|
+
out[new] = size * np.sign(np.interp(lines[new], old_f,
|
|
1948
|
+
np.nan_to_num(values)))
|
|
1938
1949
|
return out
|
{visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/channel_table.py
RENAMED
|
@@ -484,7 +484,13 @@ class ChannelTable:
|
|
|
484
484
|
|
|
485
485
|
A unit that is not of the channel's declared type is refused;
|
|
486
486
|
change the type first. Changing the type clears a unit of another
|
|
487
|
-
kind.
|
|
487
|
+
kind. A model in the sensor catalog (`core.sensor_catalog`) fills
|
|
488
|
+
a blank Make with its maker; a make already written is kept. A
|
|
489
|
+
triaxial accelerometer's model or serial number is written once:
|
|
490
|
+
the other acceleration rows at its node, its other legs, get its
|
|
491
|
+
Make, Model and Serial Number where theirs are blank (and their
|
|
492
|
+
model is blank or the same). A row of another type at that node,
|
|
493
|
+
a force gauge, is never filled.
|
|
488
494
|
|
|
489
495
|
Parameters
|
|
490
496
|
----------
|
|
@@ -537,6 +543,43 @@ class ChannelTable:
|
|
|
537
543
|
from ..units import dimension_of
|
|
538
544
|
if dimension_of(unit) != coerced:
|
|
539
545
|
self.frame.loc[row, 'unit'] = ''
|
|
546
|
+
if name == 'model' and coerced:
|
|
547
|
+
# picking a model names its maker too (Brandon, 2026-10-26)
|
|
548
|
+
from .sensor_catalog import lookup
|
|
549
|
+
|
|
550
|
+
entry = lookup(str(coerced))
|
|
551
|
+
if entry is not None and not str(self.frame.loc[row, 'make']).strip():
|
|
552
|
+
self.frame.loc[row, 'make'] = entry.make
|
|
553
|
+
if name in ('model', 'serial_number') and coerced:
|
|
554
|
+
self._fill_triax(row)
|
|
555
|
+
|
|
556
|
+
def _fill_triax(self, row: int) -> None:
|
|
557
|
+
"""Copy a triax row's Make, Model and Serial Number into the blank
|
|
558
|
+
cells of its other legs: the other acceleration rows at its node
|
|
559
|
+
whose model is blank or the same. Only a model the catalog knows
|
|
560
|
+
is a triax is spread, and only into acceleration rows."""
|
|
561
|
+
# Brandon, 2026-10-27: a triax's identity typed three times was
|
|
562
|
+
# one too many; legs wired one or two at a time are why the cells
|
|
563
|
+
# are filled rather than merged, and a force gauge at the node is
|
|
564
|
+
# why the type has to match.
|
|
565
|
+
from .sensor_catalog import lookup
|
|
566
|
+
|
|
567
|
+
frame = self.frame
|
|
568
|
+
entry = lookup(str(frame.loc[row, 'model']))
|
|
569
|
+
node = str(frame.loc[row, 'node']).strip()
|
|
570
|
+
if (entry is None or entry.kind != 'triax' or not node
|
|
571
|
+
or str(frame.loc[row, 'channel_type']) != 'acceleration'):
|
|
572
|
+
return
|
|
573
|
+
for other in range(self.num_channels):
|
|
574
|
+
if (other == row or str(frame.loc[other, 'node']).strip() != node
|
|
575
|
+
or str(frame.loc[other, 'channel_type']) != 'acceleration'):
|
|
576
|
+
continue
|
|
577
|
+
model = str(frame.loc[other, 'model']).strip()
|
|
578
|
+
if model and lookup(model) is not entry:
|
|
579
|
+
continue
|
|
580
|
+
for column in ('make', 'model', 'serial_number'):
|
|
581
|
+
if not str(frame.loc[other, column]).strip():
|
|
582
|
+
frame.loc[other, column] = frame.loc[row, column]
|
|
540
583
|
|
|
541
584
|
def _agrees_with_type(self, row: int, unit: str) -> None:
|
|
542
585
|
"""A declared type and a unit have to describe one channel."""
|
{visualdynamics-0.1.0a74 → visualdynamics-0.1.0a76}/src/visualdynamics/core/classical_pulse.py
RENAMED
|
@@ -906,28 +906,14 @@ def check_pulse(history: TimeHistory, pulse: ClassicalPulse,
|
|
|
906
906
|
def pulse_channel(history: TimeHistory,
|
|
907
907
|
channel: int | str | None = None) -> int:
|
|
908
908
|
"""The row of `history` a pulse is read from: `channel` by index or
|
|
909
|
-
DOF, or the first acceleration channel
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
if isinstance(channel, str):
|
|
921
|
-
dofs = [str(dof) for dof in history.response_dof]
|
|
922
|
-
if channel not in dofs:
|
|
923
|
-
raise ValueError(f'the record has no channel {channel}')
|
|
924
|
-
row = dofs.index(channel)
|
|
925
|
-
else:
|
|
926
|
-
row = int(channel)
|
|
927
|
-
if not 0 <= row < history.num_records:
|
|
928
|
-
raise ValueError(f'the record has {history.num_records} '
|
|
929
|
-
f'channels; there is no channel {row}')
|
|
930
|
-
if history.known_dim(row) != 'acceleration':
|
|
931
|
-
raise ValueError(f'{history.response_dof[row]} is not an '
|
|
932
|
-
'acceleration, and a pulse is read from one')
|
|
933
|
-
return row
|
|
909
|
+
DOF, or the first acceleration channel whose unit is declared
|
|
910
|
+
(`core.shaker.acceleration_channel`, the one rule for the stroke
|
|
911
|
+
and the replication fit too); refused otherwise, since a pulse's
|
|
912
|
+
velocity change is the area under an acceleration in a known scale
|
|
913
|
+
and nothing else."""
|
|
914
|
+
# It read a channel by `known_dim` (2026-10-28), so a record whose
|
|
915
|
+
# unit was only hinted was judged raw against an SI nominal: a 50 g
|
|
916
|
+
# half-sine stored in g passed as 5.1 g with every point inside.
|
|
917
|
+
from .shaker import acceleration_channel
|
|
918
|
+
|
|
919
|
+
return acceleration_channel(history, channel)
|
|
@@ -88,6 +88,116 @@ def log_interpolate(frequencies: ArrayLike, spec_frequencies: ArrayLike,
|
|
|
88
88
|
return out
|
|
89
89
|
|
|
90
90
|
|
|
91
|
+
def cross_term_at(frequencies: ArrayLike, values: ArrayLike,
|
|
92
|
+
lines: ArrayLike) -> np.ndarray:
|
|
93
|
+
"""A cross term written at breakpoints, read at `lines` the way a
|
|
94
|
+
specification is drawn: the magnitude log-log between two points
|
|
95
|
+
that are both above zero and straight between two that are not (a
|
|
96
|
+
coherence of zero, two channels stated to be independent, is a
|
|
97
|
+
value and not a gap), the phase linearly. Not a number outside the
|
|
98
|
+
points, and at lines at or below zero hertz.
|
|
99
|
+
|
|
100
|
+
Parameters
|
|
101
|
+
----------
|
|
102
|
+
frequencies : array_like
|
|
103
|
+
The breakpoints' frequencies, in Hz, in any order.
|
|
104
|
+
values : array_like
|
|
105
|
+
The complex cross term at each breakpoint.
|
|
106
|
+
lines : array_like
|
|
107
|
+
Where to read it.
|
|
108
|
+
|
|
109
|
+
Returns
|
|
110
|
+
-------
|
|
111
|
+
numpy.ndarray
|
|
112
|
+
Complex, one value per line.
|
|
113
|
+
"""
|
|
114
|
+
# Two readers of this existed, one in the matrix comparison and one
|
|
115
|
+
# in the drive prediction through the authoring sheet, and they
|
|
116
|
+
# disagreed on a zero at a breakpoint: the sheet's bridged it as a
|
|
117
|
+
# gap, so one specification read 0.81 coherence in the prediction
|
|
118
|
+
# and 0.32 in the comparison at the same line (2026-10-28). This is
|
|
119
|
+
# the one reader, and the zero is a statement.
|
|
120
|
+
frequencies = np.asarray(frequencies, dtype=float)
|
|
121
|
+
values = np.asarray(values)
|
|
122
|
+
lines = np.asarray(lines, dtype=float)
|
|
123
|
+
usable = np.isfinite(frequencies) & (frequencies > 0.0)
|
|
124
|
+
order = np.argsort(frequencies[usable])
|
|
125
|
+
f = frequencies[usable][order]
|
|
126
|
+
v = values[usable][order].astype(complex)
|
|
127
|
+
out = np.full(lines.shape, np.nan, dtype=complex)
|
|
128
|
+
if f.size < 2:
|
|
129
|
+
return out
|
|
130
|
+
inside = (lines >= f[0]) & (lines <= f[-1])
|
|
131
|
+
at = lines[inside]
|
|
132
|
+
k = np.clip(np.searchsorted(f, at, side='right') - 1, 0, f.size - 2)
|
|
133
|
+
m1, m2 = np.abs(v[k]), np.abs(v[k + 1])
|
|
134
|
+
part = (at - f[k]) / (f[k + 1] - f[k])
|
|
135
|
+
with np.errstate(divide='ignore', invalid='ignore'):
|
|
136
|
+
logs = np.exp(np.log(m1) + (np.log(m2) - np.log(m1))
|
|
137
|
+
* np.log(at / f[k]) / np.log(f[k + 1] / f[k]))
|
|
138
|
+
magnitude = np.where((m1 > 0.0) & (m2 > 0.0), logs,
|
|
139
|
+
m1 + (m2 - m1) * part)
|
|
140
|
+
phase = np.interp(at, f, np.unwrap(np.angle(v)))
|
|
141
|
+
out[inside] = magnitude * np.exp(1j * phase)
|
|
142
|
+
return out
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def record_at(data: Any, record: int, lines: ArrayLike) -> np.ndarray:
|
|
146
|
+
"""One record of a specification read at `lines`, the way it is
|
|
147
|
+
drawn, and not a number where it is not written.
|
|
148
|
+
|
|
149
|
+
An autospectrum is written where it is above zero, and between
|
|
150
|
+
breakpoints ('log_log') is read log-log with its gaps
|
|
151
|
+
(`log_interpolate`). A cross term is written wherever it is a
|
|
152
|
+
number, zero included, and is read between breakpoints by
|
|
153
|
+
`cross_term_at`; whether its coherence is defined is for its two
|
|
154
|
+
autospectra to say. On lines or bands ('bin') a record takes the
|
|
155
|
+
value of the bin the line falls in. The matrix comparison and the
|
|
156
|
+
drive prediction both read a specification here.
|
|
157
|
+
|
|
158
|
+
Parameters
|
|
159
|
+
----------
|
|
160
|
+
data : Psd or Specification
|
|
161
|
+
The specification.
|
|
162
|
+
record : int
|
|
163
|
+
Which record.
|
|
164
|
+
lines : array_like
|
|
165
|
+
Where to read it, in Hz.
|
|
166
|
+
|
|
167
|
+
Returns
|
|
168
|
+
-------
|
|
169
|
+
numpy.ndarray
|
|
170
|
+
Real for an autospectrum, complex for a cross term.
|
|
171
|
+
"""
|
|
172
|
+
from .data import autospectrum
|
|
173
|
+
|
|
174
|
+
lines = np.asarray(lines, dtype=float)
|
|
175
|
+
frequencies = np.asarray(data.abscissa, dtype=float)
|
|
176
|
+
values = np.asarray(data.ordinate)[record]
|
|
177
|
+
auto = autospectrum(data, record)
|
|
178
|
+
written = np.isfinite(frequencies) & (
|
|
179
|
+
data.written(record) if auto else np.isfinite(values))
|
|
180
|
+
if getattr(data, 'interpolation', 'bin') == 'log_log':
|
|
181
|
+
if auto:
|
|
182
|
+
return log_interpolate(lines, frequencies, np.real(values))
|
|
183
|
+
return cross_term_at(frequencies[written], values[written], lines)
|
|
184
|
+
left, right = data.bin_bounds()
|
|
185
|
+
order = np.argsort(left)
|
|
186
|
+
left, right = left[order], right[order]
|
|
187
|
+
out = np.full(lines.shape, np.nan, dtype=float if auto else complex)
|
|
188
|
+
if not left.size:
|
|
189
|
+
return out
|
|
190
|
+
# the last bin starting at or below each line, if that bin reaches it
|
|
191
|
+
k = np.searchsorted(left, lines, side='right') - 1
|
|
192
|
+
found = k >= 0
|
|
193
|
+
k = np.maximum(k, 0)
|
|
194
|
+
found &= (lines >= left[k]) & (lines < right[k])
|
|
195
|
+
source = order[k]
|
|
196
|
+
found &= written[source]
|
|
197
|
+
out[found] = (np.real(values) if auto else values)[source[found]]
|
|
198
|
+
return out
|
|
199
|
+
|
|
200
|
+
|
|
91
201
|
def log_log_areas(frequencies: ArrayLike, values: ArrayLike,
|
|
92
202
|
lows: ArrayLike, highs: ArrayLike) -> np.ndarray:
|
|
93
203
|
"""The exact area under the log-log curve through these points, over
|
|
@@ -423,6 +533,24 @@ def cells(specification: Specification, measured: DataArray,
|
|
|
423
533
|
return out
|
|
424
534
|
|
|
425
535
|
|
|
536
|
+
def cell_lines(cell: dict[str, Any], left: np.ndarray,
|
|
537
|
+
right: np.ndarray) -> slice | None:
|
|
538
|
+
"""The measured lines one cell of `cells` reaches, as a slice of
|
|
539
|
+
the bins whose edges are `left` and `right` (sorted), or None when
|
|
540
|
+
it reaches none.
|
|
541
|
+
|
|
542
|
+
Found by where the cell's ends fall among the bins: a bin that ends
|
|
543
|
+
where the cell begins shares an edge and no width, and is not in
|
|
544
|
+
it. `judge` marks the lines of an out cell by this, and a reading
|
|
545
|
+
that spreads a cell's result over its lines uses the same rule.
|
|
546
|
+
"""
|
|
547
|
+
first = int(np.searchsorted(right, cell['low'] * (1.0 + 1e-12),
|
|
548
|
+
side='right'))
|
|
549
|
+
last = int(np.searchsorted(left, cell['high'] * (1.0 - 1e-12),
|
|
550
|
+
side='left'))
|
|
551
|
+
return slice(first, last) if last > first else None
|
|
552
|
+
|
|
553
|
+
|
|
426
554
|
def judge(specification: Specification, measured: DataArray,
|
|
427
555
|
spec_record: int = 0, measured_record: int = 0,
|
|
428
556
|
limit: str | None = 'abort_upper', over: bool = True,
|
|
@@ -494,16 +622,11 @@ def judge(specification: Specification, measured: DataArray,
|
|
|
494
622
|
lines = np.zeros(left.shape, dtype=bool)
|
|
495
623
|
level = np.full(left.shape, np.nan)
|
|
496
624
|
start, stop = left.copy(), right.copy()
|
|
497
|
-
# the lines each out cell reaches, found by where the cell's ends
|
|
498
|
-
# fall among the sorted bins: a bin that ends where the cell
|
|
499
|
-
# begins shares an edge and no width, and is not in it
|
|
500
625
|
for k in np.flatnonzero(out):
|
|
501
626
|
cell = found[k]
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
if last <= first:
|
|
627
|
+
inside = cell_lines(cell, left, right)
|
|
628
|
+
if inside is None:
|
|
505
629
|
continue
|
|
506
|
-
inside = slice(first, last)
|
|
507
630
|
lines[inside] = True
|
|
508
631
|
level[inside] = asked[k] / cell['width']
|
|
509
632
|
start[inside] = np.maximum(left[inside], cell['low'])
|
|
@@ -816,6 +939,7 @@ def detect_scale_db(specification: Specification, measured: DataArray,
|
|
|
816
939
|
# the same bands. `significant_band` is there because a waveform's
|
|
817
940
|
# spectrum ending at 2 kHz left half a transient's bands 40 dB apart,
|
|
818
941
|
# saying nothing about level.
|
|
942
|
+
from .data import autospectrum
|
|
819
943
|
from .octave import PER_OCTAVE
|
|
820
944
|
|
|
821
945
|
if comparable(specification, measured) is not None:
|
|
@@ -833,6 +957,10 @@ def detect_scale_db(specification: Specification, measured: DataArray,
|
|
|
833
957
|
if wanted is not None and label not in wanted and str(
|
|
834
958
|
specification.response_dof[spec_index]) not in wanted:
|
|
835
959
|
continue
|
|
960
|
+
# a cross term has no level to read a scale from: its real part
|
|
961
|
+
# moves with its phase
|
|
962
|
+
if not autospectrum(specification, spec_index):
|
|
963
|
+
continue
|
|
836
964
|
# a pair the comparison refuses has no level to read a scale from
|
|
837
965
|
if units_check(specification.ordinate_dim[spec_index],
|
|
838
966
|
measured.ordinate_dim[measured_index])[0] is not None:
|
|
@@ -973,6 +1101,16 @@ def exceedances(specification: Specification, measured: DataArray,
|
|
|
973
1101
|
return marks[0], marks[1]
|
|
974
1102
|
|
|
975
1103
|
|
|
1104
|
+
#: why `compare` does not read a cross term. A cross spectrum is complex,
|
|
1105
|
+
#: and its real part is not the power of either channel: read as a
|
|
1106
|
+
#: level, a cross term held at its specified magnitude but 60 degrees
|
|
1107
|
+
#: off its phase came out 3 dB low, and one 120 degrees off had no
|
|
1108
|
+
#: level at all. Its coherence and phase are what is judged
|
|
1109
|
+
#: (`core.matrix_comparison`).
|
|
1110
|
+
CROSS_TERM_REFUSAL = ('a cross term is judged by its coherence and phase '
|
|
1111
|
+
'(the matrix comparison), not by its level')
|
|
1112
|
+
|
|
1113
|
+
|
|
976
1114
|
def compare(specification: Specification, measured: DataArray,
|
|
977
1115
|
spec_record: int = 0,
|
|
978
1116
|
measured_record: int = 0,
|
|
@@ -992,17 +1130,20 @@ def compare(specification: Specification, measured: DataArray,
|
|
|
992
1130
|
fraction of its bin that was judged, so a band at the top of the
|
|
993
1131
|
range counts the same as one at the bottom.
|
|
994
1132
|
|
|
995
|
-
A pair of objects that cannot be compared (`comparable`),
|
|
1133
|
+
A pair of objects that cannot be compared (`comparable`), a pair
|
|
996
1134
|
of records of different quantities or with only one side's units
|
|
997
|
-
declared (`units_check`),
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
'
|
|
1135
|
+
declared (`units_check`), or a specification record that is a cross
|
|
1136
|
+
term (`CROSS_TERM_REFUSAL`), returns {'lines': 0, 'refused':
|
|
1137
|
+
reason}, and one with nothing to compare returns {'lines': 0}. With
|
|
1138
|
+
neither side's units declared the pair is compared and the result
|
|
1139
|
+
carries 'note' (`RAW_NOTE`).
|
|
1001
1140
|
|
|
1002
1141
|
Every number is of the scaled measurement: `scale_db` is resolved
|
|
1003
1142
|
through `comparison_scale_db` unless given, and is returned so a
|
|
1004
1143
|
table can say what was compared.
|
|
1005
1144
|
"""
|
|
1145
|
+
from .data import autospectrum
|
|
1146
|
+
|
|
1006
1147
|
if scale_db is None:
|
|
1007
1148
|
scale_db = comparison_scale_db(specification, measured)
|
|
1008
1149
|
# the level, cell by cell: both integrated over the very same
|
|
@@ -1013,6 +1154,8 @@ def compare(specification: Specification, measured: DataArray,
|
|
|
1013
1154
|
refused = comparable(specification, measured)
|
|
1014
1155
|
if refused is not None:
|
|
1015
1156
|
return {'lines': 0, 'refused': refused}
|
|
1157
|
+
if not autospectrum(specification, spec_record):
|
|
1158
|
+
return {'lines': 0, 'refused': CROSS_TERM_REFUSAL}
|
|
1016
1159
|
refused, note = units_check(specification.ordinate_dim[spec_record],
|
|
1017
1160
|
measured.ordinate_dim[measured_record],
|
|
1018
1161
|
wanted_name='the specification')
|
|
@@ -1284,10 +1427,11 @@ def compare_all(specification: Specification, measured: DataArray,
|
|
|
1284
1427
|
result from `compare`.
|
|
1285
1428
|
|
|
1286
1429
|
A specification bounds the control channels, and a measurement
|
|
1287
|
-
usually holds those and many more. Channels are paired by DOF
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1430
|
+
usually holds those and many more. Channels are paired by DOF and
|
|
1431
|
+
quantity (`matched_records`). Only the specification's autospectra
|
|
1432
|
+
are compared here: a cross term is not a level, and its coherence
|
|
1433
|
+
and phase are judged by `core.matrix_comparison.compare_matrix`,
|
|
1434
|
+
which compares the autospectra through this same function.
|
|
1291
1435
|
|
|
1292
1436
|
Rows are in the specification's order, the order a control room
|
|
1293
1437
|
reads its channels in. The scale is resolved once for the whole set,
|
|
@@ -1301,24 +1445,46 @@ def compare_all(specification: Specification, measured: DataArray,
|
|
|
1301
1445
|
'scale_db', but `channel_errors` and the charts built from it do
|
|
1302
1446
|
not. Pass `scale_db=0` to compare as measured.
|
|
1303
1447
|
"""
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
import warnings
|
|
1311
|
-
|
|
1312
|
-
warnings.warn(
|
|
1313
|
-
f'the measurement was scaled by {scale_db:+g} dB, detected '
|
|
1314
|
-
f'from the data as a run at {-scale_db:+g} dB; pass '
|
|
1315
|
-
'scale_db=0 to compare it as measured, or set '
|
|
1316
|
-
'measured.scale_db to the level it was run at',
|
|
1317
|
-
ScaleWarning, stacklevel=2)
|
|
1448
|
+
from .data import autospectrum
|
|
1449
|
+
|
|
1450
|
+
scale_db = resolved_scale_db(specification, measured, spec_records,
|
|
1451
|
+
measured_records, scale_db, stacklevel=3)
|
|
1452
|
+
# cross terms out since 2026-10-24: `compare` read one by its real
|
|
1453
|
+
# part, which moves with its phase
|
|
1318
1454
|
return [(label, compare(specification, measured, spec_index,
|
|
1319
1455
|
measured_index, scale_db=scale_db))
|
|
1320
1456
|
for label, spec_index, measured_index in matched_records(
|
|
1321
|
-
specification, measured, spec_records, measured_records)
|
|
1457
|
+
specification, measured, spec_records, measured_records)
|
|
1458
|
+
if autospectrum(specification, spec_index)]
|
|
1459
|
+
|
|
1460
|
+
|
|
1461
|
+
def resolved_scale_db(specification: Specification, measured: DataArray,
|
|
1462
|
+
spec_records: Sequence[int] | None = None,
|
|
1463
|
+
measured_records: Sequence[int] | None = None,
|
|
1464
|
+
scale_db: float | None = None,
|
|
1465
|
+
stacklevel: int = 2) -> float:
|
|
1466
|
+
"""The one scale a whole set of comparisons is made at: `scale_db`
|
|
1467
|
+
when given, else `comparison_scale_db`'s, with a `ScaleWarning`
|
|
1468
|
+
when that scale was detected from the data rather than set on the
|
|
1469
|
+
measurement. `compare_all` and the matrix comparison resolve their
|
|
1470
|
+
scale here, so both warn alike. `stacklevel` points the warning at
|
|
1471
|
+
the caller's caller."""
|
|
1472
|
+
# the warning is there because a whole campaign was once scored on
|
|
1473
|
+
# shifted numbers without a word (2026-09-26)
|
|
1474
|
+
if scale_db is not None:
|
|
1475
|
+
return scale_db
|
|
1476
|
+
scale_db = comparison_scale_db(specification, measured,
|
|
1477
|
+
spec_records, measured_records)
|
|
1478
|
+
if scale_db and getattr(measured, 'scale_db', None) is None:
|
|
1479
|
+
import warnings
|
|
1480
|
+
|
|
1481
|
+
warnings.warn(
|
|
1482
|
+
f'the measurement was scaled by {scale_db:+g} dB, detected '
|
|
1483
|
+
f'from the data as a run at {-scale_db:+g} dB; pass '
|
|
1484
|
+
'scale_db=0 to compare it as measured, or set '
|
|
1485
|
+
'measured.scale_db to the level it was run at',
|
|
1486
|
+
ScaleWarning, stacklevel=stacklevel)
|
|
1487
|
+
return scale_db
|
|
1322
1488
|
|
|
1323
1489
|
|
|
1324
1490
|
#: The RMS dB deviation an SRS comparison is shaded past, either side.
|