Normalize line endings to LF across entire repository

Apply .gitattributes normalization to convert all CRLF line
endings inherited from Windows-origin source files to Unix LF.
175 files, zero content changes.
This commit is contained in:
Ryan Malloy 2026-02-20 10:55:50 -07:00
parent 696d2dd387
commit bbdcb243dc
175 changed files with 56794 additions and 56794 deletions

View file

@ -1,8 +1,8 @@
{
"mcpServers": {
"skywalker-mcp": {
"command": "uv",
"args": ["run", "--directory", ".", "skywalker-mcp"]
}
}
}
{
"mcpServers": {
"skywalker-mcp": {
"command": "uv",
"args": ["run", "--directory", ".", "skywalker-mcp"]
}
}
}

View file

@ -1,35 +1,35 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "skywalker-mcp"
version = "2026.2.17"
description = "MCP server for the Genpix SkyWalker-1 DVB-S USB receiver"
requires-python = ">=3.11"
authors = [{name = "Ryan Malloy", email = "ryan@supported.systems"}]
dependencies = [
"fastmcp>=2.0",
"pyusb>=1.3",
]
[project.scripts]
skywalker-mcp = "skywalker_mcp.server:main"
[tool.hatch.build.targets.wheel]
packages = ["src/skywalker_mcp"]
[project.optional-dependencies]
dev = [
"pytest>=8.0",
"pytest-asyncio>=0.24",
"ruff>=0.9",
]
[tool.ruff]
target-version = "py311"
line-length = 100
[tool.pytest.ini_options]
testpaths = ["tests"]
asyncio_mode = "auto"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "skywalker-mcp"
version = "2026.2.17"
description = "MCP server for the Genpix SkyWalker-1 DVB-S USB receiver"
requires-python = ">=3.11"
authors = [{name = "Ryan Malloy", email = "ryan@supported.systems"}]
dependencies = [
"fastmcp>=2.0",
"pyusb>=1.3",
]
[project.scripts]
skywalker-mcp = "skywalker_mcp.server:main"
[tool.hatch.build.targets.wheel]
packages = ["src/skywalker_mcp"]
[project.optional-dependencies]
dev = [
"pytest>=8.0",
"pytest-asyncio>=0.24",
"ruff>=0.9",
]
[tool.ruff]
target-version = "py311"
line-length = 100
[tool.pytest.ini_options]
testpaths = ["tests"]
asyncio_mode = "auto"

View file

@ -1,3 +1,3 @@
"""MCP server for the Genpix SkyWalker-1 DVB-S USB receiver."""
__version__ = "2026.2.17"
"""MCP server for the Genpix SkyWalker-1 DVB-S USB receiver."""
__version__ = "2026.2.17"

View file

