inav-toolkit 2.18.0__tar.gz → 2.19.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.
Files changed (24) hide show
  1. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/PKG-INFO +1 -1
  2. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/inav_toolkit/__init__.py +1 -1
  3. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/inav_toolkit/blackbox_analyzer.py +132 -7
  4. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/inav_toolkit/flight_db.py +1 -1
  5. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/inav_toolkit/msp.py +1 -1
  6. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/inav_toolkit/param_analyzer.py +1 -1
  7. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/inav_toolkit/vtol_configurator.py +1 -1
  8. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/inav_toolkit/wizard.py +1 -1
  9. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/inav_toolkit.egg-info/PKG-INFO +1 -1
  10. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/pyproject.toml +1 -1
  11. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/LICENSE +0 -0
  12. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/README.md +0 -0
  13. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/inav_toolkit/autotune.py +0 -0
  14. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/inav_toolkit/i18n.py +0 -0
  15. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/inav_toolkit/locales/en.json +0 -0
  16. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/inav_toolkit/locales/es.json +0 -0
  17. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/inav_toolkit/locales/pt_BR.json +0 -0
  18. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/inav_toolkit.egg-info/SOURCES.txt +0 -0
  19. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/inav_toolkit.egg-info/dependency_links.txt +0 -0
  20. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/inav_toolkit.egg-info/entry_points.txt +0 -0
  21. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/inav_toolkit.egg-info/requires.txt +0 -0
  22. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/inav_toolkit.egg-info/top_level.txt +0 -0
  23. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/setup.cfg +0 -0
  24. {inav_toolkit-2.18.0 → inav_toolkit-2.19.0}/tests/test_smoke.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: inav-toolkit
3
- Version: 2.18.0
3
+ Version: 2.19.0
4
4
  Summary: Blackbox analyzer, parameter checker, and tuning wizard for INAV flight controllers
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://github.com/agoliveira/INAV-Toolkit
@@ -1,3 +1,3 @@
1
1
  """INAV Toolkit - Blackbox analyzer, parameter checker, and tuning wizard for INAV flight controllers."""
2
2
 
3
- __version__ = "2.18.0"
3
+ __version__ = "2.19.0"
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
- INAV Blackbox Analyzer - Multirotor Tuning Tool v2.18.0
3
+ INAV Blackbox Analyzer - Multirotor Tuning Tool v2.19.0
4
4
  =====================================================
5
5
  Analyzes INAV blackbox logs and tells you EXACTLY what to change.
6
6
 
@@ -104,7 +104,7 @@ def _disable_colors():
104
104
  AXIS_NAMES = ["Roll", "Pitch", "Yaw"]
105
105
  AXIS_COLORS = ["#FF6B6B", "#4ECDC4", "#FFD93D"]
106
106
  MOTOR_COLORS = ["#FF6B6B", "#4ECDC4", "#FFD93D", "#A78BFA"]
107
- REPORT_VERSION = "2.18.0"
107
+ REPORT_VERSION = "2.19.0"
108
108
 
109
109
  # ─── Frame and Prop Profiles ─────────────────────────────────────────────────
110
110
  # Two separate concerns:
@@ -6098,10 +6098,95 @@ def count_blackbox_logs(filepath):
6098
6098
  return max(1, count)
6099
6099
 
6100
6100
 
