diff --git a/dev-python/onnxruntime-bin/Manifest b/dev-python/onnxruntime-bin/Manifest
new file mode 100644
index 0000000000..6f7c13bf1d
--- /dev/null
+++ b/dev-python/onnxruntime-bin/Manifest
@@ -0,0 +1,6 @@
+DIST onnxruntime-1.30.0-cp312-cp312-manylinux_2_28_aarch64.whl 21345202 BLAKE2B 8bd08d30a15437a9955e96662f6d3cf406bc89412cf11e1f25f5f326ed6c951136e4276154a42fba9af2176c4277e004d38d1b9bec86f7cb6f7169966b5a1b3c SHA512 43e54f967edf93156a17d022d89da428314bd23912d8be3bd6680ffc619065e564123b73d73e3f9e1b6cf7595bd013ae5afd87462fcdd881972031750178c97f
+DIST onnxruntime-1.30.0-cp312-cp312-manylinux_2_28_x86_64.whl 23585654 BLAKE2B d8da7a40fcd35f7067833bf6e6cecfac4442eb3497f3d1251534bc723d71bcfc0049222ff9b91233fb754ac499b0cd74bc0bd456f4ed20a52402da10f1432f74 SHA512 b56a27445444e110e2415cddc51f52ab34090f2631b065af7db77548941bb0e4e28419246cb4c4019332c87a4a1a37261c10f2e1feef4cc8a8e98d4d6c408581
+DIST onnxruntime-1.30.0-cp313-cp313-manylinux_2_28_aarch64.whl 21344996 BLAKE2B bbf15566968628c460636f99c29c315c3f161833736fe516808573191a755f62b920715cbbe62064e5e9413f641a85c07045fc32b4325372f58e4627e8b3a6c6 SHA512 33f6ef756ba0d3ba62350aae9ff61d12863fc36d8fc437d3feb1d9896c19d96e06d01133bcc18e8ea8e9be37d7b2b6a296265654097d18bc9a0510f0a7c5f2b8
+DIST onnxruntime-1.30.0-cp313-cp313-manylinux_2_28_x86_64.whl 23585560 BLAKE2B 5348a17d3cd0999a91eb1b09afe1ca72dc38963b2543d97e0456d3cca1feec25bbe7c6623de14af7a540ef596cc1e0cade1136fb4afb1c7be2be49cccb790857 SHA512 bb888277876e5f17564560549579136c17a5fd51deb9914e4cc8f7b0ee88863ad2e016d999c74b079630cff7f6ae75b2ac2d23a74df511934aef2a2697f4218f
+DIST onnxruntime-1.30.0-cp314-cp314-manylinux_2_28_aarch64.whl 21346830 BLAKE2B 78bc07533b821eecd2b543de0e8ea0adeb8c0d345fb37b558b5cce4ff411b495ee8414b7ee3e9280a3b507ccd96be394739812fd9b6dda325e7a01ab5047e629 SHA512 538bab371a19137ce3e0fbc39e27c3bcae2aae08eea979557f23a09d09f09ab15d763d8f6b57121e014a03c2a0909dd8a9352e06ba377634386763de957d2738
+DIST onnxruntime-1.30.0-cp314-cp314-manylinux_2_28_x86_64.whl 23586333 BLAKE2B e1787eabdae5b22c3f8a86a47c7dc6e0a8c78492ff08b0e73c301de84143f19e616dc2e3881162725618a8b2aba014216395ffae1b0a5a389821d7d125304e70 SHA512 cda93eb6cf1b034e788777d503ca0ce3bca86098ec626e5d5f014d164f3ee24b683caf660612e7850fc4d5aa19c1dd43f24a80db08c736a8570c418b673002db
diff --git a/dev-python/onnxruntime-bin/metadata.xml b/dev-python/onnxruntime-bin/metadata.xml
new file mode 100644
index 0000000000..209451fc29
--- /dev/null
+++ b/dev-python/onnxruntime-bin/metadata.xml
@@ -0,0 +1,17 @@
+
+
+
+
+ blshkv@gmail.com
+ Anton Bolshakov
+
+
+ ONNX Runtime is a cross-platform inference and training machine-learning
+ accelerator. This package provides the prebuilt binary wheels from PyPI
+ since no source distribution is available.
+
+
+ microsoft/onnxruntime
+ onnxruntime
+
+
diff --git a/dev-python/onnxruntime-bin/onnxruntime-bin-1.30.0.ebuild b/dev-python/onnxruntime-bin/onnxruntime-bin-1.30.0.ebuild
new file mode 100644
index 0000000000..f35d449f83
--- /dev/null
+++ b/dev-python/onnxruntime-bin/onnxruntime-bin-1.30.0.ebuild
@@ -0,0 +1,70 @@
+# Copyright 1999-2026 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+EAPI=8
+
+DISTUTILS_USE_PEP517=setuptools
+PYTHON_COMPAT=( python3_{12..14} )
+inherit distutils-r1 pypi
+
+DESCRIPTION="Cross-platform inference accelerator for ONNX models"
+HOMEPAGE="https://onnxruntime.ai https://github.com/microsoft/onnxruntime"
+
+# Per-Python prebuilt wheels only (no sdist available on PyPI)
+# pypi_wheel_url generates /// paths which 404 for onnxruntime;
+# use direct hash-based CDN URLs instead.
+SRC_URI="
+ amd64? (
+ python_targets_python3_12? (
+ https://files.pythonhosted.org/packages/34/35/e7f862dbacbc99fadd9b14a614e49c99bf0f35fd9927a82f096e3de33531/onnxruntime-1.30.0-cp312-cp312-manylinux_2_28_x86_64.whl
+ )
+ python_targets_python3_13? (
+ https://files.pythonhosted.org/packages/f1/a1/ede48ab5dc54907a2999362777f541e132639fb06628ded1932058aa8a36/onnxruntime-1.30.0-cp313-cp313-manylinux_2_28_x86_64.whl
+ )
+ python_targets_python3_14? (
+ https://files.pythonhosted.org/packages/f1/38/8138eed225c5bc6ddfc05879ecac7dacc63c34b9b6f99be72839c1f6dc49/onnxruntime-1.30.0-cp314-cp314-manylinux_2_28_x86_64.whl
+ )
+ )
+ arm64? (
+ python_targets_python3_12? (
+ https://files.pythonhosted.org/packages/16/bd/cbc5b8f91963689fdd622f463508c01d0aa95d3f944747b1e0b1eb2160b8/onnxruntime-1.30.0-cp312-cp312-manylinux_2_28_aarch64.whl
+ )
+ python_targets_python3_13? (
+ https://files.pythonhosted.org/packages/89/06/e603c71f43f4fe3fd156a053af79cbed6e27a2c649f0988a67d97fedd39f/onnxruntime-1.30.0-cp313-cp313-manylinux_2_28_aarch64.whl
+ )
+ python_targets_python3_14? (
+ https://files.pythonhosted.org/packages/c6/bc/1069e58b24779ba9d2fd479db5ecb3a15a6f49b585107c898819c0789558/onnxruntime-1.30.0-cp314-cp314-manylinux_2_28_aarch64.whl
+ )
+ )
+"
+
+S="${WORKDIR}"
+LICENSE="MIT"
+SLOT="0"
+KEYWORDS="amd64 ~arm64"
+RESTRICT="strip"
+
+RDEPEND="
+ >=dev-python/flatbuffers-23.5.26[${PYTHON_USEDEP}]
+ >=dev-python/numpy-1.21.6[${PYTHON_USEDEP}]
+ >=dev-python/packaging-21[${PYTHON_USEDEP}]
+ >=dev-python/protobuf-4.25.8[${PYTHON_USEDEP}]"
+DEPEND="${PYTHON_DEPS}"
+
+QA_PREBUILT="usr/lib/python*/site-packages/onnxruntime/capi/*.so
+ usr/lib/python*/site-packages/onnxruntime/capi/libonnxruntime*.so.*"
+
+python_compile() {
+ local cpver="cp${EPYTHON/python/}"
+ cpver="${cpver/./}"
+
+ local abitag
+ if use amd64; then
+ abitag="manylinux_2_28_x86_64"
+ elif use arm64; then
+ abitag="manylinux_2_28_aarch64"
+ fi
+
+ distutils_wheel_install "${BUILD_DIR}/install" \
+ "${DISTDIR}/$(pypi_wheel_name onnxruntime ${PV} ${cpver} ${cpver}-${abitag})"
+}
diff --git a/net-wireless/vulture/Manifest b/net-wireless/vulture/Manifest
new file mode 100644
index 0000000000..b008bf71e2
--- /dev/null
+++ b/net-wireless/vulture/Manifest
@@ -0,0 +1 @@
+DIST vulture-0.1.0.tar.gz 189470 BLAKE2B fee02e0df4a55a48001fd936e631b493b87d24c59160fd712db21a4f0d365bd96be4368b2d37e966f97b414cd92a88edb1284a33ff292c992e5aaba7c1dfa4cc SHA512 08219d99a7c68e1463904c3c87824eba89919597ace25f59a88783fe463344ab841749dd9e7b7d06e8cdf0761b9ca441233236a5a42e385920c43a452fbef05d
diff --git a/net-wireless/vulture/files/vulture-0.1.0-python314.patch b/net-wireless/vulture/files/vulture-0.1.0-python314.patch
new file mode 100644
index 0000000000..e9d7e5bf8a
--- /dev/null
+++ b/net-wireless/vulture/files/vulture-0.1.0-python314.patch
@@ -0,0 +1,309 @@
+diff --git a/pytest.ini b/pytest.ini
+index dea91f8..eea2c18 100644
+--- a/pytest.ini
++++ b/pytest.ini
+@@ -1 +1 @@
+-"""Pytest markers used by optional, physical lab tests."""
++[pytest]
+diff --git a/src/vulture/chemical_rf/__init__.py b/src/vulture/chemical_rf/__init__.py
+index 9739459..5e04527 100644
+--- a/src/vulture/chemical_rf/__init__.py
++++ b/src/vulture/chemical_rf/__init__.py
+@@ -8,6 +8,7 @@ copying or deleting its public API.
+ from __future__ import annotations
+
+ import importlib.util
++import sys
+ from pathlib import Path
+
+ _legacy_path = Path(__file__).resolve().parents[1] / "chemical_rf.py"
+@@ -15,6 +16,7 @@ _spec = importlib.util.spec_from_file_location("vulture._chemical_rf_legacy", _l
+ if _spec is None or _spec.loader is None:
+ raise ImportError(f"cannot load legacy Chemical-RF module: {_legacy_path}")
+ _legacy = importlib.util.module_from_spec(_spec)
++sys.modules[_spec.name] = _legacy # required for dataclasses on Python 3.14+
+ _spec.loader.exec_module(_legacy)
+
+ ChemicalBond = _legacy.ChemicalBond
+@@ -26,7 +28,7 @@ from .calibration import ImpedanceCalibrator, LinearCalibration, combine_uncerta
+ from .models import CalibrationPoint, ExperimentRecord, Measurement
+ from .materials import (
+ complex_permittivity, dielectric_resonance_frequency,
+- maxwell_garnett_permittivity, reflection_coefficient,
++ maxwell_garnett_permittivity, reflection_coefficient, absorption_coefficient,
+ )
+ from .pipeline import ChemicalRFAnalysisPipeline, ChemicalRFReport
+ from .spectroscopy import larmor_frequency_hz, simulate_fid, spectrum_from_fid
+@@ -36,6 +38,7 @@ __all__ = [
+ "CalibrationPoint", "ExperimentRecord", "Measurement", "ImpedanceCalibrator",
+ "LinearCalibration", "combine_uncertainty", "complex_permittivity",
+ "dielectric_resonance_frequency", "maxwell_garnett_permittivity",
+- "reflection_coefficient", "ChemicalRFAnalysisPipeline", "ChemicalRFReport",
++ "reflection_coefficient", "absorption_coefficient",
++ "ChemicalRFAnalysisPipeline", "ChemicalRFReport",
+ "larmor_frequency_hz", "simulate_fid", "spectrum_from_fid",
+ ]
+diff --git a/src/vulture/chemical_rf/materials.py b/src/vulture/chemical_rf/materials.py
+index 4f26b1b..a2941e7 100644
+--- a/src/vulture/chemical_rf/materials.py
++++ b/src/vulture/chemical_rf/materials.py
+@@ -31,8 +31,15 @@ def dielectric_resonance_frequency(epsilon_r: float, length_m: float, mode: int
+
+
+
+-def absorption_coefficient(load_ohm: complex, reference_ohm: complex = 50 + 0j) -> complex:
++def reflection_coefficient(load_ohm: complex, reference_ohm: complex = 50 + 0j) -> complex:
++ """Return the complex reflection coefficient Γ = (Z_L - Z_0) / (Z_L + Z_0)."""
+ if reference_ohm == 0:
+ raise ValueError("reference impedance cannot be zero")
+- return 2 * np.abs(reflection_coefficient(load_ohm, reference_ohm))**2 / (1 + np.abs(reflection_coefficient(load_ohm, reference_ohm))**2)
++ return (load_ohm - reference_ohm) / (load_ohm + reference_ohm)
++
++
++def absorption_coefficient(load_ohm: complex, reference_ohm: complex = 50 + 0j) -> float:
++ """Return the power absorption coefficient derived from the reflection coefficient."""
++ gamma = reflection_coefficient(load_ohm, reference_ohm)
++ return 1 - abs(gamma) ** 2
+
+diff --git a/src/vulture/cli.py b/src/vulture/cli.py
+index 7004dd9..e7eb8c3 100644
+--- a/src/vulture/cli.py
++++ b/src/vulture/cli.py
+@@ -15,6 +15,7 @@ import click
+ from vulture.chemical_rf.science import free_space_path_loss_db, wavelength_m
+ from vulture.chemical_rf.spectroscopy import larmor_frequency_hz
+ from vulture.forensics import audit_chemistry, audit_mathematics, audit_physics, audit_protocol
++from vulture.lab_cli import lab_cli
+
+
+ @click.group(invoke_without_command=True)
+@@ -28,6 +29,9 @@ def cli(ctx: click.Context, interactive: bool) -> None:
+ click.echo(ctx.get_help())
+
+
++cli.add_command(lab_cli, name="lab")
++
++
+ @cli.command()
+ def info() -> None:
+ """Display platform capabilities."""
+diff --git a/src/vulture/forensics/__init__.py b/src/vulture/forensics/__init__.py
+index c63255b..a28a790 100644
+--- a/src/vulture/forensics/__init__.py
++++ b/src/vulture/forensics/__init__.py
+@@ -1,6 +1,23 @@
+-from .forensics import (
+- Finding, ForensicReport, audit_chemistry, audit_mathematics,
+- audit_physics, audit_protocol, write_report,
+-)
++from __future__ import annotations
++
++import importlib.util
++import sys
++from pathlib import Path
++
++_legacy_path = Path(__file__).resolve().parents[1] / "forensics.py"
++_spec = importlib.util.spec_from_file_location("vulture._forensics_legacy", _legacy_path)
++if _spec is None or _spec.loader is None:
++ raise ImportError(f"cannot load legacy forensics module: {_legacy_path}")
++_legacy = importlib.util.module_from_spec(_spec)
++sys.modules[_spec.name] = _legacy
++_spec.loader.exec_module(_legacy)
++
++Finding = _legacy.Finding
++ForensicReport = _legacy.ForensicReport
++audit_chemistry = _legacy.audit_chemistry
++audit_mathematics = _legacy.audit_mathematics
++audit_physics = _legacy.audit_physics
++audit_protocol = _legacy.audit_protocol
++write_report = _legacy.write_report
+
+ __all__ = ["Finding", "ForensicReport", "audit_chemistry", "audit_mathematics", "audit_physics", "audit_protocol", "write_report"]
+diff --git a/src/vulture/lab/__init__.py b/src/vulture/lab/__init__.py
+index e8214c2..58bb1d6 100644
+--- a/src/vulture/lab/__init__.py
++++ b/src/vulture/lab/__init__.py
+@@ -1,3 +1,19 @@
+-from .lab import LabResult, attack_simulation, defense_simulation
++from __future__ import annotations
++
++import importlib.util
++import sys
++from pathlib import Path
++
++_legacy_path = Path(__file__).resolve().parents[1] / "lab.py"
++_spec = importlib.util.spec_from_file_location("vulture._lab_legacy", _legacy_path)
++if _spec is None or _spec.loader is None:
++ raise ImportError(f"cannot load legacy lab module: {_legacy_path}")
++_legacy = importlib.util.module_from_spec(_spec)
++sys.modules[_spec.name] = _legacy
++_spec.loader.exec_module(_legacy)
++
++LabResult = _legacy.LabResult
++attack_simulation = _legacy.attack_simulation
++defense_simulation = _legacy.defense_simulation
+
+ __all__ = ["LabResult", "attack_simulation", "defense_simulation"]
+diff --git a/src/vulture/physics_laboratory/physics_laboratory.py b/src/vulture/physics_laboratory/physics_laboratory.py
+index a2514c7..f6f8c23 100644
+--- a/src/vulture/physics_laboratory/physics_laboratory.py
++++ b/src/vulture/physics_laboratory/physics_laboratory.py
+@@ -73,7 +73,7 @@ class LinkBudget:
+ 'erp_dbm': erp,
+ 'received_power_dbm': rx_power,
+ 'path_loss_db': path_loss_db,
+- 'margin_db': margin,
++ 'link_margin_db': margin,
+ 'link_available': margin > -120 # Typical sensitivity
+ }
+
+@@ -309,39 +309,6 @@ class RadarSimulator:
+ 'velocity_ms': target_velocity_ms,
+ 'shifted_frequency_hz': frequency + doppler_freq
+ }
+- def calculate_radar_range(self, tx_power_dbm: float, frequency: float,):
+- """Calculate maximum radar range"""
+- callable_radar_ranger = lambda antenna_gain_db, rcs_dbsm : self.calculate_radar_range(tx_power_dbm, frequency, antenna_gain_db, rcs_dbsm)
+- return callable_radar_ranger
+- if antenna_gain_db == 0 and rcs_dbsm == 0:
+- return {
+- 'max_range_m': 0,
+- 'max_range_km': 0,
+- 'range_resolution_m': 0
+- }
+-
+- else:
+- raise ValueError("Antenna gain and RCS must be positive values")
+- def calculate_doppler_shift(self, target_velocity_ms: float, frequency: float):
+- """Calculate Doppler shift"""
+- callable_doppler_shift = lambda : self.doppler_shift(target_velocity_ms, frequency)
+- np.random.seed(42)
+- return callable_doppler_shift
+- if target_velocity_ms == 0 or frequency == 0:
+- return {
+- 'doppler_shift_hz': 0,
+- 'velocity_ms': 0,
+- 'shifted_frequency_hz': frequency
+- }
+- else:
+- raise ValueError("Target velocity and frequency must be positive values")
+- def calculate_signal_path(self, tx_power_dbm: float, frequency: float,):
+- """Calculate complete signal path"""
+- callable_signal_path = lambda tx_antenna_gain_db, rx_antenna_gain_db, distance, environmental_loss_db : self.calculate_signal_path(tx_power_dbm, frequency, tx_antenna_gain_db, rx_antenna_gain_db, distance, environmental_loss_db)
+- return callable_signal_path
+- def calculate_radar_range(self, tx_power_dbm: float, frequency: float, antenna_gain_db: float, rcs_dbsm: float, receiver_sensitivity_dbm: float = -110):
+- """Calculate maximum radar range"""
+- return self.calculate_radar_range(tx_power_dbm, frequency, antenna_gain_db, rcs_dbsm, receiver_sensitivity_dbm)
+
+
+
+diff --git a/src/vulture/rf_dna/fingerprint.py b/src/vulture/rf_dna/fingerprint.py
+index 6c37cf3..2bfed82 100644
+--- a/src/vulture/rf_dna/fingerprint.py
++++ b/src/vulture/rf_dna/fingerprint.py
+@@ -29,8 +29,8 @@ def extract_fingerprint(iq: np.ndarray, sample_rate: float) -> Fingerprint:
+ raise ValueError("IQ must be non-empty and finite; sample_rate must be positive")
+ amp = np.abs(x).astype(np.float64)
+ power = amp * amp
+- spectrum = np.abs(np.fft.rfft(x)) ** 2
+- frequencies = np.fft.rfftfreq(x.size, 1.0 / sample_rate)
++ spectrum = np.abs(np.fft.fft(x)) ** 2
++ frequencies = np.fft.fftfreq(x.size, 1.0 / sample_rate)
+ total = float(spectrum.sum())
+ centroid = float((frequencies * spectrum).sum() / total) if total else 0.0
+ spread = float(np.sqrt(((frequencies - centroid) ** 2 * spectrum).sum() / total)) if total else 0.0
+@@ -53,8 +53,6 @@ def similarity(left: Fingerprint, right: Fingerprint) -> float:
+ b = np.array([right.mean_amplitude, right.rms_amplitude, right.crest_factor, right.spectral_centroid_hz, right.spectral_spread_hz], dtype=float)
+ scale = np.maximum(np.maximum(np.abs(a), np.abs(b)), 1e-12)
+ distance = float(np.mean(np.abs(a - b) / scale))
+- c = np.array([left.sample_count, right.sample_count], dtype=float)
+- d = np.array([left.digest, right.digest], dtype=np.uint8)
+ return float(max(0.0, min(1.0, 1.0 - distance)))
+
+
+diff --git a/src/vulture/timeseries_framework/timeseries_engine.py b/src/vulture/timeseries_framework/timeseries_engine.py
+index c4b21ae..c655672 100644
+--- a/src/vulture/timeseries_framework/timeseries_engine.py
++++ b/src/vulture/timeseries_framework/timeseries_engine.py
+@@ -20,7 +20,10 @@ class TimeSeriesEngine:
+ """Add data with circular buffer."""
+ data = np.asarray(data)
+ n = len(data)
+- if self.position + n <= self.buffer_size:
++ if n >= self.buffer_size:
++ self.buffer[:] = data[-self.buffer_size:]
++ self.position = self.buffer_size
++ elif self.position + n <= self.buffer_size:
+ self.buffer[self.position:self.position+n] = data
+ self.position += n
+ else:
+diff --git a/tests/conftest.py b/tests/conftest.py
+index afc676c..f513bb0 100644
+--- a/tests/conftest.py
++++ b/tests/conftest.py
+@@ -1,15 +1,62 @@
+ """Conftest for pytest configuration."""
+ import pytest
+ import numpy as np
++from scipy import signal
++
+
+ @pytest.fixture
+ def sample_signal():
+- return np.sin(2 * np.pi * 0.1 * np.arange(1000))
++ t = np.arange(0, 1, 1/1e6)
++ return np.sin(2*np.pi*1e3*t) + 0.1*np.random.randn(len(t))
++
+
+ @pytest.fixture
+ def iq_data():
+- return np.exp(1j * 2 * np.pi * 0.1 * np.arange(1000))
++ t = np.arange(0, 1, 1/1e6)
++ return np.exp(1j * 2*np.pi*1e3*t) + 0.05*np.random.randn(len(t))
++
+
+ @pytest.fixture
+ def random_data():
+- return np.random.randn(1000)
++ return np.random.randn(10000)
++
++
++@pytest.fixture
++def bpsk_signal():
++ bits = np.random.randint(0, 2, 1000)
++ symbols = 2*bits - 1
++ t = np.arange(0, len(symbols)*10, 1)
++ carrier = np.sin(2*np.pi*0.01*t)
++ return (np.repeat(symbols, 10) * carrier)
++
++
++@pytest.fixture
++def qpsk_signal():
++ bits = np.random.randint(0, 4, 1000)
++ symbols = np.exp(1j * np.pi/2 * bits)
++ return np.repeat(symbols, 100)
++
++
++@pytest.fixture
++def chirp_signal():
++ t = np.linspace(0, 1, int(1e6))
++ return signal.chirp(t, 100, 1, 1000)
++
++
++@pytest.fixture
++def burst_signal():
++ t = np.arange(0, 1, 1/1e6)
++ burst1 = np.sin(2*np.pi*1e3*t[0:200000])
++ silence = np.zeros(300000)
++ burst2 = np.sin(2*np.pi*1e3*t[0:200000])
++ burst3 = np.zeros(300000)
++ return np.concatenate([burst1, silence, burst2, burst3])
++
++
++@pytest.fixture
++def multipath_signal():
++ t = np.arange(0, 1, 1/1e6)
++ direct = np.sin(2*np.pi*1e3*t)
++ path1 = 0.5*np.sin(2*np.pi*1e3*(t-0.0001))
++ path2 = 0.3*np.sin(2*np.pi*1e3*(t-0.0002))
++ return direct + path1 + path2
diff --git a/net-wireless/vulture/metadata.xml b/net-wireless/vulture/metadata.xml
new file mode 100644
index 0000000000..fcb19f7871
--- /dev/null
+++ b/net-wireless/vulture/metadata.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ blshkv@gmail.com
+ Anton Bolshakov
+
+
+ VULTURE is an integrated RF Fingerprinting, Visual Flowgraph Editor, Model Hub
+ and AI Orchestration Framework. It provides tools for signal analysis,
+ machine learning model management, and RF fingerprinting capabilities.
+
+
+ black-210/VULTURE
+
+
diff --git a/net-wireless/vulture/vulture-0.1.0.ebuild b/net-wireless/vulture/vulture-0.1.0.ebuild
new file mode 100644
index 0000000000..4e3508b892
--- /dev/null
+++ b/net-wireless/vulture/vulture-0.1.0.ebuild
@@ -0,0 +1,49 @@
+# Copyright 1999-2026 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+EAPI=8
+
+DISTUTILS_USE_PEP517=setuptools
+PYTHON_COMPAT=( python3_{12..14} )
+inherit distutils-r1
+
+DESCRIPTION="Integrated RF Fingerprinting, Visual Flowgraph Editor, Model Hub and AI Orchestration Framework"
+HOMEPAGE="https://github.com/black-210/VULTURE"
+SRC_URI="https://github.com/black-210/VULTURE/archive/refs/heads/main.tar.gz -> ${P}.tar.gz"
+
+S="${WORKDIR}/VULTURE-main"
+LICENSE="AGPL-3"
+SLOT="0"
+KEYWORDS="amd64"
+RESTRICT="test"
+
+RDEPEND="
+ >=dev-python/numpy-1.21.0[${PYTHON_USEDEP}]
+ >=dev-python/scipy-1.7.0[${PYTHON_USEDEP}]
+ >=dev-python/scikit-learn-1.0.0[${PYTHON_USEDEP}]
+ >=dev-python/pandas-1.3.0[${PYTHON_USEDEP}]
+ >=dev-python/matplotlib-3.4.0[${PYTHON_USEDEP}]
+ >=dev-python/pyqt6-6.2.0[${PYTHON_USEDEP}]
+ >=sci-ml/onnx-1.12.0[${PYTHON_USEDEP}]
+ >=dev-python/onnxruntime-bin-1.13.0[${PYTHON_USEDEP}]
+ >=dev-python/requests-2.28.0[${PYTHON_USEDEP}]
+ >=dev-python/cryptography-38.0.0[${PYTHON_USEDEP}]
+ >=dev-python/pyyaml-6.0[${PYTHON_USEDEP}]
+ >=dev-python/pydantic-1.9.0[${PYTHON_USEDEP}]
+ >=dev-python/aiohttp-3.8.0[${PYTHON_USEDEP}]
+ >=dev-python/pillow-9.0.0[${PYTHON_USEDEP}]"
+DEPEND="${RDEPEND}"
+
+src_prepare() {
+ # setup.py uses wrong package name and doesn't find vulture in src/
+ sed -i \
+ -e "s|name='TERFALCOM'|name='vulture'|" \
+ -e "s|packages=find_packages()|package_dir={'': 'src'}, packages=find_packages('src')|" \
+ -e "s|python_requires='>=3.8',|python_requires='>=3.8', entry_points={'console_scripts': ['vulture=vulture.cli:cli', 'vulture-chemical-rf=vulture.chemical_rf.cli:chemical_rf_cli']},|" \
+ setup.py || die
+ # pyproject.toml has [project.scripts] but no [project.version], which
+ # causes setuptools to fail; entry_points moved to setup.py above
+ rm pyproject.toml || die
+ eapply "${FILESDIR}/${P}-python314.patch"
+ eapply_user
+}
diff --git a/profiles/pentoo/base/package.accept_keywords/sci-ml b/profiles/pentoo/base/package.accept_keywords/sci-ml
new file mode 100644
index 0000000000..3848106045
--- /dev/null
+++ b/profiles/pentoo/base/package.accept_keywords/sci-ml
@@ -0,0 +1,2 @@
+# not stable yet in Gentoo
+=sci-ml/onnx-1*
diff --git a/profiles/pentoo/overlay/package.accept_keywords/sci-ml b/profiles/pentoo/overlay/package.accept_keywords/sci-ml
new file mode 120000
index 0000000000..2d28e10009
--- /dev/null
+++ b/profiles/pentoo/overlay/package.accept_keywords/sci-ml
@@ -0,0 +1 @@
+../../base/package.accept_keywords/sci-ml
\ No newline at end of file