signalviewer 1.8.0__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.
- signalviewer-1.8.0/LICENSE +21 -0
- signalviewer-1.8.0/PKG-INFO +517 -0
- signalviewer-1.8.0/README.md +479 -0
- signalviewer-1.8.0/pyproject.toml +37 -0
- signalviewer-1.8.0/setup.cfg +4 -0
- signalviewer-1.8.0/signalviewer/__init__.py +84 -0
- signalviewer-1.8.0/signalviewer/__main__.py +5 -0
- signalviewer-1.8.0/signalviewer/_core/__init__.py +0 -0
- signalviewer-1.8.0/signalviewer/_core/app/__init__.py +0 -0
- signalviewer-1.8.0/signalviewer/_core/app/basemap.py +417 -0
- signalviewer-1.8.0/signalviewer/_core/app/basemap_data/__init__.py +0 -0
- signalviewer-1.8.0/signalviewer/_core/app/basemap_data/dem_israel.npz +0 -0
- signalviewer-1.8.0/signalviewer/_core/app/basemap_data/dem_region.npz +0 -0
- signalviewer-1.8.0/signalviewer/_core/app/basemap_data/dem_world.npz +0 -0
- signalviewer-1.8.0/signalviewer/_core/app/basemap_data/vectors_israel.json.gz +0 -0
- signalviewer-1.8.0/signalviewer/_core/app/basemap_data/vectors_world.json.gz +0 -0
- signalviewer-1.8.0/signalviewer/_core/app/batch.py +148 -0
- signalviewer-1.8.0/signalviewer/_core/app/common.py +93 -0
- signalviewer-1.8.0/signalviewer/_core/app/crashlog.py +109 -0
- signalviewer-1.8.0/signalviewer/_core/app/fleet.py +330 -0
- signalviewer-1.8.0/signalviewer/_core/app/instance.py +76 -0
- signalviewer-1.8.0/signalviewer/_core/app/jobs.py +95 -0
- signalviewer-1.8.0/signalviewer/_core/app/main.py +178 -0
- signalviewer-1.8.0/signalviewer/_core/app/patterns.py +8 -0
- signalviewer-1.8.0/signalviewer/_core/app/prefs.py +368 -0
- signalviewer-1.8.0/signalviewer/_core/app/push.py +120 -0
- signalviewer-1.8.0/signalviewer/_core/app/pyops.py +165 -0
- signalviewer-1.8.0/signalviewer/_core/app/report.py +773 -0
- signalviewer-1.8.0/signalviewer/_core/app/report_assets/__init__.py +0 -0
- signalviewer-1.8.0/signalviewer/_core/app/report_assets/fonts/OFL-LICENSE.txt +93 -0
- signalviewer-1.8.0/signalviewer/_core/app/report_assets/fonts/__init__.py +0 -0
- signalviewer-1.8.0/signalviewer/_core/app/report_assets/fonts/ibm-plex-sans-hebrew-hebrew-400-normal.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/app/report_assets/fonts/ibm-plex-sans-hebrew-hebrew-600-normal.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/app/report_assets/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/app/report_assets/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/app/report_assets/uPlot.LICENSE +21 -0
- signalviewer-1.8.0/signalviewer/_core/app/report_assets/uPlot.iife.min.js +2 -0
- signalviewer-1.8.0/signalviewer/_core/app/report_assets/uPlot.min.css +1 -0
- signalviewer-1.8.0/signalviewer/_core/app/report_assets/viewer.js +251 -0
- signalviewer-1.8.0/signalviewer/_core/app/routes/__init__.py +1 -0
- signalviewer-1.8.0/signalviewer/_core/app/routes/analysis.py +380 -0
- signalviewer-1.8.0/signalviewer/_core/app/routes/compare.py +157 -0
- signalviewer-1.8.0/signalviewer/_core/app/routes/data.py +430 -0
- signalviewer-1.8.0/signalviewer/_core/app/routes/export.py +184 -0
- signalviewer-1.8.0/signalviewer/_core/app/routes/files.py +340 -0
- signalviewer-1.8.0/signalviewer/_core/app/routes/fleet.py +67 -0
- signalviewer-1.8.0/signalviewer/_core/app/routes/fs.py +234 -0
- signalviewer-1.8.0/signalviewer/_core/app/routes/jobs.py +105 -0
- signalviewer-1.8.0/signalviewer/_core/app/routes/live.py +125 -0
- signalviewer-1.8.0/signalviewer/_core/app/routes/maps.py +46 -0
- signalviewer-1.8.0/signalviewer/_core/app/routes/push.py +87 -0
- signalviewer-1.8.0/signalviewer/_core/app/routes/pyops.py +62 -0
- signalviewer-1.8.0/signalviewer/_core/app/routes/session.py +191 -0
- signalviewer-1.8.0/signalviewer/_core/app/routes/system.py +78 -0
- signalviewer-1.8.0/signalviewer/_core/app/routes/templates.py +185 -0
- signalviewer-1.8.0/signalviewer/_core/app/routes/video.py +56 -0
- signalviewer-1.8.0/signalviewer/_core/app/store.py +824 -0
- signalviewer-1.8.0/signalviewer/_core/app/tiles.py +98 -0
- signalviewer-1.8.0/signalviewer/_core/app/version.py +3 -0
- signalviewer-1.8.0/signalviewer/_core/examples/__init__.py +0 -0
- signalviewer-1.8.0/signalviewer/_core/examples/copter_flight.bin +0 -0
- signalviewer-1.8.0/signalviewer/_core/examples/run_012.csv +6001 -0
- signalviewer-1.8.0/signalviewer/_core/examples/run_013.csv +6001 -0
- signalviewer-1.8.0/signalviewer/_core/examples/run_014.csv +6001 -0
- signalviewer-1.8.0/signalviewer/_core/examples/uav_flight.csv +6001 -0
- signalviewer-1.8.0/signalviewer/_core/examples/uav_flight_windy.csv +6001 -0
- signalviewer-1.8.0/signalviewer/_core/run.py +315 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/__init__.py +0 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/analysis.py +626 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/bin_loader.py +598 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/builtin_templates.json +1613 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/compare.py +202 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/csv_loader.py +832 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/decimate.py +279 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/envelope.py +43 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/flightlog.py +75 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/formula.py +195 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/hdf5_loader.py +158 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/health.py +279 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/limits.py +49 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/mat_loader.py +210 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/mavlive.py +180 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/mcap_loader.py +225 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/models.py +247 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/naming.py +103 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/ops.py +276 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/parquet_loader.py +127 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/pyworker.py +158 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/sync.py +370 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/templates.py +294 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/time_pairs.py +115 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/tlog_loader.py +439 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/ulog_loader.py +152 -0
- signalviewer-1.8.0/signalviewer/_core/svcore/xyz.py +32 -0
- signalviewer-1.8.0/signalviewer/_core/ui/__init__.py +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/AnalysisPanel-ByQ6ZIbY.js +2 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/Cesium-B8iOCw7W.js +18248 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ComparePanel-ZoGSOpUp.js +2 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/CompareTable-BKF4QqKD.js +2 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ExprInput-Aic1BpA8.js +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/FleetPanel-BpCE62qL.js +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/GUIDE-Ddv0ytPq.js +117 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/GUIDE.he-DMAjlf7i.js +107 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/Globe3D-DX4wF4ms.js +2 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/Globe3D-ZYnpHEFn.css +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/GuideDialog-wPvL8f3P.js +2 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/Help-DeaOhyVD.js +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ImportDialog-Cpb91O3m.js +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/LogPanel-BtzscNkI.js +3 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ObjectDialog-SEzCYD_7.js +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/PlaceOnMapDialog-_g48Yf7F.js +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ReportPanel-DwmNirsp.js +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ReportPreview-4f7ajURz.js +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/RetargetDialog-DTVQOLwm.js +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/SettingsDialog-AtUhRdCl.js +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/SyncDialog-uKzHkCZb.js +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/TemplateLibrary-mQ0Ctpvq.js +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/TransformDialog-BtBZnOR4.js +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/VideoView-Cx0hOldo.js +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/__init__.py +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/frames-BUQWGJvT.js +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-cyrillic-400-normal-BTotfTJu.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-cyrillic-400-normal-DZqxrq2p.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-cyrillic-500-normal-ByOcLdNv.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-cyrillic-500-normal-CocWQlwt.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-cyrillic-600-normal-71GNu3SW.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-cyrillic-600-normal-BGq0mW3O.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-cyrillic-ext-400-normal-Dsrv2Tcn.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-cyrillic-ext-400-normal-g30qAdWV.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-cyrillic-ext-500-normal-Cs5J6C77.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-cyrillic-ext-500-normal-DB5PtV2g.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-cyrillic-ext-600-normal-Bz0x94Yp.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-cyrillic-ext-600-normal-DUMzJB7m.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-greek-400-normal-D9ESIMu3.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-greek-400-normal-_efipK4i.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-greek-500-normal-CuWXN6rf.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-greek-500-normal-JMMifIXV.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-greek-600-normal-D-CqTdkO.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-greek-600-normal-DzTrcv_p.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-hebrew-hebrew-400-normal-CPsCACZF.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-hebrew-hebrew-400-normal-DtcEHBoE.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-hebrew-hebrew-500-normal-BiCWpF8d.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-hebrew-hebrew-500-normal-Ddg_2VaK.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-hebrew-hebrew-600-normal-CQU8iefp.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-hebrew-hebrew-600-normal-C_LU4WLU.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-hebrew-latin-400-normal-BlLOxiF5.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-hebrew-latin-400-normal-LNOA4mnG.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-hebrew-latin-500-normal-3kJePiyr.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-hebrew-latin-500-normal-CPKaftYY.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-hebrew-latin-600-normal-B_0mKknD.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-hebrew-latin-600-normal-D8In27CT.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-hebrew-latin-ext-400-normal-CWZcAcQ-.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-hebrew-latin-ext-400-normal-CoGypoWm.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-hebrew-latin-ext-500-normal-CJ8nbqtk.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-hebrew-latin-ext-500-normal-CuH7nMAG.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-hebrew-latin-ext-600-normal-BR-dSdDq.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-hebrew-latin-ext-600-normal-DhIRDdWU.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-latin-ext-400-normal-C5H60-Va.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-latin-ext-400-normal-RBey6euL.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-latin-ext-500-normal-D0aIdm-b.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-latin-ext-500-normal-DakdToA3.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-latin-ext-600-normal-DIrixKbi.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-latin-ext-600-normal-DOrvGEcy.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-vietnamese-400-normal-DG4YqDda.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-vietnamese-400-normal-fK1oJ5dG.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-vietnamese-500-normal-BEb3_waV.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-vietnamese-500-normal-e4dixQRQ.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-vietnamese-600-normal-DgdngZtN.woff +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/ibm-plex-sans-vietnamese-600-normal-DpPYBSTl.woff2 +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/index-BDvhyAoU.css +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/index-BRHcHeNE.js +16 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/preload-helper-BKNWuTSc.js +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/segments-BHQvsAZk.js +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/store-BDHEr0ft.js +2 -0
- signalviewer-1.8.0/signalviewer/_core/ui/assets/worldCountries-DTBsAzb4.js +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Assets/Images/__init__.py +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Assets/Images/bing_maps_credit.png +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Assets/Images/cesium_credit.png +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Assets/Images/google_earth_credit.png +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Assets/Images/ion-credit.png +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Assets/__init__.py +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Assets/approximateTerrainHeights.json +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/ThirdParty/Workers/__init__.py +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/ThirdParty/Workers/package.json +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/ThirdParty/Workers/zip-web-worker.js +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/ThirdParty/__init__.py +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/ThirdParty/basis_transcoder.wasm +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/ThirdParty/draco_decoder.wasm +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/ThirdParty/google-earth-dbroot-parser.js +1 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/ThirdParty/wasm_splats_bg.wasm +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/ThirdParty/zip-module.wasm +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Widgets/CesiumWidget/CesiumWidget.css +119 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Widgets/CesiumWidget/__init__.py +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Widgets/CesiumWidget/lighter.css +14 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Widgets/__init__.py +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/__init__.py +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-2X5O55FT.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-2Z5ROPWA.js +28 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-3UWTFT5C.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-4FYX2TIB.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-5LJSZ5QI.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-5MJM22JO.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-5ZC5RGYZ.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-6BBZNYDX.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-6ZKGZZOJ.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-7ISS7FL3.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-7VA5PH7H.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-7VKG2T2K.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-A7XN2WE4.js +28 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-AE2X3VOY.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-B2EYCJFP.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-C2BJQ5BV.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-C56CH3QH.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-CAOBKNCT.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-CM2CEKZH.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-CRBPAMOT.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-CXF7IXM3.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-FR3EIV3D.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-G7SEP2D7.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-GME3JRJ5.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-H3LK4GAX.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-HV5UNN3G.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-HXD4BXHU.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-IU5DFVEB.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-K7WHNMF7.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-KGDHXBGK.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-KPLTYDCZ.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-KRWMVIB7.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-KUNU57LF.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-LINIBQGU.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-LK26B6IJ.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-LNFEKMMV.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-MMOHJWGL.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-MVPJ3TXD.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-NNVR3ORE.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-NQ23OHF7.js +63 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-NVY6OTQX.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-OSDTVMFT.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-PJAQIYS5.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-PORF43EC.js +27 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-QZXBZP7L.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-R33GIGQZ.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-RMA3XZED.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-RO3XFS37.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-S7GDAEEX.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-SLUQ566D.js +27 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-TUO3HKCZ.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-U6RTDYNE.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-VIIAT3WL.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-VZF5ZKSB.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-WSA4IOQR.js +29 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-XWHQ3HEC.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/chunk-YO6NOGYA.js +29 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/combineGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createBoxGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createBoxOutlineGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createCircleGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createCircleOutlineGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createCoplanarPolygonGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createCoplanarPolygonOutlineGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createCorridorGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createCorridorOutlineGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createCylinderGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createCylinderOutlineGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createEllipseGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createEllipseOutlineGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createEllipsoidGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createEllipsoidOutlineGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createFrustumGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createFrustumOutlineGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createGroundPolylineGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createPlaneGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createPlaneOutlineGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createPolygonGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createPolygonOutlineGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createPolylineGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createPolylineVolumeGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createPolylineVolumeOutlineGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createRectangleGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createRectangleOutlineGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createSimplePolylineGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createSphereGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createSphereOutlineGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createTaskProcessorWorker.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createVectorTileClampedPolylines.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createVectorTileGeometries.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createVectorTilePoints.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createVectorTilePolygons.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createVectorTilePolylines.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createVerticesFromCesium3DTilesTerrain.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createVerticesFromGoogleEarthEnterpriseBuffer.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createVerticesFromHeightmap.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createVerticesFromQuantizedTerrainMesh.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createWallGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/createWallOutlineGeometry.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/decodeDraco.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/decodeGoogleEarthEnterprisePacket.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/decodeI3S.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/gaussianSplatSorter.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/gaussianSplatTextureGenerator.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/incrementallyBuildTerrainPicker.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/transcodeKTX2.js +56 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/transferTypedArrayTest.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/upsampleQuantizedTerrainMesh.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/Workers/upsampleVerticesFromCesium3DTilesTerrain.js +26 -0
- signalviewer-1.8.0/signalviewer/_core/ui/cesium/__init__.py +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/favicon-32.png +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/favicon.svg +8 -0
- signalviewer-1.8.0/signalviewer/_core/ui/icon-512.png +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/icons.svg +24 -0
- signalviewer-1.8.0/signalviewer/_core/ui/index.html +18 -0
- signalviewer-1.8.0/signalviewer/_core/ui/models/__init__.py +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/models/aircraft.glb +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/models/drone.glb +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/models/missile.glb +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/models/ship.glb +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/models/uav.glb +0 -0
- signalviewer-1.8.0/signalviewer/_core/ui/models/vehicle.glb +0 -0
- signalviewer-1.8.0/signalviewer/cli.py +142 -0
- signalviewer-1.8.0/signalviewer/launcher.py +18 -0
- signalviewer-1.8.0/signalviewer/push_client.py +82 -0
- signalviewer-1.8.0/signalviewer.egg-info/PKG-INFO +517 -0
- signalviewer-1.8.0/signalviewer.egg-info/SOURCES.txt +332 -0
- signalviewer-1.8.0/signalviewer.egg-info/dependency_links.txt +1 -0
- signalviewer-1.8.0/signalviewer.egg-info/entry_points.txt +3 -0
- signalviewer-1.8.0/signalviewer.egg-info/requires.txt +22 -0
- signalviewer-1.8.0/signalviewer.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 David Alpert
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: signalviewer
|
|
3
|
+
Version: 1.8.0
|
|
4
|
+
Summary: Look at recorded signals - test runs, flight logs (ArduPilot, PX4, MAVLink), simulation output - offline.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/DavidAlpert11/SignalViewer
|
|
7
|
+
Project-URL: Guide, https://github.com/DavidAlpert11/SignalViewer/blob/main/docs/GUIDE.md
|
|
8
|
+
Keywords: signals,plotting,flight log,ardupilot,px4,mavlink,telemetry,csv,matlab
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Requires-Dist: fastapi>=0.110
|
|
17
|
+
Requires-Dist: pydantic>=2.0
|
|
18
|
+
Requires-Dist: uvicorn>=0.27
|
|
19
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
20
|
+
Requires-Dist: numpy>=1.24
|
|
21
|
+
Requires-Dist: pandas>=2.0
|
|
22
|
+
Requires-Dist: pyarrow>=12.0
|
|
23
|
+
Requires-Dist: scipy>=1.10
|
|
24
|
+
Requires-Dist: h5py>=3.8
|
|
25
|
+
Requires-Dist: python-bidi>=0.4
|
|
26
|
+
Requires-Dist: matplotlib>=3.7
|
|
27
|
+
Requires-Dist: python-docx>=1.0
|
|
28
|
+
Requires-Dist: pymavlink>=2.4.40
|
|
29
|
+
Requires-Dist: pyserial>=3.5
|
|
30
|
+
Requires-Dist: pyulog>=1.0
|
|
31
|
+
Requires-Dist: mcap>=1.1
|
|
32
|
+
Requires-Dist: mcap-ros2-support>=0.5
|
|
33
|
+
Requires-Dist: lz4>=4.0
|
|
34
|
+
Requires-Dist: zstandard>=0.21
|
|
35
|
+
Provides-Extra: desktop
|
|
36
|
+
Requires-Dist: pywebview>=5.0; extra == "desktop"
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
<p align="center">
|
|
40
|
+
<img src="assets/png/icon-128.png" width="96" alt="Signal Viewer icon">
|
|
41
|
+
</p>
|
|
42
|
+
|
|
43
|
+
<h1 align="center">Signal Viewer</h1>
|
|
44
|
+
|
|
45
|
+
<p align="center">
|
|
46
|
+
<b>A fast, offline desktop app for exploring, comparing and reporting on time-series data</b><br>
|
|
47
|
+
CSV · Parquet · MATLAB .mat · ArduPilot .bin · MAVLink .tlog · PX4 .ulg · ROS 2 .mcap · HDF5
|
|
48
|
+
</p>
|
|
49
|
+
|
|
50
|
+
<p align="center">
|
|
51
|
+
<img alt="Python" src="https://img.shields.io/badge/Python-3.8%2B-3776AB?logo=python&logoColor=white">
|
|
52
|
+
<img alt="React" src="https://img.shields.io/badge/UI-React%20%2B%20TypeScript-61DAFB?logo=react&logoColor=black">
|
|
53
|
+
<img alt="Platforms" src="https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-lightgrey">
|
|
54
|
+
<img alt="Offline" src="https://img.shields.io/badge/runs-100%25%20offline-2ea44f">
|
|
55
|
+
<img alt="Languages" src="https://img.shields.io/badge/UI-English%20%7C%20%D7%A2%D7%91%D7%A8%D7%99%D7%AA-orange">
|
|
56
|
+
<a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue"></a>
|
|
57
|
+
</p>
|
|
58
|
+
|
|
59
|
+
<p align="center">
|
|
60
|
+
<img src="docs/images/01_overview.png" alt="Signal Viewer: four linked plots, two cursors and statistics between them" width="100%">
|
|
61
|
+
</p>
|
|
62
|
+
|
|
63
|
+
Signal Viewer is a Python computation core (FastAPI + NumPy) with a React/TypeScript UI, in one
|
|
64
|
+
native desktop window. It opens multi-gigabyte recordings in seconds, keeps every sample available
|
|
65
|
+
at full resolution, and works with **no internet connection at all** – including the map and the
|
|
66
|
+
3D globe.
|
|
67
|
+
|
|
68
|
+
- **Plot anything quickly** – up to 9 linked plots per tab, Time / X-Y / FFT / Spectrogram / Map / 3D modes, two cursors with Δ and statistics, zoom history, notes.
|
|
69
|
+
- **Compare runs** – every shared signal ranked by similarity, automatic time alignment, a full comparison table, CSV export – over the whole runs or **the same segment of each** (the 2nd AUTO of every flight).
|
|
70
|
+
- **Understand flight logs** – ArduPilot, MAVLink and PX4 logs read directly: flight modes, events, parameters and an automatic **health check**; the first log you open gets a suggested view and its three worst findings.
|
|
71
|
+
- **Watch it live** – MAVLink from a vehicle or a simulator over UDP, TCP or a serial radio, recorded to a `.tlog` as it arrives and followed like any growing file.
|
|
72
|
+
- **See where it happened** – an offline map and a 3D terrain view with the vehicle model, its attitude and its track, synchronised with the timeline.
|
|
73
|
+
- **Do the math** – formulas such as `2*Speed - RPM/95` with autocomplete, filters, derivatives and integrals that update when the data changes; **coordinate frames** (NED / ENU / body / ECEF / LLA, suggested from the signal names); motor orders on spectra.
|
|
74
|
+
- **Share the result** – interactive offline HTML, PDF and Word reports – also **one per file from the command line**; sessions and reusable templates.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## See it in action
|
|
79
|
+
|
|
80
|
+
### Open a file and build a view
|
|
81
|
+
|
|
82
|
+
Open a file, choose a layout, click signals into plots, then zoom and read values with two cursors.
|
|
83
|
+
|
|
84
|
+
<p align="center"><img src="docs/media/01_quickstart.gif" alt="Opening a CSV, plotting four signals, zooming and placing two cursors" width="100%"></p>
|
|
85
|
+
|
|
86
|
+
### A flight on the map and in 3D
|
|
87
|
+
|
|
88
|
+
One template turns latitude / longitude / altitude / attitude signals into a map and a 3D view. Press
|
|
89
|
+
play and both follow the timeline; *Up to the time* draws the track as it is flown.
|
|
90
|
+
|
|
91
|
+
<p align="center"><img src="docs/media/02_flight_map_3d.gif" alt="A UAV flight over the Sea of Galilee played back on the offline map and in 3D" width="100%"></p>
|
|
92
|
+
|
|
93
|
+
### Compare runs
|
|
94
|
+
|
|
95
|
+
Pick several files: every shared signal is ranked from least to most similar. Click a row to overlay
|
|
96
|
+
it, align the runs in time automatically, or open the table of all signals.
|
|
97
|
+
|
|
98
|
+
<p align="center"><img src="docs/media/03_compare_runs.gif" alt="Comparing three runs: similarity list, overlay and comparison table" width="100%"></p>
|
|
99
|
+
|
|
100
|
+
### Flight-log health check
|
|
101
|
+
|
|
102
|
+
Open an ArduPilot `.bin` and the **Log** panel finds vibration, clipping, battery, EKF and motor
|
|
103
|
+
problems. Click a finding to jump to it; click an event to move the cursor there.
|
|
104
|
+
|
|
105
|
+
<p align="center"><img src="docs/media/04_flight_log_health.gif" alt="ArduPilot log: vibration template, health findings, events and parameters" width="100%"></p>
|
|
106
|
+
|
|
107
|
+
### Live from the vehicle
|
|
108
|
+
|
|
109
|
+
*Live ▸ MAVLink*: connect to a vehicle, a simulator or a ground station that forwards (UDP, TCP or
|
|
110
|
+
a serial radio). The link is recorded to a `.tlog` that opens like any file and grows in live mode;
|
|
111
|
+
*Follow* keeps the last seconds in view.
|
|
112
|
+
|
|
113
|
+
<p align="center"><img src="docs/media/05_live_mavlink.gif" alt="Live MAVLink: attitude and altitude growing as the vehicle sends them" width="100%"></p>
|
|
114
|
+
|
|
115
|
+
> Higher-quality MP4 versions of these clips are in [`docs/media/`](docs/media).
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Gallery
|
|
120
|
+
|
|
121
|
+
| | |
|
|
122
|
+
|---|---|
|
|
123
|
+
| <img src="docs/images/05_map_and_3d.png" alt="Map and 3D view side by side"><br>**Map + 3D** – offline map with terrain, roads and place names; 3D view with the vehicle and a live data box | <img src="docs/images/06_3d_terrain.png" alt="3D terrain view"><br>**3D terrain** – the bundled elevation (the Jordan rift really is below sea level) |
|
|
124
|
+
| <img src="docs/images/03_compare_runs.png" alt="Compare runs"><br>**Compare** – similarity list, overlay of every run, RMS Δ / max Δ / correlation per run | <img src="docs/images/04_comparison_table.png" alt="Comparison table"><br>**Comparison table** – every shared signal, coloured by how different it is |
|
|
125
|
+
| <img src="docs/images/07_ardupilot_attitude.png" alt="ArduPilot attitude template"><br>**Built-in views** – ArduPilot attitude with the flight mode drawn on every plot | <img src="docs/images/08_vibration_spectrogram.png" alt="Vibration and spectrogram"><br>**Vibration** – VIBE levels, clipping and an IMU spectrogram |
|
|
126
|
+
| <img src="docs/images/09_health_check.png" alt="Health check"><br>**Health check** – each finding with its moment, its stretches and a *Plot* button | <img src="docs/images/10_log_events.png" alt="Log events"><br>**Events** – messages, modes and errors; click one to put the cursor there |
|
|
127
|
+
| <img src="docs/images/11_formula_editor.png" alt="Formula editor"><br>**Formulas** – autocomplete, argument help and *did you mean…?* | <img src="docs/images/13_template_library.png" alt="Template library"><br>**Templates** – built-in and your own, matched to the open files |
|
|
128
|
+
| <img src="docs/images/14_smart_start.png" alt="Smart start card"><br>**Smart start** – the first log gets a suggested view and its worst findings, each a click from its moment | <img src="docs/images/15_segment_compare.png" alt="Compare the same segment of each run"><br>**Same segment, every run** – the files' segments lined up at 0, in a tab named after the segment |
|
|
129
|
+
| <img src="docs/images/16_live_mavlink.png" alt="Live MAVLink"><br>**Live MAVLink** – UDP / TCP / serial, recorded as it arrives; only listens unless asked | <img src="docs/images/17_fft_motor_orders.png" alt="Motor orders on a spectrum"><br>**Motor orders** – 1×, 2×, 3× the rotation rate and the blade pass, over the spectrum's window |
|
|
130
|
+
| <img src="docs/images/18_frames.png" alt="Coordinate frames with a plot per axis"><br>**Coordinate frames** – LLA → NED and more, previewed in a plot per axis, created as one group | <img src="docs/images/01_overview.png" alt="Four plots, two cursors and statistics"><br>**Every signal at a glance** – its unit, rate and a small picture in the file list |
|
|
131
|
+
| <img src="docs/images/02_dark_theme.png" alt="Dark theme"><br>**Dark theme** | <img src="docs/images/12_hebrew_rtl.png" alt="Hebrew interface"><br>**עברית** – full Hebrew interface, right to left |
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Quick start
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
git clone https://github.com/DavidAlpert11/SignalViewer.git
|
|
139
|
+
cd SignalViewer
|
|
140
|
+
pip install -r requirements.txt
|
|
141
|
+
python run.py # opens the desktop window (or the browser)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The welcome screen opens each example in one click (*a drone flight · the same mission flown twice ·
|
|
145
|
+
three car runs to compare · an ArduPilot flight log*), or press **Ctrl+O** and open the files in
|
|
146
|
+
[`examples/`](examples):
|
|
147
|
+
|
|
148
|
+
| File | Try |
|
|
149
|
+
|---|---|
|
|
150
|
+
| `examples/run_012.csv` | click *Speed*, *RPM*, *Throttle*, *DriveMode* into a 2 × 2 layout; press **Space** and **D** for two cursors |
|
|
151
|
+
| `examples/run_012.csv` + `run_013.csv` + `run_014.csv` | open all three, then **Compare** in the side rail |
|
|
152
|
+
| `examples/uav_flight.csv` | **Templates ▾ → Position on the map & 3D**, then press **P** to play |
|
|
153
|
+
| `examples/uav_flight.csv` + `uav_flight_windy.csv` | the same mission flown twice, the second in a cross-wind: both tracks on the map, **Compare** shows where they differ (roll, ground speed, battery) |
|
|
154
|
+
| `examples/copter_flight.bin` | an ArduPilot quad-copter flight: the start card suggests *ArduPilot · Full review* and points at the vibration burst it found; the **Log** button has the events, parameters and the whole health check |
|
|
155
|
+
| a report per file | `python cli.py report --template "ArduPilot · Full review" --out reports/ logs/*.bin` (HTML, `--format pdf` or `docx`; `python cli.py templates` lists them) |
|
|
156
|
+
| live data | ArduPilot SITL or MAVProxy on UDP 14550, then **Live ▾ → MAVLink → Connect and record** |
|
|
157
|
+
|
|
158
|
+
The demo files are synthetic and are regenerated by `python tools/make_demo_data.py` (the .bin by
|
|
159
|
+
`tools/make_example_bin.py`, a real DataFlash log; `tools/make_test_bin.py` makes test logs of any length).
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Contents
|
|
164
|
+
|
|
165
|
+
- [Download / build](#download--build)
|
|
166
|
+
- [Run from source](#run-from-source)
|
|
167
|
+
- [What works](#what-works) – the full feature list
|
|
168
|
+
- [Keyboard](#keyboard)
|
|
169
|
+
- [Performance](#performance)
|
|
170
|
+
- [Known limitations](#known-limitations)
|
|
171
|
+
- [Structure](#structure)
|
|
172
|
+
- [בעברית בקצרה](#בעברית-בקצרה)
|
|
173
|
+
- [Offline map data](#offline-map-data)
|
|
174
|
+
- [3D view](#3d-view)
|
|
175
|
+
- [License](#license)
|
|
176
|
+
- [Changelog](CHANGELOG.md)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
## About
|
|
180
|
+
|
|
181
|
+
Signal Viewer is a Python computation core and a React/TypeScript UI, in one desktop window.
|
|
182
|
+
|
|
183
|
+
## User guide
|
|
184
|
+
|
|
185
|
+
A short guide to the app – start, open, plot, zoom, measure, compare, keep your work:
|
|
186
|
+
**[English](docs/GUIDE.md)** · **[עברית](docs/GUIDE.he.md)**. The same guide is in the app: *Help → User guide* (F1).
|
|
187
|
+
|
|
188
|
+
## Download / build
|
|
189
|
+
|
|
190
|
+
**Download:** the [latest release](https://github.com/DavidAlpert11/SignalViewer/releases/latest).
|
|
191
|
+
On Windows take **`SignalViewer-Setup-X.Y.Z.exe`** (installs for you only, no administrator rights);
|
|
192
|
+
to run without installing, the **`…-Windows-x64-portable.zip`** (extract, double-click *Signal
|
|
193
|
+
Viewer.bat*). Linux and macOS: the `-portable.7z` for your system.
|
|
194
|
+
|
|
195
|
+
**With Python (3.9 or newer):** `pip install signalviewer`, then `signalviewer` starts the app
|
|
196
|
+
(`pip install "signalviewer[desktop]"` for its own window instead of the browser).
|
|
197
|
+
`signalviewer-cli` and `import signalviewer` read and export recordings from scripts.
|
|
198
|
+
|
|
199
|
+
**Nothing needs the internet at run time.** The installer, the portable folder and the single
|
|
200
|
+
file contain Python, every library, the UI with its fonts, the report assets and the icon.
|
|
201
|
+
Verified by running the built app with no network interface at all.
|
|
202
|
+
|
|
203
|
+
| Build command (Windows) | Result | When |
|
|
204
|
+
|---|---|---|
|
|
205
|
+
| `build.bat installer` | `dist\SignalViewer-Setup-1.8.0.exe` | **Recommended.** One file; installs for the current user (no admin), Start-menu and desktop icons, *Open with* for data files, uninstaller |
|
|
206
|
+
| `build.bat portable` | `dist\SignalViewer-portable.zip` | Unzip anywhere and run `SignalViewer.exe` (fast start) |
|
|
207
|
+
| `build.bat onefile` | `dist\SignalViewer.exe` | A single file to copy; starts a few seconds slower (it unpacks itself each time) |
|
|
208
|
+
| `build_windows_portable.bat` (**Windows**) / `python tools/make_windows_portable.py` | `dist\SignalViewer-Windows-portable.7z` | **Windows portable build on a Windows PC**: double-click the `.bat` (needs Python 3.8+ with pip and internet, once, only to build). Downloads the official embeddable Python + the Windows builds of the libraries; the result needs no Python and no internet. **7z** (about 80 MB instead of 126 MB as zip): made with 7-Zip when installed, else with the py7zr library fetched once; Windows 11 opens .7z in Explorer, Windows 10 needs 7-Zip. `--format zip` for a zip, `--keep-folder` keeps the assembled folder, `--clean` forces a fresh download, `--build-ui` rebuilds the web UI first (Node.js; done automatically when the UI sources are newer than the built UI) |
|
|
209
|
+
| `tools/make_windows_portable.sh` (Linux/macOS) | `dist/SignalViewer-Windows-portable.7z` | Builds the same Windows package **without a Windows PC** (runs the same Python builder; options are passed on) |
|
|
210
|
+
| `build.bat wheels` | `wheels\` | On a connected PC: downloads the libraries once, so the build itself can run on an offline PC |
|
|
211
|
+
|
|
212
|
+
The installer needs Inno Setup 6 on the BUILD PC only (free).
|
|
213
|
+
|
|
214
|
+
**Windows window engine.** The app window uses Microsoft Edge WebView2 (built into Windows 11 and
|
|
215
|
+
updated Windows 10). If it is missing (e.g. an old offline Windows 10 LTSC), the app opens in the local
|
|
216
|
+
browser instead — still with no internet — and stops by itself when no tab has been open for 30 s.
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
**Ready-made packages** (no Python and no installation needed, except the Windows installer itself) are built by the GitHub
|
|
220
|
+
Actions workflow in `.github/workflows/build.yml` for Windows (x64: the installer, a portable `.zip` and `.7z`), Linux (x64) and macOS (Apple silicon):
|
|
221
|
+
run it manually (*Actions → build → Run workflow*) and download them from the run's artifacts, or push a
|
|
222
|
+
tag such as `v1.0.0` to also get a draft Release with every file and a "which file?" note. Windows: the embeddable-Python
|
|
223
|
+
package described above; Linux / macOS: the PyInstaller folder (`SignalViewer.spec`) - extract it and run
|
|
224
|
+
`SignalViewer/SignalViewer`.
|
|
225
|
+
|
|
226
|
+
Build it yourself: `build.bat` (Windows) or `./build.sh` (Linux / macOS) → `dist/SignalViewer[.exe]`.
|
|
227
|
+
|
|
228
|
+
## Run from source
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
pip install -r requirements.txt
|
|
232
|
+
python run.py # native window (pywebview), or the browser if pywebview is missing
|
|
233
|
+
python run.py --browser # always use the browser
|
|
234
|
+
python run.py flight.bin # open files (or a .svsession) - in the app that is already running, if there is one
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The UI is already built (`frontend/dist`). When the UI code is newer than that build (after a
|
|
238
|
+
pull, a patch or an edit), `run.py` builds it once by itself - Node.js needed - into
|
|
239
|
+
`frontend/.local-dist` (ignored by git; the tracked `frontend/dist` is not touched) and serves
|
|
240
|
+
that; the next start sees it is current and starts at once. `python run.py --build` forces a
|
|
241
|
+
build, `--no-build` (or `SV_NO_BUILD=1`) never builds. Without Node.js it says so and shows the
|
|
242
|
+
last build. For working on the UI:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
python run.py --port 8765 --no-open # backend
|
|
246
|
+
cd frontend
|
|
247
|
+
npm install
|
|
248
|
+
npm run dev # dev server with hot reload, proxies /api to port 8765
|
|
249
|
+
npm run build # rebuilds frontend/dist
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Tests:
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
python -m pytest backend/tests # ~670 server tests (incl. damaged copies of every format)
|
|
256
|
+
pip install -r requirements-dev.txt && python -m playwright install chromium
|
|
257
|
+
python -m pytest tests/e2e # ~190 browser tests, the whole suite in one run (~10 min)
|
|
258
|
+
python -m pytest tests/e2e/test_features.py # or one file: one test per catalog feature
|
|
259
|
+
cd backend && mypy --config-file ../pyproject.toml app svcore signalviewer # type check
|
|
260
|
+
python tools/benchmark.py big.csv # performance against the targets (results in docs/)
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## What works
|
|
264
|
+
|
|
265
|
+
Every feature of the requirements catalog is covered by an automated test that drives the
|
|
266
|
+
real UI: `tests/e2e/test_features.py` (110 tests) and the other `tests/e2e/` files (193 in all, `test_fresh_user.py` with an empty profile),
|
|
267
|
+
plus 673 backend tests.
|
|
268
|
+
|
|
269
|
+
| Area | Status |
|
|
270
|
+
|---|---|
|
|
271
|
+
| Files | the file dialog sorts by **last modified** (newest first, the default) or by name, with a date column · a file dragged in opens from its **original** (desktop window: the real path; otherwise found by name + size + modified time, one exact match only), so refresh and live mode follow it; only when it cannot be found is a copy used (marked "copy", with *Choose the original file…*) · CSV (auto-detect delimiter/header/time column, Excel BOM, text columns skipped, unsorted time sorted) and MATLAB .mat v5 / v7.3 · built-in file browser · drag & drop onto the window · import options with live preview · load report after opening (what was skipped and why) · rename, description, reload, **replace with another file** (plots keep their signals) · **time offset per file** (badge in the list) · **link files**: a signal is added to and removed from the plot together with the same-named signals of every other file (toggle in the file panel, saved with the workspace) · opening a file does not plot anything until you choose · **damaged files** (cut off by a power loss, bad bytes, a block of zeros) open with what is still readable, with a note; a `.mat` cut off while MATLAB saved it keeps every complete variable · in the file dialog **Shift+click selects a range** and **Ctrl+A** every file shown; each use (open files, save a session, a report, an image) keeps **its own last folder**, and a first save starts where the data is · opening a file that is **already open** reads it again from the disk (*Open another copy* on request) · several files opened together: the start card suggests **Compare open files** · **places** beside the folder: the folders of the open files, **favourite folders** (★ next to the path; drag to reorder), **recent folders**, home / Desktop / Documents / Downloads and the drives · **Ctrl+P**: quick open of recent files and folders · **columns with a time column of their own** (found by name: *Speed* + *Speed_time*, *msg.roll* + *msg.time_boot_ms*, or alternating *time, Speed, time, RPM*) are drawn against their own time – CSV / text, MAT, and a *.tlog* that is a text table (*Import options* to turn it off) |
|
|
272
|
+
| Signals | virtualized list (thousands of signals) · grouping by name prefix · **smart search**: substrings, letters in order (“spd” → Speed), one typo (“sepeed” → Speed); exact matches win · right-click: add, new plot, use as X, **Create signal ▸** (derivative, integral, other operations… – Create signals opens with the signal chosen) |
|
|
273
|
+
| Legend names | one data file → signal name only · several files → each file has a **colour and a short tag** (A, B… – rename it in *File options*, click it in the file list to change the colour): the legend, cursor values, statistics and signal pickers show the tag; the tooltip has the full name and path; the same signal from several files in one plot is **coloured by file** (*Colour by file* in the plot menu for all) · exports and reports name the file (“signal · file”), with as many parent folders as needed (“A/test.csv”, “x/A/test.csv”) |
|
|
274
|
+
| Plots | tabs (rename, duplicate, close, **close others**, drag to reorder; **many tabs**: an *All tabs* list next to +, the active tab always in view, the mouse wheel scrolls them, **Ctrl+PageUp / PageDown**, *Go to tab* in the command palette) · **any layout up to 9 plots** (one layout button: the common ones, a grid picker or type rows × columns; **drop a signal on a plot's right or bottom edge to split it**) · shrinking the layout removes the extra plots (Undo) · **drag the gaps between plots to resize** · Time / X-Y / FFT / **Map** per plot (longitude + latitude signals; wheel = zoom, drag = pan, double-click = fit; online map layers, or the **offline map** that ships with the app – terrain shading and colour by height, water, rivers, roads and runways, place and airport names in Hebrew or English; detailed for **Israel and its surroundings** (elevation ~130 m, roads down to regional roads, villages), the rest of the world coarser; it also fills in for online tiles that cannot be had without internet) · **objects** on the map: an aircraft, UAV, drone, missile, ship or vehicle whose latitude / longitude (and altitude, heading, pitch, roll) come from signals – from any open files, filled in from their names (GPS.Lat / Lng / Alt, ATT.Yaw…; MAVLink 1e7 degrees handled) – drawn as an icon at the **timeline's time**, pointing along its heading or, without one, where it moves; its track follows the timeline's mode; **follow** keeps it in the middle; edit / remove from its chip (Undo) · **3D view** (a plot mode): the earth with the bundled terrain and the same map tiles, the plot's objects as **3D models** (aircraft, UAV, drone, missile, ship, vehicle) at the timeline's time with heading, pitch and roll, their tracks, names, and a readout (altitude, **height above ground**, attitude); altitude given above sea level, above the take-off point or above the ground; a bar to choose the **object** (its values in a **data box** that can be hidden: position, altitude, height above ground, heading, pitch, roll, ground speed, climb), the **camera** – general, or **on the object** (turning goes around it, zooming goes towards it) – and a **second / third** object: a dashed line to each with the distance, and in the box the horizontal distance, height difference, slant range and bearing (on the WGS84 ellipsoid); works with no internet (CesiumJS ships with the app, no Cesium ion); on a computer without graphics acceleration it switches to lighter settings and says so · **own time**: a 3D plot can leave the timeline and get its own bar, play and speed (the other plots keep the timeline's; framed so it shows), and go back · **events**: the log's errors, warnings and mode changes as marks on the tracks · **measure**: two clicks on the ground give the distance over the ground, straight and the height difference · **image**: the view as a PNG · **manual axis limits** · link X and **link Y** (the plots of a column **line up**: the same moment is at the same place in each, whatever their units; an *Envelope* plot zooms with them) · drag-zoom, wheel-zoom, **pan** (Alt+drag or middle button), **zoom history** (Backspace) · min/max decimation that follows the zoom down to raw samples · **clear plot / clear all** (Undo) |
|
|
275
|
+
| Top bar | **File** menu (open files, recent and favourites, refresh all, open / save session, settings, clear everything - with their shortcuts), **Settings** (theme, text size, time as seconds or a clock; kept between starts, not changed by sessions) and **Help** menu (keyboard shortcuts, the examples, copy error details, about) |
|
|
276
|
+
| Plot menus | the plot's **type** is one button with its name (*Time ▾*: Time, X-Y, XYZ, Map, 3D, FFT, Spectrogram, Envelope, Video); **right-click in a plot**: cursor here, second cursor here, note here, previous zoom, show everything, export data, save / copy image, axis limits, clear plot (the ⋯ button has the plot's part) |
|
|
277
|
+
| Legend chips | click: properties · **Ctrl+click: select several and edit them together** (scale, offset, time offset, width, display, remove) · **drag to reorder or to another plot** (Ctrl: copy) · right-click: properties, states, **states only (hide the line, keep the markers)**, move up/down, copy to new plot, use as X, **Create signal ▸**, remove · right-click: **To the right axis** – a second value axis with its own scale and unit for a signal in other units (offered when you add one); reports and images draw both axes |
|
|
278
|
+
| Cursor | Space on/off · click / drag · two cursors with ΔT, Δ and **Δ/ΔT** · values at full resolution · **values drawn on the plot** (optional) |
|
|
279
|
+
| Timeline | a bar under the plots for the **whole recording** (all open files, time offsets included); its handle **is the cursor** – drag or click it, arrows move 1 %, **Home / End** jump to the start / end · a row per file showing when it has data, log errors / warnings and your notes as marks to jump to · **play / pause** (P) at **× real time** (×0.25 … ×60), a zoomed time plot follows the time · the plots show **all + cursor**, **up to the time** (they draw themselves) or **the last N seconds** – time plots, X-Y plots (by each point's time), maps (the whole track faint, the part shown in full colour) and spectrograms · can be made small (a thin bar that still drags) · time written in **seconds as recorded** (the axes' number) or, in Settings, a clock from the start · saved in sessions |
|
|
280
|
+
| Video | a plot in **Video** mode plays a local video (mp4, webm, mov…) **with the timeline**: moving the cursor moves the video, play plays both, and playing or seeking in the video moves the cursor · **Line up…** then **Lock here**: put the video and the cursor on the same moment (take-off, a step) and they stay together (the offset can also be typed) · saved with the session, part of Undo · "No video at this time" outside the clip |
|
|
281
|
+
| Inspector | cursor values (active plot / all) · statistics for the visible range or between cursors, **copy as a table** · signal properties incl. **time offset per signal** |
|
|
282
|
+
| Formulas | write a new signal as an equation, e.g. `2*Speed - RPM/95 + where(Gear > 2, 1, 0)` · signal names as they are, `{file.csv:Speed}` when a name is in several files · autocomplete while typing (Tab / Enter) · `+ - * / ^ %`, comparisons (1 / 0), `abs sqrt exp log log10 sin cos tan asin acos atan atan2 sign floor ceil round min max clip where deriv integ smooth lowpass highpass` (`lowpass(Speed, 5)`: cut-off in Hz), `t`, `pi`, `e` · signals from different files are aligned in time · clear errors (“Unknown signal: Spede. Did you mean Speed?”) · evaluated safely on the server (a closed list of operations, no code execution) · saved as a formula, updates when sources change |
|
|
283
|
+
| Python operations | *Create signals ▸ Python*: your own operations as Python files in **your operations folder** (`~/.signalviewer/operations`, or one you choose): every function marked `@operation(name=…, inputs=…, params={…})` is listed with its description and parameters; it gets the time and the signal(s) as numpy arrays (several inputs aligned onto the first one's time) and returns the new values, or new times and values · **Make an example** writes a file with three operations to start from; *Reload* after editing; files that do not load are listed with the line · the result is a derived signal like any other (preview, live updates, sessions, Undo) · runs in **a process of its own**: an error names the line in your file, an endless loop is stopped after 2 minutes, a crash does not take the app down · only your folder runs: a session or template keeps just the operation's name |
|
|
284
|
+
| Send data from Python | from Jupyter, a simulation or any script: `from signalviewer_push import show; show(t, {"altitude": alt}, name="sim 3")` (also `show(t, y)` and `show(dataframe)` – index = time) · the client is **one file, numpy only** (*Create signals ▸ Python ▸ Save the Python client…*) · a new name goes into the active plot; **the same name again updates it in place** (plots, cursor values and signals made from it follow) – send every few simulation steps to watch it run · binary (.npz, data only), to 127.0.0.1 only, with a token the app writes at every start to `~/.signalviewer/push.json` (readable by you only) · F5 and live mode leave such data alone (there is no file behind it) |
|
|
285
|
+
| Choosing many signals | in *Analysis*, “each of several” and “several signals” use a picker: type part of a name and *select all N matching*, or tick one by one — the signals do not need to be in a plot. Shortcuts: from the active plot, from the legend selection. Bulk selection uses exact matches only; approximate names are offered one by one |
|
|
286
|
+
| Analysis | one signal (derivative, integral, abs, negate, normalize, **k·x + c**, smoothing, RMS, low/high-pass – 4th-order Butterworth, run both ways so nothing is delayed; a cut-off it cannot use is explained, never the raw signal under the filter's name), two signals, several signals · **apply to the selected signals** or to the whole plot · **preview** (dashed) before creating · derived signals keep their formula and update when sources change · **two signals, A − B: how far apart they are** - RMS, mean and largest |A − B| over the time both have and **per stretch of a state** (each flight mode), the worst stretch marked (a simulation against the flight, a model against the test) |
|
|
287
|
+
| Similarity list | choose two or more files in *Compare*: every shared signal is listed at once, ranked by similarity (least similar first, one click to reverse) · **match %** = 100 − error %, **error %** = RMS of the difference relative to the baseline's range · colour bar (same ≥ 99 % / close 90–99 % / different < 90 %) and filter pills with counts · a small curve shows **where in time** the difference is (flat = uniform) · click a row: the signal from every file in the active plot, with the details below (Shift+click: new plot) · computed in the background with quiet progress; changing the choice **cancels the running computation on the server**, waits 0.35 s for further changes, and earlier choices are cached · the first chosen file is the baseline by default (against the mean, two files would each show half their real difference) · **angles** (yaw, heading, course… in deg / rad) are compared around the circle: 359° and 1° are 2° apart |
|
|
288
|
+
| Compare | the table colours the RMS Δ and correlation cells by how bad they are, for quick scanning · any number of files · baseline run or mean · one signal: RMS / max / mean Δ, correlation, tolerance, automatic time alignment (Undo), plot runs / differences · **comparison table**: every signal name found in at least two of the chosen files, with Δ mean and Δ std between files, worst RMS / max Δ, lowest correlation, pass/fail; sort, search, expand a row for each file's mean / std / min / max and its differences from the baseline, plot a row, export everything to CSV |
|
|
289
|
+
| Reports | **preview in the app** · interactive offline HTML · PDF · Word · per-plot captions, include/exclude, report language |
|
|
290
|
+
| Sessions | save / open, autosave and restore · **locate missing files** when a session is opened |
|
|
291
|
+
| Reading values | hover over a plot (no cursor needed): the legend shows each signal's value under the pointer, from the points already drawn (no server round-trip) · units taken from names such as `Speed [km/h]` or `Temp (°C)` are shown with values and on the value axis (`imu[1]` is not a unit) |
|
|
292
|
+
| New runs, same work | **Replace the input files…** (file panel, command palette) or **Open a session with new files…** (command palette, or the icon next to a recent session on the welcome screen): the same layout, formulas, edits (ƒ), axes and cursors with new input files · for each file the newest file in the same folder with the same name pattern is suggested (run_012 → run_013), or choose any other · before opening, each new file is checked for the signals the work uses (missing ones are listed) · replacing the current files offers *Back to the previous files* |
|
|
293
|
+
| Templates | for **any file type**: the **file panel** shows the templates that fit the open files, *All templates…* and *Save as template…*; the top bar's **Templates ▾** menu opens a template or collection, saves one, or starts a collection (also in the command palette and a tab's right-click menu). The layout and its signals are stored by **name**; opening a template applies one to the open files in a new tab — to one file, or to every file that has the signals · missing signals are listed · a chip in the file panel **opens its template in one click** when it is clear which file goes where (right-click: the library) |
|
|
294
|
+
| Welcome screen | recent files and recent sessions, one click to reopen; missing ones are marked and can be forgotten · **four examples in one click**: a drone flight, the same mission flown twice, three car runs to compare, an ArduPilot flight log (offered only when all their files are there) · an example opens **with its view already applied** (Undo: an empty plot) |
|
|
295
|
+
| Import settings | settings chosen in *Import options* are remembered for that folder. Auto-detection still comes first; the remembered settings are used only when auto-detection does not recognise a file (so other files in the folder are never misread). A note says when they were used, with *Forget* |
|
|
296
|
+
| Start over | **Clear everything** (top bar, with confirmation): closes all files, deletes derived signals, one empty tab — files on disk are not touched |
|
|
297
|
+
| ArduPilot logs (.bin) | DataFlash logs read directly (no pymavlink): about 50 MB/s · **every message type keeps its own time** (TimeUS), so IMU at 400 Hz and GPS at 5 Hz are not resampled onto each other · sensors with several instances split (`IMU[0].AccX`, `IMU[1].AccX`) · units and multipliers from the log's FMTU/UNIT/MULT (`GPS[0].Alt [m]`, cm → m) · **flight modes by name** (Copter / Plane / Rover / Sub, detected from the log) and EV events by name · text fields (MSG…) as state signals · PARM values and MSG texts in the load report · damaged blocks skipped (resync) · live mode reads only the newly written messages. Checked value-by-value against pymavlink. Test log generator: `python tools/make_test_bin.py out.bin [--live 30]` |
|
|
298
|
+
| MAVLink telemetry (.tlog) | what Mission Planner / QGroundControl / MAVProxy record from the radio link. Message definitions from pymavlink (ArduPilot dialect), decoded per message type with numpy (a 3.5 MB log in about 0.4 s; values identical to pymavlink). Units converted (degE7 → deg, mV → V, cm → m, cdeg → deg…), enums by name, flight mode by name (ArduPilot per vehicle type; PX4 main/sub modes), HEARTBEAT.armed; arrays per element, unused battery cells → empty; the ground station's own messages left out, other components / vehicles tagged (`ATTITUDE[c154].roll`). **Messages that carry the autopilot's clock (time_boot_ms, or time_usec when it is boot time) are placed at their own time, not at their receive time** - no radio delay in them. Log panel: STATUSTEXT with its real severity, mode changes, arm / disarm, PARAM_VALUE. Live mode reads only new packets |
|
|
299
|
+
| ROS 2 / Foxglove (.mcap) | every numeric field of every topic becomes a signal (`imu/accel.vector.x`), **each topic on its own time** (the recording time, seconds from the first message) · ROS 2 messages (CDR, decoded from the definitions stored in the file – **no ROS installation needed**) and JSON channels · a ROS time (`header.stamp`) becomes one value in seconds, text fields become states with their texts, true / false 1 / 0, short arrays one signal per element · images, point clouds, maps and other bulk topics are not decoded, arrays over 16 values and byte blobs are left out, other encodings (protobuf…) named – all listed when the file opens · a recording cut off by a crash opens with everything before the cut |
|
|
300
|
+
| Parquet (.parquet / .pq) | every numeric and true / false column; text and categorical columns become states · the time: the column you choose, else the **pandas index** (date-times in seconds from the start), else a column named like a time, else the first date-time column; no time: sample numbers · columns of other types (lists, structs) are listed as left out |
|
|
301
|
+
| HDF5 (.h5 / .hdf5) | every numeric dataset (`sensors/imu/ax`), each column of a matrix up to 16 columns (`pos[0]`…), each numeric field of a table (pandas / PyTables / records) · a group's own `time` / `t` / `timestamp` for its signals, else the file's main time; signals with no time of their length are listed, a file with no time uses sample numbers · damaged parts are skipped and counted, the rest opens |
|
|
302
|
+
| PX4 logs (.ulg) | read with pyulog: one signal per topic field (`vehicle_attitude.q[0]`, `sensor_gps[1].…` when a topic has several instances), each topic with its own time; navigation state and arming by name; topics without time stamps skipped (reported); Log panel: logged messages by level, parameters with changes and defaults |
|
|
303
|
+
| Map | the Geo plot (longitude × latitude) on real maps: **streets** (OpenStreetMap, or Esri), **light / dark grey** (Esri, with labels; *automatic* follows the theme), **satellite** (Esri World Imagery), **topographic** (OpenTopoMap). Tiles come through the app and are **kept on disk** (`~/.signalviewer/tiles`), so places already seen work without internet; **without internet** the map shows country borders and names (Natural Earth, English / Hebrew). Track with a halo, start (green) / end (red), the cursor's position, scale bar in metres, the provider's attribution. Wheel = zoom at the pointer, drag = pan, double-click = fit |
|
|
304
|
+
| Shared state lanes | a state (the flight mode…) drawn at the bottom of **every** time plot of a tab: right-click its chip → *Show this state on every plot of the tab*. The built-in views do it for the flight mode; saved templates keep it |
|
|
305
|
+
| Compare views | with an autopilot log and a telemetry log of the same flight open: *Autopilot log vs telemetry* views (roll / pitch in degrees from both, altitude, battery, satellites, ground speed, radio link) with both flight modes on every plot - choose which two files when more are open. Align their time first (it is offered automatically) |
|
|
306
|
+
| Align any two files | right-click a file → *Align time with another file* works for **every file type** (CSV, MAT, logs): **automatically** by signals with the same name in both files (normalised correlation of the overlapping part, judged together with how much overlaps, so a short accidental match does not win); **by two signals you pick** (any names, any units), optionally only within ±N s of the current alignment (periodic signals); **by a moment** in a signal of each file - rises / falls through a level (a share of each signal's own range, so units do not matter), the biggest jump, the first change of value, the highest / lowest point, the start of the recording, first / second / third / last occurrence; **by the cursors** - cursor 1 on a moment in the reference, cursor 2 on the same moment in this file, then right-click the file → *Shift this file so T2 falls on T1*. Undo |
|
|
307
|
+
| Align two recordings | **an autopilot log (.bin / .ulg) and a telemetry log (.tlog) of the same flight**: noticed automatically ("looks like the same flight – Align"), or right-click a file → *Align time with another file*. Three independent methods, shown with how reliable they are and whether they agree: **autopilot boot clock** (the .tlog carries it - exact for every message that has it), **matching events** (the same texts, mode changes, arm / disarm), **signal correlation** (ATT.Roll ~ ATTITUDE.roll… or two signals you pick; units do not matter). *Apply & check* opens a tab with the same quantity and both flight modes on top of each other. Undo. Test pair: `python tools/make_same_flight.py out_dir` |
|
|
308
|
+
| Health check | Log panel → **Health**: vibration, accelerometer clipping, GPS satellites / HDop / fix, battery (against BATT_LOW_VOLT / BATT_CRT_VOLT from the log), EKF variances (FS_EKF_THRESH), motors at full output and motor imbalance, board power, errors / warnings in the log. Each finding: a sentence, the moment (click → cursor), the stretches (click → zoom with both cursors on it), *Plot* (its signals in a new tab). Works for .bin, .tlog and .ulg |
|
|
309
|
+
| Fleet | rail ▸ **Fleet**: add folders of flight logs (.bin / .tlog / .ulg, sub-folders too) – each log is read once into a local index (`~/.signalviewer/fleet.sqlite`; **Update** reads only new and changed logs, drops removed ones, lists unreadable ones with the reason) · search all flights at once: name / vehicle / firmware, a **finding** at a level (error, warning or error), a **number** above / below a limit (every number the health check measures: vibration max, clipping count, battery min, EKF max…, and the duration) · the chosen number's **trend** over the flights' dates, the limit drawn, flights over it in red, click a point to open that flight · per flight: vehicle, firmware, duration, time in each flight mode, its findings with **Open here** (opens the log, cursor at the finding, Log panel) · the date comes from the log's GPS time (current ArduPilot firmware too, since 1.6.0 – an older index reads those logs again once), else the file's date (marked *) |
|
|
310
|
+
| Templates | **a template is a set of tabs** (one tab, or many); **a collection** includes other templates by reference (opening it opens all their tabs, and it follows their changes; loops are refused). Signals are stored by name with the **file** they come from: the files a template uses are found from its tabs when it is saved (one per source file, named after it; shown as a read-only list). When a template is opened, an open file **with the same name** is used; if it is not open, the open file with most of its signals is suggested (*12 of 14 signals*) - or choose another, *all files that fit* (overlaid), or *Open run_a.csv* when the original still exists. Choose which tabs to open; plots the files cannot fill are left out and named. **Library** (top bar → Templates): grouped list (my templates, any file, ArduPilot, MAVLink, PX4, comparisons) with how well the open files fit, search, details; **New** from the current tab, from all open tabs, or as a collection; **edit** name, description, group, tabs (a new template starts with the current tab under a relevant name; *+ Add a tab…* lists the open tabs, those already in the template marked *added*, plus *all remaining tabs*; rename, reorder, remove) and included templates; built-in templates are copied to *my templates* to change them; **delete** your own templates (the trash icon in the list, right-click, the details or the editor; built-in ones cannot be deleted); **export / import** (.json) and **open the folder**: map / 3D objects are kept in templates by signal names like the plots' signals; built-in **Flight on the map & 3D** templates for ArduPilot, MAVLink and PX4 (PX4's attitude from its quaternion), and **Position on the map & 3D** for any file with latitude / longitude (found by their names; suggested only then); every template is its own JSON file, by default in `~/.signalviewer/templates` – **Change folder…** keeps them in any folder you choose (a shared drive, say; the current templates are copied there, and the app looks for templates there from then on), *Default folder* goes back (the old `templates.json` is converted). For any file: *Quick look* (each signal in its own plot, 4 per tab) and *Compare open files* (every signal two or more files share, overlaid) · a chip in the file panel **opens its template in one click** when it is clear which file goes where (right-click: the library) |
|
|
311
|
+
| Built-in views | Templates (file panel or top bar): ArduPilot (attitude, rates, vibration + IMU spectrogram, battery & power, GPS with the track on a map, motors & RC, altitude, EKF), MAVLink and PX4 views. Signals matched without units / instance numbers, with alternatives (EKF3 / EKF2); plots the log cannot fill are left out. User templates match the same flexible way |
|
|
312
|
+
| Spectrogram | plot mode next to FFT: frequency over time of the plot's first signal, over the tab's time window (zoom, link, cursor as usual); window length and highest frequency selectable; time / frequency / dB on hover; in reports too |
|
|
313
|
+
| States | click a block of a state lane → both cursors on that stretch (statistics cover exactly it); Shift+click → zoom to it. Statistics **only while** a state has a value ("while MODE = LOITER": time in that state, number of stretches) |
|
|
314
|
+
| Notes | N (or + in the Inspector) notes the moment at the cursor - **with two cursors, the time between them** (a shaded span); notes show on every time plot, are listed in the Inspector (go to / edit / remove), saved with the session, undoable, and listed in reports (with markers on the PDF / Word plots) |
|
|
315
|
+
| `param()` in formulas | `x / param("BATT_CAPACITY")` - a parameter's value from the open flight log, with suggestions of the names and their values; `param("flight.bin:NAME")` when several logs differ |
|
|
316
|
+
| Flight log (ArduPilot) | a **Log** button appears in the side rail only while a `.bin` is open (red dot when the log has errors); nothing changes for CSV / MAT. **Events**: messages, flight-mode changes, events (ARMED…) and errors in time order, coloured by severity (from the text: PreArm / low / glitch → warning; fail / crash / EKF variance → error), search and filters; **click an event → the cursor goes there** and every plot scrolls to it; the row in force at the cursor is highlighted. **On the plots**: warnings and errors (or every message, or none) as markers along the top, text when there is room, full text on hover; events at the same moment are merged. **Parameters**: search (prefix in bold), **changed in flight** with each change clickable (`20 → 25 @ 11 s`), **not default** (when the log records defaults; the default is shown crossed out), **differences** against another open log; **copy** or **save as .param** (`NAME,VALUE`, for Mission Planner / MAVProxy) exactly the rows shown |
|
|
317
|
+
| Formula help | while typing a formula (*Analysis → Formula*, and *Change this signal…* on a signal's right-click): suggestions for functions **with what each one does**, variables (x = the signal, t = time, pi, e) and signals; a function goes in with its parentheses and the caret inside; **the arguments of the function the caret is in** are shown with the current one highlighted (`smooth(x, n=10)`); Ctrl+Space lists everything. Checked while typing: unclosed brackets, unknown names with *did you mean…?*, and a signal name typed where only `x` is allowed is explained (combine signals in *Analysis → Formula*) |
|
|
318
|
+
| Error details | Help → **Copy error details**: the version, the system and the end of the app's own log, to paste into a bug report · errors in the server, background jobs, threads and the interface are written to `~/.signalviewer/logs/signalviewer.log` (1 MB × 4; `SV_LOG_DIR` moves it) · an unexpected server error shows a readable message · nothing is sent anywhere |
|
|
319
|
+
| Text values | a CSV column of text (`IDLE` / `ARMED` / `FLYING`…) becomes a **state signal** with its texts (up to 1000 different; more → skipped, e.g. IDs). Shown as text everywhere: cursor, legend, hover, value axis. Live mode adds new texts as they appear. Sparse columns (blank cells) keep the last value. The cursor shows the value **in force** for states (never an interpolation) |
|
|
320
|
+
| State display | a **state lane** per signal: a coloured block per state with its text inside (same state = same colour everywhere). Lanes alone fill the plot one under the other with their names; next to lines they sit along the bottom and the value axis makes room. Stretches with too many changes to draw are hatched (zoom in). Text signals start as lanes; *Line* shows them as steps with the texts on the value axis |
|
|
321
|
+
| Zoom | **X / Y / XY** (the zoom menu in the stage bar, `Z` cycles): drag zooms time, values, or a box (a nearly flat drag in XY zooms one axis); for one drag **Shift** = values, **Ctrl** = a box, whatever the mode · wheel over the value axis zooms values only, over the time axis time only; **Shift+wheel** values only, **Ctrl+wheel** both, **Alt+wheel** time only · **Alt+drag** pans (follows the mode) · the wheel follows the size of the scroll (a **trackpad** zooms smoothly; a sideways swipe moves along the time) and one gesture is one step back · Backspace undoes one zoom step of both axes · R / double-click reset both |
|
|
322
|
+
| Live MAVLink | *Live ▸ MAVLink*: UDP (`udpin:127.0.0.1:14550` for SITL / MAVProxy / a forwarding ground station, `udpin:0.0.0.0:14550` from another computer), TCP (`tcp:127.0.0.1:5760`) or a serial port (listed, with its baud rate) · the link is **recorded to a `.tlog`** in `~/.signalviewer/recordings`, opened like any file and followed by live mode; Disconnect keeps it · only listens by default; *Ask the vehicle to send data* acts as a minimal ground station (heartbeat + one request for all streams) · a pill next to Live shows messages per second and turns red when the link goes quiet |
|
|
323
|
+
| Same segment across runs | *Compare runs ▸ Compare over: A segment*: a state signal (flight mode, gear, any text column), a value and which occurrence (1st, 2nd… or last); each file is compared from the start of its own segment, the list shows where it is in each file; clicking a signal opens the files over that segment, lined up at 0, in a tab named after it; the comparison table names it too |
|
|
324
|
+
| Batch reports | `python cli.py report --template T --out DIR files…` – one report per file (HTML, PDF or Word, English or Hebrew) with the **same template matching and drawing as the app** (the matching lives in one place, `backend/svcore/templates.py`); files that cannot be read or have none of the template's signals are skipped and named; `--template` takes a name, an id or a template `.json` |
|
|
325
|
+
| Coordinate frames | *Create signals ▸ Frames*: NED, ENU, body, ECEF, LLA – a vector (rotated; body needs the attitude as Euler angles or a quaternion) or a position (also moves the origin; NED / ENU from a reference point: the first sample, other signals or numbers) · **suggested from the names** (GPS position → NED, a NED velocity → body, an IMU acceleration → NED with the log's attitude) · the three axes are created as **one group** (`ned_Pos_lla.N/E/D`), previewed in the active plot or **a plot per axis**, and go to the active plot, a new plot each, or none · a plain **XYZ plot** (any three signals, no earth) can be placed on the map |
|
|
326
|
+
| Spectra | FFT or Welch, window, log scale · **motor orders**: pick an RPM / ESC signal and the spectrum is marked at 1×, 2×, 3× its mean rotation rate over the same window, plus the blade-pass frequency for 2–5 blades |
|
|
327
|
+
| File list | every signal with its **unit** (kept apart from the name, never cut off), its **sample rate** (wider panel, and the tooltip with the number of samples) and a **small picture** of the whole signal (spikes included; only the rows on screen fetch theirs) · checkboxes only while signals are selected (Ctrl / Shift + click, or *select all* of a search): plot them (one undo step), change them, or make new signals from them |
|
|
328
|
+
| Refresh & live | **Refresh all files** (file panel ⟳, F5 / Ctrl+R): files unchanged on disk are not read again. **Live**: refreshes every file first, then once a second reads only what was added (CSV rows, .bin / .tlog messages; a MAT, ULog, MCAP, HDF5 or Parquet file is read again once it is quiet for a moment, and at least every 2 s while a program keeps saving it). A green dot marks files that are still being written; the status bar shows "2 of 5 updating". Settings (▾ next to Live): **stop after N s without new data** (10 s – 15 min, or never; default 1 min), **new files** in the same folders with the same name pattern (run_013.csv after run_012.csv, 00000043.BIN): ask / open automatically / ignore, follow last N seconds. Files opened during live mode are followed too |
|
|
329
|
+
| Opening from outside | **Open with** in Explorer (the installer offers it for .bin, .tlog, .ulg, .mcap, .mat, .csv, .parquet, .h5 and makes .svsession sessions open with the app; your default programs stay as they are) and `SignalViewer file…` on the command line: when the app is already running the files open **in it** (its window comes forward) instead of in a second app; several files chosen together end up in one app; `--new-window` for a separate one |
|
|
330
|
+
| General | progress + cancel for long operations · Undo/Redo · command palette · Hebrew/English with RTL · light/dark · WCAG AA contrast · text size 100–125 % · live mode with **follow last N seconds** |
|
|
331
|
+
|
|
332
|
+
## Keyboard
|
|
333
|
+
|
|
334
|
+
| Key | Action |
|
|
335
|
+
|---|---|
|
|
336
|
+
| Ctrl+K | Command palette (commands and signals) |
|
|
337
|
+
| Ctrl+O | Open files |
|
|
338
|
+
| Ctrl+P | Quick open: recent files, recent and favourite folders |
|
|
339
|
+
| Ctrl+S / Ctrl+Shift+O | Save / open session |
|
|
340
|
+
| Ctrl+Z / Ctrl+Y | Undo / Redo |
|
|
341
|
+
| Ctrl+PageUp / Ctrl+PageDown | Previous / next tab |
|
|
342
|
+
| Space | Cursor on/off |
|
|
343
|
+
| D | Two cursors |
|
|
344
|
+
| ← → (Shift = ×10) | Move the cursor from one recorded sample to the next (no interpolation; only the mouse interpolates) |
|
|
345
|
+
| 1–9 | Select plot |
|
|
346
|
+
| R | Reset zoom (to the axis limits, if set) |
|
|
347
|
+
| Z | Zoom mode X → Y → XY (for one drag: Shift = values, Ctrl = a box, Alt = pan) |
|
|
348
|
+
| N | Note at the cursor |
|
|
349
|
+
| F5 / Ctrl+R | Refresh all files |
|
|
350
|
+
| Backspace | Previous zoom |
|
|
351
|
+
| P | Play / pause (the timeline) |
|
|
352
|
+
| Home / End | The timeline: start / end of the recording |
|
|
353
|
+
| Ctrl + / Ctrl − / Ctrl 0 | Text size |
|
|
354
|
+
| ? | Keyboard shortcuts |
|
|
355
|
+
| Delete | Remove the selected signal from its plot |
|
|
356
|
+
|
|
357
|
+
## Performance
|
|
358
|
+
|
|
359
|
+
Measured with `tools/benchmark.py` on a 1-CPU, 4 GB test machine (full results in `docs/`).
|
|
360
|
+
The first-open target is scaled by the number of values (5 s per 40 M values, the reference file).
|
|
361
|
+
|
|
362
|
+
| | 5 M rows × 7 (730 MB) | 20 M rows × 4 (1.7 GB) | 5,000 rows × 5,000 columns (262 MB) |
|
|
363
|
+
|---|---|---|---|
|
|
364
|
+
| Open, first time | 4.2 – 4.8 s (target 5.0) | 12.2 s (target 12.5) | 3.6 s (target 3.1) |
|
|
365
|
+
| Open again (cache) | 0.07 s | 0.06 s | 0.44 s |
|
|
366
|
+
| Memory for the data | 1.07× binary size | 1.03× | 0.12× (only shown columns are read) |
|
|
367
|
+
| Redraw full / zoom / deep zoom | 6 / 9 / 5 ms | 6 / 11 / 6 ms | 5 / 4 / 4 ms |
|
|
368
|
+
| Cursor values | 1 ms | 1 ms | 1 ms |
|
|
369
|
+
| Live mode, 1,000 new rows | 149 ms | 295 ms | 741 ms |
|
|
370
|
+
|
|
371
|
+
Parsing runs at about 8.5 M values per second per CPU core for both long and wide files and scales
|
|
372
|
+
with cores (64 MB parts are parsed in parallel). Redraw time does not grow with file size: min/max
|
|
373
|
+
summaries per 1,024-sample block are computed once per signal. Wide files: the cache is one
|
|
374
|
+
column-major matrix (not one file per column), discrete-signal detection runs on demand, the signal
|
|
375
|
+
list is virtualized, and signal choosers are searchable instead of long dropdowns.
|
|
376
|
+
|
|
377
|
+
## Icon
|
|
378
|
+
|
|
379
|
+
`assets/icon.svg` (full) and `assets/icon-small.svg` (simplified for 16–32 px), rendered to
|
|
380
|
+
`assets/png/`, `assets/icon.ico` (Windows) and `assets/icon.icns` (macOS). Used for the executable,
|
|
381
|
+
the window and the browser tab. A signal trace read by the amber measurement cursor, the same
|
|
382
|
+
visual language as the app.
|
|
383
|
+
|
|
384
|
+
## Known limitations
|
|
385
|
+
|
|
386
|
+
* Interactive HTML reports embed signals longer than 150,000 samples as three min/max levels, so very deep zoom inside the report shows less detail than the app.
|
|
387
|
+
* Copying an image to the clipboard depends on the window's clipboard support; Save image always works.
|
|
388
|
+
* A derived signal whose source file was closed stays in the list, marked, until you remove it.
|
|
389
|
+
* The macOS package is not signed or notarized: after extracting, run `xattr -dr com.apple.quarantine SignalViewer` (or right-click → Open) the first time.
|
|
390
|
+
* Without `pyarrow` the pandas parser keeps a malformed row that has too few fields (missing values become empty), while `pyarrow` skips it.
|
|
391
|
+
* The timeline's modes apply to time, X-Y, map and spectrogram plots, not to FFT.
|
|
392
|
+
* In a web browser (not the desktop window) Ctrl+PageUp / Ctrl+PageDown and Ctrl+Tab switch the browser's own tabs; use the *All tabs* list or *Go to tab* (Ctrl+K) there.
|
|
393
|
+
* The right value axis follows its data: zooming the values (Shift+drag), panning, the axis limits dialog and linked value axes act on the left axis.
|
|
394
|
+
* *Open with* (Explorer) is set up by the installer; with the portable packages, open files from the app or `SignalViewer file…`.
|
|
395
|
+
* Installed with pip, the app opens in the browser; `pip install "signalviewer[desktop]"` gives it its own window (on Linux that needs GTK or Qt).
|
|
396
|
+
* The interactive HTML report lists the notes under its summary; it does not draw them on its plots (the PDF and Word reports do).
|
|
397
|
+
* Columns with a time column of their own keep that column's unit: a `time_boot_ms` column stays in milliseconds (as the main time column always has), so it does not line up with a time in seconds.
|
|
398
|
+
|
|
399
|
+
## Structure
|
|
400
|
+
|
|
401
|
+
```
|
|
402
|
+
run.py launcher (server + window)
|
|
403
|
+
SignalViewer.spec PyInstaller build definition (build.bat / build.sh)
|
|
404
|
+
tools/benchmark.py performance benchmark
|
|
405
|
+
tests/e2e/ end-to-end browser tests
|
|
406
|
+
backend/app/main.py local API, 127.0.0.1 only (wires the routes, local-only guard, UI files)
|
|
407
|
+
backend/app/routes/ the API endpoints, one module per area (files, data, analysis, compare...)
|
|
408
|
+
backend/app/store.py loaded files with stable ids
|
|
409
|
+
backend/svcore/ computation core: loaders, decimation, analysis, compare, sync
|
|
410
|
+
backend/tests/ pytest suite
|
|
411
|
+
frontend/src/ React + TypeScript UI (uPlot, zustand)
|
|
412
|
+
frontend/dist/ built UI served by the backend
|
|
413
|
+
examples/ demo files (tools/make_demo_data.py)
|
|
414
|
+
docs/images, docs/media screenshots and clips used in this README
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Environment variables: `SV_FLOAT32`, `SV_CACHE`, `SV_CACHE_MIN_MB`, `SV_MAX_POINTS`.
|
|
418
|
+
Workspace autosave and the binary cache live in `~/.signalviewer/`; the error log in
|
|
419
|
+
`~/.signalviewer/logs/` (Help > Copy error details puts its end on the clipboard; `SV_LOG_DIR` moves it).
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
## בעברית בקצרה
|
|
424
|
+
|
|
425
|
+
הפעלה: `pip install -r requirements.txt` ואז `python run.py`.
|
|
426
|
+
כשקוד הממשק חדש יותר מהבנייה הקיימת (אחרי pull או patch), `run.py` בונה אותו פעם אחת לבד (נדרש Node.js) לתיקייה `frontend/.local-dist` שגיט מתעלם ממנה.
|
|
427
|
+
האפליקציה נפתחת בחלון משלה (או בדפדפן), והקבצים לא יוצאים מהמחשב.
|
|
428
|
+
גררו קבצי CSV, MAT או לוגים של ArduPilot (.bin) לחלון, לחצו על סיגנל כדי להוסיף אותו לגרף, ורווח מפעיל את הסמן.
|
|
429
|
+
זום: X / Y / XY בתפריט הזום בסרגל (המקש Z מחליף). לגרירה אחת: Shift = ערכים בלבד, Ctrl = מלבן, Alt = הזזה (pan).
|
|
430
|
+
גלגלת מעל ציר עושה זום רק בו; Shift+גלגלת בערכים בלבד, Ctrl+גלגלת בשניהם. כפתור פריסה אחד, וגרירת סיגנל לקצה של גרף מפצלת אותו.
|
|
431
|
+
רענון כל הקבצים: הכפתור ⟳ בפאנל הקבצים או F5. "חי" מרענן הכל ואז עוקב אחרי הקבצים שממשיכים לגדול;
|
|
432
|
+
בחץ שליד הכפתור: עצירה אוטומטית אחרי זמן ללא נתונים, ומה לעשות עם קבצים חדשים באותה תיקייה.
|
|
433
|
+
עמודות טקסט (מצבים) ומצבי טיסה מוצגים כרצועות צבעוניות עם שם המצב.
|
|
434
|
+
לוג של ArduPilot: כפתור "לוג טיסה" מופיע בסרגל הצד רק כשפתוח קובץ .bin – אירועים והודעות (לחיצה מזיזה את הסמן לרגע),
|
|
435
|
+
סימון אזהרות ושגיאות על הגרפים, ופרמטרים: חיפוש, שונו בטיסה, לא ברירת מחדל, השוואה מול לוג אחר ושמירה כ־.param.
|
|
436
|
+
לוגים: .bin (ArduPilot), .tlog (טלמטריית MAVLink) ו־.ulg (PX4); הקלטות ROS 2 / Foxglove (.mcap, בלי התקנת ROS) וקובצי HDF5. לוג של הבקר וטלמטריה של אותה טיסה מזוהים ומיושרים בזמן –
|
|
437
|
+
הודעות שנושאות את שעון הבקר ממוקמות בזמן המדויק שלהן. בפאנל הלוג: תקינות, אירועים, פרמטרים ותצוגות מוכנות. ספקטרוגרם כמצב גרף,
|
|
438
|
+
הערות (N), סטטיסטיקה "רק בזמן ש…", ו־param("NAME") בנוסחאות.
|
|
439
|
+
מפה: רחובות, לוויין, טופוגרפית ואפורה בהירה/כהה – נשמרות בדיסק לעבודה בלי אינטרנט; בלי אינטרנט: גבולות ושמות מדינות.
|
|
440
|
+
מצב הטיסה יכול להופיע בכל הגרפים בלשונית (קליק ימני על המצב). תצוגות השוואה של לוג הבקר מול הטלמטריה.
|
|
441
|
+
תבניות: תבנית היא אוסף לשוניות, ואוסף יכול לכלול תבניות אחרות. כל סיגנל שייך ל"משבצת קובץ", וכשפותחים תבנית
|
|
442
|
+
בוחרים קובץ לכל משבצת (עם הצעה אוטומטית). כל תבנית היא קובץ JSON בתיקייה ~/.signalviewer/templates –
|
|
443
|
+
עריכה, ייצוא וייבוא מתוך ספריית התבניות. "מבט מהיר" ו"השוואת קבצים פתוחים" מתאימות לכל קובץ.
|
|
444
|
+
יישור זמן בין כל שני קבצים (CSV, MAT, לוגים): אוטומטית לפי סיגנלים באותו שם, לפי שני סיגנלים לבחירה, לפי רגע בסיגנל
|
|
445
|
+
(עלייה דרך רמה, קפיצה, שינוי מצב…) או לפי שני הסמנים – קליק ימני על הקובץ ← יישור זמן מול קובץ אחר.
|
|
446
|
+
בנייה לחלונות: הפעילו `build_windows_portable.bat` – נוצר `dist\SignalViewer-Windows-portable.7z` שלא צריך התקנה
|
|
447
|
+
(Windows 11 פותח 7z ישירות; ב־Windows 10 צריך 7-Zip. לקובץ zip: `--format zip`).
|
|
448
|
+
בנוסחאות: השלמה אוטומטית של פונקציות (עם הסבר), משתנים וסיגנלים, והצגת הארגומנטים של הפונקציה שבה נמצא הסמן.
|
|
449
|
+
Ctrl+K פותח חיפוש של כל הפקודות והסיגנלים. בורר השפה נמצא בסרגל העליון.
|
|
450
|
+
חדש ב־1.3: MAVLink חי (UDP / TCP / פורט טורי, מוקלט ל־.tlog תוך כדי), השוואת אותו מקטע בכל הריצות (למשל ה־AUTO השני של כל טיסה),
|
|
451
|
+
דוח לכל קובץ משורת הפקודה (`python cli.py report --template … --out … קבצים`), המרת מערכות צירים (NED / ENU / גוף / ECEF / LLA, מוצעת לפי
|
|
452
|
+
שמות הסיגנלים), גרף XYZ, סדרי מנוע בספקטרום, כרטיס פתיחה חכם לקובץ הראשון, וברשימת הקבצים יחידה, קצב דגימה ותמונה קטנה לכל סיגנל.
|
|
453
|
+
חדש ב־1.4: מסנני Low-pass / High-pass אמיתיים (Butterworth בלי השהיה; הגרסאות הקודמות השתמשו בממוצע נע, ולכן ערכים מסוננים בסשנים ישנים ישתנו מעט),
|
|
454
|
+
קבצים פגומים (קטועים, בתים פגומים, בלוק אפסים) נפתחים עם מה שאפשר לקרוא, Help ← "העתקת פרטי שגיאה" לדיווח על תקלות
|
|
455
|
+
(הלוג נשמר רק במחשב, ב־~/.signalviewer/logs), ורישיון MIT.
|
|
456
|
+
חדש ב־1.5: וידאו מסונכרן עם הנתונים (מצב גרף Video), פעולות Python משלכם ושליחת נתונים מ־Python, תצוגת Fleet על כל לוגי הטיסה
|
|
457
|
+
(חיפוש לפי ממצא או מספר, מגמה לאורך הטיסות), וקבצי ROS 2 (.mcap), HDF5 ו־Parquet.
|
|
458
|
+
חדש ב־1.6: שתי דוגמאות חדשות במסך הפתיחה – אותה משימה בשתי טיסות (רגילה וברוח צד) ולוג טיסה של ArduPilot עם ממצא לחפש,
|
|
459
|
+
"יצירת סיגנל ▸" אחד בתפריטי הסיגנל, ותיקונים ל־1.5: הדוגמאות חזרו לחבילת Windows, מספר הגרסה ב־Help, תאריך הטיסה ב־Fleet
|
|
460
|
+
ללוגים של ArduPilot, ו־Undo של "מחיקת כל הנגזרים" שכבר לא מוחק עבודה שנעשתה אחריו.
|
|
461
|
+
חדש ב־1.7: לכל קובץ צבע ותג קצר (A, B, C…) במקרא, בסטטיסטיקה ובבוחרי הסיגנלים - סיגנלים באותו שם משני קבצים
|
|
462
|
+
נצבעים לפי קובץ; מועדפים ותיקיות אחרונות בדיאלוג הקבצים ו־Ctrl+P לפתיחה מהירה; ציר ערכים שני (ימני) לסיגנל
|
|
463
|
+
ביחידות אחרות; דוגמאות ותבניות שנפתחות מוכנות בלחיצה אחת; עמודות עם עמודת זמן משלהן (Speed + Speed_time,
|
|
464
|
+
msg.time_boot_ms) נקראות כך; מצב Live עוקב גם אחרי קובץ MATLAB שנשמר שוב ושוב; תבניות מובנות בעברית.
|
|
465
|
+
חדש ב־1.8: מתקין ל־Windows ו־zip בכל שחרור, "פתח באמצעות" מהסייר (לחלון שכבר פתוח), `pip install signalviewer`, ומדריך
|
|
466
|
+
למשתמש בעברית ובאנגלית (F1); תפריטי קובץ ועזרה, כפתור אחד לסוג הגרף והמקרא לידו (יותר מקום לעקומות), תפריט קליק ימני
|
|
467
|
+
בכל גרף, והגדרות שנשמרות בין הפעלות; הערות על טווח זמן, וכמה רחוקים שני סיגנלים לכל קטע של מצב טיסה; ותיקונים:
|
|
468
|
+
סטטיסטיקה שיכלה להציג מספרים של סיגנל אחר, גרפים בעמודה שלא היו מיושרים, וזום בטאצ'פד.
|
|
469
|
+
חדש ב־1.6.1 (ליטוש): כל הטאבים נגישים גם כשיש הרבה (רשימת "כל הטאבים", Ctrl+PageUp/PageDown), לכל קובץ צבע משלו
|
|
470
|
+
ב־Compare open files, השוואת ריצות מחשבת זוויות (Yaw / Heading) סביב המעגל, Shift+קליק ו־Ctrl+A בדיאלוג הקבצים,
|
|
471
|
+
תיקייה אחרונה נפרדת לכל שימוש, קובץ שכבר פתוח נקרא מחדש במקום להיפתח פעמיים, ודיאלוגים ותפריטים שנסגרים ב־Esc.
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
## Offline map data
|
|
475
|
+
|
|
476
|
+
`backend/app/basemap_data/` (17 MB) is built by `python tools/make_basemap.py --all` on a machine
|
|
477
|
+
with internet (needs `pyshp` and `osmium` besides the app's libraries) and ships with the app:
|
|
478
|
+
elevation grids (world ~11 km, Israel and surroundings ~500 m, Israel ~130 m) from the AWS
|
|
479
|
+
"Terrain Tiles" (Mapzen / Tilezen: SRTM, GMTED2010, ETOPO1), Natural Earth (public domain), and
|
|
480
|
+
OpenStreetMap from Geofabrik extracts (© OpenStreetMap contributors, ODbL). The app draws map tiles
|
|
481
|
+
from them (`backend/app/basemap.py`, cached in `~/.signalviewer/tiles`); the map shows the
|
|
482
|
+
attribution. To refresh the data, run the tool again.
|
|
483
|
+
|
|
484
|
+
**More detailed areas (packs).** Any area can get the same detail as Israel: elevation (~130 m),
|
|
485
|
+
OpenStreetMap roads down to regional ones, places down to villages, airports and runways:
|
|
486
|
+
|
|
487
|
+
python tools/make_basemap.py --pack cyprus --bbox 34.5,32.2,35.75,34.65 --countries europe/cyprus
|
|
488
|
+
|
|
489
|
+
`--bbox` is south,west,north,east; `--countries` the Geofabrik extracts covering it (comma-separated);
|
|
490
|
+
`--zoom 9` or `8` for coarser (smaller) elevation. The pack is written to
|
|
491
|
+
`backend/app/basemap_data/packs/<name>/` (`pack.json`, `dem.npz`, `vectors.json.gz`; ~1.4 MB for
|
|
492
|
+
Cyprus) and the app uses every pack in that folder - copy a pack folder there on an offline machine.
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
## 3D view
|
|
496
|
+
|
|
497
|
+
CesiumJS (pinned, `frontend/package.json`) is loaded only when a plot is switched to 3D; its web
|
|
498
|
+
workers and data files are copied into the UI build under `/cesium/` (`frontend/vite.config.ts`),
|
|
499
|
+
nothing comes from the internet. Terrain heights come from `/api/terrain/{z}/{x}/{y}` (the same
|
|
500
|
+
bundled elevation grids as the offline map: sea at 0 m, the Jordan rift really below it). The
|
|
501
|
+
object models are built by `python tools/make_models.py` into `frontend/public/models/`.
|
|
502
|
+
Graphics: with a graphics card Cesium draws at the display's frame rate; drawn in software (virtual
|
|
503
|
+
machines, remote desktop) it is slow - the app then uses one sample per pixel and less terrain
|
|
504
|
+
detail (about 3x faster) and shows a note. Not in reports.
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
## License
|
|
508
|
+
|
|
509
|
+
Signal Viewer is released under the [MIT License](LICENSE): free to use, change and ship, also
|
|
510
|
+
commercially, as long as the copyright notice stays with it.
|
|
511
|
+
|
|
512
|
+
It ships with third-party parts under their own licences: uPlot (MIT,
|
|
513
|
+
`backend/app/report_assets/uPlot.LICENSE`), the report fonts (SIL Open Font License,
|
|
514
|
+
`backend/app/report_assets/fonts/OFL-LICENSE.txt`), CesiumJS (Apache-2.0), and map data from
|
|
515
|
+
OpenStreetMap (© OpenStreetMap contributors, ODbL), Natural Earth (public domain) and the AWS
|
|
516
|
+
Terrain Tiles (see [Offline map data](#offline-map-data)). Python and JavaScript libraries keep
|
|
517
|
+
their own licences (`requirements.txt`, `frontend/package.json`).
|