@ -1,136 +1,136 @@
"""Mock SkyWalker1 device for testing without USB hardware.
Used by:
- Server lifespan when SKYWALKER_MOCK=1 is set (integration testing)
- conftest.py fixtures for unit tests
"""
class MockSkyWalker1:
"""Returns plausible data for all SkyWalker1 API calls without USB hardware."""
def __init__(self, verbose=False):
self.verbose = verbose
self._motor_halted = False
self._armed = False
self._lnb_on = True
self._calls = []
def _record(self, method, *args, **kwargs):
self._calls.append((method, args, kwargs))
def open(self):
self._record("open")
def close(self):
self._record("close")
def ensure_booted(self):
self._record("ensure_booted")
def get_fw_version(self):
self._record("get_fw_version")
return {"version": "3.05.0-mock", "date": "2026-02-17", "raw": b"\x03\x05\x00"}
def get_config(self):
self._record("get_config")
return 0x3F
def get_usb_speed(self):
self._record("get_usb_speed")
return 2
def get_serial_number(self):
self._record("get_serial_number")
return bytes([0xDE, 0xAD, 0xBE, 0xEF])
def get_last_error(self):
self._record("get_last_error")
return 0x00
def signal_monitor(self):
self._record("signal_monitor")
return {
"snr_raw": 200, "snr_db": 8.5, "snr_pct": 42.5,
"agc1": 1200, "agc2": 800, "power_db": -45.3,
"locked": True, "lock": 0x1F, "status": 0x01,
}
def get_stream_diag(self, reset=False):
self._record("get_stream_diag", reset=reset)
return {"poll_count": 100, "overflow_count": 0, "sync_loss": 0, "armed": self._armed}
def sweep_spectrum(self, start_mhz, stop_mhz, step_mhz=5.0, dwell_ms=15):
self._record("sweep_spectrum", start_mhz, stop_mhz, step_mhz=step_mhz, dwell_ms=dwell_ms)
n_points = int((stop_mhz - start_mhz) / step_mhz) + 1
freqs = [start_mhz + i * step_mhz for i in range(n_points)]
powers = []
for f in freqs:
base = -50.0
if abs(f - 1420.0) < 5:
base += 8.0 * (1.0 - abs(f - 1420.0) / 5.0)
powers.append(base)
raw = [(int((p + 60) * 100), 0) for p in powers]
return freqs, powers, raw
def tune_monitor(self, sr_sps, freq_khz, mod_idx, fec_idx, dwell_ms):
self._record("tune_monitor", sr_sps, freq_khz, mod_idx, fec_idx, dwell_ms)
return {
"snr_raw": 180, "snr_db": 7.8, "snr_pct": 39.0,
"agc1": 1100, "agc2": 750, "power_db": -46.1,
"locked": True, "lock": 0x1F, "status": 0x01,
"dwell_ms": dwell_ms,
}
def adaptive_blind_scan(self, freq_khz, sr_min, sr_max, sr_step):
self._record("adaptive_blind_scan", freq_khz, sr_min, sr_max, sr_step)
return {"freq_khz": freq_khz, "locked": True, "sr_sps": 20000000}
def motor_halt(self):
self._record("motor_halt")
self._motor_halted = True
def motor_drive_east(self, steps):
self._record("motor_drive_east", steps)
self._motor_halted = False
def motor_drive_west(self, steps):
self._record("motor_drive_west", steps)
self._motor_halted = False
def motor_goto_position(self, slot):
self._record("motor_goto_position", slot)
def motor_goto_x(self, observer_lon, sat_lon):
self._record("motor_goto_x", observer_lon, sat_lon)
def motor_store_position(self, slot):
self._record("motor_store_position", slot)
def start_intersil(self, on=True):
self._record("start_intersil", on=on)
self._lnb_on = on
def set_lnb_voltage(self, high):
self._record("set_lnb_voltage", high)
def set_22khz_tone(self, on):
self._record("set_22khz_tone", on)
def i2c_bus_scan(self):
self._record("i2c_bus_scan")
return [0x08, 0x10, 0x51]
def i2c_raw_read(self, slave, register):
self._record("i2c_raw_read", slave, register)
return 0xAB
def arm_transfer(self, on):
self._record("arm_transfer", on)
self._armed = on
def read_stream(self, timeout=500):
self._record("read_stream", timeout=timeout)
if self._armed:
return bytes([0x47, 0x00, 0x00, 0x10] + [0xFF] * 184)
return None
"""Mock SkyWalker1 device for testing without USB hardware.
Used by:
- Server lifespan when SKYWALKER_MOCK=1 is set (integration testing)
- conftest.py fixtures for unit tests
"""
class MockSkyWalker1:
"""Returns plausible data for all SkyWalker1 API calls without USB hardware."""
def __init__(self, verbose=False):
self.verbose = verbose
self._motor_halted = False
self._armed = False
self._lnb_on = True
self._calls = []
def _record(self, method, *args, **kwargs):
self._calls.append((method, args, kwargs))
def open(self):
self._record("open")
def close(self):
self._record("close")
def ensure_booted(self):
self._record("ensure_booted")
def get_fw_version(self):
self._record("get_fw_version")
return {"version": "3.05.0-mock", "date": "2026-02-17", "raw": b"\x03\x05\x00"}
def get_config(self):
self._record("get_config")
return 0x3F
def get_usb_speed(self):
self._record("get_usb_speed")
return 2
def get_serial_number(self):
self._record("get_serial_number")
return bytes([0xDE, 0xAD, 0xBE, 0xEF])
def get_last_error(self):
self._record("get_last_error")
return 0x00
def signal_monitor(self):
self._record("signal_monitor")
return {
"snr_raw": 200, "snr_db": 8.5, "snr_pct": 42.5,
"agc1": 1200, "agc2": 800, "power_db": -45.3,
"locked": True, "lock": 0x1F, "status": 0x01,
}
def get_stream_diag(self, reset=False):
self._record("get_stream_diag", reset=reset)
return {"poll_count": 100, "overflow_count": 0, "sync_loss": 0, "armed": self._armed}
def sweep_spectrum(self, start_mhz, stop_mhz, step_mhz=5.0, dwell_ms=15):
self._record("sweep_spectrum", start_mhz, stop_mhz, step_mhz=step_mhz, dwell_ms=dwell_ms)
n_points = int((stop_mhz - start_mhz) / step_mhz) + 1
freqs = [start_mhz + i * step_mhz for i in range(n_points)]
powers = []
for f in freqs:
base = -50.0
if abs(f - 1420.0) < 5:
base += 8.0 * (1.0 - abs(f - 1420.0) / 5.0)
powers.append(base)
raw = [(int((p + 60) * 100), 0) for p in powers]
return freqs, powers, raw
def tune_monitor(self, sr_sps, freq_khz, mod_idx, fec_idx, dwell_ms):
self._record("tune_monitor", sr_sps, freq_khz, mod_idx, fec_idx, dwell_ms)
return {
"snr_raw": 180, "snr_db": 7.8, "snr_pct": 39.0,
"agc1": 1100, "agc2": 750, "power_db": -46.1,
"locked": True, "lock": 0x1F, "status": 0x01,
"dwell_ms": dwell_ms,
}
def adaptive_blind_scan(self, freq_khz, sr_min, sr_max, sr_step):
self._record("adaptive_blind_scan", freq_khz, sr_min, sr_max, sr_step)
return {"freq_khz": freq_khz, "locked": True, "sr_sps": 20000000}
def motor_halt(self):
self._record("motor_halt")
self._motor_halted = True
def motor_drive_east(self, steps):
self._record("motor_drive_east", steps)
self._motor_halted = False
def motor_drive_west(self, steps):
self._record("motor_drive_west", steps)
self._motor_halted = False
def motor_goto_position(self, slot):
self._record("motor_goto_position", slot)
def motor_goto_x(self, observer_lon, sat_lon):
self._record("motor_goto_x", observer_lon, sat_lon)
def motor_store_position(self, slot):
self._record("motor_store_position", slot)
def start_intersil(self, on=True):
self._record("start_intersil", on=on)
self._lnb_on = on
def set_lnb_voltage(self, high):
self._record("set_lnb_voltage", high)
def set_22khz_tone(self, on):
self._record("set_22khz_tone", on)
def i2c_bus_scan(self):
self._record("i2c_bus_scan")
return [0x08, 0x10, 0x51]
def i2c_raw_read(self, slave, register):
self._record("i2c_raw_read", slave, register)
return 0xAB
def arm_transfer(self, on):
self._record("arm_transfer", on)
self._armed = on
def read_stream(self, timeout=500):
self._record("read_stream", timeout=timeout)
if self._armed:
return bytes([0x47, 0x00, 0x00, 0x10] + [0xFF] * 184)
return None