6101
+ # ─── Post-Analysis Cleanup ────────────────────────────────────────────────────
6102
+
6103
+ def _post_analysis_cleanup(blackbox_dir, raw_download, split_files, analyzed_file,
6104
+ archive=False, keep_logs=False):
6105
+ """Clean up blackbox directory after successful analysis.
6106
+
6107
+ Default: delete raw download and all split files.
6108
+ --archive: compress analyzed flight to archive/ first.
6109
+ --keep-logs: skip everything.
6110
+
6111
+ Also cleans stale .bbl files from previous sessions.
6112
+ """
6113
+ if keep_logs:
6114
+ return
6115
+
6116
+ R, B, C, G, Y, RED, DIM = _colors()
6117
+ deleted = 0
6118
+ archived = 0
6119
+
6120
+ # Archive the analyzed flight if requested
6121
+ if archive and analyzed_file and os.path.isfile(analyzed_file):
6122
+ import gzip
6123
+ archive_dir = os.path.join(blackbox_dir, "archive")
6124
+ os.makedirs(archive_dir, exist_ok=True)
6125
+ gz_name = os.path.basename(analyzed_file) + ".gz"
6126
+ gz_path = os.path.join(archive_dir, gz_name)
6127
+ try:
6128
+ with open(analyzed_file, "rb") as f_in:
6129
+ with gzip.open(gz_path, "wb") as f_out:
6130
+ f_out.write(f_in.read())
6131
+ archived_size = os.path.getsize(gz_path)
6132
+ original_size = os.path.getsize(analyzed_file)
6133
+ ratio = archived_size / original_size * 100 if original_size > 0 else 0
6134
+ print(f" Archived: {gz_name} ({archived_size // 1024}KB, {ratio:.0f}% of original)")
6135
+ archived = 1
6136
+ except Exception as e:
6137
+ print(f" Warning: archive failed: {e}")
6138
+
6139
+ # Delete the raw download file
6140
+ if raw_download and os.path.isfile(raw_download):
6141
+ try:
6142
+ sz = os.path.getsize(raw_download) // 1024
6143
+ os.remove(raw_download)
6144
+ deleted += 1
6145
+ except Exception:
6146
+ pass
6147
+
6148
+ # Delete all split files
6149
+ if split_files:
6150
+ for sf in split_files:
6151
+ if sf and os.path.isfile(sf):
6152
+ try:
6153
+ os.remove(sf)
6154
+ deleted += 1
6155
+ except Exception:
6156
+ pass
6157
+
6158
+ # Clean stale .bbl files from previous sessions
6159
+ # (anything not from the current download)
6160
+ current_base = os.path.basename(raw_download) if raw_download else ""
6161
+ if os.path.isdir(blackbox_dir):
6162
+ for fname in os.listdir(blackbox_dir):
6163
+ if not fname.endswith(".bbl"):
6164
+ continue
6165
+ fpath = os.path.join(blackbox_dir, fname)
6166
+ if fpath == raw_download:
6167
+ continue # already handled
6168
+ if any(fpath == sf for sf in (split_files or [])):
6169
+ continue # already handled
6170
+ # This is a stale .bbl from a previous session
6171
+ try:
6172
+ os.remove(fpath)
6173
+ deleted += 1
6174
+ except Exception:
6175
+ pass
6176
+
6177
+ if deleted > 0 or archived > 0:
6178
+ parts = []
6179
+ if deleted > 0:
6180
+ parts.append(f"{deleted} log files removed")
6181
+ if archived > 0:
6182
+ parts.append(f"1 archived")
6183
+ print(f" Cleanup: {', '.join(parts)}")
6184
+
6185
+
6101
6186
  # ─── Main ─────────────────────────────────────────────────────────────────────
6102
6187
 
6103
6188
  def main():
