Add Starlight docs for v3.02 signal monitoring and RF analysis tools

New pages (Diátaxis framework):
- tools/skywalker.mdx: reference for all 5 skywalker.py modes
- tools/spectrum-analysis.mdx: how-to guide for RF analysis workflows
- hardware/rf-coverage.mdx: explanation of LNB shifting and L-band coverage

Updated pages:
- firmware/custom-v301.mdx: v3.02 commands (0xB7-0xB9) with protocol docs
- usb/vendor-commands.mdx: signal monitoring command reference
- astro.config.mjs: sidebar entries and firmware version label
This commit is contained in:
Ryan Malloy 2026-02-12 17:38:48 -07:00
parent 23055f34ab
commit 3f970967c4
6 changed files with 747 additions and 19 deletions

View File

@ -29,6 +29,7 @@ export default defineConfig({
{ label: 'Overview', slug: 'hardware/overview' },
{ label: 'GPIO Pin Map', slug: 'hardware/gpio-pinmap' },
{ label: 'RF Specifications', slug: 'hardware/rf-specifications' },
{ label: 'RF Coverage', slug: 'hardware/rf-coverage' },
],
},
{
@ -77,7 +78,7 @@ export default defineConfig({
{ label: 'Rev.2 Analysis', slug: 'firmware/rev2-analysis' },
{ label: 'Kernel FW01', slug: 'firmware/kernel-fw01' },
{ label: 'FW2.13 Variants', slug: 'firmware/fw213-variants' },
{ label: 'Custom v3.01', slug: 'firmware/custom-v301' },
{ label: 'Custom v3.01\u2013v3.02', slug: 'firmware/custom-v301' },
{ label: 'Storage Formats', slug: 'firmware/storage-formats' },
],
},
@ -93,9 +94,11 @@ export default defineConfig({
items: [
{ label: 'Firmware Loader', slug: 'tools/firmware-loader' },
{ label: 'Tuning', slug: 'tools/tuning' },
{ label: 'SkyWalker RF Tool', slug: 'tools/skywalker' },
{ label: 'EEPROM Utilities', slug: 'tools/eeprom-utilities' },
{ label: 'Debugging', slug: 'tools/debugging' },
{ label: 'TS Analyzer', slug: 'tools/ts-analyzer' },
{ label: 'Spectrum Analysis', slug: 'tools/spectrum-analysis' },
],
},
{

View File

@ -1,11 +1,16 @@
---
title: Custom Firmware v3.01.0
description: Open-source SDCC + fx2lib replacement firmware with diagnostic commands, spectrum sweep, and blind scan.
title: Custom Firmware v3.01v3.02
description: Open-source SDCC + fx2lib replacement firmware with diagnostic commands, spectrum sweep, blind scan, and signal monitoring modes.
---
import { Steps, Badge, Aside, Tabs, TabItem, FileTree } from '@astrojs/starlight/components';
The custom v3.01.0 firmware is an open-source replacement for the stock SkyWalker-1 FX2 firmware, built with the SDCC compiler and fx2lib library. It implements all stock vendor commands for kernel driver compatibility and adds new diagnostic, spectrum sweep, and blind scan capabilities. <Badge text="Custom" variant="success" />
The custom firmware is an open-source replacement for the stock SkyWalker-1 FX2 firmware, built with the SDCC compiler and fx2lib library. It implements all stock vendor commands for kernel driver compatibility and adds diagnostic, spectrum sweep, blind scan, and signal monitoring capabilities. <Badge text="Custom" variant="success" />
| Version | Date | New Commands | Purpose |
|---------|------|-------------|---------|
| v3.01.0 | 2026-02-12 | `0xB0`--`0xB6` | Spectrum sweep, raw demod access, blind scan, hardware diagnostics |
| v3.02.0 | 2026-02-12 | `0xB7`--`0xB9` | Signal monitoring, tune-and-measure, batch register read |
## Project Structure
@ -123,6 +128,59 @@ EP0BUF[0] = val;
bcm_indirect_write(page, val);
```
## Signal Monitoring Commands (v3.02)
Three new commands (`0xB7`--`0xB9`) optimize the USB protocol for spectrum analysis and real-time signal monitoring: <Badge text="v3.02" variant="success" />
| Command | Name | Direction | Payload | Purpose |
|---------|------|-----------|---------|---------|
| `0xB7` | SIGNAL_MONITOR | IN | 8 bytes | Fast combined read: SNR + AGC + lock + status |
| `0xB8` | TUNE_MONITOR | OUT+IN | 10 bytes each | Tune + dwell + read in one round-trip |
| `0xB9` | MULTI_REG_READ | IN | 1--64 bytes | Batch read contiguous indirect registers |
### Signal Monitor (0xB7)
Combines six indirect register reads and two direct register reads into a single 8-byte USB transfer. Replaces three separate transfers (`GET_SIGNAL_STRENGTH` + `GET_SIGNAL_LOCK` + individual register reads) with one.
```c title="SIGNAL_MONITOR response format (8 bytes)"
Bytes 0-1: SNR (u16 LE, indirect regs 0x00-0x01, dBu × 256)
Bytes 2-3: AGC1 (u16 LE, indirect regs 0x02-0x03)
Bytes 4-5: AGC2 (u16 LE, indirect regs 0x04-0x05)
Byte 6: Lock (direct reg 0xA4, bit 5 = locked)
Byte 7: Status (direct reg 0xA2)
```
Enables ~50 Hz polling for real-time dish alignment feedback.
### Tune Monitor (0xB8)
Combines tune + configurable dwell + signal read into one command round-trip. This is the building block for host-driven spectrum sweeps.
The command uses two USB control transfers sharing the same bRequest code, distinguished by direction:
```c title="TUNE_MONITOR protocol"
// Phase 1: OUT (0x40) — host sends 10-byte tune payload
// wValue = dwell_ms (1-255), firmware tunes, waits, reads signal
// Phase 2: IN (0xC0) — host reads 10-byte result
// Bytes 0-5: SNR(2) + AGC1(2) + AGC2(2)
// Byte 6: lock, Byte 7: status
// Bytes 8-9: dwell_ms echo (u16 LE)
```
<Aside type="note">
The OUT phase blocks for the full dwell time inside the FX2 vendor command handler. The USB STATUS phase does not complete until `handle_vendorcommand()` returns. With a maximum dwell of 255 ms and a USB timeout of 2000 ms, this is well within safe bounds.
</Aside>
### Multi Register Read (0xB9)
Batch-reads up to 64 contiguous BCM4500 indirect registers in a single USB transfer. Each register still requires an individual I2C read sequence internally, but eliminating 63 USB control transfer round-trips provides ~64× speedup for register exploration.
```c title="MULTI_REG_READ parameters"
wValue = start register number
wIndex = count (1-64)
Returns: count bytes, one per register
```
## BCM4500 Boot Sequence
The `bcm4500_boot()` function replicates the stock firmware's initialization with added diagnostic instrumentation. The `boot_stage` variable tracks progress for debugging failed boots.
@ -250,19 +308,21 @@ Initial state after `TD_Init()`:
## Differences from Stock Firmware
| Feature | Stock v2.06 | Custom v3.01 |
|---------|-------------|--------------|
| Toolchain | Unknown (proprietary) | SDCC + fx2lib (open source) |
| I2C timeout | None (infinite spin) | 6000-count (~5 ms) |
| Boot diagnostics | None | Incremental debug modes |
| New commands | None | 7 commands (`0xB0`--`0xB6`) |
| Spectrum sweep | Not possible | `0xB0` with configurable step |
| Blind scan | Not possible | `0xB3` with SR sweep |
| Raw register access | Not possible | `0xB1`/`0xB2` |
| I2C bus scan | Not possible | `0xB4` |
| GPIO read | Not possible | `0xB6` |
| Anti-tampering | Present (v2.13) | Removed |
| Source available | No | Yes (`firmware/skywalker1.c`) |
| Feature | Stock v2.06 | Custom v3.01 | Custom v3.02 |
|---------|-------------|--------------|--------------|
| Toolchain | Unknown (proprietary) | SDCC + fx2lib (open source) | Same |
| I2C timeout | None (infinite spin) | 6000-count (~5 ms) | Same |
| Boot diagnostics | None | Incremental debug modes | Same |
| Custom commands | None | 7 (`0xB0`--`0xB6`) | 10 (`0xB0`--`0xB9`) |
| Spectrum sweep | Not possible | `0xB0` via EP2 bulk | `0xB8` via control EP (host-driven) |
| Blind scan | Not possible | `0xB3` with SR sweep | Same |
| Signal monitoring | 3 USB transfers | Same as stock | 1 transfer (`0xB7`, 8 bytes) |
| Batch register read | 1 reg per transfer | Same | 64 regs per transfer (`0xB9`) |
| Raw register access | Not possible | `0xB1`/`0xB2` | Same |
| I2C bus scan | Not possible | `0xB4` | Same |
| GPIO read | Not possible | `0xB6` | Same |
| Anti-tampering | Present (v2.13) | Removed | Removed |
| Source available | No | Yes | Yes (`firmware/skywalker1.c`) |
## Tuning Implementation

View File

@ -0,0 +1,110 @@
---
title: RF Coverage and Limitations
description: How LNB frequency shifting maps the 950-2150 MHz IF range to different RF bands, and what the hardware can and cannot do beyond satellite reception.
---
import { Aside } from '@astrojs/starlight/components';
The SkyWalker-1 was designed as a DVB-S satellite receiver, but the BCM4500's signal monitoring registers work whether or not a signal is locked. This makes the hardware useful as a crude spectrum analyzer and RF power detector across any band an LNB can translate into the 950--2150 MHz IF window. Understanding what the hardware actually measures -- and where the measurement breaks down -- is essential to interpreting sweep results correctly.
## How the Power Detector Works
The BCM4500 demodulator contains an AGC (Automatic Gain Control) loop that adjusts the tuner gain to maintain a constant signal level at the ADC. The AGC register values (indirect registers 0x02--0x05) reflect how much gain the system is applying. This mechanism operates continuously, regardless of whether the demodulator achieves signal lock.
The key insight: **higher AGC values mean weaker signals** (the system needs more gain to reach the target level). The AGC responds to the total received power within the tuner's passband at the tuned frequency. It does not discriminate between modulated carriers, noise floors, or interference -- it measures raw RF energy.
The SIGNAL_MONITOR command (0xB7) reads both the SNR and AGC registers in a single USB transfer, avoiding the round-trip overhead of separate indirect register reads. The TUNE_MONITOR command (0xB8) combines tune + settle + read into one operation, forming the building block for spectrum sweeps. See [Signal Monitoring](/bcm4500/signal-monitoring/) for the register-level details.
## LNB Frequency Shifting
A Low-Noise Block downconverter (LNB) mounted at the dish feed performs frequency translation: it mixes the incoming RF signal with its internal local oscillator (LO), producing an intermediate frequency (IF) output that travels down the coaxial cable to the receiver.
```
actual_rf = if_frequency + lnb_lo
if_frequency = actual_rf - lnb_lo
```
The SkyWalker-1 tunes within a fixed IF range: 950--2150 MHz. The actual RF band covered depends entirely on which LNB is connected and what its LO frequency is. The receiver has no knowledge of the LNB's LO -- the host software must account for the frequency offset when computing tune parameters and interpreting results.
## RF Coverage Map
| Configuration | LNB LO (MHz) | IF Range (MHz) | Actual RF Covered (MHz) | Typical Use |
|---|---|---|---|---|
| Ku-band low | 9,750 | 950--2,150 | 10,700--11,900 | Satellite TV low band |
| Ku-band high | 10,600 | 950--2,150 | 11,550--12,750 | Satellite TV high band |
| C-band | 5,150 | 950--2,150 | 6,100--7,300 | C-band satellite |
| No LNB (direct) | 0 | 950--2,150 | 950--2,150 | L-band direct input |
| Custom (9.0 GHz) | 9,000 | 950--2,150 | 9,950--11,150 | QO-100 DATV |
<Aside type="note" title="Universal LNB Band Switching">
Universal Ku-band LNBs switch between low (9,750 MHz) and high (10,600 MHz) LO frequencies using the 22 kHz tone signal. The SkyWalker-1 controls this tone via GPIO P0.3 -- see [LNB Control](/lnb-diseqc/lnb-control/) for the command interface. The `tune.py` tool handles band switching automatically via the `--band low/high` option.
</Aside>
## L-Band Direct Input
With no LNB -- cable connected directly to an antenna or feed -- the hardware covers the raw 950--2,150 MHz IF range. This encompasses several interesting allocations:
| Range (MHz) | Allocation | Detectable? | Demodulatable? |
|---|---|---|---|
| 1,240--1,300 | Amateur 23 cm band | Yes (energy) | No (SSB/CW/FM) |
| 1,525--1,559 | Inmarsat downlink | Yes (energy) | No (proprietary) |
| 1,559--1,610 | GNSS (GPS L1, Galileo E1) | Yes (spread spectrum) | No (CDMA/BOC) |
| 1,610--1,626 | Iridium downlink | Yes (energy) | No (TDMA/FDMA) |
| 1,670--1,710 | MetSat (GOES LRIT, NOAA HRPT) | Yes (carrier) | No (non-DVB framing) |
| 1,710--1,785 | LTE/AWS uplink | Yes (energy) | No (OFDM) |
| 1,920--2,025 | UMTS uplink | Yes (energy) | No (WCDMA) |
"Detectable" means the AGC registers respond to RF energy at that frequency -- the hardware sees something. "Demodulatable" means the BCM4500 can lock onto the signal and produce a decoded transport stream. Only DVB-S, Turbo-coded, DCII, and DSS signals can be demodulated. Everything else shows up as an energy level without any ability to extract data.
### The QO-100 Exception
The Es'hail-2/QO-100 amateur satellite at 25.9 degrees East carries DVB-S DATV signals in the 10,491--10,499 MHz range. These are actual DVB-S QPSK signals at low symbol rates (333--2,000 ksps) -- the one case where amateur satellite signals use a modulation the SkyWalker-1 can natively demodulate.
The problem is IF range. With a standard 9,750 MHz universal LNB, 10,494 MHz maps to 744 MHz IF -- below the SkyWalker-1's 950 MHz minimum. A custom LNB with a ~9.0 GHz local oscillator puts the same signal at ~1,494 MHz IF, comfortably in range.
<Aside type="caution" title="Symbol Rate Floor">
The minimum symbol rate the BCM4500 supports is 256 ksps. Some QO-100 DATV signals run as low as 333 ksps, which is within range but near the lower boundary. Lock acquisition at these rates may require longer settling times.
</Aside>
## Hardware Limitations
The SkyWalker-1 is a satellite receiver repurposed as a measurement tool. The results are useful but carry inherent constraints worth understanding before interpreting sweep data.
### Resolution Bandwidth
The minimum symbol rate is 256 ksps, giving a minimum resolution bandwidth of approximately 346 kHz (`symbol_rate * 1.35` roll-off factor). This is far coarser than a dedicated spectrum analyzer, which might offer 1 kHz or even 10 Hz RBW. Narrowband signals -- SSB at 3 kHz, CW at 500 Hz, FM repeater outputs at 25 kHz -- cannot be individually resolved. They appear as a single energy bump within the ~346 kHz measurement window, indistinguishable from each other or from broadband noise at similar levels.
### Dynamic Range
The AGC provides approximately 30--40 dB of usable dynamic range. A professional spectrum analyzer offers 70+ dB. The practical consequence: weak signals near strong ones get masked. A satellite transponder 25 dB above the noise floor is clearly visible; a signal 5 dB above the noise floor next to a strong adjacent carrier may not be.
### Sweep Speed
Each tune-measure step takes approximately 12 ms (tune settling + dwell + USB transfer). A full 950--2,150 MHz sweep at 5 MHz steps requires about 240 steps at 12 ms each, totaling approximately 2.9 seconds. This is adequate for mapping satellite transponders or identifying persistent carriers, but too slow for capturing transient signals or frequency-hopping transmissions.
### What It Does Well
| Strength | Detail |
|---|---|
| Built-in LNB power | 13V/18V, 22 kHz tone, DiSEqC 1.0/1.2 -- complete satellite receiver chain with no external hardware |
| Native DVB-S demodulation | Can lock, decode, and stream DVB-S, Turbo, DCII, and DSS signals as MPEG-2 transport streams |
| Power measurement | Detects RF energy across the full 950--2,150 MHz IF range regardless of modulation |
| Transport stream capture | GPIF streaming provides real MPEG-2 TS data for locked signals |
| Low cost | Repurposes existing satellite receiver hardware for spectrum awareness |
### What It Cannot Do
| Limitation | Detail |
|---|---|
| Not an SDR | Cannot capture raw IQ samples or demodulate arbitrary waveforms |
| Fixed demod pipeline | Only DVB-S / Turbo / DCII / DSS modulations -- no FM, SSB, CW, OFDM, ATSC |
| Coarse RBW | Minimum ~346 kHz resolution bandwidth; narrowband signals are unresolvable |
| Limited dynamic range | ~30--40 dB usable vs 70+ dB for dedicated spectrum analyzers |
| No DVB-S2 | Incompatible FEC (LDPC vs Reed-Solomon) -- see [DVB-S2 Incompatibility](/driver/dvb-s2/) |
## See Also
- [Tuning Tool](/tools/tuning/) -- the primary user-facing tool for tuning, monitoring, and capture
- [RF Specifications](/hardware/rf-specifications/) -- electrical parameters, signal path, and LNB current limits
- [Signal Monitoring](/bcm4500/signal-monitoring/) -- AGC and SNR register details used by the power detector
- [LNB Control](/lnb-diseqc/lnb-control/) -- voltage, tone, and DiSEqC command interface

View File

@ -0,0 +1,329 @@
---
title: SkyWalker Multi-Mode RF Tool
description: Spectrum analysis, transponder scanning, signal monitoring, L-band analysis, and carrier tracking for the SkyWalker-1.
---
import { Tabs, TabItem, Steps, Aside, Badge } from '@astrojs/starlight/components';
`skywalker.py` provides five alternative operating modes beyond satellite feed tuning. These modes repurpose the BCM4500's AGC registers as a crude power detector across the 950--2150 MHz IF range, allowing spectrum sweeps, blind transponder scanning, real-time dish alignment, L-band direct input analysis, and carrier tracking -- all without requiring demodulator lock.
<Badge text="Requires custom firmware v3.02.0+" variant="caution" />
Commands `0xB7` (SIGNAL_MONITOR), `0xB8` (TUNE_MONITOR), and `0xB9` (BLIND_SCAN) must be present in the FX2 firmware. See [Custom v3.01](/firmware/custom-v301/) for the base firmware these commands build on.
## Subcommands
| Subcommand | Purpose |
|------------|---------|
| `spectrum` | Sweep spectrum analyzer (950--2150 MHz IF range) |
| `scan` | Automated transponder scanner (sweep + blind scan) |
| `monitor` | Real-time signal strength / dish alignment |
| `lband` | L-band direct input analyzer (no LNB) |
| `track` | Carrier/beacon tracker with logging |
## Global Options
| Flag | Description |
|------|-------------|
| `-v, --verbose` | Show raw USB traffic |
---
## spectrum -- Sweep Spectrum Analyzer
Sweeps the IF range using TUNE_MONITOR (`0xB8`) at each step and renders an ASCII bar chart to the terminal. Optionally produces waterfall displays, matplotlib plots, and CSV exports.
### Options
| Flag | Description |
|------|-------------|
| `--start FLOAT` | Start IF frequency in MHz (default: `950`) |
| `--stop FLOAT` | Stop IF frequency in MHz (default: `2150`) |
| `--step FLOAT` | Step size in MHz (default: `5`) |
| `--dwell INT` | Dwell time per step in ms (default: `10`) |
| `--lnb-lo FLOAT` | LNB LO frequency in MHz; 0 = direct input (default: `0`) |
| `--sr INT` | Symbol rate for measurement in ksps (default: `20000`) |
| `--waterfall` | Waterfall display (time x frequency x power) |
| `--sweeps INT` | Number of sweeps (default: `1`) |
| `--threshold FLOAT` | Peak detection threshold in dB above noise floor (default: `3`) |
| `--plot` | Show matplotlib plot after sweep completes |
| `--csv FILE` | Save sweep data to CSV |
### Examples
```bash title="Full IF range sweep"
sudo python3 tools/skywalker.py spectrum
```
```bash title="Narrow sweep with LNB LO, save CSV"
sudo python3 tools/skywalker.py spectrum --start 1200 --stop 1400 --step 2 --lnb-lo 9750 --csv sweep.csv
```
```bash title="Waterfall display, 10 sweeps"
sudo python3 tools/skywalker.py spectrum --waterfall --sweeps 10 --step 10
```
```bash title="Plot with peak markers"
sudo python3 tools/skywalker.py spectrum --plot --threshold 5
```
### Sample Output
```
SkyWalker-1 Spectrum Sweep
==================================================
Range: 950.0 - 2150.0 MHz IF (step 5.0 MHz)
Dwell: 10 ms/step SR: 20000 ksps
Steps: 240 Est. time: ~2.9s
950.0 | -62.3 dBm
955.0 | -61.8 dBm
960.0 | -62.1 dBm
...
1120.0 | ████████████████████ -48.2 dBm
1125.0 | ██████████████████████████████████ -42.7 dBm << PEAK
1130.0 | ████████████████████ -48.5 dBm
...
1920.0 | █████████████████████████████ -44.1 dBm << PEAK
1925.0 | ████████████████████████████████████ -41.3 dBm << PEAK
1930.0 | ████████████████████████ -46.0 dBm
...
2145.0 | -63.1 dBm
2150.0 | -62.9 dBm
Peaks detected (>3.0 dB above floor):
1125.0 MHz IF -42.7 dBm (RF: 1125.0 MHz)
1925.0 MHz IF -41.3 dBm (RF: 1925.0 MHz)
Sweep complete: 240 steps in 2.88s
```
<Aside type="tip">
When `--lnb-lo` is set, the X-axis labels and peak report convert IF to RF using `actual_rf = if_freq + lnb_lo`. This makes it straightforward to identify transponders by their published downlink frequency.
</Aside>
<Aside type="note">
Sweep time scales linearly: 240 steps at 5 MHz spacing takes approximately 2.9 seconds (12 ms per step including USB overhead). Reduce `--step` for finer resolution at the cost of longer sweep times.
</Aside>
---
## scan -- Automated Transponder Scanner
Performs a three-phase automated transponder search: coarse spectrum sweep, fine sweep around detected peaks, and blind scan at each refined peak across a range of symbol rates.
### Phases
<Steps>
1. **Coarse sweep** at 10 MHz steps across the full IF range to identify candidate carriers.
2. **Fine sweep** at 2 MHz steps around each peak detected in the coarse pass.
3. **Blind scan** (`0xB3`) at each refined peak, trying symbol rates from `--sr-min` to `--sr-max` in `--sr-step` increments.
</Steps>
### Options
| Flag | Description |
|------|-------------|
| `--start FLOAT` | Start IF frequency in MHz (default: `950`) |
| `--stop FLOAT` | Stop IF frequency in MHz (default: `2150`) |
| `--threshold FLOAT` | Peak detection threshold in dB (default: `3`) |
| `--sr-min INT` | Minimum symbol rate in ksps (default: `1000`) |
| `--sr-max INT` | Maximum symbol rate in ksps (default: `30000`) |
| `--sr-step INT` | Symbol rate step in ksps (default: `500`) |
| `--lnb-lo FLOAT` | LNB LO frequency in MHz (default: `9750`) |
| `--pol H/V/L/R` | Polarization |
| `--band low/high` | LNB band |
| `--json` | JSON output |
| `--csv FILE` | CSV output |
### Examples
```bash title="Full Ku-band low scan, horizontal"
sudo python3 tools/skywalker.py scan --pol H --band low
```
```bash title="Narrow scan, fast SR range, JSON output"
sudo python3 tools/skywalker.py scan --start 1100 --stop 1200 --sr-min 10000 --sr-max 30000 --sr-step 1000 --json
```
```bash title="C-band scan with custom LO"
sudo python3 tools/skywalker.py scan --lnb-lo 5150 --pol V --csv cband_scan.csv
```
<Aside type="caution">
A full scan across the default frequency and symbol rate ranges can take several minutes. Narrowing `--start`/`--stop` or `--sr-min`/`--sr-max` significantly reduces scan time.
</Aside>
---
## monitor -- Real-Time Signal Strength
Tunes to a specified frequency and symbol rate, then polls SIGNAL_MONITOR (`0xB7`) at a configurable rate to display a continuously updating signal strength indicator. Designed for hands-free dish alignment.
### Required Arguments
| Argument | Description |
|----------|-------------|
| `FREQ_MHZ` | Transponder frequency in MHz |
| `SR_KSPS` | Symbol rate in ksps |
### Options
| Flag | Description |
|------|-------------|
| `--pol H/V/L/R` | Polarization |
| `--band low/high` | LNB band |
| `--lnb-lo FLOAT` | LNB LO frequency in MHz (default: `9750`) |
| `--rate FLOAT` | Poll rate in Hz (default: `10`, max ~50) |
| `--audio` | Pitch-proportional beep for hands-free alignment |
| `--peak-hold` | Track and display maximum signal level |
| `--history INT` | Sparkline history length in samples (default: `60`) |
| `--plot` | Show matplotlib plot after stopping |
### Examples
```bash title="Basic monitor, horizontal low-band"
sudo python3 tools/skywalker.py monitor 12520 27500 --pol H --band high
```
```bash title="Audio-assisted dish alignment"
sudo python3 tools/skywalker.py monitor 11836 27500 --pol V --band low --audio --peak-hold
```
```bash title="Fast polling with long history"
sudo python3 tools/skywalker.py monitor 12520 27500 --pol H --band high --rate 30 --history 120
```
### Display Format
```
[LOCK] SNR 8.2dB AGC 0x3A [████████████████░░░░░░░░░░░░░░░░░░░░░░░░] 42% ▁▃▅▇█▇▅▃▅▇█▇▅▃▁
[----] SNR 0.0dB AGC 0xFF [░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0% ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
```
The display updates in-place using terminal control codes. The sparkline at the right edge shows `--history` samples of recent signal strength. Press Ctrl-C to stop.
<Aside type="tip">
The `--audio` mode produces a tone whose pitch rises with signal strength. This frees you from watching the screen while adjusting the dish. Combine with `--peak-hold` to know when you have passed the optimum position.
</Aside>
<Aside type="note">
SIGNAL_MONITOR returns 8 bytes per read. Practical poll rates above 50 Hz are limited by USB round-trip latency, not the BCM4500.
</Aside>
---
## lband -- L-Band Direct Input Analyzer
Identical to `spectrum` mode but with `lnb_lo=0` and LNB power disabled. Intended for direct RF input in the 950--2150 MHz range (L-band). Annotates known L-band frequency allocations in the output.
### Options
| Flag | Description |
|------|-------------|
| `--start FLOAT` | Start frequency in MHz (default: `950`) |
| `--stop FLOAT` | Stop frequency in MHz (default: `2150`) |
| `--step FLOAT` | Step size in MHz (default: `2`) |
| `--dwell INT` | Dwell time per step in ms (default: `20`) |
| `--23cm` | Narrow to 1240--1300 MHz with 500 kHz steps |
| `--band-info` | Print L-band allocation table and exit |
| `--waterfall` | Waterfall display |
| `--plot` | Show matplotlib plot |
| `--csv FILE` | Save data to CSV |
### L-Band Frequency Allocations
| Range (MHz) | Service |
|-------------|---------|
| 1240--1300 | Amateur 23cm |
| 1525--1559 | Inmarsat downlink |
| 1559--1610 | GNSS (GPS L1, Galileo E1) |
| 1610--1626 | Iridium downlink |
| 1670--1710 | MetSat (GOES LRIT, NOAA HRPT) |
| 1710--1785 | LTE/AWS uplink |
| 1920--2025 | UMTS uplink |
### Examples
```bash title="Full L-band sweep"
sudo python3 tools/skywalker.py lband
```
```bash title="23cm amateur band, fine resolution"
sudo python3 tools/skywalker.py lband --23cm
```
```bash title="Waterfall with CSV export"
sudo python3 tools/skywalker.py lband --waterfall --csv lband_sweep.csv
```
```bash title="Print allocation table only"
sudo python3 tools/skywalker.py lband --band-info
```
<Aside type="caution">
Can detect carrier **presence** at any frequency even if the modulation is incompatible with the BCM4500 demodulator. The AGC power reading does not depend on demodulation -- only on RF energy at the tuned frequency. This makes L-band mode useful for detecting GNSS, Inmarsat, Iridium, and other signals that the BCM4500 cannot decode.
</Aside>
<Aside type="tip">
Connect an L-band antenna or filtered preamp directly to the SkyWalker-1's F-type input. Do not power an LNB through this mode -- LNB voltage output is explicitly disabled.
</Aside>
---
## track -- Carrier/Beacon Tracker
Tunes to a single frequency and symbol rate, then polls SIGNAL_MONITOR continuously with timestamped logging. Detects lock/unlock transitions and optionally tracks frequency drift by periodically sweeping a narrow window around the target.
### Required Arguments
| Argument | Description |
|----------|-------------|
| `FREQ_MHZ` | Carrier frequency in MHz |
| `SR_KSPS` | Symbol rate in ksps |
### Options
| Flag | Description |
|------|-------------|
| `--pol H/V/L/R` | Polarization |
| `--band low/high` | LNB band |
| `--lnb-lo FLOAT` | LNB LO frequency in MHz (default: `9750`) |
| `--rate FLOAT` | Poll rate in Hz (default: `1`) |
| `--duration FLOAT` | Duration in seconds (default: until Ctrl-C) |
| `--log FILE` | CSV log file |
| `--drift-track` | Sweep +/-5 MHz every 5s to measure frequency drift |
| `--plot` | Show matplotlib plot after stopping |
| `--json-lines FILE` | JSON-lines log file |
### Examples
```bash title="Track a beacon for 1 hour, log to CSV"
sudo python3 tools/skywalker.py track 11700 27500 --pol V --band low --duration 3600 --log beacon.csv
```
```bash title="Drift tracking with JSON-lines output"
sudo python3 tools/skywalker.py track 12520 27500 --pol H --band high --drift-track --json-lines drift.jsonl
```
```bash title="Quick track with matplotlib plot on exit"
sudo python3 tools/skywalker.py track 11836 27500 --pol V --band low --rate 5 --plot
```
<Aside type="note">
Lock/unlock transitions are detected and logged with timestamps. Each transition line in the CSV includes the previous state duration, making it straightforward to calculate uptime and outage windows.
</Aside>
<Aside type="tip">
The `--drift-track` option sweeps +/-5 MHz around the target frequency every 5 seconds. This is useful for characterizing LNB drift over temperature changes or for tracking satellites with measurable Doppler shift.
</Aside>
---
## See Also
- [Signal Monitoring](/bcm4500/signal-monitoring/) -- SIGNAL_MONITOR register protocol and data format
- [Vendor Commands](/usb/vendor-commands/) -- complete USB vendor command reference including 0xB7--0xB9
- [Tuning Tool](/tools/tuning/) -- primary tuning, LNB control, and transport stream capture
- [RF Coverage](/hardware/rf-coverage/) -- frequency coverage and antenna considerations
- [Spectrum Analysis How-to](/tools/spectrum-analysis/) -- practical guides for each operating mode

View File

@ -0,0 +1,212 @@
---
title: Spectrum Analysis Guides
description: How-to guides for spectrum sweeps, transponder scanning, dish alignment, L-band monitoring, and carrier tracking.
---
import { Tabs, TabItem, Steps, Aside, Badge } from '@astrojs/starlight/components';
The SkyWalker-1's BCM4500 demodulator and BCM3440 tuner accept any signal in the 950--2150 MHz IF range. While the demod pipeline only locks to DVB-S, Turbo, DCII, and DSS carriers, the AGC registers respond to RF energy at any frequency in range. Custom firmware v3.02.0 exposes this through `SIGNAL_MONITOR` (0xB7) and `TUNE_MONITOR` (0xB8) commands, turning the hardware into a crude but useful power detector.
`skywalker.py` wraps these commands into practical modes: spectrum sweeps, transponder scanning, dish alignment monitoring, L-band direct input, and long-duration carrier tracking.
```bash
pip install pyusb matplotlib
```
---
## How to Sweep the Ku-Band Spectrum
Survey what transponders are active on a satellite by stepping across the IF range and recording AGC power at each frequency.
<Steps>
1. Confirm the dish is pointed at the target satellite and the LNB coax is connected to the SkyWalker-1 F-connector.
2. Run a spectrum sweep. The tool steps from `--start` to `--stop` in MHz (IF frequency), reading AGC power at each step. Specify your LNB local oscillator frequency so the output labels show true downlink frequencies.
```bash title="Sweep Ku-band low (H-pol)"
sudo python3 tools/skywalker.py spectrum --lnb-lo 9750 --start 950 --stop 2150 --step 5
```
```bash title="Sweep Ku-band high (V-pol)"
sudo python3 tools/skywalker.py spectrum --lnb-lo 10600 --start 950 --stop 2150 --step 5
```
3. Look for peaks in the output. Each peak corresponds to a carrier -- the wider the peak, the higher the symbol rate. The AGC value is relative, not calibrated, so compare peaks to each other rather than treating the numbers as absolute power.
4. Note the downlink frequencies of interesting peaks. Use these as inputs to the [Tuning Tool](/tools/tuning/) to attempt demodulator lock, or feed them into the transponder scanner below for automated identification.
</Steps>
<Aside type="tip">
Sweep both polarizations (H and V) and both bands (low and high) for complete satellite coverage. A universal Ku-band LNB uses 9750 MHz LO for low band and 10600 MHz LO for high band, selected by the 22 kHz tone. The tool handles tone and voltage switching automatically when `--pol` and `--band` are specified.
</Aside>
---
## How to Find Transponders Automatically
The scan mode automates the full process of discovering and identifying transponders on a satellite. It runs three phases internally: coarse AGC sweep, peak refinement, and blind demod lock attempts.
<Steps>
1. Start a scan with your LNB parameters and a detection threshold. The threshold controls how many dB above the noise floor a peak must be to count as a candidate.
```bash title="Full satellite scan, H-pol high band"
sudo python3 tools/skywalker.py scan --lnb-lo 10600 --pol H --band high --threshold 3
```
2. The scanner runs three phases automatically:
- **Phase 1 -- Coarse sweep**: Steps across the full IF range reading AGC power. Builds a noise floor baseline and identifies candidate peaks above the threshold.
- **Phase 2 -- Peak refinement**: Re-sweeps each candidate with finer step size to pinpoint center frequencies and estimate bandwidths.
- **Phase 3 -- Blind scan**: Attempts demodulator lock at each refined peak, cycling through modulation types (QPSK, Turbo QPSK, Turbo 8PSK, DCII) and common symbol rates. Reports lock status, SNR, and modulation parameters for each successful lock.
3. Review the results. Successfully locked transponders are reported with their downlink frequency, symbol rate, modulation type, FEC rate, and SNR. Peaks that produced AGC response but no demod lock may be DVB-S2, analog, or non-satellite signals.
</Steps>
<Aside type="note">
Lower `--threshold` values (e.g., `1` or `2`) find weaker transponders but produce more false positives from noise spikes and adjacent channel spillover. Start with `3` and decrease if you suspect you are missing carriers.
</Aside>
---
## How to Align a Dish
The monitor mode provides real-time signal quality feedback at high poll rates, suitable for peaking a dish on a known transponder.
<Steps>
1. Pick a known strong transponder on the target satellite. You need the downlink frequency and symbol rate. Transponder lists for most satellites are available from LyngSat or SatBeams.
2. Start the signal monitor at a high poll rate. The tool tunes to the specified transponder and continuously reads SNR and lock status.
```bash title="Monitor for dish alignment (50 Hz polling)"
sudo python3 tools/skywalker.py monitor 12520 27500 --lnb-lo 10600 --pol H --band high --rate 50 --audio --peak-hold
```
3. Slowly move the dish in azimuth and elevation while watching the SNR reading. The `--peak-hold` flag keeps the highest observed value on screen so you can tell when you have passed the peak.
4. The `--audio` flag produces a pitch-proportional tone through the system audio -- higher pitch means stronger signal. This frees you from watching the screen while adjusting the dish.
5. Fine-tune for maximum SNR. Once you find the general peak, make small adjustments in azimuth, then elevation, then azimuth again. Tighten the mount hardware and verify the SNR holds.
</Steps>
<Aside type="tip">
The `--audio` flag produces a pitch-proportional beep -- higher pitch means stronger signal. This is the practical way to align a dish when you cannot see the screen from the mount. Bring headphones or a Bluetooth speaker.
</Aside>
---
## How to Monitor L-Band Direct
The SkyWalker-1 accepts 950--2150 MHz directly at the F-connector. With no LNB in the path, you can survey L-band signals from a direct-connect antenna or feed.
<Steps>
1. Disconnect the LNB coax cable. Connect your L-band antenna, preamp, or feed directly to the SkyWalker-1 F-connector.
2. Run the `lband` mode. This automatically disables LNB power output to protect direct-connect equipment, then sweeps the IF range with zero LO offset.
```bash title="Full L-band survey with allocation annotations"
sudo python3 tools/skywalker.py lband --band-info
```
3. The `--band-info` flag annotates the sweep output with ITU frequency allocation data for the 950--2150 MHz range, identifying which services (GPS, Iridium, Inmarsat, amateur, weather satellite, ATC) occupy each segment.
4. Narrow to a specific band of interest for higher resolution sweeps.
```bash title="Focus on 23cm amateur band"
sudo python3 tools/skywalker.py lband --23cm --plot
```
```bash title="Focus on weather satellite frequencies"
sudo python3 tools/skywalker.py lband --start 1670 --stop 1710 --step 0.5 --dwell 50
```
</Steps>
<Aside type="caution">
The SkyWalker-1 can detect carrier presence but cannot demodulate non-DVB signals (FM, SSB, CW, OFDM). Weather satellite HRPT, GPS, and Iridium signals show as energy peaks but the data cannot be decoded.
</Aside>
<Aside type="caution">
When connecting equipment directly (no LNB), `lband` mode automatically disables the 13V/18V LNB power supply to avoid damaging your antenna or preamp. Do NOT use `spectrum` mode with `--lnb-lo 0` without first manually disabling LNB power.
</Aside>
---
## How to Receive QO-100 DATV
The Es'hail 2 / QO-100 geostationary amateur satellite carries DVB-S QPSK signals on its narrowband transponder at 10489--10499 MHz downlink. This is the one scenario where amateur satellite signals are both in the SkyWalker-1's IF range AND use a compatible modulation scheme.
The catch: a standard 9.75 GHz universal LNB places QO-100 at ~741 MHz IF, which is below the 950 MHz minimum. You need a modified or purpose-built LNB with a lower LO frequency.
| LNB LO | QO-100 IF Range | In Range? |
|--------|----------------|-----------|
| 9750 MHz (standard) | 739 -- 749 MHz | No |
| 9000 MHz (custom) | 1489 -- 1499 MHz | Yes |
| 9250 MHz (custom) | 1239 -- 1249 MHz | Yes |
With a suitable LNB, sweep the QO-100 DATV range:
```bash title="Sweep QO-100 DATV range with custom 9.0 GHz LO LNB"
sudo python3 tools/skywalker.py spectrum --lnb-lo 9000 --start 1480 --stop 1510 --step 1 --dwell 30
```
To tune to a specific carrier once you have identified it:
```bash title="Tune to a specific QO-100 DATV carrier"
sudo python3 tools/tune.py tune 10494 1000 --lnb-lo 9000 --mod qpsk --fec auto
```
<Aside type="note">
Most QO-100 DATV uses DVB-S QPSK at low symbol rates (333--2000 ksps). The SkyWalker-1 supports symbol rates down to 256 Ksps, so the majority of QO-100 DATV carriers are within range. Use `--fec auto` and let the demodulator search for the correct code rate.
</Aside>
---
## How to Track a Carrier Over Time
Track mode continuously monitors a transponder's signal quality and logs timestamped samples. Useful for rain fade analysis, antenna drift detection, and long-term signal characterization.
<Steps>
1. Tune to the target transponder frequency and symbol rate, specifying LNB parameters as usual.
2. Start tracking with logging enabled. Specify a duration in seconds, or omit `--duration` to run until Ctrl-C.
```bash title="Track a transponder for 1 hour with CSV logging"
sudo python3 tools/skywalker.py track 12520 27500 --lnb-lo 10600 --pol H --band high --log signal_log.csv --duration 3600
```
3. The tracker logs SNR, lock status, and AGC values at each sample. Lock/unlock transitions are flagged in the log for easy filtering.
4. For frequency drift analysis, add `--drift-track` to monitor the demodulator's carrier frequency offset over time.
```bash title="Track with drift detection"
sudo python3 tools/skywalker.py track 12520 27500 --lnb-lo 10600 --pol H --band high --drift-track --log drift.csv
```
</Steps>
<Aside type="tip">
Track mode detects and logs lock/unlock transitions. Use `--json-lines` for machine-parseable output that can be piped into analysis scripts or time-series databases.
</Aside>
---
## How to Export Data
All spectrum analysis modes support data export. Choose the format that fits your workflow.
| Flag | Applicable Modes | Output Format |
|------|-----------------|---------------|
| `--csv FILE` | `spectrum`, `scan`, `lband` | Comma-separated: frequency, power, annotations |
| `--log FILE` | `track` | Timestamped CSV: time, frequency, SNR, lock, AGC |
| `--json-lines FILE` | `track` | One JSON object per sample, newline-delimited |
| `--json` | `scan` | Full scan results as a single JSON document |
| `--plot` | `spectrum`, `scan`, `lband` | Interactive matplotlib window |
Combine `--csv` or `--json` with `--plot` to both save data and visualize it in a single run.
---
## See Also
- [Tuning Tool](/tools/tuning/) -- transponder tuning, LNB control, transport stream capture
- [RF Specifications](/hardware/rf-specifications/) -- IF range, symbol rate limits, LNB current limits
- [Signal Monitoring](/bcm4500/signal-monitoring/) -- SNR, lock status, and AGC register details

View File

@ -136,9 +136,9 @@ Returns 1 byte (0 or 1) from a single GPIO input pin -- likely an LNB overcurren
</TabItem>
<TabItem label="Custom Firmware">
## Custom Firmware Commands (0xB0--0xB6)
## Custom Firmware Commands (0xB0--0xB9)
Commands added in custom firmware v3.01.0 for development and diagnostics:
Commands added in custom firmware v3.01.0--v3.02.0 for development, diagnostics, and signal monitoring:
| Cmd | Name | Dir | wValue | wIndex | wLength | Purpose |
|-----|------|-----|--------|--------|---------|---------|
@ -158,6 +158,20 @@ Commands added in custom firmware v3.01.0 for development and diagnostics:
**0xB4 I2C_BUS_SCAN**: Returns a 16-byte bitmap (128 bits for addresses 0x00--0x77). Each bit set = ACK received at that 7-bit address.
### Signal Monitoring Commands (v3.02.0) <Badge text="v3.02" variant="success" />
| Cmd | Name | Dir | wValue | wIndex | wLength | Purpose |
|-----|------|-----|--------|--------|---------|---------|
| 0xB7 | SIGNAL_MONITOR | IN | 0 | 0 | 8 | Fast combined signal read: SNR(2) + AGC1(2) + AGC2(2) + lock(1) + status(1) |
| 0xB8 | TUNE_MONITOR | OUT+IN | dwell_ms | 0 | 10 | Tune + dwell + signal read. OUT=tune payload, IN=result |
| 0xB9 | MULTI_REG_READ | IN | start_reg | count | count | Batch read of contiguous BCM4500 indirect registers (1--64) |
**0xB7 SIGNAL_MONITOR**: Returns 8 bytes in one transfer: `[snr_lo, snr_hi, agc1_lo, agc1_hi, agc2_lo, agc2_hi, lock_0xA4, status_0xA2]`. SNR is from indirect regs 0x00--0x01 (dBu × 256). AGC values from regs 0x02--0x05. Lock/status from direct registers. Enables ~50 Hz polling.
**0xB8 TUNE_MONITOR**: Two-phase protocol sharing bRequest=0xB8. Phase 1 (OUT, bmRequestType=0x40): 10-byte EP0 payload in TUNE_8PSK format, `wValue=dwell_ms` (1--255). Firmware tunes, waits dwell_ms, reads signal into static buffer. Phase 2 (IN, bmRequestType=0xC0): returns 10-byte result `[snr(6), lock(1), status(1), dwell_echo(2)]`. The OUT phase blocks for the full dwell time.
**0xB9 MULTI_REG_READ**: `wValue` = start register, `wIndex` = count (1--64). Returns count bytes, one per register, in a single USB transfer. Each register still requires an individual I2C indirect read internally, but saves 63 USB round-trips for register exploration.
</TabItem>
</Tabs>