File diff suppressed because it is too large Load diff

View file

@ -1,47 +1,47 @@
"""Shared fixtures for skywalker-mcp tests."""
from unittest.mock import MagicMock
import pytest
import skywalker_mcp.server as srv
from skywalker_mcp.mock_device import MockSkyWalker1
from skywalker_mcp.server import DeviceBridge
class MockContext:
"""Minimal mock of FastMCP Context for direct tool function calls.
Provides the bridge via request_context.lifespan_context["bridge"],
matching what _get_bridge(ctx) expects.
"""
def __init__(self, bridge: DeviceBridge):
self.request_context = MagicMock()
self.request_context.lifespan_context = {"bridge": bridge}
self._progress = []
async def report_progress(self, current, total):
self._progress.append((current, total))
@pytest.fixture
def mock_device():
"""Provide a fresh MockSkyWalker1 instance."""
return MockSkyWalker1()
@pytest.fixture
def bridge(mock_device):
"""Provide a DeviceBridge wrapping the mock device."""
b = DeviceBridge(mock_device)
srv._bridge = b
yield b
b.cancel_motor_watchdog()
srv._bridge = None
@pytest.fixture
def ctx(bridge):
"""Provide a MockContext wired to the bridge."""
return MockContext(bridge)
"""Shared fixtures for skywalker-mcp tests."""
from unittest.mock import MagicMock
import pytest
import skywalker_mcp.server as srv
from skywalker_mcp.mock_device import MockSkyWalker1
from skywalker_mcp.server import DeviceBridge
class MockContext:
"""Minimal mock of FastMCP Context for direct tool function calls.
Provides the bridge via request_context.lifespan_context["bridge"],
matching what _get_bridge(ctx) expects.
"""
def __init__(self, bridge: DeviceBridge):
self.request_context = MagicMock()
self.request_context.lifespan_context = {"bridge": bridge}
self._progress = []
async def report_progress(self, current, total):
self._progress.append((current, total))
@pytest.fixture
def mock_device():
"""Provide a fresh MockSkyWalker1 instance."""
return MockSkyWalker1()
@pytest.fixture
def bridge(mock_device):
"""Provide a DeviceBridge wrapping the mock device."""
b = DeviceBridge(mock_device)
srv._bridge = b
yield b
b.cancel_motor_watchdog()
srv._bridge = None
@pytest.fixture
def ctx(bridge):
"""Provide a MockContext wired to the bridge."""
return MockContext(bridge)

File diff suppressed because it is too large Load diff