6104
- parser = argparse.ArgumentParser(description="INAV Blackbox Analyzer v2.18.0 - Prescriptive Tuning",
6189
+ parser = argparse.ArgumentParser(description="INAV Blackbox Analyzer v2.19.0 - Prescriptive Tuning",
6105
6190
  formatter_class=argparse.RawDescriptionHelpFormatter)
6106
6191
  parser.add_argument("--version", action="version", version=f"inav-analyze {REPORT_VERSION}")
6107
6192
  parser.add_argument("logfile", nargs="?", default=None,
@@ -6115,8 +6200,14 @@ def main():
6115
6200
  parser.add_argument("--device", metavar="PORT",
6116
6201
  help="Download blackbox from FC via serial. Use 'auto' to scan "
6117
6202
  "or specify port (e.g., /dev/ttyACM0, COM3).")
6118
- parser.add_argument("--erase", action="store_true",
6119
- help="Erase dataflash after successful download.")
6203
+ parser.add_argument("--no-erase", action="store_true",
6204
+ help="Don't erase FC flash after successful download and analysis. "
6205
+ "Default: flash is erased automatically after pipeline completes.")
6206
+ parser.add_argument("--archive", action="store_true",
6207
+ help="Compress the analyzed flight log to blackbox/archive/ instead of deleting. "
6208
+ "Builds a compressed history for re-analysis with future versions.")
6209
+ parser.add_argument("--keep-logs", action="store_true",
6210
+ help="Skip all cleanup - keep raw downloads, splits, and don't erase flash.")
6120
6211
  parser.add_argument("--download-only", action="store_true",
6121
6212
  help="Download blackbox from device but don't analyze.")
6122
6213
  parser.add_argument("--blackbox-dir", default="./blackbox",
@@ -6188,6 +6279,7 @@ def main():
6188
6279
  # ── Device mode: download blackbox from FC ──
6189
6280
  logfile = args.logfile
6190
6281
  config_raw = None
6282
+ device_port = None
6191
6283
  if args.device:
6192
6284
  try:
6193
6285
  try:
@@ -6279,13 +6371,16 @@ def main():
6279
6371
  print()
6280
6372
  filepath = fc.download_blackbox(
6281
6373
  output_dir=args.blackbox_dir,
6282
- erase_after=args.erase,
6374
+ erase_after=False, # erase happens after successful analysis
6283
6375
  )
6284
6376
 
6285
6377
  if not filepath:
6286
6378
  print(" ERROR: Download failed.")
6287
6379
  sys.exit(1)
6288
6380
 
6381
+ # Store port for post-analysis erase
6382
+ device_port = fc.port_path if hasattr(fc, 'port_path') else args.device
6383
+
6289
6384
  if args.download_only:
6290
6385
  print(f"\n To analyze:\n python3 {sys.argv[0]} {filepath}")
6291
6386
  sys.exit(0)
@@ -6417,17 +6512,45 @@ def main():
6417
6512
  # Flash may contain flights from multiple sessions (config changes between
6418
6513
  # arm cycles). Only the LATEST session's best flight deserves full analysis.
6419
6514
  # Earlier flights are stored in DB for progression but shown as one-liners.
6515
+ analyzed_file = None
6420
6516
  if getattr(args, 'nav', False):
6421
6517
  # Nav mode: skip multi-flight tuning scan, analyze last flight directly
6422
6518
  target = log_files[-1]
6423
6519
  if len(log_files) > 1:
6424
6520
  print(f"\n Nav mode: analyzing last flight ({len(log_files)} in flash)")
6425
6521
  _analyze_single_log(target, args, config_raw)
6522
+ analyzed_file = target
6426
6523
  elif len(log_files) > 1:
6427
- _process_multi_log(log_files, args, config_raw)
6524
+ analyzed_file = _process_multi_log(log_files, args, config_raw)
6428
6525
  else:
6429
6526
  # Single flight - full analysis
6430
6527
  _analyze_single_log(log_files[0], args, config_raw)
6528
+ analyzed_file = log_files[0]
6529
+
6530
+ # ── Post-analysis cleanup (only in device mode) ──
6531
+ if device_port and not args.keep_logs:
6532
+ # raw_download is the original downloaded file (pre-split)
6533
+ # For single-flight, it's the same as the analyzed file
6534
+ raw_download = logfile
6535
+ split_files = log_files if n_logs > 1 else []
6536
+
6537
+ _post_analysis_cleanup(
6538
+ args.blackbox_dir, raw_download, split_files, analyzed_file,
6539
+ archive=args.archive, keep_logs=args.keep_logs)
6540
+
6541
+ # Erase FC flash
6542
+ if not args.no_erase:
6543
+ try:
6544
+ try:
6545
+ from inav_toolkit.msp import INAVDevice
6546
+ except ImportError:
6547
+ from inav_msp import INAVDevice
6548
+ fc2 = INAVDevice(device_port)
6549
+ fc2.open()
6550
+ fc2.erase_dataflash()
6551
+ fc2.close()
6552
+ except Exception as e:
6553
+ print(f" Erase failed: {e}")
6431
6554
 
6432
6555
 
6433
6556
  def _process_multi_log(log_files, args, config_raw):
@@ -6613,6 +6736,8 @@ def _process_multi_log(log_files, args, config_raw):
6613
6736
  except Exception:
6614
6737
  pass
6615
6738
 
6739
+ return log_files[best_idx]
6740
+
6616
6741
 
6617
6742
  def _verdict_short(verdict):
6618
6743
  """Short display string for verdict codes."""
@@ -21,7 +21,7 @@ import json
21
21
  import os
22
22
  from datetime import datetime
23
23
 
24
- VERSION = "2.18.0"
24
+ VERSION = "2.19.0"
25
25
 
26
26
  SCHEMA_VERSION = 1
27
27
 
@@ -33,7 +33,7 @@ try:
33
33
  except ImportError:
34
34
  serial = None # Checked in open()
35
35
 
36
- VERSION = "2.18.0"
36
+ VERSION = "2.19.0"
37
37
 
38
38
  # ─── MSP Command IDs ─────────────────────────────────────────────────────────
39
39
 
@@ -21,7 +21,7 @@ import sys
21
21
  import textwrap
22
22
  from datetime import datetime
23
23
 
24
- VERSION = "2.18.0"
24
+ VERSION = "2.19.0"
25
25
 
26
26
 
27
27
  def _enable_ansi_colors():
@@ -25,7 +25,7 @@ import re
25
25
  import sys
26
26
  import textwrap
27
27
 
28
- VERSION = "2.18.0"
28
+ VERSION = "2.19.0"
29
29
 
30
30
 
31
31
  def _enable_ansi_colors():
@@ -22,7 +22,7 @@ import time
22
22
  try:
23
23
  from inav_toolkit import __version__ as VERSION
24
24
  except ImportError:
25
- VERSION = "2.18.0"
25
+ VERSION = "2.19.0"
26
26
 
27
27
  # Module paths for subprocess invocation (package-aware)
28
28
  ANALYZER_MODULE = "inav_toolkit.blackbox_analyzer"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: inav-toolkit
3
- Version: 2.18.0
3
+ Version: 2.19.0
4
4
  Summary: Blackbox analyzer, parameter checker, and tuning wizard for INAV flight controllers
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://github.com/agoliveira/INAV-Toolkit
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "inav-toolkit"
7
- version = "2.18.0"
7
+ version = "2.19.0"
8
8
  description = "Blackbox analyzer, parameter checker, and tuning wizard for INAV flight controllers"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
File without changes
File without changes
File without changes