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:
parent
696d2dd387
commit
bbdcb243dc
175 changed files with 56794 additions and 56794 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import { defineCollection } from 'astro:content';
|
||||
import { docsSchema } from '@astrojs/starlight/schema';
|
||||
|
||||
export const collections = {
|
||||
docs: defineCollection({ schema: docsSchema() }),
|
||||
};
|
||||
import { defineCollection } from 'astro:content';
|
||||
import { docsSchema } from '@astrojs/starlight/schema';
|
||||
|
||||
export const collections = {
|
||||
docs: defineCollection({ schema: docsSchema() }),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,230 +1,230 @@
|
|||
---
|
||||
title: BCM4500 Demodulator Interface
|
||||
description: Broadcom BCM4500 I2C addressing, register access protocol, and indirect register programming.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Badge, Aside, Steps } from '@astrojs/starlight/components';
|
||||
|
||||
The Broadcom BCM4500 is a 128-pin MQFP satellite demodulator that handles RF demodulation, forward error correction, and MPEG-2 transport stream output. The FX2 communicates with it exclusively over the shared I2C bus using an indirect register protocol.
|
||||
|
||||
See the [Register Map](/bcm4500/register-map/) for a consolidated lookup of all BCM4500 and FX2 register addresses referenced on this page.
|
||||
|
||||
## I2C Addressing
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| 7-bit I2C address | 0x08 |
|
||||
| 8-bit write address | 0x10 |
|
||||
| 8-bit read address | 0x11 |
|
||||
| Bus speed | 400 kHz |
|
||||
| FX2 I2C controller registers | I2CS (0xE678), I2DAT (0xE679), I2CTL (0xE67A) |
|
||||
| Alternate probe addresses (v2.13) | 0x3F, 0x7F |
|
||||
|
||||
The custom firmware and kernel driver use the 7-bit address 0x08. The stock firmware writes `addr << 1` = 0x10 for write and `(addr << 1) | 1` = 0x11 for read, following standard I2C convention.
|
||||
|
||||
<Aside type="note" title="Alternate Addresses">
|
||||
The v2.13 firmware probes I2C addresses 0x7F and 0x3F at startup (INT0 handler) to detect which demodulator variant is present. These may be alternative I2C address configurations or addresses for different demodulator sub-systems on hardware variants.
|
||||
</Aside>
|
||||
|
||||
## Direct Registers
|
||||
|
||||
These registers are accessed via standard I2C write/read to the BCM4500's device address:
|
||||
|
||||
| Register | Function | Used By |
|
||||
|----------|----------|---------|
|
||||
| 0xA2 | Status register (polled for readiness during boot) | Boot probe, signal strength |
|
||||
| 0xA4 | Lock/ready register; bit 5 (0x20) = signal locked | Signal lock check |
|
||||
| 0xA6 | Indirect page/address select | Indirect register protocol |
|
||||
| 0xA7 | Indirect data register (read/write) | Indirect register protocol |
|
||||
| 0xA8 | Indirect command register | Indirect register protocol |
|
||||
| 0xF9 | Demod status (v2.13 only) | GET_DEMOD_STATUS (0x99) / INT0 polling |
|
||||
|
||||
## Indirect Register Protocol
|
||||
|
||||
The BCM4500 uses an indirect register access scheme through three directly-addressable registers (0xA6, 0xA7, 0xA8). All tuning configuration, initialization data, and signal monitoring are performed through this protocol.
|
||||
|
||||
### Indirect Write Sequence
|
||||
|
||||
<Steps>
|
||||
1. **Page select** -- I2C WRITE to 0x08, register 0xA6 with the page number (typically 0x00).
|
||||
|
||||
2. **Data write** -- I2C WRITE to 0x08, register 0xA7 with N data bytes. The auto-increment feature allows writing multiple bytes in a single I2C transaction.
|
||||
|
||||
3. **Execute** -- I2C WRITE to 0x08, register 0xA8 with value 0x03 (indirect write command).
|
||||
|
||||
4. **Poll completion** -- I2C READ register 0xA8 until bit 0 clears (command complete).
|
||||
|
||||
5. **Verify** -- Optionally I2C READ register 0xA7 to read back and compare.
|
||||
</Steps>
|
||||
|
||||
```c title="Indirect Write Protocol"
|
||||
// Step 1: Page select
|
||||
I2C WRITE: [START] [0x10] [0xA6] [0x00] [STOP]
|
||||
|
||||
// Step 2: Data write (multi-byte, auto-increment)
|
||||
I2C WRITE: [START] [0x10] [0xA7] [data0] [data1] ... [dataN] [STOP]
|
||||
|
||||
// Step 3: Execute indirect write
|
||||
I2C WRITE: [START] [0x10] [0xA8] [0x03] [STOP]
|
||||
|
||||
// Step 4: Poll until complete
|
||||
I2C READ: [START] [0x10] [0xA8] [Sr] [0x11] [result] [STOP]
|
||||
// Repeat until (result & 0x01) == 0
|
||||
```
|
||||
|
||||
### Indirect Read Sequence
|
||||
|
||||
<Steps>
|
||||
1. **Address select** -- I2C WRITE to 0x08, register 0xA6 with the target register address.
|
||||
|
||||
2. **Placeholder write** -- I2C WRITE to 0x08, register 0xA7 with value 0x00.
|
||||
|
||||
3. **Execute** -- I2C WRITE to 0x08, register 0xA8 with value 0x01 (indirect read command).
|
||||
|
||||
4. **Wait** -- Short delay (~1 ms) for the BCM4500 to fetch the data.
|
||||
|
||||
5. **Read result** -- I2C READ from 0x08, register 0xA7 to get the result byte.
|
||||
</Steps>
|
||||
|
||||
### Auto-Increment Behavior
|
||||
|
||||
The BCM4500's data register (0xA7) supports auto-increment for multi-byte writes within a single I2C transaction. When writing N data bytes to 0xA7 without issuing STOP between bytes, the BCM4500 internally advances its data buffer pointer:
|
||||
|
||||
```
|
||||
I2C transaction (single write, multiple data bytes):
|
||||
START -> 0x10 (write) -> 0xA7 (reg) -> data[0] -> data[1] -> ... -> data[N-1] -> STOP
|
||||
```
|
||||
|
||||
The firmware uses this for initialization blocks and tuning data, reducing I2C bus overhead compared to byte-by-byte writes.
|
||||
|
||||
### Stock Firmware Init Block Write Pattern
|
||||
|
||||
The stock firmware uses a specific pattern for writing initialization blocks, extracted from `FUN_CODE_0ddd`:
|
||||
|
||||
```c title="Init Block Write Sequence"
|
||||
// 1. Page select = 0
|
||||
I2C WRITE: [0x10] [0xA6] [0x00]
|
||||
|
||||
// 2. Multi-byte data (auto-increment)
|
||||
I2C WRITE: [0x10] [0xA7] [data0] [data1] ... [dataN]
|
||||
|
||||
// 3. Trailing zero (stock firmware quirk)
|
||||
I2C WRITE: [0x10] [0xA7] [0x00]
|
||||
|
||||
// 4. Commit indirect write
|
||||
I2C WRITE: [0x10] [0xA8] [0x03]
|
||||
|
||||
// 5. Poll for completion
|
||||
I2C READ: [0xA8] until bit 0 clear
|
||||
```
|
||||
|
||||
<Aside type="note" title="Trailing Zero">
|
||||
The trailing zero write (step 3) appears in all stock firmware versions. Its purpose is unclear -- it may zero-pad the data buffer or serve as an end-of-data marker within the BCM4500's indirect register engine.
|
||||
</Aside>
|
||||
|
||||
## Demodulator Scan
|
||||
|
||||
### Tuning Retry (All Firmware Versions)
|
||||
|
||||
The tune function wraps the BCM4500 I2C programming sequence in a two-level retry loop:
|
||||
|
||||
- **Inner loop**: tries up to 3 different I2C address configurations per attempt. This supports hardware variants where the BCM4500 may appear at different bus addresses.
|
||||
- **Outer loop**: retries the entire inner scan up to 3 times.
|
||||
|
||||
This yields up to **3 × 3 = 9 total I2C programming attempts** per tune command. The v2.13 firmware adds a 20-attempt retry with checksum verification on each individual I2C write within the programming step.
|
||||
|
||||
### Boot-Time Probe (v2.13 Only)
|
||||
|
||||
The v2.13 firmware repurposed the INT0 interrupt handler (vector at `CODE:0003`) from USB re-enumeration to demodulator availability detection. The handler runs once during boot, before the main loop:
|
||||
|
||||
```c title="INT0 handler (v2.13) — demod probe"
|
||||
void INT0_vector(void) {
|
||||
for (counter = 0x28; counter != 0; counter--) { // 40 iterations
|
||||
byte result = I2C_read(0x7F); // Probe first alternate address
|
||||
if (result != 0x01) {
|
||||
result = I2C_read(0x3F); // Probe second alternate address
|
||||
if (result != 0x01) break; // Got a response — demod present
|
||||
}
|
||||
}
|
||||
no_demod_flag = (counter == 0); // True if all 40 attempts failed
|
||||
}
|
||||
```
|
||||
|
||||
If neither address responds after 40 iterations, `no_demod_flag` is set. This flag causes the firmware to skip all tuning attempts, avoiding I2C bus hangs on boards where the demodulator is absent or unpopulated.
|
||||
|
||||
<Aside type="note" title="Probe vs. Operating Address">
|
||||
The probe addresses (0x7F and 0x3F) are **not** the normal operating address. During regular tuning and signal monitoring, the BCM4500 is always accessed at its standard 7-bit address **0x08**. The probe addresses may be factory-default or configuration-pin-selected addresses that the BCM4500 responds to before its I2C address is programmed, or they may belong to different sub-systems on the demodulator IC. See the [Register Map — I2C Bus Addresses](/bcm4500/register-map/#i2c-bus-addresses) for the full address table.
|
||||
</Aside>
|
||||
|
||||
### `no_demod_flag` Behavior
|
||||
|
||||
When the boot probe fails (all 40 attempts exhausted):
|
||||
|
||||
1. `no_demod_flag` is set to a non-zero value
|
||||
2. The firmware enters its normal main loop but skips BCM4500 initialization
|
||||
3. `TUNE_8PSK` (0x86) commands are accepted but silently fail (no I2C transactions issued)
|
||||
4. `GET_SIGNAL_LOCK` (0x90) returns 0x00 (unlocked)
|
||||
5. The device remains functional for USB communication but cannot tune
|
||||
|
||||
This graceful degradation allows the host software to detect the condition via persistent lock failure rather than USB timeouts.
|
||||
|
||||
## FEC Architecture
|
||||
|
||||
The BCM4500 contains two FEC decoder paths:
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Turbo FEC">
|
||||
|
||||
**Advanced Modulation Turbo FEC Decoder**
|
||||
|
||||
Iterative turbo code decoder with the following code rates:
|
||||
|
||||
| Modulation | Supported Rates |
|
||||
|-----------|----------------|
|
||||
| QPSK | 1/4, 1/2, 3/4 |
|
||||
| 8PSK | 2/3, 3/4, 5/6, 8/9 |
|
||||
| 16QAM | 3/4 |
|
||||
|
||||
Outer code: Reed-Solomon (t=10).
|
||||
|
||||
These turbo codes are Broadcom/EchoStar proprietary and are not part of any open standard. They were used by Dish Network for high-definition satellite broadcasts.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Legacy FEC">
|
||||
|
||||
**Legacy DVB/DIRECTV/DCII-Compliant FEC Decoder**
|
||||
|
||||
Concatenated decoder with:
|
||||
|
||||
| Stage | Type | Rates |
|
||||
|-------|------|-------|
|
||||
| Inner | Viterbi (convolutional code) | 1/2, 2/3, 3/4, 5/6, 7/8 |
|
||||
| Outer | Reed-Solomon | Standard DVB-S parameters |
|
||||
|
||||
This decoder handles standard DVB-S QPSK, DVB-S BPSK, DSS QPSK, and all Digicipher II modes.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<Aside type="caution" title="No DVB-S2 Support">
|
||||
There is no LDPC or BCH decoder hardware in the BCM4500. DVB-S2 requires both of these, making it physically impossible to add DVB-S2 support through firmware updates.
|
||||
</Aside>
|
||||
|
||||
## Modulation Mode Constants
|
||||
|
||||
The firmware uses a 10-entry dispatch table for modulation types. See [Tuning Protocol](/bcm4500/tuning-protocol/) for the full tuning sequence.
|
||||
|
||||
| Index | Modulation | FEC Path |
|
||||
|-------|-----------|----------|
|
||||
| 0 | DVB-S QPSK | Legacy (Viterbi + RS) |
|
||||
| 1 | Turbo QPSK | Turbo |
|
||||
| 2 | Turbo 8PSK | Turbo |
|
||||
| 3 | Turbo 16QAM | Turbo |
|
||||
| 4 | DCII Combo | Legacy |
|
||||
| 5 | DCII I-stream | Legacy |
|
||||
| 6 | DCII Q-stream | Legacy |
|
||||
| 7 | DCII Offset QPSK | Legacy |
|
||||
| 8 | DSS QPSK | Legacy (Viterbi + RS) |
|
||||
| 9 | DVB-S BPSK | Legacy (Viterbi + RS) |
|
||||
|
||||
Indices 8 and 9 (DSS and DVB BPSK) share the same firmware handler. The FEC lookup uses the same table as DVB-S QPSK but ORs the result with 0x80 to distinguish them.
|
||||
---
|
||||
title: BCM4500 Demodulator Interface
|
||||
description: Broadcom BCM4500 I2C addressing, register access protocol, and indirect register programming.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Badge, Aside, Steps } from '@astrojs/starlight/components';
|
||||
|
||||
The Broadcom BCM4500 is a 128-pin MQFP satellite demodulator that handles RF demodulation, forward error correction, and MPEG-2 transport stream output. The FX2 communicates with it exclusively over the shared I2C bus using an indirect register protocol.
|
||||
|
||||
See the [Register Map](/bcm4500/register-map/) for a consolidated lookup of all BCM4500 and FX2 register addresses referenced on this page.
|
||||
|
||||
## I2C Addressing
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| 7-bit I2C address | 0x08 |
|
||||
| 8-bit write address | 0x10 |
|
||||
| 8-bit read address | 0x11 |
|
||||
| Bus speed | 400 kHz |
|
||||
| FX2 I2C controller registers | I2CS (0xE678), I2DAT (0xE679), I2CTL (0xE67A) |
|
||||
| Alternate probe addresses (v2.13) | 0x3F, 0x7F |
|
||||
|
||||
The custom firmware and kernel driver use the 7-bit address 0x08. The stock firmware writes `addr << 1` = 0x10 for write and `(addr << 1) | 1` = 0x11 for read, following standard I2C convention.
|
||||
|
||||
<Aside type="note" title="Alternate Addresses">
|
||||
The v2.13 firmware probes I2C addresses 0x7F and 0x3F at startup (INT0 handler) to detect which demodulator variant is present. These may be alternative I2C address configurations or addresses for different demodulator sub-systems on hardware variants.
|
||||
</Aside>
|
||||
|
||||
## Direct Registers
|
||||
|
||||
These registers are accessed via standard I2C write/read to the BCM4500's device address:
|
||||
|
||||
| Register | Function | Used By |
|
||||
|----------|----------|---------|
|
||||
| 0xA2 | Status register (polled for readiness during boot) | Boot probe, signal strength |
|
||||
| 0xA4 | Lock/ready register; bit 5 (0x20) = signal locked | Signal lock check |
|
||||
| 0xA6 | Indirect page/address select | Indirect register protocol |
|
||||
| 0xA7 | Indirect data register (read/write) | Indirect register protocol |
|
||||
| 0xA8 | Indirect command register | Indirect register protocol |
|
||||
| 0xF9 | Demod status (v2.13 only) | GET_DEMOD_STATUS (0x99) / INT0 polling |
|
||||
|
||||
## Indirect Register Protocol
|
||||
|
||||
The BCM4500 uses an indirect register access scheme through three directly-addressable registers (0xA6, 0xA7, 0xA8). All tuning configuration, initialization data, and signal monitoring are performed through this protocol.
|
||||
|
||||
### Indirect Write Sequence
|
||||
|
||||
<Steps>
|
||||
1. **Page select** -- I2C WRITE to 0x08, register 0xA6 with the page number (typically 0x00).
|
||||
|
||||
2. **Data write** -- I2C WRITE to 0x08, register 0xA7 with N data bytes. The auto-increment feature allows writing multiple bytes in a single I2C transaction.
|
||||
|
||||
3. **Execute** -- I2C WRITE to 0x08, register 0xA8 with value 0x03 (indirect write command).
|
||||
|
||||
4. **Poll completion** -- I2C READ register 0xA8 until bit 0 clears (command complete).
|
||||
|
||||
5. **Verify** -- Optionally I2C READ register 0xA7 to read back and compare.
|
||||
</Steps>
|
||||
|
||||
```c title="Indirect Write Protocol"
|
||||
// Step 1: Page select
|
||||
I2C WRITE: [START] [0x10] [0xA6] [0x00] [STOP]
|
||||
|
||||
// Step 2: Data write (multi-byte, auto-increment)
|
||||
I2C WRITE: [START] [0x10] [0xA7] [data0] [data1] ... [dataN] [STOP]
|
||||
|
||||
// Step 3: Execute indirect write
|
||||
I2C WRITE: [START] [0x10] [0xA8] [0x03] [STOP]
|
||||
|
||||
// Step 4: Poll until complete
|
||||
I2C READ: [START] [0x10] [0xA8] [Sr] [0x11] [result] [STOP]
|
||||
// Repeat until (result & 0x01) == 0
|
||||
```
|
||||
|
||||
### Indirect Read Sequence
|
||||
|
||||
<Steps>
|
||||
1. **Address select** -- I2C WRITE to 0x08, register 0xA6 with the target register address.
|
||||
|
||||
2. **Placeholder write** -- I2C WRITE to 0x08, register 0xA7 with value 0x00.
|
||||
|
||||
3. **Execute** -- I2C WRITE to 0x08, register 0xA8 with value 0x01 (indirect read command).
|
||||
|
||||
4. **Wait** -- Short delay (~1 ms) for the BCM4500 to fetch the data.
|
||||
|
||||
5. **Read result** -- I2C READ from 0x08, register 0xA7 to get the result byte.
|
||||
</Steps>
|
||||
|
||||
### Auto-Increment Behavior
|
||||
|
||||
The BCM4500's data register (0xA7) supports auto-increment for multi-byte writes within a single I2C transaction. When writing N data bytes to 0xA7 without issuing STOP between bytes, the BCM4500 internally advances its data buffer pointer:
|
||||
|
||||
```
|
||||
I2C transaction (single write, multiple data bytes):
|
||||
START -> 0x10 (write) -> 0xA7 (reg) -> data[0] -> data[1] -> ... -> data[N-1] -> STOP
|
||||
```
|
||||
|
||||
The firmware uses this for initialization blocks and tuning data, reducing I2C bus overhead compared to byte-by-byte writes.
|
||||
|
||||
### Stock Firmware Init Block Write Pattern
|
||||
|
||||
The stock firmware uses a specific pattern for writing initialization blocks, extracted from `FUN_CODE_0ddd`:
|
||||
|
||||
```c title="Init Block Write Sequence"
|
||||
// 1. Page select = 0
|
||||
I2C WRITE: [0x10] [0xA6] [0x00]
|
||||
|
||||
// 2. Multi-byte data (auto-increment)
|
||||
I2C WRITE: [0x10] [0xA7] [data0] [data1] ... [dataN]
|
||||
|
||||
// 3. Trailing zero (stock firmware quirk)
|
||||
I2C WRITE: [0x10] [0xA7] [0x00]
|
||||
|
||||
// 4. Commit indirect write
|
||||
I2C WRITE: [0x10] [0xA8] [0x03]
|
||||
|
||||
// 5. Poll for completion
|
||||
I2C READ: [0xA8] until bit 0 clear
|
||||
```
|
||||
|
||||
<Aside type="note" title="Trailing Zero">
|
||||
The trailing zero write (step 3) appears in all stock firmware versions. Its purpose is unclear -- it may zero-pad the data buffer or serve as an end-of-data marker within the BCM4500's indirect register engine.
|
||||
</Aside>
|
||||
|
||||
## Demodulator Scan
|
||||
|
||||
### Tuning Retry (All Firmware Versions)
|
||||
|
||||
The tune function wraps the BCM4500 I2C programming sequence in a two-level retry loop:
|
||||
|
||||
- **Inner loop**: tries up to 3 different I2C address configurations per attempt. This supports hardware variants where the BCM4500 may appear at different bus addresses.
|
||||
- **Outer loop**: retries the entire inner scan up to 3 times.
|
||||
|
||||
This yields up to **3 × 3 = 9 total I2C programming attempts** per tune command. The v2.13 firmware adds a 20-attempt retry with checksum verification on each individual I2C write within the programming step.
|
||||
|
||||
### Boot-Time Probe (v2.13 Only)
|
||||
|
||||
The v2.13 firmware repurposed the INT0 interrupt handler (vector at `CODE:0003`) from USB re-enumeration to demodulator availability detection. The handler runs once during boot, before the main loop:
|
||||
|
||||
```c title="INT0 handler (v2.13) — demod probe"
|
||||
void INT0_vector(void) {
|
||||
for (counter = 0x28; counter != 0; counter--) { // 40 iterations
|
||||
byte result = I2C_read(0x7F); // Probe first alternate address
|
||||
if (result != 0x01) {
|
||||
result = I2C_read(0x3F); // Probe second alternate address
|
||||
if (result != 0x01) break; // Got a response — demod present
|
||||
}
|
||||
}
|
||||
no_demod_flag = (counter == 0); // True if all 40 attempts failed
|
||||
}
|
||||
```
|
||||
|
||||
If neither address responds after 40 iterations, `no_demod_flag` is set. This flag causes the firmware to skip all tuning attempts, avoiding I2C bus hangs on boards where the demodulator is absent or unpopulated.
|
||||
|
||||
<Aside type="note" title="Probe vs. Operating Address">
|
||||
The probe addresses (0x7F and 0x3F) are **not** the normal operating address. During regular tuning and signal monitoring, the BCM4500 is always accessed at its standard 7-bit address **0x08**. The probe addresses may be factory-default or configuration-pin-selected addresses that the BCM4500 responds to before its I2C address is programmed, or they may belong to different sub-systems on the demodulator IC. See the [Register Map — I2C Bus Addresses](/bcm4500/register-map/#i2c-bus-addresses) for the full address table.
|
||||
</Aside>
|
||||
|
||||
### `no_demod_flag` Behavior
|
||||
|
||||
When the boot probe fails (all 40 attempts exhausted):
|
||||
|
||||
1. `no_demod_flag` is set to a non-zero value
|
||||
2. The firmware enters its normal main loop but skips BCM4500 initialization
|
||||
3. `TUNE_8PSK` (0x86) commands are accepted but silently fail (no I2C transactions issued)
|
||||
4. `GET_SIGNAL_LOCK` (0x90) returns 0x00 (unlocked)
|
||||
5. The device remains functional for USB communication but cannot tune
|
||||
|
||||
This graceful degradation allows the host software to detect the condition via persistent lock failure rather than USB timeouts.
|
||||
|
||||
## FEC Architecture
|
||||
|
||||
The BCM4500 contains two FEC decoder paths:
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Turbo FEC">
|
||||
|
||||
**Advanced Modulation Turbo FEC Decoder**
|
||||
|
||||
Iterative turbo code decoder with the following code rates:
|
||||
|
||||
| Modulation | Supported Rates |
|
||||
|-----------|----------------|
|
||||
| QPSK | 1/4, 1/2, 3/4 |
|
||||
| 8PSK | 2/3, 3/4, 5/6, 8/9 |
|
||||
| 16QAM | 3/4 |
|
||||
|
||||
Outer code: Reed-Solomon (t=10).
|
||||
|
||||
These turbo codes are Broadcom/EchoStar proprietary and are not part of any open standard. They were used by Dish Network for high-definition satellite broadcasts.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Legacy FEC">
|
||||
|
||||
**Legacy DVB/DIRECTV/DCII-Compliant FEC Decoder**
|
||||
|
||||
Concatenated decoder with:
|
||||
|
||||
| Stage | Type | Rates |
|
||||
|-------|------|-------|
|
||||
| Inner | Viterbi (convolutional code) | 1/2, 2/3, 3/4, 5/6, 7/8 |
|
||||
| Outer | Reed-Solomon | Standard DVB-S parameters |
|
||||
|
||||
This decoder handles standard DVB-S QPSK, DVB-S BPSK, DSS QPSK, and all Digicipher II modes.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<Aside type="caution" title="No DVB-S2 Support">
|
||||
There is no LDPC or BCH decoder hardware in the BCM4500. DVB-S2 requires both of these, making it physically impossible to add DVB-S2 support through firmware updates.
|
||||
</Aside>
|
||||
|
||||
## Modulation Mode Constants
|
||||
|
||||
The firmware uses a 10-entry dispatch table for modulation types. See [Tuning Protocol](/bcm4500/tuning-protocol/) for the full tuning sequence.
|
||||
|
||||
| Index | Modulation | FEC Path |
|
||||
|-------|-----------|----------|
|
||||
| 0 | DVB-S QPSK | Legacy (Viterbi + RS) |
|
||||
| 1 | Turbo QPSK | Turbo |
|
||||
| 2 | Turbo 8PSK | Turbo |
|
||||
| 3 | Turbo 16QAM | Turbo |
|
||||
| 4 | DCII Combo | Legacy |
|
||||
| 5 | DCII I-stream | Legacy |
|
||||
| 6 | DCII Q-stream | Legacy |
|
||||
| 7 | DCII Offset QPSK | Legacy |
|
||||
| 8 | DSS QPSK | Legacy (Viterbi + RS) |
|
||||
| 9 | DVB-S BPSK | Legacy (Viterbi + RS) |
|
||||
|
||||
Indices 8 and 9 (DSS and DVB BPSK) share the same firmware handler. The FEC lookup uses the same table as DVB-S QPSK but ORs the result with 0x80 to distinguish them.
|
||||
|
|
|
|||
|
|
@ -1,241 +1,241 @@
|
|||
---
|
||||
title: GPIF Streaming
|
||||
description: GPIF engine configuration, waveform descriptors, and transport stream data path from BCM4500 to USB host.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Badge, Aside, Steps } from '@astrojs/starlight/components';
|
||||
|
||||
The GPIF (General Programmable Interface) engine in the Cypress FX2 provides a hardware-managed data path from the BCM4500 demodulator to the USB host. After initial setup, no firmware intervention occurs in the data path -- the GPIF engine reads transport stream data directly into the EP2 FIFO, and the AUTOIN mechanism automatically commits full packets to the USB controller.
|
||||
|
||||
## Data Flow Architecture
|
||||
|
||||
```
|
||||
Cypress FX2 (CY7C68013A)
|
||||
+-----------------------------+
|
||||
| |
|
||||
BCM4500 P3.5 TS_EN | GPIF Engine EP2 FIFO | USB 2.0 HS
|
||||
Demodulator <-----------------+ (Master Read) (AUTOIN) +------------> Host
|
||||
(I2C:0x08) GPIF Data Bus | 0xE4xx wfm 4x buf | EP2 (0x82)
|
||||
-------------------> CTL/RDY pins 8-bit | Bulk IN
|
||||
8-bit parallel TS | | 7 URBs x 8KB
|
||||
+-----------------------------+
|
||||
```
|
||||
|
||||
<Aside type="note" title="Zero-Copy Path">
|
||||
The data path is fully hardware-managed. The GPIF engine reads data directly into the EP2 FIFO buffer. The EP2FIFOCFG AUTOIN bit causes the hardware to automatically commit full packets to the USB controller for transfer to the host. The FLOWSTATE engine re-triggers GPIF transactions when buffer space becomes available. The firmware only needs to start/stop the GPIF engine.
|
||||
</Aside>
|
||||
|
||||
## Key Register Configuration
|
||||
|
||||
All values are identical across the three stock firmware versions (v2.06, Rev.2 v2.10, v2.13):
|
||||
|
||||
| Register | Address | Value | Function |
|
||||
|----------|---------|-------|----------|
|
||||
| IFCONFIG | 0xE601 | 0xEE | Internal 48 MHz clock, GPIF master, async, debug output |
|
||||
| EP2FIFOCFG | 0xE618 | 0x0C | AUTOIN=1, ZEROLENIN=1, 8-bit data path |
|
||||
| REVCTL | 0xE60B | 0x03 | NOAUTOARM + SKIPCOMMIT |
|
||||
| CPUCS | 0xE600 | bits [4:3]=10 | 48 MHz CPU clock |
|
||||
| FLOWSTATEA | 0xE668 | OR 0x09 | FSEN (flow state enable) + FS[3] |
|
||||
| GPIFIE | 0xE65C | OR 0x3D | Waveform, TC, DONE, FIFO flag, WF2 interrupts |
|
||||
|
||||
### IFCONFIG Decode (0xEE = 1110_1110)
|
||||
|
||||
| Bit | Name | Value | Meaning |
|
||||
|-----|------|-------|---------|
|
||||
| 7 | IFCLKSRC | 1 | Internal clock source |
|
||||
| 6 | 3048MHZ | 1 | 48 MHz IFCLK frequency |
|
||||
| 5 | IFCLKOE | 1 | IFCLK pin drives output (clock to BCM4500) |
|
||||
| 4 | IFCLKPOL | 0 | Non-inverted clock polarity |
|
||||
| 3 | ASYNC | 1 | Asynchronous GPIF (RDY pin handshaking) |
|
||||
| 2 | GSTATE | 1 | Debug state output on PORTE |
|
||||
| 1:0 | IFCFG | 10 | GPIF internal master mode |
|
||||
|
||||
The FX2 operates as a GPIF master, reading data from the BCM4500's parallel transport stream output. Asynchronous mode means the GPIF uses RDY pin handshaking rather than clock-edge sampling.
|
||||
|
||||
### EP2FIFOCFG Decode (0x0C = 0000_1100)
|
||||
|
||||
| Bit | Name | Value | Meaning |
|
||||
|-----|------|-------|---------|
|
||||
| 4 | INFM1 | 0 | Packet count not decremented |
|
||||
| 3 | AUTOIN | 1 | Auto-commit IN packets when FIFO buffer full |
|
||||
| 2 | ZEROLENIN | 1 | Allow zero-length IN packets |
|
||||
| 1 | (reserved) | 0 | -- |
|
||||
| 0 | WORDWIDE | 0 | 8-bit data path (not 16-bit) |
|
||||
|
||||
The AUTOIN bit is critical: when the GPIF fills an EP2 FIFO buffer to the configured packet size, the FX2 hardware automatically arms the buffer for USB transfer.
|
||||
|
||||
## GPIF Waveform Configuration
|
||||
|
||||
The GPIF waveform descriptors occupy 128 bytes at 0xE400-0xE47F and are loaded from a compressed init table during firmware startup. The waveform programs a straightforward read cycle:
|
||||
|
||||
```
|
||||
States 0-5: CTL outputs = 0x01 (control line asserted), 1 IFCLK each
|
||||
State 6: CTL = 0x07, length = 0 (idle/terminate)
|
||||
Opcode: 0x00 (SDP = sample data point)
|
||||
Output: 0xF0 (FIFO write flags)
|
||||
```
|
||||
|
||||
This encodes a simple "assert read strobe, capture data, de-assert" cycle that reads one byte per GPIF transaction from the BCM4500's parallel port into the EP2 FIFO.
|
||||
|
||||
## FIFO Reset Sequence
|
||||
|
||||
All endpoint FIFOs are reset during initialization using the Cypress-prescribed procedure:
|
||||
|
||||
```c title="FIFO Reset (FUN_CODE_10d9)"
|
||||
FIFORESET = 0x80; // NAKALL: NAK all host transfers during reset
|
||||
// 3-NOP SYNCDELAY
|
||||
FIFORESET = 0x02; // Reset EP2 FIFO
|
||||
// 3-NOP SYNCDELAY
|
||||
FIFORESET = 0x04; // Reset EP4 FIFO
|
||||
// 3-NOP SYNCDELAY
|
||||
FIFORESET = 0x06; // Reset EP6 FIFO
|
||||
// 3-NOP SYNCDELAY
|
||||
FIFORESET = 0x08; // Reset EP8 FIFO
|
||||
// 3-NOP SYNCDELAY
|
||||
FIFORESET = 0x00; // Release NAKALL
|
||||
```
|
||||
|
||||
The triple-NOP delays (mandatory SYNCDELAY) between writes are required by the FX2 architecture: XRAM register writes take 2 cycles to propagate, and back-to-back writes to the same register need at least 3 instruction cycles.
|
||||
|
||||
## ARM_TRANSFER Command (0x85)
|
||||
|
||||
The host issues vendor command 0x85 to start or stop the MPEG-2 transport stream. The handler checks the [configuration status byte](/usb/config-status/) bit 0 (demodulator active) before proceeding.
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Start Streaming">
|
||||
|
||||
When `wValue=1` and the demodulator is active:
|
||||
|
||||
<Steps>
|
||||
1. **Set streaming flag** -- config_byte bit 7 = 1 (bmArmed).
|
||||
|
||||
2. **Load GPIF transaction count** -- GPIFTCB3:2 = 0x8000 (2 GB, effectively infinite).
|
||||
|
||||
3. **Reset address and byte count** -- Clear GPIF address registers and EP2 FIFO byte count.
|
||||
|
||||
4. **Assert TS_EN** -- P3.5 LOW (BCM4500 transport stream output enabled).
|
||||
|
||||
5. **Wait for initial GPIF transaction** -- Poll GPIFTRIG bit 7 (DONE) until set.
|
||||
|
||||
6. **De-assert TS_EN** -- P3.5 HIGH.
|
||||
|
||||
7. **Trigger continuous GPIF read** -- GPIFTRIG = 0x04 (read direction, EP2 select).
|
||||
|
||||
8. **Set streaming indicator** -- P0.7 LOW.
|
||||
</Steps>
|
||||
|
||||
```asm title="Start Streaming (Rev.2 at CODE:0D84)"
|
||||
ORL 0x4e, #0x80 ; config_byte |= 0x80 (streaming flag)
|
||||
MOV DPTR, #0xE630 ; GPIFTCB3
|
||||
MOV A, #0x80
|
||||
MOVX @DPTR, A ; GPIFTCB3 = 0x80 (huge transaction count)
|
||||
; ... address/FIFO reset ...
|
||||
ANL 0xb0, #0xDF ; P3 &= 0xDF -> P3.5 = 0 (TS_EN assert)
|
||||
; Poll GPIFTRIG.DONE ...
|
||||
ORL 0xb0, #0x20 ; P3 |= 0x20 -> P3.5 = 1
|
||||
MOV 0xbb, #0x04 ; GPIFTRIG = 0x04 (read EP2)
|
||||
ANL 0x80, #0x7F ; P0 &= 0x7F -> P0.7 = 0 (streaming)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Stop Streaming">
|
||||
|
||||
When `wValue=0` and currently streaming (config_byte bit 7 set):
|
||||
|
||||
<Steps>
|
||||
1. **Set stopped indicator** -- P0.7 HIGH.
|
||||
|
||||
2. **Force-flush buffer** -- EP2FIFOBCH = 0xFF (skip current FIFO packet).
|
||||
|
||||
3. **Wait for GPIF idle** -- Poll GPIFTRIG bit 7 (DONE) until set.
|
||||
|
||||
4. **Discard partial packet** -- OUTPKTEND = 0x82 (skip bit set, EP2 select).
|
||||
|
||||
5. **Clear streaming flag** -- config_byte bit 7 = 0.
|
||||
|
||||
6. **De-assert control lines** -- P3 bits 7:5 = 1 (all BCM4500 controls idle).
|
||||
</Steps>
|
||||
|
||||
```asm title="Stop Streaming (Rev.2 at CODE:0DE1)"
|
||||
ORL 0x80, #0x80 ; P0 |= 0x80 -> P0.7 = 1 (stopped)
|
||||
MOV DPTR, #0xE6F5 ; EP2FIFOBCH
|
||||
MOV A, #0xFF
|
||||
MOVX @DPTR, A ; Force flush
|
||||
; Poll GPIFTRIG.DONE ...
|
||||
MOV DPTR, #0xE648 ; OUTPKTEND
|
||||
MOV A, #0x82 ; Skip=1, EP2
|
||||
MOVX @DPTR, A ; Discard partial packet
|
||||
ANL 0x4e, #0x7F ; config_byte &= 0x7F (clear streaming)
|
||||
ORL 0xb0, #0xE0 ; P3 |= 0xE0 (de-assert all controls)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Interrupt Handling
|
||||
|
||||
INT4 and INT6 (GPIF/FIFO events) share a common handler that sets a software flag and clears EXIF.4:
|
||||
|
||||
```asm title="GPIF Interrupt Handler (Rev.2 at CODE:2084)"
|
||||
PUSH A
|
||||
PUSH DPH
|
||||
PUSH DPL
|
||||
SETB 0x01 ; Set GPIF event flag (_0_1)
|
||||
ANL 0x91, #0xEF ; Clear EXIF.4 (INT4/INT6 IRQ flag)
|
||||
MOV DPTR, #0xE65D ; GPIFIRQ
|
||||
MOV A, #0x01
|
||||
MOVX @DPTR, A ; Clear GPIFIRQ bit 0
|
||||
POP DPL
|
||||
POP DPH
|
||||
POP A
|
||||
RETI
|
||||
```
|
||||
|
||||
The main loop polls this flag, enters CPU idle mode (PCON.0) between events, and checks EP2CS for buffer availability before re-arming the GPIF. The FLOWSTATE engine (FSEN=1) automatically re-triggers GPIF transactions when EP2 buffers become available.
|
||||
|
||||
## GPIF Interrupt Enable (GPIFIE)
|
||||
|
||||
| Bit | Name | Enabled | Purpose |
|
||||
|-----|------|---------|---------|
|
||||
| 0 | GPIFWF | Yes | Waveform completion interrupt |
|
||||
| 1 | (reserved) | No | -- |
|
||||
| 2 | GPIFTCEXP | Yes | Transaction count expired |
|
||||
| 3 | GPIFGPIFDONE | Yes | GPIF operation done |
|
||||
| 4 | GPIFFF | Yes | FIFO flag interrupt |
|
||||
| 5 | GPIFWF2 | Yes | Waveform 2 completion |
|
||||
|
||||
## Throughput Analysis
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| USB 2.0 HS bulk (theoretical) | 480 Mbps |
|
||||
| USB 2.0 HS bulk (practical) | ~280 Mbps (~35 MB/s) |
|
||||
| GPIF engine (theoretical) | 48 MHz x 8 bits = 384 Mbps |
|
||||
| Typical DVB-S TS rate | 1--5 MB/s |
|
||||
| Maximum DVB-S2 rate (hypothetical) | ~7.25 MB/s (58 Mbps) |
|
||||
|
||||
The USB/GPIF path has approximately 5x headroom even at the maximum theoretical data rate. The bottleneck for all supported modes is the satellite link, not the USB data path.
|
||||
|
||||
## Timing
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| GPIF clock | 48 MHz internal |
|
||||
| CPU clock | 48 MHz |
|
||||
| GPIF mode | Asynchronous (RDY pin handshaking) |
|
||||
| NOP delays | 3 NOPs between XRAM writes (~62.5 ns at 48 MHz) |
|
||||
| EP2 buffer commit | Automatic via AUTOIN on FIFO fullness |
|
||||
| GPIF re-trigger | Automatic via FLOWSTATE when EP2 buffer space available |
|
||||
|
||||
## Cross-Version Comparison
|
||||
|
||||
The GPIF streaming path is functionally identical across all three firmware versions. Only code addresses differ due to recompilation:
|
||||
|
||||
| Aspect | v2.06 | v2.13 FW1 | Rev.2 v2.10 |
|
||||
|--------|-------|-----------|-------------|
|
||||
| ARM_TRANSFER handler | CODE:0110 | CODE:0110 | CODE:00FA |
|
||||
| GPIF control function | CODE:1919 | CODE:1800 | CODE:0D7C |
|
||||
| Config byte IRAM | 0x6D | 0x4F | 0x4E |
|
||||
| EP2FIFOCFG value | 0x0C | 0x0C | 0x0C |
|
||||
| IFCONFIG value | 0xEE | 0xEE | 0xEE |
|
||||
| FLOWSTATEA setting | OR 0x09 | OR 0x09 | OR 0x09 |
|
||||
---
|
||||
title: GPIF Streaming
|
||||
description: GPIF engine configuration, waveform descriptors, and transport stream data path from BCM4500 to USB host.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Badge, Aside, Steps } from '@astrojs/starlight/components';
|
||||
|
||||
The GPIF (General Programmable Interface) engine in the Cypress FX2 provides a hardware-managed data path from the BCM4500 demodulator to the USB host. After initial setup, no firmware intervention occurs in the data path -- the GPIF engine reads transport stream data directly into the EP2 FIFO, and the AUTOIN mechanism automatically commits full packets to the USB controller.
|
||||
|
||||
## Data Flow Architecture
|
||||
|
||||
```
|
||||
Cypress FX2 (CY7C68013A)
|
||||
+-----------------------------+
|
||||
| |
|
||||
BCM4500 P3.5 TS_EN | GPIF Engine EP2 FIFO | USB 2.0 HS
|
||||
Demodulator <-----------------+ (Master Read) (AUTOIN) +------------> Host
|
||||
(I2C:0x08) GPIF Data Bus | 0xE4xx wfm 4x buf | EP2 (0x82)
|
||||
-------------------> CTL/RDY pins 8-bit | Bulk IN
|
||||
8-bit parallel TS | | 7 URBs x 8KB
|
||||
+-----------------------------+
|
||||
```
|
||||
|
||||
<Aside type="note" title="Zero-Copy Path">
|
||||
The data path is fully hardware-managed. The GPIF engine reads data directly into the EP2 FIFO buffer. The EP2FIFOCFG AUTOIN bit causes the hardware to automatically commit full packets to the USB controller for transfer to the host. The FLOWSTATE engine re-triggers GPIF transactions when buffer space becomes available. The firmware only needs to start/stop the GPIF engine.
|
||||
</Aside>
|
||||
|
||||
## Key Register Configuration
|
||||
|
||||
All values are identical across the three stock firmware versions (v2.06, Rev.2 v2.10, v2.13):
|
||||
|
||||
| Register | Address | Value | Function |
|
||||
|----------|---------|-------|----------|
|
||||
| IFCONFIG | 0xE601 | 0xEE | Internal 48 MHz clock, GPIF master, async, debug output |
|
||||
| EP2FIFOCFG | 0xE618 | 0x0C | AUTOIN=1, ZEROLENIN=1, 8-bit data path |
|
||||
| REVCTL | 0xE60B | 0x03 | NOAUTOARM + SKIPCOMMIT |
|
||||
| CPUCS | 0xE600 | bits [4:3]=10 | 48 MHz CPU clock |
|
||||
| FLOWSTATEA | 0xE668 | OR 0x09 | FSEN (flow state enable) + FS[3] |
|
||||
| GPIFIE | 0xE65C | OR 0x3D | Waveform, TC, DONE, FIFO flag, WF2 interrupts |
|
||||
|
||||
### IFCONFIG Decode (0xEE = 1110_1110)
|
||||
|
||||
| Bit | Name | Value | Meaning |
|
||||
|-----|------|-------|---------|
|
||||
| 7 | IFCLKSRC | 1 | Internal clock source |
|
||||
| 6 | 3048MHZ | 1 | 48 MHz IFCLK frequency |
|
||||
| 5 | IFCLKOE | 1 | IFCLK pin drives output (clock to BCM4500) |
|
||||
| 4 | IFCLKPOL | 0 | Non-inverted clock polarity |
|
||||
| 3 | ASYNC | 1 | Asynchronous GPIF (RDY pin handshaking) |
|
||||
| 2 | GSTATE | 1 | Debug state output on PORTE |
|
||||
| 1:0 | IFCFG | 10 | GPIF internal master mode |
|
||||
|
||||
The FX2 operates as a GPIF master, reading data from the BCM4500's parallel transport stream output. Asynchronous mode means the GPIF uses RDY pin handshaking rather than clock-edge sampling.
|
||||
|
||||
### EP2FIFOCFG Decode (0x0C = 0000_1100)
|
||||
|
||||
| Bit | Name | Value | Meaning |
|
||||
|-----|------|-------|---------|
|
||||
| 4 | INFM1 | 0 | Packet count not decremented |
|
||||
| 3 | AUTOIN | 1 | Auto-commit IN packets when FIFO buffer full |
|
||||
| 2 | ZEROLENIN | 1 | Allow zero-length IN packets |
|
||||
| 1 | (reserved) | 0 | -- |
|
||||
| 0 | WORDWIDE | 0 | 8-bit data path (not 16-bit) |
|
||||
|
||||
The AUTOIN bit is critical: when the GPIF fills an EP2 FIFO buffer to the configured packet size, the FX2 hardware automatically arms the buffer for USB transfer.
|
||||
|
||||
## GPIF Waveform Configuration
|
||||
|
||||
The GPIF waveform descriptors occupy 128 bytes at 0xE400-0xE47F and are loaded from a compressed init table during firmware startup. The waveform programs a straightforward read cycle:
|
||||
|
||||
```
|
||||
States 0-5: CTL outputs = 0x01 (control line asserted), 1 IFCLK each
|
||||
State 6: CTL = 0x07, length = 0 (idle/terminate)
|
||||
Opcode: 0x00 (SDP = sample data point)
|
||||
Output: 0xF0 (FIFO write flags)
|
||||
```
|
||||
|
||||
This encodes a simple "assert read strobe, capture data, de-assert" cycle that reads one byte per GPIF transaction from the BCM4500's parallel port into the EP2 FIFO.
|
||||
|
||||
## FIFO Reset Sequence
|
||||
|
||||
All endpoint FIFOs are reset during initialization using the Cypress-prescribed procedure:
|
||||
|
||||
```c title="FIFO Reset (FUN_CODE_10d9)"
|
||||
FIFORESET = 0x80; // NAKALL: NAK all host transfers during reset
|
||||
// 3-NOP SYNCDELAY
|
||||
FIFORESET = 0x02; // Reset EP2 FIFO
|
||||
// 3-NOP SYNCDELAY
|
||||
FIFORESET = 0x04; // Reset EP4 FIFO
|
||||
// 3-NOP SYNCDELAY
|
||||
FIFORESET = 0x06; // Reset EP6 FIFO
|
||||
// 3-NOP SYNCDELAY
|
||||
FIFORESET = 0x08; // Reset EP8 FIFO
|
||||
// 3-NOP SYNCDELAY
|
||||
FIFORESET = 0x00; // Release NAKALL
|
||||
```
|
||||
|
||||
The triple-NOP delays (mandatory SYNCDELAY) between writes are required by the FX2 architecture: XRAM register writes take 2 cycles to propagate, and back-to-back writes to the same register need at least 3 instruction cycles.
|
||||
|
||||
## ARM_TRANSFER Command (0x85)
|
||||
|
||||
The host issues vendor command 0x85 to start or stop the MPEG-2 transport stream. The handler checks the [configuration status byte](/usb/config-status/) bit 0 (demodulator active) before proceeding.
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Start Streaming">
|
||||
|
||||
When `wValue=1` and the demodulator is active:
|
||||
|
||||
<Steps>
|
||||
1. **Set streaming flag** -- config_byte bit 7 = 1 (bmArmed).
|
||||
|
||||
2. **Load GPIF transaction count** -- GPIFTCB3:2 = 0x8000 (2 GB, effectively infinite).
|
||||
|
||||
3. **Reset address and byte count** -- Clear GPIF address registers and EP2 FIFO byte count.
|
||||
|
||||
4. **Assert TS_EN** -- P3.5 LOW (BCM4500 transport stream output enabled).
|
||||
|
||||
5. **Wait for initial GPIF transaction** -- Poll GPIFTRIG bit 7 (DONE) until set.
|
||||
|
||||
6. **De-assert TS_EN** -- P3.5 HIGH.
|
||||
|
||||
7. **Trigger continuous GPIF read** -- GPIFTRIG = 0x04 (read direction, EP2 select).
|
||||
|
||||
8. **Set streaming indicator** -- P0.7 LOW.
|
||||
</Steps>
|
||||
|
||||
```asm title="Start Streaming (Rev.2 at CODE:0D84)"
|
||||
ORL 0x4e, #0x80 ; config_byte |= 0x80 (streaming flag)
|
||||
MOV DPTR, #0xE630 ; GPIFTCB3
|
||||
MOV A, #0x80
|
||||
MOVX @DPTR, A ; GPIFTCB3 = 0x80 (huge transaction count)
|
||||
; ... address/FIFO reset ...
|
||||
ANL 0xb0, #0xDF ; P3 &= 0xDF -> P3.5 = 0 (TS_EN assert)
|
||||
; Poll GPIFTRIG.DONE ...
|
||||
ORL 0xb0, #0x20 ; P3 |= 0x20 -> P3.5 = 1
|
||||
MOV 0xbb, #0x04 ; GPIFTRIG = 0x04 (read EP2)
|
||||
ANL 0x80, #0x7F ; P0 &= 0x7F -> P0.7 = 0 (streaming)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Stop Streaming">
|
||||
|
||||
When `wValue=0` and currently streaming (config_byte bit 7 set):
|
||||
|
||||
<Steps>
|
||||
1. **Set stopped indicator** -- P0.7 HIGH.
|
||||
|
||||
2. **Force-flush buffer** -- EP2FIFOBCH = 0xFF (skip current FIFO packet).
|
||||
|
||||
3. **Wait for GPIF idle** -- Poll GPIFTRIG bit 7 (DONE) until set.
|
||||
|
||||
4. **Discard partial packet** -- OUTPKTEND = 0x82 (skip bit set, EP2 select).
|
||||
|
||||
5. **Clear streaming flag** -- config_byte bit 7 = 0.
|
||||
|
||||
6. **De-assert control lines** -- P3 bits 7:5 = 1 (all BCM4500 controls idle).
|
||||
</Steps>
|
||||
|
||||
```asm title="Stop Streaming (Rev.2 at CODE:0DE1)"
|
||||
ORL 0x80, #0x80 ; P0 |= 0x80 -> P0.7 = 1 (stopped)
|
||||
MOV DPTR, #0xE6F5 ; EP2FIFOBCH
|
||||
MOV A, #0xFF
|
||||
MOVX @DPTR, A ; Force flush
|
||||
; Poll GPIFTRIG.DONE ...
|
||||
MOV DPTR, #0xE648 ; OUTPKTEND
|
||||
MOV A, #0x82 ; Skip=1, EP2
|
||||
MOVX @DPTR, A ; Discard partial packet
|
||||
ANL 0x4e, #0x7F ; config_byte &= 0x7F (clear streaming)
|
||||
ORL 0xb0, #0xE0 ; P3 |= 0xE0 (de-assert all controls)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Interrupt Handling
|
||||
|
||||
INT4 and INT6 (GPIF/FIFO events) share a common handler that sets a software flag and clears EXIF.4:
|
||||
|
||||
```asm title="GPIF Interrupt Handler (Rev.2 at CODE:2084)"
|
||||
PUSH A
|
||||
PUSH DPH
|
||||
PUSH DPL
|
||||
SETB 0x01 ; Set GPIF event flag (_0_1)
|
||||
ANL 0x91, #0xEF ; Clear EXIF.4 (INT4/INT6 IRQ flag)
|
||||
MOV DPTR, #0xE65D ; GPIFIRQ
|
||||
MOV A, #0x01
|
||||
MOVX @DPTR, A ; Clear GPIFIRQ bit 0
|
||||
POP DPL
|
||||
POP DPH
|
||||
POP A
|
||||
RETI
|
||||
```
|
||||
|
||||
The main loop polls this flag, enters CPU idle mode (PCON.0) between events, and checks EP2CS for buffer availability before re-arming the GPIF. The FLOWSTATE engine (FSEN=1) automatically re-triggers GPIF transactions when EP2 buffers become available.
|
||||
|
||||
## GPIF Interrupt Enable (GPIFIE)
|
||||
|
||||
| Bit | Name | Enabled | Purpose |
|
||||
|-----|------|---------|---------|
|
||||
| 0 | GPIFWF | Yes | Waveform completion interrupt |
|
||||
| 1 | (reserved) | No | -- |
|
||||
| 2 | GPIFTCEXP | Yes | Transaction count expired |
|
||||
| 3 | GPIFGPIFDONE | Yes | GPIF operation done |
|
||||
| 4 | GPIFFF | Yes | FIFO flag interrupt |
|
||||
| 5 | GPIFWF2 | Yes | Waveform 2 completion |
|
||||
|
||||
## Throughput Analysis
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| USB 2.0 HS bulk (theoretical) | 480 Mbps |
|
||||
| USB 2.0 HS bulk (practical) | ~280 Mbps (~35 MB/s) |
|
||||
| GPIF engine (theoretical) | 48 MHz x 8 bits = 384 Mbps |
|
||||
| Typical DVB-S TS rate | 1--5 MB/s |
|
||||
| Maximum DVB-S2 rate (hypothetical) | ~7.25 MB/s (58 Mbps) |
|
||||
|
||||
The USB/GPIF path has approximately 5x headroom even at the maximum theoretical data rate. The bottleneck for all supported modes is the satellite link, not the USB data path.
|
||||
|
||||
## Timing
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| GPIF clock | 48 MHz internal |
|
||||
| CPU clock | 48 MHz |
|
||||
| GPIF mode | Asynchronous (RDY pin handshaking) |
|
||||
| NOP delays | 3 NOPs between XRAM writes (~62.5 ns at 48 MHz) |
|
||||
| EP2 buffer commit | Automatic via AUTOIN on FIFO fullness |
|
||||
| GPIF re-trigger | Automatic via FLOWSTATE when EP2 buffer space available |
|
||||
|
||||
## Cross-Version Comparison
|
||||
|
||||
The GPIF streaming path is functionally identical across all three firmware versions. Only code addresses differ due to recompilation:
|
||||
|
||||
| Aspect | v2.06 | v2.13 FW1 | Rev.2 v2.10 |
|
||||
|--------|-------|-----------|-------------|
|
||||
| ARM_TRANSFER handler | CODE:0110 | CODE:0110 | CODE:00FA |
|
||||
| GPIF control function | CODE:1919 | CODE:1800 | CODE:0D7C |
|
||||
| Config byte IRAM | 0x6D | 0x4F | 0x4E |
|
||||
| EP2FIFOCFG value | 0x0C | 0x0C | 0x0C |
|
||||
| IFCONFIG value | 0xEE | 0xEE | 0xEE |
|
||||
| FLOWSTATEA setting | OR 0x09 | OR 0x09 | OR 0x09 |
|
||||
|
|
|
|||
|
|
@ -1,241 +1,241 @@
|
|||
---
|
||||
title: BCM4500 Register Map
|
||||
description: Definitive register lookup for BCM4500 direct registers, indirect registers, FX2 XRAM/IRAM locations, and I2C controller addresses.
|
||||
---
|
||||
|
||||
import { Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
<Aside type="caution" title="Reverse-Engineered Data">
|
||||
No public BCM4500 datasheet exists. All register addresses and functions on this page were determined through firmware disassembly, I2C bus captures, and behavioral testing. Register names are conventions used throughout this documentation, not official Broadcom nomenclature.
|
||||
</Aside>
|
||||
|
||||
This page consolidates every known register address used by the SkyWalker-1 hardware into a single lookup reference.
|
||||
|
||||
## BCM4500 Direct Registers
|
||||
|
||||
These registers are accessed via standard I2C read/write to the BCM4500 at 7-bit address **0x08** (write byte 0x10, read byte 0x11).
|
||||
|
||||
| Address | Name | R/W | Function |
|
||||
|---------|------|-----|----------|
|
||||
| 0xA2 | Status | R | Readiness status. Returns 0x02 when powered on, no signal locked. Polled during boot probe and [signal strength readback](/bcm4500/signal-monitoring/). |
|
||||
| 0xA4 | Lock | R | Signal lock indicator. Bit 5 (mask 0x20) = signal locked. Read by `GET_SIGNAL_LOCK` (0x90). The kernel treats any non-zero value as locked. |
|
||||
| 0xA6 | Indirect Page | W | Page/address select for the [indirect register protocol](/bcm4500/demodulator/#indirect-register-protocol). Typically written with 0x00 (page 0). |
|
||||
| 0xA7 | Indirect Data | R/W | Data register for indirect reads and writes. Supports auto-increment for multi-byte writes within a single I2C transaction. |
|
||||
| 0xA8 | Indirect Command | R/W | Command register. Write 0x01 = indirect read, 0x03 = indirect write. Read to poll: bit 0 clear = command complete. |
|
||||
| 0xF9 | Demod Status | R | Extended demodulator status. Read by `GET_DEMOD_STATUS` (0x99) in v2.13 firmware. Also polled by the v2.13 INT0 handler. Not accessed by v2.06 or Rev.2. |
|
||||
|
||||
### Indirect Register Triad
|
||||
|
||||
Registers 0xA6, 0xA7, and 0xA8 form a triad that provides access to the BCM4500's internal register space. The sequence is always: select page (0xA6) → write/read data (0xA7) → execute command (0xA8) → poll 0xA8 for completion.
|
||||
|
||||
See [Demodulator — Indirect Register Protocol](/bcm4500/demodulator/#indirect-register-protocol) for the full byte-level I2C sequences.
|
||||
|
||||
## Indirect Registers (Page 0)
|
||||
|
||||
These are the BCM4500 internal registers accessed through the 0xA6/0xA7/0xA8 indirect protocol. All known registers are on page 0x00.
|
||||
|
||||
### Signal Quality
|
||||
|
||||
| Register | Size | Function | Read By |
|
||||
|----------|------|----------|---------|
|
||||
| 0x00--0x01 | 2 bytes | SNR value (16-bit, little-endian). Bytes 0--1 of `GET_SIGNAL_STRENGTH` (0x87) response. | [Signal Monitoring](/bcm4500/signal-monitoring/) |
|
||||
| 0x02--0x05 | 4 bytes | AGC and diagnostic data. Bytes 2--5 of `GET_SIGNAL_STRENGTH` (0x87) response. | [Signal Monitoring](/bcm4500/signal-monitoring/) |
|
||||
|
||||
### Initialization Blocks
|
||||
|
||||
Three blocks written during BCM4500 firmware load. The first byte of each block is the starting register address.
|
||||
|
||||
| Block | Start Reg | Length | Data (hex) | Purpose |
|
||||
|-------|-----------|--------|------------|---------|
|
||||
| 0 | 0x06 | 7 bytes | `06 0b 17 38 9f d9 80` | Primary demod configuration |
|
||||
| 1 | 0x07 | 8 bytes | `07 09 39 4f 00 65 b7 10` | Secondary demod configuration |
|
||||
| 2 | 0x0F | 3 bytes | `0f 0c 09` | Final demod configuration |
|
||||
|
||||
<Aside type="note" title="Init Block Overlap">
|
||||
Blocks 0 and 1 both write to register 0x07 and above, meaning block 1 overwrites the tail end of block 0. This is intentional: block 0 sets initial defaults, block 1 refines registers 0x07--0x0E, and block 2 configures the 0x0F--0x11 range independently. All stock firmware versions use the same init block data.
|
||||
</Aside>
|
||||
|
||||
### Tuning Configuration
|
||||
|
||||
After modulation dispatch, the [tuning protocol](/bcm4500/tuning-protocol/) writes frequency, symbol rate, FEC, and modulation parameters into BCM4500 page 0 registers via the indirect write protocol. The exact target register range depends on the configuration data length (typically 12--18 bytes starting at register 0x00).
|
||||
|
||||
## FX2 XRAM Locations
|
||||
|
||||
External RAM (XRAM) addresses used by the FX2 microcontroller for tuning state and configuration. These are **not** BCM4500 registers — they are FX2 memory locations that hold data destined for or read from the demodulator.
|
||||
|
||||
### Modulation Configuration
|
||||
|
||||
| Address | Name | Function |
|
||||
|---------|------|----------|
|
||||
| 0xE0EB | FEC Code Rate | Looked up from the FEC table for the active modulation. DCII modes use fixed value 0xFC. DSS/BPSK modes OR the lookup with 0x80. |
|
||||
| 0xE0EC | Modulation Type | 0x09 for DVB-S, Turbo, DSS, and BPSK modes. DCII modes load from the DCII lookup table. |
|
||||
| 0xE0F5 | Demod Mode | 0x10 for most modes. DCII variants use 0x10 (combo), 0x11 (offset QPSK), 0x12 (I-stream), or 0x16 (Q-stream). |
|
||||
| 0xE0F6 | Turbo Flag | 0x00 = standard FEC. 0x01 = turbo FEC (QPSK/8PSK/16QAM turbo modes). |
|
||||
|
||||
### Tuning Parameters
|
||||
|
||||
| Address | Size | Content | Source |
|
||||
|---------|------|---------|--------|
|
||||
| 0xE0CB--0xE0CE | 4 bytes | Symbol rate (big-endian) | Byte-reversed from EP0BUF[0--3] during `TUNE_8PSK` |
|
||||
| 0xE0DB--0xE0DE | 4 bytes | IF frequency (big-endian) | Byte-reversed from EP0BUF[4--7] during `TUNE_8PSK` |
|
||||
|
||||
### FEC Rate Lookup Tables
|
||||
|
||||
Populated at boot from CODE-space initialization tables. Indexed by the FEC rate byte from the `TUNE_8PSK` command payload.
|
||||
|
||||
| Base Address | Modulation | Max Index | Rates |
|
||||
|-------------|-----------|-----------|-------|
|
||||
| 0xE0B1 | Turbo 8PSK | 5 | Turbo-specific code rates |
|
||||
| 0xE0B7 | Turbo QPSK | 5 | Turbo-specific code rates |
|
||||
| 0xE0BC | Turbo 16QAM | 1 | Single code rate |
|
||||
| 0xE0BD | DCII (all variants) | 9 | Combined code + modulation values |
|
||||
| 0xE0F9 | DVB-S QPSK / DSS / BPSK | 7 | 1/2, 2/3, 3/4, 5/6, 7/8, auto, none |
|
||||
|
||||
### FW2/FW3 External Configuration
|
||||
|
||||
| Address | Size | Content |
|
||||
|---------|------|---------|
|
||||
| 0xE080--0xE08E | 15 bytes | External calibration data loaded by [FW2 and FW3](/firmware/fw213-variants/) into demod registers at 0xE6C0--0xE6CD. Not used by FW1 (hardcoded config). |
|
||||
|
||||
### USB Endpoint Buffer
|
||||
|
||||
| Address | Size | Content |
|
||||
|---------|------|---------|
|
||||
| 0xE740--0xE749 | 10 bytes | EP0BUF — USB control transfer buffer. Contains the raw `TUNE_8PSK` payload before parsing. |
|
||||
|
||||
## FX2 IRAM by Firmware Version
|
||||
|
||||
Internal RAM (IRAM) addresses vary between firmware versions due to different stack pointer placement. This table maps the key locations for each version.
|
||||
|
||||
### Stack and Status
|
||||
|
||||
| Location | v2.06 | Rev.2 v2.10 | v2.13 FW1/FW2 | v2.13 FW3 |
|
||||
|----------|-------|-------------|---------------|-----------|
|
||||
| Stack pointer (SP) | 0x72 | 0x4F | 0x50 | 0x52 |
|
||||
| Config status byte | 0x6D | 0x4E | 0x4F | 0x51 |
|
||||
| I2C buffer high | -- | 0x48 | 0x48 | 0x4A |
|
||||
| I2C buffer low | -- | 0x49 | 0x49 | 0x4B |
|
||||
|
||||
The config status byte is returned by `GET_8PSK_CONFIG` (0x80). See [Config Status](/usb/config-status/) for bit definitions.
|
||||
|
||||
### Tuning Parameter Storage
|
||||
|
||||
| Location | Address | Function |
|
||||
|----------|---------|----------|
|
||||
| Modulation type | 0x4D | Copied from EP0BUF[8] during `TUNE_8PSK` parsing |
|
||||
| FEC rate index | 0x4F | Copied from EP0BUF[9] during `TUNE_8PSK` parsing |
|
||||
|
||||
<Aside type="note">
|
||||
On v2.13 FW1/FW2, the FEC rate index (IRAM 0x4F) shares the same address as the config status byte. The firmware distinguishes between contexts — the FEC index is only written during tune operations, while config status is read/written at other times. FW3 avoids this collision by moving the config status to 0x51.
|
||||
</Aside>
|
||||
|
||||
### Custom Firmware (v3.01+)
|
||||
|
||||
The custom firmware built with SDCC + fx2lib uses C variables instead of fixed IRAM addresses. The compiler manages allocation, so addresses are not guaranteed stable across builds. Key variables:
|
||||
|
||||
| Variable | Type | Purpose |
|
||||
|----------|------|---------|
|
||||
| `config_status` | `volatile BYTE` | Configuration status byte |
|
||||
| `boot_stage` | `volatile BYTE` | Boot progress (0x00 = not started, 0xFF = complete) |
|
||||
| `i2c_buf[16]` | `__xdata BYTE` | I2C scratch buffer for writes |
|
||||
| `i2c_rd[8]` | `__xdata BYTE` | I2C scratch buffer for reads |
|
||||
| `tm_result[10]` | `__xdata BYTE` | Tune monitor result buffer |
|
||||
|
||||
## FX2 USB Controller Registers
|
||||
|
||||
Core FX2LP registers used for USB control transfers, CPU management, and peripheral configuration.
|
||||
|
||||
### CPU and Peripheral Config
|
||||
|
||||
| Register | Address | Function |
|
||||
|----------|---------|----------|
|
||||
| CPUCS | 0xE600 | CPU control/status. Write 0x01 to halt, 0x00 to run. Bits 4:3 select clock speed (10 = 48 MHz). |
|
||||
| IFCONFIG | 0xE601 | Interface configuration. Value 0xEE = internal 48 MHz clock, GPIF master, async mode. |
|
||||
| REVCTL | 0xE60B | Revision control. Value 0x03 = NOAUTOARM + SKIPCOMMIT (required for manual EP management). |
|
||||
| EP2FIFOCFG | 0xE618 | EP2 FIFO configuration. Value 0x0C = AUTOIN + ZEROLENIN, 8-bit data bus. |
|
||||
|
||||
### USB Setup Data (SETUPDAT)
|
||||
|
||||
Populated by the FX2 hardware when a SETUP packet arrives on EP0. The vendor command dispatcher reads `SETUPDAT[1]` to determine the command.
|
||||
|
||||
| Register | Address | Content |
|
||||
|----------|---------|---------|
|
||||
| SETUPDAT[0] | 0xE6B8 | bmRequestType (0x40 = vendor OUT, 0xC0 = vendor IN) |
|
||||
| SETUPDAT[1] | 0xE6B9 | bRequest (vendor command ID: 0x80--0xB9) |
|
||||
| SETUPDAT[2] | 0xE6BA | wValueL |
|
||||
| SETUPDAT[3] | 0xE6BB | wValueH |
|
||||
| SETUPDAT[4] | 0xE6BC | wIndexL |
|
||||
| SETUPDAT[5] | 0xE6BD | wIndexH |
|
||||
| SETUPDAT[6] | 0xE6BE | wLengthL |
|
||||
| SETUPDAT[7] | 0xE6BF | wLengthH |
|
||||
|
||||
### EP0 Buffer
|
||||
|
||||
| Register | Address | Function |
|
||||
|----------|---------|----------|
|
||||
| EP0BCH | 0xE68A | EP0 byte count high. |
|
||||
| EP0BCL | 0xE68B | EP0 byte count low. Writing this register arms the EP0 IN transfer. |
|
||||
| EP0BUF | 0xE740 | EP0 data buffer start (64 bytes). Contains [TUNE_8PSK](/bcm4500/tuning-protocol/) payload bytes 0xE740--0xE749. |
|
||||
|
||||
### LNB Control (XRAM)
|
||||
|
||||
| Address | Function |
|
||||
|---------|----------|
|
||||
| 0xE0B6 | LNB voltage control register. Written by `SET_LNB_VOLTAGE` (0x8B) in the custom firmware. |
|
||||
|
||||
## FX2 I2C Controller
|
||||
|
||||
The Cypress FX2LP's built-in I2C master controller uses three hardware registers in the SFR-mapped XRAM space.
|
||||
|
||||
| Register | Address | Function |
|
||||
|----------|---------|----------|
|
||||
| I2CS | 0xE678 | Control/Status. Bit fields: DONE (bit 0), ACK (bit 1), BERR (bit 2), ID (bits 4:3), LASTRD (bit 5), STOP (bit 6), START (bit 7). |
|
||||
| I2DAT | 0xE679 | Data register. Write to transmit a byte, read to receive. First write after START sends the slave address byte. |
|
||||
| I2CTL | 0xE67A | Control register. Bit 0 = 400 kHz mode (set by all firmware versions at init). Bit 1 = STOPIE (stop interrupt enable). |
|
||||
|
||||
### I2C Bus Addresses
|
||||
|
||||
| 7-bit Address | 8-bit Write | 8-bit Read | Device |
|
||||
|--------------|-------------|------------|--------|
|
||||
| 0x08 | 0x10 | 0x11 | BCM4500 demodulator (operating address) |
|
||||
| 0x10 | 0x20 | 0x21 | Tuner / LNB controller |
|
||||
| 0x51 | 0xA2 | 0xA3 | Configuration EEPROM (serial number, calibration, firmware storage) |
|
||||
| 0x3F | 0x7E | 0x7F | BCM4500 alternate probe address (v2.13 boot detection only) |
|
||||
| 0x7F | 0xFE | 0xFF | BCM4500 alternate probe address (v2.13 boot detection only) |
|
||||
|
||||
See [I2C Bus Architecture](/i2c/bus-architecture/) for bus topology and the [STOP Corruption Bug](/i2c/stop-corruption-bug/) for the spurious STOP issue affecting the FX2 controller.
|
||||
|
||||
## Cross-Reference Index
|
||||
|
||||
Every documentation page that references specific registers or memory addresses:
|
||||
|
||||
### BCM4500 Direct Registers
|
||||
|
||||
| Register | Pages |
|
||||
|----------|-------|
|
||||
| 0xA2 (Status) | [Demodulator](/bcm4500/demodulator/), [Signal Monitoring](/bcm4500/signal-monitoring/), [Version Comparison](/firmware/version-comparison/) |
|
||||
| 0xA4 (Lock) | [Demodulator](/bcm4500/demodulator/), [Signal Monitoring](/bcm4500/signal-monitoring/), [Tuning Protocol](/bcm4500/tuning-protocol/) |
|
||||
| 0xA6/0xA7/0xA8 (Indirect) | [Demodulator](/bcm4500/demodulator/), [Tuning Protocol](/bcm4500/tuning-protocol/), [Signal Monitoring](/bcm4500/signal-monitoring/) |
|
||||
| 0xF9 (Demod Status) | [Demodulator](/bcm4500/demodulator/), [Signal Monitoring](/bcm4500/signal-monitoring/), [Version Comparison](/firmware/version-comparison/) |
|
||||
|
||||
### FX2 XRAM
|
||||
|
||||
| Address Range | Pages |
|
||||
|--------------|-------|
|
||||
| 0xE0B1--0xE0F9 (FEC tables) | [Tuning Protocol](/bcm4500/tuning-protocol/) |
|
||||
| 0xE0CB--0xE0DE (Tune params) | [Tuning Protocol](/bcm4500/tuning-protocol/) |
|
||||
| 0xE0EB--0xE0F6 (Mod config) | [Tuning Protocol](/bcm4500/tuning-protocol/) |
|
||||
| 0xE080--0xE08E (FW2/FW3 cal) | [FW2.13 Variants](/firmware/fw213-variants/) |
|
||||
|
||||
### FX2 IRAM
|
||||
|
||||
| Address | Pages |
|
||||
|---------|-------|
|
||||
| Config status (version-dependent) | [Config Status](/usb/config-status/), [Version Comparison](/firmware/version-comparison/), [FW2.13 Variants](/firmware/fw213-variants/) |
|
||||
| SP, I2C buffers | [FW2.13 Variants](/firmware/fw213-variants/), [Version Comparison](/firmware/version-comparison/) |
|
||||
|
||||
### I2C Controller
|
||||
|
||||
| Register | Pages |
|
||||
|----------|-------|
|
||||
| I2CS/I2DAT/I2CTL (0xE678--0xE67A) | [I2C Bus Architecture](/i2c/bus-architecture/), [STOP Corruption Bug](/i2c/stop-corruption-bug/), [Demodulator](/bcm4500/demodulator/) |
|
||||
---
|
||||
title: BCM4500 Register Map
|
||||
description: Definitive register lookup for BCM4500 direct registers, indirect registers, FX2 XRAM/IRAM locations, and I2C controller addresses.
|
||||
---
|
||||
|
||||
import { Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
<Aside type="caution" title="Reverse-Engineered Data">
|
||||
No public BCM4500 datasheet exists. All register addresses and functions on this page were determined through firmware disassembly, I2C bus captures, and behavioral testing. Register names are conventions used throughout this documentation, not official Broadcom nomenclature.
|
||||
</Aside>
|
||||
|
||||
This page consolidates every known register address used by the SkyWalker-1 hardware into a single lookup reference.
|
||||
|
||||
## BCM4500 Direct Registers
|
||||
|
||||
These registers are accessed via standard I2C read/write to the BCM4500 at 7-bit address **0x08** (write byte 0x10, read byte 0x11).
|
||||
|
||||
| Address | Name | R/W | Function |
|
||||
|---------|------|-----|----------|
|
||||
| 0xA2 | Status | R | Readiness status. Returns 0x02 when powered on, no signal locked. Polled during boot probe and [signal strength readback](/bcm4500/signal-monitoring/). |
|
||||
| 0xA4 | Lock | R | Signal lock indicator. Bit 5 (mask 0x20) = signal locked. Read by `GET_SIGNAL_LOCK` (0x90). The kernel treats any non-zero value as locked. |
|
||||
| 0xA6 | Indirect Page | W | Page/address select for the [indirect register protocol](/bcm4500/demodulator/#indirect-register-protocol). Typically written with 0x00 (page 0). |
|
||||
| 0xA7 | Indirect Data | R/W | Data register for indirect reads and writes. Supports auto-increment for multi-byte writes within a single I2C transaction. |
|
||||
| 0xA8 | Indirect Command | R/W | Command register. Write 0x01 = indirect read, 0x03 = indirect write. Read to poll: bit 0 clear = command complete. |
|
||||
| 0xF9 | Demod Status | R | Extended demodulator status. Read by `GET_DEMOD_STATUS` (0x99) in v2.13 firmware. Also polled by the v2.13 INT0 handler. Not accessed by v2.06 or Rev.2. |
|
||||
|
||||
### Indirect Register Triad
|
||||
|
||||
Registers 0xA6, 0xA7, and 0xA8 form a triad that provides access to the BCM4500's internal register space. The sequence is always: select page (0xA6) → write/read data (0xA7) → execute command (0xA8) → poll 0xA8 for completion.
|
||||
|
||||
See [Demodulator — Indirect Register Protocol](/bcm4500/demodulator/#indirect-register-protocol) for the full byte-level I2C sequences.
|
||||
|
||||
## Indirect Registers (Page 0)
|
||||
|
||||
These are the BCM4500 internal registers accessed through the 0xA6/0xA7/0xA8 indirect protocol. All known registers are on page 0x00.
|
||||
|
||||
### Signal Quality
|
||||
|
||||
| Register | Size | Function | Read By |
|
||||
|----------|------|----------|---------|
|
||||
| 0x00--0x01 | 2 bytes | SNR value (16-bit, little-endian). Bytes 0--1 of `GET_SIGNAL_STRENGTH` (0x87) response. | [Signal Monitoring](/bcm4500/signal-monitoring/) |
|
||||
| 0x02--0x05 | 4 bytes | AGC and diagnostic data. Bytes 2--5 of `GET_SIGNAL_STRENGTH` (0x87) response. | [Signal Monitoring](/bcm4500/signal-monitoring/) |
|
||||
|
||||
### Initialization Blocks
|
||||
|
||||
Three blocks written during BCM4500 firmware load. The first byte of each block is the starting register address.
|
||||
|
||||
| Block | Start Reg | Length | Data (hex) | Purpose |
|
||||
|-------|-----------|--------|------------|---------|
|
||||
| 0 | 0x06 | 7 bytes | `06 0b 17 38 9f d9 80` | Primary demod configuration |
|
||||
| 1 | 0x07 | 8 bytes | `07 09 39 4f 00 65 b7 10` | Secondary demod configuration |
|
||||
| 2 | 0x0F | 3 bytes | `0f 0c 09` | Final demod configuration |
|
||||
|
||||
<Aside type="note" title="Init Block Overlap">
|
||||
Blocks 0 and 1 both write to register 0x07 and above, meaning block 1 overwrites the tail end of block 0. This is intentional: block 0 sets initial defaults, block 1 refines registers 0x07--0x0E, and block 2 configures the 0x0F--0x11 range independently. All stock firmware versions use the same init block data.
|
||||
</Aside>
|
||||
|
||||
### Tuning Configuration
|
||||
|
||||
After modulation dispatch, the [tuning protocol](/bcm4500/tuning-protocol/) writes frequency, symbol rate, FEC, and modulation parameters into BCM4500 page 0 registers via the indirect write protocol. The exact target register range depends on the configuration data length (typically 12--18 bytes starting at register 0x00).
|
||||
|
||||
## FX2 XRAM Locations
|
||||
|
||||
External RAM (XRAM) addresses used by the FX2 microcontroller for tuning state and configuration. These are **not** BCM4500 registers — they are FX2 memory locations that hold data destined for or read from the demodulator.
|
||||
|
||||
### Modulation Configuration
|
||||
|
||||
| Address | Name | Function |
|
||||
|---------|------|----------|
|
||||
| 0xE0EB | FEC Code Rate | Looked up from the FEC table for the active modulation. DCII modes use fixed value 0xFC. DSS/BPSK modes OR the lookup with 0x80. |
|
||||
| 0xE0EC | Modulation Type | 0x09 for DVB-S, Turbo, DSS, and BPSK modes. DCII modes load from the DCII lookup table. |
|
||||
| 0xE0F5 | Demod Mode | 0x10 for most modes. DCII variants use 0x10 (combo), 0x11 (offset QPSK), 0x12 (I-stream), or 0x16 (Q-stream). |
|
||||
| 0xE0F6 | Turbo Flag | 0x00 = standard FEC. 0x01 = turbo FEC (QPSK/8PSK/16QAM turbo modes). |
|
||||
|
||||
### Tuning Parameters
|
||||
|
||||
| Address | Size | Content | Source |
|
||||
|---------|------|---------|--------|
|
||||
| 0xE0CB--0xE0CE | 4 bytes | Symbol rate (big-endian) | Byte-reversed from EP0BUF[0--3] during `TUNE_8PSK` |
|
||||
| 0xE0DB--0xE0DE | 4 bytes | IF frequency (big-endian) | Byte-reversed from EP0BUF[4--7] during `TUNE_8PSK` |
|
||||
|
||||
### FEC Rate Lookup Tables
|
||||
|
||||
Populated at boot from CODE-space initialization tables. Indexed by the FEC rate byte from the `TUNE_8PSK` command payload.
|
||||
|
||||
| Base Address | Modulation | Max Index | Rates |
|
||||
|-------------|-----------|-----------|-------|
|
||||
| 0xE0B1 | Turbo 8PSK | 5 | Turbo-specific code rates |
|
||||
| 0xE0B7 | Turbo QPSK | 5 | Turbo-specific code rates |
|
||||
| 0xE0BC | Turbo 16QAM | 1 | Single code rate |
|
||||
| 0xE0BD | DCII (all variants) | 9 | Combined code + modulation values |
|
||||
| 0xE0F9 | DVB-S QPSK / DSS / BPSK | 7 | 1/2, 2/3, 3/4, 5/6, 7/8, auto, none |
|
||||
|
||||
### FW2/FW3 External Configuration
|
||||
|
||||
| Address | Size | Content |
|
||||
|---------|------|---------|
|
||||
| 0xE080--0xE08E | 15 bytes | External calibration data loaded by [FW2 and FW3](/firmware/fw213-variants/) into demod registers at 0xE6C0--0xE6CD. Not used by FW1 (hardcoded config). |
|
||||
|
||||
### USB Endpoint Buffer
|
||||
|
||||
| Address | Size | Content |
|
||||
|---------|------|---------|
|
||||
| 0xE740--0xE749 | 10 bytes | EP0BUF — USB control transfer buffer. Contains the raw `TUNE_8PSK` payload before parsing. |
|
||||
|
||||
## FX2 IRAM by Firmware Version
|
||||
|
||||
Internal RAM (IRAM) addresses vary between firmware versions due to different stack pointer placement. This table maps the key locations for each version.
|
||||
|
||||
### Stack and Status
|
||||
|
||||
| Location | v2.06 | Rev.2 v2.10 | v2.13 FW1/FW2 | v2.13 FW3 |
|
||||
|----------|-------|-------------|---------------|-----------|
|
||||
| Stack pointer (SP) | 0x72 | 0x4F | 0x50 | 0x52 |
|
||||
| Config status byte | 0x6D | 0x4E | 0x4F | 0x51 |
|
||||
| I2C buffer high | -- | 0x48 | 0x48 | 0x4A |
|
||||
| I2C buffer low | -- | 0x49 | 0x49 | 0x4B |
|
||||
|
||||
The config status byte is returned by `GET_8PSK_CONFIG` (0x80). See [Config Status](/usb/config-status/) for bit definitions.
|
||||
|
||||
### Tuning Parameter Storage
|
||||
|
||||
| Location | Address | Function |
|
||||
|----------|---------|----------|
|
||||
| Modulation type | 0x4D | Copied from EP0BUF[8] during `TUNE_8PSK` parsing |
|
||||
| FEC rate index | 0x4F | Copied from EP0BUF[9] during `TUNE_8PSK` parsing |
|
||||
|
||||
<Aside type="note">
|
||||
On v2.13 FW1/FW2, the FEC rate index (IRAM 0x4F) shares the same address as the config status byte. The firmware distinguishes between contexts — the FEC index is only written during tune operations, while config status is read/written at other times. FW3 avoids this collision by moving the config status to 0x51.
|
||||
</Aside>
|
||||
|
||||
### Custom Firmware (v3.01+)
|
||||
|
||||
The custom firmware built with SDCC + fx2lib uses C variables instead of fixed IRAM addresses. The compiler manages allocation, so addresses are not guaranteed stable across builds. Key variables:
|
||||
|
||||
| Variable | Type | Purpose |
|
||||
|----------|------|---------|
|
||||
| `config_status` | `volatile BYTE` | Configuration status byte |
|
||||
| `boot_stage` | `volatile BYTE` | Boot progress (0x00 = not started, 0xFF = complete) |
|
||||
| `i2c_buf[16]` | `__xdata BYTE` | I2C scratch buffer for writes |
|
||||
| `i2c_rd[8]` | `__xdata BYTE` | I2C scratch buffer for reads |
|
||||
| `tm_result[10]` | `__xdata BYTE` | Tune monitor result buffer |
|
||||
|
||||
## FX2 USB Controller Registers
|
||||
|
||||
Core FX2LP registers used for USB control transfers, CPU management, and peripheral configuration.
|
||||
|
||||
### CPU and Peripheral Config
|
||||
|
||||
| Register | Address | Function |
|
||||
|----------|---------|----------|
|
||||
| CPUCS | 0xE600 | CPU control/status. Write 0x01 to halt, 0x00 to run. Bits 4:3 select clock speed (10 = 48 MHz). |
|
||||
| IFCONFIG | 0xE601 | Interface configuration. Value 0xEE = internal 48 MHz clock, GPIF master, async mode. |
|
||||
| REVCTL | 0xE60B | Revision control. Value 0x03 = NOAUTOARM + SKIPCOMMIT (required for manual EP management). |
|
||||
| EP2FIFOCFG | 0xE618 | EP2 FIFO configuration. Value 0x0C = AUTOIN + ZEROLENIN, 8-bit data bus. |
|
||||
|
||||
### USB Setup Data (SETUPDAT)
|
||||
|
||||
Populated by the FX2 hardware when a SETUP packet arrives on EP0. The vendor command dispatcher reads `SETUPDAT[1]` to determine the command.
|
||||
|
||||
| Register | Address | Content |
|
||||
|----------|---------|---------|
|
||||
| SETUPDAT[0] | 0xE6B8 | bmRequestType (0x40 = vendor OUT, 0xC0 = vendor IN) |
|
||||
| SETUPDAT[1] | 0xE6B9 | bRequest (vendor command ID: 0x80--0xB9) |
|
||||
| SETUPDAT[2] | 0xE6BA | wValueL |
|
||||
| SETUPDAT[3] | 0xE6BB | wValueH |
|
||||
| SETUPDAT[4] | 0xE6BC | wIndexL |
|
||||
| SETUPDAT[5] | 0xE6BD | wIndexH |
|
||||
| SETUPDAT[6] | 0xE6BE | wLengthL |
|
||||
| SETUPDAT[7] | 0xE6BF | wLengthH |
|
||||
|
||||
### EP0 Buffer
|
||||
|
||||
| Register | Address | Function |
|
||||
|----------|---------|----------|
|
||||
| EP0BCH | 0xE68A | EP0 byte count high. |
|
||||
| EP0BCL | 0xE68B | EP0 byte count low. Writing this register arms the EP0 IN transfer. |
|
||||
| EP0BUF | 0xE740 | EP0 data buffer start (64 bytes). Contains [TUNE_8PSK](/bcm4500/tuning-protocol/) payload bytes 0xE740--0xE749. |
|
||||
|
||||
### LNB Control (XRAM)
|
||||
|
||||
| Address | Function |
|
||||
|---------|----------|
|
||||
| 0xE0B6 | LNB voltage control register. Written by `SET_LNB_VOLTAGE` (0x8B) in the custom firmware. |
|
||||
|
||||
## FX2 I2C Controller
|
||||
|
||||
The Cypress FX2LP's built-in I2C master controller uses three hardware registers in the SFR-mapped XRAM space.
|
||||
|
||||
| Register | Address | Function |
|
||||
|----------|---------|----------|
|
||||
| I2CS | 0xE678 | Control/Status. Bit fields: DONE (bit 0), ACK (bit 1), BERR (bit 2), ID (bits 4:3), LASTRD (bit 5), STOP (bit 6), START (bit 7). |
|
||||
| I2DAT | 0xE679 | Data register. Write to transmit a byte, read to receive. First write after START sends the slave address byte. |
|
||||
| I2CTL | 0xE67A | Control register. Bit 0 = 400 kHz mode (set by all firmware versions at init). Bit 1 = STOPIE (stop interrupt enable). |
|
||||
|
||||
### I2C Bus Addresses
|
||||
|
||||
| 7-bit Address | 8-bit Write | 8-bit Read | Device |
|
||||
|--------------|-------------|------------|--------|
|
||||
| 0x08 | 0x10 | 0x11 | BCM4500 demodulator (operating address) |
|
||||
| 0x10 | 0x20 | 0x21 | Tuner / LNB controller |
|
||||
| 0x51 | 0xA2 | 0xA3 | Configuration EEPROM (serial number, calibration, firmware storage) |
|
||||
| 0x3F | 0x7E | 0x7F | BCM4500 alternate probe address (v2.13 boot detection only) |
|
||||
| 0x7F | 0xFE | 0xFF | BCM4500 alternate probe address (v2.13 boot detection only) |
|
||||
|
||||
See [I2C Bus Architecture](/i2c/bus-architecture/) for bus topology and the [STOP Corruption Bug](/i2c/stop-corruption-bug/) for the spurious STOP issue affecting the FX2 controller.
|
||||
|
||||
## Cross-Reference Index
|
||||
|
||||
Every documentation page that references specific registers or memory addresses:
|
||||
|
||||
### BCM4500 Direct Registers
|
||||
|
||||
| Register | Pages |
|
||||
|----------|-------|
|
||||
| 0xA2 (Status) | [Demodulator](/bcm4500/demodulator/), [Signal Monitoring](/bcm4500/signal-monitoring/), [Version Comparison](/firmware/version-comparison/) |
|
||||
| 0xA4 (Lock) | [Demodulator](/bcm4500/demodulator/), [Signal Monitoring](/bcm4500/signal-monitoring/), [Tuning Protocol](/bcm4500/tuning-protocol/) |
|
||||
| 0xA6/0xA7/0xA8 (Indirect) | [Demodulator](/bcm4500/demodulator/), [Tuning Protocol](/bcm4500/tuning-protocol/), [Signal Monitoring](/bcm4500/signal-monitoring/) |
|
||||
| 0xF9 (Demod Status) | [Demodulator](/bcm4500/demodulator/), [Signal Monitoring](/bcm4500/signal-monitoring/), [Version Comparison](/firmware/version-comparison/) |
|
||||
|
||||
### FX2 XRAM
|
||||
|
||||
| Address Range | Pages |
|
||||
|--------------|-------|
|
||||
| 0xE0B1--0xE0F9 (FEC tables) | [Tuning Protocol](/bcm4500/tuning-protocol/) |
|
||||
| 0xE0CB--0xE0DE (Tune params) | [Tuning Protocol](/bcm4500/tuning-protocol/) |
|
||||
| 0xE0EB--0xE0F6 (Mod config) | [Tuning Protocol](/bcm4500/tuning-protocol/) |
|
||||
| 0xE080--0xE08E (FW2/FW3 cal) | [FW2.13 Variants](/firmware/fw213-variants/) |
|
||||
|
||||
### FX2 IRAM
|
||||
|
||||
| Address | Pages |
|
||||
|---------|-------|
|
||||
| Config status (version-dependent) | [Config Status](/usb/config-status/), [Version Comparison](/firmware/version-comparison/), [FW2.13 Variants](/firmware/fw213-variants/) |
|
||||
| SP, I2C buffers | [FW2.13 Variants](/firmware/fw213-variants/), [Version Comparison](/firmware/version-comparison/) |
|
||||
|
||||
### I2C Controller
|
||||
|
||||
| Register | Pages |
|
||||
|----------|-------|
|
||||
| I2CS/I2DAT/I2CTL (0xE678--0xE67A) | [I2C Bus Architecture](/i2c/bus-architecture/), [STOP Corruption Bug](/i2c/stop-corruption-bug/), [Demodulator](/bcm4500/demodulator/) |
|
||||
|
|
|
|||
|
|
@ -1,119 +1,119 @@
|
|||
---
|
||||
title: Signal Monitoring
|
||||
description: SNR, signal strength, and lock status readback from the BCM4500 demodulator.
|
||||
---
|
||||
|
||||
import { Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
Signal monitoring uses two vendor commands: GET_SIGNAL_LOCK (0x90) for lock status and GET_SIGNAL_STRENGTH (0x87) for SNR and diagnostic data. Both read BCM4500 direct registers via I2C.
|
||||
|
||||
See the [Register Map](/bcm4500/register-map/) for a consolidated lookup of all register addresses referenced on this page.
|
||||
|
||||
## Signal Lock (GET_SIGNAL_LOCK, 0x90)
|
||||
|
||||
Returns 1 byte from BCM4500 direct register 0xA4.
|
||||
|
||||
| Bit | Mask | Meaning |
|
||||
|-----|------|---------|
|
||||
| 5 | 0x20 | Signal locked |
|
||||
| Other bits | -- | Additional status (undocumented) |
|
||||
|
||||
The kernel driver interprets **any non-zero value** as locked and reports the full lock status:
|
||||
|
||||
```c title="Kernel Lock Status Flags"
|
||||
FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER
|
||||
```
|
||||
|
||||
### Lock States
|
||||
|
||||
| Value | State | Description |
|
||||
|-------|-------|-------------|
|
||||
| 0x00 | <Badge text="Unlocked" variant="danger" /> | No signal or signal not acquired |
|
||||
| 0x20 | <Badge text="Locked" variant="success" /> | Signal locked, FEC decoding active |
|
||||
| Other non-zero | <Badge text="Locked" variant="success" /> | Signal acquired, partial status bits |
|
||||
|
||||
<Aside type="note" title="Lock Polling">
|
||||
After sending a [TUNE_8PSK](/bcm4500/tuning-protocol/) command, the host should poll GET_SIGNAL_LOCK repeatedly until a non-zero value is returned or a timeout expires. Typical lock acquisition takes 100-500 ms depending on signal quality and modulation type.
|
||||
</Aside>
|
||||
|
||||
## Signal Strength (GET_SIGNAL_STRENGTH, 0x87)
|
||||
|
||||
Returns 6 bytes. The first two bytes contain a 16-bit SNR value; the remaining bytes are diagnostic register data from the BCM4500.
|
||||
|
||||
### Response Format
|
||||
|
||||
| Byte | Content | Notes |
|
||||
|------|---------|-------|
|
||||
| 0 | SNR low byte (LSB) | 16-bit little-endian |
|
||||
| 1 | SNR high byte (MSB) | dBu x 256 units |
|
||||
| 2 | Reserved | BCM4500 diagnostic register |
|
||||
| 3 | Reserved | BCM4500 diagnostic register |
|
||||
| 4 | Reserved | BCM4500 diagnostic register |
|
||||
| 5 | Reserved | BCM4500 diagnostic register |
|
||||
|
||||
### SNR Scaling
|
||||
|
||||
The Windows BDA driver provides the scaling formula:
|
||||
|
||||
```c title="SNR to Signal Strength Conversion"
|
||||
uint16_t snr_raw = (buf[1] << 8) | buf[0];
|
||||
|
||||
if (snr_raw <= 0x0F00) {
|
||||
signal_strength = snr_raw * 17; // Maps 0--0x0F00 to 0--65535
|
||||
} else {
|
||||
signal_strength = 0xFFFF; // 100% at SNR >= 0x0F00
|
||||
}
|
||||
```
|
||||
|
||||
| SNR Raw Value | Signal Strength | Quality |
|
||||
|--------------|----------------|---------|
|
||||
| 0x0000 | 0 (0%) | No signal |
|
||||
| 0x0400 | ~27% | Poor |
|
||||
| 0x0800 | ~53% | Fair |
|
||||
| 0x0C00 | ~80% | Good |
|
||||
| 0x0F00 | 100% | Maximum |
|
||||
| > 0x0F00 | 100% (clamped) | Maximum |
|
||||
|
||||
### Firmware Implementation Differences
|
||||
|
||||
The signal strength readback involves I2C transactions to the BCM4500's [indirect register protocol](/bcm4500/demodulator/). The implementation varies between firmware versions:
|
||||
|
||||
| Aspect | v2.06 | Rev.2 v2.10 | v2.13 |
|
||||
|--------|-------|-------------|-------|
|
||||
| Registers polled | 0xA2, 0xA8, 0xA4 | 0xA2, 0xA8, 0xA4 | Consolidated (1 register) |
|
||||
| Max poll iterations | 6 | 6 | Simplified |
|
||||
| Read-back verification | No | Yes (explicit) | No |
|
||||
| Call chain | 3-register loop | 3-register + verify | Single register path |
|
||||
|
||||
All versions ultimately return the same 6-byte response format to the host.
|
||||
|
||||
## BCM4500 Status Registers
|
||||
|
||||
These direct registers are used for signal monitoring:
|
||||
|
||||
| Register | Address | Function | Access |
|
||||
|----------|---------|----------|--------|
|
||||
| Status | 0xA2 | BCM4500 readiness status | Polled during boot and signal checks |
|
||||
| Lock | 0xA4 | Lock/ready; bit 5 = locked | Read by GET_SIGNAL_LOCK (0x90) |
|
||||
| Command | 0xA8 | Indirect command status; bit 0 = busy | Polled during register operations |
|
||||
| Demod Status | 0xF9 | Extended demod status | Read by GET_DEMOD_STATUS (0x99, v2.13 only) |
|
||||
|
||||
### Register 0xA2 -- Status
|
||||
|
||||
Read during boot probing and signal strength readback. Returns 0x02 when the BCM4500 is powered on but no signal is locked. Exact bit field definitions are not documented in the public BCM4500 datasheet.
|
||||
|
||||
### Register 0xA4 -- Lock
|
||||
|
||||
The primary lock indicator. The kernel driver reads this as a single byte via GET_SIGNAL_LOCK (0x90). Bit 5 (0x20) is the definitive lock status flag.
|
||||
|
||||
### Register 0xA8 -- Command
|
||||
|
||||
Used by the [indirect register protocol](/bcm4500/demodulator/) to track command completion. Bit 0 clear indicates the previous indirect read/write command has completed. This register is polled after every indirect register operation.
|
||||
|
||||
### Register 0xF9 -- Demod Status (v2.13 only)
|
||||
|
||||
Read by the GET_DEMOD_STATUS vendor command (0x99). Also polled by the v2.13 INT0 handler for demodulator availability detection. This register is not accessed by v2.06 or Rev.2 firmware.
|
||||
|
||||
<Aside type="tip" title="Custom Firmware Commands">
|
||||
The custom firmware v3.01.0 adds RAW_DEMOD_READ (0xB1) and I2C_DIAG (0xB6) commands that can read any BCM4500 indirect register for diagnostic purposes. See [Vendor Commands](/usb/vendor-commands/) for details.
|
||||
</Aside>
|
||||
---
|
||||
title: Signal Monitoring
|
||||
description: SNR, signal strength, and lock status readback from the BCM4500 demodulator.
|
||||
---
|
||||
|
||||
import { Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
Signal monitoring uses two vendor commands: GET_SIGNAL_LOCK (0x90) for lock status and GET_SIGNAL_STRENGTH (0x87) for SNR and diagnostic data. Both read BCM4500 direct registers via I2C.
|
||||
|
||||
See the [Register Map](/bcm4500/register-map/) for a consolidated lookup of all register addresses referenced on this page.
|
||||
|
||||
## Signal Lock (GET_SIGNAL_LOCK, 0x90)
|
||||
|
||||
Returns 1 byte from BCM4500 direct register 0xA4.
|
||||
|
||||
| Bit | Mask | Meaning |
|
||||
|-----|------|---------|
|
||||
| 5 | 0x20 | Signal locked |
|
||||
| Other bits | -- | Additional status (undocumented) |
|
||||
|
||||
The kernel driver interprets **any non-zero value** as locked and reports the full lock status:
|
||||
|
||||
```c title="Kernel Lock Status Flags"
|
||||
FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER
|
||||
```
|
||||
|
||||
### Lock States
|
||||
|
||||
| Value | State | Description |
|
||||
|-------|-------|-------------|
|
||||
| 0x00 | <Badge text="Unlocked" variant="danger" /> | No signal or signal not acquired |
|
||||
| 0x20 | <Badge text="Locked" variant="success" /> | Signal locked, FEC decoding active |
|
||||
| Other non-zero | <Badge text="Locked" variant="success" /> | Signal acquired, partial status bits |
|
||||
|
||||
<Aside type="note" title="Lock Polling">
|
||||
After sending a [TUNE_8PSK](/bcm4500/tuning-protocol/) command, the host should poll GET_SIGNAL_LOCK repeatedly until a non-zero value is returned or a timeout expires. Typical lock acquisition takes 100-500 ms depending on signal quality and modulation type.
|
||||
</Aside>
|
||||
|
||||
## Signal Strength (GET_SIGNAL_STRENGTH, 0x87)
|
||||
|
||||
Returns 6 bytes. The first two bytes contain a 16-bit SNR value; the remaining bytes are diagnostic register data from the BCM4500.
|
||||
|
||||
### Response Format
|
||||
|
||||
| Byte | Content | Notes |
|
||||
|------|---------|-------|
|
||||
| 0 | SNR low byte (LSB) | 16-bit little-endian |
|
||||
| 1 | SNR high byte (MSB) | dBu x 256 units |
|
||||
| 2 | Reserved | BCM4500 diagnostic register |
|
||||
| 3 | Reserved | BCM4500 diagnostic register |
|
||||
| 4 | Reserved | BCM4500 diagnostic register |
|
||||
| 5 | Reserved | BCM4500 diagnostic register |
|
||||
|
||||
### SNR Scaling
|
||||
|
||||
The Windows BDA driver provides the scaling formula:
|
||||
|
||||
```c title="SNR to Signal Strength Conversion"
|
||||
uint16_t snr_raw = (buf[1] << 8) | buf[0];
|
||||
|
||||
if (snr_raw <= 0x0F00) {
|
||||
signal_strength = snr_raw * 17; // Maps 0--0x0F00 to 0--65535
|
||||
} else {
|
||||
signal_strength = 0xFFFF; // 100% at SNR >= 0x0F00
|
||||
}
|
||||
```
|
||||
|
||||
| SNR Raw Value | Signal Strength | Quality |
|
||||
|--------------|----------------|---------|
|
||||
| 0x0000 | 0 (0%) | No signal |
|
||||
| 0x0400 | ~27% | Poor |
|
||||
| 0x0800 | ~53% | Fair |
|
||||
| 0x0C00 | ~80% | Good |
|
||||
| 0x0F00 | 100% | Maximum |
|
||||
| > 0x0F00 | 100% (clamped) | Maximum |
|
||||
|
||||
### Firmware Implementation Differences
|
||||
|
||||
The signal strength readback involves I2C transactions to the BCM4500's [indirect register protocol](/bcm4500/demodulator/). The implementation varies between firmware versions:
|
||||
|
||||
| Aspect | v2.06 | Rev.2 v2.10 | v2.13 |
|
||||
|--------|-------|-------------|-------|
|
||||
| Registers polled | 0xA2, 0xA8, 0xA4 | 0xA2, 0xA8, 0xA4 | Consolidated (1 register) |
|
||||
| Max poll iterations | 6 | 6 | Simplified |
|
||||
| Read-back verification | No | Yes (explicit) | No |
|
||||
| Call chain | 3-register loop | 3-register + verify | Single register path |
|
||||
|
||||
All versions ultimately return the same 6-byte response format to the host.
|
||||
|
||||
## BCM4500 Status Registers
|
||||
|
||||
These direct registers are used for signal monitoring:
|
||||
|
||||
| Register | Address | Function | Access |
|
||||
|----------|---------|----------|--------|
|
||||
| Status | 0xA2 | BCM4500 readiness status | Polled during boot and signal checks |
|
||||
| Lock | 0xA4 | Lock/ready; bit 5 = locked | Read by GET_SIGNAL_LOCK (0x90) |
|
||||
| Command | 0xA8 | Indirect command status; bit 0 = busy | Polled during register operations |
|
||||
| Demod Status | 0xF9 | Extended demod status | Read by GET_DEMOD_STATUS (0x99, v2.13 only) |
|
||||
|
||||
### Register 0xA2 -- Status
|
||||
|
||||
Read during boot probing and signal strength readback. Returns 0x02 when the BCM4500 is powered on but no signal is locked. Exact bit field definitions are not documented in the public BCM4500 datasheet.
|
||||
|
||||
### Register 0xA4 -- Lock
|
||||
|
||||
The primary lock indicator. The kernel driver reads this as a single byte via GET_SIGNAL_LOCK (0x90). Bit 5 (0x20) is the definitive lock status flag.
|
||||
|
||||
### Register 0xA8 -- Command
|
||||
|
||||
Used by the [indirect register protocol](/bcm4500/demodulator/) to track command completion. Bit 0 clear indicates the previous indirect read/write command has completed. This register is polled after every indirect register operation.
|
||||
|
||||
### Register 0xF9 -- Demod Status (v2.13 only)
|
||||
|
||||
Read by the GET_DEMOD_STATUS vendor command (0x99). Also polled by the v2.13 INT0 handler for demodulator availability detection. This register is not accessed by v2.06 or Rev.2 firmware.
|
||||
|
||||
<Aside type="tip" title="Custom Firmware Commands">
|
||||
The custom firmware v3.01.0 adds RAW_DEMOD_READ (0xB1) and I2C_DIAG (0xB6) commands that can read any BCM4500 indirect register for diagnostic purposes. See [Vendor Commands](/usb/vendor-commands/) for details.
|
||||
</Aside>
|
||||
|
|
|
|||
|
|
@ -1,260 +1,260 @@
|
|||
---
|
||||
title: Tuning Protocol
|
||||
description: Complete TUNE_8PSK command protocol including parameter encoding, modulation dispatch, and BCM4500 I2C programming.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Badge, Steps, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
The TUNE_8PSK vendor command (0x86) is the primary mechanism for programming the BCM4500 demodulator to receive a satellite signal. The command carries a 10-byte payload encoding frequency, symbol rate, modulation type, and FEC rate. The firmware parses this payload, dispatches to a modulation-specific handler, programs the BCM4500 via I2C, and the host then polls for signal lock.
|
||||
|
||||
See the [Register Map](/bcm4500/register-map/) for a consolidated lookup of all XRAM addresses and BCM4500 registers referenced on this page.
|
||||
|
||||
## Command Format
|
||||
|
||||
```
|
||||
USB SETUP:
|
||||
bmRequestType = 0x40 (Vendor, Host-to-Device, OUT)
|
||||
bRequest = 0x86 (TUNE_8PSK)
|
||||
wValue = 0x0000
|
||||
wIndex = 0x0000
|
||||
wLength = 10
|
||||
```
|
||||
|
||||
### EP0 Payload Layout
|
||||
|
||||
| Byte | Content | Encoding |
|
||||
|------|---------|----------|
|
||||
| [0] | Symbol Rate byte 0 | Little-endian LSB |
|
||||
| [1] | Symbol Rate byte 1 | |
|
||||
| [2] | Symbol Rate byte 2 | |
|
||||
| [3] | Symbol Rate byte 3 | Little-endian MSB |
|
||||
| [4] | Frequency byte 0 | Little-endian LSB |
|
||||
| [5] | Frequency byte 1 | |
|
||||
| [6] | Frequency byte 2 | |
|
||||
| [7] | Frequency byte 3 | Little-endian MSB |
|
||||
| [8] | Modulation Type | 0--9 (see table below) |
|
||||
| [9] | Inner FEC Rate | Index into modulation-specific table |
|
||||
|
||||
**Symbol Rate** is in samples per second (sps). The Windows driver converts from ksps: `ulTempSymbolRate = pDeviceParameter->ulSymbolRate * 1000`. Valid range: 256,000 -- 30,000,000 sps.
|
||||
|
||||
**Frequency** is the IF frequency in kHz (950,000 -- 2,150,000), computed by the host as `(RF_freq - LO_freq) * multiplier`.
|
||||
|
||||
## Firmware Parameter Parsing
|
||||
|
||||
The firmware reads the 10-byte payload from EP0BUF (XRAM 0xE740--0xE749) and stores:
|
||||
|
||||
| Source | Destination | Notes |
|
||||
|--------|-------------|-------|
|
||||
| EP0BUF[8] (modulation) | IRAM 0x4D | Direct copy |
|
||||
| EP0BUF[9] (FEC rate) | IRAM 0x4F | Direct copy |
|
||||
| EP0BUF[4--7] (frequency) | XRAM 0xE0DB--0xE0DE | Byte-reversed (LE to BE) |
|
||||
| EP0BUF[0--3] (symbol rate) | XRAM 0xE0CB--0xE0CE | Byte-reversed (LE to BE) |
|
||||
|
||||
The byte reversal converts host little-endian to BCM4500 big-endian, allowing values to be written directly to the demodulator via I2C without further conversion.
|
||||
|
||||
```asm title="EP0BUF Read (Rev.2 at CODE:0802)"
|
||||
; Read modulation type and FEC rate
|
||||
MOV DPTR, #0xE748 ; EP0BUF[8] = modulation type
|
||||
MOVX A, @DPTR
|
||||
MOV 0x4D, A ; Store to IRAM 0x4D
|
||||
INC DPTR ; DPTR = 0xE749
|
||||
MOVX A, @DPTR ; EP0BUF[9] = FEC rate
|
||||
MOV 0x4F, A ; Store to IRAM 0x4F
|
||||
```
|
||||
|
||||
## Modulation Dispatch
|
||||
|
||||
After parsing, the firmware validates the modulation type (bounds check against 10) and dispatches via a 20-byte jump table (10 entries x 2 bytes) at CODE:0873. Values >= 10 are rejected silently.
|
||||
|
||||
### Dispatch Table (Rev.2)
|
||||
|
||||
| Entry | Target | Modulation |
|
||||
|-------|--------|-----------|
|
||||
| 0 | 0x08B7 | DVB-S QPSK |
|
||||
| 1 | 0x08DF | Turbo QPSK |
|
||||
| 2 | 0x08FA | Turbo 8PSK |
|
||||
| 3 | 0x0915 | Turbo 16QAM |
|
||||
| 4 | 0x0947 | DCII Combo |
|
||||
| 5 | 0x094F | DCII I-stream |
|
||||
| 6 | 0x0957 | DCII Q-stream |
|
||||
| 7 | 0x095F | DCII Offset QPSK |
|
||||
| 8 | 0x0887 | DSS QPSK |
|
||||
| 9 | 0x0887 | DVB BPSK (shares DSS handler) |
|
||||
|
||||
Each handler validates the FEC index, looks up a preconfigured byte from an XRAM table, and writes configuration to four XRAM registers.
|
||||
|
||||
## Modulation Handler Details
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="DVB-S / DSS / BPSK">
|
||||
|
||||
### DVB-S QPSK (Index 0)
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| FEC table base | XRAM 0xE0F9 |
|
||||
| Max FEC index | 7 |
|
||||
| Modulation type register | 0x09 |
|
||||
| Turbo flag | 0x00 (off) |
|
||||
| Demod mode | 0x10 (standard) |
|
||||
| bmDCtuned | Cleared |
|
||||
|
||||
**FEC rates available** (table at 0xE0F9): 1/2, 2/3, 3/4, 5/6, 7/8, auto, none.
|
||||
|
||||
### DSS QPSK (Index 8) and DVB-S BPSK (Index 9)
|
||||
|
||||
DSS and DVB BPSK share the same handler at 0x0887. They use the same FEC table as DVB-S QPSK (0xE0F9) but OR the lookup value with 0x80 to distinguish them:
|
||||
|
||||
```c title="DSS/BPSK FEC Encoding"
|
||||
XRAM[0xE0EB] = XRAM[0xE0F9 + FEC_index] | 0x80;
|
||||
// Out-of-range default: 0x8C
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Turbo Modes">
|
||||
|
||||
### Turbo QPSK (Index 1)
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| FEC table base | XRAM 0xE0B7 |
|
||||
| Max FEC index | 5 |
|
||||
| Modulation type register | 0x09 |
|
||||
| Turbo flag | 0x01 (on) |
|
||||
| Demod mode | 0x10 |
|
||||
| bmDCtuned | Cleared |
|
||||
|
||||
### Turbo 8PSK (Index 2)
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| FEC table base | XRAM 0xE0B1 |
|
||||
| Max FEC index | 5 |
|
||||
| Modulation type register | 0x09 |
|
||||
| Turbo flag | 0x01 (on) |
|
||||
| Demod mode | 0x10 |
|
||||
| bmDCtuned | Cleared |
|
||||
|
||||
### Turbo 16QAM (Index 3)
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| FEC table base | XRAM 0xE0BC |
|
||||
| Max FEC index | 1 |
|
||||
| Modulation type register | 0x09 |
|
||||
| Turbo flag | 0x01 (on) |
|
||||
| Demod mode | 0x10 |
|
||||
| bmDCtuned | Cleared |
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="DCII Modes">
|
||||
|
||||
All four DCII variants share a common post-processing path but set different demod mode values:
|
||||
|
||||
| Modulation | Index | Demod Mode (0xE0F5) |
|
||||
|-----------|-------|---------------------|
|
||||
| DCII Combo | 4 | 0x10 |
|
||||
| DCII Offset QPSK | 7 | 0x11 |
|
||||
| DCII I-stream (split) | 5 | 0x12 |
|
||||
| DCII Q-stream (split) | 6 | 0x16 |
|
||||
|
||||
**Common DCII parameters:**
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| FEC table base | XRAM 0xE0BD |
|
||||
| Max FEC index | 9 |
|
||||
| FEC code rate register | 0xFC (fixed for all DCII) |
|
||||
| Turbo flag | 0x00 (off) |
|
||||
| bmDCtuned | **Set** (0x40 OR'd into config status) |
|
||||
|
||||
The DCII modulation type register (0xE0EC) is loaded from the lookup table at `0xE0BD + FEC_index`, unlike other modulations which use the fixed value 0x09.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## XRAM Configuration Summary
|
||||
|
||||
After modulation dispatch, four XRAM registers hold the BCM4500 configuration:
|
||||
|
||||
| XRAM Address | Register Name | DVB-S QPSK | Turbo (Q/8/16) | DCII | DSS/BPSK |
|
||||
|-------------|---------------|-----------|---------------|------|----------|
|
||||
| 0xE0EB | FEC Code Rate | Table lookup | Table lookup | 0xFC (fixed) | Lookup OR 0x80 |
|
||||
| 0xE0EC | Modulation Type | 0x09 | 0x09 | From DCII table | 0x09 |
|
||||
| 0xE0F5 | Demod Mode | 0x10 | 0x10 | 0x10/0x11/0x12/0x16 | 0x10 |
|
||||
| 0xE0F6 | Turbo Flag | 0x00 | 0x01 | 0x00 | 0x00 |
|
||||
|
||||
## FEC Rate Lookup Tables
|
||||
|
||||
These tables are populated at boot from the CODE-space init table:
|
||||
|
||||
| XRAM Base | Modulation | Max Index | Code Rates |
|
||||
|-----------|-----------|-----------|------------|
|
||||
| 0xE0F9 | DVB-S QPSK, DSS, BPSK | 7 | 1/2, 2/3, 3/4, 5/6, 7/8, auto, none |
|
||||
| 0xE0B7 | Turbo QPSK | 5 | Turbo-specific rates |
|
||||
| 0xE0B1 | Turbo 8PSK | 5 | Turbo-specific rates |
|
||||
| 0xE0BC | Turbo 16QAM | 1 | Single code rate |
|
||||
| 0xE0BD | DCII (all variants) | 9 | Combined code + modulation |
|
||||
|
||||
## Complete Tuning Sequence
|
||||
|
||||
The full sequence from host command to signal acquisition:
|
||||
|
||||
<Steps>
|
||||
1. **LNB Configuration** (separate vendor commands, before TUNE_8PSK)
|
||||
- SET_LNB_VOLTAGE (0x8B): GPIO P0.4, no I2C. wValue=1 for 18V (H/Circular-L), wValue=0 for 13V (V/Circular-R).
|
||||
- SET_22KHZ_TONE (0x8C): GPIO P0.3, no I2C. wValue=1 for high band, wValue=0 for low band.
|
||||
- SEND_DISEQC_COMMAND (0x8D): If multi-switch is needed.
|
||||
|
||||
2. **TUNE_8PSK (0x86)** -- Host sends 10-byte payload via USB control transfer.
|
||||
|
||||
3. **EP0BUF Parsing** -- Firmware reads modulation/FEC to IRAM, byte-reverses frequency/symbol rate to XRAM.
|
||||
|
||||
4. **Modulation Dispatch** -- FEC lookup via jump table, XRAM configuration registers set.
|
||||
|
||||
5. **GPIO P3.6** -- DVB mode select pin driven based on modulation type.
|
||||
|
||||
6. **BCM4500 I2C Programming** (3 outer retries, each trying up to 3 I2C addresses):
|
||||
- Poll BCM4500 ready: I2C READ registers 0xA2, 0xA8, 0xA4.
|
||||
- Write page: I2C WRITE register 0xA6 with 0x00.
|
||||
- Write config data: I2C WRITE register 0xA7 with frequency, symbol rate, FEC, modulation, and demod parameters.
|
||||
- Execute: I2C WRITE register 0xA8 with 0x03 (indirect write command).
|
||||
- Poll completion: I2C READ registers 0xA8, 0xA2.
|
||||
- Verify: I2C READ register 0xA7 (read-back compare).
|
||||
|
||||
7. **Signal Acquisition** (host polling):
|
||||
- GET_SIGNAL_LOCK (0x90): Poll until non-zero.
|
||||
- GET_SIGNAL_STRENGTH (0x87): Read [SNR value](/bcm4500/signal-monitoring/).
|
||||
</Steps>
|
||||
|
||||
## I2C Programming Details
|
||||
|
||||
The core I2C write function (`FUN_CODE_1670` on Rev.2) implements the [BCM4500 indirect register protocol](/bcm4500/demodulator/):
|
||||
|
||||
```c title="BCM4500 Indirect Write (Decompiled)"
|
||||
void bcm4500_indirect_write(byte *data, byte count) {
|
||||
// Wait for BCM4500 ready (poll regs 0xA2, 0xA8, 0xA4)
|
||||
bus_wait_ready();
|
||||
|
||||
// Write page address (0x00) to register 0xA6
|
||||
i2c_write(1, 0, 0xA6, 0x10); // [0x00]
|
||||
|
||||
// Write data to register 0xA7
|
||||
i2c_write(count, 0, 0xA7, 0x10); // [data0..dataN]
|
||||
|
||||
// Write command (0x03 = indirect write) to 0xA8
|
||||
i2c_write(1, 0, 0xA8, 0x10); // [0x03]
|
||||
|
||||
// Poll completion (regs 0xA8, 0xA2)
|
||||
poll_write_complete();
|
||||
|
||||
// Verify: read back from 0xA7 and compare
|
||||
verify_readback();
|
||||
}
|
||||
```
|
||||
|
||||
The demod scan function (`FUN_CODE_1dd0`) wraps this in a 3-address iteration loop, supporting hardware variants where the BCM4500 may respond at different I2C addresses. The outer tune function retries the entire scan up to 3 times.
|
||||
|
||||
<Aside type="note" title="Version Differences">
|
||||
The tuning logic is functionally identical across all three firmware versions. Differences are limited to code addresses (due to recompilation), the config status byte IRAM location, and minor implementation details in the signal strength polling path. v2.13 adds a 20-attempt retry loop with checksum verification on the I2C programming step.
|
||||
</Aside>
|
||||
---
|
||||
title: Tuning Protocol
|
||||
description: Complete TUNE_8PSK command protocol including parameter encoding, modulation dispatch, and BCM4500 I2C programming.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Badge, Steps, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
The TUNE_8PSK vendor command (0x86) is the primary mechanism for programming the BCM4500 demodulator to receive a satellite signal. The command carries a 10-byte payload encoding frequency, symbol rate, modulation type, and FEC rate. The firmware parses this payload, dispatches to a modulation-specific handler, programs the BCM4500 via I2C, and the host then polls for signal lock.
|
||||
|
||||
See the [Register Map](/bcm4500/register-map/) for a consolidated lookup of all XRAM addresses and BCM4500 registers referenced on this page.
|
||||
|
||||
## Command Format
|
||||
|
||||
```
|
||||
USB SETUP:
|
||||
bmRequestType = 0x40 (Vendor, Host-to-Device, OUT)
|
||||
bRequest = 0x86 (TUNE_8PSK)
|
||||
wValue = 0x0000
|
||||
wIndex = 0x0000
|
||||
wLength = 10
|
||||
```
|
||||
|
||||
### EP0 Payload Layout
|
||||
|
||||
| Byte | Content | Encoding |
|
||||
|------|---------|----------|
|
||||
| [0] | Symbol Rate byte 0 | Little-endian LSB |
|
||||
| [1] | Symbol Rate byte 1 | |
|
||||
| [2] | Symbol Rate byte 2 | |
|
||||
| [3] | Symbol Rate byte 3 | Little-endian MSB |
|
||||
| [4] | Frequency byte 0 | Little-endian LSB |
|
||||
| [5] | Frequency byte 1 | |
|
||||
| [6] | Frequency byte 2 | |
|
||||
| [7] | Frequency byte 3 | Little-endian MSB |
|
||||
| [8] | Modulation Type | 0--9 (see table below) |
|
||||
| [9] | Inner FEC Rate | Index into modulation-specific table |
|
||||
|
||||
**Symbol Rate** is in samples per second (sps). The Windows driver converts from ksps: `ulTempSymbolRate = pDeviceParameter->ulSymbolRate * 1000`. Valid range: 256,000 -- 30,000,000 sps.
|
||||
|
||||
**Frequency** is the IF frequency in kHz (950,000 -- 2,150,000), computed by the host as `(RF_freq - LO_freq) * multiplier`.
|
||||
|
||||
## Firmware Parameter Parsing
|
||||
|
||||
The firmware reads the 10-byte payload from EP0BUF (XRAM 0xE740--0xE749) and stores:
|
||||
|
||||
| Source | Destination | Notes |
|
||||
|--------|-------------|-------|
|
||||
| EP0BUF[8] (modulation) | IRAM 0x4D | Direct copy |
|
||||
| EP0BUF[9] (FEC rate) | IRAM 0x4F | Direct copy |
|
||||
| EP0BUF[4--7] (frequency) | XRAM 0xE0DB--0xE0DE | Byte-reversed (LE to BE) |
|
||||
| EP0BUF[0--3] (symbol rate) | XRAM 0xE0CB--0xE0CE | Byte-reversed (LE to BE) |
|
||||
|
||||
The byte reversal converts host little-endian to BCM4500 big-endian, allowing values to be written directly to the demodulator via I2C without further conversion.
|
||||
|
||||
```asm title="EP0BUF Read (Rev.2 at CODE:0802)"
|
||||
; Read modulation type and FEC rate
|
||||
MOV DPTR, #0xE748 ; EP0BUF[8] = modulation type
|
||||
MOVX A, @DPTR
|
||||
MOV 0x4D, A ; Store to IRAM 0x4D
|
||||
INC DPTR ; DPTR = 0xE749
|
||||
MOVX A, @DPTR ; EP0BUF[9] = FEC rate
|
||||
MOV 0x4F, A ; Store to IRAM 0x4F
|
||||
```
|
||||
|
||||
## Modulation Dispatch
|
||||
|
||||
After parsing, the firmware validates the modulation type (bounds check against 10) and dispatches via a 20-byte jump table (10 entries x 2 bytes) at CODE:0873. Values >= 10 are rejected silently.
|
||||
|
||||
### Dispatch Table (Rev.2)
|
||||
|
||||
| Entry | Target | Modulation |
|
||||
|-------|--------|-----------|
|
||||
| 0 | 0x08B7 | DVB-S QPSK |
|
||||
| 1 | 0x08DF | Turbo QPSK |
|
||||
| 2 | 0x08FA | Turbo 8PSK |
|
||||
| 3 | 0x0915 | Turbo 16QAM |
|
||||
| 4 | 0x0947 | DCII Combo |
|
||||
| 5 | 0x094F | DCII I-stream |
|
||||
| 6 | 0x0957 | DCII Q-stream |
|
||||
| 7 | 0x095F | DCII Offset QPSK |
|
||||
| 8 | 0x0887 | DSS QPSK |
|
||||
| 9 | 0x0887 | DVB BPSK (shares DSS handler) |
|
||||
|
||||
Each handler validates the FEC index, looks up a preconfigured byte from an XRAM table, and writes configuration to four XRAM registers.
|
||||
|
||||
## Modulation Handler Details
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="DVB-S / DSS / BPSK">
|
||||
|
||||
### DVB-S QPSK (Index 0)
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| FEC table base | XRAM 0xE0F9 |
|
||||
| Max FEC index | 7 |
|
||||
| Modulation type register | 0x09 |
|
||||
| Turbo flag | 0x00 (off) |
|
||||
| Demod mode | 0x10 (standard) |
|
||||
| bmDCtuned | Cleared |
|
||||
|
||||
**FEC rates available** (table at 0xE0F9): 1/2, 2/3, 3/4, 5/6, 7/8, auto, none.
|
||||
|
||||
### DSS QPSK (Index 8) and DVB-S BPSK (Index 9)
|
||||
|
||||
DSS and DVB BPSK share the same handler at 0x0887. They use the same FEC table as DVB-S QPSK (0xE0F9) but OR the lookup value with 0x80 to distinguish them:
|
||||
|
||||
```c title="DSS/BPSK FEC Encoding"
|
||||
XRAM[0xE0EB] = XRAM[0xE0F9 + FEC_index] | 0x80;
|
||||
// Out-of-range default: 0x8C
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Turbo Modes">
|
||||
|
||||
### Turbo QPSK (Index 1)
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| FEC table base | XRAM 0xE0B7 |
|
||||
| Max FEC index | 5 |
|
||||
| Modulation type register | 0x09 |
|
||||
| Turbo flag | 0x01 (on) |
|
||||
| Demod mode | 0x10 |
|
||||
| bmDCtuned | Cleared |
|
||||
|
||||
### Turbo 8PSK (Index 2)
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| FEC table base | XRAM 0xE0B1 |
|
||||
| Max FEC index | 5 |
|
||||
| Modulation type register | 0x09 |
|
||||
| Turbo flag | 0x01 (on) |
|
||||
| Demod mode | 0x10 |
|
||||
| bmDCtuned | Cleared |
|
||||
|
||||
### Turbo 16QAM (Index 3)
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| FEC table base | XRAM 0xE0BC |
|
||||
| Max FEC index | 1 |
|
||||
| Modulation type register | 0x09 |
|
||||
| Turbo flag | 0x01 (on) |
|
||||
| Demod mode | 0x10 |
|
||||
| bmDCtuned | Cleared |
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="DCII Modes">
|
||||
|
||||
All four DCII variants share a common post-processing path but set different demod mode values:
|
||||
|
||||
| Modulation | Index | Demod Mode (0xE0F5) |
|
||||
|-----------|-------|---------------------|
|
||||
| DCII Combo | 4 | 0x10 |
|
||||
| DCII Offset QPSK | 7 | 0x11 |
|
||||
| DCII I-stream (split) | 5 | 0x12 |
|
||||
| DCII Q-stream (split) | 6 | 0x16 |
|
||||
|
||||
**Common DCII parameters:**
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| FEC table base | XRAM 0xE0BD |
|
||||
| Max FEC index | 9 |
|
||||
| FEC code rate register | 0xFC (fixed for all DCII) |
|
||||
| Turbo flag | 0x00 (off) |
|
||||
| bmDCtuned | **Set** (0x40 OR'd into config status) |
|
||||
|
||||
The DCII modulation type register (0xE0EC) is loaded from the lookup table at `0xE0BD + FEC_index`, unlike other modulations which use the fixed value 0x09.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## XRAM Configuration Summary
|
||||
|
||||
After modulation dispatch, four XRAM registers hold the BCM4500 configuration:
|
||||
|
||||
| XRAM Address | Register Name | DVB-S QPSK | Turbo (Q/8/16) | DCII | DSS/BPSK |
|
||||
|-------------|---------------|-----------|---------------|------|----------|
|
||||
| 0xE0EB | FEC Code Rate | Table lookup | Table lookup | 0xFC (fixed) | Lookup OR 0x80 |
|
||||
| 0xE0EC | Modulation Type | 0x09 | 0x09 | From DCII table | 0x09 |
|
||||
| 0xE0F5 | Demod Mode | 0x10 | 0x10 | 0x10/0x11/0x12/0x16 | 0x10 |
|
||||
| 0xE0F6 | Turbo Flag | 0x00 | 0x01 | 0x00 | 0x00 |
|
||||
|
||||
## FEC Rate Lookup Tables
|
||||
|
||||
These tables are populated at boot from the CODE-space init table:
|
||||
|
||||
| XRAM Base | Modulation | Max Index | Code Rates |
|
||||
|-----------|-----------|-----------|------------|
|
||||
| 0xE0F9 | DVB-S QPSK, DSS, BPSK | 7 | 1/2, 2/3, 3/4, 5/6, 7/8, auto, none |
|
||||
| 0xE0B7 | Turbo QPSK | 5 | Turbo-specific rates |
|
||||
| 0xE0B1 | Turbo 8PSK | 5 | Turbo-specific rates |
|
||||
| 0xE0BC | Turbo 16QAM | 1 | Single code rate |
|
||||
| 0xE0BD | DCII (all variants) | 9 | Combined code + modulation |
|
||||
|
||||
## Complete Tuning Sequence
|
||||
|
||||
The full sequence from host command to signal acquisition:
|
||||
|
||||
<Steps>
|
||||
1. **LNB Configuration** (separate vendor commands, before TUNE_8PSK)
|
||||
- SET_LNB_VOLTAGE (0x8B): GPIO P0.4, no I2C. wValue=1 for 18V (H/Circular-L), wValue=0 for 13V (V/Circular-R).
|
||||
- SET_22KHZ_TONE (0x8C): GPIO P0.3, no I2C. wValue=1 for high band, wValue=0 for low band.
|
||||
- SEND_DISEQC_COMMAND (0x8D): If multi-switch is needed.
|
||||
|
||||
2. **TUNE_8PSK (0x86)** -- Host sends 10-byte payload via USB control transfer.
|
||||
|
||||
3. **EP0BUF Parsing** -- Firmware reads modulation/FEC to IRAM, byte-reverses frequency/symbol rate to XRAM.
|
||||
|
||||
4. **Modulation Dispatch** -- FEC lookup via jump table, XRAM configuration registers set.
|
||||
|
||||
5. **GPIO P3.6** -- DVB mode select pin driven based on modulation type.
|
||||
|
||||
6. **BCM4500 I2C Programming** (3 outer retries, each trying up to 3 I2C addresses):
|
||||
- Poll BCM4500 ready: I2C READ registers 0xA2, 0xA8, 0xA4.
|
||||
- Write page: I2C WRITE register 0xA6 with 0x00.
|
||||
- Write config data: I2C WRITE register 0xA7 with frequency, symbol rate, FEC, modulation, and demod parameters.
|
||||
- Execute: I2C WRITE register 0xA8 with 0x03 (indirect write command).
|
||||
- Poll completion: I2C READ registers 0xA8, 0xA2.
|
||||
- Verify: I2C READ register 0xA7 (read-back compare).
|
||||
|
||||
7. **Signal Acquisition** (host polling):
|
||||
- GET_SIGNAL_LOCK (0x90): Poll until non-zero.
|
||||
- GET_SIGNAL_STRENGTH (0x87): Read [SNR value](/bcm4500/signal-monitoring/).
|
||||
</Steps>
|
||||
|
||||
## I2C Programming Details
|
||||
|
||||
The core I2C write function (`FUN_CODE_1670` on Rev.2) implements the [BCM4500 indirect register protocol](/bcm4500/demodulator/):
|
||||
|
||||
```c title="BCM4500 Indirect Write (Decompiled)"
|
||||
void bcm4500_indirect_write(byte *data, byte count) {
|
||||
// Wait for BCM4500 ready (poll regs 0xA2, 0xA8, 0xA4)
|
||||
bus_wait_ready();
|
||||
|
||||
// Write page address (0x00) to register 0xA6
|
||||
i2c_write(1, 0, 0xA6, 0x10); // [0x00]
|
||||
|
||||
// Write data to register 0xA7
|
||||
i2c_write(count, 0, 0xA7, 0x10); // [data0..dataN]
|
||||
|
||||
// Write command (0x03 = indirect write) to 0xA8
|
||||
i2c_write(1, 0, 0xA8, 0x10); // [0x03]
|
||||
|
||||
// Poll completion (regs 0xA8, 0xA2)
|
||||
poll_write_complete();
|
||||
|
||||
// Verify: read back from 0xA7 and compare
|
||||
verify_readback();
|
||||
}
|
||||
```
|
||||
|
||||
The demod scan function (`FUN_CODE_1dd0`) wraps this in a 3-address iteration loop, supporting hardware variants where the BCM4500 may respond at different I2C addresses. The outer tune function retries the entire scan up to 3 times.
|
||||
|
||||
<Aside type="note" title="Version Differences">
|
||||
The tuning logic is functionally identical across all three firmware versions. Differences are limited to code addresses (due to recompilation), the config status byte IRAM location, and minor implementation details in the signal strength polling path. v2.13 adds a 20-attempt retry loop with checksum verification on the I2C programming step.
|
||||
</Aside>
|
||||
|
|
|
|||
|
|
@ -1,169 +1,169 @@
|
|||
---
|
||||
title: DVB-S2 Incompatibility
|
||||
description: Why the SkyWalker-1 cannot support DVB-S2 and what the BCM4500 demodulator actually provides.
|
||||
---
|
||||
|
||||
import { Badge, Aside, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
<Aside type="danger">
|
||||
**DVB-S2 is not supported on the SkyWalker-1.** This is a fundamental hardware limitation of the Broadcom BCM4500 demodulator silicon, not a firmware limitation. No firmware update can add DVB-S2 capability. The BCM4500 was designed and fabricated before the DVB-S2 standard was ratified (March 2005) and contains no LDPC or BCH decoder hardware.
|
||||
</Aside>
|
||||
|
||||
## The Core Problem
|
||||
|
||||
DVB-S2 (ETSI EN 302 307) requires two forward error correction technologies that do not exist in the BCM4500:
|
||||
|
||||
| FEC Component | BCM4500 Has | DVB-S2 Requires |
|
||||
|---------------|-------------|-----------------|
|
||||
| Inner code | Viterbi (convolutional) + Turbo | **LDPC** (Low-Density Parity-Check) |
|
||||
| Outer code | Reed-Solomon (t=10) | **BCH** (Bose-Chaudhuri-Hocquenghem) |
|
||||
| Block size | Streaming (Viterbi) or short turbo blocks | **64,800 or 16,200 bits** |
|
||||
| Decoder type | Trellis-based / iterative turbo | **Iterative belief propagation** |
|
||||
|
||||
LDPC decoding requires dedicated silicon: large block RAM for message passing (the LDPC block is 64,800 bits), iterative belief propagation logic, and a fundamentally different decoder architecture. This cannot be emulated in firmware on the BCM4500's simple 8-bit on-chip microcontroller, which handles only configuration, acquisition, and monitoring -- not data-path processing.
|
||||
|
||||
## BCM4500 FEC Architecture
|
||||
|
||||
The BCM4500 contains exactly two FEC decoder paths (from the [BCM4500 datasheet](https://elcodis.com/parts/5786421/BCM4500.html)):
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Legacy FEC">
|
||||
|
||||
### Viterbi + Reed-Solomon (Legacy)
|
||||
|
||||
Used for DVB-S QPSK, DSS QPSK, DVB-S BPSK, and Digicipher II modes.
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| Inner decoder | Viterbi (convolutional) |
|
||||
| Code rates | 1/2, 2/3, 3/4, 5/6, 7/8 |
|
||||
| Outer decoder | Reed-Solomon |
|
||||
| Signal path | Soft decisions fed to Viterbi, then RS outer code |
|
||||
| Modulations | BPSK, QPSK |
|
||||
|
||||
**Firmware evidence** (XRAM 0xE0F9): FEC lookup table with max index 7. Modulation dispatch sets `XRAM 0xE0F6 = 0x00` (turbo flag OFF), `XRAM 0xE0F5 = 0x10` (standard demod mode).
|
||||
|
||||
**Windows driver**: `m_CurResource.ulInnerFecType = BDA_FEC_VITERBI` -- explicitly rejects any FEC type other than Viterbi.
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="Turbo FEC">
|
||||
|
||||
### Turbo Code Decoder (Proprietary)
|
||||
|
||||
Used for Turbo QPSK, Turbo 8PSK, and Turbo 16QAM -- proprietary "advanced modulation" modes developed by Broadcom for EchoStar/Dish Network.
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| Inner decoder | Iterative turbo code |
|
||||
| QPSK rates | 1/4, 1/2, 3/4 |
|
||||
| 8PSK rates | 2/3, 3/4, 5/6, 8/9 |
|
||||
| 16QAM rates | 3/4 |
|
||||
| Outer decoder | Reed-Solomon (t=10) |
|
||||
| Modulations | QPSK, 8PSK, 16QAM |
|
||||
|
||||
**Firmware evidence** (XRAM 0xE0B7, 0xE0B1, 0xE0BC): Turbo FEC lookup tables. All turbo modes set `XRAM 0xE0F6 = 0x01` (turbo flag ON).
|
||||
|
||||
These turbo codes are NOT the same as DVB-S2's LDPC codes. The turbo decoder uses parallel concatenated convolutional codes, while LDPC uses sparse parity-check matrix belief propagation. Different algorithms, different silicon.
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="Digicipher II">
|
||||
|
||||
### DCII Decoder
|
||||
|
||||
Used for DCII combo, split I/Q, and offset QPSK modes.
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| FEC table | XRAM 0xE0BD, max index 9 |
|
||||
| Fixed FEC code | `0xFC` written to XRAM 0xE0EB |
|
||||
| Modulations | QPSK variants (combo, split, offset) |
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
The BCM4500 datasheet states explicitly: "Optimized soft decisions are then fed into either a DVB/DIRECTV/DCII-compliant FEC decoder, or an advanced modulation turbo decoder." These are the only two FEC paths. There is no third path for LDPC/BCH.
|
||||
|
||||
## Zero DVB-S2 Evidence in Firmware or Driver
|
||||
|
||||
Exhaustive search across all firmware versions and Windows driver source:
|
||||
|
||||
| What Was Searched | Result |
|
||||
|-------------------|--------|
|
||||
| All firmware binaries (v2.06, Rev.2, v2.13) via Ghidra | No LDPC/BCH/DVB-S2 references |
|
||||
| Windows driver `SkyWalker1Control.h` | Modulation constants 0--9 only, none for DVB-S2 |
|
||||
| Windows driver `SkyWalker1TunerFilter.cpp` | Rejects non-Viterbi FEC types |
|
||||
| Windows driver `SkyWalker1Control.cpp` | Hardcodes `ADV_MOD_DVB_QPSK` (value 0) |
|
||||
| Firmware dispatch table (CODE:0873) | 10 entries max, values >= 10 rejected |
|
||||
| All FEC lookup tables in XRAM | Only Viterbi rates and turbo rates, no LDPC rates |
|
||||
| I2C register addresses | BCM4500-specific protocol only (page 0x00, regs 0xA6/A7/A8) |
|
||||
|
||||
**Specific proof points:**
|
||||
- `SkyWalker1TunerFilter.cpp`, line 1070: `if(ulNewInnerFecType == BDA_FEC_VITERBI)` -- only Viterbi accepted; any other FEC type returns `STATUS_INVALID_PARAMETER`
|
||||
- `SkyWalker1Control.cpp`, line 292: `ucCommand[8] = ADV_MOD_DVB_QPSK;` -- always sends modulation type 0
|
||||
- Firmware jump table at CODE:0866: bounds check rejects modulation values >= 10
|
||||
|
||||
## Is the USB Data Path a Bottleneck?
|
||||
|
||||
**No.** The GPIF/USB 2.0 streaming architecture has roughly 5x headroom for DVB-S2 data rates. The bottleneck is the demodulator silicon, not the transport path.
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| DVB-S2 max net rate (8PSK 9/10, 30 Msps) | ~58 Mbps (~7.25 MB/s) |
|
||||
| Typical HD transponder (8PSK 3/4, 27.5 Msps) | ~44 Mbps |
|
||||
| USB 2.0 practical bulk throughput | ~280 Mbps (~35 MB/s) |
|
||||
| GPIF engine theoretical throughput (48 MHz, 8-bit) | 384 Mbps (48 MB/s) |
|
||||
| Current DVB-S typical TS rate | 1--5 MB/s |
|
||||
|
||||
DVB-S2 uses the same MPEG-TS output format (188-byte packets) as DVB-S, so the GPIF waveform and AUTOIN configuration would work unchanged.
|
||||
|
||||
However, this is a moot point: even if the BCM4500 were physically replaced with a DVB-S2-capable chip, the entire FX2 firmware would need rewriting (I2C register protocol, tuning sequence, modulation dispatch, FEC configuration), since every DVB-S2 demodulator uses a completely different register interface.
|
||||
|
||||
## Broadcom's DVB-S2 Silicon Timeline
|
||||
|
||||
Broadcom addressed DVB-S2 by designing entirely new chips -- they did not add LDPC to the BCM4500:
|
||||
|
||||
| Chip | Year | DVB-S2 | Key Addition |
|
||||
|------|------|--------|-------------|
|
||||
| **BCM4500** | ~2003 | No | Turbo FEC + Viterbi/RS |
|
||||
| **BCM4501** | 2006 | **Yes** | First dual-tuner DVB-S2; LDPC/BCH decoder |
|
||||
| **BCM4505** | 2007 | **Yes** | Single-channel, 65nm, LDPC/BCH + legacy |
|
||||
| **BCM4506** | 2007 | **Yes** | Dual-channel, 65nm, LDPC/BCH + legacy |
|
||||
|
||||
The BCM4501 datasheet explicitly states it includes "four 8-bit ADCs, all-digital variable rate QPSK/8PSK receivers, advanced modulation LDPC/BCH, and DVB-S-compliant forward error correction decoder." Adding LDPC/BCH required new silicon.
|
||||
|
||||
<Aside type="note">
|
||||
Broadcom restricted sales of the BCM4501/4505/4506 to set-top box manufacturers (EchoStar, DIRECTV). They did not sell to PC peripheral makers. This is why Genpix could not simply drop in a BCM4501 as a replacement.
|
||||
</Aside>
|
||||
|
||||
## What Genpix Did: The SkyWalker-3
|
||||
|
||||
Genpix released the SkyWalker-3 as a DVB-S2-capable successor using a completely different demodulator (likely STMicroelectronics STV0903):
|
||||
|
||||
| Feature | SkyWalker-1 (BCM4500) | SkyWalker-3 (likely STV0903) |
|
||||
|---------|----------------------|---------------------------|
|
||||
| DVB-S QPSK | Yes | Yes |
|
||||
| DVB-S2 QPSK | **No** | Yes |
|
||||
| DVB-S2 8PSK | **No** | Yes |
|
||||
| Turbo QPSK | Yes | **No** |
|
||||
| Turbo 8PSK | Yes | **No** |
|
||||
| Turbo 16QAM | Yes | **No** |
|
||||
| DCII | Yes | Yes |
|
||||
| DSS | Yes | Yes |
|
||||
| Symbol rate (DVB-S) | 256 Ksps -- 30 Msps | 1 -- 45 Msps |
|
||||
| Symbol rate (DVB-S2) | N/A | 5 -- 33 Msps |
|
||||
| FEC inner (DVB-S) | Viterbi | Viterbi |
|
||||
| FEC inner (DVB-S2) | N/A | LDPC |
|
||||
| FEC outer (DVB-S2) | N/A | BCH |
|
||||
|
||||
The trade-off is clear: the SkyWalker-3 gained DVB-S2 but **lost turbo-FEC support entirely**. The turbo codes were proprietary to Broadcom/EchoStar, and the STV0903 does not implement them. This means the SkyWalker-3 cannot receive Dish Network's legacy turbo-coded 8PSK transmissions.
|
||||
|
||||
## Summary
|
||||
|
||||
| Question | Answer |
|
||||
|----------|--------|
|
||||
| Is DVB-S2 a hardware or firmware limitation? | **Hardware** -- BCM4500 has no LDPC/BCH decoder |
|
||||
| Could a firmware update add DVB-S2? | **No** -- LDPC requires dedicated silicon |
|
||||
| Which Broadcom chip first added LDPC? | **BCM4501** (2006) |
|
||||
| Any DVB-S2 hints in firmware/driver? | **None** -- zero references anywhere |
|
||||
| Is the USB data path a bottleneck? | **No** -- ~5x headroom for DVB-S2 rates |
|
||||
| What did Genpix do for DVB-S2? | Released SkyWalker-3 with STV0903 demodulator |
|
||||
| What was lost in the SkyWalker-3? | Turbo-FEC support (Broadcom/EchoStar proprietary) |
|
||||
---
|
||||
title: DVB-S2 Incompatibility
|
||||
description: Why the SkyWalker-1 cannot support DVB-S2 and what the BCM4500 demodulator actually provides.
|
||||
---
|
||||
|
||||
import { Badge, Aside, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
<Aside type="danger">
|
||||
**DVB-S2 is not supported on the SkyWalker-1.** This is a fundamental hardware limitation of the Broadcom BCM4500 demodulator silicon, not a firmware limitation. No firmware update can add DVB-S2 capability. The BCM4500 was designed and fabricated before the DVB-S2 standard was ratified (March 2005) and contains no LDPC or BCH decoder hardware.
|
||||
</Aside>
|
||||
|
||||
## The Core Problem
|
||||
|
||||
DVB-S2 (ETSI EN 302 307) requires two forward error correction technologies that do not exist in the BCM4500:
|
||||
|
||||
| FEC Component | BCM4500 Has | DVB-S2 Requires |
|
||||
|---------------|-------------|-----------------|
|
||||
| Inner code | Viterbi (convolutional) + Turbo | **LDPC** (Low-Density Parity-Check) |
|
||||
| Outer code | Reed-Solomon (t=10) | **BCH** (Bose-Chaudhuri-Hocquenghem) |
|
||||
| Block size | Streaming (Viterbi) or short turbo blocks | **64,800 or 16,200 bits** |
|
||||
| Decoder type | Trellis-based / iterative turbo | **Iterative belief propagation** |
|
||||
|
||||
LDPC decoding requires dedicated silicon: large block RAM for message passing (the LDPC block is 64,800 bits), iterative belief propagation logic, and a fundamentally different decoder architecture. This cannot be emulated in firmware on the BCM4500's simple 8-bit on-chip microcontroller, which handles only configuration, acquisition, and monitoring -- not data-path processing.
|
||||
|
||||
## BCM4500 FEC Architecture
|
||||
|
||||
The BCM4500 contains exactly two FEC decoder paths (from the [BCM4500 datasheet](https://elcodis.com/parts/5786421/BCM4500.html)):
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Legacy FEC">
|
||||
|
||||
### Viterbi + Reed-Solomon (Legacy)
|
||||
|
||||
Used for DVB-S QPSK, DSS QPSK, DVB-S BPSK, and Digicipher II modes.
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| Inner decoder | Viterbi (convolutional) |
|
||||
| Code rates | 1/2, 2/3, 3/4, 5/6, 7/8 |
|
||||
| Outer decoder | Reed-Solomon |
|
||||
| Signal path | Soft decisions fed to Viterbi, then RS outer code |
|
||||
| Modulations | BPSK, QPSK |
|
||||
|
||||
**Firmware evidence** (XRAM 0xE0F9): FEC lookup table with max index 7. Modulation dispatch sets `XRAM 0xE0F6 = 0x00` (turbo flag OFF), `XRAM 0xE0F5 = 0x10` (standard demod mode).
|
||||
|
||||
**Windows driver**: `m_CurResource.ulInnerFecType = BDA_FEC_VITERBI` -- explicitly rejects any FEC type other than Viterbi.
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="Turbo FEC">
|
||||
|
||||
### Turbo Code Decoder (Proprietary)
|
||||
|
||||
Used for Turbo QPSK, Turbo 8PSK, and Turbo 16QAM -- proprietary "advanced modulation" modes developed by Broadcom for EchoStar/Dish Network.
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| Inner decoder | Iterative turbo code |
|
||||
| QPSK rates | 1/4, 1/2, 3/4 |
|
||||
| 8PSK rates | 2/3, 3/4, 5/6, 8/9 |
|
||||
| 16QAM rates | 3/4 |
|
||||
| Outer decoder | Reed-Solomon (t=10) |
|
||||
| Modulations | QPSK, 8PSK, 16QAM |
|
||||
|
||||
**Firmware evidence** (XRAM 0xE0B7, 0xE0B1, 0xE0BC): Turbo FEC lookup tables. All turbo modes set `XRAM 0xE0F6 = 0x01` (turbo flag ON).
|
||||
|
||||
These turbo codes are NOT the same as DVB-S2's LDPC codes. The turbo decoder uses parallel concatenated convolutional codes, while LDPC uses sparse parity-check matrix belief propagation. Different algorithms, different silicon.
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="Digicipher II">
|
||||
|
||||
### DCII Decoder
|
||||
|
||||
Used for DCII combo, split I/Q, and offset QPSK modes.
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| FEC table | XRAM 0xE0BD, max index 9 |
|
||||
| Fixed FEC code | `0xFC` written to XRAM 0xE0EB |
|
||||
| Modulations | QPSK variants (combo, split, offset) |
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
The BCM4500 datasheet states explicitly: "Optimized soft decisions are then fed into either a DVB/DIRECTV/DCII-compliant FEC decoder, or an advanced modulation turbo decoder." These are the only two FEC paths. There is no third path for LDPC/BCH.
|
||||
|
||||
## Zero DVB-S2 Evidence in Firmware or Driver
|
||||
|
||||
Exhaustive search across all firmware versions and Windows driver source:
|
||||
|
||||
| What Was Searched | Result |
|
||||
|-------------------|--------|
|
||||
| All firmware binaries (v2.06, Rev.2, v2.13) via Ghidra | No LDPC/BCH/DVB-S2 references |
|
||||
| Windows driver `SkyWalker1Control.h` | Modulation constants 0--9 only, none for DVB-S2 |
|
||||
| Windows driver `SkyWalker1TunerFilter.cpp` | Rejects non-Viterbi FEC types |
|
||||
| Windows driver `SkyWalker1Control.cpp` | Hardcodes `ADV_MOD_DVB_QPSK` (value 0) |
|
||||
| Firmware dispatch table (CODE:0873) | 10 entries max, values >= 10 rejected |
|
||||
| All FEC lookup tables in XRAM | Only Viterbi rates and turbo rates, no LDPC rates |
|
||||
| I2C register addresses | BCM4500-specific protocol only (page 0x00, regs 0xA6/A7/A8) |
|
||||
|
||||
**Specific proof points:**
|
||||
- `SkyWalker1TunerFilter.cpp`, line 1070: `if(ulNewInnerFecType == BDA_FEC_VITERBI)` -- only Viterbi accepted; any other FEC type returns `STATUS_INVALID_PARAMETER`
|
||||
- `SkyWalker1Control.cpp`, line 292: `ucCommand[8] = ADV_MOD_DVB_QPSK;` -- always sends modulation type 0
|
||||
- Firmware jump table at CODE:0866: bounds check rejects modulation values >= 10
|
||||
|
||||
## Is the USB Data Path a Bottleneck?
|
||||
|
||||
**No.** The GPIF/USB 2.0 streaming architecture has roughly 5x headroom for DVB-S2 data rates. The bottleneck is the demodulator silicon, not the transport path.
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| DVB-S2 max net rate (8PSK 9/10, 30 Msps) | ~58 Mbps (~7.25 MB/s) |
|
||||
| Typical HD transponder (8PSK 3/4, 27.5 Msps) | ~44 Mbps |
|
||||
| USB 2.0 practical bulk throughput | ~280 Mbps (~35 MB/s) |
|
||||
| GPIF engine theoretical throughput (48 MHz, 8-bit) | 384 Mbps (48 MB/s) |
|
||||
| Current DVB-S typical TS rate | 1--5 MB/s |
|
||||
|
||||
DVB-S2 uses the same MPEG-TS output format (188-byte packets) as DVB-S, so the GPIF waveform and AUTOIN configuration would work unchanged.
|
||||
|
||||
However, this is a moot point: even if the BCM4500 were physically replaced with a DVB-S2-capable chip, the entire FX2 firmware would need rewriting (I2C register protocol, tuning sequence, modulation dispatch, FEC configuration), since every DVB-S2 demodulator uses a completely different register interface.
|
||||
|
||||
## Broadcom's DVB-S2 Silicon Timeline
|
||||
|
||||
Broadcom addressed DVB-S2 by designing entirely new chips -- they did not add LDPC to the BCM4500:
|
||||
|
||||
| Chip | Year | DVB-S2 | Key Addition |
|
||||
|------|------|--------|-------------|
|
||||
| **BCM4500** | ~2003 | No | Turbo FEC + Viterbi/RS |
|
||||
| **BCM4501** | 2006 | **Yes** | First dual-tuner DVB-S2; LDPC/BCH decoder |
|
||||
| **BCM4505** | 2007 | **Yes** | Single-channel, 65nm, LDPC/BCH + legacy |
|
||||
| **BCM4506** | 2007 | **Yes** | Dual-channel, 65nm, LDPC/BCH + legacy |
|
||||
|
||||
The BCM4501 datasheet explicitly states it includes "four 8-bit ADCs, all-digital variable rate QPSK/8PSK receivers, advanced modulation LDPC/BCH, and DVB-S-compliant forward error correction decoder." Adding LDPC/BCH required new silicon.
|
||||
|
||||
<Aside type="note">
|
||||
Broadcom restricted sales of the BCM4501/4505/4506 to set-top box manufacturers (EchoStar, DIRECTV). They did not sell to PC peripheral makers. This is why Genpix could not simply drop in a BCM4501 as a replacement.
|
||||
</Aside>
|
||||
|
||||
## What Genpix Did: The SkyWalker-3
|
||||
|
||||
Genpix released the SkyWalker-3 as a DVB-S2-capable successor using a completely different demodulator (likely STMicroelectronics STV0903):
|
||||
|
||||
| Feature | SkyWalker-1 (BCM4500) | SkyWalker-3 (likely STV0903) |
|
||||
|---------|----------------------|---------------------------|
|
||||
| DVB-S QPSK | Yes | Yes |
|
||||
| DVB-S2 QPSK | **No** | Yes |
|
||||
| DVB-S2 8PSK | **No** | Yes |
|
||||
| Turbo QPSK | Yes | **No** |
|
||||
| Turbo 8PSK | Yes | **No** |
|
||||
| Turbo 16QAM | Yes | **No** |
|
||||
| DCII | Yes | Yes |
|
||||
| DSS | Yes | Yes |
|
||||
| Symbol rate (DVB-S) | 256 Ksps -- 30 Msps | 1 -- 45 Msps |
|
||||
| Symbol rate (DVB-S2) | N/A | 5 -- 33 Msps |
|
||||
| FEC inner (DVB-S) | Viterbi | Viterbi |
|
||||
| FEC inner (DVB-S2) | N/A | LDPC |
|
||||
| FEC outer (DVB-S2) | N/A | BCH |
|
||||
|
||||
The trade-off is clear: the SkyWalker-3 gained DVB-S2 but **lost turbo-FEC support entirely**. The turbo codes were proprietary to Broadcom/EchoStar, and the STV0903 does not implement them. This means the SkyWalker-3 cannot receive Dish Network's legacy turbo-coded 8PSK transmissions.
|
||||
|
||||
## Summary
|
||||
|
||||
| Question | Answer |
|
||||
|----------|--------|
|
||||
| Is DVB-S2 a hardware or firmware limitation? | **Hardware** -- BCM4500 has no LDPC/BCH decoder |
|
||||
| Could a firmware update add DVB-S2? | **No** -- LDPC requires dedicated silicon |
|
||||
| Which Broadcom chip first added LDPC? | **BCM4501** (2006) |
|
||||
| Any DVB-S2 hints in firmware/driver? | **None** -- zero references anywhere |
|
||||
| Is the USB data path a bottleneck? | **No** -- ~5x headroom for DVB-S2 rates |
|
||||
| What did Genpix do for DVB-S2? | Released SkyWalker-3 with STV0903 demodulator |
|
||||
| What was lost in the SkyWalker-3? | Turbo-FEC support (Broadcom/EchoStar proprietary) |
|
||||
|
|
|
|||
|
|
@ -1,272 +1,272 @@
|
|||
---
|
||||
title: Linux Kernel Driver
|
||||
description: Architecture and command analysis of the dvb_usb_gp8psk kernel module for Genpix satellite receivers.
|
||||
---
|
||||
|
||||
import { Steps, Badge, Aside, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
The `dvb_usb_gp8psk` module is a Linux kernel DVB-USB driver supporting multiple Genpix satellite receiver models. It communicates with the FX2 microcontroller via USB vendor control requests to manage tuning, demodulation, LNB control, DiSEqC switching, and transport stream capture.
|
||||
|
||||
**Source files**: `drivers/media/usb/dvb-usb/gp8psk.c`, `gp8psk.h`, `gp8psk-fe.c`
|
||||
|
||||
## USB Device Table
|
||||
|
||||
| USB ID | Device Name | cold_ids | warm_ids | FW01 Needed? |
|
||||
|--------|-------------|----------|----------|-------------|
|
||||
| `v09C0p0200` | Rev.1 Cold | Yes | -- | **Yes** |
|
||||
| `v09C0p0201` | Rev.1 Warm | -- | Yes | No (FW02 needed) |
|
||||
| `v09C0p0202` | Rev.2 | -- | Yes | No |
|
||||
| `v09C0p0203` | SkyWalker-1 | -- | Yes | No |
|
||||
| `v09C0p0204` | SkyWalker-1 (alt) | -- | Yes | No |
|
||||
| `v09C0p0206` | SkyWalker CW3K | -- | Yes | No |
|
||||
|
||||
<Aside type="note">
|
||||
PID `0x0205` (SkyWalker-2) is absent from the kernel 6.16.5 build. PID `0x0203` was not present in earlier kernel versions (e.g., v6.6.1) -- it was added later as the SkyWalker-1 gained community attention.
|
||||
</Aside>
|
||||
|
||||
## Driver Architecture
|
||||
|
||||
The driver consists of three source files:
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `gp8psk.c` | USB device management, firmware loading, power control, vendor command wrappers |
|
||||
| `gp8psk.h` | Vendor command constants, firmware version thresholds, USB PID definitions |
|
||||
| `gp8psk-fe.c` | DVB frontend implementation: tuning, signal monitoring, LNB/DiSEqC callbacks |
|
||||
|
||||
The driver registers with the DVB-USB framework (`dvb_usb_device_properties`) which handles:
|
||||
- USB device enumeration and firmware download (for cold devices)
|
||||
- DVB adapter and frontend creation
|
||||
- URB management for bulk transport stream capture
|
||||
- Power management callbacks
|
||||
|
||||
## USB Transfer Parameters
|
||||
|
||||
```c title="Driver USB configuration"
|
||||
gp8psk_properties {
|
||||
.usb_ctrl = CYPRESS_FX2;
|
||||
.firmware = "dvb-usb-gp8psk-01.fw";
|
||||
.num_adapters = 1;
|
||||
.generic_bulk_ctrl_endpoint = 0x01;
|
||||
// Streaming:
|
||||
.endpoint = 0x82; // IN bulk
|
||||
.stream = USB_BULK;
|
||||
.count = 7; // URBs
|
||||
.buffersize = 8192; // bytes per URB
|
||||
}
|
||||
```
|
||||
|
||||
The driver allocates 7 URBs of 8192 bytes each (56 KB total) for transport stream reception on endpoint `0x82` (IN bulk). Vendor commands use endpoint `0x01` with a 2000 ms timeout.
|
||||
|
||||
### Retry Logic
|
||||
|
||||
IN operations retry up to 3 times if partial data is received. The command buffer is 80 bytes maximum:
|
||||
|
||||
```c title="Vendor command wrapper"
|
||||
static int gp8psk_usb_in_op(struct dvb_usb_device *d,
|
||||
u8 req, u16 value, u16 index, u8 *b, int blen)
|
||||
{
|
||||
int ret;
|
||||
int try;
|
||||
for (try = 0; try < 3; try++) {
|
||||
ret = usb_control_msg(d->udev,
|
||||
usb_rcvctrlpipe(d->udev, 0),
|
||||
req, USB_TYPE_VENDOR | USB_DIR_IN,
|
||||
value, index, b, blen, 2000);
|
||||
if (ret == blen) break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
## Complete Vendor Command Map
|
||||
|
||||
| Cmd | Name | Dir | wValue | wLength | Purpose |
|
||||
|-----|------|-----|--------|---------|---------|
|
||||
| `0x80` | GET_8PSK_CONFIG | IN | `0x0000` | 1 | Read config status byte |
|
||||
| `0x81` | SET_8PSK_CONFIG | OUT | varies | 0 | STALL (not implemented) |
|
||||
| `0x83` | I2C_WRITE | OUT | dev_addr | N | Write to BCM4500 via I2C |
|
||||
| `0x84` | I2C_READ | IN | dev_addr | N | Read from BCM4500 via I2C |
|
||||
| `0x85` | ARM_TRANSFER | OUT | 0/1 | 0 | Start/stop TS streaming |
|
||||
| `0x86` | TUNE_8PSK | OUT | `0x0000` | 10 | Send tuning parameters |
|
||||
| `0x87` | GET_SIGNAL_STRENGTH | IN | `0x0000` | 6 | Read SNR values |
|
||||
| `0x88` | LOAD_BCM4500 | OUT | 1 | 0 | Initiate demod FW download |
|
||||
| `0x89` | BOOT_8PSK | IN | 0/1 | 1 | Power on/off demodulator |
|
||||
| `0x8A` | START_INTERSIL | IN | 0/1 | 1 | Enable/disable LNB supply |
|
||||
| `0x8B` | SET_LNB_VOLTAGE | OUT | 0/1 | 0 | Set 13V (0) or 18V (1) |
|
||||
| `0x8C` | SET_22KHZ_TONE | OUT | 0/1 | 0 | Enable/disable 22 kHz tone |
|
||||
| `0x8D` | SEND_DISEQC | OUT | msg[0] | 3-6 | Send DiSEqC message |
|
||||
| `0x8E` | SET_DVB_MODE | OUT | 1 | 0 | Enable DVB-S mode |
|
||||
| `0x8F` | SET_DN_SWITCH | OUT | cmd | 0 | Legacy Dish switch command |
|
||||
| `0x90` | GET_SIGNAL_LOCK | IN | `0x0000` | 1 | Read lock status |
|
||||
| `0x92` | GET_FW_VERS | IN | `0x0000` | 6 | Read firmware version |
|
||||
| `0x94` | USE_EXTRA_VOLT | OUT | 0/1 | 0 | Enable +1V LNB boost |
|
||||
| `0x95` | GET_FPGA_VERS | IN | `0x0000` | 1 | Read hardware platform ID |
|
||||
| `0x99` | GET_DEMOD_STATUS | IN | `0x0000` | 1 | Read BCM4500 reg 0xF9 (v2.13+) |
|
||||
| `0x9A` | INIT_DEMOD | OUT | `0x0000` | 0 | Re-init demodulator (v2.13+) |
|
||||
| `0x9C` | DELAY_COMMAND | OUT | param | 0 | Tuning delay with polling (v2.13+) |
|
||||
| `0x9D` | CW3K_INIT | OUT | 0/1 | 0 | CW3K model initialization |
|
||||
|
||||
## Configuration Status Byte
|
||||
|
||||
`GET_8PSK_CONFIG` (`0x80`) returns a bit-mapped status register:
|
||||
|
||||
```
|
||||
Bit 0 (0x01): bm8pskStarted - Device booted and running
|
||||
Bit 1 (0x02): bm8pskFW_Loaded - BCM4500 firmware loaded
|
||||
Bit 2 (0x04): bmIntersilOn - LNB power supply enabled
|
||||
Bit 3 (0x08): bmDVBmode - DVB mode enabled
|
||||
Bit 4 (0x10): bm22kHz - 22 kHz tone active
|
||||
Bit 5 (0x20): bmSEL18V - 18V LNB voltage selected
|
||||
Bit 6 (0x40): bmDCtuned - DC offset tuning complete
|
||||
Bit 7 (0x80): bmArmed - MPEG-2 stream transfer armed
|
||||
```
|
||||
|
||||
## Boot Sequence
|
||||
|
||||
<Steps>
|
||||
|
||||
1. **Read config** (`GET_8PSK_CONFIG 0x80`): Check bit 0 (`bm8pskStarted`)
|
||||
|
||||
2. **Boot device** if not started: Send `BOOT_8PSK 0x89` with `wValue=1`, then read firmware version via `GET_FW_VERS 0x92`
|
||||
|
||||
3. **Load BCM4500 firmware** if bit 1 not set: Only for Rev.1 Warm (PID `0x0201`). Send `LOAD_BCM4500 0x88` followed by firmware chunks. Skipped for SkyWalker-1 (bit already set from EEPROM boot).
|
||||
|
||||
4. **Enable LNB** if bit 2 not set: Send `START_INTERSIL 0x8A` with `wValue=1`
|
||||
|
||||
5. **Set DVB mode**: Send `SET_DVB_MODE 0x8E` with `wValue=1` (STALL on some revisions)
|
||||
|
||||
6. **Cancel pending stream**: Send `ARM_TRANSFER 0x85` with `wValue=0`
|
||||
|
||||
7. **Ready for tuning**
|
||||
|
||||
</Steps>
|
||||
|
||||
## Tuning Flow
|
||||
|
||||
```c title="10-byte tuning payload"
|
||||
Bytes 0-3: Symbol Rate (u32 LE, in sps)
|
||||
Bytes 4-7: Frequency (u32 LE, in kHz)
|
||||
Byte 8: Modulation (0-9, see modulation types)
|
||||
Byte 9: FEC Rate (index into firmware FEC table)
|
||||
```
|
||||
|
||||
### Modulation Types
|
||||
|
||||
| Value | Constant | Mode |
|
||||
|-------|----------|------|
|
||||
| 0 | `ADV_MOD_DVB_QPSK` | DVB-S QPSK |
|
||||
| 1 | `ADV_MOD_TURBO_QPSK` | Turbo QPSK |
|
||||
| 2 | `ADV_MOD_TURBO_8PSK` | Turbo 8PSK |
|
||||
| 3 | `ADV_MOD_TURBO_16QAM` | Turbo 16QAM |
|
||||
| 4 | `ADV_MOD_DCII_C_QPSK` | Digicipher II Combo |
|
||||
| 5 | `ADV_MOD_DCII_I_QPSK` | Digicipher II I-stream |
|
||||
| 6 | `ADV_MOD_DCII_Q_QPSK` | Digicipher II Q-stream |
|
||||
| 7 | `ADV_MOD_DCII_C_OQPSK` | Digicipher II Offset QPSK |
|
||||
| 8 | `ADV_MOD_DSS_QPSK` | DSS/DIRECTV QPSK |
|
||||
| 9 | `ADV_MOD_DVB_BPSK` | DVB-S BPSK |
|
||||
|
||||
### Signal Quality
|
||||
|
||||
`GET_SIGNAL_STRENGTH` (`0x87`) returns 6 bytes:
|
||||
|
||||
```
|
||||
Bytes 0-1: SNR value (u16 LE, in dBu*256 units)
|
||||
Bytes 2-5: Reserved / diagnostics
|
||||
```
|
||||
|
||||
SNR scaling in the kernel: `snr_value * 17` maps to the 0--65535 range. 100% signal quality corresponds to SNR >= `0x0F00`.
|
||||
|
||||
## Firmware Version Handling
|
||||
|
||||
The driver defines two version constants in `gp8psk-fe.h`:
|
||||
|
||||
```c title="Firmware version thresholds"
|
||||
#define GP8PSK_FW_REV1 0x020604 // v2.06.4
|
||||
#define GP8PSK_FW_REV2 0x020704 // v2.07.4
|
||||
```
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="< FW_REV1">
|
||||
Oldest firmware. No extended commands available.
|
||||
</TabItem>
|
||||
<TabItem label=">= FW_REV1">
|
||||
v2.06.4 baseline. All standard commands operational. Uses `GET_SIGNAL_STRENGTH` for BER monitoring.
|
||||
</TabItem>
|
||||
<TabItem label=">= FW_REV2">
|
||||
v2.07.4+. Enables additional code paths:
|
||||
- `GET_DEMOD_STATUS` (`0x99`) for demod health check
|
||||
- `INIT_DEMOD` (`0x9A`) for demod re-initialization
|
||||
- `DELAY_COMMAND` (`0x9C`) for tuning acquisition delays
|
||||
- Different signal quality calculation
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Command Correlation with Firmware
|
||||
|
||||
Not all vendor commands work on all firmware versions. The STALL behavior varies:
|
||||
|
||||
| Command | v2.06 FW | v2.13 FW | Rev.2 FW | Custom v3.01 |
|
||||
|---------|----------|----------|----------|-------------|
|
||||
| `0x80` GET_8PSK_CONFIG | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| `0x86` TUNE_8PSK | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| `0x87` GET_SIGNAL_STRENGTH | <Badge text="OK" variant="success" /> | <Badge text="Changed" variant="caution" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| `0x88` LOAD_BCM4500 | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> |
|
||||
| `0x99` GET_DEMOD_STATUS | <Badge text="STALL" variant="danger" /> | <Badge text="OK" variant="success" /> | <Badge text="Proto" variant="caution" /> | N/A |
|
||||
| `0x9A` INIT_DEMOD | <Badge text="STALL" variant="danger" /> | <Badge text="OK" variant="success" /> | <Badge text="Proto" variant="caution" /> | N/A |
|
||||
| `0x9C` DELAY_COMMAND | <Badge text="STALL" variant="danger" /> | <Badge text="OK" variant="success" /> | N/A | N/A |
|
||||
|
||||
<Aside type="note">
|
||||
Command `0x88` (`LOAD_BCM4500`) routes to the STALL handler in **all** extracted firmware versions. The kernel only sends it for Rev.1 Warm devices (PID `0x0201`) after confirming `bm8pskFW_Loaded` is not set. On SkyWalker-1 hardware, the BCM4500 firmware is in ROM, so this bit is always set at boot.
|
||||
</Aside>
|
||||
|
||||
## Kernel Module Parameters
|
||||
|
||||
The gp8psk module inherits standard DVB-USB parameters:
|
||||
|
||||
| Parameter | Default | Description |
|
||||
|-----------|---------|-------------|
|
||||
| `debug` | 0 | Enable debug logging (bitmask) |
|
||||
| `force_pid_filter` | 0 | Force PID filtering on/off |
|
||||
| `generic_bulk_ctrl_endpoint` | `0x01` | Control endpoint |
|
||||
|
||||
Enable verbose logging with:
|
||||
|
||||
```bash
|
||||
modprobe dvb_usb_gp8psk debug=0xff
|
||||
```
|
||||
|
||||
Or at runtime:
|
||||
|
||||
```bash
|
||||
echo 0xff > /sys/module/dvb_usb_gp8psk/parameters/debug
|
||||
```
|
||||
|
||||
## DVB Frontend Properties
|
||||
|
||||
The gp8psk frontend (`gp8psk-fe.c`) registers with the following capabilities:
|
||||
|
||||
```c title="Frontend info structure"
|
||||
.name = "Genpix 8psk-to-USB2 DVB-S"
|
||||
.frequency_min_hz = 800 * MHz
|
||||
.frequency_max_hz = 2250 * MHz
|
||||
.frequency_stepsize_hz = 100 * kHz
|
||||
.symbol_rate_min = 256000 // 256 Ksps
|
||||
.symbol_rate_max = 30000000 // 30 Msps
|
||||
.caps = FE_CAN_INVERSION_AUTO |
|
||||
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
|
||||
FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 |
|
||||
FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
|
||||
FE_CAN_QPSK
|
||||
```
|
||||
|
||||
<Aside type="note">
|
||||
The frontend capabilities only advertise `FE_CAN_QPSK` -- the turbo 8PSK, turbo 16QAM, and DCII modes are not exposed through the standard DVB API. Accessing these modes requires direct vendor command access or the Windows BDA driver.
|
||||
</Aside>
|
||||
|
||||
## Related Pages
|
||||
|
||||
- [Vendor Commands](/usb/vendor-commands/) -- Complete command reference
|
||||
- [Kernel FW01](/firmware/kernel-fw01/) -- Firmware format and loading analysis
|
||||
- [DVB-S2 Investigation](/driver/dvb-s2/) -- Why DVB-S2 cannot be added
|
||||
- [Custom Firmware v3.01](/firmware/custom-v301/) -- Open-source replacement with extended commands
|
||||
---
|
||||
title: Linux Kernel Driver
|
||||
description: Architecture and command analysis of the dvb_usb_gp8psk kernel module for Genpix satellite receivers.
|
||||
---
|
||||
|
||||
import { Steps, Badge, Aside, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
The `dvb_usb_gp8psk` module is a Linux kernel DVB-USB driver supporting multiple Genpix satellite receiver models. It communicates with the FX2 microcontroller via USB vendor control requests to manage tuning, demodulation, LNB control, DiSEqC switching, and transport stream capture.
|
||||
|
||||
**Source files**: `drivers/media/usb/dvb-usb/gp8psk.c`, `gp8psk.h`, `gp8psk-fe.c`
|
||||
|
||||
## USB Device Table
|
||||
|
||||
| USB ID | Device Name | cold_ids | warm_ids | FW01 Needed? |
|
||||
|--------|-------------|----------|----------|-------------|
|
||||
| `v09C0p0200` | Rev.1 Cold | Yes | -- | **Yes** |
|
||||
| `v09C0p0201` | Rev.1 Warm | -- | Yes | No (FW02 needed) |
|
||||
| `v09C0p0202` | Rev.2 | -- | Yes | No |
|
||||
| `v09C0p0203` | SkyWalker-1 | -- | Yes | No |
|
||||
| `v09C0p0204` | SkyWalker-1 (alt) | -- | Yes | No |
|
||||
| `v09C0p0206` | SkyWalker CW3K | -- | Yes | No |
|
||||
|
||||
<Aside type="note">
|
||||
PID `0x0205` (SkyWalker-2) is absent from the kernel 6.16.5 build. PID `0x0203` was not present in earlier kernel versions (e.g., v6.6.1) -- it was added later as the SkyWalker-1 gained community attention.
|
||||
</Aside>
|
||||
|
||||
## Driver Architecture
|
||||
|
||||
The driver consists of three source files:
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `gp8psk.c` | USB device management, firmware loading, power control, vendor command wrappers |
|
||||
| `gp8psk.h` | Vendor command constants, firmware version thresholds, USB PID definitions |
|
||||
| `gp8psk-fe.c` | DVB frontend implementation: tuning, signal monitoring, LNB/DiSEqC callbacks |
|
||||
|
||||
The driver registers with the DVB-USB framework (`dvb_usb_device_properties`) which handles:
|
||||
- USB device enumeration and firmware download (for cold devices)
|
||||
- DVB adapter and frontend creation
|
||||
- URB management for bulk transport stream capture
|
||||
- Power management callbacks
|
||||
|
||||
## USB Transfer Parameters
|
||||
|
||||
```c title="Driver USB configuration"
|
||||
gp8psk_properties {
|
||||
.usb_ctrl = CYPRESS_FX2;
|
||||
.firmware = "dvb-usb-gp8psk-01.fw";
|
||||
.num_adapters = 1;
|
||||
.generic_bulk_ctrl_endpoint = 0x01;
|
||||
// Streaming:
|
||||
.endpoint = 0x82; // IN bulk
|
||||
.stream = USB_BULK;
|
||||
.count = 7; // URBs
|
||||
.buffersize = 8192; // bytes per URB
|
||||
}
|
||||
```
|
||||
|
||||
The driver allocates 7 URBs of 8192 bytes each (56 KB total) for transport stream reception on endpoint `0x82` (IN bulk). Vendor commands use endpoint `0x01` with a 2000 ms timeout.
|
||||
|
||||
### Retry Logic
|
||||
|
||||
IN operations retry up to 3 times if partial data is received. The command buffer is 80 bytes maximum:
|
||||
|
||||
```c title="Vendor command wrapper"
|
||||
static int gp8psk_usb_in_op(struct dvb_usb_device *d,
|
||||
u8 req, u16 value, u16 index, u8 *b, int blen)
|
||||
{
|
||||
int ret;
|
||||
int try;
|
||||
for (try = 0; try < 3; try++) {
|
||||
ret = usb_control_msg(d->udev,
|
||||
usb_rcvctrlpipe(d->udev, 0),
|
||||
req, USB_TYPE_VENDOR | USB_DIR_IN,
|
||||
value, index, b, blen, 2000);
|
||||
if (ret == blen) break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
## Complete Vendor Command Map
|
||||
|
||||
| Cmd | Name | Dir | wValue | wLength | Purpose |
|
||||
|-----|------|-----|--------|---------|---------|
|
||||
| `0x80` | GET_8PSK_CONFIG | IN | `0x0000` | 1 | Read config status byte |
|
||||
| `0x81` | SET_8PSK_CONFIG | OUT | varies | 0 | STALL (not implemented) |
|
||||
| `0x83` | I2C_WRITE | OUT | dev_addr | N | Write to BCM4500 via I2C |
|
||||
| `0x84` | I2C_READ | IN | dev_addr | N | Read from BCM4500 via I2C |
|
||||
| `0x85` | ARM_TRANSFER | OUT | 0/1 | 0 | Start/stop TS streaming |
|
||||
| `0x86` | TUNE_8PSK | OUT | `0x0000` | 10 | Send tuning parameters |
|
||||
| `0x87` | GET_SIGNAL_STRENGTH | IN | `0x0000` | 6 | Read SNR values |
|
||||
| `0x88` | LOAD_BCM4500 | OUT | 1 | 0 | Initiate demod FW download |
|
||||
| `0x89` | BOOT_8PSK | IN | 0/1 | 1 | Power on/off demodulator |
|
||||
| `0x8A` | START_INTERSIL | IN | 0/1 | 1 | Enable/disable LNB supply |
|
||||
| `0x8B` | SET_LNB_VOLTAGE | OUT | 0/1 | 0 | Set 13V (0) or 18V (1) |
|
||||
| `0x8C` | SET_22KHZ_TONE | OUT | 0/1 | 0 | Enable/disable 22 kHz tone |
|
||||
| `0x8D` | SEND_DISEQC | OUT | msg[0] | 3-6 | Send DiSEqC message |
|
||||
| `0x8E` | SET_DVB_MODE | OUT | 1 | 0 | Enable DVB-S mode |
|
||||
| `0x8F` | SET_DN_SWITCH | OUT | cmd | 0 | Legacy Dish switch command |
|
||||
| `0x90` | GET_SIGNAL_LOCK | IN | `0x0000` | 1 | Read lock status |
|
||||
| `0x92` | GET_FW_VERS | IN | `0x0000` | 6 | Read firmware version |
|
||||
| `0x94` | USE_EXTRA_VOLT | OUT | 0/1 | 0 | Enable +1V LNB boost |
|
||||
| `0x95` | GET_FPGA_VERS | IN | `0x0000` | 1 | Read hardware platform ID |
|
||||
| `0x99` | GET_DEMOD_STATUS | IN | `0x0000` | 1 | Read BCM4500 reg 0xF9 (v2.13+) |
|
||||
| `0x9A` | INIT_DEMOD | OUT | `0x0000` | 0 | Re-init demodulator (v2.13+) |
|
||||
| `0x9C` | DELAY_COMMAND | OUT | param | 0 | Tuning delay with polling (v2.13+) |
|
||||
| `0x9D` | CW3K_INIT | OUT | 0/1 | 0 | CW3K model initialization |
|
||||
|
||||
## Configuration Status Byte
|
||||
|
||||
`GET_8PSK_CONFIG` (`0x80`) returns a bit-mapped status register:
|
||||
|
||||
```
|
||||
Bit 0 (0x01): bm8pskStarted - Device booted and running
|
||||
Bit 1 (0x02): bm8pskFW_Loaded - BCM4500 firmware loaded
|
||||
Bit 2 (0x04): bmIntersilOn - LNB power supply enabled
|
||||
Bit 3 (0x08): bmDVBmode - DVB mode enabled
|
||||
Bit 4 (0x10): bm22kHz - 22 kHz tone active
|
||||
Bit 5 (0x20): bmSEL18V - 18V LNB voltage selected
|
||||
Bit 6 (0x40): bmDCtuned - DC offset tuning complete
|
||||
Bit 7 (0x80): bmArmed - MPEG-2 stream transfer armed
|
||||
```
|
||||
|
||||
## Boot Sequence
|
||||
|
||||
<Steps>
|
||||
|
||||
1. **Read config** (`GET_8PSK_CONFIG 0x80`): Check bit 0 (`bm8pskStarted`)
|
||||
|
||||
2. **Boot device** if not started: Send `BOOT_8PSK 0x89` with `wValue=1`, then read firmware version via `GET_FW_VERS 0x92`
|
||||
|
||||
3. **Load BCM4500 firmware** if bit 1 not set: Only for Rev.1 Warm (PID `0x0201`). Send `LOAD_BCM4500 0x88` followed by firmware chunks. Skipped for SkyWalker-1 (bit already set from EEPROM boot).
|
||||
|
||||
4. **Enable LNB** if bit 2 not set: Send `START_INTERSIL 0x8A` with `wValue=1`
|
||||
|
||||
5. **Set DVB mode**: Send `SET_DVB_MODE 0x8E` with `wValue=1` (STALL on some revisions)
|
||||
|
||||
6. **Cancel pending stream**: Send `ARM_TRANSFER 0x85` with `wValue=0`
|
||||
|
||||
7. **Ready for tuning**
|
||||
|
||||
</Steps>
|
||||
|
||||
## Tuning Flow
|
||||
|
||||
```c title="10-byte tuning payload"
|
||||
Bytes 0-3: Symbol Rate (u32 LE, in sps)
|
||||
Bytes 4-7: Frequency (u32 LE, in kHz)
|
||||
Byte 8: Modulation (0-9, see modulation types)
|
||||
Byte 9: FEC Rate (index into firmware FEC table)
|
||||
```
|
||||
|
||||
### Modulation Types
|
||||
|
||||
| Value | Constant | Mode |
|
||||
|-------|----------|------|
|
||||
| 0 | `ADV_MOD_DVB_QPSK` | DVB-S QPSK |
|
||||
| 1 | `ADV_MOD_TURBO_QPSK` | Turbo QPSK |
|
||||
| 2 | `ADV_MOD_TURBO_8PSK` | Turbo 8PSK |
|
||||
| 3 | `ADV_MOD_TURBO_16QAM` | Turbo 16QAM |
|
||||
| 4 | `ADV_MOD_DCII_C_QPSK` | Digicipher II Combo |
|
||||
| 5 | `ADV_MOD_DCII_I_QPSK` | Digicipher II I-stream |
|
||||
| 6 | `ADV_MOD_DCII_Q_QPSK` | Digicipher II Q-stream |
|
||||
| 7 | `ADV_MOD_DCII_C_OQPSK` | Digicipher II Offset QPSK |
|
||||
| 8 | `ADV_MOD_DSS_QPSK` | DSS/DIRECTV QPSK |
|
||||
| 9 | `ADV_MOD_DVB_BPSK` | DVB-S BPSK |
|
||||
|
||||
### Signal Quality
|
||||
|
||||
`GET_SIGNAL_STRENGTH` (`0x87`) returns 6 bytes:
|
||||
|
||||
```
|
||||
Bytes 0-1: SNR value (u16 LE, in dBu*256 units)
|
||||
Bytes 2-5: Reserved / diagnostics
|
||||
```
|
||||
|
||||
SNR scaling in the kernel: `snr_value * 17` maps to the 0--65535 range. 100% signal quality corresponds to SNR >= `0x0F00`.
|
||||
|
||||
## Firmware Version Handling
|
||||
|
||||
The driver defines two version constants in `gp8psk-fe.h`:
|
||||
|
||||
```c title="Firmware version thresholds"
|
||||
#define GP8PSK_FW_REV1 0x020604 // v2.06.4
|
||||
#define GP8PSK_FW_REV2 0x020704 // v2.07.4
|
||||
```
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="< FW_REV1">
|
||||
Oldest firmware. No extended commands available.
|
||||
</TabItem>
|
||||
<TabItem label=">= FW_REV1">
|
||||
v2.06.4 baseline. All standard commands operational. Uses `GET_SIGNAL_STRENGTH` for BER monitoring.
|
||||
</TabItem>
|
||||
<TabItem label=">= FW_REV2">
|
||||
v2.07.4+. Enables additional code paths:
|
||||
- `GET_DEMOD_STATUS` (`0x99`) for demod health check
|
||||
- `INIT_DEMOD` (`0x9A`) for demod re-initialization
|
||||
- `DELAY_COMMAND` (`0x9C`) for tuning acquisition delays
|
||||
- Different signal quality calculation
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Command Correlation with Firmware
|
||||
|
||||
Not all vendor commands work on all firmware versions. The STALL behavior varies:
|
||||
|
||||
| Command | v2.06 FW | v2.13 FW | Rev.2 FW | Custom v3.01 |
|
||||
|---------|----------|----------|----------|-------------|
|
||||
| `0x80` GET_8PSK_CONFIG | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| `0x86` TUNE_8PSK | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| `0x87` GET_SIGNAL_STRENGTH | <Badge text="OK" variant="success" /> | <Badge text="Changed" variant="caution" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| `0x88` LOAD_BCM4500 | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> |
|
||||
| `0x99` GET_DEMOD_STATUS | <Badge text="STALL" variant="danger" /> | <Badge text="OK" variant="success" /> | <Badge text="Proto" variant="caution" /> | N/A |
|
||||
| `0x9A` INIT_DEMOD | <Badge text="STALL" variant="danger" /> | <Badge text="OK" variant="success" /> | <Badge text="Proto" variant="caution" /> | N/A |
|
||||
| `0x9C` DELAY_COMMAND | <Badge text="STALL" variant="danger" /> | <Badge text="OK" variant="success" /> | N/A | N/A |
|
||||
|
||||
<Aside type="note">
|
||||
Command `0x88` (`LOAD_BCM4500`) routes to the STALL handler in **all** extracted firmware versions. The kernel only sends it for Rev.1 Warm devices (PID `0x0201`) after confirming `bm8pskFW_Loaded` is not set. On SkyWalker-1 hardware, the BCM4500 firmware is in ROM, so this bit is always set at boot.
|
||||
</Aside>
|
||||
|
||||
## Kernel Module Parameters
|
||||
|
||||
The gp8psk module inherits standard DVB-USB parameters:
|
||||
|
||||
| Parameter | Default | Description |
|
||||
|-----------|---------|-------------|
|
||||
| `debug` | 0 | Enable debug logging (bitmask) |
|
||||
| `force_pid_filter` | 0 | Force PID filtering on/off |
|
||||
| `generic_bulk_ctrl_endpoint` | `0x01` | Control endpoint |
|
||||
|
||||
Enable verbose logging with:
|
||||
|
||||
```bash
|
||||
modprobe dvb_usb_gp8psk debug=0xff
|
||||
```
|
||||
|
||||
Or at runtime:
|
||||
|
||||
```bash
|
||||
echo 0xff > /sys/module/dvb_usb_gp8psk/parameters/debug
|
||||
```
|
||||
|
||||
## DVB Frontend Properties
|
||||
|
||||
The gp8psk frontend (`gp8psk-fe.c`) registers with the following capabilities:
|
||||
|
||||
```c title="Frontend info structure"
|
||||
.name = "Genpix 8psk-to-USB2 DVB-S"
|
||||
.frequency_min_hz = 800 * MHz
|
||||
.frequency_max_hz = 2250 * MHz
|
||||
.frequency_stepsize_hz = 100 * kHz
|
||||
.symbol_rate_min = 256000 // 256 Ksps
|
||||
.symbol_rate_max = 30000000 // 30 Msps
|
||||
.caps = FE_CAN_INVERSION_AUTO |
|
||||
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
|
||||
FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 |
|
||||
FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
|
||||
FE_CAN_QPSK
|
||||
```
|
||||
|
||||
<Aside type="note">
|
||||
The frontend capabilities only advertise `FE_CAN_QPSK` -- the turbo 8PSK, turbo 16QAM, and DCII modes are not exposed through the standard DVB API. Accessing these modes requires direct vendor command access or the Windows BDA driver.
|
||||
</Aside>
|
||||
|
||||
## Related Pages
|
||||
|
||||
- [Vendor Commands](/usb/vendor-commands/) -- Complete command reference
|
||||
- [Kernel FW01](/firmware/kernel-fw01/) -- Firmware format and loading analysis
|
||||
- [DVB-S2 Investigation](/driver/dvb-s2/) -- Why DVB-S2 cannot be added
|
||||
- [Custom Firmware v3.01](/firmware/custom-v301/) -- Open-source replacement with extended commands
|
||||
|
|
|
|||
|
|
@ -1,291 +1,291 @@
|
|||
---
|
||||
title: Custom Firmware v3.01
|
||||
description: Open-source SDCC + fx2lib replacement firmware with spectrum sweep, blind scan, raw demod access, and hardware diagnostics.
|
||||
---
|
||||
|
||||
import { Steps, Badge, Aside, Tabs, TabItem, FileTree } from '@astrojs/starlight/components';
|
||||
|
||||
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 raw register access capabilities. <Badge text="Custom" variant="success" />
|
||||
|
||||
See also: [v3.02](/firmware/custom-v302/) adds signal monitoring, tune-and-measure, and batch register read commands on top of this base.
|
||||
|
||||
## Project Structure
|
||||
|
||||
<FileTree>
|
||||
- firmware/
|
||||
- skywalker1.c Main firmware source (1351 lines)
|
||||
- Makefile SDCC build rules
|
||||
- skywalker1.ihx Compiled Intel HEX output
|
||||
- tools/
|
||||
- fw_load.py FX2 RAM loader utility
|
||||
- eeprom_flash.py EEPROM flash tool
|
||||
</FileTree>
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Toolchain | SDCC 4.x + fx2lib |
|
||||
| Target | Cypress CY7C68013A (FX2LP) |
|
||||
| Load method | RAM upload via `fw_load.py` (USB 0xA0 vendor request) |
|
||||
| Binary size | ~3 KB |
|
||||
| Source lines | 1351 |
|
||||
| DiSEqC data pin | P0.7 (matches v2.06 hardware) |
|
||||
| BCM4500 I2C address | `0x08` (7-bit); wire address `0x10`/`0x11` |
|
||||
|
||||
<Aside type="tip">
|
||||
To build the firmware from source:
|
||||
|
||||
```bash
|
||||
cd firmware/
|
||||
make # Produces skywalker1.ihx
|
||||
```
|
||||
|
||||
To load into FX2 RAM (does not persist across power cycles):
|
||||
|
||||
```bash
|
||||
python tools/fw_load.py firmware/skywalker1.ihx
|
||||
```
|
||||
|
||||
To make the firmware persistent, flash it to the onboard EEPROM using `eeprom_flash.py`.
|
||||
</Aside>
|
||||
|
||||
## Stock-Compatible Commands
|
||||
|
||||
All stock vendor commands (`0x80`--`0x94`) are implemented for full compatibility with the Linux `dvb_usb_gp8psk` kernel driver:
|
||||
|
||||
| Command | Name | Implementation |
|
||||
|---------|------|---------------|
|
||||
| `0x80` | GET_8PSK_CONFIG | Returns `config_status` byte (1 byte) |
|
||||
| `0x85` | ARM_TRANSFER | Calls `gpif_start()` / `gpif_stop()` |
|
||||
| `0x86` | TUNE_8PSK | Parses 10-byte EP0 payload, programs BCM4500 |
|
||||
| `0x87` | GET_SIGNAL_STRENGTH | Reads 6 BCM4500 indirect registers |
|
||||
| `0x89` | BOOT_8PSK | Full BCM4500 boot sequence (see below) |
|
||||
| `0x8A` | START_INTERSIL | Enables/disables LNB power supply |
|
||||
| `0x8B` | SET_LNB_VOLTAGE | Sets P0.4 for 13V/18V selection |
|
||||
| `0x8C` | SET_22KHZ_TONE | Sets P0.3 for 22 kHz oscillator gate |
|
||||
| `0x8D` | SEND_DISEQC | DiSEqC tone burst via Timer2 bit-bang |
|
||||
| `0x90` | GET_SIGNAL_LOCK | Reads BCM4500 lock register `0xA4` |
|
||||
| `0x92` | GET_FW_VERS | Returns version `0x030100`, build date |
|
||||
| `0x94` | USE_EXTRA_VOLT | Writes `0x62`/`0x6A` to XRAM `0xE0B6` |
|
||||
|
||||
## Custom Diagnostic Commands
|
||||
|
||||
Seven new vendor commands (`0xB0`--`0xB6`) extend the firmware with capabilities absent from all stock versions: <Badge text="New" variant="success" />
|
||||
|
||||
| Command | Name | Direction | Payload | Purpose |
|
||||
|---------|------|-----------|---------|---------|
|
||||
| `0xB0` | SPECTRUM_SWEEP | OUT+Bulk | 10 bytes EP0 | Step through frequencies, return power readings via EP2 |
|
||||
| `0xB1` | RAW_DEMOD_READ | IN | 2 bytes | Read arbitrary BCM4500 indirect register |
|
||||
| `0xB2` | RAW_DEMOD_WRITE | OUT | 3 bytes | Write arbitrary BCM4500 indirect register |
|
||||
| `0xB3` | BLIND_SCAN | OUT+EP0 | 16 bytes EP0 | Sweep symbol rates at a frequency, report lock |
|
||||
| `0xB4` | I2C_SCAN | IN | N bytes | Scan I2C bus for responsive devices |
|
||||
| `0xB5` | GET_BOOT_STAGE | IN | 2 bytes | Read `config_status` + `boot_stage` |
|
||||
| `0xB6` | GET_GPIO_STATE | IN | 3 bytes | Read IOA, IOB, IOD port registers |
|
||||
|
||||
### Spectrum Sweep (0xB0)
|
||||
|
||||
Steps through frequencies from start to stop, reading BCM4500 signal energy at each step. Results are packed as u16 LE values into EP2 bulk endpoint.
|
||||
|
||||
```c title="Spectrum sweep EP0 payload (10 bytes)"
|
||||
EP0BUF[0..3] start_freq (u32 LE, kHz)
|
||||
EP0BUF[4..7] stop_freq (u32 LE, kHz)
|
||||
EP0BUF[8..9] step_khz (u16 LE, default 1000 if 0)
|
||||
```
|
||||
|
||||
At each step, the firmware programs the BCM4500 frequency register via indirect write, waits 10 ms for settling, reads the SNR register pair, and packs the result into EP2 FIFO. When the buffer reaches 512 bytes, it is committed to the host.
|
||||
|
||||
### Blind Scan (0xB3)
|
||||
|
||||
Sweeps symbol rates from `sr_min` to `sr_max` at a given frequency, checking for signal lock at each step.
|
||||
|
||||
```c title="Blind scan EP0 payload (16 bytes)"
|
||||
EP0BUF[0..3] freq_khz (u32 LE)
|
||||
EP0BUF[4..7] sr_min (u32 LE, sps)
|
||||
EP0BUF[8..11] sr_max (u32 LE, sps)
|
||||
EP0BUF[12..15] sr_step (u32 LE, sps, default 1000000 if 0)
|
||||
```
|
||||
|
||||
Returns 8 bytes on lock (`freq_khz[4] + sr_locked[4]`), or 1 byte `0x00` if no lock found.
|
||||
|
||||
### Raw Demod Access (0xB1 / 0xB2)
|
||||
|
||||
Direct access to any BCM4500 indirect register, bypassing the stock firmware's limited register set:
|
||||
|
||||
```c title="Raw demod read (0xB1)"
|
||||
// wValue = register page, wIndex = register number
|
||||
// Returns 1 byte in EP0
|
||||
bcm_indirect_read(page, &val);
|
||||
EP0BUF[0] = val;
|
||||
```
|
||||
|
||||
```c title="Raw demod write (0xB2)"
|
||||
// wValue = register page, wIndex = register number
|
||||
// EP0 data = 1 byte value
|
||||
bcm_indirect_write(page, val);
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
<Steps>
|
||||
|
||||
1. **GPIO setup** (`boot_stage = 1`): Set P3.7/P3.6/P3.5 HIGH (control lines idle), assert BCM4500 RESET (P0.5 LOW)
|
||||
|
||||
2. **Power on** (`boot_stage = 2`): Enable power supply (P0.1 HIGH, P0.2 LOW), wait 30 ms for settling, release RESET (P0.5 HIGH), wait 50 ms for BCM4500 POR
|
||||
|
||||
3. **I2C probe** (`boot_stage = 3`): Read BCM4500 status register `0xA2` to verify the chip is alive on the I2C bus
|
||||
|
||||
4. **Init block 0** (`boot_stage = 4`): Write 7-byte configuration block to BCM4500 page 0 indirect registers
|
||||
|
||||
5. **Init block 1** (`boot_stage = 5`): Write 8-byte configuration block
|
||||
|
||||
6. **Init block 2** (`boot_stage = 6`): Write 3-byte configuration block
|
||||
|
||||
7. **Success** (`boot_stage = 0xFF`): Set `BM_STARTED | BM_FW_LOADED` in config status
|
||||
|
||||
</Steps>
|
||||
|
||||
### BCM4500 Init Data
|
||||
|
||||
Three initialization blocks extracted from stock v2.06 firmware (`FUN_CODE_0ddd`):
|
||||
|
||||
```c title="BCM4500 register initialization data"
|
||||
static const __code BYTE bcm_init_block0[] = {
|
||||
0x06, 0x0b, 0x17, 0x38, 0x9f, 0xd9, 0x80
|
||||
};
|
||||
static const __code BYTE bcm_init_block1[] = {
|
||||
0x07, 0x09, 0x39, 0x4f, 0x00, 0x65, 0xb7, 0x10
|
||||
};
|
||||
static const __code BYTE bcm_init_block2[] = {
|
||||
0x0f, 0x0c, 0x09
|
||||
};
|
||||
```
|
||||
|
||||
Each block is written to BCM4500 page 0 via the indirect register protocol: page select to `0xA6`, data bytes to `0xA7`, trailing zero to `0xA7`, commit `0x03` to `0xA8`, then poll for completion.
|
||||
|
||||
### Debug Boot Modes
|
||||
|
||||
The BOOT_8PSK command (`0x89`) accepts debug wValue parameters that execute partial boot sequences for incremental hardware debugging:
|
||||
|
||||
| wValue | Stage | What It Does | Success Marker |
|
||||
|--------|-------|-------------|----------------|
|
||||
| `0x80` | None | No-op, return current state | -- |
|
||||
| `0x81` | GPIO only | GPIO setup + power + reset, no I2C | `0xA1` |
|
||||
| `0x82` | GPIO + probe | GPIO + I2C read of status register | `0xA2` |
|
||||
| `0x83` | GPIO + probe + block 0 | GPIO + I2C + first init block | `0xA3` |
|
||||
| `0x84` | I2C only | Probe without GPIO (chip must be powered) | `0xA4` |
|
||||
| `0x85` | GPIO + probe (no bus reset) | Same as `0x82` without I2CS bmSTOP | `0xA5` |
|
||||
| `0x01` | Full boot | Complete `bcm4500_boot()` sequence | `0xFF` |
|
||||
| `0x00` | Shutdown | Power off BCM4500 | -- |
|
||||
|
||||
<Aside type="note">
|
||||
Debug mode `0x85` was created to isolate a critical bug: sending an I2C STOP when no transaction is active corrupts the FX2 I2C controller state, causing subsequent START+ACK detection to fail. The spurious STOP was present in early custom firmware revisions and was the root cause of BCM4500 boot failures. Removing it fixed the issue.
|
||||
</Aside>
|
||||
|
||||
## I2C Implementation
|
||||
|
||||
The custom firmware implements I2C from scratch rather than using fx2lib's I2C functions, providing full timeout protection:
|
||||
|
||||
```c title="I2C timeout constant"
|
||||
#define I2C_TIMEOUT 6000 // ~5ms at 48MHz (4 clocks/cycle, ~12 MIPS)
|
||||
```
|
||||
|
||||
Key I2C functions:
|
||||
|
||||
| Function | Purpose |
|
||||
|----------|---------|
|
||||
| `i2c_wait_done()` | Poll `I2CS.bmDONE` with 6000-count timeout |
|
||||
| `i2c_wait_stop()` | Poll `I2CS.bmSTOP` clear with timeout |
|
||||
| `i2c_combined_read()` | Write-then-read with repeated START (no intermediate STOP) |
|
||||
| `i2c_write_timeout()` | Single-byte write with timeout on each phase |
|
||||
| `i2c_write_multi_timeout()` | Multi-byte write with timeout |
|
||||
|
||||
<Aside type="caution">
|
||||
Stock firmware has no I2C timeout -- if the BCM4500 holds SCL low (clock stretching), the FX2 spins forever. The custom firmware's 6000-count timeout provides ~5 ms margin at 48 MHz, which is more than 200x the time needed for a single I2C byte at 400 kHz.
|
||||
</Aside>
|
||||
|
||||
### BCM4500 Register Access
|
||||
|
||||
The BCM4500 uses an indirect register protocol through three I2C registers:
|
||||
|
||||
| Register | Address | Purpose |
|
||||
|----------|---------|---------|
|
||||
| BCM_REG_PAGE | `0xA6` | Page/register select |
|
||||
| BCM_REG_DATA | `0xA7` | Data read/write |
|
||||
| BCM_REG_CMD | `0xA8` | Command trigger (`0x01` = read, `0x03` = write) |
|
||||
|
||||
```c title="Indirect register read sequence"
|
||||
// 1. Write [page, 0x00, 0x01] to A6/A7/A8 in one I2C transaction
|
||||
// 2. Wait for command completion (poll A8 bit 0 == 0)
|
||||
// 3. Read result from A7
|
||||
```
|
||||
|
||||
## GPIF Streaming
|
||||
|
||||
Transport stream data from the BCM4500 flows through the FX2's GPIF engine into USB endpoint EP2:
|
||||
|
||||
```c title="GPIF configuration"
|
||||
IFCONFIG = 0xEE; // Internal 48MHz, GPIF master, async, clock output
|
||||
EP2FIFOCFG = 0x0C; // AUTOIN, ZEROLENIN, 8-bit
|
||||
FLOWSTATE |= 0x09; // Enable flow state + FS[3]
|
||||
GPIFTCB3 = 0x80; // Transaction count = 0x80000000 (effectively infinite)
|
||||
```
|
||||
|
||||
The `gpif_start()` function arms the GPIF for continuous read into EP2, while `gpif_stop()` flushes the FIFO and de-asserts the BCM4500 control lines on P3.
|
||||
|
||||
## GPIO Pin Map
|
||||
|
||||
```c title="GPIO pin definitions (v2.06 hardware)"
|
||||
#define PIN_PWR_EN 0x02 // P0.1 -- power supply enable
|
||||
#define PIN_PWR_DIS 0x04 // P0.2 -- power supply disable
|
||||
#define PIN_22KHZ 0x08 // P0.3 -- 22kHz oscillator gate
|
||||
#define PIN_LNB_VOLT 0x10 // P0.4 -- LNB voltage select
|
||||
#define PIN_BCM_RESET 0x20 // P0.5 -- BCM4500 hardware reset
|
||||
#define PIN_DISEQC 0x80 // P0.7 -- DiSEqC data
|
||||
```
|
||||
|
||||
Initial state after `TD_Init()`:
|
||||
- `IOA = 0x84` (P0.7 HIGH, P0.2 HIGH -- power disabled, streaming off)
|
||||
- `OEA = 0xBE` (P0.1 through P0.5 and P0.7 as outputs)
|
||||
|
||||
## 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 |
|
||||
| Custom commands | None | 7 (`0xB0`--`0xB6`) |
|
||||
| Spectrum sweep | Not possible | `0xB0` via EP2 bulk |
|
||||
| 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`) |
|
||||
|
||||
See the [v3.02 comparison table](/firmware/custom-v302/#what-changed-from-v301) for signal monitoring and batch register improvements added on top of v3.01.
|
||||
|
||||
## Tuning Implementation
|
||||
|
||||
The `do_tune()` function parses the same 10-byte EP0 payload as the stock firmware:
|
||||
|
||||
```c title="Tune command payload parsing"
|
||||
// Byte-reverse symbol rate and frequency from LE to BE
|
||||
for (i = 0; i < 4; i++) {
|
||||
tune_data[i] = EP0BUF[3 - i]; // Symbol rate (BE)
|
||||
tune_data[4 + i] = EP0BUF[7 - i]; // Frequency (BE)
|
||||
}
|
||||
tune_data[8] = EP0BUF[8]; // Modulation type (0-9)
|
||||
tune_data[9] = EP0BUF[9]; // FEC index
|
||||
tune_data[10] = 0x10; // Demod mode (standard)
|
||||
tune_data[11] = 0x00; // Turbo flag
|
||||
```
|
||||
|
||||
Modulation-specific handling:
|
||||
- Modulation types 1--3 (turbo modes): Set turbo flag `tune_data[11] = 0x01`
|
||||
- Modulation type 5: DCII I-stream, demod mode `0x12`
|
||||
- Modulation type 6: DCII Q-stream, demod mode `0x16`
|
||||
- Modulation type 7: DCII offset QPSK, demod mode `0x11`
|
||||
---
|
||||
title: Custom Firmware v3.01
|
||||
description: Open-source SDCC + fx2lib replacement firmware with spectrum sweep, blind scan, raw demod access, and hardware diagnostics.
|
||||
---
|
||||
|
||||
import { Steps, Badge, Aside, Tabs, TabItem, FileTree } from '@astrojs/starlight/components';
|
||||
|
||||
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 raw register access capabilities. <Badge text="Custom" variant="success" />
|
||||
|
||||
See also: [v3.02](/firmware/custom-v302/) adds signal monitoring, tune-and-measure, and batch register read commands on top of this base.
|
||||
|
||||
## Project Structure
|
||||
|
||||
<FileTree>
|
||||
- firmware/
|
||||
- skywalker1.c Main firmware source (1351 lines)
|
||||
- Makefile SDCC build rules
|
||||
- skywalker1.ihx Compiled Intel HEX output
|
||||
- tools/
|
||||
- fw_load.py FX2 RAM loader utility
|
||||
- eeprom_flash.py EEPROM flash tool
|
||||
</FileTree>
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Toolchain | SDCC 4.x + fx2lib |
|
||||
| Target | Cypress CY7C68013A (FX2LP) |
|
||||
| Load method | RAM upload via `fw_load.py` (USB 0xA0 vendor request) |
|
||||
| Binary size | ~3 KB |
|
||||
| Source lines | 1351 |
|
||||
| DiSEqC data pin | P0.7 (matches v2.06 hardware) |
|
||||
| BCM4500 I2C address | `0x08` (7-bit); wire address `0x10`/`0x11` |
|
||||
|
||||
<Aside type="tip">
|
||||
To build the firmware from source:
|
||||
|
||||
```bash
|
||||
cd firmware/
|
||||
make # Produces skywalker1.ihx
|
||||
```
|
||||
|
||||
To load into FX2 RAM (does not persist across power cycles):
|
||||
|
||||
```bash
|
||||
python tools/fw_load.py firmware/skywalker1.ihx
|
||||
```
|
||||
|
||||
To make the firmware persistent, flash it to the onboard EEPROM using `eeprom_flash.py`.
|
||||
</Aside>
|
||||
|
||||
## Stock-Compatible Commands
|
||||
|
||||
All stock vendor commands (`0x80`--`0x94`) are implemented for full compatibility with the Linux `dvb_usb_gp8psk` kernel driver:
|
||||
|
||||
| Command | Name | Implementation |
|
||||
|---------|------|---------------|
|
||||
| `0x80` | GET_8PSK_CONFIG | Returns `config_status` byte (1 byte) |
|
||||
| `0x85` | ARM_TRANSFER | Calls `gpif_start()` / `gpif_stop()` |
|
||||
| `0x86` | TUNE_8PSK | Parses 10-byte EP0 payload, programs BCM4500 |
|
||||
| `0x87` | GET_SIGNAL_STRENGTH | Reads 6 BCM4500 indirect registers |
|
||||
| `0x89` | BOOT_8PSK | Full BCM4500 boot sequence (see below) |
|
||||
| `0x8A` | START_INTERSIL | Enables/disables LNB power supply |
|
||||
| `0x8B` | SET_LNB_VOLTAGE | Sets P0.4 for 13V/18V selection |
|
||||
| `0x8C` | SET_22KHZ_TONE | Sets P0.3 for 22 kHz oscillator gate |
|
||||
| `0x8D` | SEND_DISEQC | DiSEqC tone burst via Timer2 bit-bang |
|
||||
| `0x90` | GET_SIGNAL_LOCK | Reads BCM4500 lock register `0xA4` |
|
||||
| `0x92` | GET_FW_VERS | Returns version `0x030100`, build date |
|
||||
| `0x94` | USE_EXTRA_VOLT | Writes `0x62`/`0x6A` to XRAM `0xE0B6` |
|
||||
|
||||
## Custom Diagnostic Commands
|
||||
|
||||
Seven new vendor commands (`0xB0`--`0xB6`) extend the firmware with capabilities absent from all stock versions: <Badge text="New" variant="success" />
|
||||
|
||||
| Command | Name | Direction | Payload | Purpose |
|
||||
|---------|------|-----------|---------|---------|
|
||||
| `0xB0` | SPECTRUM_SWEEP | OUT+Bulk | 10 bytes EP0 | Step through frequencies, return power readings via EP2 |
|
||||
| `0xB1` | RAW_DEMOD_READ | IN | 2 bytes | Read arbitrary BCM4500 indirect register |
|
||||
| `0xB2` | RAW_DEMOD_WRITE | OUT | 3 bytes | Write arbitrary BCM4500 indirect register |
|
||||
| `0xB3` | BLIND_SCAN | OUT+EP0 | 16 bytes EP0 | Sweep symbol rates at a frequency, report lock |
|
||||
| `0xB4` | I2C_SCAN | IN | N bytes | Scan I2C bus for responsive devices |
|
||||
| `0xB5` | GET_BOOT_STAGE | IN | 2 bytes | Read `config_status` + `boot_stage` |
|
||||
| `0xB6` | GET_GPIO_STATE | IN | 3 bytes | Read IOA, IOB, IOD port registers |
|
||||
|
||||
### Spectrum Sweep (0xB0)
|
||||
|
||||
Steps through frequencies from start to stop, reading BCM4500 signal energy at each step. Results are packed as u16 LE values into EP2 bulk endpoint.
|
||||
|
||||
```c title="Spectrum sweep EP0 payload (10 bytes)"
|
||||
EP0BUF[0..3] start_freq (u32 LE, kHz)
|
||||
EP0BUF[4..7] stop_freq (u32 LE, kHz)
|
||||
EP0BUF[8..9] step_khz (u16 LE, default 1000 if 0)
|
||||
```
|
||||
|
||||
At each step, the firmware programs the BCM4500 frequency register via indirect write, waits 10 ms for settling, reads the SNR register pair, and packs the result into EP2 FIFO. When the buffer reaches 512 bytes, it is committed to the host.
|
||||
|
||||
### Blind Scan (0xB3)
|
||||
|
||||
Sweeps symbol rates from `sr_min` to `sr_max` at a given frequency, checking for signal lock at each step.
|
||||
|
||||
```c title="Blind scan EP0 payload (16 bytes)"
|
||||
EP0BUF[0..3] freq_khz (u32 LE)
|
||||
EP0BUF[4..7] sr_min (u32 LE, sps)
|
||||
EP0BUF[8..11] sr_max (u32 LE, sps)
|
||||
EP0BUF[12..15] sr_step (u32 LE, sps, default 1000000 if 0)
|
||||
```
|
||||
|
||||
Returns 8 bytes on lock (`freq_khz[4] + sr_locked[4]`), or 1 byte `0x00` if no lock found.
|
||||
|
||||
### Raw Demod Access (0xB1 / 0xB2)
|
||||
|
||||
Direct access to any BCM4500 indirect register, bypassing the stock firmware's limited register set:
|
||||
|
||||
```c title="Raw demod read (0xB1)"
|
||||
// wValue = register page, wIndex = register number
|
||||
// Returns 1 byte in EP0
|
||||
bcm_indirect_read(page, &val);
|
||||
EP0BUF[0] = val;
|
||||
```
|
||||
|
||||
```c title="Raw demod write (0xB2)"
|
||||
// wValue = register page, wIndex = register number
|
||||
// EP0 data = 1 byte value
|
||||
bcm_indirect_write(page, val);
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
<Steps>
|
||||
|
||||
1. **GPIO setup** (`boot_stage = 1`): Set P3.7/P3.6/P3.5 HIGH (control lines idle), assert BCM4500 RESET (P0.5 LOW)
|
||||
|
||||
2. **Power on** (`boot_stage = 2`): Enable power supply (P0.1 HIGH, P0.2 LOW), wait 30 ms for settling, release RESET (P0.5 HIGH), wait 50 ms for BCM4500 POR
|
||||
|
||||
3. **I2C probe** (`boot_stage = 3`): Read BCM4500 status register `0xA2` to verify the chip is alive on the I2C bus
|
||||
|
||||
4. **Init block 0** (`boot_stage = 4`): Write 7-byte configuration block to BCM4500 page 0 indirect registers
|
||||
|
||||
5. **Init block 1** (`boot_stage = 5`): Write 8-byte configuration block
|
||||
|
||||
6. **Init block 2** (`boot_stage = 6`): Write 3-byte configuration block
|
||||
|
||||
7. **Success** (`boot_stage = 0xFF`): Set `BM_STARTED | BM_FW_LOADED` in config status
|
||||
|
||||
</Steps>
|
||||
|
||||
### BCM4500 Init Data
|
||||
|
||||
Three initialization blocks extracted from stock v2.06 firmware (`FUN_CODE_0ddd`):
|
||||
|
||||
```c title="BCM4500 register initialization data"
|
||||
static const __code BYTE bcm_init_block0[] = {
|
||||
0x06, 0x0b, 0x17, 0x38, 0x9f, 0xd9, 0x80
|
||||
};
|
||||
static const __code BYTE bcm_init_block1[] = {
|
||||
0x07, 0x09, 0x39, 0x4f, 0x00, 0x65, 0xb7, 0x10
|
||||
};
|
||||
static const __code BYTE bcm_init_block2[] = {
|
||||
0x0f, 0x0c, 0x09
|
||||
};
|
||||
```
|
||||
|
||||
Each block is written to BCM4500 page 0 via the indirect register protocol: page select to `0xA6`, data bytes to `0xA7`, trailing zero to `0xA7`, commit `0x03` to `0xA8`, then poll for completion.
|
||||
|
||||
### Debug Boot Modes
|
||||
|
||||
The BOOT_8PSK command (`0x89`) accepts debug wValue parameters that execute partial boot sequences for incremental hardware debugging:
|
||||
|
||||
| wValue | Stage | What It Does | Success Marker |
|
||||
|--------|-------|-------------|----------------|
|
||||
| `0x80` | None | No-op, return current state | -- |
|
||||
| `0x81` | GPIO only | GPIO setup + power + reset, no I2C | `0xA1` |
|
||||
| `0x82` | GPIO + probe | GPIO + I2C read of status register | `0xA2` |
|
||||
| `0x83` | GPIO + probe + block 0 | GPIO + I2C + first init block | `0xA3` |
|
||||
| `0x84` | I2C only | Probe without GPIO (chip must be powered) | `0xA4` |
|
||||
| `0x85` | GPIO + probe (no bus reset) | Same as `0x82` without I2CS bmSTOP | `0xA5` |
|
||||
| `0x01` | Full boot | Complete `bcm4500_boot()` sequence | `0xFF` |
|
||||
| `0x00` | Shutdown | Power off BCM4500 | -- |
|
||||
|
||||
<Aside type="note">
|
||||
Debug mode `0x85` was created to isolate a critical bug: sending an I2C STOP when no transaction is active corrupts the FX2 I2C controller state, causing subsequent START+ACK detection to fail. The spurious STOP was present in early custom firmware revisions and was the root cause of BCM4500 boot failures. Removing it fixed the issue.
|
||||
</Aside>
|
||||
|
||||
## I2C Implementation
|
||||
|
||||
The custom firmware implements I2C from scratch rather than using fx2lib's I2C functions, providing full timeout protection:
|
||||
|
||||
```c title="I2C timeout constant"
|
||||
#define I2C_TIMEOUT 6000 // ~5ms at 48MHz (4 clocks/cycle, ~12 MIPS)
|
||||
```
|
||||
|
||||
Key I2C functions:
|
||||
|
||||
| Function | Purpose |
|
||||
|----------|---------|
|
||||
| `i2c_wait_done()` | Poll `I2CS.bmDONE` with 6000-count timeout |
|
||||
| `i2c_wait_stop()` | Poll `I2CS.bmSTOP` clear with timeout |
|
||||
| `i2c_combined_read()` | Write-then-read with repeated START (no intermediate STOP) |
|
||||
| `i2c_write_timeout()` | Single-byte write with timeout on each phase |
|
||||
| `i2c_write_multi_timeout()` | Multi-byte write with timeout |
|
||||
|
||||
<Aside type="caution">
|
||||
Stock firmware has no I2C timeout -- if the BCM4500 holds SCL low (clock stretching), the FX2 spins forever. The custom firmware's 6000-count timeout provides ~5 ms margin at 48 MHz, which is more than 200x the time needed for a single I2C byte at 400 kHz.
|
||||
</Aside>
|
||||
|
||||
### BCM4500 Register Access
|
||||
|
||||
The BCM4500 uses an indirect register protocol through three I2C registers:
|
||||
|
||||
| Register | Address | Purpose |
|
||||
|----------|---------|---------|
|
||||
| BCM_REG_PAGE | `0xA6` | Page/register select |
|
||||
| BCM_REG_DATA | `0xA7` | Data read/write |
|
||||
| BCM_REG_CMD | `0xA8` | Command trigger (`0x01` = read, `0x03` = write) |
|
||||
|
||||
```c title="Indirect register read sequence"
|
||||
// 1. Write [page, 0x00, 0x01] to A6/A7/A8 in one I2C transaction
|
||||
// 2. Wait for command completion (poll A8 bit 0 == 0)
|
||||
// 3. Read result from A7
|
||||
```
|
||||
|
||||
## GPIF Streaming
|
||||
|
||||
Transport stream data from the BCM4500 flows through the FX2's GPIF engine into USB endpoint EP2:
|
||||
|
||||
```c title="GPIF configuration"
|
||||
IFCONFIG = 0xEE; // Internal 48MHz, GPIF master, async, clock output
|
||||
EP2FIFOCFG = 0x0C; // AUTOIN, ZEROLENIN, 8-bit
|
||||
FLOWSTATE |= 0x09; // Enable flow state + FS[3]
|
||||
GPIFTCB3 = 0x80; // Transaction count = 0x80000000 (effectively infinite)
|
||||
```
|
||||
|
||||
The `gpif_start()` function arms the GPIF for continuous read into EP2, while `gpif_stop()` flushes the FIFO and de-asserts the BCM4500 control lines on P3.
|
||||
|
||||
## GPIO Pin Map
|
||||
|
||||
```c title="GPIO pin definitions (v2.06 hardware)"
|
||||
#define PIN_PWR_EN 0x02 // P0.1 -- power supply enable
|
||||
#define PIN_PWR_DIS 0x04 // P0.2 -- power supply disable
|
||||
#define PIN_22KHZ 0x08 // P0.3 -- 22kHz oscillator gate
|
||||
#define PIN_LNB_VOLT 0x10 // P0.4 -- LNB voltage select
|
||||
#define PIN_BCM_RESET 0x20 // P0.5 -- BCM4500 hardware reset
|
||||
#define PIN_DISEQC 0x80 // P0.7 -- DiSEqC data
|
||||
```
|
||||
|
||||
Initial state after `TD_Init()`:
|
||||
- `IOA = 0x84` (P0.7 HIGH, P0.2 HIGH -- power disabled, streaming off)
|
||||
- `OEA = 0xBE` (P0.1 through P0.5 and P0.7 as outputs)
|
||||
|
||||
## 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 |
|
||||
| Custom commands | None | 7 (`0xB0`--`0xB6`) |
|
||||
| Spectrum sweep | Not possible | `0xB0` via EP2 bulk |
|
||||
| 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`) |
|
||||
|
||||
See the [v3.02 comparison table](/firmware/custom-v302/#what-changed-from-v301) for signal monitoring and batch register improvements added on top of v3.01.
|
||||
|
||||
## Tuning Implementation
|
||||
|
||||
The `do_tune()` function parses the same 10-byte EP0 payload as the stock firmware:
|
||||
|
||||
```c title="Tune command payload parsing"
|
||||
// Byte-reverse symbol rate and frequency from LE to BE
|
||||
for (i = 0; i < 4; i++) {
|
||||
tune_data[i] = EP0BUF[3 - i]; // Symbol rate (BE)
|
||||
tune_data[4 + i] = EP0BUF[7 - i]; // Frequency (BE)
|
||||
}
|
||||
tune_data[8] = EP0BUF[8]; // Modulation type (0-9)
|
||||
tune_data[9] = EP0BUF[9]; // FEC index
|
||||
tune_data[10] = 0x10; // Demod mode (standard)
|
||||
tune_data[11] = 0x00; // Turbo flag
|
||||
```
|
||||
|
||||
Modulation-specific handling:
|
||||
- Modulation types 1--3 (turbo modes): Set turbo flag `tune_data[11] = 0x01`
|
||||
- Modulation type 5: DCII I-stream, demod mode `0x12`
|
||||
- Modulation type 6: DCII Q-stream, demod mode `0x16`
|
||||
- Modulation type 7: DCII offset QPSK, demod mode `0x11`
|
||||
|
|
|
|||
|
|
@ -1,80 +1,80 @@
|
|||
---
|
||||
title: Custom Firmware v3.02
|
||||
description: Signal monitoring, tune-and-measure, and batch register read commands for real-time RF analysis.
|
||||
---
|
||||
|
||||
import { Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
Firmware v3.02 adds three new vendor commands (`0xB7`--`0xB9`) optimized for real-time signal monitoring and RF analysis workflows. These commands reduce USB round-trips by combining multiple register reads into single transfers. <Badge text="v3.02" variant="success" />
|
||||
|
||||
Built on the same [v3.01 codebase](/firmware/custom-v301/) — same SDCC + fx2lib toolchain, same stock-compatible command set, same I2C timeout protection.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Version ID | `0x030200` |
|
||||
| Date | 2026-02-12 |
|
||||
| New commands | `0xB7`--`0xB9` (3 added to v3.01's 7) |
|
||||
| Total custom commands | 10 (`0xB0`--`0xB9`) |
|
||||
|
||||
## New Commands
|
||||
|
||||
| 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 ~64x speedup for register exploration.
|
||||
|
||||
```c title="MULTI_REG_READ parameters"
|
||||
wValue = start register number
|
||||
wIndex = count (1-64)
|
||||
Returns: count bytes, one per register
|
||||
```
|
||||
|
||||
## What Changed from v3.01
|
||||
|
||||
| Feature | v3.01 | v3.02 |
|
||||
|---------|-------|-------|
|
||||
| Custom commands | 7 (`0xB0`--`0xB6`) | 10 (`0xB0`--`0xB9`) |
|
||||
| Spectrum sweep | `0xB0` via EP2 bulk (firmware-driven) | Also `0xB8` via control EP (host-driven) |
|
||||
| Signal monitoring | 3 USB transfers (stock method) | 1 transfer (`0xB7`, 8 bytes) |
|
||||
| Batch register read | 1 reg per transfer | 64 regs per transfer (`0xB9`) |
|
||||
| All other features | -- | Unchanged |
|
||||
|
||||
The v3.01 commands (`0xB0`--`0xB6`) remain fully functional. See the [v3.01 documentation](/firmware/custom-v301/) for spectrum sweep, blind scan, raw demod access, I2C scan, boot stage, and GPIO state commands.
|
||||
---
|
||||
title: Custom Firmware v3.02
|
||||
description: Signal monitoring, tune-and-measure, and batch register read commands for real-time RF analysis.
|
||||
---
|
||||
|
||||
import { Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
Firmware v3.02 adds three new vendor commands (`0xB7`--`0xB9`) optimized for real-time signal monitoring and RF analysis workflows. These commands reduce USB round-trips by combining multiple register reads into single transfers. <Badge text="v3.02" variant="success" />
|
||||
|
||||
Built on the same [v3.01 codebase](/firmware/custom-v301/) — same SDCC + fx2lib toolchain, same stock-compatible command set, same I2C timeout protection.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Version ID | `0x030200` |
|
||||
| Date | 2026-02-12 |
|
||||
| New commands | `0xB7`--`0xB9` (3 added to v3.01's 7) |
|
||||
| Total custom commands | 10 (`0xB0`--`0xB9`) |
|
||||
|
||||
## New Commands
|
||||
|
||||
| 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 ~64x speedup for register exploration.
|
||||
|
||||
```c title="MULTI_REG_READ parameters"
|
||||
wValue = start register number
|
||||
wIndex = count (1-64)
|
||||
Returns: count bytes, one per register
|
||||
```
|
||||
|
||||
## What Changed from v3.01
|
||||
|
||||
| Feature | v3.01 | v3.02 |
|
||||
|---------|-------|-------|
|
||||
| Custom commands | 7 (`0xB0`--`0xB6`) | 10 (`0xB0`--`0xB9`) |
|
||||
| Spectrum sweep | `0xB0` via EP2 bulk (firmware-driven) | Also `0xB8` via control EP (host-driven) |
|
||||
| Signal monitoring | 3 USB transfers (stock method) | 1 transfer (`0xB7`, 8 bytes) |
|
||||
| Batch register read | 1 reg per transfer | 64 regs per transfer (`0xB9`) |
|
||||
| All other features | -- | Unchanged |
|
||||
|
||||
The v3.01 commands (`0xB0`--`0xB6`) remain fully functional. See the [v3.01 documentation](/firmware/custom-v301/) for spectrum sweep, blind scan, raw demod access, I2C scan, boot stage, and GPIO state commands.
|
||||
|
|
|
|||
|
|
@ -1,181 +1,181 @@
|
|||
---
|
||||
title: Custom Firmware v3.05
|
||||
description: Safety-hardened firmware with software watchdog, timeout protection on all I2C/USB/GPIF paths, and comprehensive error reporting.
|
||||
---
|
||||
|
||||
import { Badge, Aside, Steps } from '@astrojs/starlight/components';
|
||||
|
||||
Firmware v3.05.0 is the result of a systematic safety review applied to the entire codebase. Every infinite-spin loop has been replaced with timeout-protected equivalents, a software watchdog cuts LNB power if the main loop stalls, and every failure mode now sets a specific error code readable by the host. <Badge text="v3.05" variant="success" /> <Badge text="Safety" variant="caution" />
|
||||
|
||||
Built on the [v3.04 codebase](/firmware/custom-v301/) — same SDCC + fx2lib toolchain, same stock-compatible command set, plus all custom commands from [v3.02](/firmware/custom-v302/) through v3.04.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Version ID | `0x030500` |
|
||||
| Date | 2026-02-16 |
|
||||
| Code size | 13,079 / 15,360 bytes (85%) |
|
||||
| XRAM | 218 / 512 bytes (43%) |
|
||||
| Stack | 132 bytes available |
|
||||
| Source lines | 2,256 |
|
||||
| New commands | None (safety hardening of existing paths) |
|
||||
|
||||
## Motivation
|
||||
|
||||
The SkyWalker-1 controls an LNB power supply capable of 750 mA at 18V with no hardware watchdog on the FX2LP. A firmware hang leaves the power supply energized indefinitely. Prior firmware versions contained multiple paths where a stuck I2C bus, an unresponsive GPIF, or a slow EP0 transfer could spin forever without recovery.
|
||||
|
||||
A structured safety review identified 21 issues across 4 severity levels:
|
||||
|
||||
| Severity | Count | Description |
|
||||
|----------|-------|-------------|
|
||||
| Critical | 3 | Infinite hangs in I2C bus scan, `do_tune()`, and DiSEqC Timer2 waits |
|
||||
| High | 6 | Unprotected GPIF/EP0/EP2 waits, unchecked I2C returns in sweep functions |
|
||||
| Medium | 7 | Missing error codes, payload validation, lock-bit magic numbers |
|
||||
| Low | 5 | Code clarity, documentation, saturation guards |
|
||||
|
||||
## Software Watchdog
|
||||
|
||||
A Timer0-based software watchdog provides a safety backstop for any remaining or unknown hang paths.
|
||||
|
||||
**Architecture:**
|
||||
|
||||
- Timer0 runs in Mode 1 (16-bit, no auto-reload) at 4 MHz (48 MHz / 12)
|
||||
- Overflow period: 16.384 ms per tick
|
||||
- Watchdog window: 122 ticks = ~2 seconds
|
||||
- The main loop calls `wdt_kick()` on every iteration to reset the counter
|
||||
- Long-running operations (spectrum sweep, blind scan) call `wdt_kick()` inside their loops
|
||||
|
||||
**If the watchdog fires:**
|
||||
|
||||
<Steps>
|
||||
1. Timer0 ISR sets `IOA` to cut LNB power (`PIN_PWR_EN` off, `PIN_PWR_DIS` on)
|
||||
2. ISR sets `wdt_armed = 2` (fired state) — prevents re-arming
|
||||
3. Main loop detects `wdt_armed == 2` on next iteration (if it recovers)
|
||||
4. Main loop sets `last_error = ERR_WDT_FIRED` (0x0D) and clears `config_status`
|
||||
5. Host can read `ERR_WDT_FIRED` via `GET_LAST_ERROR` (0xBC)
|
||||
</Steps>
|
||||
|
||||
<Aside type="caution">
|
||||
The watchdog shares `IOA` with the main loop via read-modify-write. To avoid RMW races between the ISR and main-loop GPIO operations, `wdt_armed` uses a tri-state protocol: `0` = off, `1` = armed, `2` = fired. The ISR only writes `IOA` once (on fire), and `wdt_kick()` refuses to re-arm when `wdt_armed == 2`.
|
||||
</Aside>
|
||||
|
||||
## Timeout Protection
|
||||
|
||||
Every bare spin loop in the firmware has been replaced with a countdown-protected equivalent. These are the paths that could previously hang forever:
|
||||
|
||||
| Path | Location | Timeout | Error Code |
|
||||
|------|----------|---------|------------|
|
||||
| `i2c_wait_done()` | I2C DONE bit | 6,000 iterations | `ERR_I2C_TIMEOUT` |
|
||||
| `i2c_wait_stop()` | I2C STOP bit | 6,000 iterations | `ERR_I2C_TIMEOUT` |
|
||||
| `ep0_wait_data()` | EP0 BUSY bit | 6,000 iterations | `ERR_EP0_TIMEOUT` |
|
||||
| `gpif_start()` | GPIF idle | 60,000 iterations | `ERR_GPIF_TIMEOUT` |
|
||||
| `gpif_stop()` | GPIF idle | 60,000 iterations | `ERR_GPIF_TIMEOUT` |
|
||||
| EP2 FIFO full | `do_param_sweep()` | 60,000 iterations | `ERR_EP2_TIMEOUT` |
|
||||
| EP2 FIFO full | `do_spectrum_sweep()` | 60,000 iterations | `ERR_EP2_TIMEOUT` |
|
||||
| Timer2 overflow | `diseqc_wait_ticks()` | 6,000 per tick | `ERR_DISEQC_TIMER` |
|
||||
| I2C bus scan | `0xB4` handler | via `i2c_wait_done/stop` | `ERR_I2C_TIMEOUT` |
|
||||
|
||||
The `ep0_wait_data()` helper replaces 7 separate bare `while (EP0CS & bmEPBUSY)` loops in vendor command handlers. After the wait, each handler validates `EP0BCL` against the expected payload size.
|
||||
|
||||
## Error Codes
|
||||
|
||||
All error codes are set in the `last_error` variable and readable via `GET_LAST_ERROR` (0xBC). The error is sticky — it persists until overwritten by a new error.
|
||||
|
||||
| Code | Name | Meaning |
|
||||
|------|------|---------|
|
||||
| `0x00` | `ERR_OK` | No error |
|
||||
| `0x01` | `ERR_I2C_TIMEOUT` | I2C DONE or STOP bit timeout |
|
||||
| `0x02` | `ERR_I2C_NAK` | I2C slave did not ACK |
|
||||
| `0x03` | `ERR_BCM_TIMEOUT` | BCM4500 poll-ready timeout |
|
||||
| `0x04` | `ERR_BCM_NOT_READY` | BCM4500 not booted (tune attempted before boot) |
|
||||
| `0x05` | `ERR_BCM_VERIFY` | BCM4500 register verify mismatch |
|
||||
| `0x06` | `ERR_TUNE_FAIL` | Tune operation failed |
|
||||
| `0x07` | `ERR_EP0_TIMEOUT` | EP0 data phase timeout or short payload |
|
||||
| `0x08` | `ERR_GPIF_TIMEOUT` | GPIF did not reach idle state |
|
||||
| `0x09` | `ERR_EP2_TIMEOUT` | EP2 FIFO full timeout during sweep |
|
||||
| `0x0A` | `ERR_NOT_SUPPORTED` | Operation not supported (e.g., tone burst B) |
|
||||
| `0x0B` | `ERR_DISEQC_LEN` | DiSEqC message length invalid |
|
||||
| `0x0C` | `ERR_DISEQC_TIMER` | Timer2 overflow timeout during DiSEqC |
|
||||
| `0x0D` | `ERR_WDT_FIRED` | Watchdog fired — LNB power was cut |
|
||||
|
||||
## I2C Return-Value Checks
|
||||
|
||||
All I2C write operations in sweep and scan functions now check return values. Failed writes skip to the next iteration instead of proceeding with stale data:
|
||||
|
||||
- `do_param_sweep()` — `bcm_indirect_write_block()` and `bcm_direct_write()` calls
|
||||
- `do_spectrum_sweep()` — `bcm_indirect_write_block()` calls
|
||||
- `do_blind_scan()` — `bcm_indirect_write_block()` and `bcm_direct_write()` calls
|
||||
- `do_adaptive_blind_scan()` — same pattern
|
||||
|
||||
## do_tune() Safety
|
||||
|
||||
The `do_tune()` function was the most dangerous code path — it performed a 12-byte I2C write using the fx2lib `i2c_write()` function, which has no timeout protection. In v3.05.0:
|
||||
|
||||
- The fx2lib `i2c_write()` call is replaced with `i2c_write_multi_timeout()`, which applies the standard I2C timeout to every byte
|
||||
- Both `bcm_poll_ready()` calls check their return values
|
||||
- Both `bcm_direct_write()` calls check their return values
|
||||
- An early guard rejects the tune if the BCM4500 is not booted (`ERR_BCM_NOT_READY`)
|
||||
|
||||
## DiSEqC Safety
|
||||
|
||||
- **Tone burst B** is explicitly rejected with `ERR_NOT_SUPPORTED` — the hardware only supports tone burst A (unmodulated carrier)
|
||||
- **Invalid message lengths** (<3 or >6 bytes) set `ERR_DISEQC_LEN`
|
||||
- **Timer2 waits** in `diseqc_wait_ticks()` have per-tick timeout protection — a stuck Timer2 no longer hangs the firmware
|
||||
- `diseqc_tone_burst()` was refactored to use `diseqc_wait_ticks()` instead of three separate bare `while (!TF2)` loops
|
||||
|
||||
## Other Fixes
|
||||
|
||||
| Fix | Description |
|
||||
|-----|-------------|
|
||||
| `hp_changes` saturation | Counter capped at 0xFFFF to prevent u16 wrap-around |
|
||||
| `stream_diag_poll()` | Only updates `sd_last_status` / `sd_last_lock` on successful I2C reads |
|
||||
| `BCM_LOCK_BIT` constant | Replaces 4 hardcoded `0x20` literals with named constant |
|
||||
| Hotplug data integrity | `hp_prev` bitmap is only updated after a successful scan completes |
|
||||
| EP0BUF zero-fill | Commands 0x87 and 0xB7 zero-fill EP0BUF before indirect reads, ensuring I2C failures return zeros instead of stale buffer contents |
|
||||
|
||||
## Memory Budget
|
||||
|
||||
```
|
||||
Code: 13,079 / 15,360 bytes (85%) +948 bytes from v3.04
|
||||
XRAM: 218 / 512 bytes (43%) +2 bytes (wdt_counter, wdt_armed)
|
||||
Stack: 132 bytes available unchanged
|
||||
```
|
||||
|
||||
The safety additions cost 948 bytes of code space — well within the 2,281-byte margin. The 8051 stack starts at `0x7C` with 132 bytes, which is sufficient for the deepest call chain (vendor command → sweep → I2C helpers → Timer0 ISR).
|
||||
|
||||
## Hardware Validation
|
||||
|
||||
Firmware v3.05.0 was loaded onto physical hardware and tested:
|
||||
|
||||
| Test | Result |
|
||||
|------|--------|
|
||||
| Firmware load + re-enumeration | Pass |
|
||||
| Version readback (0x92) | `v3.05.0 (2026-02-16)` |
|
||||
| BCM4500 boot (0x89) | `STARTED \| FW_LOADED`, stage `COMPLETE` |
|
||||
| I2C bus scan (0xB4) | Found BCM4500 (0x08), tuner (0x10), EEPROM (0x51) |
|
||||
| Spectrum sweep (950–2150 MHz) | 121 points, no hang |
|
||||
| LNB power control (13V/18V/22kHz) | All config bits track correctly |
|
||||
| DiSEqC 1.0 switch | No error |
|
||||
| DiSEqC tone burst A | No error |
|
||||
| DiSEqC 1.2 motor commands | No error |
|
||||
| Tune attempt (no signal) | No hang, `last_error` stays OK |
|
||||
| Signal monitor (no signal) | 0.0 dB SNR, no lock |
|
||||
| Adversarial test suite | 55/55 passed |
|
||||
| Watchdog false-fire | None observed |
|
||||
|
||||
See [Safety Testing](/tools/safety-testing/) for the full adversarial test methodology and results.
|
||||
|
||||
## What Changed from v3.04
|
||||
|
||||
| Feature | v3.04 | v3.05 |
|
||||
|---------|-------|-------|
|
||||
| Software watchdog | None | Timer0-based, 2-second window |
|
||||
| I2C timeout paths | Helpers only | All paths including bus scan, DiSEqC |
|
||||
| EP0 BUSY protection | None (bare spin) | `ep0_wait_data()` with timeout |
|
||||
| GPIF idle protection | None (bare spin) | Timeout with `ERR_GPIF_TIMEOUT` |
|
||||
| EP2 full protection | None (bare spin) | Timeout with `ERR_EP2_TIMEOUT` |
|
||||
| `do_tune()` I2C | fx2lib `i2c_write()` (no timeout) | `i2c_write_multi_timeout()` |
|
||||
| Error codes | 6 (`0x00`–`0x05`) | 14 (`0x00`–`0x0D`) |
|
||||
| Payload validation | None | EP0BCL checked against expected size |
|
||||
| DiSEqC Timer2 | Bare `while (!TF2)` | Timeout-protected `diseqc_wait_ticks()` |
|
||||
| Sweep I2C checks | Unchecked writes | Returns checked, `continue` on fail |
|
||||
| Adversarial testing | None | 55-test Hamilton suite |
|
||||
---
|
||||
title: Custom Firmware v3.05
|
||||
description: Safety-hardened firmware with software watchdog, timeout protection on all I2C/USB/GPIF paths, and comprehensive error reporting.
|
||||
---
|
||||
|
||||
import { Badge, Aside, Steps } from '@astrojs/starlight/components';
|
||||
|
||||
Firmware v3.05.0 is the result of a systematic safety review applied to the entire codebase. Every infinite-spin loop has been replaced with timeout-protected equivalents, a software watchdog cuts LNB power if the main loop stalls, and every failure mode now sets a specific error code readable by the host. <Badge text="v3.05" variant="success" /> <Badge text="Safety" variant="caution" />
|
||||
|
||||
Built on the [v3.04 codebase](/firmware/custom-v301/) — same SDCC + fx2lib toolchain, same stock-compatible command set, plus all custom commands from [v3.02](/firmware/custom-v302/) through v3.04.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Version ID | `0x030500` |
|
||||
| Date | 2026-02-16 |
|
||||
| Code size | 13,079 / 15,360 bytes (85%) |
|
||||
| XRAM | 218 / 512 bytes (43%) |
|
||||
| Stack | 132 bytes available |
|
||||
| Source lines | 2,256 |
|
||||
| New commands | None (safety hardening of existing paths) |
|
||||
|
||||
## Motivation
|
||||
|
||||
The SkyWalker-1 controls an LNB power supply capable of 750 mA at 18V with no hardware watchdog on the FX2LP. A firmware hang leaves the power supply energized indefinitely. Prior firmware versions contained multiple paths where a stuck I2C bus, an unresponsive GPIF, or a slow EP0 transfer could spin forever without recovery.
|
||||
|
||||
A structured safety review identified 21 issues across 4 severity levels:
|
||||
|
||||
| Severity | Count | Description |
|
||||
|----------|-------|-------------|
|
||||
| Critical | 3 | Infinite hangs in I2C bus scan, `do_tune()`, and DiSEqC Timer2 waits |
|
||||
| High | 6 | Unprotected GPIF/EP0/EP2 waits, unchecked I2C returns in sweep functions |
|
||||
| Medium | 7 | Missing error codes, payload validation, lock-bit magic numbers |
|
||||
| Low | 5 | Code clarity, documentation, saturation guards |
|
||||
|
||||
## Software Watchdog
|
||||
|
||||
A Timer0-based software watchdog provides a safety backstop for any remaining or unknown hang paths.
|
||||
|
||||
**Architecture:**
|
||||
|
||||
- Timer0 runs in Mode 1 (16-bit, no auto-reload) at 4 MHz (48 MHz / 12)
|
||||
- Overflow period: 16.384 ms per tick
|
||||
- Watchdog window: 122 ticks = ~2 seconds
|
||||
- The main loop calls `wdt_kick()` on every iteration to reset the counter
|
||||
- Long-running operations (spectrum sweep, blind scan) call `wdt_kick()` inside their loops
|
||||
|
||||
**If the watchdog fires:**
|
||||
|
||||
<Steps>
|
||||
1. Timer0 ISR sets `IOA` to cut LNB power (`PIN_PWR_EN` off, `PIN_PWR_DIS` on)
|
||||
2. ISR sets `wdt_armed = 2` (fired state) — prevents re-arming
|
||||
3. Main loop detects `wdt_armed == 2` on next iteration (if it recovers)
|
||||
4. Main loop sets `last_error = ERR_WDT_FIRED` (0x0D) and clears `config_status`
|
||||
5. Host can read `ERR_WDT_FIRED` via `GET_LAST_ERROR` (0xBC)
|
||||
</Steps>
|
||||
|
||||
<Aside type="caution">
|
||||
The watchdog shares `IOA` with the main loop via read-modify-write. To avoid RMW races between the ISR and main-loop GPIO operations, `wdt_armed` uses a tri-state protocol: `0` = off, `1` = armed, `2` = fired. The ISR only writes `IOA` once (on fire), and `wdt_kick()` refuses to re-arm when `wdt_armed == 2`.
|
||||
</Aside>
|
||||
|
||||
## Timeout Protection
|
||||
|
||||
Every bare spin loop in the firmware has been replaced with a countdown-protected equivalent. These are the paths that could previously hang forever:
|
||||
|
||||
| Path | Location | Timeout | Error Code |
|
||||
|------|----------|---------|------------|
|
||||
| `i2c_wait_done()` | I2C DONE bit | 6,000 iterations | `ERR_I2C_TIMEOUT` |
|
||||
| `i2c_wait_stop()` | I2C STOP bit | 6,000 iterations | `ERR_I2C_TIMEOUT` |
|
||||
| `ep0_wait_data()` | EP0 BUSY bit | 6,000 iterations | `ERR_EP0_TIMEOUT` |
|
||||
| `gpif_start()` | GPIF idle | 60,000 iterations | `ERR_GPIF_TIMEOUT` |
|
||||
| `gpif_stop()` | GPIF idle | 60,000 iterations | `ERR_GPIF_TIMEOUT` |
|
||||
| EP2 FIFO full | `do_param_sweep()` | 60,000 iterations | `ERR_EP2_TIMEOUT` |
|
||||
| EP2 FIFO full | `do_spectrum_sweep()` | 60,000 iterations | `ERR_EP2_TIMEOUT` |
|
||||
| Timer2 overflow | `diseqc_wait_ticks()` | 6,000 per tick | `ERR_DISEQC_TIMER` |
|
||||
| I2C bus scan | `0xB4` handler | via `i2c_wait_done/stop` | `ERR_I2C_TIMEOUT` |
|
||||
|
||||
The `ep0_wait_data()` helper replaces 7 separate bare `while (EP0CS & bmEPBUSY)` loops in vendor command handlers. After the wait, each handler validates `EP0BCL` against the expected payload size.
|
||||
|
||||
## Error Codes
|
||||
|
||||
All error codes are set in the `last_error` variable and readable via `GET_LAST_ERROR` (0xBC). The error is sticky — it persists until overwritten by a new error.
|
||||
|
||||
| Code | Name | Meaning |
|
||||
|------|------|---------|
|
||||
| `0x00` | `ERR_OK` | No error |
|
||||
| `0x01` | `ERR_I2C_TIMEOUT` | I2C DONE or STOP bit timeout |
|
||||
| `0x02` | `ERR_I2C_NAK` | I2C slave did not ACK |
|
||||
| `0x03` | `ERR_BCM_TIMEOUT` | BCM4500 poll-ready timeout |
|
||||
| `0x04` | `ERR_BCM_NOT_READY` | BCM4500 not booted (tune attempted before boot) |
|
||||
| `0x05` | `ERR_BCM_VERIFY` | BCM4500 register verify mismatch |
|
||||
| `0x06` | `ERR_TUNE_FAIL` | Tune operation failed |
|
||||
| `0x07` | `ERR_EP0_TIMEOUT` | EP0 data phase timeout or short payload |
|
||||
| `0x08` | `ERR_GPIF_TIMEOUT` | GPIF did not reach idle state |
|
||||
| `0x09` | `ERR_EP2_TIMEOUT` | EP2 FIFO full timeout during sweep |
|
||||
| `0x0A` | `ERR_NOT_SUPPORTED` | Operation not supported (e.g., tone burst B) |
|
||||
| `0x0B` | `ERR_DISEQC_LEN` | DiSEqC message length invalid |
|
||||
| `0x0C` | `ERR_DISEQC_TIMER` | Timer2 overflow timeout during DiSEqC |
|
||||
| `0x0D` | `ERR_WDT_FIRED` | Watchdog fired — LNB power was cut |
|
||||
|
||||
## I2C Return-Value Checks
|
||||
|
||||
All I2C write operations in sweep and scan functions now check return values. Failed writes skip to the next iteration instead of proceeding with stale data:
|
||||
|
||||
- `do_param_sweep()` — `bcm_indirect_write_block()` and `bcm_direct_write()` calls
|
||||
- `do_spectrum_sweep()` — `bcm_indirect_write_block()` calls
|
||||
- `do_blind_scan()` — `bcm_indirect_write_block()` and `bcm_direct_write()` calls
|
||||
- `do_adaptive_blind_scan()` — same pattern
|
||||
|
||||
## do_tune() Safety
|
||||
|
||||
The `do_tune()` function was the most dangerous code path — it performed a 12-byte I2C write using the fx2lib `i2c_write()` function, which has no timeout protection. In v3.05.0:
|
||||
|
||||
- The fx2lib `i2c_write()` call is replaced with `i2c_write_multi_timeout()`, which applies the standard I2C timeout to every byte
|
||||
- Both `bcm_poll_ready()` calls check their return values
|
||||
- Both `bcm_direct_write()` calls check their return values
|
||||
- An early guard rejects the tune if the BCM4500 is not booted (`ERR_BCM_NOT_READY`)
|
||||
|
||||
## DiSEqC Safety
|
||||
|
||||
- **Tone burst B** is explicitly rejected with `ERR_NOT_SUPPORTED` — the hardware only supports tone burst A (unmodulated carrier)
|
||||
- **Invalid message lengths** (<3 or >6 bytes) set `ERR_DISEQC_LEN`
|
||||
- **Timer2 waits** in `diseqc_wait_ticks()` have per-tick timeout protection — a stuck Timer2 no longer hangs the firmware
|
||||
- `diseqc_tone_burst()` was refactored to use `diseqc_wait_ticks()` instead of three separate bare `while (!TF2)` loops
|
||||
|
||||
## Other Fixes
|
||||
|
||||
| Fix | Description |
|
||||
|-----|-------------|
|
||||
| `hp_changes` saturation | Counter capped at 0xFFFF to prevent u16 wrap-around |
|
||||
| `stream_diag_poll()` | Only updates `sd_last_status` / `sd_last_lock` on successful I2C reads |
|
||||
| `BCM_LOCK_BIT` constant | Replaces 4 hardcoded `0x20` literals with named constant |
|
||||
| Hotplug data integrity | `hp_prev` bitmap is only updated after a successful scan completes |
|
||||
| EP0BUF zero-fill | Commands 0x87 and 0xB7 zero-fill EP0BUF before indirect reads, ensuring I2C failures return zeros instead of stale buffer contents |
|
||||
|
||||
## Memory Budget
|
||||
|
||||
```
|
||||
Code: 13,079 / 15,360 bytes (85%) +948 bytes from v3.04
|
||||
XRAM: 218 / 512 bytes (43%) +2 bytes (wdt_counter, wdt_armed)
|
||||
Stack: 132 bytes available unchanged
|
||||
```
|
||||
|
||||
The safety additions cost 948 bytes of code space — well within the 2,281-byte margin. The 8051 stack starts at `0x7C` with 132 bytes, which is sufficient for the deepest call chain (vendor command → sweep → I2C helpers → Timer0 ISR).
|
||||
|
||||
## Hardware Validation
|
||||
|
||||
Firmware v3.05.0 was loaded onto physical hardware and tested:
|
||||
|
||||
| Test | Result |
|
||||
|------|--------|
|
||||
| Firmware load + re-enumeration | Pass |
|
||||
| Version readback (0x92) | `v3.05.0 (2026-02-16)` |
|
||||
| BCM4500 boot (0x89) | `STARTED \| FW_LOADED`, stage `COMPLETE` |
|
||||
| I2C bus scan (0xB4) | Found BCM4500 (0x08), tuner (0x10), EEPROM (0x51) |
|
||||
| Spectrum sweep (950–2150 MHz) | 121 points, no hang |
|
||||
| LNB power control (13V/18V/22kHz) | All config bits track correctly |
|
||||
| DiSEqC 1.0 switch | No error |
|
||||
| DiSEqC tone burst A | No error |
|
||||
| DiSEqC 1.2 motor commands | No error |
|
||||
| Tune attempt (no signal) | No hang, `last_error` stays OK |
|
||||
| Signal monitor (no signal) | 0.0 dB SNR, no lock |
|
||||
| Adversarial test suite | 55/55 passed |
|
||||
| Watchdog false-fire | None observed |
|
||||
|
||||
See [Safety Testing](/tools/safety-testing/) for the full adversarial test methodology and results.
|
||||
|
||||
## What Changed from v3.04
|
||||
|
||||
| Feature | v3.04 | v3.05 |
|
||||
|---------|-------|-------|
|
||||
| Software watchdog | None | Timer0-based, 2-second window |
|
||||
| I2C timeout paths | Helpers only | All paths including bus scan, DiSEqC |
|
||||
| EP0 BUSY protection | None (bare spin) | `ep0_wait_data()` with timeout |
|
||||
| GPIF idle protection | None (bare spin) | Timeout with `ERR_GPIF_TIMEOUT` |
|
||||
| EP2 full protection | None (bare spin) | Timeout with `ERR_EP2_TIMEOUT` |
|
||||
| `do_tune()` I2C | fx2lib `i2c_write()` (no timeout) | `i2c_write_multi_timeout()` |
|
||||
| Error codes | 6 (`0x00`–`0x05`) | 14 (`0x00`–`0x0D`) |
|
||||
| Payload validation | None | EP0BCL checked against expected size |
|
||||
| DiSEqC Timer2 | Bare `while (!TF2)` | Timeout-protected `diseqc_wait_ticks()` |
|
||||
| Sweep I2C checks | Unchecked writes | Returns checked, `continue` on fail |
|
||||
| Adversarial testing | None | 55-test Hamilton suite |
|
||||
|
|
|
|||
|
|
@ -1,243 +1,243 @@
|
|||
---
|
||||
title: FW2.13 Sub-Variant Comparison
|
||||
description: Binary and functional comparison of v2.13 firmware sub-variants FW1, FW2, and FW3.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
The v2.13 firmware was distributed as three sub-variants via the `SW1_update_2_13_x.exe` Windows updater tool. Ghidra analysis reveals that these target fundamentally different hardware interfaces, not just minor revisions.
|
||||
|
||||
## Overview
|
||||
|
||||
| Aspect | FW1 (v2.13.1) | FW2 (v2.13.2) | FW3 (v2.13.3) |
|
||||
|--------|---------------|---------------|---------------|
|
||||
| Version ID | `0x020D01` | `0x020D01` | `0x020D01` |
|
||||
| Build date | 2010-03-12 | 2010-03-12 | 2010-03-12 |
|
||||
| Functions | 82 | 83 | 83 |
|
||||
| Binary size | 9,322 bytes | 9,377 bytes | 9,369 bytes |
|
||||
| Stack pointer | `0x50` | `0x50` | **`0x52`** |
|
||||
| P0 init | `0xA4` | `0xA4` | **`0xA0`** |
|
||||
| Status register | INTMEM `0x4F` | INTMEM `0x4F` | **INTMEM `0x51`** |
|
||||
| Demod interface | **I2C bus** | **Parallel bus (P0/P1)** | **Parallel bus (enhanced)** |
|
||||
| Config source | Hardcoded | External (`0xE080`--`0xE08E`) | External (`0xE080`--`0xE08E`) |
|
||||
|
||||
## Hardware Detection
|
||||
|
||||
All three sub-variants report the same version ID (`0x020D01`) to the host. The Windows updater `SW1_update_2_13_x.exe` selects which sub-variant to flash, but the detection mechanism is not yet fully understood.
|
||||
|
||||
<Aside type="caution" title="Open Investigation">
|
||||
The exact hardware detection method remains unknown. The updater binary has not been fully disassembled. Candidates include a GPIO strap read (cheap, common in consumer electronics), an I2C device ID probe (the demod responds differently on I2C vs. parallel-bus hardware), or a USB descriptor field that encodes the PCB revision. If you have access to the updater binary or multiple hardware revisions, this would be a valuable area to investigate.
|
||||
</Aside>
|
||||
|
||||
### Distinguishing Characteristics
|
||||
|
||||
Although the updater's detection method is opaque, the firmware binaries themselves contain clear markers that distinguish each target hardware:
|
||||
|
||||
| Characteristic | FW1 | FW2 | FW3 | Docs |
|
||||
|---------------|-----|-----|-----|------|
|
||||
| P0 init value | `0xA4` | `0xA4` | `0xA0` (bit 2 cleared) | [Register Map — IRAM](/bcm4500/register-map/#fx2-iram-by-firmware-version) |
|
||||
| Stack pointer | `0x50` | `0x50` | `0x52` (+2 for status reg) | [Register Map — IRAM](/bcm4500/register-map/#fx2-iram-by-firmware-version) |
|
||||
| Config status IRAM | `0x4F` | `0x4F` | `0x51` | [Config Status](/usb/config-status/) |
|
||||
| I2C buffer IRAM | `0x48`/`0x49` | `0x48`/`0x49` | `0x4A`/`0x4B` | [Register Map — IRAM](/bcm4500/register-map/#fx2-iram-by-firmware-version) |
|
||||
| Demod interface | I2C bus | Parallel (P0/P1) | Parallel (enhanced, dual-phase) | See tabs below |
|
||||
| Config source | Hardcoded | External `0xE080`--`0xE08E` | External `0xE080`--`0xE08E` | [Register Map — XRAM](/bcm4500/register-map/#fw2fw3-external-configuration) |
|
||||
|
||||
### FW1 Demod Type Detection
|
||||
|
||||
FW1 is the only sub-variant that performs demodulator type identification at runtime. Function `FUN_CODE_1405` reads the P1 port after an I2C transaction and matches the result against known silicon signatures:
|
||||
|
||||
| Type Code | P1 Signature | Likely Silicon |
|
||||
|-----------|--------------|----------------|
|
||||
| Type 3 | `0xA5` or `0xB5` | Original BCM4500 |
|
||||
| Type 4 | `0x5A` | BCM4500 revision A |
|
||||
| Type 5 | `0x5B` | BCM4500 revision B |
|
||||
| Type 6 | `0x5C` | BCM4500 revision C |
|
||||
|
||||
FW2 and FW3 use a different signature check (`P1 ^ 0x1D`) through the parallel bus, suggesting the demod variant is already known at flash time on those PCBs.
|
||||
|
||||
## Hardware Interface Evolution
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="FW1 -- I2C Bus">
|
||||
|
||||
### FW1 (v2.13.1) <Badge text="I2C" variant="note" />
|
||||
|
||||
FW1 targets the original SkyWalker-1 PCB with an **I2C-connected demodulator**. The FX2 communicates with the demod entirely through standard I2C master-mode transactions.
|
||||
|
||||
**Evidence from `FUN_CODE_0eea`:**
|
||||
- Uses `FUN_CODE_23ae` (I2C START), `FUN_CODE_23ee` (I2C byte write), `FUN_CODE_23d0` (I2C address)
|
||||
- Standard I2C retry with NACK detection
|
||||
- Timer2-based I2C timeout (TR2 check in vendor handler)
|
||||
- Reads back via `FUN_CODE_2164`
|
||||
|
||||
**Unique functions:**
|
||||
- `FUN_CODE_0fc7` -- I2C write-with-retry (20 attempts via I2C bus)
|
||||
- `FUN_CODE_1405` -- Tuner/demodulator identification via I2C + P1 port reads with signature matching
|
||||
- `FUN_CODE_14b9` -- Calibrated delay function with CPUCS clock divider awareness
|
||||
|
||||
**Demodulator type detection** (from `FUN_CODE_1405`):
|
||||
| Type Code | P1 Signature |
|
||||
|-----------|--------------|
|
||||
| Type 3 | `0xA5` or `0xB5` |
|
||||
| Type 4 | `0x5A` |
|
||||
| Type 5 | `0x5B` |
|
||||
| Type 6 | `0x5C` |
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="FW2 -- Parallel Bus">
|
||||
|
||||
### FW2 (v2.13.2) <Badge text="Parallel" variant="caution" />
|
||||
|
||||
FW2 targets a revised PCB with a **parallel-bus connected demodulator**. The demod data port is connected directly to the FX2's P1, with P0 bits controlling bus signals.
|
||||
|
||||
**Evidence from `FUN_CODE_0eea`:**
|
||||
- Reads demod type from address table (BANK1 pointer + offset)
|
||||
- Uses `FUN_CODE_11b6` for demod selection
|
||||
- Toggles P0 bits 6/7 for bus control (P0.6 = chip select, P0.7 = read strobe)
|
||||
- Reads data from **P1 port** (8-bit parallel data bus)
|
||||
- Single-phase read: one P1 read per bus cycle
|
||||
|
||||
**Bus protocol (decompiled):**
|
||||
```c title="FW2 parallel bus read"
|
||||
uVar1 = P1; // Read data with one bus state
|
||||
P0 |= 0x40; // Change control line
|
||||
uVar2 = P1; // Read again with new state
|
||||
FUN_CODE_1b2a(uVar2, uVar1); // Process both samples
|
||||
```
|
||||
|
||||
**Configuration loading:**
|
||||
- Loads 15 configuration bytes from external memory (`0xE080`--`0xE08E`) into demod registers (`0xE6C0`--`0xE6CD`)
|
||||
- Same device signature matching as FW1 but via parallel bus (`P1 ^ 0x1D` check)
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="FW3 -- Enhanced Parallel">
|
||||
|
||||
### FW3 (v2.13.3) <Badge text="Enhanced" variant="success" />
|
||||
|
||||
FW3 targets a further revised PCB with the same parallel-bus architecture as FW2 but with a **different bus timing protocol**. Uses dual-phase reads with OR-accumulation.
|
||||
|
||||
**Evidence from `FUN_CODE_0eea`:**
|
||||
- Initializes OR-accumulators: `DAT_INTMEM_3f = 0; DAT_INTMEM_40 = 0`
|
||||
- Sets P0 | 0x80 once at start (not per-iteration like FW2)
|
||||
- Two separate P1 reads per cycle with different P0.6 states
|
||||
- OR-accumulates results before processing
|
||||
|
||||
**Bus protocol (decompiled):**
|
||||
```c title="FW3 dual-phase parallel bus read"
|
||||
DAT_INTMEM_3f = 0;
|
||||
DAT_INTMEM_40 = 0; // Clear accumulators
|
||||
|
||||
// Phase 1: P0.6 high
|
||||
P0 |= 0x44;
|
||||
bVar2 = P1;
|
||||
DAT_INTMEM_3f |= bVar2; // OR-accumulate
|
||||
|
||||
// Phase 2: P0.6 low
|
||||
P0 &= ~0x40;
|
||||
bVar2 = P1;
|
||||
DAT_INTMEM_40 |= bVar2; // OR-accumulate
|
||||
|
||||
FUN_CODE_1b2a(0, DAT_INTMEM_3f, DAT_INTMEM_40);
|
||||
```
|
||||
|
||||
**Why OR-accumulation?** This pattern suggests the demod chip variant has either:
|
||||
- Open-drain outputs requiring multiple read cycles
|
||||
- Bus settling time issues on the newer PCB layout
|
||||
- A chip revision that serializes data across multiple bus phases
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Binary Distance Matrix
|
||||
|
||||
Byte-level differences between sub-variants:
|
||||
|
||||
| Pair | Different Bytes | Percentage Different |
|
||||
|------|----------------:|---------------------:|
|
||||
| FW1 vs FW2 | 3,993 | 42.8% |
|
||||
| FW1 vs FW3 | 3,789 | 40.6% |
|
||||
| FW2 vs FW3 | **1,525** | **16.5%** |
|
||||
|
||||
FW2 and FW3 are 83.5% identical at the byte level, confirming they share the same parallel-bus architecture. FW1 diverges significantly because it uses a completely different bus interface (I2C vs. parallel).
|
||||
|
||||
## Memory Comparison at Key Offsets
|
||||
|
||||
### Identical Regions
|
||||
|
||||
These regions are byte-identical across all three sub-variants:
|
||||
|
||||
| Address Range | Content |
|
||||
|---------------|---------|
|
||||
| `0x0000`--`0x000F` | RESET vector (`LJMP 0x170D`), INT0 handler |
|
||||
| `0x0B88`--`0x0B9F` | Init table (same XRAM register initialization) |
|
||||
| `0x06D9`--`0x06F0` | Generic memory access utilities |
|
||||
| `0x1740`--`0x174F` | Bit manipulation lookup table |
|
||||
|
||||
### Critical Divergence: `CODE:0EEA`
|
||||
|
||||
This is where the three sub-variants diverge most dramatically:
|
||||
|
||||
```
|
||||
FW1: 8f44 8c45 8d46 8b47 754a14 e544 b451...
|
||||
(I2C transfer parameters in registers)
|
||||
|
||||
FW2: 753e14 e50d 240a f582 e435 0cf5 83e0...
|
||||
(reads from DPTR+offset table)
|
||||
|
||||
FW3: 753e14 e4f5 3ff5 40 e50d 240a f582...
|
||||
(similar to FW2 + accumulator initialization)
|
||||
```
|
||||
|
||||
FW1's `FUN_CODE_0eea` is a standard I2C master transfer function. FW2/FW3's version is a parallel bus demodulator interface.
|
||||
|
||||
### Thunk Target Divergence (`CODE:1500`)
|
||||
|
||||
```
|
||||
FW1: 02 2252 00 02 22dd 00 02 22c7 00 02 226a 00
|
||||
FW2: 02 228d 00 02 2318 00 02 2302 00 02 22a5 00
|
||||
FW3: 02 228d 00 02 2318 00 02 2302 00 02 22a5 00
|
||||
```
|
||||
|
||||
FW2 and FW3 share identical interrupt handler targets. FW1 jumps to different addresses, reflecting its different internal function layout.
|
||||
|
||||
## Detailed Differences
|
||||
|
||||
### Stack Pointer and Status Register
|
||||
|
||||
| Property | FW1 | FW2 | FW3 |
|
||||
|----------|-----|-----|-----|
|
||||
| SP value | `0x50` | `0x50` | `0x52` |
|
||||
| Status IRAM | `0x4F` | `0x4F` | `0x51` |
|
||||
| I2C buffer IRAM | `0x48`/`0x49` | `0x48`/`0x49` | `0x4A`/`0x4B` |
|
||||
|
||||
FW3 pushes the stack pointer up by 2 bytes to make room for the additional status register at IRAM `0x51`. The 2-byte SP difference exactly accounts for moving the status register from `0x4F` to `0x51`.
|
||||
|
||||
### P0 Init Value
|
||||
|
||||
| Variant | P0 Init | Binary | Difference |
|
||||
|---------|---------|--------|------------|
|
||||
| FW1/FW2 | `0xA4` | `1010 0100` | Bit 2 = 1 |
|
||||
| FW3 | `0xA0` | `1010 0000` | Bit 2 = 0 |
|
||||
|
||||
P0 bit 2 controls a GPIO signal likely related to demodulator interface mode or reset polarity on the FW3 target PCB.
|
||||
|
||||
### Vendor Handler Differences
|
||||
|
||||
| Feature | FW1 | FW2/FW3 |
|
||||
|---------|-----|---------|
|
||||
| Case `0x3D3` | TR2 timer check (I2C timeout) | OR operation (parallel bus) |
|
||||
| Case `0x421`-`0x423` | Simple check | P2.1 write + rotate-left (bus direction) |
|
||||
| Error path | `func_0x06e4` | `DAT=0x0` |
|
||||
|
||||
FW1's timer-based case is used for I2C bus timeout recovery. FW2/FW3's rotate-left and P2.1 write is a parallel bus data direction control.
|
||||
|
||||
## Hardware Progression Theory
|
||||
|
||||
The three sub-variants represent an evolutionary progression:
|
||||
|
||||
1. **FW1 (v2.13.1)**: Original design with I2C-connected demodulator. Simple interface but limited in bandwidth. The FX2 acts purely as an I2C master bridge.
|
||||
|
||||
2. **FW2 (v2.13.2)**: Redesigned with parallel-bus demodulator for higher throughput. P1 carries 8-bit data, P0 provides control signals. External calibration data at `0xE080`--`0xE08E`.
|
||||
|
||||
3. **FW3 (v2.13.3)**: Refined parallel interface for a newer demod silicon revision. Dual-phase reads with OR-accumulation handle bus timing differences. Additional IRAM state tracking (SP bumped to `0x52`).
|
||||
|
||||
All three support the same modulation types (DVB-S QPSK, Turbo QPSK/8PSK/16QAM, DCII, DSS) and the same demod type codes (3--6). The differences are purely hardware interface, not feature set.
|
||||
---
|
||||
title: FW2.13 Sub-Variant Comparison
|
||||
description: Binary and functional comparison of v2.13 firmware sub-variants FW1, FW2, and FW3.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
The v2.13 firmware was distributed as three sub-variants via the `SW1_update_2_13_x.exe` Windows updater tool. Ghidra analysis reveals that these target fundamentally different hardware interfaces, not just minor revisions.
|
||||
|
||||
## Overview
|
||||
|
||||
| Aspect | FW1 (v2.13.1) | FW2 (v2.13.2) | FW3 (v2.13.3) |
|
||||
|--------|---------------|---------------|---------------|
|
||||
| Version ID | `0x020D01` | `0x020D01` | `0x020D01` |
|
||||
| Build date | 2010-03-12 | 2010-03-12 | 2010-03-12 |
|
||||
| Functions | 82 | 83 | 83 |
|
||||
| Binary size | 9,322 bytes | 9,377 bytes | 9,369 bytes |
|
||||
| Stack pointer | `0x50` | `0x50` | **`0x52`** |
|
||||
| P0 init | `0xA4` | `0xA4` | **`0xA0`** |
|
||||
| Status register | INTMEM `0x4F` | INTMEM `0x4F` | **INTMEM `0x51`** |
|
||||
| Demod interface | **I2C bus** | **Parallel bus (P0/P1)** | **Parallel bus (enhanced)** |
|
||||
| Config source | Hardcoded | External (`0xE080`--`0xE08E`) | External (`0xE080`--`0xE08E`) |
|
||||
|
||||
## Hardware Detection
|
||||
|
||||
All three sub-variants report the same version ID (`0x020D01`) to the host. The Windows updater `SW1_update_2_13_x.exe` selects which sub-variant to flash, but the detection mechanism is not yet fully understood.
|
||||
|
||||
<Aside type="caution" title="Open Investigation">
|
||||
The exact hardware detection method remains unknown. The updater binary has not been fully disassembled. Candidates include a GPIO strap read (cheap, common in consumer electronics), an I2C device ID probe (the demod responds differently on I2C vs. parallel-bus hardware), or a USB descriptor field that encodes the PCB revision. If you have access to the updater binary or multiple hardware revisions, this would be a valuable area to investigate.
|
||||
</Aside>
|
||||
|
||||
### Distinguishing Characteristics
|
||||
|
||||
Although the updater's detection method is opaque, the firmware binaries themselves contain clear markers that distinguish each target hardware:
|
||||
|
||||
| Characteristic | FW1 | FW2 | FW3 | Docs |
|
||||
|---------------|-----|-----|-----|------|
|
||||
| P0 init value | `0xA4` | `0xA4` | `0xA0` (bit 2 cleared) | [Register Map — IRAM](/bcm4500/register-map/#fx2-iram-by-firmware-version) |
|
||||
| Stack pointer | `0x50` | `0x50` | `0x52` (+2 for status reg) | [Register Map — IRAM](/bcm4500/register-map/#fx2-iram-by-firmware-version) |
|
||||
| Config status IRAM | `0x4F` | `0x4F` | `0x51` | [Config Status](/usb/config-status/) |
|
||||
| I2C buffer IRAM | `0x48`/`0x49` | `0x48`/`0x49` | `0x4A`/`0x4B` | [Register Map — IRAM](/bcm4500/register-map/#fx2-iram-by-firmware-version) |
|
||||
| Demod interface | I2C bus | Parallel (P0/P1) | Parallel (enhanced, dual-phase) | See tabs below |
|
||||
| Config source | Hardcoded | External `0xE080`--`0xE08E` | External `0xE080`--`0xE08E` | [Register Map — XRAM](/bcm4500/register-map/#fw2fw3-external-configuration) |
|
||||
|
||||
### FW1 Demod Type Detection
|
||||
|
||||
FW1 is the only sub-variant that performs demodulator type identification at runtime. Function `FUN_CODE_1405` reads the P1 port after an I2C transaction and matches the result against known silicon signatures:
|
||||
|
||||
| Type Code | P1 Signature | Likely Silicon |
|
||||
|-----------|--------------|----------------|
|
||||
| Type 3 | `0xA5` or `0xB5` | Original BCM4500 |
|
||||
| Type 4 | `0x5A` | BCM4500 revision A |
|
||||
| Type 5 | `0x5B` | BCM4500 revision B |
|
||||
| Type 6 | `0x5C` | BCM4500 revision C |
|
||||
|
||||
FW2 and FW3 use a different signature check (`P1 ^ 0x1D`) through the parallel bus, suggesting the demod variant is already known at flash time on those PCBs.
|
||||
|
||||
## Hardware Interface Evolution
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="FW1 -- I2C Bus">
|
||||
|
||||
### FW1 (v2.13.1) <Badge text="I2C" variant="note" />
|
||||
|
||||
FW1 targets the original SkyWalker-1 PCB with an **I2C-connected demodulator**. The FX2 communicates with the demod entirely through standard I2C master-mode transactions.
|
||||
|
||||
**Evidence from `FUN_CODE_0eea`:**
|
||||
- Uses `FUN_CODE_23ae` (I2C START), `FUN_CODE_23ee` (I2C byte write), `FUN_CODE_23d0` (I2C address)
|
||||
- Standard I2C retry with NACK detection
|
||||
- Timer2-based I2C timeout (TR2 check in vendor handler)
|
||||
- Reads back via `FUN_CODE_2164`
|
||||
|
||||
**Unique functions:**
|
||||
- `FUN_CODE_0fc7` -- I2C write-with-retry (20 attempts via I2C bus)
|
||||
- `FUN_CODE_1405` -- Tuner/demodulator identification via I2C + P1 port reads with signature matching
|
||||
- `FUN_CODE_14b9` -- Calibrated delay function with CPUCS clock divider awareness
|
||||
|
||||
**Demodulator type detection** (from `FUN_CODE_1405`):
|
||||
| Type Code | P1 Signature |
|
||||
|-----------|--------------|
|
||||
| Type 3 | `0xA5` or `0xB5` |
|
||||
| Type 4 | `0x5A` |
|
||||
| Type 5 | `0x5B` |
|
||||
| Type 6 | `0x5C` |
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="FW2 -- Parallel Bus">
|
||||
|
||||
### FW2 (v2.13.2) <Badge text="Parallel" variant="caution" />
|
||||
|
||||
FW2 targets a revised PCB with a **parallel-bus connected demodulator**. The demod data port is connected directly to the FX2's P1, with P0 bits controlling bus signals.
|
||||
|
||||
**Evidence from `FUN_CODE_0eea`:**
|
||||
- Reads demod type from address table (BANK1 pointer + offset)
|
||||
- Uses `FUN_CODE_11b6` for demod selection
|
||||
- Toggles P0 bits 6/7 for bus control (P0.6 = chip select, P0.7 = read strobe)
|
||||
- Reads data from **P1 port** (8-bit parallel data bus)
|
||||
- Single-phase read: one P1 read per bus cycle
|
||||
|
||||
**Bus protocol (decompiled):**
|
||||
```c title="FW2 parallel bus read"
|
||||
uVar1 = P1; // Read data with one bus state
|
||||
P0 |= 0x40; // Change control line
|
||||
uVar2 = P1; // Read again with new state
|
||||
FUN_CODE_1b2a(uVar2, uVar1); // Process both samples
|
||||
```
|
||||
|
||||
**Configuration loading:**
|
||||
- Loads 15 configuration bytes from external memory (`0xE080`--`0xE08E`) into demod registers (`0xE6C0`--`0xE6CD`)
|
||||
- Same device signature matching as FW1 but via parallel bus (`P1 ^ 0x1D` check)
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="FW3 -- Enhanced Parallel">
|
||||
|
||||
### FW3 (v2.13.3) <Badge text="Enhanced" variant="success" />
|
||||
|
||||
FW3 targets a further revised PCB with the same parallel-bus architecture as FW2 but with a **different bus timing protocol**. Uses dual-phase reads with OR-accumulation.
|
||||
|
||||
**Evidence from `FUN_CODE_0eea`:**
|
||||
- Initializes OR-accumulators: `DAT_INTMEM_3f = 0; DAT_INTMEM_40 = 0`
|
||||
- Sets P0 | 0x80 once at start (not per-iteration like FW2)
|
||||
- Two separate P1 reads per cycle with different P0.6 states
|
||||
- OR-accumulates results before processing
|
||||
|
||||
**Bus protocol (decompiled):**
|
||||
```c title="FW3 dual-phase parallel bus read"
|
||||
DAT_INTMEM_3f = 0;
|
||||
DAT_INTMEM_40 = 0; // Clear accumulators
|
||||
|
||||
// Phase 1: P0.6 high
|
||||
P0 |= 0x44;
|
||||
bVar2 = P1;
|
||||
DAT_INTMEM_3f |= bVar2; // OR-accumulate
|
||||
|
||||
// Phase 2: P0.6 low
|
||||
P0 &= ~0x40;
|
||||
bVar2 = P1;
|
||||
DAT_INTMEM_40 |= bVar2; // OR-accumulate
|
||||
|
||||
FUN_CODE_1b2a(0, DAT_INTMEM_3f, DAT_INTMEM_40);
|
||||
```
|
||||
|
||||
**Why OR-accumulation?** This pattern suggests the demod chip variant has either:
|
||||
- Open-drain outputs requiring multiple read cycles
|
||||
- Bus settling time issues on the newer PCB layout
|
||||
- A chip revision that serializes data across multiple bus phases
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Binary Distance Matrix
|
||||
|
||||
Byte-level differences between sub-variants:
|
||||
|
||||
| Pair | Different Bytes | Percentage Different |
|
||||
|------|----------------:|---------------------:|
|
||||
| FW1 vs FW2 | 3,993 | 42.8% |
|
||||
| FW1 vs FW3 | 3,789 | 40.6% |
|
||||
| FW2 vs FW3 | **1,525** | **16.5%** |
|
||||
|
||||
FW2 and FW3 are 83.5% identical at the byte level, confirming they share the same parallel-bus architecture. FW1 diverges significantly because it uses a completely different bus interface (I2C vs. parallel).
|
||||
|
||||
## Memory Comparison at Key Offsets
|
||||
|
||||
### Identical Regions
|
||||
|
||||
These regions are byte-identical across all three sub-variants:
|
||||
|
||||
| Address Range | Content |
|
||||
|---------------|---------|
|
||||
| `0x0000`--`0x000F` | RESET vector (`LJMP 0x170D`), INT0 handler |
|
||||
| `0x0B88`--`0x0B9F` | Init table (same XRAM register initialization) |
|
||||
| `0x06D9`--`0x06F0` | Generic memory access utilities |
|
||||
| `0x1740`--`0x174F` | Bit manipulation lookup table |
|
||||
|
||||
### Critical Divergence: `CODE:0EEA`
|
||||
|
||||
This is where the three sub-variants diverge most dramatically:
|
||||
|
||||
```
|
||||
FW1: 8f44 8c45 8d46 8b47 754a14 e544 b451...
|
||||
(I2C transfer parameters in registers)
|
||||
|
||||
FW2: 753e14 e50d 240a f582 e435 0cf5 83e0...
|
||||
(reads from DPTR+offset table)
|
||||
|
||||
FW3: 753e14 e4f5 3ff5 40 e50d 240a f582...
|
||||
(similar to FW2 + accumulator initialization)
|
||||
```
|
||||
|
||||
FW1's `FUN_CODE_0eea` is a standard I2C master transfer function. FW2/FW3's version is a parallel bus demodulator interface.
|
||||
|
||||
### Thunk Target Divergence (`CODE:1500`)
|
||||
|
||||
```
|
||||
FW1: 02 2252 00 02 22dd 00 02 22c7 00 02 226a 00
|
||||
FW2: 02 228d 00 02 2318 00 02 2302 00 02 22a5 00
|
||||
FW3: 02 228d 00 02 2318 00 02 2302 00 02 22a5 00
|
||||
```
|
||||
|
||||
FW2 and FW3 share identical interrupt handler targets. FW1 jumps to different addresses, reflecting its different internal function layout.
|
||||
|
||||
## Detailed Differences
|
||||
|
||||
### Stack Pointer and Status Register
|
||||
|
||||
| Property | FW1 | FW2 | FW3 |
|
||||
|----------|-----|-----|-----|
|
||||
| SP value | `0x50` | `0x50` | `0x52` |
|
||||
| Status IRAM | `0x4F` | `0x4F` | `0x51` |
|
||||
| I2C buffer IRAM | `0x48`/`0x49` | `0x48`/`0x49` | `0x4A`/`0x4B` |
|
||||
|
||||
FW3 pushes the stack pointer up by 2 bytes to make room for the additional status register at IRAM `0x51`. The 2-byte SP difference exactly accounts for moving the status register from `0x4F` to `0x51`.
|
||||
|
||||
### P0 Init Value
|
||||
|
||||
| Variant | P0 Init | Binary | Difference |
|
||||
|---------|---------|--------|------------|
|
||||
| FW1/FW2 | `0xA4` | `1010 0100` | Bit 2 = 1 |
|
||||
| FW3 | `0xA0` | `1010 0000` | Bit 2 = 0 |
|
||||
|
||||
P0 bit 2 controls a GPIO signal likely related to demodulator interface mode or reset polarity on the FW3 target PCB.
|
||||
|
||||
### Vendor Handler Differences
|
||||
|
||||
| Feature | FW1 | FW2/FW3 |
|
||||
|---------|-----|---------|
|
||||
| Case `0x3D3` | TR2 timer check (I2C timeout) | OR operation (parallel bus) |
|
||||
| Case `0x421`-`0x423` | Simple check | P2.1 write + rotate-left (bus direction) |
|
||||
| Error path | `func_0x06e4` | `DAT=0x0` |
|
||||
|
||||
FW1's timer-based case is used for I2C bus timeout recovery. FW2/FW3's rotate-left and P2.1 write is a parallel bus data direction control.
|
||||
|
||||
## Hardware Progression Theory
|
||||
|
||||
The three sub-variants represent an evolutionary progression:
|
||||
|
||||
1. **FW1 (v2.13.1)**: Original design with I2C-connected demodulator. Simple interface but limited in bandwidth. The FX2 acts purely as an I2C master bridge.
|
||||
|
||||
2. **FW2 (v2.13.2)**: Redesigned with parallel-bus demodulator for higher throughput. P1 carries 8-bit data, P0 provides control signals. External calibration data at `0xE080`--`0xE08E`.
|
||||
|
||||
3. **FW3 (v2.13.3)**: Refined parallel interface for a newer demod silicon revision. Dual-phase reads with OR-accumulation handle bus timing differences. Additional IRAM state tracking (SP bumped to `0x52`).
|
||||
|
||||
All three support the same modulation types (DVB-S QPSK, Turbo QPSK/8PSK/16QAM, DCII, DSS) and the same demod type codes (3--6). The differences are purely hardware interface, not feature set.
|
||||
|
|
|
|||
|
|
@ -1,184 +1,184 @@
|
|||
---
|
||||
title: Kernel FW01 Analysis
|
||||
description: Analysis of the dvb-usb-gp8psk-01.fw firmware format, loading mechanism, and why SkyWalker-1 does not need it.
|
||||
---
|
||||
|
||||
import { Steps, Badge, Aside, Tabs, TabItem, FileTree } from '@astrojs/starlight/components';
|
||||
|
||||
The Linux kernel `dvb_usb_gp8psk` driver references two firmware files: `dvb-usb-gp8psk-01.fw` (FX2 microcontroller code) and `dvb-usb-gp8psk-02.fw` (BCM4500 demodulator code). Neither file was ever open-sourced or included in the `linux-firmware` repository. The SkyWalker-1 does not need them.
|
||||
|
||||
## Firmware File Status
|
||||
|
||||
| File | Purpose | Available? | Needed by SkyWalker-1? |
|
||||
|------|---------|------------|----------------------|
|
||||
| `dvb-usb-gp8psk-01.fw` | FX2 RAM code | **Not in linux-firmware** | No |
|
||||
| `dvb-usb-gp8psk-02.fw` | BCM4500 demod code | **Not in linux-firmware** | No |
|
||||
|
||||
Standard locations checked:
|
||||
|
||||
| Path | Result |
|
||||
|------|--------|
|
||||
| `/lib/firmware/dvb-usb-gp8psk-01.fw` | Not found |
|
||||
| `/lib/firmware/dvb-usb-gp8psk-02.fw` | Not found |
|
||||
| `linux-firmware` WHENCE manifest | No gp8psk entry |
|
||||
| Kernel `scripts/get_dvb_firmware` | No gp8psk handler |
|
||||
| `pacman -F dvb-usb-gp8psk-01.fw` | No package provides it |
|
||||
|
||||
<Aside type="note">
|
||||
The gp8psk firmware was presumably distributed by Genpix Electronics with their Windows BDA driver installer. It was never contributed to the Linux firmware collection.
|
||||
</Aside>
|
||||
|
||||
## Why SkyWalker-1 Works Without Firmware Files
|
||||
|
||||
The answer is in the kernel driver's device table. Only Rev.1 Cold devices (PID `0x0200`) have a `cold_ids` entry, which triggers firmware download:
|
||||
|
||||
```c title="Kernel device properties (from gp8psk.c)"
|
||||
.devices = {
|
||||
{ .name = "Genpix 8PSK-to-USB2 Rev.1 DVB-S receiver",
|
||||
.cold_ids = { &gp8psk_usb_table[GENPIX_8PSK_REV_1_COLD], NULL },
|
||||
.warm_ids = { &gp8psk_usb_table[GENPIX_8PSK_REV_1_WARM], NULL },
|
||||
},
|
||||
{ .name = "Genpix SkyWalker-1 DVB-S receiver",
|
||||
.cold_ids = { NULL }, // <-- NO cold_ids: skip firmware
|
||||
.warm_ids = { &gp8psk_usb_table[GENPIX_SKYWALKER_1], NULL },
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
When `cold_ids` is NULL, the DVB-USB framework skips firmware download entirely. The SkyWalker-1 boots from its onboard EEPROM and enumerates directly as a "warm" device.
|
||||
|
||||
| Device | PID | Needs FW01? | Needs FW02? | Boot Source |
|
||||
|--------|-----|-------------|-------------|-------------|
|
||||
| Rev.1 Cold | `0x0200` | **Yes** | -- | RAM (empty) |
|
||||
| Rev.1 Warm | `0x0201` | No | **Yes** | RAM (FW01 loaded) |
|
||||
| Rev.2 | `0x0202` | No | No | EEPROM |
|
||||
| SkyWalker-1 | `0x0203` | No | No | EEPROM |
|
||||
| SkyWalker CW3K | `0x0206` | No | No | EEPROM |
|
||||
|
||||
## FW01 Loading Mechanism
|
||||
|
||||
For Rev.1 Cold devices, the firmware loading follows this sequence:
|
||||
|
||||
<Steps>
|
||||
|
||||
1. **DVB-USB framework** matches the USB VID:PID against `cold_ids` and calls `dvb_usb_download_firmware()`
|
||||
|
||||
2. **Kernel requests firmware** via `request_firmware("dvb-usb-gp8psk-01.fw", ...)` from the userspace firmware loader
|
||||
|
||||
3. **Cypress FX2 loader** (`usb_cypress_load_firmware()`) halts the FX2 CPU by writing `0x01` to CPUCS register (`0xE600`) via the 0xA0 vendor request
|
||||
|
||||
4. **Hexline records** are parsed and written to FX2 RAM via USB control transfers (0xA0 vendor request)
|
||||
|
||||
5. **FX2 CPU restarted** by writing `0x00` to CPUCS. The device re-enumerates with a new PID (0x0201, "warm")
|
||||
|
||||
6. **DVB-USB framework** re-matches the new PID against `warm_ids` and proceeds to frontend attach
|
||||
|
||||
</Steps>
|
||||
|
||||
The 0xA0 vendor request is handled by the FX2's built-in silicon boot ROM, which provides RAM read/write access regardless of whether user firmware is running. This is the same mechanism used by the custom firmware's `fw_load.py` tool.
|
||||
|
||||
## FW01 Binary Hexline Format
|
||||
|
||||
The kernel's `dvb_usb_get_hexline()` parser expects a compact binary representation of Intel HEX records. This is **not** standard Intel HEX text (`:10000000...`), nor the kernel's `ihex_binrec` format from `<linux/ihex.h>`.
|
||||
|
||||
### Record Structure
|
||||
|
||||
```
|
||||
Offset Size Field
|
||||
------ ---- -----
|
||||
0 1 len - Number of data bytes
|
||||
1 1 addr_lo - Target address low byte
|
||||
2 1 addr_hi - Target address high byte
|
||||
3 1 type - Record type
|
||||
4 len data[] - Payload bytes
|
||||
4+len 1 chk - Checksum byte
|
||||
|
||||
Total per record: len + 5 bytes
|
||||
```
|
||||
|
||||
### Record Types
|
||||
|
||||
| Type | Name | Purpose |
|
||||
|------|------|---------|
|
||||
| `0x00` | Data | Code/data bytes for FX2 RAM |
|
||||
| `0x01` | EOF | End of file |
|
||||
| `0x04` | Extended Address | Sets upper 16 bits of target address |
|
||||
|
||||
## FW02 Chunk Format (BCM4500 Firmware)
|
||||
|
||||
FW02 is only relevant for Rev.1 Warm devices (PID `0x0201`). It uses a custom chunk protocol:
|
||||
|
||||
```
|
||||
Chunk format:
|
||||
Byte 0: payload_length (N)
|
||||
Bytes 1-3: header/address bytes
|
||||
Bytes 4..N+3: payload data
|
||||
Terminator: single byte 0xFF
|
||||
Maximum chunk size: 64 bytes (USB control transfer limit)
|
||||
```
|
||||
|
||||
The loading sequence:
|
||||
|
||||
<Steps>
|
||||
|
||||
1. **Initiate transfer**: Send `LOAD_BCM4500` command (`0x88`, `wValue=1`)
|
||||
|
||||
2. **Download chunks**: Iterate through firmware data, sending each chunk via `dvb_usb_generic_write()` on bulk endpoint `0x01`
|
||||
|
||||
3. **Detect end**: Stop when a byte with value `0xFF` is encountered
|
||||
|
||||
</Steps>
|
||||
|
||||
```c title="BCM4500 firmware loading (from gp8psk.c)"
|
||||
ptr = fw_data;
|
||||
while (ptr[0] != 0xFF) {
|
||||
chunk_size = ptr[0] + 4;
|
||||
if (chunk_size > 64) {
|
||||
// Error: chunk too large
|
||||
}
|
||||
usb_control_msg(device, USB_SNDCTRLPIPE, 0, ...,
|
||||
buf, chunk_size, 2000);
|
||||
ptr += chunk_size;
|
||||
}
|
||||
```
|
||||
|
||||
<Aside type="note">
|
||||
On the SkyWalker-1, command `0x88` (`LOAD_BCM4500`) routes to the STALL handler in all firmware versions -- the BCM4500 firmware is burned into ROM. The kernel driver only attempts this command for Rev.1 Warm devices after checking that `bm8pskFW_Loaded` (bit 1 of `GET_8PSK_CONFIG`) is not set.
|
||||
</Aside>
|
||||
|
||||
## C2 EEPROM Format vs Kernel Hexline
|
||||
|
||||
The firmware as stored in the SkyWalker-1's EEPROM uses Cypress C2 format, which is structurally different from the kernel's binary hexline format. They carry identical payload data but are different containers.
|
||||
|
||||
| Property | C2 (EEPROM) | Hexline (Kernel FW01) |
|
||||
|----------|-------------|-----------------------|
|
||||
| Header | 8-byte C2 with VID/PID/DID | None |
|
||||
| Address encoding | Big-endian 16-bit per segment | Little-endian split (lo, hi) per record |
|
||||
| Data chunking | 1023-byte segments | Typically 16-byte records |
|
||||
| Record overhead | 4 bytes per segment | 5 bytes per record |
|
||||
| Terminator | `0x80xx` + entry point | Type `0x01` EOF record |
|
||||
| Entry point | Explicit in terminator | Implicit (CPUCS at `0xE600`) |
|
||||
|
||||
A C2 file can theoretically be converted to hexline format by:
|
||||
1. Stripping the 8-byte C2 header
|
||||
2. Splitting each segment into 16-byte records with type `0x00`
|
||||
3. Appending an EOF record (len=0, type=`0x01`)
|
||||
|
||||
For the v2.06 EEPROM (9,472 code bytes), this would produce approximately 12,442 bytes in hexline format.
|
||||
|
||||
See the [Storage Formats](/firmware/storage-formats/) page for detailed C2 format documentation.
|
||||
|
||||
## Kernel dmesg Output
|
||||
|
||||
When the SkyWalker-1 is connected, the kernel logs:
|
||||
|
||||
```
|
||||
gp8psk: FW Version = 2.06.4 (0x20604) Build 2007/07/13
|
||||
gp8psk: usb in 149 operation failed.
|
||||
gp8psk: failed to get FPGA version
|
||||
gp8psk_fe: Frontend attached
|
||||
gp8psk: found Genpix USB device pID = 203 (hex)
|
||||
```
|
||||
|
||||
The "failed to get FPGA version" error is command `0x95` (`GET_FPGA_VERS`, decimal 149) returning an error on some units. Despite the name, there is no FPGA on the SkyWalker-1 -- this command reads a hardware platform ID from the EEPROM. The driver logs the failure but continues normally.
|
||||
---
|
||||
title: Kernel FW01 Analysis
|
||||
description: Analysis of the dvb-usb-gp8psk-01.fw firmware format, loading mechanism, and why SkyWalker-1 does not need it.
|
||||
---
|
||||
|
||||
import { Steps, Badge, Aside, Tabs, TabItem, FileTree } from '@astrojs/starlight/components';
|
||||
|
||||
The Linux kernel `dvb_usb_gp8psk` driver references two firmware files: `dvb-usb-gp8psk-01.fw` (FX2 microcontroller code) and `dvb-usb-gp8psk-02.fw` (BCM4500 demodulator code). Neither file was ever open-sourced or included in the `linux-firmware` repository. The SkyWalker-1 does not need them.
|
||||
|
||||
## Firmware File Status
|
||||
|
||||
| File | Purpose | Available? | Needed by SkyWalker-1? |
|
||||
|------|---------|------------|----------------------|
|
||||
| `dvb-usb-gp8psk-01.fw` | FX2 RAM code | **Not in linux-firmware** | No |
|
||||
| `dvb-usb-gp8psk-02.fw` | BCM4500 demod code | **Not in linux-firmware** | No |
|
||||
|
||||
Standard locations checked:
|
||||
|
||||
| Path | Result |
|
||||
|------|--------|
|
||||
| `/lib/firmware/dvb-usb-gp8psk-01.fw` | Not found |
|
||||
| `/lib/firmware/dvb-usb-gp8psk-02.fw` | Not found |
|
||||
| `linux-firmware` WHENCE manifest | No gp8psk entry |
|
||||
| Kernel `scripts/get_dvb_firmware` | No gp8psk handler |
|
||||
| `pacman -F dvb-usb-gp8psk-01.fw` | No package provides it |
|
||||
|
||||
<Aside type="note">
|
||||
The gp8psk firmware was presumably distributed by Genpix Electronics with their Windows BDA driver installer. It was never contributed to the Linux firmware collection.
|
||||
</Aside>
|
||||
|
||||
## Why SkyWalker-1 Works Without Firmware Files
|
||||
|
||||
The answer is in the kernel driver's device table. Only Rev.1 Cold devices (PID `0x0200`) have a `cold_ids` entry, which triggers firmware download:
|
||||
|
||||
```c title="Kernel device properties (from gp8psk.c)"
|
||||
.devices = {
|
||||
{ .name = "Genpix 8PSK-to-USB2 Rev.1 DVB-S receiver",
|
||||
.cold_ids = { &gp8psk_usb_table[GENPIX_8PSK_REV_1_COLD], NULL },
|
||||
.warm_ids = { &gp8psk_usb_table[GENPIX_8PSK_REV_1_WARM], NULL },
|
||||
},
|
||||
{ .name = "Genpix SkyWalker-1 DVB-S receiver",
|
||||
.cold_ids = { NULL }, // <-- NO cold_ids: skip firmware
|
||||
.warm_ids = { &gp8psk_usb_table[GENPIX_SKYWALKER_1], NULL },
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
When `cold_ids` is NULL, the DVB-USB framework skips firmware download entirely. The SkyWalker-1 boots from its onboard EEPROM and enumerates directly as a "warm" device.
|
||||
|
||||
| Device | PID | Needs FW01? | Needs FW02? | Boot Source |
|
||||
|--------|-----|-------------|-------------|-------------|
|
||||
| Rev.1 Cold | `0x0200` | **Yes** | -- | RAM (empty) |
|
||||
| Rev.1 Warm | `0x0201` | No | **Yes** | RAM (FW01 loaded) |
|
||||
| Rev.2 | `0x0202` | No | No | EEPROM |
|
||||
| SkyWalker-1 | `0x0203` | No | No | EEPROM |
|
||||
| SkyWalker CW3K | `0x0206` | No | No | EEPROM |
|
||||
|
||||
## FW01 Loading Mechanism
|
||||
|
||||
For Rev.1 Cold devices, the firmware loading follows this sequence:
|
||||
|
||||
<Steps>
|
||||
|
||||
1. **DVB-USB framework** matches the USB VID:PID against `cold_ids` and calls `dvb_usb_download_firmware()`
|
||||
|
||||
2. **Kernel requests firmware** via `request_firmware("dvb-usb-gp8psk-01.fw", ...)` from the userspace firmware loader
|
||||
|
||||
3. **Cypress FX2 loader** (`usb_cypress_load_firmware()`) halts the FX2 CPU by writing `0x01` to CPUCS register (`0xE600`) via the 0xA0 vendor request
|
||||
|
||||
4. **Hexline records** are parsed and written to FX2 RAM via USB control transfers (0xA0 vendor request)
|
||||
|
||||
5. **FX2 CPU restarted** by writing `0x00` to CPUCS. The device re-enumerates with a new PID (0x0201, "warm")
|
||||
|
||||
6. **DVB-USB framework** re-matches the new PID against `warm_ids` and proceeds to frontend attach
|
||||
|
||||
</Steps>
|
||||
|
||||
The 0xA0 vendor request is handled by the FX2's built-in silicon boot ROM, which provides RAM read/write access regardless of whether user firmware is running. This is the same mechanism used by the custom firmware's `fw_load.py` tool.
|
||||
|
||||
## FW01 Binary Hexline Format
|
||||
|
||||
The kernel's `dvb_usb_get_hexline()` parser expects a compact binary representation of Intel HEX records. This is **not** standard Intel HEX text (`:10000000...`), nor the kernel's `ihex_binrec` format from `<linux/ihex.h>`.
|
||||
|
||||
### Record Structure
|
||||
|
||||
```
|
||||
Offset Size Field
|
||||
------ ---- -----
|
||||
0 1 len - Number of data bytes
|
||||
1 1 addr_lo - Target address low byte
|
||||
2 1 addr_hi - Target address high byte
|
||||
3 1 type - Record type
|
||||
4 len data[] - Payload bytes
|
||||
4+len 1 chk - Checksum byte
|
||||
|
||||
Total per record: len + 5 bytes
|
||||
```
|
||||
|
||||
### Record Types
|
||||
|
||||
| Type | Name | Purpose |
|
||||
|------|------|---------|
|
||||
| `0x00` | Data | Code/data bytes for FX2 RAM |
|
||||
| `0x01` | EOF | End of file |
|
||||
| `0x04` | Extended Address | Sets upper 16 bits of target address |
|
||||
|
||||
## FW02 Chunk Format (BCM4500 Firmware)
|
||||
|
||||
FW02 is only relevant for Rev.1 Warm devices (PID `0x0201`). It uses a custom chunk protocol:
|
||||
|
||||
```
|
||||
Chunk format:
|
||||
Byte 0: payload_length (N)
|
||||
Bytes 1-3: header/address bytes
|
||||
Bytes 4..N+3: payload data
|
||||
Terminator: single byte 0xFF
|
||||
Maximum chunk size: 64 bytes (USB control transfer limit)
|
||||
```
|
||||
|
||||
The loading sequence:
|
||||
|
||||
<Steps>
|
||||
|
||||
1. **Initiate transfer**: Send `LOAD_BCM4500` command (`0x88`, `wValue=1`)
|
||||
|
||||
2. **Download chunks**: Iterate through firmware data, sending each chunk via `dvb_usb_generic_write()` on bulk endpoint `0x01`
|
||||
|
||||
3. **Detect end**: Stop when a byte with value `0xFF` is encountered
|
||||
|
||||
</Steps>
|
||||
|
||||
```c title="BCM4500 firmware loading (from gp8psk.c)"
|
||||
ptr = fw_data;
|
||||
while (ptr[0] != 0xFF) {
|
||||
chunk_size = ptr[0] + 4;
|
||||
if (chunk_size > 64) {
|
||||
// Error: chunk too large
|
||||
}
|
||||
usb_control_msg(device, USB_SNDCTRLPIPE, 0, ...,
|
||||
buf, chunk_size, 2000);
|
||||
ptr += chunk_size;
|
||||
}
|
||||
```
|
||||
|
||||
<Aside type="note">
|
||||
On the SkyWalker-1, command `0x88` (`LOAD_BCM4500`) routes to the STALL handler in all firmware versions -- the BCM4500 firmware is burned into ROM. The kernel driver only attempts this command for Rev.1 Warm devices after checking that `bm8pskFW_Loaded` (bit 1 of `GET_8PSK_CONFIG`) is not set.
|
||||
</Aside>
|
||||
|
||||
## C2 EEPROM Format vs Kernel Hexline
|
||||
|
||||
The firmware as stored in the SkyWalker-1's EEPROM uses Cypress C2 format, which is structurally different from the kernel's binary hexline format. They carry identical payload data but are different containers.
|
||||
|
||||
| Property | C2 (EEPROM) | Hexline (Kernel FW01) |
|
||||
|----------|-------------|-----------------------|
|
||||
| Header | 8-byte C2 with VID/PID/DID | None |
|
||||
| Address encoding | Big-endian 16-bit per segment | Little-endian split (lo, hi) per record |
|
||||
| Data chunking | 1023-byte segments | Typically 16-byte records |
|
||||
| Record overhead | 4 bytes per segment | 5 bytes per record |
|
||||
| Terminator | `0x80xx` + entry point | Type `0x01` EOF record |
|
||||
| Entry point | Explicit in terminator | Implicit (CPUCS at `0xE600`) |
|
||||
|
||||
A C2 file can theoretically be converted to hexline format by:
|
||||
1. Stripping the 8-byte C2 header
|
||||
2. Splitting each segment into 16-byte records with type `0x00`
|
||||
3. Appending an EOF record (len=0, type=`0x01`)
|
||||
|
||||
For the v2.06 EEPROM (9,472 code bytes), this would produce approximately 12,442 bytes in hexline format.
|
||||
|
||||
See the [Storage Formats](/firmware/storage-formats/) page for detailed C2 format documentation.
|
||||
|
||||
## Kernel dmesg Output
|
||||
|
||||
When the SkyWalker-1 is connected, the kernel logs:
|
||||
|
||||
```
|
||||
gp8psk: FW Version = 2.06.4 (0x20604) Build 2007/07/13
|
||||
gp8psk: usb in 149 operation failed.
|
||||
gp8psk: failed to get FPGA version
|
||||
gp8psk_fe: Frontend attached
|
||||
gp8psk: found Genpix USB device pID = 203 (hex)
|
||||
```
|
||||
|
||||
The "failed to get FPGA version" error is command `0x95` (`GET_FPGA_VERS`, decimal 149) returning an error on some units. Despite the name, there is no FPGA on the SkyWalker-1 -- this command reads a hardware platform ID from the EEPROM. The driver logs the failure but continues normally.
|
||||
|
|
|
|||
|
|
@ -1,210 +1,210 @@
|
|||
---
|
||||
title: Rev.2 Firmware Analysis
|
||||
description: Deep analysis of the Rev.2 v2.10.4 firmware variant with 107 functions and transitional architecture.
|
||||
---
|
||||
|
||||
import { Badge, Aside, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
The Rev.2 v2.10.4 firmware targets the Rev.2 hardware variant (PID `0x0202`) and contains **107 functions** -- the most of any firmware version. Despite this, it produces the smallest binary (8,843 bytes) due to aggressive function decomposition into small helper routines.
|
||||
|
||||
## Architectural Position
|
||||
|
||||
Rev.2 sits architecturally between v2.06 and v2.13:
|
||||
|
||||
| Aspect | v2.06 | Rev.2 v2.10 | v2.13 |
|
||||
|--------|-------|-------------|-------|
|
||||
| INT0 behavior | USB re-enumeration | USB re-enumeration | Demod polling |
|
||||
| Descriptor base | `0x1200` | `0x0E00` | `0x0E00` |
|
||||
| Stack pointer | `0x72` | `0x4F` | `0x50` |
|
||||
| Vendor command range | `0x80`--`0x9D` (30) | `0x80`--`0x9A` (27) | `0x80`--`0x9D` (30) |
|
||||
| Demod probe at boot | No | No | Yes |
|
||||
| Retry loops | No | No | Yes |
|
||||
| Function count | 61 | **107** | 82-88 |
|
||||
| Binary size | 9,472 bytes | **8,843 bytes** | 9,322 bytes |
|
||||
|
||||
<Aside type="note">
|
||||
The Rev.2 already adopted v2.13's descriptor base offset (`0x0E00`) and similar stack pointer pattern, but retained v2.06's INT0 USB re-enumeration behavior. It lacks v2.13's demodulator polling, retry logic, and the three additional vendor commands (`0x9B`--`0x9D` are out of range).
|
||||
</Aside>
|
||||
|
||||
## Why 107 Functions?
|
||||
|
||||
The high function count is driven by three factors:
|
||||
|
||||
1. **Granular decomposition**: Rev.2 breaks large operations into many small helper functions (10-30 bytes each), where v2.06 inlines the same logic and v2.13 recombines it differently.
|
||||
|
||||
2. **Massive configuration dispatcher**: `FUN_CODE_0800` is 874 bytes and contains an embedded copy of the main loop, causing Ghidra to count additional entry points as separate functions.
|
||||
|
||||
3. **Extra I2C/demodulator helper chains**: GPIO control primitives, hardware-polling wait loops, and I2C bus management exist as individual callable units rather than being inlined.
|
||||
|
||||
## Function Inventory Overview
|
||||
|
||||
The 107 functions are organized into logical groups:
|
||||
|
||||
### Vector Table and ISR Region (0x0000--0x0055)
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x0000` | `RESET_vector` | 3 | Jump to `main` at `0x155F` |
|
||||
| `0x0003` | `INT0_ISR` | 12 | INT0 handler -- USB re-enumeration |
|
||||
| `0x000F` | `INT0_ISR_bit_clear` | 36 | CPUCS pulse, IRQ clear, delay |
|
||||
| `0x0033` | `INT2_USB_GPIF_vector` | 3 | Clears CCON.4 (PCA timer) |
|
||||
| `0x0036` | `i2c_exchange_byte` | 5 | I2C byte exchange primitive |
|
||||
| `0x003B` | `I2C_ISR` | 8 | I2C interrupt handler |
|
||||
| `0x0043` | `INT4_FX2_vector` | 8 | Sets `_0_1` flag, clears EXIF.4 |
|
||||
| `0x004B` | `INT5_FX2_vector` | 3 | Empty (RETI) |
|
||||
| `0x0053` | `INT6_FX2_vector` | 3 | Sets `_0_1` flag, clears EXIF.4 |
|
||||
|
||||
### Vendor Command Dispatch (0x0056--0x0319)
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x0056` | `vendor_cmd_dispatch` | 342 | Range check `0x80`--`0x9A`, jump table at `0x0076` |
|
||||
| `0x01AC` | GET_8PSK_CONFIG handler | 361 | Reads config byte, calls LNB probe |
|
||||
| `0x0315` | `vendor_cmd_stall` | 2 | Stall handler (empty RET) |
|
||||
| `0x0319` | Standard USB request handler | 869 | Switch for `bRequest` `0x00`--`0x0B` |
|
||||
|
||||
### Configuration and Tuning (0x0800--0x09A8)
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x0800` | Config/tuning dispatcher | **874** | 128-entry switch on demod type, embedded main loop |
|
||||
| `0x09A9` | Main init + main loop | 699 | Hardware init, infinite poll loop |
|
||||
|
||||
### BCM4500 and GPIF (0x0C64--0x0F00)
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x0C64` | BCM4500 firmware loader | 280 | I2C block transfer with address tracking |
|
||||
| `0x0D7C` | GPIF/slave FIFO config | 128 | Enable/disable streaming mode |
|
||||
| `0x0F00` | I2C multi-byte read | 256 | Parameter setup for bus transfer |
|
||||
|
||||
### DiSEqC Implementation (0x07D1, 0x1D5E--0x1E3D)
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x07D1` | `DiSEqC byte transmit` | 45 | 8 data bits + odd parity via **P0.4** |
|
||||
| `0x1D5E` | DiSEqC message sender | 59 | Iterates bytes, calls bit-bang per byte |
|
||||
| `0x1E3D` | DiSEqC byte wrapper | 54 | Sets P0.2, adds inter-byte delay |
|
||||
| `0x213C` | DiSEqC bit symbol | 22 | Carrier on/off via P0.3, data via P0.4 |
|
||||
| `0x20E2` | 22 kHz tone burst | 23 | P0.3 ON, 25 ticks, P0.3 OFF |
|
||||
| `0x225F` | Timer2 tick wait | 6 | TF2 poll and clear (500 us tick) |
|
||||
|
||||
<Aside type="note">
|
||||
Rev.2 uses **P0.4** as the DiSEqC data pin, unlike v2.06 (P0.7) and v2.13 (P0.0). The carrier pin P0.3 remains the same across all versions. This pin reassignment reflects the different PCB layout of the Rev.2 board.
|
||||
</Aside>
|
||||
|
||||
### LNB and GPIO Control (0x1F5C--0x2038)
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x1F5C` | LNB voltage I2C select | 41 | Probes I2C device `0x60` or address from `0xE0B6` |
|
||||
| `0x1FCF` | GPIO pin controller | 46 | Sets P0.6, P0.0, P3.4 based on parameter bits |
|
||||
| `0x2038` | GPIO clock strobe | 51 | Calls pin controller 3 times (setup, clock, cleanup) |
|
||||
| `0x21B1` | LNB voltage select | 17 | Sets/clears P0.4, updates config bit 5 |
|
||||
| `0x21C2` | 22 kHz tone enable | 17 | Sets/clears P0.3, updates config bit 4 |
|
||||
| `0x21D3` | DiSEqC port direction | 17 | Sets/clears P3.6, updates config bit 3 |
|
||||
|
||||
### I2C Bus Management (0x19F4--0x1B90)
|
||||
|
||||
Rev.2 decomposes I2C operations into particularly fine-grained functions:
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x19F4` | I2C bus controller | 92 | Manages SDA/SCL via XRAM `0xE678` |
|
||||
| `0x1A50` | I2C address select + start | 83 | START condition generation |
|
||||
| `0x1AA3` | I2C stop + cleanup | 82 | STOP condition and bus release |
|
||||
| `0x1AF5` | I2C address write helper | 9 | Writes device address byte |
|
||||
| `0x1B01` | I2C byte-level transfer | 67 | Single byte send/receive |
|
||||
| `0x1B44` | I2C ACK/NAK handling | 76 | Acknowledge detection |
|
||||
| `0x1B90` | I2C bus reset/recovery | 74 | Error recovery sequence |
|
||||
| `0x1F06` | I2C completion wait | 43 | Polls XRAM `0xE678` bit 0 with 16-bit timeout |
|
||||
| `0x1F85` | I2C completion wait (2-flag) | 37 | Polls bits 0 and 2 |
|
||||
| `0x2000` | I2C busy wait | 30 | Polls XRAM `0xE678` bit 6 with timeout |
|
||||
|
||||
## Rev.2-Specific Features
|
||||
|
||||
### GPIO Pin Controller (`FUN_CODE_1fcf`)
|
||||
|
||||
A unique function that provides parameterized GPIO control through a bit-field interface:
|
||||
|
||||
```c title="GPIO pin controller (decompiled)"
|
||||
void gpio_pin_controller(BYTE param) {
|
||||
if (param & 0x02) P0 |= 0x01; // P0.0
|
||||
else P0 &= ~0x01;
|
||||
|
||||
if (param & 0x04) P0 |= 0x40; // P0.6
|
||||
else P0 &= ~0x40;
|
||||
|
||||
if (param & 0x08) P3 |= 0x10; // P3.4
|
||||
else P3 &= ~0x10;
|
||||
}
|
||||
```
|
||||
|
||||
This function is called via `FUN_CODE_2038` (GPIO clock strobe) which invokes it three times per cycle -- setup, clock edge, and cleanup -- suggesting it controls a clocked peripheral interface.
|
||||
|
||||
### Descriptor Version Checker (`FUN_CODE_1f31`)
|
||||
|
||||
Walks a USB descriptor chain checking whether `descriptor_byte + 1 == 0x03`, enabling hardware-revision-aware code paths. This mechanism appears in a simplified form in v2.13 as the `_1_3` flag check.
|
||||
|
||||
### Prototype Commands 0x99/0x9A
|
||||
|
||||
Commands 0x99 and 0x9A exist in Rev.2 as partial prototype implementations, before becoming fully functional in v2.13:
|
||||
|
||||
| Command | Rev.2 Behavior | v2.13 Behavior |
|
||||
|---------|---------------|---------------|
|
||||
| `0x99` | Prototype -- limited status read | Full GET_DEMOD_STATUS (reads BCM4500 reg `0xF9`) |
|
||||
| `0x9A` | Prototype -- basic init call | Full INIT_DEMOD (3-attempt re-init with flag check) |
|
||||
|
||||
## Cross-Version Function Mapping
|
||||
|
||||
Key Rev.2 functions and their counterparts in other versions:
|
||||
|
||||
| Rev.2 Function | Role | v2.06 | v2.13 |
|
||||
|----------------|------|-------|-------|
|
||||
| `0x155F` main | RESET entry, IRAM clear | `0x188D` | `0x170D` |
|
||||
| `0x09A9` main init | Init + main loop | `0x09A7` | `0x0800` |
|
||||
| `0x10D9` USB setup | Descriptor/peripheral init | `0x13C3` | `0x11AB` |
|
||||
| `0x0056` vendor dispatch | Vendor command dispatcher | `0x0056` | `0x0056` |
|
||||
| `0x0C64` BCM4500 loader | Firmware block transfer | `0x0DDD` | `0x0CA4` |
|
||||
| `0x0D7C` GPIF/FIFO | Streaming management | `0x1919` | `0x1800` |
|
||||
| `0x1BDA` delay | Clock-compensated delay | `0x1DFB` | `0x14B9` |
|
||||
|
||||
## GPIO Differences from SkyWalker-1
|
||||
|
||||
The Rev.2 board has a different GPIO assignment from the standard SkyWalker-1:
|
||||
|
||||
| Pin | Rev.2 v2.10 | v2.06 / v2.13 |
|
||||
|-----|-------------|---------------|
|
||||
| P0.0 | LNB control (cmd `0x97`) | DiSEqC data (v2.13) / unused (v2.06) |
|
||||
| P0.4 | LNB voltage **+ DiSEqC data** | LNB voltage only |
|
||||
| P0.5 | GPIO status input (cmd `0x98`) | BCM4500 RESET |
|
||||
| P0.6 | GPIO control (cmd `0x97`) | Unused |
|
||||
| P0.7 | Streaming indicator | DiSEqC data (v2.06) / streaming (v2.13) |
|
||||
|
||||
The most significant difference is that Rev.2 multiplexes DiSEqC data onto the LNB voltage pin (P0.4), and the BCM4500 RESET function on P0.5 is replaced by a GPIO status input.
|
||||
|
||||
## Main Loop Structure
|
||||
|
||||
The Rev.2 main loop follows the same pattern as other versions but with the event handling delegated to `FUN_CODE_201e`:
|
||||
|
||||
```c title="Main loop poll (simplified)"
|
||||
void main_loop(void) {
|
||||
// Process init table from CODE:0B48
|
||||
// Call USB/peripheral setup
|
||||
// Enable interrupts
|
||||
|
||||
while (1) {
|
||||
if (sudav_flag) {
|
||||
handle_setupdata();
|
||||
sudav_flag = 0;
|
||||
}
|
||||
FUN_CODE_201e(); // Delegated I2C config read/write
|
||||
if (gpif_flag) {
|
||||
handle_gpif_event();
|
||||
gpif_flag = 0;
|
||||
} else {
|
||||
PCON |= 0x01; // CPU idle
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
---
|
||||
title: Rev.2 Firmware Analysis
|
||||
description: Deep analysis of the Rev.2 v2.10.4 firmware variant with 107 functions and transitional architecture.
|
||||
---
|
||||
|
||||
import { Badge, Aside, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
The Rev.2 v2.10.4 firmware targets the Rev.2 hardware variant (PID `0x0202`) and contains **107 functions** -- the most of any firmware version. Despite this, it produces the smallest binary (8,843 bytes) due to aggressive function decomposition into small helper routines.
|
||||
|
||||
## Architectural Position
|
||||
|
||||
Rev.2 sits architecturally between v2.06 and v2.13:
|
||||
|
||||
| Aspect | v2.06 | Rev.2 v2.10 | v2.13 |
|
||||
|--------|-------|-------------|-------|
|
||||
| INT0 behavior | USB re-enumeration | USB re-enumeration | Demod polling |
|
||||
| Descriptor base | `0x1200` | `0x0E00` | `0x0E00` |
|
||||
| Stack pointer | `0x72` | `0x4F` | `0x50` |
|
||||
| Vendor command range | `0x80`--`0x9D` (30) | `0x80`--`0x9A` (27) | `0x80`--`0x9D` (30) |
|
||||
| Demod probe at boot | No | No | Yes |
|
||||
| Retry loops | No | No | Yes |
|
||||
| Function count | 61 | **107** | 82-88 |
|
||||
| Binary size | 9,472 bytes | **8,843 bytes** | 9,322 bytes |
|
||||
|
||||
<Aside type="note">
|
||||
The Rev.2 already adopted v2.13's descriptor base offset (`0x0E00`) and similar stack pointer pattern, but retained v2.06's INT0 USB re-enumeration behavior. It lacks v2.13's demodulator polling, retry logic, and the three additional vendor commands (`0x9B`--`0x9D` are out of range).
|
||||
</Aside>
|
||||
|
||||
## Why 107 Functions?
|
||||
|
||||
The high function count is driven by three factors:
|
||||
|
||||
1. **Granular decomposition**: Rev.2 breaks large operations into many small helper functions (10-30 bytes each), where v2.06 inlines the same logic and v2.13 recombines it differently.
|
||||
|
||||
2. **Massive configuration dispatcher**: `FUN_CODE_0800` is 874 bytes and contains an embedded copy of the main loop, causing Ghidra to count additional entry points as separate functions.
|
||||
|
||||
3. **Extra I2C/demodulator helper chains**: GPIO control primitives, hardware-polling wait loops, and I2C bus management exist as individual callable units rather than being inlined.
|
||||
|
||||
## Function Inventory Overview
|
||||
|
||||
The 107 functions are organized into logical groups:
|
||||
|
||||
### Vector Table and ISR Region (0x0000--0x0055)
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x0000` | `RESET_vector` | 3 | Jump to `main` at `0x155F` |
|
||||
| `0x0003` | `INT0_ISR` | 12 | INT0 handler -- USB re-enumeration |
|
||||
| `0x000F` | `INT0_ISR_bit_clear` | 36 | CPUCS pulse, IRQ clear, delay |
|
||||
| `0x0033` | `INT2_USB_GPIF_vector` | 3 | Clears CCON.4 (PCA timer) |
|
||||
| `0x0036` | `i2c_exchange_byte` | 5 | I2C byte exchange primitive |
|
||||
| `0x003B` | `I2C_ISR` | 8 | I2C interrupt handler |
|
||||
| `0x0043` | `INT4_FX2_vector` | 8 | Sets `_0_1` flag, clears EXIF.4 |
|
||||
| `0x004B` | `INT5_FX2_vector` | 3 | Empty (RETI) |
|
||||
| `0x0053` | `INT6_FX2_vector` | 3 | Sets `_0_1` flag, clears EXIF.4 |
|
||||
|
||||
### Vendor Command Dispatch (0x0056--0x0319)
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x0056` | `vendor_cmd_dispatch` | 342 | Range check `0x80`--`0x9A`, jump table at `0x0076` |
|
||||
| `0x01AC` | GET_8PSK_CONFIG handler | 361 | Reads config byte, calls LNB probe |
|
||||
| `0x0315` | `vendor_cmd_stall` | 2 | Stall handler (empty RET) |
|
||||
| `0x0319` | Standard USB request handler | 869 | Switch for `bRequest` `0x00`--`0x0B` |
|
||||
|
||||
### Configuration and Tuning (0x0800--0x09A8)
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x0800` | Config/tuning dispatcher | **874** | 128-entry switch on demod type, embedded main loop |
|
||||
| `0x09A9` | Main init + main loop | 699 | Hardware init, infinite poll loop |
|
||||
|
||||
### BCM4500 and GPIF (0x0C64--0x0F00)
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x0C64` | BCM4500 firmware loader | 280 | I2C block transfer with address tracking |
|
||||
| `0x0D7C` | GPIF/slave FIFO config | 128 | Enable/disable streaming mode |
|
||||
| `0x0F00` | I2C multi-byte read | 256 | Parameter setup for bus transfer |
|
||||
|
||||
### DiSEqC Implementation (0x07D1, 0x1D5E--0x1E3D)
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x07D1` | `DiSEqC byte transmit` | 45 | 8 data bits + odd parity via **P0.4** |
|
||||
| `0x1D5E` | DiSEqC message sender | 59 | Iterates bytes, calls bit-bang per byte |
|
||||
| `0x1E3D` | DiSEqC byte wrapper | 54 | Sets P0.2, adds inter-byte delay |
|
||||
| `0x213C` | DiSEqC bit symbol | 22 | Carrier on/off via P0.3, data via P0.4 |
|
||||
| `0x20E2` | 22 kHz tone burst | 23 | P0.3 ON, 25 ticks, P0.3 OFF |
|
||||
| `0x225F` | Timer2 tick wait | 6 | TF2 poll and clear (500 us tick) |
|
||||
|
||||
<Aside type="note">
|
||||
Rev.2 uses **P0.4** as the DiSEqC data pin, unlike v2.06 (P0.7) and v2.13 (P0.0). The carrier pin P0.3 remains the same across all versions. This pin reassignment reflects the different PCB layout of the Rev.2 board.
|
||||
</Aside>
|
||||
|
||||
### LNB and GPIO Control (0x1F5C--0x2038)
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x1F5C` | LNB voltage I2C select | 41 | Probes I2C device `0x60` or address from `0xE0B6` |
|
||||
| `0x1FCF` | GPIO pin controller | 46 | Sets P0.6, P0.0, P3.4 based on parameter bits |
|
||||
| `0x2038` | GPIO clock strobe | 51 | Calls pin controller 3 times (setup, clock, cleanup) |
|
||||
| `0x21B1` | LNB voltage select | 17 | Sets/clears P0.4, updates config bit 5 |
|
||||
| `0x21C2` | 22 kHz tone enable | 17 | Sets/clears P0.3, updates config bit 4 |
|
||||
| `0x21D3` | DiSEqC port direction | 17 | Sets/clears P3.6, updates config bit 3 |
|
||||
|
||||
### I2C Bus Management (0x19F4--0x1B90)
|
||||
|
||||
Rev.2 decomposes I2C operations into particularly fine-grained functions:
|
||||
|
||||
| Address | Name | Size | Role |
|
||||
|---------|------|-----:|------|
|
||||
| `0x19F4` | I2C bus controller | 92 | Manages SDA/SCL via XRAM `0xE678` |
|
||||
| `0x1A50` | I2C address select + start | 83 | START condition generation |
|
||||
| `0x1AA3` | I2C stop + cleanup | 82 | STOP condition and bus release |
|
||||
| `0x1AF5` | I2C address write helper | 9 | Writes device address byte |
|
||||
| `0x1B01` | I2C byte-level transfer | 67 | Single byte send/receive |
|
||||
| `0x1B44` | I2C ACK/NAK handling | 76 | Acknowledge detection |
|
||||
| `0x1B90` | I2C bus reset/recovery | 74 | Error recovery sequence |
|
||||
| `0x1F06` | I2C completion wait | 43 | Polls XRAM `0xE678` bit 0 with 16-bit timeout |
|
||||
| `0x1F85` | I2C completion wait (2-flag) | 37 | Polls bits 0 and 2 |
|
||||
| `0x2000` | I2C busy wait | 30 | Polls XRAM `0xE678` bit 6 with timeout |
|
||||
|
||||
## Rev.2-Specific Features
|
||||
|
||||
### GPIO Pin Controller (`FUN_CODE_1fcf`)
|
||||
|
||||
A unique function that provides parameterized GPIO control through a bit-field interface:
|
||||
|
||||
```c title="GPIO pin controller (decompiled)"
|
||||
void gpio_pin_controller(BYTE param) {
|
||||
if (param & 0x02) P0 |= 0x01; // P0.0
|
||||
else P0 &= ~0x01;
|
||||
|
||||
if (param & 0x04) P0 |= 0x40; // P0.6
|
||||
else P0 &= ~0x40;
|
||||
|
||||
if (param & 0x08) P3 |= 0x10; // P3.4
|
||||
else P3 &= ~0x10;
|
||||
}
|
||||
```
|
||||
|
||||
This function is called via `FUN_CODE_2038` (GPIO clock strobe) which invokes it three times per cycle -- setup, clock edge, and cleanup -- suggesting it controls a clocked peripheral interface.
|
||||
|
||||
### Descriptor Version Checker (`FUN_CODE_1f31`)
|
||||
|
||||
Walks a USB descriptor chain checking whether `descriptor_byte + 1 == 0x03`, enabling hardware-revision-aware code paths. This mechanism appears in a simplified form in v2.13 as the `_1_3` flag check.
|
||||
|
||||
### Prototype Commands 0x99/0x9A
|
||||
|
||||
Commands 0x99 and 0x9A exist in Rev.2 as partial prototype implementations, before becoming fully functional in v2.13:
|
||||
|
||||
| Command | Rev.2 Behavior | v2.13 Behavior |
|
||||
|---------|---------------|---------------|
|
||||
| `0x99` | Prototype -- limited status read | Full GET_DEMOD_STATUS (reads BCM4500 reg `0xF9`) |
|
||||
| `0x9A` | Prototype -- basic init call | Full INIT_DEMOD (3-attempt re-init with flag check) |
|
||||
|
||||
## Cross-Version Function Mapping
|
||||
|
||||
Key Rev.2 functions and their counterparts in other versions:
|
||||
|
||||
| Rev.2 Function | Role | v2.06 | v2.13 |
|
||||
|----------------|------|-------|-------|
|
||||
| `0x155F` main | RESET entry, IRAM clear | `0x188D` | `0x170D` |
|
||||
| `0x09A9` main init | Init + main loop | `0x09A7` | `0x0800` |
|
||||
| `0x10D9` USB setup | Descriptor/peripheral init | `0x13C3` | `0x11AB` |
|
||||
| `0x0056` vendor dispatch | Vendor command dispatcher | `0x0056` | `0x0056` |
|
||||
| `0x0C64` BCM4500 loader | Firmware block transfer | `0x0DDD` | `0x0CA4` |
|
||||
| `0x0D7C` GPIF/FIFO | Streaming management | `0x1919` | `0x1800` |
|
||||
| `0x1BDA` delay | Clock-compensated delay | `0x1DFB` | `0x14B9` |
|
||||
|
||||
## GPIO Differences from SkyWalker-1
|
||||
|
||||
The Rev.2 board has a different GPIO assignment from the standard SkyWalker-1:
|
||||
|
||||
| Pin | Rev.2 v2.10 | v2.06 / v2.13 |
|
||||
|-----|-------------|---------------|
|
||||
| P0.0 | LNB control (cmd `0x97`) | DiSEqC data (v2.13) / unused (v2.06) |
|
||||
| P0.4 | LNB voltage **+ DiSEqC data** | LNB voltage only |
|
||||
| P0.5 | GPIO status input (cmd `0x98`) | BCM4500 RESET |
|
||||
| P0.6 | GPIO control (cmd `0x97`) | Unused |
|
||||
| P0.7 | Streaming indicator | DiSEqC data (v2.06) / streaming (v2.13) |
|
||||
|
||||
The most significant difference is that Rev.2 multiplexes DiSEqC data onto the LNB voltage pin (P0.4), and the BCM4500 RESET function on P0.5 is replaced by a GPIO status input.
|
||||
|
||||
## Main Loop Structure
|
||||
|
||||
The Rev.2 main loop follows the same pattern as other versions but with the event handling delegated to `FUN_CODE_201e`:
|
||||
|
||||
```c title="Main loop poll (simplified)"
|
||||
void main_loop(void) {
|
||||
// Process init table from CODE:0B48
|
||||
// Call USB/peripheral setup
|
||||
// Enable interrupts
|
||||
|
||||
while (1) {
|
||||
if (sudav_flag) {
|
||||
handle_setupdata();
|
||||
sudav_flag = 0;
|
||||
}
|
||||
FUN_CODE_201e(); // Delegated I2C config read/write
|
||||
if (gpif_flag) {
|
||||
handle_gpif_event();
|
||||
gpif_flag = 0;
|
||||
} else {
|
||||
PCON |= 0x01; // CPU idle
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,256 +1,256 @@
|
|||
---
|
||||
title: Firmware Storage Formats
|
||||
description: Cypress C2 EEPROM boot format, kernel hexline format, and flat binary extraction.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Aside, FileTree, Badge } from '@astrojs/starlight/components';
|
||||
|
||||
The SkyWalker-1 firmware exists in multiple container formats depending on context: the onboard EEPROM uses the Cypress C2 IIC boot format, the Linux kernel expects a custom binary hexline format, and analysis tools work with flat extracted binaries.
|
||||
|
||||
## Format Overview
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="C2 EEPROM">
|
||||
|
||||
### Cypress C2 IIC Second-Stage Boot Format <Badge text="Device Storage" variant="note" />
|
||||
|
||||
This is the native format stored in the SkyWalker-1's onboard I2C EEPROM. The FX2's internal boot ROM reads this format on power-up.
|
||||
|
||||
The `0xC2` marker byte in the header identifies this as "external memory, large code model" -- it tells the boot ROM to load code from the EEPROM into internal RAM using the segment map that follows.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Hexline">
|
||||
|
||||
### Kernel Binary Hexline Format <Badge text="Linux Driver" variant="caution" />
|
||||
|
||||
Used only by `dvb-usb-gp8psk-01.fw` for Rev.1 Cold devices. This is a compact binary representation of Intel HEX records parsed by `dvb_usb_get_hexline()` in the kernel. It is NOT standard Intel HEX text.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Flat Binary">
|
||||
|
||||
### Flat Extracted Binary <Badge text="Analysis" variant="success" />
|
||||
|
||||
Raw 8051 machine code extracted from C2 segments. No headers or framing -- just code bytes at their target RAM addresses. Used for Ghidra analysis and binary diffing.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## C2 EEPROM Format
|
||||
|
||||
### Header Structure (8 bytes)
|
||||
|
||||
```
|
||||
Offset Size Field Value (SkyWalker-1)
|
||||
------ ---- ---------- -------------------
|
||||
0x00 1 marker 0xC2 (external memory, large code model)
|
||||
0x01 2 VID 0xC009 -> 0x09C0 (little-endian, Genpix)
|
||||
0x03 2 PID 0x0302 -> 0x0203 (little-endian, SkyWalker-1)
|
||||
0x05 2 DID 0x0000 (device ID, unused)
|
||||
0x07 1 config 0x40 (400 kHz I2C bus speed)
|
||||
```
|
||||
|
||||
The VID and PID in the C2 header determine the USB identifiers that the FX2 enumerates with after boot. This is how the kernel driver identifies the device model.
|
||||
|
||||
### Config Byte (offset 0x07)
|
||||
|
||||
| Value | I2C Speed | Notes |
|
||||
|-------|-----------|-------|
|
||||
| `0x00` | 100 kHz | Default if EEPROM missing |
|
||||
| `0x20` | 200 kHz | |
|
||||
| `0x40` | **400 kHz** | Used by SkyWalker-1 |
|
||||
|
||||
### Code Segment Structure
|
||||
|
||||
Following the 8-byte header, one or more code segments:
|
||||
|
||||
```
|
||||
Offset Size Field
|
||||
------ ---------- -----
|
||||
0 2 seg_len (big-endian) -- number of data bytes
|
||||
2 2 seg_addr (big-endian) -- target RAM address
|
||||
4 seg_len data[] -- code/data bytes
|
||||
```
|
||||
|
||||
Segments are packed contiguously. The 1023-byte maximum segment size is the limit of the FX2 boot ROM's internal I2C read buffer.
|
||||
|
||||
### Terminator
|
||||
|
||||
```
|
||||
Offset Size Field
|
||||
------ ---- -----
|
||||
0 2 0x8001 -- high bit set signals terminator (length with MSB set)
|
||||
2 2 entry -- entry point address (big-endian) = 0xE600 (CPUCS)
|
||||
```
|
||||
|
||||
Writing to CPUCS (`0xE600`) with value `0x00` releases the CPU from reset and begins execution at the reset vector (`0x0000`).
|
||||
|
||||
## Decoded C2 Files
|
||||
|
||||
### Header Comparison
|
||||
|
||||
| File | VID | PID | DID | I2C Speed | Code Size |
|
||||
|------|-----|-----|-----|-----------|-----------|
|
||||
| `skywalker1_eeprom.bin` (v2.06) | `0x09C0` | **`0x0203`** | `0x0000` | 400 kHz | 9,472 bytes |
|
||||
| `sw1_v213_fw_1_c2.bin` (v2.13.1) | `0x09C0` | **`0x0203`** | `0x0000` | 400 kHz | 9,322 bytes |
|
||||
| `sw1_v213_fw_2_c2.bin` (v2.13.2) | `0x09C0` | **`0x0203`** | `0x0000` | 400 kHz | 9,377 bytes |
|
||||
| `sw1_v213_fw_3_c2.bin` (v2.13.3) | `0x09C0` | **`0x0203`** | `0x0000` | 400 kHz | 9,369 bytes |
|
||||
| `rev2_v210_fw_1_c2.bin` (Rev.2) | `0x09C0` | **`0x0202`** | `0x0000` | 400 kHz | 8,843 bytes |
|
||||
|
||||
<Aside type="note">
|
||||
Rev.2 uses PID `0x0202` while all SkyWalker-1 variants use `0x0203`. This PID difference is the primary mechanism by which the kernel driver distinguishes hardware revisions. The DID field is unused (always `0x0000`).
|
||||
</Aside>
|
||||
|
||||
### Segment Layout (SkyWalker-1 Variants)
|
||||
|
||||
All SkyWalker-1 C2 files use uniform 1023-byte segments (except the last):
|
||||
|
||||
| Segment | Address | Length | Notes |
|
||||
|---------|---------|-------:|-------|
|
||||
| 1 | `0x0000` | 1023 | Reset vector, interrupt handlers |
|
||||
| 2 | `0x03FF` | 1023 | |
|
||||
| 3 | `0x07FE` | 1023 | |
|
||||
| 4 | `0x0BFD` | 1023 | |
|
||||
| 5 | `0x0FFC` | 1023 | |
|
||||
| 6 | `0x13FB` | 1023 | |
|
||||
| 7 | `0x17FA` | 1023 | |
|
||||
| 8 | `0x1BF9` | 1023 | |
|
||||
| 9 | `0x1FF8` | 1023 | |
|
||||
| 10 | `0x23F7` | varies | 115--265 bytes depending on version |
|
||||
|
||||
The address of each segment is exactly `previous_addr + 1023`, creating a contiguous mapping from `0x0000` to the end of the firmware image.
|
||||
|
||||
### Rev.2 Segment Layout
|
||||
|
||||
Rev.2 has only 9 segments (one fewer than SkyWalker-1 variants) because its firmware is smaller:
|
||||
|
||||
| Segment | Address | Length |
|
||||
|---------|---------|-------:|
|
||||
| 1 | `0x0000` | 1023 |
|
||||
| 2 | `0x03FF` | 1023 |
|
||||
| 3 | `0x07FE` | 1023 |
|
||||
| 4 | `0x0BFD` | 1023 |
|
||||
| 5 | `0x0FFC` | 1023 |
|
||||
| 6 | `0x13FB` | 1023 |
|
||||
| 7 | `0x17FA` | 1023 |
|
||||
| 8 | `0x1BF9` | 1023 |
|
||||
| 9 | `0x1FF8` | 659 |
|
||||
|
||||
## Kernel Binary Hexline Format
|
||||
|
||||
This format is used exclusively by `dvb-usb-gp8psk-01.fw` for loading firmware into Rev.1 Cold devices (PID `0x0200`). The SkyWalker-1 never uses this format at runtime.
|
||||
|
||||
### Record Structure
|
||||
|
||||
```
|
||||
Offset Size Field
|
||||
------ ---- -----
|
||||
0 1 len Number of data bytes in this record
|
||||
1 1 addr_lo Target address, low byte
|
||||
2 1 addr_hi Target address, high byte
|
||||
3 1 type Record type
|
||||
4 len data[] Payload bytes
|
||||
4+len 1 chk Checksum byte
|
||||
|
||||
Total per record: len + 5 bytes
|
||||
```
|
||||
|
||||
### Record Types
|
||||
|
||||
| Type | Name | Purpose |
|
||||
|------|------|---------|
|
||||
| `0x00` | Data | Write data bytes to FX2 RAM at address `(addr_hi * 256 + addr_lo)` |
|
||||
| `0x01` | EOF | End of file, no more records |
|
||||
| `0x04` | Extended Linear Address | `data[0]:data[1]` = upper 16 bits of target address |
|
||||
|
||||
### Comparison With Other DVB-USB Firmware
|
||||
|
||||
Files from other DVB-USB devices confirm the format:
|
||||
|
||||
| File | Size | First Record |
|
||||
|------|-----:|-------------|
|
||||
| `dvb-usb-dib0700-1.20.fw` | 33,768 | len=2, addr=0x0000, type=0x04 |
|
||||
| `dvb-usb-it9135-01.fw` | 8,128 | len=3, addr=0x0000, type=0x03 |
|
||||
| `dvb-usb-it9135-02.fw` | 5,834 | len=3, addr=0x0000, type=0x03 |
|
||||
|
||||
## 64K Full EEPROM Dump
|
||||
|
||||
The complete EEPROM can be dumped as a 65,536-byte raw image. The firmware occupies the first ~10 KB; the remainder contains:
|
||||
|
||||
| Offset Range | Content |
|
||||
|-------------|---------|
|
||||
| `0x0000`--`0x0007` | C2 header (8 bytes) |
|
||||
| `0x0008`--`0x25xx` | Code segments (varies by version) |
|
||||
| `0x25xx`--`0x26xx` | C2 terminator |
|
||||
| `0x2700`--`0x3FFF` | Padding / unused (typically `0xFF`) |
|
||||
| `0x4000`--`0x7FFF` | Mirror/unused (some EEPROMs wrap at 32K) |
|
||||
|
||||
<Aside type="caution">
|
||||
Not all 64K images are identical even for the same firmware version. The "unused" region after the terminator may contain artifacts from previous firmware flashes or EEPROM test patterns. Only the data from the C2 header through the terminator is meaningful.
|
||||
</Aside>
|
||||
|
||||
## Format Conversion
|
||||
|
||||
### C2 to Flat Binary
|
||||
|
||||
Extract raw code from a C2 file by stripping headers:
|
||||
|
||||
```python title="C2 to flat binary extraction"
|
||||
def c2_to_flat(c2_data):
|
||||
"""Extract flat binary from C2 EEPROM format."""
|
||||
pos = 8 # Skip 8-byte C2 header
|
||||
flat = bytearray(0x10000) # 64K address space
|
||||
|
||||
while pos < len(c2_data):
|
||||
seg_len = (c2_data[pos] << 8) | c2_data[pos + 1]
|
||||
seg_addr = (c2_data[pos + 2] << 8) | c2_data[pos + 3]
|
||||
pos += 4
|
||||
|
||||
if seg_len & 0x8000: # Terminator (high bit set)
|
||||
break
|
||||
|
||||
flat[seg_addr:seg_addr + seg_len] = c2_data[pos:pos + seg_len]
|
||||
pos += seg_len
|
||||
|
||||
return flat
|
||||
```
|
||||
|
||||
### C2 to Kernel Hexline
|
||||
|
||||
Theoretical conversion for producing a `dvb-usb-gp8psk-01.fw` equivalent:
|
||||
|
||||
```python title="C2 to kernel hexline conversion"
|
||||
def c2_to_hexline(c2_data):
|
||||
"""Convert C2 EEPROM format to kernel binary hexline."""
|
||||
records = bytearray()
|
||||
flat = c2_to_flat(c2_data)
|
||||
code_end = find_code_end(flat)
|
||||
|
||||
for addr in range(0, code_end, 16):
|
||||
chunk = flat[addr:addr + 16]
|
||||
rec_len = len(chunk)
|
||||
addr_lo = addr & 0xFF
|
||||
addr_hi = (addr >> 8) & 0xFF
|
||||
rec_type = 0x00 # Data record
|
||||
chk = (rec_len + addr_lo + addr_hi + rec_type
|
||||
+ sum(chunk)) & 0xFF
|
||||
records.extend([rec_len, addr_lo, addr_hi, rec_type])
|
||||
records.extend(chunk)
|
||||
records.append((~chk + 1) & 0xFF)
|
||||
|
||||
# EOF record
|
||||
records.extend([0x00, 0x00, 0x00, 0x01, 0xFF])
|
||||
return records
|
||||
```
|
||||
|
||||
For the v2.06 firmware (9,472 code bytes), this produces approximately 12,442 bytes in hexline format.
|
||||
|
||||
## File Identification
|
||||
|
||||
Quick format identification by examining the first byte:
|
||||
|
||||
| First Byte | Format | Notes |
|
||||
|------------|--------|-------|
|
||||
| `0xC2` | C2 EEPROM | Standard SkyWalker-1 EEPROM dump |
|
||||
| `0xC0` | C0 EEPROM | VID/PID-only header (no code segments) |
|
||||
| `0x02` | Flat binary | Likely starts with `LJMP` instruction (`0x02 xx xx`) |
|
||||
| Other | Hexline or unknown | Check record structure |
|
||||
---
|
||||
title: Firmware Storage Formats
|
||||
description: Cypress C2 EEPROM boot format, kernel hexline format, and flat binary extraction.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Aside, FileTree, Badge } from '@astrojs/starlight/components';
|
||||
|
||||
The SkyWalker-1 firmware exists in multiple container formats depending on context: the onboard EEPROM uses the Cypress C2 IIC boot format, the Linux kernel expects a custom binary hexline format, and analysis tools work with flat extracted binaries.
|
||||
|
||||
## Format Overview
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="C2 EEPROM">
|
||||
|
||||
### Cypress C2 IIC Second-Stage Boot Format <Badge text="Device Storage" variant="note" />
|
||||
|
||||
This is the native format stored in the SkyWalker-1's onboard I2C EEPROM. The FX2's internal boot ROM reads this format on power-up.
|
||||
|
||||
The `0xC2` marker byte in the header identifies this as "external memory, large code model" -- it tells the boot ROM to load code from the EEPROM into internal RAM using the segment map that follows.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Hexline">
|
||||
|
||||
### Kernel Binary Hexline Format <Badge text="Linux Driver" variant="caution" />
|
||||
|
||||
Used only by `dvb-usb-gp8psk-01.fw` for Rev.1 Cold devices. This is a compact binary representation of Intel HEX records parsed by `dvb_usb_get_hexline()` in the kernel. It is NOT standard Intel HEX text.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Flat Binary">
|
||||
|
||||
### Flat Extracted Binary <Badge text="Analysis" variant="success" />
|
||||
|
||||
Raw 8051 machine code extracted from C2 segments. No headers or framing -- just code bytes at their target RAM addresses. Used for Ghidra analysis and binary diffing.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## C2 EEPROM Format
|
||||
|
||||
### Header Structure (8 bytes)
|
||||
|
||||
```
|
||||
Offset Size Field Value (SkyWalker-1)
|
||||
------ ---- ---------- -------------------
|
||||
0x00 1 marker 0xC2 (external memory, large code model)
|
||||
0x01 2 VID 0xC009 -> 0x09C0 (little-endian, Genpix)
|
||||
0x03 2 PID 0x0302 -> 0x0203 (little-endian, SkyWalker-1)
|
||||
0x05 2 DID 0x0000 (device ID, unused)
|
||||
0x07 1 config 0x40 (400 kHz I2C bus speed)
|
||||
```
|
||||
|
||||
The VID and PID in the C2 header determine the USB identifiers that the FX2 enumerates with after boot. This is how the kernel driver identifies the device model.
|
||||
|
||||
### Config Byte (offset 0x07)
|
||||
|
||||
| Value | I2C Speed | Notes |
|
||||
|-------|-----------|-------|
|
||||
| `0x00` | 100 kHz | Default if EEPROM missing |
|
||||
| `0x20` | 200 kHz | |
|
||||
| `0x40` | **400 kHz** | Used by SkyWalker-1 |
|
||||
|
||||
### Code Segment Structure
|
||||
|
||||
Following the 8-byte header, one or more code segments:
|
||||
|
||||
```
|
||||
Offset Size Field
|
||||
------ ---------- -----
|
||||
0 2 seg_len (big-endian) -- number of data bytes
|
||||
2 2 seg_addr (big-endian) -- target RAM address
|
||||
4 seg_len data[] -- code/data bytes
|
||||
```
|
||||
|
||||
Segments are packed contiguously. The 1023-byte maximum segment size is the limit of the FX2 boot ROM's internal I2C read buffer.
|
||||
|
||||
### Terminator
|
||||
|
||||
```
|
||||
Offset Size Field
|
||||
------ ---- -----
|
||||
0 2 0x8001 -- high bit set signals terminator (length with MSB set)
|
||||
2 2 entry -- entry point address (big-endian) = 0xE600 (CPUCS)
|
||||
```
|
||||
|
||||
Writing to CPUCS (`0xE600`) with value `0x00` releases the CPU from reset and begins execution at the reset vector (`0x0000`).
|
||||
|
||||
## Decoded C2 Files
|
||||
|
||||
### Header Comparison
|
||||
|
||||
| File | VID | PID | DID | I2C Speed | Code Size |
|
||||
|------|-----|-----|-----|-----------|-----------|
|
||||
| `skywalker1_eeprom.bin` (v2.06) | `0x09C0` | **`0x0203`** | `0x0000` | 400 kHz | 9,472 bytes |
|
||||
| `sw1_v213_fw_1_c2.bin` (v2.13.1) | `0x09C0` | **`0x0203`** | `0x0000` | 400 kHz | 9,322 bytes |
|
||||
| `sw1_v213_fw_2_c2.bin` (v2.13.2) | `0x09C0` | **`0x0203`** | `0x0000` | 400 kHz | 9,377 bytes |
|
||||
| `sw1_v213_fw_3_c2.bin` (v2.13.3) | `0x09C0` | **`0x0203`** | `0x0000` | 400 kHz | 9,369 bytes |
|
||||
| `rev2_v210_fw_1_c2.bin` (Rev.2) | `0x09C0` | **`0x0202`** | `0x0000` | 400 kHz | 8,843 bytes |
|
||||
|
||||
<Aside type="note">
|
||||
Rev.2 uses PID `0x0202` while all SkyWalker-1 variants use `0x0203`. This PID difference is the primary mechanism by which the kernel driver distinguishes hardware revisions. The DID field is unused (always `0x0000`).
|
||||
</Aside>
|
||||
|
||||
### Segment Layout (SkyWalker-1 Variants)
|
||||
|
||||
All SkyWalker-1 C2 files use uniform 1023-byte segments (except the last):
|
||||
|
||||
| Segment | Address | Length | Notes |
|
||||
|---------|---------|-------:|-------|
|
||||
| 1 | `0x0000` | 1023 | Reset vector, interrupt handlers |
|
||||
| 2 | `0x03FF` | 1023 | |
|
||||
| 3 | `0x07FE` | 1023 | |
|
||||
| 4 | `0x0BFD` | 1023 | |
|
||||
| 5 | `0x0FFC` | 1023 | |
|
||||
| 6 | `0x13FB` | 1023 | |
|
||||
| 7 | `0x17FA` | 1023 | |
|
||||
| 8 | `0x1BF9` | 1023 | |
|
||||
| 9 | `0x1FF8` | 1023 | |
|
||||
| 10 | `0x23F7` | varies | 115--265 bytes depending on version |
|
||||
|
||||
The address of each segment is exactly `previous_addr + 1023`, creating a contiguous mapping from `0x0000` to the end of the firmware image.
|
||||
|
||||
### Rev.2 Segment Layout
|
||||
|
||||
Rev.2 has only 9 segments (one fewer than SkyWalker-1 variants) because its firmware is smaller:
|
||||
|
||||
| Segment | Address | Length |
|
||||
|---------|---------|-------:|
|
||||
| 1 | `0x0000` | 1023 |
|
||||
| 2 | `0x03FF` | 1023 |
|
||||
| 3 | `0x07FE` | 1023 |
|
||||
| 4 | `0x0BFD` | 1023 |
|
||||
| 5 | `0x0FFC` | 1023 |
|
||||
| 6 | `0x13FB` | 1023 |
|
||||
| 7 | `0x17FA` | 1023 |
|
||||
| 8 | `0x1BF9` | 1023 |
|
||||
| 9 | `0x1FF8` | 659 |
|
||||
|
||||
## Kernel Binary Hexline Format
|
||||
|
||||
This format is used exclusively by `dvb-usb-gp8psk-01.fw` for loading firmware into Rev.1 Cold devices (PID `0x0200`). The SkyWalker-1 never uses this format at runtime.
|
||||
|
||||
### Record Structure
|
||||
|
||||
```
|
||||
Offset Size Field
|
||||
------ ---- -----
|
||||
0 1 len Number of data bytes in this record
|
||||
1 1 addr_lo Target address, low byte
|
||||
2 1 addr_hi Target address, high byte
|
||||
3 1 type Record type
|
||||
4 len data[] Payload bytes
|
||||
4+len 1 chk Checksum byte
|
||||
|
||||
Total per record: len + 5 bytes
|
||||
```
|
||||
|
||||
### Record Types
|
||||
|
||||
| Type | Name | Purpose |
|
||||
|------|------|---------|
|
||||
| `0x00` | Data | Write data bytes to FX2 RAM at address `(addr_hi * 256 + addr_lo)` |
|
||||
| `0x01` | EOF | End of file, no more records |
|
||||
| `0x04` | Extended Linear Address | `data[0]:data[1]` = upper 16 bits of target address |
|
||||
|
||||
### Comparison With Other DVB-USB Firmware
|
||||
|
||||
Files from other DVB-USB devices confirm the format:
|
||||
|
||||
| File | Size | First Record |
|
||||
|------|-----:|-------------|
|
||||
| `dvb-usb-dib0700-1.20.fw` | 33,768 | len=2, addr=0x0000, type=0x04 |
|
||||
| `dvb-usb-it9135-01.fw` | 8,128 | len=3, addr=0x0000, type=0x03 |
|
||||
| `dvb-usb-it9135-02.fw` | 5,834 | len=3, addr=0x0000, type=0x03 |
|
||||
|
||||
## 64K Full EEPROM Dump
|
||||
|
||||
The complete EEPROM can be dumped as a 65,536-byte raw image. The firmware occupies the first ~10 KB; the remainder contains:
|
||||
|
||||
| Offset Range | Content |
|
||||
|-------------|---------|
|
||||
| `0x0000`--`0x0007` | C2 header (8 bytes) |
|
||||
| `0x0008`--`0x25xx` | Code segments (varies by version) |
|
||||
| `0x25xx`--`0x26xx` | C2 terminator |
|
||||
| `0x2700`--`0x3FFF` | Padding / unused (typically `0xFF`) |
|
||||
| `0x4000`--`0x7FFF` | Mirror/unused (some EEPROMs wrap at 32K) |
|
||||
|
||||
<Aside type="caution">
|
||||
Not all 64K images are identical even for the same firmware version. The "unused" region after the terminator may contain artifacts from previous firmware flashes or EEPROM test patterns. Only the data from the C2 header through the terminator is meaningful.
|
||||
</Aside>
|
||||
|
||||
## Format Conversion
|
||||
|
||||
### C2 to Flat Binary
|
||||
|
||||
Extract raw code from a C2 file by stripping headers:
|
||||
|
||||
```python title="C2 to flat binary extraction"
|
||||
def c2_to_flat(c2_data):
|
||||
"""Extract flat binary from C2 EEPROM format."""
|
||||
pos = 8 # Skip 8-byte C2 header
|
||||
flat = bytearray(0x10000) # 64K address space
|
||||
|
||||
while pos < len(c2_data):
|
||||
seg_len = (c2_data[pos] << 8) | c2_data[pos + 1]
|
||||
seg_addr = (c2_data[pos + 2] << 8) | c2_data[pos + 3]
|
||||
pos += 4
|
||||
|
||||
if seg_len & 0x8000: # Terminator (high bit set)
|
||||
break
|
||||
|
||||
flat[seg_addr:seg_addr + seg_len] = c2_data[pos:pos + seg_len]
|
||||
pos += seg_len
|
||||
|
||||
return flat
|
||||
```
|
||||
|
||||
### C2 to Kernel Hexline
|
||||
|
||||
Theoretical conversion for producing a `dvb-usb-gp8psk-01.fw` equivalent:
|
||||
|
||||
```python title="C2 to kernel hexline conversion"
|
||||
def c2_to_hexline(c2_data):
|
||||
"""Convert C2 EEPROM format to kernel binary hexline."""
|
||||
records = bytearray()
|
||||
flat = c2_to_flat(c2_data)
|
||||
code_end = find_code_end(flat)
|
||||
|
||||
for addr in range(0, code_end, 16):
|
||||
chunk = flat[addr:addr + 16]
|
||||
rec_len = len(chunk)
|
||||
addr_lo = addr & 0xFF
|
||||
addr_hi = (addr >> 8) & 0xFF
|
||||
rec_type = 0x00 # Data record
|
||||
chk = (rec_len + addr_lo + addr_hi + rec_type
|
||||
+ sum(chunk)) & 0xFF
|
||||
records.extend([rec_len, addr_lo, addr_hi, rec_type])
|
||||
records.extend(chunk)
|
||||
records.append((~chk + 1) & 0xFF)
|
||||
|
||||
# EOF record
|
||||
records.extend([0x00, 0x00, 0x00, 0x01, 0xFF])
|
||||
return records
|
||||
```
|
||||
|
||||
For the v2.06 firmware (9,472 code bytes), this produces approximately 12,442 bytes in hexline format.
|
||||
|
||||
## File Identification
|
||||
|
||||
Quick format identification by examining the first byte:
|
||||
|
||||
| First Byte | Format | Notes |
|
||||
|------------|--------|-------|
|
||||
| `0xC2` | C2 EEPROM | Standard SkyWalker-1 EEPROM dump |
|
||||
| `0xC0` | C0 EEPROM | VID/PID-only header (no code segments) |
|
||||
| `0x02` | Flat binary | Likely starts with `LJMP` instruction (`0x02 xx xx`) |
|
||||
| Other | Hexline or unknown | Check record structure |
|
||||
|
|
|
|||
|
|
@ -1,330 +1,330 @@
|
|||
---
|
||||
title: Firmware Version Comparison
|
||||
description: Side-by-side comparison of all known SkyWalker-1 firmware revisions including stock and custom builds.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
Six firmware versions have been analyzed through Ghidra reverse engineering and source code review. This page compares their architecture, features, and binary characteristics.
|
||||
|
||||
## Version Summary
|
||||
|
||||
| Firmware | Version ID | Build Date | Target PID | Functions | Binary Size | Stack Pointer |
|
||||
|----------|-----------|------------|------------|-----------|-------------|---------------|
|
||||
| Custom v3.05.0 | `0x030500` | 2026-02-16 | `0x0203` | N/A | 13,079 bytes (RAM) | `0x7B` |
|
||||
| Custom v3.01.0 | `0x030100` | 2026-02-12 | `0x0203` | N/A | ~3 KB (RAM) | N/A |
|
||||
| v2.13.01 (FW1) | `0x020D01` | 2010-03-12 | `0x0203` | 82-88 | 9,322 bytes | `0x50` |
|
||||
| v2.13.02 (FW2) | `0x020D01` | 2010-03-12 | `0x0203` | 83 | 9,377 bytes | `0x50` |
|
||||
| v2.13.03 (FW3) | `0x020D01` | 2010-03-12 | `0x0203` | 83 | 9,369 bytes | `0x52` |
|
||||
| Rev.2 v2.10.04 | `0x020A04` | 2010-03-12 | `0x0202` | 107 | 8,843 bytes | `0x4F` |
|
||||
| v2.06.04 | `0x020604` | 2007-07-13 | `0x0203` | 61 | 9,472 bytes | `0x72` |
|
||||
|
||||
<Aside type="note">
|
||||
Rev.2 v2.10 targets PID `0x0202` (a different product line). All other versions target `0x0203` (SkyWalker-1). The custom firmware versions are compiled with SDCC + fx2lib and loaded into FX2 RAM, not flashed to EEPROM. Custom v3.05.0 is the safety-hardened release with software watchdog and comprehensive timeout protection.
|
||||
</Aside>
|
||||
|
||||
## Version-by-Version Details
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Custom v3.05">
|
||||
|
||||
### Custom v3.05.0 <Badge text="Custom" variant="success" /> <Badge text="Safety" variant="caution" />
|
||||
|
||||
Safety-hardened firmware with software watchdog and timeout protection on all code paths. Built on the v3.04 codebase with all features from v3.01 through v3.04.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Version ID | `0x030500` |
|
||||
| Build date | 2026-02-16 |
|
||||
| Toolchain | SDCC + fx2lib |
|
||||
| Source | `firmware/skywalker1.c` (2,256 lines) |
|
||||
| Code size | 13,079 / 15,360 bytes (85%) |
|
||||
| XRAM | 218 / 512 bytes (43%) |
|
||||
| Stack | 132 bytes available |
|
||||
| Error codes | 14 (`0x00`--`0x0D`) |
|
||||
|
||||
**Additions over v3.04:**
|
||||
- Timer0 software watchdog (2-second window, cuts LNB power on stall)
|
||||
- Timeout protection on all EP0, GPIF, EP2, and DiSEqC Timer2 spin loops
|
||||
- `ep0_wait_data()` helper replacing 7 bare `while (EP0CS & bmEPBUSY)` loops
|
||||
- EP0 payload length validation on all OUT vendor commands
|
||||
- I2C return-value checks on all write operations in sweep/scan functions
|
||||
- `do_tune()` rewritten with timeout-protected I2C and early guard for unbooted BCM4500
|
||||
- 8 new error codes for observable failure modes
|
||||
- 55-test Hamilton adversarial test suite (`test_hamilton.py`)
|
||||
|
||||
See the [Custom v3.05](/firmware/custom-v305/) page for the full safety review findings and hardware validation results.
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="Custom v3.01">
|
||||
|
||||
### Custom v3.01.0 <Badge text="Custom" variant="success" />
|
||||
|
||||
Open-source replacement firmware built with SDCC + fx2lib. RAM-loaded for testing.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Version ID | `0x030100` |
|
||||
| Build date | 2026-02-12 |
|
||||
| Toolchain | SDCC + fx2lib |
|
||||
| Source | `firmware/skywalker1.c` (1351 lines) |
|
||||
| Binary size | ~3 KB |
|
||||
| Load method | RAM upload via `tools/fw_load.py` |
|
||||
| DiSEqC data pin | P0.7 (v2.06 assignment) |
|
||||
|
||||
**Additions over stock:**
|
||||
- Seven new diagnostic commands (`0xB0`--`0xB6`)
|
||||
- Incremental debug boot modes (wValue `0x80`--`0x85` for BOOT_8PSK)
|
||||
- I2C timeout protection (6000-iteration countdown vs. infinite spin)
|
||||
- I2C bus scan for device discovery
|
||||
- Spectrum sweep and blind scan capabilities
|
||||
- Raw BCM4500 register access
|
||||
|
||||
See the [Custom v3.01](/firmware/custom-v301/) page for full details.
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="v2.13 FW1">
|
||||
|
||||
### v2.13.01 (FW1) <Badge text="Stock" variant="note" />
|
||||
|
||||
The most feature-complete stock firmware, targeting the original I2C-connected SkyWalker-1 hardware.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Version ID | `0x020D01` |
|
||||
| Build date | 2010-03-12 |
|
||||
| Functions | 82-88 |
|
||||
| Binary size | 9,322 bytes |
|
||||
| Stack pointer | `0x50` |
|
||||
| Config byte IRAM | `0x4F` |
|
||||
| Descriptor base | `0x0E00` |
|
||||
| Init table address | `CODE:0B88` |
|
||||
| Vendor commands | 30 (`0x80`--`0x9D`) |
|
||||
| DiSEqC data pin | P0.0 |
|
||||
|
||||
**New features over v2.06:**
|
||||
- Three new vendor commands: `0x99` (GET_DEMOD_STATUS), `0x9A` (INIT_DEMOD), `0x9C` (DELAY_COMMAND)
|
||||
- INT0 repurposed for demodulator availability polling (40 attempts at addresses 0x7F and 0x3F)
|
||||
- USB re-enumeration moved to `FUN_CODE_2031` (called as normal function before main loop)
|
||||
- Demodulator signature verification (`FUN_CODE_1799`) with 20 retry attempts
|
||||
- Descriptor checksum verification (`FUN_CODE_1ca0`) with 20 retry attempts
|
||||
- Hardware revision detection via descriptor byte (flag `_1_3`)
|
||||
- Consolidated BCM4500 status polling (1 register instead of 3)
|
||||
- Anti-tampering string at firmware offset 0x1880
|
||||
|
||||
See the [FW2.13 Variants](/firmware/fw213-variants/) page for sub-variant comparison.
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="Rev.2 v2.10">
|
||||
|
||||
### Rev.2 v2.10.04 <Badge text="Transitional" variant="caution" />
|
||||
|
||||
Firmware for the Rev.2 hardware variant (PID `0x0202`). Architecturally sits between v2.06 and v2.13.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Version ID | `0x020A04` |
|
||||
| Build date | 2010-03-12 |
|
||||
| Functions | 107 (most of any version) |
|
||||
| Binary size | 8,843 bytes (smallest) |
|
||||
| Stack pointer | `0x4F` |
|
||||
| Config byte IRAM | `0x4E` |
|
||||
| Descriptor base | `0x0E00` |
|
||||
| Init table address | `CODE:0B48` |
|
||||
| Vendor commands | 27 (`0x80`--`0x9A`) |
|
||||
| DiSEqC data pin | P0.4 |
|
||||
|
||||
**Characteristics:**
|
||||
- Highest function count due to granular decomposition (10-30 byte helper functions)
|
||||
- Smallest binary despite having the most functions
|
||||
- Retained v2.06's INT0 USB re-enumeration behavior
|
||||
- Already adopted v2.13's descriptor base (`0x0E00`) and similar stack pointer
|
||||
- Contains `FUN_CODE_0800`: a massive 874-byte configuration dispatcher with embedded main loop
|
||||
- Lacks v2.13's demodulator polling, retry loops, and additional vendor commands (0x9B--0x9D out of range)
|
||||
- 0x99/0x9A present as prototype implementations
|
||||
|
||||
See the [Rev.2 Analysis](/firmware/rev2-analysis/) page for the complete 107-function inventory.
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="v2.06">
|
||||
|
||||
### v2.06.04 <Badge text="Stock" variant="note" />
|
||||
|
||||
The original SkyWalker-1 firmware extracted from the device's onboard EEPROM.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Version ID | `0x020604` |
|
||||
| Build date | 2007-07-13 |
|
||||
| Functions | 61 |
|
||||
| Binary size | 9,472 bytes |
|
||||
| Stack pointer | `0x72` |
|
||||
| Config byte IRAM | `0x6D` |
|
||||
| Descriptor base | `0x1200` |
|
||||
| Init table address | `CODE:0B46` |
|
||||
| Vendor commands | 30 (`0x80`--`0x9D`) |
|
||||
| DiSEqC data pin | P0.7 |
|
||||
|
||||
**Characteristics:**
|
||||
- Simplest firmware with the fewest functions
|
||||
- INT0 handler performs USB re-enumeration (CPUCS pulse)
|
||||
- No demodulator probe at boot
|
||||
- No retry loops or integrity verification
|
||||
- BCM4500 status polling reads 3 registers (0xA2, 0xA8, 0xA4) up to 6 times
|
||||
- Commands 0x99, 0x9A, 0x9C route to STALL
|
||||
- Command 0x9D reads descriptor byte and sets mode flag based on hardware revision (4, 5, or 6)
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Architectural Differences
|
||||
|
||||
| Feature | Custom v3.05 | Custom v3.01 | v2.13 | Rev.2 v2.10 | v2.06 |
|
||||
|---------|--------------|--------------|-------|-------------|-------|
|
||||
| Vendor commands | 30 stock + 18 custom | 30 stock + 7 custom | 30 | 27 | 30 |
|
||||
| INT0 handler | N/A (fx2lib ISR) | N/A (fx2lib ISR) | Demod polling | USB re-enum | USB re-enum |
|
||||
| Demod probe at boot | Yes (with timeout) | Yes (with timeout) | Yes (40 attempts) | No | No |
|
||||
| Retry loops | Yes (with timeout) | Yes (with timeout) | Yes (20-attempt) | No | No |
|
||||
| HW revision detect | No | No | Yes (flag `_1_3`) | Yes (descriptor walker) | No |
|
||||
| DiSEqC data pin | P0.7 | P0.7 | P0.0 | P0.4 | P0.7 |
|
||||
| Config byte IRAM addr | C variable | C variable | `0x4F` | `0x4E` | `0x6D` |
|
||||
| BCM4500 status poll | 1 register | 1 register | 1 register | 3 registers | 3 registers |
|
||||
| I2C timeout | All paths (6000-count) | Helpers only (6000-count) | None | None | None |
|
||||
| Software watchdog | Timer0 (2-second) | None | None | None | None |
|
||||
| EP0/GPIF/EP2 timeout | All protected | None | None | None | None |
|
||||
| Error codes | 14 | 6 | N/A | N/A | N/A |
|
||||
| Anti-tampering | No | No | Yes | No | No |
|
||||
| New commands | 0xB0--0xBE | 0xB0--0xB6 | 0x99, 0x9A, 0x9C | 0x99/0x9A proto | -- |
|
||||
|
||||
## Kernel Version Constants
|
||||
|
||||
The Linux kernel driver defines two firmware version thresholds in `gp8psk-fe.h`:
|
||||
|
||||
```c title="Kernel firmware version constants"
|
||||
GP8PSK_FW_REV1 = 0x020604 // v2.06.4
|
||||
GP8PSK_FW_REV2 = 0x020704 // v2.07.4
|
||||
```
|
||||
|
||||
If the firmware version reported by `GET_FW_VERS` (command `0x92`) is >= `GP8PSK_FW_REV2`, the kernel enables Rev.2-specific code paths. All v2.10 and v2.13 firmwares are newer than either constant.
|
||||
|
||||
<Aside type="caution" title="Version ID Gaps">
|
||||
`GP8PSK_FW_REV2` (`0x020704` = v2.07.04) is a **kernel threshold constant**, not an actual firmware release. No firmware with version 2.07.x was ever distributed. The real version progression is:
|
||||
|
||||
**v2.06.04** → **v2.10.04** → **v2.13.01**
|
||||
|
||||
Versions v2.07 through v2.09 and v2.11 through v2.12 do not exist. The kernel developers chose `0x020704` as a convenient threshold above the known v2.06.04 release.
|
||||
|
||||
All three v2.13 sub-variants (FW1, FW2, FW3) report the same version ID `0x020D01` despite targeting different hardware. See [FW2.13 Variants](/firmware/fw213-variants/) for how they differ.
|
||||
</Aside>
|
||||
|
||||
## Binary Similarity Matrix
|
||||
|
||||
Byte-level comparison across the shared code length (percentage of identical bytes):
|
||||
|
||||
| | v2.06 | v2.13.1 | v2.13.2 | v2.13.3 | Rev.2 |
|
||||
|---|---|---|---|---|---|
|
||||
| **v2.06** | -- | 4.8% | 4.3% | 4.3% | 6.0% |
|
||||
| **v2.13.1** | | -- | 57.2% | 59.4% | 8.0% |
|
||||
| **v2.13.2** | | | -- | 83.5% | 5.8% |
|
||||
| **v2.13.3** | | | | -- | 5.8% |
|
||||
| **Rev.2** | | | | | -- |
|
||||
|
||||
<Aside type="note">
|
||||
The extremely low similarity between major versions (4--8%) indicates complete recompilation with different linker configurations. Functions relocate to entirely different addresses even when the logic is identical. Only the vendor command dispatcher at `CODE:0056` maintains the same address across all versions.
|
||||
</Aside>
|
||||
|
||||
## Key Function Correspondence
|
||||
|
||||
Functions that serve the same role but reside at different addresses:
|
||||
|
||||
| Role | v2.06 | Rev.2 | v2.13 |
|
||||
|------|-------|-------|-------|
|
||||
| RESET vector / main | `0x188D` | `0x155F` | `0x170D` |
|
||||
| Main init + loop | `0x09A7` | `0x09A9` | `0x0800` |
|
||||
| USB descriptor setup | `0x13C3` | `0x10D9` | `0x11AB` |
|
||||
| Standard USB handler | `0x032A` | `0x0319` | `0x034E` |
|
||||
| Vendor cmd dispatch | `0x0056` | `0x0056` | `0x0056` |
|
||||
| Main loop poll | `0x2297` | -- | `0x21EC` |
|
||||
| GPIF/FIFO management | `0x1919` | `0x0D7C` | `0x1800` |
|
||||
| BCM4500 firmware loader | `0x0DDD` | `0x0C64` | `0x0CA4` |
|
||||
| BCM4500 status polling | `0x2000` | -- | `0x208D` |
|
||||
| Delay loop | `0x1DFB` | `0x1BDA` | `0x14B9` |
|
||||
|
||||
## INT0 Handler Evolution
|
||||
|
||||
The INT0 interrupt vector (`CODE:0003`) was repurposed between firmware generations:
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="v2.06 / Rev.2">
|
||||
|
||||
**USB Re-enumeration** -- pulses CPUCS bit 3 to trigger controlled USB disconnect/reconnect:
|
||||
|
||||
```c title="INT0 handler (v2.06 and Rev.2)"
|
||||
void INT0_vec(void) {
|
||||
if (flag == 0) CPUCS |= 0x08; // CPUCS bit 3
|
||||
else CPUCS |= 0x0A; // CPUCS bits 3+1
|
||||
delay(5, 0xDC); // ~1500 cycles
|
||||
EPIRQ = 0xFF; // Clear endpoint IRQs
|
||||
USBIRQ = 0xFF; // Clear USB IRQs
|
||||
EXIF &= 0xEF; // Clear external interrupt flag
|
||||
CPUCS &= 0xF7; // Clear CPUCS bit 3
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="v2.13">
|
||||
|
||||
**Demodulator Availability Polling** -- probes two I2C addresses up to 40 times:
|
||||
|
||||
```c title="INT0 handler (v2.13)"
|
||||
void INT0_vector(void) {
|
||||
for (counter = 0x28; counter != 0; counter--) {
|
||||
byte result = I2C_read(0x7F);
|
||||
if (result != 0x01) {
|
||||
result = I2C_read(0x3F);
|
||||
if (result != 0x01) break;
|
||||
}
|
||||
}
|
||||
no_demod_flag = (counter == 0);
|
||||
}
|
||||
```
|
||||
|
||||
The USB re-enumeration logic was moved to `FUN_CODE_2031` and called as a normal function before the main loop.
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## XRAM Initialization Table
|
||||
|
||||
All versions initialize FX2 peripheral registers from a CODE-space table at startup. The table format is identical: `[addr_hi] [addr_lo] [data_byte]` triplets terminated by `0x0000`.
|
||||
|
||||
| Firmware | Table Address | Key Registers Set |
|
||||
|----------|--------------|-------------------|
|
||||
| v2.06 | `CODE:0B46` | IFCONFIG, EP2CFG, EP2FIFOCFG, REVCTL, I2CTL |
|
||||
| Rev.2 | `CODE:0B48` | Same set, 2 bytes later |
|
||||
| v2.13 | `CODE:0B88` | Same set, different offsets |
|
||||
|
||||
All versions set the same final values: `IFCONFIG=0xEE`, `EP2CFG=0xE2`, `EP2FIFOCFG=0x0C`, `REVCTL=0x03`, `I2CTL=0x01`.
|
||||
|
||||
## Anti-Tampering (v2.13 Only)
|
||||
|
||||
All v2.13 sub-variants contain this string at firmware offset `0x1880`:
|
||||
|
||||
```
|
||||
"Tampering is detected. Attempt is logged. Warranty is voided ! \n"
|
||||
```
|
||||
|
||||
This is followed by I2C register write commands (`01 10 aa 82 02 41 41 83`). The mechanism is absent from v2.06, Rev.2, and the custom firmware.
|
||||
|
||||
## Version Identification
|
||||
|
||||
The `GET_FW_VERS` command (`0x92`) returns 6 bytes of hardcoded constants:
|
||||
|
||||
```
|
||||
Byte 0: version minor_minor (e.g., 0x04)
|
||||
Byte 1: version minor (e.g., 0x06)
|
||||
Byte 2: version major (e.g., 0x02)
|
||||
Byte 3: build day (e.g., 0x0D = 13)
|
||||
Byte 4: build month (e.g., 0x07 = July)
|
||||
Byte 5: build year - 2000 (e.g., 0x07 = 2007)
|
||||
```
|
||||
|
||||
Full version = `byte[2] << 16 | byte[1] << 8 | byte[0]`. Build date = `(2000 + byte[5]) / byte[4] / byte[3]`.
|
||||
---
|
||||
title: Firmware Version Comparison
|
||||
description: Side-by-side comparison of all known SkyWalker-1 firmware revisions including stock and custom builds.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
Six firmware versions have been analyzed through Ghidra reverse engineering and source code review. This page compares their architecture, features, and binary characteristics.
|
||||
|
||||
## Version Summary
|
||||
|
||||
| Firmware | Version ID | Build Date | Target PID | Functions | Binary Size | Stack Pointer |
|
||||
|----------|-----------|------------|------------|-----------|-------------|---------------|
|
||||
| Custom v3.05.0 | `0x030500` | 2026-02-16 | `0x0203` | N/A | 13,079 bytes (RAM) | `0x7B` |
|
||||
| Custom v3.01.0 | `0x030100` | 2026-02-12 | `0x0203` | N/A | ~3 KB (RAM) | N/A |
|
||||
| v2.13.01 (FW1) | `0x020D01` | 2010-03-12 | `0x0203` | 82-88 | 9,322 bytes | `0x50` |
|
||||
| v2.13.02 (FW2) | `0x020D01` | 2010-03-12 | `0x0203` | 83 | 9,377 bytes | `0x50` |
|
||||
| v2.13.03 (FW3) | `0x020D01` | 2010-03-12 | `0x0203` | 83 | 9,369 bytes | `0x52` |
|
||||
| Rev.2 v2.10.04 | `0x020A04` | 2010-03-12 | `0x0202` | 107 | 8,843 bytes | `0x4F` |
|
||||
| v2.06.04 | `0x020604` | 2007-07-13 | `0x0203` | 61 | 9,472 bytes | `0x72` |
|
||||
|
||||
<Aside type="note">
|
||||
Rev.2 v2.10 targets PID `0x0202` (a different product line). All other versions target `0x0203` (SkyWalker-1). The custom firmware versions are compiled with SDCC + fx2lib and loaded into FX2 RAM, not flashed to EEPROM. Custom v3.05.0 is the safety-hardened release with software watchdog and comprehensive timeout protection.
|
||||
</Aside>
|
||||
|
||||
## Version-by-Version Details
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Custom v3.05">
|
||||
|
||||
### Custom v3.05.0 <Badge text="Custom" variant="success" /> <Badge text="Safety" variant="caution" />
|
||||
|
||||
Safety-hardened firmware with software watchdog and timeout protection on all code paths. Built on the v3.04 codebase with all features from v3.01 through v3.04.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Version ID | `0x030500` |
|
||||
| Build date | 2026-02-16 |
|
||||
| Toolchain | SDCC + fx2lib |
|
||||
| Source | `firmware/skywalker1.c` (2,256 lines) |
|
||||
| Code size | 13,079 / 15,360 bytes (85%) |
|
||||
| XRAM | 218 / 512 bytes (43%) |
|
||||
| Stack | 132 bytes available |
|
||||
| Error codes | 14 (`0x00`--`0x0D`) |
|
||||
|
||||
**Additions over v3.04:**
|
||||
- Timer0 software watchdog (2-second window, cuts LNB power on stall)
|
||||
- Timeout protection on all EP0, GPIF, EP2, and DiSEqC Timer2 spin loops
|
||||
- `ep0_wait_data()` helper replacing 7 bare `while (EP0CS & bmEPBUSY)` loops
|
||||
- EP0 payload length validation on all OUT vendor commands
|
||||
- I2C return-value checks on all write operations in sweep/scan functions
|
||||
- `do_tune()` rewritten with timeout-protected I2C and early guard for unbooted BCM4500
|
||||
- 8 new error codes for observable failure modes
|
||||
- 55-test Hamilton adversarial test suite (`test_hamilton.py`)
|
||||
|
||||
See the [Custom v3.05](/firmware/custom-v305/) page for the full safety review findings and hardware validation results.
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="Custom v3.01">
|
||||
|
||||
### Custom v3.01.0 <Badge text="Custom" variant="success" />
|
||||
|
||||
Open-source replacement firmware built with SDCC + fx2lib. RAM-loaded for testing.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Version ID | `0x030100` |
|
||||
| Build date | 2026-02-12 |
|
||||
| Toolchain | SDCC + fx2lib |
|
||||
| Source | `firmware/skywalker1.c` (1351 lines) |
|
||||
| Binary size | ~3 KB |
|
||||
| Load method | RAM upload via `tools/fw_load.py` |
|
||||
| DiSEqC data pin | P0.7 (v2.06 assignment) |
|
||||
|
||||
**Additions over stock:**
|
||||
- Seven new diagnostic commands (`0xB0`--`0xB6`)
|
||||
- Incremental debug boot modes (wValue `0x80`--`0x85` for BOOT_8PSK)
|
||||
- I2C timeout protection (6000-iteration countdown vs. infinite spin)
|
||||
- I2C bus scan for device discovery
|
||||
- Spectrum sweep and blind scan capabilities
|
||||
- Raw BCM4500 register access
|
||||
|
||||
See the [Custom v3.01](/firmware/custom-v301/) page for full details.
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="v2.13 FW1">
|
||||
|
||||
### v2.13.01 (FW1) <Badge text="Stock" variant="note" />
|
||||
|
||||
The most feature-complete stock firmware, targeting the original I2C-connected SkyWalker-1 hardware.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Version ID | `0x020D01` |
|
||||
| Build date | 2010-03-12 |
|
||||
| Functions | 82-88 |
|
||||
| Binary size | 9,322 bytes |
|
||||
| Stack pointer | `0x50` |
|
||||
| Config byte IRAM | `0x4F` |
|
||||
| Descriptor base | `0x0E00` |
|
||||
| Init table address | `CODE:0B88` |
|
||||
| Vendor commands | 30 (`0x80`--`0x9D`) |
|
||||
| DiSEqC data pin | P0.0 |
|
||||
|
||||
**New features over v2.06:**
|
||||
- Three new vendor commands: `0x99` (GET_DEMOD_STATUS), `0x9A` (INIT_DEMOD), `0x9C` (DELAY_COMMAND)
|
||||
- INT0 repurposed for demodulator availability polling (40 attempts at addresses 0x7F and 0x3F)
|
||||
- USB re-enumeration moved to `FUN_CODE_2031` (called as normal function before main loop)
|
||||
- Demodulator signature verification (`FUN_CODE_1799`) with 20 retry attempts
|
||||
- Descriptor checksum verification (`FUN_CODE_1ca0`) with 20 retry attempts
|
||||
- Hardware revision detection via descriptor byte (flag `_1_3`)
|
||||
- Consolidated BCM4500 status polling (1 register instead of 3)
|
||||
- Anti-tampering string at firmware offset 0x1880
|
||||
|
||||
See the [FW2.13 Variants](/firmware/fw213-variants/) page for sub-variant comparison.
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="Rev.2 v2.10">
|
||||
|
||||
### Rev.2 v2.10.04 <Badge text="Transitional" variant="caution" />
|
||||
|
||||
Firmware for the Rev.2 hardware variant (PID `0x0202`). Architecturally sits between v2.06 and v2.13.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Version ID | `0x020A04` |
|
||||
| Build date | 2010-03-12 |
|
||||
| Functions | 107 (most of any version) |
|
||||
| Binary size | 8,843 bytes (smallest) |
|
||||
| Stack pointer | `0x4F` |
|
||||
| Config byte IRAM | `0x4E` |
|
||||
| Descriptor base | `0x0E00` |
|
||||
| Init table address | `CODE:0B48` |
|
||||
| Vendor commands | 27 (`0x80`--`0x9A`) |
|
||||
| DiSEqC data pin | P0.4 |
|
||||
|
||||
**Characteristics:**
|
||||
- Highest function count due to granular decomposition (10-30 byte helper functions)
|
||||
- Smallest binary despite having the most functions
|
||||
- Retained v2.06's INT0 USB re-enumeration behavior
|
||||
- Already adopted v2.13's descriptor base (`0x0E00`) and similar stack pointer
|
||||
- Contains `FUN_CODE_0800`: a massive 874-byte configuration dispatcher with embedded main loop
|
||||
- Lacks v2.13's demodulator polling, retry loops, and additional vendor commands (0x9B--0x9D out of range)
|
||||
- 0x99/0x9A present as prototype implementations
|
||||
|
||||
See the [Rev.2 Analysis](/firmware/rev2-analysis/) page for the complete 107-function inventory.
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="v2.06">
|
||||
|
||||
### v2.06.04 <Badge text="Stock" variant="note" />
|
||||
|
||||
The original SkyWalker-1 firmware extracted from the device's onboard EEPROM.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Version ID | `0x020604` |
|
||||
| Build date | 2007-07-13 |
|
||||
| Functions | 61 |
|
||||
| Binary size | 9,472 bytes |
|
||||
| Stack pointer | `0x72` |
|
||||
| Config byte IRAM | `0x6D` |
|
||||
| Descriptor base | `0x1200` |
|
||||
| Init table address | `CODE:0B46` |
|
||||
| Vendor commands | 30 (`0x80`--`0x9D`) |
|
||||
| DiSEqC data pin | P0.7 |
|
||||
|
||||
**Characteristics:**
|
||||
- Simplest firmware with the fewest functions
|
||||
- INT0 handler performs USB re-enumeration (CPUCS pulse)
|
||||
- No demodulator probe at boot
|
||||
- No retry loops or integrity verification
|
||||
- BCM4500 status polling reads 3 registers (0xA2, 0xA8, 0xA4) up to 6 times
|
||||
- Commands 0x99, 0x9A, 0x9C route to STALL
|
||||
- Command 0x9D reads descriptor byte and sets mode flag based on hardware revision (4, 5, or 6)
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Architectural Differences
|
||||
|
||||
| Feature | Custom v3.05 | Custom v3.01 | v2.13 | Rev.2 v2.10 | v2.06 |
|
||||
|---------|--------------|--------------|-------|-------------|-------|
|
||||
| Vendor commands | 30 stock + 18 custom | 30 stock + 7 custom | 30 | 27 | 30 |
|
||||
| INT0 handler | N/A (fx2lib ISR) | N/A (fx2lib ISR) | Demod polling | USB re-enum | USB re-enum |
|
||||
| Demod probe at boot | Yes (with timeout) | Yes (with timeout) | Yes (40 attempts) | No | No |
|
||||
| Retry loops | Yes (with timeout) | Yes (with timeout) | Yes (20-attempt) | No | No |
|
||||
| HW revision detect | No | No | Yes (flag `_1_3`) | Yes (descriptor walker) | No |
|
||||
| DiSEqC data pin | P0.7 | P0.7 | P0.0 | P0.4 | P0.7 |
|
||||
| Config byte IRAM addr | C variable | C variable | `0x4F` | `0x4E` | `0x6D` |
|
||||
| BCM4500 status poll | 1 register | 1 register | 1 register | 3 registers | 3 registers |
|
||||
| I2C timeout | All paths (6000-count) | Helpers only (6000-count) | None | None | None |
|
||||
| Software watchdog | Timer0 (2-second) | None | None | None | None |
|
||||
| EP0/GPIF/EP2 timeout | All protected | None | None | None | None |
|
||||
| Error codes | 14 | 6 | N/A | N/A | N/A |
|
||||
| Anti-tampering | No | No | Yes | No | No |
|
||||
| New commands | 0xB0--0xBE | 0xB0--0xB6 | 0x99, 0x9A, 0x9C | 0x99/0x9A proto | -- |
|
||||
|
||||
## Kernel Version Constants
|
||||
|
||||
The Linux kernel driver defines two firmware version thresholds in `gp8psk-fe.h`:
|
||||
|
||||
```c title="Kernel firmware version constants"
|
||||
GP8PSK_FW_REV1 = 0x020604 // v2.06.4
|
||||
GP8PSK_FW_REV2 = 0x020704 // v2.07.4
|
||||
```
|
||||
|
||||
If the firmware version reported by `GET_FW_VERS` (command `0x92`) is >= `GP8PSK_FW_REV2`, the kernel enables Rev.2-specific code paths. All v2.10 and v2.13 firmwares are newer than either constant.
|
||||
|
||||
<Aside type="caution" title="Version ID Gaps">
|
||||
`GP8PSK_FW_REV2` (`0x020704` = v2.07.04) is a **kernel threshold constant**, not an actual firmware release. No firmware with version 2.07.x was ever distributed. The real version progression is:
|
||||
|
||||
**v2.06.04** → **v2.10.04** → **v2.13.01**
|
||||
|
||||
Versions v2.07 through v2.09 and v2.11 through v2.12 do not exist. The kernel developers chose `0x020704` as a convenient threshold above the known v2.06.04 release.
|
||||
|
||||
All three v2.13 sub-variants (FW1, FW2, FW3) report the same version ID `0x020D01` despite targeting different hardware. See [FW2.13 Variants](/firmware/fw213-variants/) for how they differ.
|
||||
</Aside>
|
||||
|
||||
## Binary Similarity Matrix
|
||||
|
||||
Byte-level comparison across the shared code length (percentage of identical bytes):
|
||||
|
||||
| | v2.06 | v2.13.1 | v2.13.2 | v2.13.3 | Rev.2 |
|
||||
|---|---|---|---|---|---|
|
||||
| **v2.06** | -- | 4.8% | 4.3% | 4.3% | 6.0% |
|
||||
| **v2.13.1** | | -- | 57.2% | 59.4% | 8.0% |
|
||||
| **v2.13.2** | | | -- | 83.5% | 5.8% |
|
||||
| **v2.13.3** | | | | -- | 5.8% |
|
||||
| **Rev.2** | | | | | -- |
|
||||
|
||||
<Aside type="note">
|
||||
The extremely low similarity between major versions (4--8%) indicates complete recompilation with different linker configurations. Functions relocate to entirely different addresses even when the logic is identical. Only the vendor command dispatcher at `CODE:0056` maintains the same address across all versions.
|
||||
</Aside>
|
||||
|
||||
## Key Function Correspondence
|
||||
|
||||
Functions that serve the same role but reside at different addresses:
|
||||
|
||||
| Role | v2.06 | Rev.2 | v2.13 |
|
||||
|------|-------|-------|-------|
|
||||
| RESET vector / main | `0x188D` | `0x155F` | `0x170D` |
|
||||
| Main init + loop | `0x09A7` | `0x09A9` | `0x0800` |
|
||||
| USB descriptor setup | `0x13C3` | `0x10D9` | `0x11AB` |
|
||||
| Standard USB handler | `0x032A` | `0x0319` | `0x034E` |
|
||||
| Vendor cmd dispatch | `0x0056` | `0x0056` | `0x0056` |
|
||||
| Main loop poll | `0x2297` | -- | `0x21EC` |
|
||||
| GPIF/FIFO management | `0x1919` | `0x0D7C` | `0x1800` |
|
||||
| BCM4500 firmware loader | `0x0DDD` | `0x0C64` | `0x0CA4` |
|
||||
| BCM4500 status polling | `0x2000` | -- | `0x208D` |
|
||||
| Delay loop | `0x1DFB` | `0x1BDA` | `0x14B9` |
|
||||
|
||||
## INT0 Handler Evolution
|
||||
|
||||
The INT0 interrupt vector (`CODE:0003`) was repurposed between firmware generations:
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="v2.06 / Rev.2">
|
||||
|
||||
**USB Re-enumeration** -- pulses CPUCS bit 3 to trigger controlled USB disconnect/reconnect:
|
||||
|
||||
```c title="INT0 handler (v2.06 and Rev.2)"
|
||||
void INT0_vec(void) {
|
||||
if (flag == 0) CPUCS |= 0x08; // CPUCS bit 3
|
||||
else CPUCS |= 0x0A; // CPUCS bits 3+1
|
||||
delay(5, 0xDC); // ~1500 cycles
|
||||
EPIRQ = 0xFF; // Clear endpoint IRQs
|
||||
USBIRQ = 0xFF; // Clear USB IRQs
|
||||
EXIF &= 0xEF; // Clear external interrupt flag
|
||||
CPUCS &= 0xF7; // Clear CPUCS bit 3
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="v2.13">
|
||||
|
||||
**Demodulator Availability Polling** -- probes two I2C addresses up to 40 times:
|
||||
|
||||
```c title="INT0 handler (v2.13)"
|
||||
void INT0_vector(void) {
|
||||
for (counter = 0x28; counter != 0; counter--) {
|
||||
byte result = I2C_read(0x7F);
|
||||
if (result != 0x01) {
|
||||
result = I2C_read(0x3F);
|
||||
if (result != 0x01) break;
|
||||
}
|
||||
}
|
||||
no_demod_flag = (counter == 0);
|
||||
}
|
||||
```
|
||||
|
||||
The USB re-enumeration logic was moved to `FUN_CODE_2031` and called as a normal function before the main loop.
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## XRAM Initialization Table
|
||||
|
||||
All versions initialize FX2 peripheral registers from a CODE-space table at startup. The table format is identical: `[addr_hi] [addr_lo] [data_byte]` triplets terminated by `0x0000`.
|
||||
|
||||
| Firmware | Table Address | Key Registers Set |
|
||||
|----------|--------------|-------------------|
|
||||
| v2.06 | `CODE:0B46` | IFCONFIG, EP2CFG, EP2FIFOCFG, REVCTL, I2CTL |
|
||||
| Rev.2 | `CODE:0B48` | Same set, 2 bytes later |
|
||||
| v2.13 | `CODE:0B88` | Same set, different offsets |
|
||||
|
||||
All versions set the same final values: `IFCONFIG=0xEE`, `EP2CFG=0xE2`, `EP2FIFOCFG=0x0C`, `REVCTL=0x03`, `I2CTL=0x01`.
|
||||
|
||||
## Anti-Tampering (v2.13 Only)
|
||||
|
||||
All v2.13 sub-variants contain this string at firmware offset `0x1880`:
|
||||
|
||||
```
|
||||
"Tampering is detected. Attempt is logged. Warranty is voided ! \n"
|
||||
```
|
||||
|
||||
This is followed by I2C register write commands (`01 10 aa 82 02 41 41 83`). The mechanism is absent from v2.06, Rev.2, and the custom firmware.
|
||||
|
||||
## Version Identification
|
||||
|
||||
The `GET_FW_VERS` command (`0x92`) returns 6 bytes of hardcoded constants:
|
||||
|
||||
```
|
||||
Byte 0: version minor_minor (e.g., 0x04)
|
||||
Byte 1: version minor (e.g., 0x06)
|
||||
Byte 2: version major (e.g., 0x02)
|
||||
Byte 3: build day (e.g., 0x0D = 13)
|
||||
Byte 4: build month (e.g., 0x07 = July)
|
||||
Byte 5: build year - 2000 (e.g., 0x07 = 2007)
|
||||
```
|
||||
|
||||
Full version = `byte[2] << 16 | byte[1] << 8 | byte[0]`. Build date = `(2000 + byte[5]) / byte[4] / byte[3]`.
|
||||
|
|
|
|||
|
|
@ -1,322 +1,322 @@
|
|||
---
|
||||
title: Applications & Use Cases
|
||||
description: What can the SkyWalker-1 actually do? Satellite TV, multi-standard signal analysis, radio astronomy, RF measurement, and more.
|
||||
---
|
||||
|
||||
import { Aside, Badge, Card, CardGrid, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
The SkyWalker-1 shipped as a DVB-S satellite TV receiver. With [custom firmware](/firmware/custom-v305/) and
|
||||
the reverse-engineered USB/I2C interface, it becomes something more interesting: a programmable RF instrument
|
||||
covering 950-2150 MHz with ten demodulation modes, 256 Ksps to 30 Msps symbol rates, and full Python control.
|
||||
|
||||
Here's what you can actually do with it.
|
||||
|
||||
## Satellite TV Reception
|
||||
|
||||
The obvious one. The SkyWalker-1 receives free-to-air (FTA) DVB-S content — unencrypted satellite television
|
||||
and radio that anyone with a dish can watch.
|
||||
|
||||
### What's Up There
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Ku-Band">
|
||||
Most FTA content in North America lives on Ku-band satellites. A standard 30-36 inch dish and
|
||||
universal LNB is all you need.
|
||||
|
||||
| Satellite | Position | What's On It |
|
||||
|-----------|----------|-------------|
|
||||
| Galaxy 19 | 97.0°W | The FTA motherlode. ~135+ channels: Chinese, Korean, South Asian, religious, shopping, some English |
|
||||
| Galaxy 16 | 99.0°W | Religious programming, international |
|
||||
| SES-2 | 87.0°W | International, government |
|
||||
| AMC-18 | 105.0°W | Mixed FTA and encrypted |
|
||||
|
||||
Typical tuning parameters: 11836 MHz V-pol, 20770 ksps, DVB-S QPSK FEC 3/4.
|
||||
</TabItem>
|
||||
<TabItem label="C-Band">
|
||||
C-band requires a larger dish (6-12 feet) and a C-band LNB, but carries content that never made it
|
||||
to Ku-band — including DigiCipher II muxes that the SkyWalker-1 uniquely supports.
|
||||
|
||||
| Satellite | Position | What's On It |
|
||||
|-----------|----------|-------------|
|
||||
| AMC-18 | 105.0°W | DCII cable distribution, some FTA |
|
||||
| SES-2 | 87.0°W | International, government feeds |
|
||||
| Galaxy 16 | 99.0°W | Mixed distribution |
|
||||
|
||||
The FCC C-band transition compressed services into the upper 3.98-4.2 GHz range. Additional
|
||||
spectrum auctions are proposed for 2027 — C-band FTA is on borrowed time.
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### FTA Resources
|
||||
|
||||
Current channel listings change frequently. These sites track what's active:
|
||||
|
||||
- [LyngSat](https://www.lyngsat.com/) — comprehensive transponder and channel database
|
||||
- [SatExpat](https://www.satexpat.com/) — FTA channel listings with satellite footprints
|
||||
- [FTAList](https://ftalist.com/) — North American FTA community and channel guide
|
||||
|
||||
<Aside type="caution" title="DVB-S2 is not supported">
|
||||
An increasing percentage of satellite content uses **DVB-S2**, which relies on LDPC forward error correction
|
||||
instead of the Reed-Solomon/Viterbi scheme the BCM4500 implements. The SkyWalker-1 can detect DVB-S2
|
||||
carriers as RF energy (they show up in spectrum sweeps), but it cannot demodulate or decode them.
|
||||
|
||||
If a transponder listing says "DVB-S2" or "8PSK" (in the DVB-S2 sense, not Turbo 8PSK), it won't work.
|
||||
</Aside>
|
||||
|
||||
## Multi-Standard Signal Analysis
|
||||
|
||||
This is where the SkyWalker-1 becomes genuinely rare hardware. The BCM4500 demodulates standards that are
|
||||
nearly extinct in available consumer equipment — standards that are still actively broadcasting.
|
||||
|
||||
<CardGrid>
|
||||
<Card title="DigiCipher II" icon="rocket">
|
||||
Cable headend distribution format (Comcast HITS, Motorola). One of very few modern devices with DCII
|
||||
support. "Zero Key" unencrypted services are directly receivable.
|
||||
</Card>
|
||||
<Card title="DSS" icon="star">
|
||||
Digital Satellite Service — legacy DirecTV format with 127-byte transport packets (vs 188-byte DVB).
|
||||
Extraordinarily rare outside DirecTV hardware.
|
||||
</Card>
|
||||
<Card title="Turbo 8PSK" icon="setting">
|
||||
DISH Network transponder format. Encrypted content, but demodulator lock and transport stream capture
|
||||
work — useful for signal analysis and protocol research.
|
||||
</Card>
|
||||
<Card title="Turbo QPSK" icon="open-book">
|
||||
Earlier turbo-coded variant. Better spectral efficiency than standard DVB-S QPSK, still used on
|
||||
some distribution paths.
|
||||
</Card>
|
||||
</CardGrid>
|
||||
|
||||
### Why This Matters
|
||||
|
||||
These standards are still active on-air, but the hardware to receive them is disappearing. Off-the-shelf
|
||||
satellite receivers dropped DCII and DSS support years ago. The SkyWalker-1, through its BCM4500 demodulator,
|
||||
retains these capabilities — making it a **preservation and research tool** for signal formats that will
|
||||
eventually go silent.
|
||||
|
||||
The [TS Analyzer](/tools/ts-analyzer/) can parse transport streams from all supported modulation types,
|
||||
making it possible to compare DVB-S, DCII, and DSS packet structures side by side.
|
||||
|
||||
See [BCM4500 Demodulator](/bcm4500/demodulator/) for register-level details on how each modulation type
|
||||
is configured.
|
||||
|
||||
## Wild Feed & Backhaul Hunting
|
||||
|
||||
Satellite transponders carry more than scheduled programming. Temporary unencrypted uplinks — "wild feeds" —
|
||||
appear and disappear throughout the day:
|
||||
|
||||
- **Live news remotes**: Raw camera feeds from field reporters, unedited and uncensored
|
||||
- **Sports backhauls**: Stadium camera feeds before production mixing
|
||||
- **Network distribution**: Programs fed to affiliates before air time
|
||||
- **Event coverage**: Press conferences, hearings, launches
|
||||
|
||||
The SkyWalker-1's blind scan capability and wide symbol rate range (256 Ksps - 30 Msps) make it well-suited
|
||||
for finding these transient signals. The [Carrier Survey](/tools/survey/) tool automates the sweep-and-lock
|
||||
cycle across a full satellite.
|
||||
|
||||
<Aside type="tip" title="Community resource">
|
||||
[Rick's Satellite Wildfeed Forum](https://www.satelliteguys.us/xen/forums/wild-feeds.42/) on SatelliteGuys
|
||||
is the primary community hub for reporting and tracking wild feeds on North American satellites.
|
||||
</Aside>
|
||||
|
||||
## Radio Astronomy
|
||||
|
||||
The 950-2150 MHz IF range — or, without an LNB, the direct input range — overlaps with several
|
||||
astrophysically interesting frequencies. The BCM4500's AGC registers respond to any RF energy at the
|
||||
tuned frequency, regardless of whether it carries a demodulatable signal.
|
||||
|
||||
### Hydrogen 21 cm
|
||||
|
||||
<Badge text="tools/h21cm.py" variant="note" />
|
||||
|
||||
Neutral hydrogen emits at **1420.405 MHz** — directly in the IF range with no LNB. Connect an L-band
|
||||
antenna (patch, helical, or horn) to the F-connector and the SkyWalker-1 becomes a hydrogen line
|
||||
radiometer. The velocity-dispersed emission from the Milky Way's spiral arms is detectable even
|
||||
with the BCM4500's ~346 kHz resolution bandwidth.
|
||||
|
||||
See [Hydrogen 21 cm Radiometer](/tools/h21cm/) for the full tool reference.
|
||||
|
||||
### Ku-Band Solar Observation
|
||||
|
||||
Point a standard satellite dish + LNB at the Sun. At 10-12 GHz, solar thermal emission produces a
|
||||
detectable **6+ dB rise** above the cold-sky background. Solar flares produce wideband bursts that
|
||||
are even more dramatic.
|
||||
|
||||
The SkyWalker-1's advantage over an RTL-SDR here is bandwidth: the 30 Msps sweep capability covers
|
||||
a much wider swath of spectrum (~30 MHz effective) compared to the RTL-SDR's ~2.4 MHz, making it
|
||||
easier to detect and characterize broadband solar events.
|
||||
|
||||
Use the [Spectrum Analysis](/tools/spectrum-analysis/) sweep mode to build solar emission profiles.
|
||||
|
||||
### Moon Thermal Emission
|
||||
|
||||
The Moon is a calibrated thermal source at microwave frequencies. Measuring its emission relative to
|
||||
cold sky provides a reference point for system noise temperature estimation — a standard radio
|
||||
astronomy calibration technique.
|
||||
|
||||
## RF Test & Measurement
|
||||
|
||||
The custom firmware turns the SkyWalker-1 into a basic but useful L-band test instrument.
|
||||
|
||||
### L-Band Spectrum Analyzer
|
||||
|
||||
<Badge text="tools/skywalker.py spectrum" variant="note" />
|
||||
|
||||
Sweep 950-2150 MHz in configurable steps, recording AGC power at each frequency. Not calibrated
|
||||
to absolute dBm, but relative measurements are consistent enough for transponder identification,
|
||||
interference detection, and comparative analysis.
|
||||
|
||||
See [Spectrum Analysis](/tools/spectrum-analysis/) for sweep techniques and interpretation.
|
||||
|
||||
### CW Injection Test Bench
|
||||
|
||||
<Badge text="tools/rf_testbench.py" variant="note" />
|
||||
|
||||
Connect a NanoVNA as a CW source through an [HMC472A digital attenuator](https://hmc472.l.zmesh.systems/)
|
||||
to the SkyWalker-1's F-connector. The `rf_testbench.py` tool automates five test sequences:
|
||||
AGC linearity mapping, IF band flatness, frequency accuracy, minimum detectable signal, and
|
||||
BPSK mode 9 probing. The HMC472A provides 0-31.5 dB of programmable attenuation in 0.5 dB
|
||||
steps via its REST API, giving precision level control without swapping fixed pads.
|
||||
|
||||
See [RF Test Bench](/tools/rf-testbench/) for hardware setup, calibration, and test descriptions.
|
||||
|
||||
### LNB Characterization
|
||||
|
||||
Measure gain flatness across the IF band by sweeping a known satellite's transponder plan and
|
||||
comparing received power levels. Track LO drift over temperature by monitoring a stable carrier's
|
||||
frequency offset over 24 hours with the [Beacon Logger](/tools/beacon-logger/).
|
||||
|
||||
The I2C-exposed tuner and demodulator registers make internal signal chain parameters directly
|
||||
readable — something most consumer receivers hide completely.
|
||||
|
||||
### Transponder Fingerprinting
|
||||
|
||||
Each satellite transponder has unique RF characteristics: center frequency, symbol rate, rolloff,
|
||||
power level, modulation type. The [Carrier Survey](/tools/survey/) tool builds a catalog of these
|
||||
parameters. Over time, this creates a fingerprint database useful for satellite identification
|
||||
and change detection.
|
||||
|
||||
### 5G Interference Monitoring
|
||||
|
||||
The FCC's C-band auction reallocated 3.7-3.98 GHz to 5G operators. Spillover from 5G base stations
|
||||
into the satellite C-band (3.98-4.2 GHz) is an increasing concern for satellite operators and
|
||||
earth station licensees. With a C-band LNB, the SkyWalker-1 can sweep the IF band and detect
|
||||
interference signatures.
|
||||
|
||||
## Propagation Science & Weather
|
||||
|
||||
Long-duration signal monitoring produces datasets that map directly to atmospheric physics.
|
||||
|
||||
### Rain Fade Analysis
|
||||
|
||||
<Badge text="tools/beacon_logger.py" variant="note" />
|
||||
|
||||
Lock onto a stable Ku-band transponder and log SNR at 1 Hz for days or weeks. Ku-band signals
|
||||
attenuate predictably in rain — the ITU-R P.618 model describes the relationship between rainfall
|
||||
rate and attenuation at specific frequencies. Real measurement data validates (or challenges)
|
||||
these models for your specific location and dish geometry.
|
||||
|
||||
### Diurnal Thermal Effects
|
||||
|
||||
LNB gain varies with temperature. A 24-hour beacon log correlated with ambient temperature data
|
||||
reveals the thermal gain coefficient of your specific LNB — useful for separating real propagation
|
||||
events from equipment drift.
|
||||
|
||||
### Link Budget Validation
|
||||
|
||||
Compare long-term average received signal levels against calculated link budgets (EIRP, free space
|
||||
loss, atmospheric absorption, antenna gain, system noise temperature). The gap between prediction
|
||||
and measurement is where engineering meets reality.
|
||||
|
||||
See [Beacon Logger](/tools/beacon-logger/) for unattended multi-day logging with auto-relock.
|
||||
|
||||
## Education & Research
|
||||
|
||||
The SkyWalker-1 exposes the complete satellite signal chain from RF input to MPEG-2 transport stream
|
||||
output, with every intermediate stage accessible over I2C.
|
||||
|
||||
### University Lab Platform
|
||||
|
||||
A single SkyWalker-1 + dish + LNB covers a semester of satellite communications topics with
|
||||
live signals:
|
||||
|
||||
| Topic | What's Observable |
|
||||
|-------|------------------|
|
||||
| QPSK/8PSK demodulation | Lock status, constellation quality via SNR |
|
||||
| Forward error correction | Viterbi, Reed-Solomon, Turbo code — switchable by modulation type |
|
||||
| Link budgets | Real measurements vs. theoretical calculations |
|
||||
| MPEG-2 transport streams | Live PSI/SI table parsing, PID analysis |
|
||||
| Spectrum analysis | Transponder identification from raw power sweeps |
|
||||
| Antenna pointing | Signal strength vs. azimuth/elevation in real time |
|
||||
|
||||
### Transport Stream Protocol Research
|
||||
|
||||
The SkyWalker-1's multi-standard support makes it uniquely suited for comparative protocol analysis:
|
||||
|
||||
- **DVB-S**: 188-byte MPEG-2 TS packets, standard PID structure
|
||||
- **DigiCipher II**: Motorola proprietary transport, conditional access
|
||||
- **DSS**: 127-byte packets — shorter than DVB, different header format
|
||||
|
||||
Tools like [TSDuck](https://tsduck.io/) and dvbsnoop can parse captured streams. The [TS Analyzer](/tools/ts-analyzer/)
|
||||
handles the initial capture and PSI extraction.
|
||||
|
||||
### Accessible Signal Chain
|
||||
|
||||
The I2C bus provides direct read access to tuner, demodulator, and FEC status registers. Students can
|
||||
observe the AGC settling, watch the demodulator acquire lock, and read error correction statistics —
|
||||
the internal workings of the signal chain, visible in real time. See [I2C Bus Architecture](/i2c/bus-architecture/)
|
||||
and [Signal Monitoring](/bcm4500/signal-monitoring/) for register details.
|
||||
|
||||
## What's NOT Compatible
|
||||
|
||||
Setting honest expectations is more valuable than overselling.
|
||||
|
||||
<Aside type="danger" title="Hardware limitations">
|
||||
The following are common requests that the SkyWalker-1 **cannot** fulfill. Understanding these
|
||||
boundaries prevents wasted time and money on incompatible setups.
|
||||
</Aside>
|
||||
|
||||
| Signal / Application | Why Not |
|
||||
|---------------------|---------|
|
||||
| **DVB-S2** | Incompatible FEC — uses LDPC instead of Reed-Solomon/Viterbi. This is a growing percentage of satellite content. |
|
||||
| **GOES weather satellite imagery** | LRIT uses BPSK/CCSDS (not DVB-S), GRB uses DVB-S2. Cannot decode imagery. However, the BCM4500's BPSK mode 9 uses the same inner FEC (Viterbi rate 1/2 K=7) as LRIT — the signal chain gets four stages deep before breaking at RS decoder block size and CCSDS framing. The LRIT carrier at 1694.1 MHz is within the [direct input range](/tools/h21cm/#antenna-setup) and can be used for antenna alignment and propagation monitoring. See [RF Test Bench](/tools/rf-testbench/) for BPSK mode 9 probing. |
|
||||
| **QO-100 from North America** | Es'hail-2 is at 25.9°E — visible from Europe, Africa, and the Middle East, but not North America. See [QO-100 DATV Reception](/guides/qo100-datv/) for coverage details. |
|
||||
| **Military/government feeds** | Encrypted and increasingly DVB-S2 or proprietary modulation. |
|
||||
| **ATSC / DVB-T terrestrial** | Completely different modulation family (OFDM), different frequency band. |
|
||||
| **Analog satellite TV** | The BCM4500 is a digital demodulator. Analog satellite is also effectively extinct. |
|
||||
|
||||
## Modulation Support Reference
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="By Standard">
|
||||
| Modulation | Standard | Typical Use | FTA Content? |
|
||||
|-----------|----------|-------------|-------------|
|
||||
| DVB-S QPSK | DVB-S EN 300 421 | Free-to-air satellite TV worldwide | Yes — most FTA content |
|
||||
| Turbo QPSK | Proprietary (Comstream) | Distribution, some DISH | Rare |
|
||||
| Turbo 8PSK | Proprietary | DISH Network | No — encrypted |
|
||||
| DCII Combo | Motorola DigiCipher II | Cable headend distribution | Some ("Zero Key") |
|
||||
| DCII Split I | Motorola DigiCipher II | Cable headend distribution | Some |
|
||||
| DCII Split Q | Motorola DigiCipher II | Cable headend distribution | Some |
|
||||
| DCII Offset QPSK | Motorola DigiCipher II | Cable headend distribution | Some |
|
||||
| DSS QPSK | Hughes DSS | Legacy DirecTV | No — service winding down |
|
||||
</TabItem>
|
||||
<TabItem label="By Receiver Use">
|
||||
| What You Want to Do | Modulation to Select | Symbol Rate Range |
|
||||
|--------------------|--------------------|------------------|
|
||||
| Watch FTA satellite TV | DVB-S QPSK | 2-30 Msps |
|
||||
| Analyze DISH Network signals | Turbo 8PSK | 20-30 Msps |
|
||||
| Receive DCII cable distribution | DCII Combo/Split/Offset | 2-30 Msps |
|
||||
| Study DSS transport format | DSS QPSK | 20 Msps typical |
|
||||
| Hydrogen 21 cm (no LNB) | N/A — AGC power only | Any (for carrier lock attempt) |
|
||||
| Spectrum sweep / signal detection | N/A — AGC power only | Set during tune, not critical |
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## See Also
|
||||
|
||||
- [RF Specifications](/hardware/rf-specifications/) — frequency range, symbol rate limits, LNB power
|
||||
- [BCM4500 Demodulator](/bcm4500/demodulator/) — register-level modulation configuration
|
||||
- [Spectrum Analysis](/tools/spectrum-analysis/) — sweep techniques and transponder scanning
|
||||
- [RF Test Bench](/tools/rf-testbench/) — CW injection testing with NanoVNA + HMC472A
|
||||
- [Experimenter's Roadmap](/guides/experimenter-roadmap/) — future experiment tiers and creative applications
|
||||
- [MCP Server](/tools/mcp-server/) — programmatic access to all hardware functions
|
||||
---
|
||||
title: Applications & Use Cases
|
||||
description: What can the SkyWalker-1 actually do? Satellite TV, multi-standard signal analysis, radio astronomy, RF measurement, and more.
|
||||
---
|
||||
|
||||
import { Aside, Badge, Card, CardGrid, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
The SkyWalker-1 shipped as a DVB-S satellite TV receiver. With [custom firmware](/firmware/custom-v305/) and
|
||||
the reverse-engineered USB/I2C interface, it becomes something more interesting: a programmable RF instrument
|
||||
covering 950-2150 MHz with ten demodulation modes, 256 Ksps to 30 Msps symbol rates, and full Python control.
|
||||
|
||||
Here's what you can actually do with it.
|
||||
|
||||
## Satellite TV Reception
|
||||
|
||||
The obvious one. The SkyWalker-1 receives free-to-air (FTA) DVB-S content — unencrypted satellite television
|
||||
and radio that anyone with a dish can watch.
|
||||
|
||||
### What's Up There
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Ku-Band">
|
||||
Most FTA content in North America lives on Ku-band satellites. A standard 30-36 inch dish and
|
||||
universal LNB is all you need.
|
||||
|
||||
| Satellite | Position | What's On It |
|
||||
|-----------|----------|-------------|
|
||||
| Galaxy 19 | 97.0°W | The FTA motherlode. ~135+ channels: Chinese, Korean, South Asian, religious, shopping, some English |
|
||||
| Galaxy 16 | 99.0°W | Religious programming, international |
|
||||
| SES-2 | 87.0°W | International, government |
|
||||
| AMC-18 | 105.0°W | Mixed FTA and encrypted |
|
||||
|
||||
Typical tuning parameters: 11836 MHz V-pol, 20770 ksps, DVB-S QPSK FEC 3/4.
|
||||
</TabItem>
|
||||
<TabItem label="C-Band">
|
||||
C-band requires a larger dish (6-12 feet) and a C-band LNB, but carries content that never made it
|
||||
to Ku-band — including DigiCipher II muxes that the SkyWalker-1 uniquely supports.
|
||||
|
||||
| Satellite | Position | What's On It |
|
||||
|-----------|----------|-------------|
|
||||
| AMC-18 | 105.0°W | DCII cable distribution, some FTA |
|
||||
| SES-2 | 87.0°W | International, government feeds |
|
||||
| Galaxy 16 | 99.0°W | Mixed distribution |
|
||||
|
||||
The FCC C-band transition compressed services into the upper 3.98-4.2 GHz range. Additional
|
||||
spectrum auctions are proposed for 2027 — C-band FTA is on borrowed time.
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### FTA Resources
|
||||
|
||||
Current channel listings change frequently. These sites track what's active:
|
||||
|
||||
- [LyngSat](https://www.lyngsat.com/) — comprehensive transponder and channel database
|
||||
- [SatExpat](https://www.satexpat.com/) — FTA channel listings with satellite footprints
|
||||
- [FTAList](https://ftalist.com/) — North American FTA community and channel guide
|
||||
|
||||
<Aside type="caution" title="DVB-S2 is not supported">
|
||||
An increasing percentage of satellite content uses **DVB-S2**, which relies on LDPC forward error correction
|
||||
instead of the Reed-Solomon/Viterbi scheme the BCM4500 implements. The SkyWalker-1 can detect DVB-S2
|
||||
carriers as RF energy (they show up in spectrum sweeps), but it cannot demodulate or decode them.
|
||||
|
||||
If a transponder listing says "DVB-S2" or "8PSK" (in the DVB-S2 sense, not Turbo 8PSK), it won't work.
|
||||
</Aside>
|
||||
|
||||
## Multi-Standard Signal Analysis
|
||||
|
||||
This is where the SkyWalker-1 becomes genuinely rare hardware. The BCM4500 demodulates standards that are
|
||||
nearly extinct in available consumer equipment — standards that are still actively broadcasting.
|
||||
|
||||
<CardGrid>
|
||||
<Card title="DigiCipher II" icon="rocket">
|
||||
Cable headend distribution format (Comcast HITS, Motorola). One of very few modern devices with DCII
|
||||
support. "Zero Key" unencrypted services are directly receivable.
|
||||
</Card>
|
||||
<Card title="DSS" icon="star">
|
||||
Digital Satellite Service — legacy DirecTV format with 127-byte transport packets (vs 188-byte DVB).
|
||||
Extraordinarily rare outside DirecTV hardware.
|
||||
</Card>
|
||||
<Card title="Turbo 8PSK" icon="setting">
|
||||
DISH Network transponder format. Encrypted content, but demodulator lock and transport stream capture
|
||||
work — useful for signal analysis and protocol research.
|
||||
</Card>
|
||||
<Card title="Turbo QPSK" icon="open-book">
|
||||
Earlier turbo-coded variant. Better spectral efficiency than standard DVB-S QPSK, still used on
|
||||
some distribution paths.
|
||||
</Card>
|
||||
</CardGrid>
|
||||
|
||||
### Why This Matters
|
||||
|
||||
These standards are still active on-air, but the hardware to receive them is disappearing. Off-the-shelf
|
||||
satellite receivers dropped DCII and DSS support years ago. The SkyWalker-1, through its BCM4500 demodulator,
|
||||
retains these capabilities — making it a **preservation and research tool** for signal formats that will
|
||||
eventually go silent.
|
||||
|
||||
The [TS Analyzer](/tools/ts-analyzer/) can parse transport streams from all supported modulation types,
|
||||
making it possible to compare DVB-S, DCII, and DSS packet structures side by side.
|
||||
|
||||
See [BCM4500 Demodulator](/bcm4500/demodulator/) for register-level details on how each modulation type
|
||||
is configured.
|
||||
|
||||
## Wild Feed & Backhaul Hunting
|
||||
|
||||
Satellite transponders carry more than scheduled programming. Temporary unencrypted uplinks — "wild feeds" —
|
||||
appear and disappear throughout the day:
|
||||
|
||||
- **Live news remotes**: Raw camera feeds from field reporters, unedited and uncensored
|
||||
- **Sports backhauls**: Stadium camera feeds before production mixing
|
||||
- **Network distribution**: Programs fed to affiliates before air time
|
||||
- **Event coverage**: Press conferences, hearings, launches
|
||||
|
||||
The SkyWalker-1's blind scan capability and wide symbol rate range (256 Ksps - 30 Msps) make it well-suited
|
||||
for finding these transient signals. The [Carrier Survey](/tools/survey/) tool automates the sweep-and-lock
|
||||
cycle across a full satellite.
|
||||
|
||||
<Aside type="tip" title="Community resource">
|
||||
[Rick's Satellite Wildfeed Forum](https://www.satelliteguys.us/xen/forums/wild-feeds.42/) on SatelliteGuys
|
||||
is the primary community hub for reporting and tracking wild feeds on North American satellites.
|
||||
</Aside>
|
||||
|
||||
## Radio Astronomy
|
||||
|
||||
The 950-2150 MHz IF range — or, without an LNB, the direct input range — overlaps with several
|
||||
astrophysically interesting frequencies. The BCM4500's AGC registers respond to any RF energy at the
|
||||
tuned frequency, regardless of whether it carries a demodulatable signal.
|
||||
|
||||
### Hydrogen 21 cm
|
||||
|
||||
<Badge text="tools/h21cm.py" variant="note" />
|
||||
|
||||
Neutral hydrogen emits at **1420.405 MHz** — directly in the IF range with no LNB. Connect an L-band
|
||||
antenna (patch, helical, or horn) to the F-connector and the SkyWalker-1 becomes a hydrogen line
|
||||
radiometer. The velocity-dispersed emission from the Milky Way's spiral arms is detectable even
|
||||
with the BCM4500's ~346 kHz resolution bandwidth.
|
||||
|
||||
See [Hydrogen 21 cm Radiometer](/tools/h21cm/) for the full tool reference.
|
||||
|
||||
### Ku-Band Solar Observation
|
||||
|
||||
Point a standard satellite dish + LNB at the Sun. At 10-12 GHz, solar thermal emission produces a
|
||||
detectable **6+ dB rise** above the cold-sky background. Solar flares produce wideband bursts that
|
||||
are even more dramatic.
|
||||
|
||||
The SkyWalker-1's advantage over an RTL-SDR here is bandwidth: the 30 Msps sweep capability covers
|
||||
a much wider swath of spectrum (~30 MHz effective) compared to the RTL-SDR's ~2.4 MHz, making it
|
||||
easier to detect and characterize broadband solar events.
|
||||
|
||||
Use the [Spectrum Analysis](/tools/spectrum-analysis/) sweep mode to build solar emission profiles.
|
||||
|
||||
### Moon Thermal Emission
|
||||
|
||||
The Moon is a calibrated thermal source at microwave frequencies. Measuring its emission relative to
|
||||
cold sky provides a reference point for system noise temperature estimation — a standard radio
|
||||
astronomy calibration technique.
|
||||
|
||||
## RF Test & Measurement
|
||||
|
||||
The custom firmware turns the SkyWalker-1 into a basic but useful L-band test instrument.
|
||||
|
||||
### L-Band Spectrum Analyzer
|
||||
|
||||
<Badge text="tools/skywalker.py spectrum" variant="note" />
|
||||
|
||||
Sweep 950-2150 MHz in configurable steps, recording AGC power at each frequency. Not calibrated
|
||||
to absolute dBm, but relative measurements are consistent enough for transponder identification,
|
||||
interference detection, and comparative analysis.
|
||||
|
||||
See [Spectrum Analysis](/tools/spectrum-analysis/) for sweep techniques and interpretation.
|
||||
|
||||
### CW Injection Test Bench
|
||||
|
||||
<Badge text="tools/rf_testbench.py" variant="note" />
|
||||
|
||||
Connect a NanoVNA as a CW source through an [HMC472A digital attenuator](https://hmc472.l.zmesh.systems/)
|
||||
to the SkyWalker-1's F-connector. The `rf_testbench.py` tool automates five test sequences:
|
||||
AGC linearity mapping, IF band flatness, frequency accuracy, minimum detectable signal, and
|
||||
BPSK mode 9 probing. The HMC472A provides 0-31.5 dB of programmable attenuation in 0.5 dB
|
||||
steps via its REST API, giving precision level control without swapping fixed pads.
|
||||
|
||||
See [RF Test Bench](/tools/rf-testbench/) for hardware setup, calibration, and test descriptions.
|
||||
|
||||
### LNB Characterization
|
||||
|
||||
Measure gain flatness across the IF band by sweeping a known satellite's transponder plan and
|
||||
comparing received power levels. Track LO drift over temperature by monitoring a stable carrier's
|
||||
frequency offset over 24 hours with the [Beacon Logger](/tools/beacon-logger/).
|
||||
|
||||
The I2C-exposed tuner and demodulator registers make internal signal chain parameters directly
|
||||
readable — something most consumer receivers hide completely.
|
||||
|
||||
### Transponder Fingerprinting
|
||||
|
||||
Each satellite transponder has unique RF characteristics: center frequency, symbol rate, rolloff,
|
||||
power level, modulation type. The [Carrier Survey](/tools/survey/) tool builds a catalog of these
|
||||
parameters. Over time, this creates a fingerprint database useful for satellite identification
|
||||
and change detection.
|
||||
|
||||
### 5G Interference Monitoring
|
||||
|
||||
The FCC's C-band auction reallocated 3.7-3.98 GHz to 5G operators. Spillover from 5G base stations
|
||||
into the satellite C-band (3.98-4.2 GHz) is an increasing concern for satellite operators and
|
||||
earth station licensees. With a C-band LNB, the SkyWalker-1 can sweep the IF band and detect
|
||||
interference signatures.
|
||||
|
||||
## Propagation Science & Weather
|
||||
|
||||
Long-duration signal monitoring produces datasets that map directly to atmospheric physics.
|
||||
|
||||
### Rain Fade Analysis
|
||||
|
||||
<Badge text="tools/beacon_logger.py" variant="note" />
|
||||
|
||||
Lock onto a stable Ku-band transponder and log SNR at 1 Hz for days or weeks. Ku-band signals
|
||||
attenuate predictably in rain — the ITU-R P.618 model describes the relationship between rainfall
|
||||
rate and attenuation at specific frequencies. Real measurement data validates (or challenges)
|
||||
these models for your specific location and dish geometry.
|
||||
|
||||
### Diurnal Thermal Effects
|
||||
|
||||
LNB gain varies with temperature. A 24-hour beacon log correlated with ambient temperature data
|
||||
reveals the thermal gain coefficient of your specific LNB — useful for separating real propagation
|
||||
events from equipment drift.
|
||||
|
||||
### Link Budget Validation
|
||||
|
||||
Compare long-term average received signal levels against calculated link budgets (EIRP, free space
|
||||
loss, atmospheric absorption, antenna gain, system noise temperature). The gap between prediction
|
||||
and measurement is where engineering meets reality.
|
||||
|
||||
See [Beacon Logger](/tools/beacon-logger/) for unattended multi-day logging with auto-relock.
|
||||
|
||||
## Education & Research
|
||||
|
||||
The SkyWalker-1 exposes the complete satellite signal chain from RF input to MPEG-2 transport stream
|
||||
output, with every intermediate stage accessible over I2C.
|
||||
|
||||
### University Lab Platform
|
||||
|
||||
A single SkyWalker-1 + dish + LNB covers a semester of satellite communications topics with
|
||||
live signals:
|
||||
|
||||
| Topic | What's Observable |
|
||||
|-------|------------------|
|
||||
| QPSK/8PSK demodulation | Lock status, constellation quality via SNR |
|
||||
| Forward error correction | Viterbi, Reed-Solomon, Turbo code — switchable by modulation type |
|
||||
| Link budgets | Real measurements vs. theoretical calculations |
|
||||
| MPEG-2 transport streams | Live PSI/SI table parsing, PID analysis |
|
||||
| Spectrum analysis | Transponder identification from raw power sweeps |
|
||||
| Antenna pointing | Signal strength vs. azimuth/elevation in real time |
|
||||
|
||||
### Transport Stream Protocol Research
|
||||
|
||||
The SkyWalker-1's multi-standard support makes it uniquely suited for comparative protocol analysis:
|
||||
|
||||
- **DVB-S**: 188-byte MPEG-2 TS packets, standard PID structure
|
||||
- **DigiCipher II**: Motorola proprietary transport, conditional access
|
||||
- **DSS**: 127-byte packets — shorter than DVB, different header format
|
||||
|
||||
Tools like [TSDuck](https://tsduck.io/) and dvbsnoop can parse captured streams. The [TS Analyzer](/tools/ts-analyzer/)
|
||||
handles the initial capture and PSI extraction.
|
||||
|
||||
### Accessible Signal Chain
|
||||
|
||||
The I2C bus provides direct read access to tuner, demodulator, and FEC status registers. Students can
|
||||
observe the AGC settling, watch the demodulator acquire lock, and read error correction statistics —
|
||||
the internal workings of the signal chain, visible in real time. See [I2C Bus Architecture](/i2c/bus-architecture/)
|
||||
and [Signal Monitoring](/bcm4500/signal-monitoring/) for register details.
|
||||
|
||||
## What's NOT Compatible
|
||||
|
||||
Setting honest expectations is more valuable than overselling.
|
||||
|
||||
<Aside type="danger" title="Hardware limitations">
|
||||
The following are common requests that the SkyWalker-1 **cannot** fulfill. Understanding these
|
||||
boundaries prevents wasted time and money on incompatible setups.
|
||||
</Aside>
|
||||
|
||||
| Signal / Application | Why Not |
|
||||
|---------------------|---------|
|
||||
| **DVB-S2** | Incompatible FEC — uses LDPC instead of Reed-Solomon/Viterbi. This is a growing percentage of satellite content. |
|
||||
| **GOES weather satellite imagery** | LRIT uses BPSK/CCSDS (not DVB-S), GRB uses DVB-S2. Cannot decode imagery. However, the BCM4500's BPSK mode 9 uses the same inner FEC (Viterbi rate 1/2 K=7) as LRIT — the signal chain gets four stages deep before breaking at RS decoder block size and CCSDS framing. The LRIT carrier at 1694.1 MHz is within the [direct input range](/tools/h21cm/#antenna-setup) and can be used for antenna alignment and propagation monitoring. See [RF Test Bench](/tools/rf-testbench/) for BPSK mode 9 probing. |
|
||||
| **QO-100 from North America** | Es'hail-2 is at 25.9°E — visible from Europe, Africa, and the Middle East, but not North America. See [QO-100 DATV Reception](/guides/qo100-datv/) for coverage details. |
|
||||
| **Military/government feeds** | Encrypted and increasingly DVB-S2 or proprietary modulation. |
|
||||
| **ATSC / DVB-T terrestrial** | Completely different modulation family (OFDM), different frequency band. |
|
||||
| **Analog satellite TV** | The BCM4500 is a digital demodulator. Analog satellite is also effectively extinct. |
|
||||
|
||||
## Modulation Support Reference
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="By Standard">
|
||||
| Modulation | Standard | Typical Use | FTA Content? |
|
||||
|-----------|----------|-------------|-------------|
|
||||
| DVB-S QPSK | DVB-S EN 300 421 | Free-to-air satellite TV worldwide | Yes — most FTA content |
|
||||
| Turbo QPSK | Proprietary (Comstream) | Distribution, some DISH | Rare |
|
||||
| Turbo 8PSK | Proprietary | DISH Network | No — encrypted |
|
||||
| DCII Combo | Motorola DigiCipher II | Cable headend distribution | Some ("Zero Key") |
|
||||
| DCII Split I | Motorola DigiCipher II | Cable headend distribution | Some |
|
||||
| DCII Split Q | Motorola DigiCipher II | Cable headend distribution | Some |
|
||||
| DCII Offset QPSK | Motorola DigiCipher II | Cable headend distribution | Some |
|
||||
| DSS QPSK | Hughes DSS | Legacy DirecTV | No — service winding down |
|
||||
</TabItem>
|
||||
<TabItem label="By Receiver Use">
|
||||
| What You Want to Do | Modulation to Select | Symbol Rate Range |
|
||||
|--------------------|--------------------|------------------|
|
||||
| Watch FTA satellite TV | DVB-S QPSK | 2-30 Msps |
|
||||
| Analyze DISH Network signals | Turbo 8PSK | 20-30 Msps |
|
||||
| Receive DCII cable distribution | DCII Combo/Split/Offset | 2-30 Msps |
|
||||
| Study DSS transport format | DSS QPSK | 20 Msps typical |
|
||||
| Hydrogen 21 cm (no LNB) | N/A — AGC power only | Any (for carrier lock attempt) |
|
||||
| Spectrum sweep / signal detection | N/A — AGC power only | Set during tune, not critical |
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## See Also
|
||||
|
||||
- [RF Specifications](/hardware/rf-specifications/) — frequency range, symbol rate limits, LNB power
|
||||
- [BCM4500 Demodulator](/bcm4500/demodulator/) — register-level modulation configuration
|
||||
- [Spectrum Analysis](/tools/spectrum-analysis/) — sweep techniques and transponder scanning
|
||||
- [RF Test Bench](/tools/rf-testbench/) — CW injection testing with NanoVNA + HMC472A
|
||||
- [Experimenter's Roadmap](/guides/experimenter-roadmap/) — future experiment tiers and creative applications
|
||||
- [MCP Server](/tools/mcp-server/) — programmatic access to all hardware functions
|
||||
|
|
|
|||
|
|
@ -1,218 +1,218 @@
|
|||
---
|
||||
title: Experimenter's Roadmap
|
||||
description: What else can the SkyWalker-1 hardware do? A field guide to creative RF experiments beyond satellite TV reception.
|
||||
---
|
||||
|
||||
import { Aside, Badge, Card, CardGrid, Steps, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
The SkyWalker-1 reverse engineering project has produced a fully documented, custom-firmware-driven,
|
||||
Python-controllable RF instrument. With v3.05.0 deployed and all 55 Hamilton safety tests passing,
|
||||
the question becomes: **what can experimenters actually do with this platform beyond satellite TV reception?**
|
||||
|
||||
The approach: start from the hardware capabilities, ask "what physical processes produce or interact
|
||||
with signals these capabilities can measure," and reason outward to creative applications.
|
||||
|
||||
## Hardware as a Platform
|
||||
|
||||
The SkyWalker-1 is simultaneously:
|
||||
|
||||
- A **power meter** (16-bit AGC, ~30-40 dB dynamic range)
|
||||
- A **spectrum analyzer** (~346 kHz RBW, 950-2150 MHz)
|
||||
- A **satellite receiver** (10 modulation types, 256 ksps - 30 Msps)
|
||||
- A **time-series data logger** (signal_monitor at ~50 Hz)
|
||||
- A **dish positioning system** (DiSEqC 1.2 motor, USALS GotoX)
|
||||
|
||||
All controllable from Python.
|
||||
|
||||
<Aside type="tip" title="The key realization">
|
||||
The AGC registers respond to **any RF energy** at the tuned frequency, regardless of modulation.
|
||||
You don't need to demodulate a signal to detect and measure it.
|
||||
</Aside>
|
||||
|
||||
## What's Directly Receivable (No LNB)
|
||||
|
||||
The 950-2150 MHz IF range contains far more than satellite TV when you connect an antenna directly:
|
||||
|
||||
| Frequency | What's There | Detectable? |
|
||||
|---|---|---|
|
||||
| **1420.405 MHz** | Hydrogen 21 cm line — galactic emission | Yes (AGC power) |
|
||||
| 1575.42 MHz | GPS L1 | Yes (energy) |
|
||||
| 1176.45 MHz | GPS L5 / Galileo E5a | Yes (energy) |
|
||||
| 1227.6 MHz | GPS L2 | Yes (energy) |
|
||||
| 1602 MHz | GLONASS L1 | Yes (energy) |
|
||||
| 1525-1559 MHz | Inmarsat downlink | Yes (energy) |
|
||||
| 1616-1626 MHz | Iridium downlink | Yes (burst energy) |
|
||||
| 1670-1710 MHz | GOES LRIT, NOAA HRPT | Yes (carrier) |
|
||||
| 1240-1300 MHz | Amateur 23 cm band | Yes (energy) |
|
||||
|
||||
## Phase 1 Tools (Available Now)
|
||||
|
||||
### Hydrogen 21 cm Drift-Scan Radiometer
|
||||
|
||||
<Badge text="tools/h21cm.py" variant="note" />
|
||||
|
||||
Neutral hydrogen emits at 1420.405 MHz — directly in the IF range. The Milky Way's spiral arms
|
||||
create a velocity-dispersed emission profile detectable even with the BCM4500's resolution bandwidth.
|
||||
|
||||
<Steps>
|
||||
1. Connect an L-band antenna (patch, helical, or horn) directly to the F-connector
|
||||
2. Run `python h21cm.py` for a single sweep centered on 1420.405 MHz
|
||||
3. Look for elevated power across the 1419-1421 MHz range (the hydrogen emission)
|
||||
4. Use `--drift --duration 3600` for a one-hour drift scan as Earth rotates
|
||||
5. Use `--averages 8` for 8x averaging to pull the signal from the noise
|
||||
</Steps>
|
||||
|
||||
```bash
|
||||
# Single sweep with 8x averaging
|
||||
python tools/h21cm.py --averages 8
|
||||
|
||||
# One-hour drift scan with CSV output
|
||||
python tools/h21cm.py --drift --duration 3600 --averages 4 --output h21cm-data.csv
|
||||
|
||||
# Include control band comparison
|
||||
python tools/h21cm.py --averages 8 --control
|
||||
```
|
||||
|
||||
The velocity of the detected hydrogen is calculated from Doppler shift:
|
||||
**v = c × (f_rest − f_observed) / f_rest**. This maps directly to the rotation curve
|
||||
of the Milky Way.
|
||||
|
||||
### Beacon Logger
|
||||
|
||||
<Badge text="tools/beacon_logger.py" variant="note" />
|
||||
|
||||
Lock onto a stable Ku-band broadcast transponder and log SNR/AGC at configurable intervals
|
||||
for hours, days, or weeks. Produces propagation datasets useful for:
|
||||
|
||||
- **Rain fade analysis** — correlate attenuation with rainfall rate
|
||||
- **Diurnal thermal drift** — track LNB gain vs. temperature over 24 hours
|
||||
- **Antenna mount stability** — detect dish drift from wind/thermal expansion
|
||||
- **ITU propagation model validation** — contribute real measurements
|
||||
|
||||
```bash
|
||||
# Log a Ku-band beacon for 24 hours, 1 sample/sec, report every minute
|
||||
python tools/beacon_logger.py --freq 1265000 --sr 20000000 \
|
||||
--output beacon-24h.csv --json-output beacon-stats.jsonl \
|
||||
--duration 86400
|
||||
|
||||
# Generate a systemd unit file for unattended operation
|
||||
python tools/beacon_logger.py --generate-systemd \
|
||||
--freq 1265000 --sr 20000000 --output /var/log/beacon.csv
|
||||
```
|
||||
|
||||
The logger automatically re-locks on signal loss and computes per-interval statistics
|
||||
(min/max/mean/stddev of SNR).
|
||||
|
||||
### Multi-Satellite Arc Survey
|
||||
|
||||
<Badge text="tools/arc_survey.py" variant="note" />
|
||||
|
||||
Automated "satellite census": points the dish motor to each GEO orbital longitude,
|
||||
runs a full-band six-stage survey at each position, and aggregates results into a comprehensive sky map.
|
||||
|
||||
```bash
|
||||
# Survey specific North American slots
|
||||
python tools/arc_survey.py --observer-lon -96.8 --slots "97W,99W,101W,103W"
|
||||
|
||||
# Survey an arc at 3-degree intervals
|
||||
python tools/arc_survey.py --observer-lon -96.8 --arc -120 -60 --step 3
|
||||
|
||||
# List common NA orbital slots
|
||||
python tools/arc_survey.py --list-slots
|
||||
|
||||
# Resume an interrupted survey
|
||||
python tools/arc_survey.py --resume ~/.skywalker1/arc-surveys/arc-survey-2026-02-17.json
|
||||
```
|
||||
|
||||
<Aside type="caution" title="Time commitment">
|
||||
Each orbital slot takes 5-15 minutes to survey. A full North American arc
|
||||
(~35 positions) is an overnight operation. The tool saves progress after each
|
||||
slot, so Ctrl-C pauses safely and `--resume` continues where you left off.
|
||||
</Aside>
|
||||
|
||||
### MCP Server
|
||||
|
||||
<Badge text="mcp/skywalker-mcp/" variant="note" />
|
||||
|
||||
The `skywalker-mcp` FastMCP server wraps the entire hardware API as MCP tools, making
|
||||
every function accessible to LLMs. This is the foundation for autonomous RF exploration.
|
||||
|
||||
```bash
|
||||
# Install and run (from project directory)
|
||||
cd mcp/skywalker-mcp && uv run skywalker-mcp
|
||||
|
||||
# Add to Claude Code
|
||||
claude mcp add skywalker-mcp -- uv run --directory mcp/skywalker-mcp skywalker-mcp
|
||||
|
||||
# Test with headless mode
|
||||
claude -p "What firmware version is loaded?" \
|
||||
--mcp-config .mcp.json --allowedTools "mcp__skywalker-mcp__*"
|
||||
```
|
||||
|
||||
**20 MCP tools** covering:
|
||||
- Device status and signal quality
|
||||
- Spectrum sweep with peak detection
|
||||
- Frequency tuning across 10 modulation types
|
||||
- Blind scan for unknown carriers
|
||||
- Six-stage carrier survey with catalog persistence
|
||||
- Dish motor control (jog, goto, USALS)
|
||||
- LNB configuration
|
||||
- I2C bus scanning
|
||||
- Transport stream capture and PSI parsing
|
||||
- Frequency identification against allocation tables
|
||||
|
||||
**MCP Resources:**
|
||||
- `skywalker://status` — live device state
|
||||
- `skywalker://catalog/latest` — most recent survey
|
||||
- `skywalker://allocations/lband` — frequency allocation table
|
||||
- `skywalker://modulations` — supported modulation types
|
||||
|
||||
**MCP Prompts:**
|
||||
- `explore_rf_environment` — autonomous RF exploration strategy
|
||||
- `hydrogen_line_observation` — guided hydrogen 21 cm procedure
|
||||
|
||||
## Future Tiers
|
||||
|
||||
### Tier B: Creative Combinations (no firmware changes)
|
||||
|
||||
| Idea | Description |
|
||||
|---|---|
|
||||
| **Gradient-descent dish auto-peaking** | Closed-loop: tune to beacon, read AGC, step motor, converge |
|
||||
| **Rain fade monitor** | Dual-frequency SNR logging + weather API correlation |
|
||||
| **NIT-driven survey** | Parse NIT from one carrier → skip to ALL transponders |
|
||||
| **Spectral anomaly detection** | Build baseline, flag N-sigma deviations, catch interference |
|
||||
| **PCR timing analysis** | Extract clock jitter from transport stream timestamps |
|
||||
|
||||
### Tier C: Firmware Enhancements (v4.x)
|
||||
|
||||
| Feature | Impact | Size |
|
||||
|---|---|---|
|
||||
| Continuous AGC streaming via EP2 | Real-time waterfall displays | ~100 bytes |
|
||||
| Firmware-side moving average | 12 dB noise floor improvement (16x avg) | ~50 bytes |
|
||||
| GPIO event counter via Timer1 | General-purpose frequency counter | ~80 bytes |
|
||||
| Multi-byte I2C transactions | External sensor integration | ~120 bytes |
|
||||
| Fast power-only read (no retune) | 2x measurement rate | ~30 bytes |
|
||||
|
||||
### Tier D: External Hardware
|
||||
|
||||
- **I2C environmental sensors** (BME280/SHT40) for LNB drift vs. temperature
|
||||
- **External TCXO/OCXO reference** for calibrated LNB frequency (±50 Hz vs ±5 MHz)
|
||||
- **Noise source + Y-factor calibration** for absolute power measurements
|
||||
- **GPIO antenna switch matrix** for automated antenna comparison
|
||||
|
||||
## Who Gets What
|
||||
|
||||
<CardGrid>
|
||||
<Card title="RF Phreaks" icon="rocket">
|
||||
Iridium burst detector, arc survey, anomaly detection, MCP autonomous explorer
|
||||
</Card>
|
||||
<Card title="Ham Radio" icon="open-book">
|
||||
Hydrogen line, dish auto-peaking, QO-100 monitoring, arc survey
|
||||
</Card>
|
||||
<Card title="Skywatchers" icon="star">
|
||||
Hydrogen line, GNSS monitoring, arc survey, beacon logger
|
||||
</Card>
|
||||
<Card title="Engineers" icon="setting">
|
||||
Beacon logger, rain fade monitor, polarization analysis, PCR timing
|
||||
</Card>
|
||||
</CardGrid>
|
||||
---
|
||||
title: Experimenter's Roadmap
|
||||
description: What else can the SkyWalker-1 hardware do? A field guide to creative RF experiments beyond satellite TV reception.
|
||||
---
|
||||
|
||||
import { Aside, Badge, Card, CardGrid, Steps, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
The SkyWalker-1 reverse engineering project has produced a fully documented, custom-firmware-driven,
|
||||
Python-controllable RF instrument. With v3.05.0 deployed and all 55 Hamilton safety tests passing,
|
||||
the question becomes: **what can experimenters actually do with this platform beyond satellite TV reception?**
|
||||
|
||||
The approach: start from the hardware capabilities, ask "what physical processes produce or interact
|
||||
with signals these capabilities can measure," and reason outward to creative applications.
|
||||
|
||||
## Hardware as a Platform
|
||||
|
||||
The SkyWalker-1 is simultaneously:
|
||||
|
||||
- A **power meter** (16-bit AGC, ~30-40 dB dynamic range)
|
||||
- A **spectrum analyzer** (~346 kHz RBW, 950-2150 MHz)
|
||||
- A **satellite receiver** (10 modulation types, 256 ksps - 30 Msps)
|
||||
- A **time-series data logger** (signal_monitor at ~50 Hz)
|
||||
- A **dish positioning system** (DiSEqC 1.2 motor, USALS GotoX)
|
||||
|
||||
All controllable from Python.
|
||||
|
||||
<Aside type="tip" title="The key realization">
|
||||
The AGC registers respond to **any RF energy** at the tuned frequency, regardless of modulation.
|
||||
You don't need to demodulate a signal to detect and measure it.
|
||||
</Aside>
|
||||
|
||||
## What's Directly Receivable (No LNB)
|
||||
|
||||
The 950-2150 MHz IF range contains far more than satellite TV when you connect an antenna directly:
|
||||
|
||||
| Frequency | What's There | Detectable? |
|
||||
|---|---|---|
|
||||
| **1420.405 MHz** | Hydrogen 21 cm line — galactic emission | Yes (AGC power) |
|
||||
| 1575.42 MHz | GPS L1 | Yes (energy) |
|
||||
| 1176.45 MHz | GPS L5 / Galileo E5a | Yes (energy) |
|
||||
| 1227.6 MHz | GPS L2 | Yes (energy) |
|
||||
| 1602 MHz | GLONASS L1 | Yes (energy) |
|
||||
| 1525-1559 MHz | Inmarsat downlink | Yes (energy) |
|
||||
| 1616-1626 MHz | Iridium downlink | Yes (burst energy) |
|
||||
| 1670-1710 MHz | GOES LRIT, NOAA HRPT | Yes (carrier) |
|
||||
| 1240-1300 MHz | Amateur 23 cm band | Yes (energy) |
|
||||
|
||||
## Phase 1 Tools (Available Now)
|
||||
|
||||
### Hydrogen 21 cm Drift-Scan Radiometer
|
||||
|
||||
<Badge text="tools/h21cm.py" variant="note" />
|
||||
|
||||
Neutral hydrogen emits at 1420.405 MHz — directly in the IF range. The Milky Way's spiral arms
|
||||
create a velocity-dispersed emission profile detectable even with the BCM4500's resolution bandwidth.
|
||||
|
||||
<Steps>
|
||||
1. Connect an L-band antenna (patch, helical, or horn) directly to the F-connector
|
||||
2. Run `python h21cm.py` for a single sweep centered on 1420.405 MHz
|
||||
3. Look for elevated power across the 1419-1421 MHz range (the hydrogen emission)
|
||||
4. Use `--drift --duration 3600` for a one-hour drift scan as Earth rotates
|
||||
5. Use `--averages 8` for 8x averaging to pull the signal from the noise
|
||||
</Steps>
|
||||
|
||||
```bash
|
||||
# Single sweep with 8x averaging
|
||||
python tools/h21cm.py --averages 8
|
||||
|
||||
# One-hour drift scan with CSV output
|
||||
python tools/h21cm.py --drift --duration 3600 --averages 4 --output h21cm-data.csv
|
||||
|
||||
# Include control band comparison
|
||||
python tools/h21cm.py --averages 8 --control
|
||||
```
|
||||
|
||||
The velocity of the detected hydrogen is calculated from Doppler shift:
|
||||
**v = c × (f_rest − f_observed) / f_rest**. This maps directly to the rotation curve
|
||||
of the Milky Way.
|
||||
|
||||
### Beacon Logger
|
||||
|
||||
<Badge text="tools/beacon_logger.py" variant="note" />
|
||||
|
||||
Lock onto a stable Ku-band broadcast transponder and log SNR/AGC at configurable intervals
|
||||
for hours, days, or weeks. Produces propagation datasets useful for:
|
||||
|
||||
- **Rain fade analysis** — correlate attenuation with rainfall rate
|
||||
- **Diurnal thermal drift** — track LNB gain vs. temperature over 24 hours
|
||||
- **Antenna mount stability** — detect dish drift from wind/thermal expansion
|
||||
- **ITU propagation model validation** — contribute real measurements
|
||||
|
||||
```bash
|
||||
# Log a Ku-band beacon for 24 hours, 1 sample/sec, report every minute
|
||||
python tools/beacon_logger.py --freq 1265000 --sr 20000000 \
|
||||
--output beacon-24h.csv --json-output beacon-stats.jsonl \
|
||||
--duration 86400
|
||||
|
||||
# Generate a systemd unit file for unattended operation
|
||||
python tools/beacon_logger.py --generate-systemd \
|
||||
--freq 1265000 --sr 20000000 --output /var/log/beacon.csv
|
||||
```
|
||||
|
||||
The logger automatically re-locks on signal loss and computes per-interval statistics
|
||||
(min/max/mean/stddev of SNR).
|
||||
|
||||
### Multi-Satellite Arc Survey
|
||||
|
||||
<Badge text="tools/arc_survey.py" variant="note" />
|
||||
|
||||
Automated "satellite census": points the dish motor to each GEO orbital longitude,
|
||||
runs a full-band six-stage survey at each position, and aggregates results into a comprehensive sky map.
|
||||
|
||||
```bash
|
||||
# Survey specific North American slots
|
||||
python tools/arc_survey.py --observer-lon -96.8 --slots "97W,99W,101W,103W"
|
||||
|
||||
# Survey an arc at 3-degree intervals
|
||||
python tools/arc_survey.py --observer-lon -96.8 --arc -120 -60 --step 3
|
||||
|
||||
# List common NA orbital slots
|
||||
python tools/arc_survey.py --list-slots
|
||||
|
||||
# Resume an interrupted survey
|
||||
python tools/arc_survey.py --resume ~/.skywalker1/arc-surveys/arc-survey-2026-02-17.json
|
||||
```
|
||||
|
||||
<Aside type="caution" title="Time commitment">
|
||||
Each orbital slot takes 5-15 minutes to survey. A full North American arc
|
||||
(~35 positions) is an overnight operation. The tool saves progress after each
|
||||
slot, so Ctrl-C pauses safely and `--resume` continues where you left off.
|
||||
</Aside>
|
||||
|
||||
### MCP Server
|
||||
|
||||
<Badge text="mcp/skywalker-mcp/" variant="note" />
|
||||
|
||||
The `skywalker-mcp` FastMCP server wraps the entire hardware API as MCP tools, making
|
||||
every function accessible to LLMs. This is the foundation for autonomous RF exploration.
|
||||
|
||||
```bash
|
||||
# Install and run (from project directory)
|
||||
cd mcp/skywalker-mcp && uv run skywalker-mcp
|
||||
|
||||
# Add to Claude Code
|
||||
claude mcp add skywalker-mcp -- uv run --directory mcp/skywalker-mcp skywalker-mcp
|
||||
|
||||
# Test with headless mode
|
||||
claude -p "What firmware version is loaded?" \
|
||||
--mcp-config .mcp.json --allowedTools "mcp__skywalker-mcp__*"
|
||||
```
|
||||
|
||||
**20 MCP tools** covering:
|
||||
- Device status and signal quality
|
||||
- Spectrum sweep with peak detection
|
||||
- Frequency tuning across 10 modulation types
|
||||
- Blind scan for unknown carriers
|
||||
- Six-stage carrier survey with catalog persistence
|
||||
- Dish motor control (jog, goto, USALS)
|
||||
- LNB configuration
|
||||
- I2C bus scanning
|
||||
- Transport stream capture and PSI parsing
|
||||
- Frequency identification against allocation tables
|
||||
|
||||
**MCP Resources:**
|
||||
- `skywalker://status` — live device state
|
||||
- `skywalker://catalog/latest` — most recent survey
|
||||
- `skywalker://allocations/lband` — frequency allocation table
|
||||
- `skywalker://modulations` — supported modulation types
|
||||
|
||||
**MCP Prompts:**
|
||||
- `explore_rf_environment` — autonomous RF exploration strategy
|
||||
- `hydrogen_line_observation` — guided hydrogen 21 cm procedure
|
||||
|
||||
## Future Tiers
|
||||
|
||||
### Tier B: Creative Combinations (no firmware changes)
|
||||
|
||||
| Idea | Description |
|
||||
|---|---|
|
||||
| **Gradient-descent dish auto-peaking** | Closed-loop: tune to beacon, read AGC, step motor, converge |
|
||||
| **Rain fade monitor** | Dual-frequency SNR logging + weather API correlation |
|
||||
| **NIT-driven survey** | Parse NIT from one carrier → skip to ALL transponders |
|
||||
| **Spectral anomaly detection** | Build baseline, flag N-sigma deviations, catch interference |
|
||||
| **PCR timing analysis** | Extract clock jitter from transport stream timestamps |
|
||||
|
||||
### Tier C: Firmware Enhancements (v4.x)
|
||||
|
||||
| Feature | Impact | Size |
|
||||
|---|---|---|
|
||||
| Continuous AGC streaming via EP2 | Real-time waterfall displays | ~100 bytes |
|
||||
| Firmware-side moving average | 12 dB noise floor improvement (16x avg) | ~50 bytes |
|
||||
| GPIO event counter via Timer1 | General-purpose frequency counter | ~80 bytes |
|
||||
| Multi-byte I2C transactions | External sensor integration | ~120 bytes |
|
||||
| Fast power-only read (no retune) | 2x measurement rate | ~30 bytes |
|
||||
|
||||
### Tier D: External Hardware
|
||||
|
||||
- **I2C environmental sensors** (BME280/SHT40) for LNB drift vs. temperature
|
||||
- **External TCXO/OCXO reference** for calibrated LNB frequency (±50 Hz vs ±5 MHz)
|
||||
- **Noise source + Y-factor calibration** for absolute power measurements
|
||||
- **GPIO antenna switch matrix** for automated antenna comparison
|
||||
|
||||
## Who Gets What
|
||||
|
||||
<CardGrid>
|
||||
<Card title="RF Phreaks" icon="rocket">
|
||||
Iridium burst detector, arc survey, anomaly detection, MCP autonomous explorer
|
||||
</Card>
|
||||
<Card title="Ham Radio" icon="open-book">
|
||||
Hydrogen line, dish auto-peaking, QO-100 monitoring, arc survey
|
||||
</Card>
|
||||
<Card title="Skywatchers" icon="star">
|
||||
Hydrogen line, GNSS monitoring, arc survey, beacon logger
|
||||
</Card>
|
||||
<Card title="Engineers" icon="setting">
|
||||
Beacon logger, rain fade monitor, polarization analysis, PCR timing
|
||||
</Card>
|
||||
</CardGrid>
|
||||
|
|
|
|||
|
|
@ -1,175 +1,175 @@
|
|||
---
|
||||
title: QO-100 DATV Reception
|
||||
description: Receiving amateur digital television from the Es'hail-2 geostationary satellite using the SkyWalker-1 DVB-S receiver.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Steps, Aside, Badge, CardGrid, Card } from '@astrojs/starlight/components';
|
||||
|
||||
The Es'hail-2 satellite (25.9 degrees East) carries the first geostationary amateur radio transponders, designated QO-100. The wideband transponder at 10491-10499 MHz carries DATV (Digital Amateur Television) signals that the SkyWalker-1 can receive — with some important caveats about symbol rate limitations and LNB selection.
|
||||
|
||||
<Badge text="Requires custom firmware v3.03.0+" variant="caution" />
|
||||
|
||||
## Overview
|
||||
|
||||
<CardGrid>
|
||||
<Card title="Wideband Transponder" icon="rocket">
|
||||
10491.000 - 10499.500 MHz (8.5 MHz bandwidth). Carries DVB-S DATV stations from amateur operators worldwide.
|
||||
</Card>
|
||||
<Card title="Engineering Beacon" icon="star">
|
||||
10489.750 MHz CW/PSK beacon. Useful for dish pointing and LNB drift calibration.
|
||||
</Card>
|
||||
<Card title="Orbital Position" icon="sun">
|
||||
25.9 degrees East — visible from Europe, Africa, western Asia, and eastern Americas.
|
||||
</Card>
|
||||
<Card title="Minimum Dish Size" icon="setting">
|
||||
60 cm in central Europe, 1.2m+ at the edges of the footprint.
|
||||
</Card>
|
||||
</CardGrid>
|
||||
|
||||
## LNB Selection
|
||||
|
||||
The SkyWalker-1 accepts IF frequencies in the 950-2150 MHz range. The LNB's local oscillator frequency determines where the QO-100 signals appear in this window.
|
||||
|
||||
| LNB Type | LO Frequency | QO-100 IF Range | Compatible? |
|
||||
|----------|-------------|-----------------|-------------|
|
||||
| Universal (low band) | 9750 MHz | 741-749 MHz | No — below 950 MHz minimum |
|
||||
| Universal (high band) | 10600 MHz | -109 to -101 MHz | No — negative IF |
|
||||
| Modified universal | 9361 MHz | 1130-1138 MHz | Yes |
|
||||
| TCXO/PLL stabilized | 9750 MHz | 741-749 MHz | No — same issue |
|
||||
| Custom downconverter | varies | varies | Check IF range |
|
||||
|
||||
<Aside type="caution">
|
||||
A **standard universal LNB** at 9750 MHz places the QO-100 wideband transponder at 741-749 MHz IF, which is **below the SkyWalker-1's 950 MHz minimum**. You need either a modified LNB or an external downconverter to shift the IF into the 950-2150 MHz range.
|
||||
</Aside>
|
||||
|
||||
### Modified LNB Option
|
||||
|
||||
The most common approach among QO-100 operators is modifying a universal LNB by replacing the crystal oscillator (typically 25 MHz) with one that produces a different LO frequency. A 24.000 MHz crystal in a universal LNB that normally multiplies by 390 (9750 MHz) will produce 9360 MHz instead, placing QO-100 at 1131-1139 MHz IF.
|
||||
|
||||
Popular modified LO frequencies and their resulting IF ranges:
|
||||
|
||||
| Crystal | Multiplier | LO (MHz) | QO-100 IF (MHz) |
|
||||
|---------|-----------|----------|-----------------|
|
||||
| 24.000 MHz | x390 | 9360 | 1131-1139 |
|
||||
| 24.385 MHz | x384 | 9363 | 1128-1136 |
|
||||
| 25.000 MHz | x374 | 9350 | 1141-1149 |
|
||||
| 27.000 MHz | x348 | 9396 | 1095-1103 |
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Steps>
|
||||
1. **Verify LNB compatibility** — check that your LO frequency places QO-100 in the 950-2150 MHz range:
|
||||
```bash
|
||||
python3 tools/qo100.py calc --lnb-lo 9361
|
||||
```
|
||||
|
||||
2. **Point the dish** — aim at 25.9 degrees East. Use the motor control tool or manual positioning:
|
||||
```bash
|
||||
sudo python3 tools/motor.py gotox --sat 25.9 --lon -97.5
|
||||
```
|
||||
|
||||
3. **Scan for carriers** — sweep the QO-100 IF range:
|
||||
```bash
|
||||
sudo python3 tools/qo100.py scan --lnb-lo 9361
|
||||
```
|
||||
|
||||
4. **Tune to a carrier** — lock onto a detected DATV station:
|
||||
```bash
|
||||
sudo python3 tools/qo100.py tune --freq 1135 --sr 1000 --lnb-lo 9361
|
||||
```
|
||||
|
||||
5. **Watch live video** — pipe the transport stream to a media player:
|
||||
```bash
|
||||
sudo python3 tools/qo100.py watch --freq 1135 --sr 1000 --lnb-lo 9361
|
||||
```
|
||||
</Steps>
|
||||
|
||||
## CLI Reference
|
||||
|
||||
### Calculate IF Frequencies
|
||||
|
||||
```bash
|
||||
python3 tools/qo100.py calc --lnb-lo 9361
|
||||
```
|
||||
|
||||
Displays the QO-100 wideband transponder IF range, known DATV frequencies with their IF equivalents, and the beacon frequency. No hardware required — pure calculation.
|
||||
|
||||
### Band Plan
|
||||
|
||||
```bash
|
||||
python3 tools/qo100.py band-plan --lnb-lo 9361
|
||||
```
|
||||
|
||||
Shows the complete QO-100 wideband transponder band plan with known station frequencies converted to your LNB's IF range. Includes both the wideband DATV segment and the engineering beacon.
|
||||
|
||||
### Scan
|
||||
|
||||
```bash
|
||||
sudo python3 tools/qo100.py scan --lnb-lo 9361
|
||||
```
|
||||
|
||||
Sweeps the QO-100 IF range with parameters tuned for narrowband DATV:
|
||||
- 250 kHz frequency steps (vs 5 MHz for broadcast)
|
||||
- 1000 ksps measurement symbol rate
|
||||
- Extended dwell time for weak signal detection
|
||||
|
||||
### Tune
|
||||
|
||||
```bash
|
||||
sudo python3 tools/qo100.py tune --freq 1135 --sr 1000 --lnb-lo 9361
|
||||
```
|
||||
|
||||
Tunes the BCM4500 to the specified IF frequency and symbol rate, then monitors lock status and signal quality. The `--freq` parameter is the IF frequency (after LNB downconversion), not the RF frequency.
|
||||
|
||||
### Watch
|
||||
|
||||
```bash
|
||||
sudo python3 tools/qo100.py watch --freq 1135 --sr 1000 --lnb-lo 9361
|
||||
```
|
||||
|
||||
Tunes, locks, and pipes the raw MPEG-2 transport stream to `ffplay` (or `mpv` if ffplay is not found). The video player receives the stream on stdin and renders it in real time.
|
||||
|
||||
| Flag | Default | Description |
|
||||
|------|---------|-------------|
|
||||
| `--freq` | — | IF frequency in MHz (required) |
|
||||
| `--sr` | 1000 | Symbol rate in ksps |
|
||||
| `--lnb-lo` | — | LNB local oscillator in MHz (required) |
|
||||
| `--player` | auto | Video player command (`ffplay` or `mpv`) |
|
||||
|
||||
## Hardware Limitations
|
||||
|
||||
### Minimum Symbol Rate
|
||||
|
||||
The BCM4500 demodulator has a minimum symbol rate of **256 ksps** (256,000 symbols per second). Many QO-100 DATV stations transmit at lower rates:
|
||||
|
||||
| Typical QO-100 SR | BCM4500 Support |
|
||||
|-------------------|----------------|
|
||||
| 125 ksps | Detectable as energy, cannot lock |
|
||||
| 250 ksps | Marginal — may lock with strong signal |
|
||||
| 333 ksps | Supported |
|
||||
| 500 ksps | Supported |
|
||||
| 1000 ksps | Supported |
|
||||
| 2000 ksps | Supported |
|
||||
|
||||
The survey tool distinguishes "carrier detected" (energy above noise floor) from "carrier locked" (BCM4500 achieved demodulator lock) in its output.
|
||||
|
||||
### Lock Acquisition Time
|
||||
|
||||
QO-100 signals are weaker than broadcast satellites. The BCM4500 may need 10-30 seconds to achieve lock at low symbol rates, compared to 1-2 seconds for typical broadcast transponders. The `qo100.py tune` command uses extended timeouts automatically.
|
||||
|
||||
### Frequency Stability
|
||||
|
||||
QO-100 DATV signals have tighter frequency tolerance requirements than the SkyWalker-1's default tuning resolution. A PLL-stabilized or TCXO LNB provides better frequency accuracy than a standard DRO LNB, reducing the chance of the demodulator losing lock due to LNB drift.
|
||||
|
||||
## Using the TUI
|
||||
|
||||
The SkyWalker TUI includes QO-100 as a tab within the Survey screen (<kbd>F10</kbd>). The QO-100 tab pre-fills the sweep parameters with values optimized for the wideband transponder and includes an info panel showing known station frequencies adjusted for your LNB's local oscillator.
|
||||
|
||||
See [SkyWalker TUI — Survey Screen](/tools/tui/#survey--qo-100) for details.
|
||||
|
||||
## See Also
|
||||
|
||||
- [Motor Control](/tools/motor/) — DiSEqC 1.2 positioner for dish pointing
|
||||
- [Carrier Survey](/tools/survey/) — automated carrier detection and cataloging
|
||||
- [LNB Control](/lnb-diseqc/lnb-control/) — LNB power and voltage configuration
|
||||
- [RF Specifications](/hardware/rf-specifications/) — SkyWalker-1 frequency range and sensitivity
|
||||
---
|
||||
title: QO-100 DATV Reception
|
||||
description: Receiving amateur digital television from the Es'hail-2 geostationary satellite using the SkyWalker-1 DVB-S receiver.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Steps, Aside, Badge, CardGrid, Card } from '@astrojs/starlight/components';
|
||||
|
||||
The Es'hail-2 satellite (25.9 degrees East) carries the first geostationary amateur radio transponders, designated QO-100. The wideband transponder at 10491-10499 MHz carries DATV (Digital Amateur Television) signals that the SkyWalker-1 can receive — with some important caveats about symbol rate limitations and LNB selection.
|
||||
|
||||
<Badge text="Requires custom firmware v3.03.0+" variant="caution" />
|
||||
|
||||
## Overview
|
||||
|
||||
<CardGrid>
|
||||
<Card title="Wideband Transponder" icon="rocket">
|
||||
10491.000 - 10499.500 MHz (8.5 MHz bandwidth). Carries DVB-S DATV stations from amateur operators worldwide.
|
||||
</Card>
|
||||
<Card title="Engineering Beacon" icon="star">
|
||||
10489.750 MHz CW/PSK beacon. Useful for dish pointing and LNB drift calibration.
|
||||
</Card>
|
||||
<Card title="Orbital Position" icon="sun">
|
||||
25.9 degrees East — visible from Europe, Africa, western Asia, and eastern Americas.
|
||||
</Card>
|
||||
<Card title="Minimum Dish Size" icon="setting">
|
||||
60 cm in central Europe, 1.2m+ at the edges of the footprint.
|
||||
</Card>
|
||||
</CardGrid>
|
||||
|
||||
## LNB Selection
|
||||
|
||||
The SkyWalker-1 accepts IF frequencies in the 950-2150 MHz range. The LNB's local oscillator frequency determines where the QO-100 signals appear in this window.
|
||||
|
||||
| LNB Type | LO Frequency | QO-100 IF Range | Compatible? |
|
||||
|----------|-------------|-----------------|-------------|
|
||||
| Universal (low band) | 9750 MHz | 741-749 MHz | No — below 950 MHz minimum |
|
||||
| Universal (high band) | 10600 MHz | -109 to -101 MHz | No — negative IF |
|
||||
| Modified universal | 9361 MHz | 1130-1138 MHz | Yes |
|
||||
| TCXO/PLL stabilized | 9750 MHz | 741-749 MHz | No — same issue |
|
||||
| Custom downconverter | varies | varies | Check IF range |
|
||||
|
||||
<Aside type="caution">
|
||||
A **standard universal LNB** at 9750 MHz places the QO-100 wideband transponder at 741-749 MHz IF, which is **below the SkyWalker-1's 950 MHz minimum**. You need either a modified LNB or an external downconverter to shift the IF into the 950-2150 MHz range.
|
||||
</Aside>
|
||||
|
||||
### Modified LNB Option
|
||||
|
||||
The most common approach among QO-100 operators is modifying a universal LNB by replacing the crystal oscillator (typically 25 MHz) with one that produces a different LO frequency. A 24.000 MHz crystal in a universal LNB that normally multiplies by 390 (9750 MHz) will produce 9360 MHz instead, placing QO-100 at 1131-1139 MHz IF.
|
||||
|
||||
Popular modified LO frequencies and their resulting IF ranges:
|
||||
|
||||
| Crystal | Multiplier | LO (MHz) | QO-100 IF (MHz) |
|
||||
|---------|-----------|----------|-----------------|
|
||||
| 24.000 MHz | x390 | 9360 | 1131-1139 |
|
||||
| 24.385 MHz | x384 | 9363 | 1128-1136 |
|
||||
| 25.000 MHz | x374 | 9350 | 1141-1149 |
|
||||
| 27.000 MHz | x348 | 9396 | 1095-1103 |
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Steps>
|
||||
1. **Verify LNB compatibility** — check that your LO frequency places QO-100 in the 950-2150 MHz range:
|
||||
```bash
|
||||
python3 tools/qo100.py calc --lnb-lo 9361
|
||||
```
|
||||
|
||||
2. **Point the dish** — aim at 25.9 degrees East. Use the motor control tool or manual positioning:
|
||||
```bash
|
||||
sudo python3 tools/motor.py gotox --sat 25.9 --lon -97.5
|
||||
```
|
||||
|
||||
3. **Scan for carriers** — sweep the QO-100 IF range:
|
||||
```bash
|
||||
sudo python3 tools/qo100.py scan --lnb-lo 9361
|
||||
```
|
||||
|
||||
4. **Tune to a carrier** — lock onto a detected DATV station:
|
||||
```bash
|
||||
sudo python3 tools/qo100.py tune --freq 1135 --sr 1000 --lnb-lo 9361
|
||||
```
|
||||
|
||||
5. **Watch live video** — pipe the transport stream to a media player:
|
||||
```bash
|
||||
sudo python3 tools/qo100.py watch --freq 1135 --sr 1000 --lnb-lo 9361
|
||||
```
|
||||
</Steps>
|
||||
|
||||
## CLI Reference
|
||||
|
||||
### Calculate IF Frequencies
|
||||
|
||||
```bash
|
||||
python3 tools/qo100.py calc --lnb-lo 9361
|
||||
```
|
||||
|
||||
Displays the QO-100 wideband transponder IF range, known DATV frequencies with their IF equivalents, and the beacon frequency. No hardware required — pure calculation.
|
||||
|
||||
### Band Plan
|
||||
|
||||
```bash
|
||||
python3 tools/qo100.py band-plan --lnb-lo 9361
|
||||
```
|
||||
|
||||
Shows the complete QO-100 wideband transponder band plan with known station frequencies converted to your LNB's IF range. Includes both the wideband DATV segment and the engineering beacon.
|
||||
|
||||
### Scan
|
||||
|
||||
```bash
|
||||
sudo python3 tools/qo100.py scan --lnb-lo 9361
|
||||
```
|
||||
|
||||
Sweeps the QO-100 IF range with parameters tuned for narrowband DATV:
|
||||
- 250 kHz frequency steps (vs 5 MHz for broadcast)
|
||||
- 1000 ksps measurement symbol rate
|
||||
- Extended dwell time for weak signal detection
|
||||
|
||||
### Tune
|
||||
|
||||
```bash
|
||||
sudo python3 tools/qo100.py tune --freq 1135 --sr 1000 --lnb-lo 9361
|
||||
```
|
||||
|
||||
Tunes the BCM4500 to the specified IF frequency and symbol rate, then monitors lock status and signal quality. The `--freq` parameter is the IF frequency (after LNB downconversion), not the RF frequency.
|
||||
|
||||
### Watch
|
||||
|
||||
```bash
|
||||
sudo python3 tools/qo100.py watch --freq 1135 --sr 1000 --lnb-lo 9361
|
||||
```
|
||||
|
||||
Tunes, locks, and pipes the raw MPEG-2 transport stream to `ffplay` (or `mpv` if ffplay is not found). The video player receives the stream on stdin and renders it in real time.
|
||||
|
||||
| Flag | Default | Description |
|
||||
|------|---------|-------------|
|
||||
| `--freq` | — | IF frequency in MHz (required) |
|
||||
| `--sr` | 1000 | Symbol rate in ksps |
|
||||
| `--lnb-lo` | — | LNB local oscillator in MHz (required) |
|
||||
| `--player` | auto | Video player command (`ffplay` or `mpv`) |
|
||||
|
||||
## Hardware Limitations
|
||||
|
||||
### Minimum Symbol Rate
|
||||
|
||||
The BCM4500 demodulator has a minimum symbol rate of **256 ksps** (256,000 symbols per second). Many QO-100 DATV stations transmit at lower rates:
|
||||
|
||||
| Typical QO-100 SR | BCM4500 Support |
|
||||
|-------------------|----------------|
|
||||
| 125 ksps | Detectable as energy, cannot lock |
|
||||
| 250 ksps | Marginal — may lock with strong signal |
|
||||
| 333 ksps | Supported |
|
||||
| 500 ksps | Supported |
|
||||
| 1000 ksps | Supported |
|
||||
| 2000 ksps | Supported |
|
||||
|
||||
The survey tool distinguishes "carrier detected" (energy above noise floor) from "carrier locked" (BCM4500 achieved demodulator lock) in its output.
|
||||
|
||||
### Lock Acquisition Time
|
||||
|
||||
QO-100 signals are weaker than broadcast satellites. The BCM4500 may need 10-30 seconds to achieve lock at low symbol rates, compared to 1-2 seconds for typical broadcast transponders. The `qo100.py tune` command uses extended timeouts automatically.
|
||||
|
||||
### Frequency Stability
|
||||
|
||||
QO-100 DATV signals have tighter frequency tolerance requirements than the SkyWalker-1's default tuning resolution. A PLL-stabilized or TCXO LNB provides better frequency accuracy than a standard DRO LNB, reducing the chance of the demodulator losing lock due to LNB drift.
|
||||
|
||||
## Using the TUI
|
||||
|
||||
The SkyWalker TUI includes QO-100 as a tab within the Survey screen (<kbd>F10</kbd>). The QO-100 tab pre-fills the sweep parameters with values optimized for the wideband transponder and includes an info panel showing known station frequencies adjusted for your LNB's local oscillator.
|
||||
|
||||
See [SkyWalker TUI — Survey Screen](/tools/tui/#survey--qo-100) for details.
|
||||
|
||||
## See Also
|
||||
|
||||
- [Motor Control](/tools/motor/) — DiSEqC 1.2 positioner for dish pointing
|
||||
- [Carrier Survey](/tools/survey/) — automated carrier detection and cataloging
|
||||
- [LNB Control](/lnb-diseqc/lnb-control/) — LNB power and voltage configuration
|
||||
- [RF Specifications](/hardware/rf-specifications/) — SkyWalker-1 frequency range and sensitivity
|
||||
|
|
|
|||
|
|
@ -1,131 +1,131 @@
|
|||
---
|
||||
title: GPIO Pin Map
|
||||
description: Complete FX2 GPIO pin assignments across all firmware versions including stock and custom firmware.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
The Cypress FX2 uses three GPIO ports for hardware control on the SkyWalker-1: Port 0 (IOA), Port 3 (IOD), and Port B (XRAM-mapped IOB). Pin assignments differ significantly between firmware versions due to PCB revisions and design changes.
|
||||
|
||||
<Aside type="caution" title="Pin Assignments Vary By Firmware">
|
||||
The DiSEqC data pin and several LNB control pins changed between firmware versions. Always verify which firmware version is running before driving GPIO pins directly.
|
||||
</Aside>
|
||||
|
||||
## Port 0 / Port A (SFR 0x80, IOA)
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Stock Firmware">
|
||||
|
||||
| Pin | v2.06 | Rev.2 v2.10 | v2.13 | Notes |
|
||||
|-----|-------|-------------|-------|-------|
|
||||
| P0.0 | -- | LNB control (0x97) | **DiSEqC data** | DiSEqC data pin moved across versions |
|
||||
| P0.1 | Power enable | Power enable | Power enable | BCM4500 power supply enable |
|
||||
| P0.2 | Power disable | Power disable (init=0x84) | Power disable | BCM4500 power supply disable |
|
||||
| P0.3 | **22 kHz tone** | **22 kHz tone** | **22 kHz tone** | Gates external 22 kHz oscillator (all versions) |
|
||||
| P0.4 | **LNB 13V/18V** | **LNB 13V/18V** + DiSEqC data | **LNB 13V/18V** | Also SET_DN_SWITCH bit-bang (all versions) |
|
||||
| P0.5 | **BCM4500 RESET** | GPIO status input (0x98) | **BCM4500 RESET** | Reset and feedback pin |
|
||||
| P0.6 | -- | GPIO control (0x97) | -- | LNB control on Rev.2 only |
|
||||
| P0.7 | **DiSEqC data** | Streaming indicator | Streaming indicator | DiSEqC data on v2.06 only |
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Custom v3.01.0">
|
||||
|
||||
| Pin | Custom v3.01.0 | Notes |
|
||||
|-----|----------------|-------|
|
||||
| P0.0 | -- | Unused |
|
||||
| P0.1 | **Power enable** | BCM4500 power supply enable (HIGH = on) |
|
||||
| P0.2 | **Power disable** | BCM4500 power supply disable (LOW = off) |
|
||||
| P0.3 | **22 kHz tone** | Gates external 22 kHz oscillator |
|
||||
| P0.4 | **LNB 13V/18V** | Voltage select: LOW = 13V, HIGH = 18V |
|
||||
| P0.5 | **BCM4500 RESET** | LOW = reset asserted, HIGH = released |
|
||||
| P0.6 | -- | Unused |
|
||||
| P0.7 | **DiSEqC data** + streaming | DiSEqC data (matches v2.06); also streaming indicator |
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Port 3 / Port D (SFR 0xB0, IOD)
|
||||
|
||||
These pins are consistent across all firmware versions:
|
||||
|
||||
| Pin | Function | Active State | Notes |
|
||||
|-----|----------|-------------|-------|
|
||||
| P3.0 | Init HIGH | -- | Set during initialization |
|
||||
| P3.4 | GPIO control | -- | Used by Rev.2 `FUN_CODE_1fcf` |
|
||||
| P3.5 | **TS_EN** | LOW | Transport stream enable: LOW = active, HIGH = idle |
|
||||
| P3.6 | **DVB mode** | -- | BCM4500 mode select; DiSEqC port direction (Rev.2) |
|
||||
| P3.7 | BCM4500 control | HIGH (idle) | De-asserted (HIGH) when streaming stops |
|
||||
|
||||
<Aside type="caution" title="P3.5 During Boot">
|
||||
P3.5 (TS_EN) must remain HIGH (idle) during the boot sequence and demodulator initialization. Driving it LOW before the BCM4500 is fully initialized may produce undefined behavior on the transport stream bus.
|
||||
</Aside>
|
||||
|
||||
## Port B (XRAM-mapped IOB)
|
||||
|
||||
Port B is used exclusively by the internal debug commands 0x96--0x98. These commands are not used by the Linux or Windows drivers.
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="v2.06 / v2.13">
|
||||
|
||||
| Pin | Function | Command |
|
||||
|-----|----------|---------|
|
||||
| IOB.0 | GPIO status input | GET_GPIO_STATUS (0x98) |
|
||||
| IOB.1 | LNB control line 1 | SET_GPIO_PINS (0x97) |
|
||||
| IOB.2 | LNB control line 2 | SET_GPIO_PINS (0x97) |
|
||||
| IOB.3 | LNB GPIO mode | SET_LNB_GPIO_MODE (0x96) |
|
||||
| IOB.4 | -- | Unused |
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Rev.2 v2.10">
|
||||
|
||||
| Pin | Function | Command |
|
||||
|-----|----------|---------|
|
||||
| IOB.0 | -- | Unused |
|
||||
| IOB.1 | -- | Unused |
|
||||
| IOB.2 | -- | Unused |
|
||||
| IOB.3 | -- | Unused |
|
||||
| IOB.4 | LNB GPIO mode + control | SET_LNB_GPIO_MODE (0x96) + SET_GPIO_PINS (0x97) |
|
||||
|
||||
Rev.2 moved LNB control from Port B to Port A (P0.6, P0.0).
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## DiSEqC Data Pin Summary
|
||||
|
||||
The DiSEqC data pin assignment is the most significant change between firmware versions. The carrier pin (P0.3) remains constant.
|
||||
|
||||
| Firmware Version | Data Pin | Carrier Pin |
|
||||
|-----------------|----------|-------------|
|
||||
| v2.06 | P0.7 | P0.3 |
|
||||
| Rev.2 v2.10 | P0.4 | P0.3 |
|
||||
| v2.13 | P0.0 | P0.3 |
|
||||
| Custom v3.01.0 | P0.7 | P0.3 |
|
||||
|
||||
The data pin is used only internally by the firmware's Manchester encoding logic. It controls whether the 22 kHz carrier gate signal (on P0.3) is cut short or held for the full bit period during DiSEqC transmission. See [DiSEqC Protocol](/lnb-diseqc/diseqc-protocol/) for the encoding details.
|
||||
|
||||
## Initial GPIO State
|
||||
|
||||
On power-up, the FX2 initialization code sets:
|
||||
|
||||
| Register | Value | Decode |
|
||||
|----------|-------|--------|
|
||||
| IOA (P0) | 0x84 | P0.7=1 (idle), P0.2=1 (power disable active) |
|
||||
| IOD (P3) | 0xE1 | P3.7:5=1 (all control lines idle), P3.0=1 |
|
||||
| OEA | 0xBE | P0.1-5,7 configured as outputs |
|
||||
|
||||
These initial states ensure:
|
||||
- The BCM4500 is held in reset (P0.5 driven by output enable, but P0 init has it low after OEA is set)
|
||||
- The transport stream bus is idle (P3.5 = HIGH)
|
||||
- The streaming indicator is off (P0.7 = HIGH)
|
||||
- All BCM4500 control lines are de-asserted (P3.7:5 = 1)
|
||||
|
||||
## Streaming GPIO State Changes
|
||||
|
||||
| Pin | SFR | Direction | During Streaming | Streaming Stopped | Function |
|
||||
|-----|-----|-----------|-----------------|-------------------|----------|
|
||||
| P0.2 | 0x80 | Output | Set during init | -- | BCM4500 config |
|
||||
| P0.7 | 0x80 | Output | **LOW** | HIGH | Streaming status indicator |
|
||||
| P3.5 | 0xB0 | Output | Pulsed LOW | HIGH | BCM4500 TS_EN (transport stream enable) |
|
||||
| P3.6 | 0xB0 | Output | Controlled | HIGH | BCM4500 DVB mode control |
|
||||
| P3.7 | 0xB0 | Output | Controlled | HIGH | BCM4500 control line |
|
||||
---
|
||||
title: GPIO Pin Map
|
||||
description: Complete FX2 GPIO pin assignments across all firmware versions including stock and custom firmware.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
The Cypress FX2 uses three GPIO ports for hardware control on the SkyWalker-1: Port 0 (IOA), Port 3 (IOD), and Port B (XRAM-mapped IOB). Pin assignments differ significantly between firmware versions due to PCB revisions and design changes.
|
||||
|
||||
<Aside type="caution" title="Pin Assignments Vary By Firmware">
|
||||
The DiSEqC data pin and several LNB control pins changed between firmware versions. Always verify which firmware version is running before driving GPIO pins directly.
|
||||
</Aside>
|
||||
|
||||
## Port 0 / Port A (SFR 0x80, IOA)
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Stock Firmware">
|
||||
|
||||
| Pin | v2.06 | Rev.2 v2.10 | v2.13 | Notes |
|
||||
|-----|-------|-------------|-------|-------|
|
||||
| P0.0 | -- | LNB control (0x97) | **DiSEqC data** | DiSEqC data pin moved across versions |
|
||||
| P0.1 | Power enable | Power enable | Power enable | BCM4500 power supply enable |
|
||||
| P0.2 | Power disable | Power disable (init=0x84) | Power disable | BCM4500 power supply disable |
|
||||
| P0.3 | **22 kHz tone** | **22 kHz tone** | **22 kHz tone** | Gates external 22 kHz oscillator (all versions) |
|
||||
| P0.4 | **LNB 13V/18V** | **LNB 13V/18V** + DiSEqC data | **LNB 13V/18V** | Also SET_DN_SWITCH bit-bang (all versions) |
|
||||
| P0.5 | **BCM4500 RESET** | GPIO status input (0x98) | **BCM4500 RESET** | Reset and feedback pin |
|
||||
| P0.6 | -- | GPIO control (0x97) | -- | LNB control on Rev.2 only |
|
||||
| P0.7 | **DiSEqC data** | Streaming indicator | Streaming indicator | DiSEqC data on v2.06 only |
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Custom v3.01.0">
|
||||
|
||||
| Pin | Custom v3.01.0 | Notes |
|
||||
|-----|----------------|-------|
|
||||
| P0.0 | -- | Unused |
|
||||
| P0.1 | **Power enable** | BCM4500 power supply enable (HIGH = on) |
|
||||
| P0.2 | **Power disable** | BCM4500 power supply disable (LOW = off) |
|
||||
| P0.3 | **22 kHz tone** | Gates external 22 kHz oscillator |
|
||||
| P0.4 | **LNB 13V/18V** | Voltage select: LOW = 13V, HIGH = 18V |
|
||||
| P0.5 | **BCM4500 RESET** | LOW = reset asserted, HIGH = released |
|
||||
| P0.6 | -- | Unused |
|
||||
| P0.7 | **DiSEqC data** + streaming | DiSEqC data (matches v2.06); also streaming indicator |
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Port 3 / Port D (SFR 0xB0, IOD)
|
||||
|
||||
These pins are consistent across all firmware versions:
|
||||
|
||||
| Pin | Function | Active State | Notes |
|
||||
|-----|----------|-------------|-------|
|
||||
| P3.0 | Init HIGH | -- | Set during initialization |
|
||||
| P3.4 | GPIO control | -- | Used by Rev.2 `FUN_CODE_1fcf` |
|
||||
| P3.5 | **TS_EN** | LOW | Transport stream enable: LOW = active, HIGH = idle |
|
||||
| P3.6 | **DVB mode** | -- | BCM4500 mode select; DiSEqC port direction (Rev.2) |
|
||||
| P3.7 | BCM4500 control | HIGH (idle) | De-asserted (HIGH) when streaming stops |
|
||||
|
||||
<Aside type="caution" title="P3.5 During Boot">
|
||||
P3.5 (TS_EN) must remain HIGH (idle) during the boot sequence and demodulator initialization. Driving it LOW before the BCM4500 is fully initialized may produce undefined behavior on the transport stream bus.
|
||||
</Aside>
|
||||
|
||||
## Port B (XRAM-mapped IOB)
|
||||
|
||||
Port B is used exclusively by the internal debug commands 0x96--0x98. These commands are not used by the Linux or Windows drivers.
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="v2.06 / v2.13">
|
||||
|
||||
| Pin | Function | Command |
|
||||
|-----|----------|---------|
|
||||
| IOB.0 | GPIO status input | GET_GPIO_STATUS (0x98) |
|
||||
| IOB.1 | LNB control line 1 | SET_GPIO_PINS (0x97) |
|
||||
| IOB.2 | LNB control line 2 | SET_GPIO_PINS (0x97) |
|
||||
| IOB.3 | LNB GPIO mode | SET_LNB_GPIO_MODE (0x96) |
|
||||
| IOB.4 | -- | Unused |
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Rev.2 v2.10">
|
||||
|
||||
| Pin | Function | Command |
|
||||
|-----|----------|---------|
|
||||
| IOB.0 | -- | Unused |
|
||||
| IOB.1 | -- | Unused |
|
||||
| IOB.2 | -- | Unused |
|
||||
| IOB.3 | -- | Unused |
|
||||
| IOB.4 | LNB GPIO mode + control | SET_LNB_GPIO_MODE (0x96) + SET_GPIO_PINS (0x97) |
|
||||
|
||||
Rev.2 moved LNB control from Port B to Port A (P0.6, P0.0).
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## DiSEqC Data Pin Summary
|
||||
|
||||
The DiSEqC data pin assignment is the most significant change between firmware versions. The carrier pin (P0.3) remains constant.
|
||||
|
||||
| Firmware Version | Data Pin | Carrier Pin |
|
||||
|-----------------|----------|-------------|
|
||||
| v2.06 | P0.7 | P0.3 |
|
||||
| Rev.2 v2.10 | P0.4 | P0.3 |
|
||||
| v2.13 | P0.0 | P0.3 |
|
||||
| Custom v3.01.0 | P0.7 | P0.3 |
|
||||
|
||||
The data pin is used only internally by the firmware's Manchester encoding logic. It controls whether the 22 kHz carrier gate signal (on P0.3) is cut short or held for the full bit period during DiSEqC transmission. See [DiSEqC Protocol](/lnb-diseqc/diseqc-protocol/) for the encoding details.
|
||||
|
||||
## Initial GPIO State
|
||||
|
||||
On power-up, the FX2 initialization code sets:
|
||||
|
||||
| Register | Value | Decode |
|
||||
|----------|-------|--------|
|
||||
| IOA (P0) | 0x84 | P0.7=1 (idle), P0.2=1 (power disable active) |
|
||||
| IOD (P3) | 0xE1 | P3.7:5=1 (all control lines idle), P3.0=1 |
|
||||
| OEA | 0xBE | P0.1-5,7 configured as outputs |
|
||||
|
||||
These initial states ensure:
|
||||
- The BCM4500 is held in reset (P0.5 driven by output enable, but P0 init has it low after OEA is set)
|
||||
- The transport stream bus is idle (P3.5 = HIGH)
|
||||
- The streaming indicator is off (P0.7 = HIGH)
|
||||
- All BCM4500 control lines are de-asserted (P3.7:5 = 1)
|
||||
|
||||
## Streaming GPIO State Changes
|
||||
|
||||
| Pin | SFR | Direction | During Streaming | Streaming Stopped | Function |
|
||||
|-----|-----|-----------|-----------------|-------------------|----------|
|
||||
| P0.2 | 0x80 | Output | Set during init | -- | BCM4500 config |
|
||||
| P0.7 | 0x80 | Output | **LOW** | HIGH | Streaming status indicator |
|
||||
| P3.5 | 0xB0 | Output | Pulsed LOW | HIGH | BCM4500 TS_EN (transport stream enable) |
|
||||
| P3.6 | 0xB0 | Output | Controlled | HIGH | BCM4500 DVB mode control |
|
||||
| P3.7 | 0xB0 | Output | Controlled | HIGH | BCM4500 control line |
|
||||
|
|
|
|||
|
|
@ -1,98 +1,98 @@
|
|||
---
|
||||
title: Hardware Overview
|
||||
description: Board layout, chip identification, supported modulations, and architecture of the Genpix SkyWalker-1 DVB-S USB 2.0 receiver.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Badge, Aside, CardGrid, Card } from '@astrojs/starlight/components';
|
||||
|
||||
The Genpix SkyWalker-1 is a standalone USB 2.0 DVB-S satellite receiver built around two primary ICs: a Cypress FX2LP USB microcontroller and a Broadcom BCM4500 satellite demodulator. The FX2 handles all USB communication, LNB control, and DiSEqC signaling, while the BCM4500 performs RF demodulation, forward error correction, and outputs an MPEG-2 transport stream on an 8-bit parallel bus.
|
||||
|
||||
<Aside type="tip" title="Get a SkyWalker-1 and support a good cause">
|
||||
Genpix Electronics is selling remaining new-in-box SkyWalker-1 units on [eBay](https://www.ebay.com/itm/196142001978), with all net proceeds benefiting the [Leleka Foundation](https://www.leleka.org/) — a charity supporting individuals affected by the conflict in Ukraine.
|
||||
</Aside>
|
||||
|
||||
## Core Components
|
||||
|
||||
| Component | Part Number | Role |
|
||||
|-----------|-------------|------|
|
||||
| MCU | Cypress CY7C68013A (FX2LP) | USB 2.0 Hi-Speed controller, 8051 core at 48 MHz |
|
||||
| Demodulator | Broadcom BCM4500 | DVB-S / Turbo / DCII / DSS demodulator, 128-pin MQFP |
|
||||
| EEPROM | 24Cxx-family (I2C address 0x51) | FX2 firmware storage, serial number, calibration data |
|
||||
| Tuner/LNB | Unknown IC (I2C address 0x10) | Tuner or LNB controller on shared I2C bus |
|
||||
|
||||
## Board Block Diagram
|
||||
|
||||
```
|
||||
+--[ I2C EEPROM 0x51 ]
|
||||
|
|
||||
USB 2.0 HS | I2C Bus (400 kHz)
|
||||
Host PC <----> [ CY7C68013A FX2LP ] <-----> [ BCM4500 Demod 0x08 ]
|
||||
| 8051 @ 48 MHz | |
|
||||
| GPIF Engine |<-----------+ 8-bit parallel TS
|
||||
| EP2 Bulk IN |
|
||||
| GPIO (P0/P3) |---> [ 22 kHz Osc ] ---> LNB/Coax
|
||||
| |---> [ LNB Voltage Ctrl ]
|
||||
+-----------------+
|
||||
|
|
||||
+--[ Tuner/LNB IC 0x10 ]
|
||||
```
|
||||
|
||||
The GPIF engine inside the FX2 transfers the BCM4500's transport stream output directly into USB bulk endpoint EP2 with zero firmware intervention in the data path. This hardware-managed pipeline provides approximately 5x bandwidth headroom over the maximum DVB-S transport stream rate.
|
||||
|
||||
## Supported Modulations
|
||||
|
||||
| Index | Modulation | Constant | FEC Family |
|
||||
|-------|-----------|----------|------------|
|
||||
| 0 | DVB-S QPSK <Badge text="Standard" variant="note" /> | `ADV_MOD_DVB_QPSK` | Viterbi + Reed-Solomon |
|
||||
| 1 | Turbo-coded QPSK <Badge text="Turbo" variant="success" /> | `ADV_MOD_TURBO_QPSK` | Turbo |
|
||||
| 2 | Turbo-coded 8PSK <Badge text="Turbo" variant="success" /> | `ADV_MOD_TURBO_8PSK` | Turbo |
|
||||
| 3 | Turbo-coded 16QAM <Badge text="Turbo" variant="success" /> | `ADV_MOD_TURBO_16QAM` | Turbo |
|
||||
| 4 | Digicipher II Combo <Badge text="DCII" variant="caution" /> | `ADV_MOD_DCII_C_QPSK` | DCII |
|
||||
| 5 | Digicipher II I-stream (split) <Badge text="DCII" variant="caution" /> | `ADV_MOD_DCII_I_QPSK` | DCII |
|
||||
| 6 | Digicipher II Q-stream (split) <Badge text="DCII" variant="caution" /> | `ADV_MOD_DCII_Q_QPSK` | DCII |
|
||||
| 7 | Digicipher II Offset QPSK <Badge text="DCII" variant="caution" /> | `ADV_MOD_DCII_C_OQPSK` | DCII |
|
||||
| 8 | DSS QPSK <Badge text="DSS" variant="note" /> | `ADV_MOD_DSS_QPSK` | Viterbi + Reed-Solomon |
|
||||
| 9 | DVB-S BPSK <Badge text="Standard" variant="note" /> | `ADV_MOD_DVB_BPSK` | Viterbi + Reed-Solomon |
|
||||
|
||||
<Aside type="caution" title="DVB-S2 Not Supported">
|
||||
DVB-S2 is **not** supported by the SkyWalker-1. This is a fundamental hardware limitation of the BCM4500 demodulator silicon -- it was designed before the DVB-S2 standard was ratified (March 2005) and contains no LDPC or BCH decoder hardware. No firmware update can add DVB-S2 support. See the [DVB-S2 Incompatibility](/driver/dvb-s2/) page for the full technical analysis.
|
||||
</Aside>
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
<CardGrid>
|
||||
<Card title="USB Interface" icon="laptop">
|
||||
EP0 for vendor commands (tuning, LNB control, status). EP2 for bulk MPEG-2 transport stream data. VID `0x09C0`, PID `0x0203`. See [USB Interface](/usb/interface/).
|
||||
</Card>
|
||||
<Card title="BCM4500 Demodulator" icon="seti:config">
|
||||
Indirect register access via I2C (0xA6/0xA7/0xA8 protocol). Two FEC decoder paths: turbo codes and legacy Viterbi/Reed-Solomon. See [Demodulator Interface](/bcm4500/demodulator/).
|
||||
</Card>
|
||||
<Card title="GPIF Streaming" icon="right-arrow">
|
||||
Hardware-managed data path. 8-bit parallel bus from BCM4500 to EP2 FIFO via GPIF master read. AUTOIN auto-commits full packets to USB. See [GPIF Streaming](/bcm4500/gpif-streaming/).
|
||||
</Card>
|
||||
<Card title="I2C Bus" icon="setting">
|
||||
400 kHz bus connecting FX2 to BCM4500 (0x08), tuner IC (0x10), and EEPROM (0x51). Bit-banged DiSEqC on separate GPIO pins. See [I2C Bus Architecture](/i2c/bus-architecture/).
|
||||
</Card>
|
||||
</CardGrid>
|
||||
|
||||
## FEC Architecture
|
||||
|
||||
The BCM4500 contains two distinct FEC decoder paths:
|
||||
|
||||
1. **Advanced Modulation Turbo FEC Decoder** -- Iterative turbo code decoder supporting QPSK (rates 1/4, 1/2, 3/4), 8PSK (rates 2/3, 3/4, 5/6, 8/9), 16QAM (rate 3/4), with Reed-Solomon (t=10) outer code.
|
||||
|
||||
2. **Legacy DVB/DIRECTV/DCII-Compliant FEC Decoder** -- Concatenated Viterbi inner decoder (convolutional code, rates 1/2 through 7/8) plus Reed-Solomon outer decoder.
|
||||
|
||||
There is no LDPC or BCH decoder hardware. The turbo FEC codes are Broadcom/EchoStar proprietary and are not part of any open standard.
|
||||
|
||||
## Firmware
|
||||
|
||||
The SkyWalker-1 boots from an on-board I2C EEPROM containing firmware in Cypress C2 format. No host-side firmware files are required. Multiple stock firmware versions have been identified:
|
||||
|
||||
| Firmware | Version ID | Build Date | Notes |
|
||||
|----------|-----------|------------|-------|
|
||||
| v2.06.04 | 0x020604 | 2007-07-13 | Original release, 61 functions |
|
||||
| v2.13.01 | 0x020D01 | 2010-03-12 | Latest revision, 82-88 functions |
|
||||
| Custom v3.01.0 | 0x030100 | 2026-02-12 | Open-source SDCC + fx2lib, RAM-loaded |
|
||||
|
||||
See [Firmware Version Comparison](/firmware/version-comparison/) for a full analysis of the differences between stock firmware versions.
|
||||
---
|
||||
title: Hardware Overview
|
||||
description: Board layout, chip identification, supported modulations, and architecture of the Genpix SkyWalker-1 DVB-S USB 2.0 receiver.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Badge, Aside, CardGrid, Card } from '@astrojs/starlight/components';
|
||||
|
||||
The Genpix SkyWalker-1 is a standalone USB 2.0 DVB-S satellite receiver built around two primary ICs: a Cypress FX2LP USB microcontroller and a Broadcom BCM4500 satellite demodulator. The FX2 handles all USB communication, LNB control, and DiSEqC signaling, while the BCM4500 performs RF demodulation, forward error correction, and outputs an MPEG-2 transport stream on an 8-bit parallel bus.
|
||||
|
||||
<Aside type="tip" title="Get a SkyWalker-1 and support a good cause">
|
||||
Genpix Electronics is selling remaining new-in-box SkyWalker-1 units on [eBay](https://www.ebay.com/itm/196142001978), with all net proceeds benefiting the [Leleka Foundation](https://www.leleka.org/) — a charity supporting individuals affected by the conflict in Ukraine.
|
||||
</Aside>
|
||||
|
||||
## Core Components
|
||||
|
||||
| Component | Part Number | Role |
|
||||
|-----------|-------------|------|
|
||||
| MCU | Cypress CY7C68013A (FX2LP) | USB 2.0 Hi-Speed controller, 8051 core at 48 MHz |
|
||||
| Demodulator | Broadcom BCM4500 | DVB-S / Turbo / DCII / DSS demodulator, 128-pin MQFP |
|
||||
| EEPROM | 24Cxx-family (I2C address 0x51) | FX2 firmware storage, serial number, calibration data |
|
||||
| Tuner/LNB | Unknown IC (I2C address 0x10) | Tuner or LNB controller on shared I2C bus |
|
||||
|
||||
## Board Block Diagram
|
||||
|
||||
```
|
||||
+--[ I2C EEPROM 0x51 ]
|
||||
|
|
||||
USB 2.0 HS | I2C Bus (400 kHz)
|
||||
Host PC <----> [ CY7C68013A FX2LP ] <-----> [ BCM4500 Demod 0x08 ]
|
||||
| 8051 @ 48 MHz | |
|
||||
| GPIF Engine |<-----------+ 8-bit parallel TS
|
||||
| EP2 Bulk IN |
|
||||
| GPIO (P0/P3) |---> [ 22 kHz Osc ] ---> LNB/Coax
|
||||
| |---> [ LNB Voltage Ctrl ]
|
||||
+-----------------+
|
||||
|
|
||||
+--[ Tuner/LNB IC 0x10 ]
|
||||
```
|
||||
|
||||
The GPIF engine inside the FX2 transfers the BCM4500's transport stream output directly into USB bulk endpoint EP2 with zero firmware intervention in the data path. This hardware-managed pipeline provides approximately 5x bandwidth headroom over the maximum DVB-S transport stream rate.
|
||||
|
||||
## Supported Modulations
|
||||
|
||||
| Index | Modulation | Constant | FEC Family |
|
||||
|-------|-----------|----------|------------|
|
||||
| 0 | DVB-S QPSK <Badge text="Standard" variant="note" /> | `ADV_MOD_DVB_QPSK` | Viterbi + Reed-Solomon |
|
||||
| 1 | Turbo-coded QPSK <Badge text="Turbo" variant="success" /> | `ADV_MOD_TURBO_QPSK` | Turbo |
|
||||
| 2 | Turbo-coded 8PSK <Badge text="Turbo" variant="success" /> | `ADV_MOD_TURBO_8PSK` | Turbo |
|
||||
| 3 | Turbo-coded 16QAM <Badge text="Turbo" variant="success" /> | `ADV_MOD_TURBO_16QAM` | Turbo |
|
||||
| 4 | Digicipher II Combo <Badge text="DCII" variant="caution" /> | `ADV_MOD_DCII_C_QPSK` | DCII |
|
||||
| 5 | Digicipher II I-stream (split) <Badge text="DCII" variant="caution" /> | `ADV_MOD_DCII_I_QPSK` | DCII |
|
||||
| 6 | Digicipher II Q-stream (split) <Badge text="DCII" variant="caution" /> | `ADV_MOD_DCII_Q_QPSK` | DCII |
|
||||
| 7 | Digicipher II Offset QPSK <Badge text="DCII" variant="caution" /> | `ADV_MOD_DCII_C_OQPSK` | DCII |
|
||||
| 8 | DSS QPSK <Badge text="DSS" variant="note" /> | `ADV_MOD_DSS_QPSK` | Viterbi + Reed-Solomon |
|
||||
| 9 | DVB-S BPSK <Badge text="Standard" variant="note" /> | `ADV_MOD_DVB_BPSK` | Viterbi + Reed-Solomon |
|
||||
|
||||
<Aside type="caution" title="DVB-S2 Not Supported">
|
||||
DVB-S2 is **not** supported by the SkyWalker-1. This is a fundamental hardware limitation of the BCM4500 demodulator silicon -- it was designed before the DVB-S2 standard was ratified (March 2005) and contains no LDPC or BCH decoder hardware. No firmware update can add DVB-S2 support. See the [DVB-S2 Incompatibility](/driver/dvb-s2/) page for the full technical analysis.
|
||||
</Aside>
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
<CardGrid>
|
||||
<Card title="USB Interface" icon="laptop">
|
||||
EP0 for vendor commands (tuning, LNB control, status). EP2 for bulk MPEG-2 transport stream data. VID `0x09C0`, PID `0x0203`. See [USB Interface](/usb/interface/).
|
||||
</Card>
|
||||
<Card title="BCM4500 Demodulator" icon="seti:config">
|
||||
Indirect register access via I2C (0xA6/0xA7/0xA8 protocol). Two FEC decoder paths: turbo codes and legacy Viterbi/Reed-Solomon. See [Demodulator Interface](/bcm4500/demodulator/).
|
||||
</Card>
|
||||
<Card title="GPIF Streaming" icon="right-arrow">
|
||||
Hardware-managed data path. 8-bit parallel bus from BCM4500 to EP2 FIFO via GPIF master read. AUTOIN auto-commits full packets to USB. See [GPIF Streaming](/bcm4500/gpif-streaming/).
|
||||
</Card>
|
||||
<Card title="I2C Bus" icon="setting">
|
||||
400 kHz bus connecting FX2 to BCM4500 (0x08), tuner IC (0x10), and EEPROM (0x51). Bit-banged DiSEqC on separate GPIO pins. See [I2C Bus Architecture](/i2c/bus-architecture/).
|
||||
</Card>
|
||||
</CardGrid>
|
||||
|
||||
## FEC Architecture
|
||||
|
||||
The BCM4500 contains two distinct FEC decoder paths:
|
||||
|
||||
1. **Advanced Modulation Turbo FEC Decoder** -- Iterative turbo code decoder supporting QPSK (rates 1/4, 1/2, 3/4), 8PSK (rates 2/3, 3/4, 5/6, 8/9), 16QAM (rate 3/4), with Reed-Solomon (t=10) outer code.
|
||||
|
||||
2. **Legacy DVB/DIRECTV/DCII-Compliant FEC Decoder** -- Concatenated Viterbi inner decoder (convolutional code, rates 1/2 through 7/8) plus Reed-Solomon outer decoder.
|
||||
|
||||
There is no LDPC or BCH decoder hardware. The turbo FEC codes are Broadcom/EchoStar proprietary and are not part of any open standard.
|
||||
|
||||
## Firmware
|
||||
|
||||
The SkyWalker-1 boots from an on-board I2C EEPROM containing firmware in Cypress C2 format. No host-side firmware files are required. Multiple stock firmware versions have been identified:
|
||||
|
||||
| Firmware | Version ID | Build Date | Notes |
|
||||
|----------|-----------|------------|-------|
|
||||
| v2.06.04 | 0x020604 | 2007-07-13 | Original release, 61 functions |
|
||||
| v2.13.01 | 0x020D01 | 2010-03-12 | Latest revision, 82-88 functions |
|
||||
| Custom v3.01.0 | 0x030100 | 2026-02-12 | Open-source SDCC + fx2lib, RAM-loaded |
|
||||
|
||||
See [Firmware Version Comparison](/firmware/version-comparison/) for a full analysis of the differences between stock firmware versions.
|
||||
|
|
|
|||
|
|
@ -1,110 +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
|
||||
---
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,94 +1,94 @@
|
|||
---
|
||||
title: RF Specifications
|
||||
description: RF input parameters, LNB voltage and current limits, and symbol rate range for the SkyWalker-1.
|
||||
---
|
||||
|
||||
import { Aside } from '@astrojs/starlight/components';
|
||||
|
||||
## RF Input Parameters
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| IF frequency range | 950 -- 2150 MHz |
|
||||
| Symbol rate | 256 Ksps -- 30 Msps |
|
||||
| Input connector | IEC F-type female |
|
||||
|
||||
The IF frequency is the intermediate frequency after LNB downconversion. The host computes it as `(RF_freq - LO_freq) * multiplier` and sends it in the [TUNE_8PSK](/bcm4500/tuning-protocol/) command payload.
|
||||
|
||||
## LNB Power Supply
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| LNB voltage (standard) | 13V / 18V |
|
||||
| LNB voltage (boosted) | 14V / 19V (with USE_EXTRA_VOLT) |
|
||||
| Maximum continuous current | 450 mA |
|
||||
| Maximum burst current | 750 mA |
|
||||
|
||||
<Aside type="caution" title="Current Limits">
|
||||
750 mA is the maximum **non-continuous** (several minutes) allowable load. To avoid overheating the on-board voltage regulator, do not exceed **450 mA** for permanent load. Exceeding these limits may damage the SkyWalker-1 hardware.
|
||||
</Aside>
|
||||
|
||||
LNB voltage is controlled via GPIO P0.4 on all firmware versions. The voltage selection determines the polarization:
|
||||
|
||||
| wValue | Voltage | GPIO P0.4 | Polarization |
|
||||
|--------|---------|-----------|-------------|
|
||||
| 0 | 13V | LOW | Vertical / Circular-Right |
|
||||
| 1 | 18V | HIGH | Horizontal / Circular-Left |
|
||||
|
||||
The USE_EXTRA_VOLT command (0x94) enables a +1V boost for long cable runs by toggling bit 3 of the LNB control register at XRAM 0xE0B6 (0x62 = normal, 0x6A = boosted). See [Vendor Commands](/usb/vendor-commands/) for the command interface.
|
||||
|
||||
## Switch Control
|
||||
|
||||
The SkyWalker-1 supports multiple satellite switching protocols:
|
||||
|
||||
| Protocol | Implementation | Command |
|
||||
|----------|---------------|---------|
|
||||
| 22 kHz tone | GPIO P0.3 gates external oscillator | SET_22KHZ_TONE (0x8C) |
|
||||
| Tone Burst (mini DiSEqC) | Timer2-based carrier gating | SEND_DISEQC_COMMAND (0x8D) |
|
||||
| DiSEqC 1.0 | Timer2 Manchester encoding | SEND_DISEQC_COMMAND (0x8D) |
|
||||
| DiSEqC 1.2 | Timer2 Manchester encoding | SEND_DISEQC_COMMAND (0x8D) |
|
||||
| Legacy Dish Network | 7-bit serial bit-bang on P0.4 | SET_DN_SWITCH (0x8F) |
|
||||
|
||||
## 22 kHz Tone
|
||||
|
||||
The 22 kHz tone signal is generated by an external oscillator on the PCB, gated by GPIO P0.3. The firmware does not generate the 22 kHz carrier directly -- it only enables or disables the oscillator output.
|
||||
|
||||
| wValue | State | GPIO P0.3 | Band Selection |
|
||||
|--------|-------|-----------|------|
|
||||
| 0 | OFF | LOW | Low band (9.75 GHz LO on universal LNB) |
|
||||
| 1 | ON | HIGH | High band (10.6 GHz LO on universal LNB) |
|
||||
|
||||
## Signal Path
|
||||
|
||||
```
|
||||
Satellite
|
||||
|
|
||||
v
|
||||
[ LNB on Dish ] <-- 13V/18V + 22kHz from SkyWalker-1
|
||||
|
|
||||
| Coax (950-2150 MHz IF)
|
||||
v
|
||||
[ SkyWalker-1 F-connector ]
|
||||
|
|
||||
v
|
||||
[ BCM4500 Demodulator ] -- demod + FEC decode
|
||||
|
|
||||
| 8-bit parallel MPEG-2 TS
|
||||
v
|
||||
[ FX2 GPIF Engine ] -- zero-copy DMA to EP2
|
||||
|
|
||||
| USB 2.0 High-Speed Bulk
|
||||
v
|
||||
[ Host PC ]
|
||||
```
|
||||
|
||||
The USB/GPIF data path has substantial headroom over the satellite link throughput:
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| USB 2.0 HS bulk (practical) | ~280 Mbps (~35 MB/s) |
|
||||
| GPIF engine (theoretical) | 48 MHz x 8 bits = 384 Mbps |
|
||||
| Typical DVB-S TS rate | 1--5 MB/s |
|
||||
| Maximum symbol rate (30 Msps) | ~58 Mbps |
|
||||
|
||||
The bottleneck for all supported modulation modes is the satellite link, not the USB data path.
|
||||
---
|
||||
title: RF Specifications
|
||||
description: RF input parameters, LNB voltage and current limits, and symbol rate range for the SkyWalker-1.
|
||||
---
|
||||
|
||||
import { Aside } from '@astrojs/starlight/components';
|
||||
|
||||
## RF Input Parameters
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| IF frequency range | 950 -- 2150 MHz |
|
||||
| Symbol rate | 256 Ksps -- 30 Msps |
|
||||
| Input connector | IEC F-type female |
|
||||
|
||||
The IF frequency is the intermediate frequency after LNB downconversion. The host computes it as `(RF_freq - LO_freq) * multiplier` and sends it in the [TUNE_8PSK](/bcm4500/tuning-protocol/) command payload.
|
||||
|
||||
## LNB Power Supply
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| LNB voltage (standard) | 13V / 18V |
|
||||
| LNB voltage (boosted) | 14V / 19V (with USE_EXTRA_VOLT) |
|
||||
| Maximum continuous current | 450 mA |
|
||||
| Maximum burst current | 750 mA |
|
||||
|
||||
<Aside type="caution" title="Current Limits">
|
||||
750 mA is the maximum **non-continuous** (several minutes) allowable load. To avoid overheating the on-board voltage regulator, do not exceed **450 mA** for permanent load. Exceeding these limits may damage the SkyWalker-1 hardware.
|
||||
</Aside>
|
||||
|
||||
LNB voltage is controlled via GPIO P0.4 on all firmware versions. The voltage selection determines the polarization:
|
||||
|
||||
| wValue | Voltage | GPIO P0.4 | Polarization |
|
||||
|--------|---------|-----------|-------------|
|
||||
| 0 | 13V | LOW | Vertical / Circular-Right |
|
||||
| 1 | 18V | HIGH | Horizontal / Circular-Left |
|
||||
|
||||
The USE_EXTRA_VOLT command (0x94) enables a +1V boost for long cable runs by toggling bit 3 of the LNB control register at XRAM 0xE0B6 (0x62 = normal, 0x6A = boosted). See [Vendor Commands](/usb/vendor-commands/) for the command interface.
|
||||
|
||||
## Switch Control
|
||||
|
||||
The SkyWalker-1 supports multiple satellite switching protocols:
|
||||
|
||||
| Protocol | Implementation | Command |
|
||||
|----------|---------------|---------|
|
||||
| 22 kHz tone | GPIO P0.3 gates external oscillator | SET_22KHZ_TONE (0x8C) |
|
||||
| Tone Burst (mini DiSEqC) | Timer2-based carrier gating | SEND_DISEQC_COMMAND (0x8D) |
|
||||
| DiSEqC 1.0 | Timer2 Manchester encoding | SEND_DISEQC_COMMAND (0x8D) |
|
||||
| DiSEqC 1.2 | Timer2 Manchester encoding | SEND_DISEQC_COMMAND (0x8D) |
|
||||
| Legacy Dish Network | 7-bit serial bit-bang on P0.4 | SET_DN_SWITCH (0x8F) |
|
||||
|
||||
## 22 kHz Tone
|
||||
|
||||
The 22 kHz tone signal is generated by an external oscillator on the PCB, gated by GPIO P0.3. The firmware does not generate the 22 kHz carrier directly -- it only enables or disables the oscillator output.
|
||||
|
||||
| wValue | State | GPIO P0.3 | Band Selection |
|
||||
|--------|-------|-----------|------|
|
||||
| 0 | OFF | LOW | Low band (9.75 GHz LO on universal LNB) |
|
||||
| 1 | ON | HIGH | High band (10.6 GHz LO on universal LNB) |
|
||||
|
||||
## Signal Path
|
||||
|
||||
```
|
||||
Satellite
|
||||
|
|
||||
v
|
||||
[ LNB on Dish ] <-- 13V/18V + 22kHz from SkyWalker-1
|
||||
|
|
||||
| Coax (950-2150 MHz IF)
|
||||
v
|
||||
[ SkyWalker-1 F-connector ]
|
||||
|
|
||||
v
|
||||
[ BCM4500 Demodulator ] -- demod + FEC decode
|
||||
|
|
||||
| 8-bit parallel MPEG-2 TS
|
||||
v
|
||||
[ FX2 GPIF Engine ] -- zero-copy DMA to EP2
|
||||
|
|
||||
| USB 2.0 High-Speed Bulk
|
||||
v
|
||||
[ Host PC ]
|
||||
```
|
||||
|
||||
The USB/GPIF data path has substantial headroom over the satellite link throughput:
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| USB 2.0 HS bulk (practical) | ~280 Mbps (~35 MB/s) |
|
||||
| GPIF engine (theoretical) | 48 MHz x 8 bits = 384 Mbps |
|
||||
| Typical DVB-S TS rate | 1--5 MB/s |
|
||||
| Maximum symbol rate (30 Msps) | ~58 Mbps |
|
||||
|
||||
The bottleneck for all supported modulation modes is the satellite link, not the USB data path.
|
||||
|
|
|
|||
|
|
@ -1,156 +1,156 @@
|
|||
---
|
||||
title: I2C Bus Architecture
|
||||
description: FX2 I2C controller details, bus topology, device addresses, and the combined write-read protocol.
|
||||
---
|
||||
|
||||
import { Aside, Steps } from '@astrojs/starlight/components';
|
||||
|
||||
The SkyWalker-1 uses a single I2C bus connecting the FX2 microcontroller (master) to three slave devices. The FX2's hardware I2C controller handles all bus transactions through XRAM-mapped registers.
|
||||
|
||||
## FX2 I2C Controller
|
||||
|
||||
| SFR | Address | Function |
|
||||
|-----|---------|----------|
|
||||
| I2CS | 0xE678 (XRAM) | I2C control/status register |
|
||||
| I2DAT | 0xE679 (XRAM) | I2C data register |
|
||||
| I2CTL | 0xE67A (XRAM) | I2C control (speed selection) |
|
||||
|
||||
### I2CS Control/Status Bits
|
||||
|
||||
| Bit | Name | Function |
|
||||
|-----|------|----------|
|
||||
| bmSTART | bit 7 | Write: initiate START condition |
|
||||
| bmSTOP | bit 6 | Write: initiate STOP condition |
|
||||
| bmLASTRD | bit 5 | Write: signal last read byte (NACK after next read) |
|
||||
| bmDONE | bit 2 | Read: byte transfer complete |
|
||||
| bmACK | bit 1 | Read: ACK received from slave |
|
||||
| bmBERR | bit 0 | Read: bus error detected |
|
||||
|
||||
## Bus Speed
|
||||
|
||||
The I2C bus operates at 400 kHz. The speed is set through two mechanisms:
|
||||
|
||||
1. **C2 EEPROM header** -- Config byte at offset 7 = 0x40, which the FX2 boot ROM uses to configure the I2C speed during initial EEPROM read.
|
||||
2. **Firmware/software** -- I2CTL = `bm400KHZ` (written by the custom firmware and init table).
|
||||
|
||||
## Known Bus Devices
|
||||
|
||||
| 7-bit Address | Wire Write | Wire Read | Identity |
|
||||
|---------------|-----------|----------|----------|
|
||||
| 0x08 | 0x10 | 0x11 | BCM4500 demodulator |
|
||||
| 0x10 | 0x20 | 0x21 | Tuner or LNB controller |
|
||||
| 0x51 | 0xA2 | 0xA3 | Configuration EEPROM (24Cxx-family) |
|
||||
|
||||
These addresses were confirmed via the I2C_BUS_SCAN command (0xB4) in custom firmware v3.01.0, which probes all 7-bit addresses from 0x01 to 0x77.
|
||||
|
||||
### BCM4500 Demodulator (0x08)
|
||||
|
||||
The primary device for all demodulation operations. Register access uses the [indirect register protocol](/bcm4500/demodulator/) through registers 0xA6, 0xA7, and 0xA8. Direct registers 0xA2, 0xA4, and 0xF9 provide status information.
|
||||
|
||||
The v2.13 firmware also probes alternate addresses 0x7F and 0x3F at startup to detect which demodulator variant is present.
|
||||
|
||||
### Tuner/LNB Controller (0x10)
|
||||
|
||||
Likely a tuner IC or LNB controller. In normal operation, the BCM4500 accesses this device internally for tuning. It is also directly addressable on the shared I2C bus. The kernel driver does not directly communicate with this device.
|
||||
|
||||
### Configuration EEPROM (0x51)
|
||||
|
||||
A 24Cxx-family serial EEPROM that stores:
|
||||
- Device serial number (read by GET_SERIAL_NUMBER, 0x93)
|
||||
- Hardware platform ID (read by GET_FPGA_VERS, 0x95)
|
||||
- Calibration data
|
||||
|
||||
## Bus Topology
|
||||
|
||||
```
|
||||
FX2 I2C Master
|
||||
(I2CS/I2DAT/I2CTL)
|
||||
|
|
||||
+----------+-----------+
|
||||
| | |
|
||||
BCM4500 Tuner/LNB EEPROM
|
||||
(0x08) (0x10) (0x51)
|
||||
```
|
||||
|
||||
All three devices share the same SCL/SDA lines. The FX2's hardware I2C controller manages bus arbitration and clock stretching detection.
|
||||
|
||||
## Combined Write-Read Protocol
|
||||
|
||||
All BCM4500 register reads use the I2C combined write-read protocol with a repeated START condition. This is required because the BCM4500 uses a register-addressed protocol where the register number must be sent in a write phase before the read phase.
|
||||
|
||||
```
|
||||
Complete I2C transaction for reading register 0xA2 from device 0x08:
|
||||
|
||||
Phase 1 (Write):
|
||||
[S] [0x10] [ACK] [0xA2] [ACK]
|
||||
| | | | |
|
||||
| | | | +-- BCM4500 ACKs register address
|
||||
| | | +--------- Register address byte
|
||||
| | +---------------- BCM4500 ACKs its device address
|
||||
| +----------------------- Device address (0x08 << 1) = 0x10 (write)
|
||||
+---------------------------- START condition
|
||||
|
||||
Phase 2 (Read with Repeated START):
|
||||
[Sr] [0x11] [ACK] [DATA] [NACK] [P]
|
||||
| | | | | |
|
||||
| | | | | +-- STOP condition
|
||||
| | | | +--------- Master NACKs (last byte)
|
||||
| | | +---------------- Register data
|
||||
| | +----------------------- BCM4500 ACKs its device address
|
||||
| +------------------------------ Device address (0x08 << 1 | 1) = 0x11 (read)
|
||||
+------------------------------------ REPEATED START (no STOP between phases)
|
||||
```
|
||||
|
||||
The repeated START (Sr) is essential. A STOP between phases would release the bus, and the BCM4500 would lose the register address context.
|
||||
|
||||
### FX2 SFR Sequence for Combined Read
|
||||
|
||||
```c title="Combined Write-Read Implementation"
|
||||
// Phase 1: Write register address
|
||||
I2CS |= bmSTART; // Generate START
|
||||
I2DAT = 0x10; // Write: device addr + W
|
||||
// wait bmDONE, check bmACK
|
||||
I2DAT = 0xA2; // Write: register address
|
||||
// wait bmDONE, check bmACK
|
||||
|
||||
// Phase 2: Read with repeated START
|
||||
I2CS |= bmSTART; // Generate REPEATED START (no STOP first!)
|
||||
I2DAT = 0x11; // Write: device addr + R
|
||||
// wait bmDONE, check bmACK
|
||||
I2CS |= bmLASTRD; // Signal this is the last read byte
|
||||
tmp = I2DAT; // Dummy read (triggers first clock burst)
|
||||
// wait bmDONE
|
||||
I2CS |= bmSTOP; // Generate STOP after reading
|
||||
data = I2DAT; // Read actual data byte
|
||||
// wait bmSTOP to clear
|
||||
```
|
||||
|
||||
<Aside type="danger" title="STOP Condition Warning">
|
||||
Do NOT issue `I2CS |= bmSTOP` when no I2C transaction is active. This corrupts the FX2 I2C controller state machine. See [I2C STOP Corruption Bug](/i2c/stop-corruption-bug/) for the full analysis.
|
||||
</Aside>
|
||||
|
||||
## Timeout Protection
|
||||
|
||||
The fx2lib I2C functions poll `bmDONE` with no timeout:
|
||||
|
||||
```c title="fx2lib Original Code (Vulnerable)"
|
||||
while (!(I2CS & bmDONE) && !cancel_i2c_trans);
|
||||
```
|
||||
|
||||
Since `cancel_i2c_trans` is never set during normal operation, these loops are effectively infinite. If a slave holds SCL low or a bus error prevents bmDONE from asserting, the firmware hangs.
|
||||
|
||||
The custom firmware replaces all fx2lib I2C functions with timeout-protected wrappers:
|
||||
|
||||
```c title="Timeout-Protected I2C Wait"
|
||||
#define I2C_TIMEOUT 6000
|
||||
|
||||
static BOOL i2c_wait_done(void) {
|
||||
WORD timeout = I2C_TIMEOUT;
|
||||
while (!(I2CS & bmDONE)) {
|
||||
if (--timeout == 0) return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
```
|
||||
|
||||
A WORD counter of 6000 decremented in a tight SDCC-compiled loop at 48 MHz gives approximately 5--10 ms per wait. At 400 kHz I2C, a single byte transfer takes 22.5 us, so the timeout provides over 200x margin for normal operations while bounding the worst case.
|
||||
---
|
||||
title: I2C Bus Architecture
|
||||
description: FX2 I2C controller details, bus topology, device addresses, and the combined write-read protocol.
|
||||
---
|
||||
|
||||
import { Aside, Steps } from '@astrojs/starlight/components';
|
||||
|
||||
The SkyWalker-1 uses a single I2C bus connecting the FX2 microcontroller (master) to three slave devices. The FX2's hardware I2C controller handles all bus transactions through XRAM-mapped registers.
|
||||
|
||||
## FX2 I2C Controller
|
||||
|
||||
| SFR | Address | Function |
|
||||
|-----|---------|----------|
|
||||
| I2CS | 0xE678 (XRAM) | I2C control/status register |
|
||||
| I2DAT | 0xE679 (XRAM) | I2C data register |
|
||||
| I2CTL | 0xE67A (XRAM) | I2C control (speed selection) |
|
||||
|
||||
### I2CS Control/Status Bits
|
||||
|
||||
| Bit | Name | Function |
|
||||
|-----|------|----------|
|
||||
| bmSTART | bit 7 | Write: initiate START condition |
|
||||
| bmSTOP | bit 6 | Write: initiate STOP condition |
|
||||
| bmLASTRD | bit 5 | Write: signal last read byte (NACK after next read) |
|
||||
| bmDONE | bit 2 | Read: byte transfer complete |
|
||||
| bmACK | bit 1 | Read: ACK received from slave |
|
||||
| bmBERR | bit 0 | Read: bus error detected |
|
||||
|
||||
## Bus Speed
|
||||
|
||||
The I2C bus operates at 400 kHz. The speed is set through two mechanisms:
|
||||
|
||||
1. **C2 EEPROM header** -- Config byte at offset 7 = 0x40, which the FX2 boot ROM uses to configure the I2C speed during initial EEPROM read.
|
||||
2. **Firmware/software** -- I2CTL = `bm400KHZ` (written by the custom firmware and init table).
|
||||
|
||||
## Known Bus Devices
|
||||
|
||||
| 7-bit Address | Wire Write | Wire Read | Identity |
|
||||
|---------------|-----------|----------|----------|
|
||||
| 0x08 | 0x10 | 0x11 | BCM4500 demodulator |
|
||||
| 0x10 | 0x20 | 0x21 | Tuner or LNB controller |
|
||||
| 0x51 | 0xA2 | 0xA3 | Configuration EEPROM (24Cxx-family) |
|
||||
|
||||
These addresses were confirmed via the I2C_BUS_SCAN command (0xB4) in custom firmware v3.01.0, which probes all 7-bit addresses from 0x01 to 0x77.
|
||||
|
||||
### BCM4500 Demodulator (0x08)
|
||||
|
||||
The primary device for all demodulation operations. Register access uses the [indirect register protocol](/bcm4500/demodulator/) through registers 0xA6, 0xA7, and 0xA8. Direct registers 0xA2, 0xA4, and 0xF9 provide status information.
|
||||
|
||||
The v2.13 firmware also probes alternate addresses 0x7F and 0x3F at startup to detect which demodulator variant is present.
|
||||
|
||||
### Tuner/LNB Controller (0x10)
|
||||
|
||||
Likely a tuner IC or LNB controller. In normal operation, the BCM4500 accesses this device internally for tuning. It is also directly addressable on the shared I2C bus. The kernel driver does not directly communicate with this device.
|
||||
|
||||
### Configuration EEPROM (0x51)
|
||||
|
||||
A 24Cxx-family serial EEPROM that stores:
|
||||
- Device serial number (read by GET_SERIAL_NUMBER, 0x93)
|
||||
- Hardware platform ID (read by GET_FPGA_VERS, 0x95)
|
||||
- Calibration data
|
||||
|
||||
## Bus Topology
|
||||
|
||||
```
|
||||
FX2 I2C Master
|
||||
(I2CS/I2DAT/I2CTL)
|
||||
|
|
||||
+----------+-----------+
|
||||
| | |
|
||||
BCM4500 Tuner/LNB EEPROM
|
||||
(0x08) (0x10) (0x51)
|
||||
```
|
||||
|
||||
All three devices share the same SCL/SDA lines. The FX2's hardware I2C controller manages bus arbitration and clock stretching detection.
|
||||
|
||||
## Combined Write-Read Protocol
|
||||
|
||||
All BCM4500 register reads use the I2C combined write-read protocol with a repeated START condition. This is required because the BCM4500 uses a register-addressed protocol where the register number must be sent in a write phase before the read phase.
|
||||
|
||||
```
|
||||
Complete I2C transaction for reading register 0xA2 from device 0x08:
|
||||
|
||||
Phase 1 (Write):
|
||||
[S] [0x10] [ACK] [0xA2] [ACK]
|
||||
| | | | |
|
||||
| | | | +-- BCM4500 ACKs register address
|
||||
| | | +--------- Register address byte
|
||||
| | +---------------- BCM4500 ACKs its device address
|
||||
| +----------------------- Device address (0x08 << 1) = 0x10 (write)
|
||||
+---------------------------- START condition
|
||||
|
||||
Phase 2 (Read with Repeated START):
|
||||
[Sr] [0x11] [ACK] [DATA] [NACK] [P]
|
||||
| | | | | |
|
||||
| | | | | +-- STOP condition
|
||||
| | | | +--------- Master NACKs (last byte)
|
||||
| | | +---------------- Register data
|
||||
| | +----------------------- BCM4500 ACKs its device address
|
||||
| +------------------------------ Device address (0x08 << 1 | 1) = 0x11 (read)
|
||||
+------------------------------------ REPEATED START (no STOP between phases)
|
||||
```
|
||||
|
||||
The repeated START (Sr) is essential. A STOP between phases would release the bus, and the BCM4500 would lose the register address context.
|
||||
|
||||
### FX2 SFR Sequence for Combined Read
|
||||
|
||||
```c title="Combined Write-Read Implementation"
|
||||
// Phase 1: Write register address
|
||||
I2CS |= bmSTART; // Generate START
|
||||
I2DAT = 0x10; // Write: device addr + W
|
||||
// wait bmDONE, check bmACK
|
||||
I2DAT = 0xA2; // Write: register address
|
||||
// wait bmDONE, check bmACK
|
||||
|
||||
// Phase 2: Read with repeated START
|
||||
I2CS |= bmSTART; // Generate REPEATED START (no STOP first!)
|
||||
I2DAT = 0x11; // Write: device addr + R
|
||||
// wait bmDONE, check bmACK
|
||||
I2CS |= bmLASTRD; // Signal this is the last read byte
|
||||
tmp = I2DAT; // Dummy read (triggers first clock burst)
|
||||
// wait bmDONE
|
||||
I2CS |= bmSTOP; // Generate STOP after reading
|
||||
data = I2DAT; // Read actual data byte
|
||||
// wait bmSTOP to clear
|
||||
```
|
||||
|
||||
<Aside type="danger" title="STOP Condition Warning">
|
||||
Do NOT issue `I2CS |= bmSTOP` when no I2C transaction is active. This corrupts the FX2 I2C controller state machine. See [I2C STOP Corruption Bug](/i2c/stop-corruption-bug/) for the full analysis.
|
||||
</Aside>
|
||||
|
||||
## Timeout Protection
|
||||
|
||||
The fx2lib I2C functions poll `bmDONE` with no timeout:
|
||||
|
||||
```c title="fx2lib Original Code (Vulnerable)"
|
||||
while (!(I2CS & bmDONE) && !cancel_i2c_trans);
|
||||
```
|
||||
|
||||
Since `cancel_i2c_trans` is never set during normal operation, these loops are effectively infinite. If a slave holds SCL low or a bus error prevents bmDONE from asserting, the firmware hangs.
|
||||
|
||||
The custom firmware replaces all fx2lib I2C functions with timeout-protected wrappers:
|
||||
|
||||
```c title="Timeout-Protected I2C Wait"
|
||||
#define I2C_TIMEOUT 6000
|
||||
|
||||
static BOOL i2c_wait_done(void) {
|
||||
WORD timeout = I2C_TIMEOUT;
|
||||
while (!(I2CS & bmDONE)) {
|
||||
if (--timeout == 0) return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
```
|
||||
|
||||
A WORD counter of 6000 decremented in a tight SDCC-compiled loop at 48 MHz gives approximately 5--10 ms per wait. At 400 kHz I2C, a single byte transfer takes 22.5 us, so the timeout provides over 200x margin for normal operations while bounding the worst case.
|
||||
|
|
|
|||
|
|
@ -1,167 +1,167 @@
|
|||
---
|
||||
title: I2C STOP Corruption Bug
|
||||
description: Root cause analysis of the spurious I2C STOP condition that corrupted the FX2 controller state during boot.
|
||||
---
|
||||
|
||||
import { Steps, Aside, Badge } from '@astrojs/starlight/components';
|
||||
|
||||
During development of the custom firmware v3.01.0, the BOOT_8PSK (0x89) command caused the FX2 to hang for over 10 seconds, making the USB device completely unresponsive. The root cause was traced to a single line of code: a spurious I2C STOP condition issued when no transaction was active.
|
||||
|
||||
<Aside type="danger" title="Critical Hardware Bug">
|
||||
Sending `I2CS |= bmSTOP` when no I2C transaction is active (no prior START, bus idle) corrupts the FX2 I2C controller's internal state machine. The bmSTOP bit may not self-clear, and subsequent START conditions fail to detect ACK from slaves. The Cypress TRM does not explicitly warn against this.
|
||||
</Aside>
|
||||
|
||||
## The Problem
|
||||
|
||||
The boot function originally included a "bus reset" step before any I2C communication:
|
||||
|
||||
```c title="Broken Code"
|
||||
I2CS |= bmSTOP;
|
||||
i2c_wait_stop();
|
||||
```
|
||||
|
||||
This pattern appears in various FX2 example code and seems reasonable -- send a STOP to ensure the I2C bus is in a known idle state before starting fresh. On the FX2's I2C controller hardware, this is incorrect.
|
||||
|
||||
## Root Cause Analysis
|
||||
|
||||
The root cause was discovered through a series of incremental debug modes added to the BOOT_8PSK handler. Each mode executes a subset of the full boot sequence, isolating which step introduces the failure.
|
||||
|
||||
### Debug Mode Results
|
||||
|
||||
| wValue | Action | Result | Key Observation |
|
||||
|--------|--------|--------|-----------------|
|
||||
| 0x80 | No-op: return status only | <Badge text="Works" variant="success" /> | Baseline |
|
||||
| 0x81 | GPIO + power + delays (no I2C) | <Badge text="Works" variant="success" /> | Power sequencing is correct |
|
||||
| 0x82 | GPIO + power + bmSTOP + I2C probe | <Badge text="Fails" variant="danger" /> | bmSTOP corrupts I2C |
|
||||
| 0x83 | GPIO + power + bmSTOP + probe + init | <Badge text="Fails" variant="danger" /> | Same root cause |
|
||||
| 0x84 | I2C probe only (chip already powered) | <Badge text="Works" variant="success" /> | BCM4500 is alive |
|
||||
| 0x85 | GPIO + power + probe (**no bmSTOP**) | <Badge text="Works" variant="success" /> | Confirms bmSTOP is the cause |
|
||||
|
||||
### Three Key Observations
|
||||
|
||||
1. **Mode 0x82 fails but mode 0x85 succeeds.** These modes are identical except that 0x82 issues `I2CS |= bmSTOP` before the probe and 0x85 does not. The bmSTOP is the only difference.
|
||||
|
||||
2. **Mode 0x84 succeeds immediately after 0x82 fails.** Mode 0x84 performs a plain I2C combined read with no GPIO manipulation or bus reset. If called after a failed 0x82, it succeeds. This proves the BCM4500 was alive and responding -- the FX2 I2C controller was in a bad state, not the bus or the slave.
|
||||
|
||||
3. **Raw I2C reads via command 0xB5 succeed after 0x82 fails.** Command 0xB5 uses the same `i2c_combined_read` function. Running it from the host after a failed 0x82 returns valid data from the BCM4500.
|
||||
|
||||
## What Happens Inside the FX2
|
||||
|
||||
The FX2's I2C master controller is a hardware peripheral accessed through the I2CS, I2DAT, and I2CTL SFRs. The controller implements an I2C state machine in silicon. Writing bmSTOP to I2CS instructs the hardware to generate a STOP condition (SDA rising while SCL is high).
|
||||
|
||||
When no I2C transaction is active -- no prior START has been issued, and the bus is idle -- writing bmSTOP puts the controller into an inconsistent internal state:
|
||||
|
||||
- The bmSTOP bit may not clear properly (it is supposed to self-clear when the STOP condition completes)
|
||||
- Subsequent START conditions fail to generate proper clock sequences
|
||||
- ACK detection from slaves becomes unreliable
|
||||
|
||||
The Cypress TRM describes STOP as a step that follows a completed read or write transaction. It is not documented as a standalone bus-reset mechanism.
|
||||
|
||||
## The Fix
|
||||
|
||||
The fix is a single deletion. Remove the spurious STOP from the boot sequence:
|
||||
|
||||
```c title="Before (Broken)"
|
||||
/* "Reset" I2C bus */
|
||||
I2CS |= bmSTOP;
|
||||
i2c_wait_stop();
|
||||
```
|
||||
|
||||
```c title="After (Correct)"
|
||||
/* NOTE: Do NOT send I2CS bmSTOP here. Sending STOP when no
|
||||
* transaction is active corrupts the FX2 I2C controller state,
|
||||
* causing subsequent START+ACK detection to fail. The I2C bus
|
||||
* will be in a clean state when we reach the probe step --
|
||||
* any prior transaction ended with STOP. */
|
||||
```
|
||||
|
||||
The correct approach is to simply proceed with a new START condition. If the bus is idle (after power-on or after the previous transaction completed normally), the START succeeds and the controller enters its normal operating state. The hardware handles bus arbitration automatically.
|
||||
|
||||
## Corrected Boot Sequence
|
||||
|
||||
```c title="bcm4500_boot() -- Corrected"
|
||||
static BOOL bcm4500_boot(void) {
|
||||
boot_stage = 1;
|
||||
cancel_i2c_trans = FALSE;
|
||||
|
||||
/* P3.7, P3.6, P3.5 HIGH (idle state for control lines) */
|
||||
IOD |= 0xE0;
|
||||
|
||||
/* Assert BCM4500 hardware RESET (P0.5 LOW) */
|
||||
OEA |= PIN_BCM_RESET;
|
||||
IOA &= ~PIN_BCM_RESET;
|
||||
|
||||
/* No I2CS bmSTOP here -- see note above */
|
||||
|
||||
/* Power on: P0.1 HIGH (enable), P0.2 LOW (disable off) */
|
||||
OEA |= (PIN_PWR_EN | PIN_PWR_DIS);
|
||||
IOA = (IOA & ~PIN_PWR_DIS) | PIN_PWR_EN;
|
||||
|
||||
boot_stage = 2;
|
||||
delay(30); /* power settle */
|
||||
|
||||
IOA |= PIN_BCM_RESET; /* release reset */
|
||||
delay(50); /* BCM4500 POR + mask ROM boot */
|
||||
|
||||
boot_stage = 3;
|
||||
/* I2C probe -- if this fails, the chip didn't respond */
|
||||
if (!bcm_direct_read(BCM_REG_STATUS, &i2c_rd[0]))
|
||||
return FALSE;
|
||||
|
||||
/* ... register init blocks follow ... */
|
||||
}
|
||||
```
|
||||
|
||||
## Boot Results After Fix
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Boot time | ~90 ms total |
|
||||
| config_status | 0x03 (STARTED + FW_LOADED) |
|
||||
| boot_stage | 0xFF (COMPLETE) |
|
||||
| Direct registers 0xA2-0xA8 | All return 0x02 (powered, not locked) |
|
||||
| Signal lock | 0x00 (no lock -- dish not aimed) |
|
||||
| USB responsiveness | No hang; fully responsive throughout |
|
||||
|
||||
## Test Scripts
|
||||
|
||||
The investigation was driven by a series of test scripts in the `tools/` directory:
|
||||
|
||||
| Script | Purpose |
|
||||
|--------|---------|
|
||||
| `test_boot_debug.py` | Sends debug modes 0x80--0x83 sequentially |
|
||||
| `test_i2c_debug.py` | Powers on via 0x81, runs bus scans, tests probe timing |
|
||||
| `test_i2c_isolate.py` | Tests whether re-reset or insufficient delay causes failure |
|
||||
| `test_i2c_pinpoint.py` | The definitive test: compares modes 0x84, 0x85, and 0x82 |
|
||||
|
||||
## Timeout Protection
|
||||
|
||||
Even with the bmSTOP fix, timeout protection on all I2C operations is essential. The FX2's I2C controller has no hardware timeout -- if a slave holds SCL low (clock stretching) or a fault prevents bmDONE from asserting, the firmware spins forever.
|
||||
|
||||
The custom firmware replaces all fx2lib I2C functions with timeout-protected wrappers:
|
||||
|
||||
```c title="Timeout-Protected I2C Waits"
|
||||
#define I2C_TIMEOUT 6000
|
||||
|
||||
static BOOL i2c_wait_done(void) {
|
||||
WORD timeout = I2C_TIMEOUT;
|
||||
while (!(I2CS & bmDONE)) {
|
||||
if (--timeout == 0) return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL i2c_wait_stop(void) {
|
||||
WORD timeout = I2C_TIMEOUT;
|
||||
while (I2CS & bmSTOP) {
|
||||
if (--timeout == 0) return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
```
|
||||
|
||||
A WORD counter of 6000 at 48 MHz gives approximately 5--10 ms per wait, providing over 200x margin above the 22.5 us required for a single byte transfer at 400 kHz.
|
||||
|
||||
<Aside type="tip" title="FX2 Recovery">
|
||||
If the firmware hangs due to I2C issues, the device can be recovered without a physical unplug. The FX2's CPUCS register (0xE600) is accessible via the boot ROM's `bRequest=0xA0` handler, which runs at hardware priority. See [Boot Sequence](/usb/boot-sequence/) for the recovery procedure.
|
||||
</Aside>
|
||||
---
|
||||
title: I2C STOP Corruption Bug
|
||||
description: Root cause analysis of the spurious I2C STOP condition that corrupted the FX2 controller state during boot.
|
||||
---
|
||||
|
||||
import { Steps, Aside, Badge } from '@astrojs/starlight/components';
|
||||
|
||||
During development of the custom firmware v3.01.0, the BOOT_8PSK (0x89) command caused the FX2 to hang for over 10 seconds, making the USB device completely unresponsive. The root cause was traced to a single line of code: a spurious I2C STOP condition issued when no transaction was active.
|
||||
|
||||
<Aside type="danger" title="Critical Hardware Bug">
|
||||
Sending `I2CS |= bmSTOP` when no I2C transaction is active (no prior START, bus idle) corrupts the FX2 I2C controller's internal state machine. The bmSTOP bit may not self-clear, and subsequent START conditions fail to detect ACK from slaves. The Cypress TRM does not explicitly warn against this.
|
||||
</Aside>
|
||||
|
||||
## The Problem
|
||||
|
||||
The boot function originally included a "bus reset" step before any I2C communication:
|
||||
|
||||
```c title="Broken Code"
|
||||
I2CS |= bmSTOP;
|
||||
i2c_wait_stop();
|
||||
```
|
||||
|
||||
This pattern appears in various FX2 example code and seems reasonable -- send a STOP to ensure the I2C bus is in a known idle state before starting fresh. On the FX2's I2C controller hardware, this is incorrect.
|
||||
|
||||
## Root Cause Analysis
|
||||
|
||||
The root cause was discovered through a series of incremental debug modes added to the BOOT_8PSK handler. Each mode executes a subset of the full boot sequence, isolating which step introduces the failure.
|
||||
|
||||
### Debug Mode Results
|
||||
|
||||
| wValue | Action | Result | Key Observation |
|
||||
|--------|--------|--------|-----------------|
|
||||
| 0x80 | No-op: return status only | <Badge text="Works" variant="success" /> | Baseline |
|
||||
| 0x81 | GPIO + power + delays (no I2C) | <Badge text="Works" variant="success" /> | Power sequencing is correct |
|
||||
| 0x82 | GPIO + power + bmSTOP + I2C probe | <Badge text="Fails" variant="danger" /> | bmSTOP corrupts I2C |
|
||||
| 0x83 | GPIO + power + bmSTOP + probe + init | <Badge text="Fails" variant="danger" /> | Same root cause |
|
||||
| 0x84 | I2C probe only (chip already powered) | <Badge text="Works" variant="success" /> | BCM4500 is alive |
|
||||
| 0x85 | GPIO + power + probe (**no bmSTOP**) | <Badge text="Works" variant="success" /> | Confirms bmSTOP is the cause |
|
||||
|
||||
### Three Key Observations
|
||||
|
||||
1. **Mode 0x82 fails but mode 0x85 succeeds.** These modes are identical except that 0x82 issues `I2CS |= bmSTOP` before the probe and 0x85 does not. The bmSTOP is the only difference.
|
||||
|
||||
2. **Mode 0x84 succeeds immediately after 0x82 fails.** Mode 0x84 performs a plain I2C combined read with no GPIO manipulation or bus reset. If called after a failed 0x82, it succeeds. This proves the BCM4500 was alive and responding -- the FX2 I2C controller was in a bad state, not the bus or the slave.
|
||||
|
||||
3. **Raw I2C reads via command 0xB5 succeed after 0x82 fails.** Command 0xB5 uses the same `i2c_combined_read` function. Running it from the host after a failed 0x82 returns valid data from the BCM4500.
|
||||
|
||||
## What Happens Inside the FX2
|
||||
|
||||
The FX2's I2C master controller is a hardware peripheral accessed through the I2CS, I2DAT, and I2CTL SFRs. The controller implements an I2C state machine in silicon. Writing bmSTOP to I2CS instructs the hardware to generate a STOP condition (SDA rising while SCL is high).
|
||||
|
||||
When no I2C transaction is active -- no prior START has been issued, and the bus is idle -- writing bmSTOP puts the controller into an inconsistent internal state:
|
||||
|
||||
- The bmSTOP bit may not clear properly (it is supposed to self-clear when the STOP condition completes)
|
||||
- Subsequent START conditions fail to generate proper clock sequences
|
||||
- ACK detection from slaves becomes unreliable
|
||||
|
||||
The Cypress TRM describes STOP as a step that follows a completed read or write transaction. It is not documented as a standalone bus-reset mechanism.
|
||||
|
||||
## The Fix
|
||||
|
||||
The fix is a single deletion. Remove the spurious STOP from the boot sequence:
|
||||
|
||||
```c title="Before (Broken)"
|
||||
/* "Reset" I2C bus */
|
||||
I2CS |= bmSTOP;
|
||||
i2c_wait_stop();
|
||||
```
|
||||
|
||||
```c title="After (Correct)"
|
||||
/* NOTE: Do NOT send I2CS bmSTOP here. Sending STOP when no
|
||||
* transaction is active corrupts the FX2 I2C controller state,
|
||||
* causing subsequent START+ACK detection to fail. The I2C bus
|
||||
* will be in a clean state when we reach the probe step --
|
||||
* any prior transaction ended with STOP. */
|
||||
```
|
||||
|
||||
The correct approach is to simply proceed with a new START condition. If the bus is idle (after power-on or after the previous transaction completed normally), the START succeeds and the controller enters its normal operating state. The hardware handles bus arbitration automatically.
|
||||
|
||||
## Corrected Boot Sequence
|
||||
|
||||
```c title="bcm4500_boot() -- Corrected"
|
||||
static BOOL bcm4500_boot(void) {
|
||||
boot_stage = 1;
|
||||
cancel_i2c_trans = FALSE;
|
||||
|
||||
/* P3.7, P3.6, P3.5 HIGH (idle state for control lines) */
|
||||
IOD |= 0xE0;
|
||||
|
||||
/* Assert BCM4500 hardware RESET (P0.5 LOW) */
|
||||
OEA |= PIN_BCM_RESET;
|
||||
IOA &= ~PIN_BCM_RESET;
|
||||
|
||||
/* No I2CS bmSTOP here -- see note above */
|
||||
|
||||
/* Power on: P0.1 HIGH (enable), P0.2 LOW (disable off) */
|
||||
OEA |= (PIN_PWR_EN | PIN_PWR_DIS);
|
||||
IOA = (IOA & ~PIN_PWR_DIS) | PIN_PWR_EN;
|
||||
|
||||
boot_stage = 2;
|
||||
delay(30); /* power settle */
|
||||
|
||||
IOA |= PIN_BCM_RESET; /* release reset */
|
||||
delay(50); /* BCM4500 POR + mask ROM boot */
|
||||
|
||||
boot_stage = 3;
|
||||
/* I2C probe -- if this fails, the chip didn't respond */
|
||||
if (!bcm_direct_read(BCM_REG_STATUS, &i2c_rd[0]))
|
||||
return FALSE;
|
||||
|
||||
/* ... register init blocks follow ... */
|
||||
}
|
||||
```
|
||||
|
||||
## Boot Results After Fix
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Boot time | ~90 ms total |
|
||||
| config_status | 0x03 (STARTED + FW_LOADED) |
|
||||
| boot_stage | 0xFF (COMPLETE) |
|
||||
| Direct registers 0xA2-0xA8 | All return 0x02 (powered, not locked) |
|
||||
| Signal lock | 0x00 (no lock -- dish not aimed) |
|
||||
| USB responsiveness | No hang; fully responsive throughout |
|
||||
|
||||
## Test Scripts
|
||||
|
||||
The investigation was driven by a series of test scripts in the `tools/` directory:
|
||||
|
||||
| Script | Purpose |
|
||||
|--------|---------|
|
||||
| `test_boot_debug.py` | Sends debug modes 0x80--0x83 sequentially |
|
||||
| `test_i2c_debug.py` | Powers on via 0x81, runs bus scans, tests probe timing |
|
||||
| `test_i2c_isolate.py` | Tests whether re-reset or insufficient delay causes failure |
|
||||
| `test_i2c_pinpoint.py` | The definitive test: compares modes 0x84, 0x85, and 0x82 |
|
||||
|
||||
## Timeout Protection
|
||||
|
||||
Even with the bmSTOP fix, timeout protection on all I2C operations is essential. The FX2's I2C controller has no hardware timeout -- if a slave holds SCL low (clock stretching) or a fault prevents bmDONE from asserting, the firmware spins forever.
|
||||
|
||||
The custom firmware replaces all fx2lib I2C functions with timeout-protected wrappers:
|
||||
|
||||
```c title="Timeout-Protected I2C Waits"
|
||||
#define I2C_TIMEOUT 6000
|
||||
|
||||
static BOOL i2c_wait_done(void) {
|
||||
WORD timeout = I2C_TIMEOUT;
|
||||
while (!(I2CS & bmDONE)) {
|
||||
if (--timeout == 0) return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL i2c_wait_stop(void) {
|
||||
WORD timeout = I2C_TIMEOUT;
|
||||
while (I2CS & bmSTOP) {
|
||||
if (--timeout == 0) return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
```
|
||||
|
||||
A WORD counter of 6000 at 48 MHz gives approximately 5--10 ms per wait, providing over 200x margin above the 22.5 us required for a single byte transfer at 400 kHz.
|
||||
|
||||
<Aside type="tip" title="FX2 Recovery">
|
||||
If the firmware hangs due to I2C issues, the device can be recovered without a physical unplug. The FX2's CPUCS register (0xE600) is accessible via the boot ROM's `bRequest=0xA0` handler, which runs at hardware priority. See [Boot Sequence](/usb/boot-sequence/) for the recovery procedure.
|
||||
</Aside>
|
||||
|
|
|
|||
|
|
@ -1,83 +1,83 @@
|
|||
---
|
||||
title: SkyWalker-1 Documentation
|
||||
description: Reverse-engineered technical documentation for the Genpix SkyWalker-1 DVB-S USB 2.0 satellite receiver.
|
||||
template: splash
|
||||
hero:
|
||||
title: SkyWalker-1
|
||||
tagline: Open-source reverse-engineered documentation for the Genpix SkyWalker-1 DVB-S USB 2.0 satellite receiver
|
||||
image:
|
||||
file: ../../assets/skywalker-1-hero.jpg
|
||||
alt: Genpix Electronics SkyWalker-1 DVB-S USB satellite receiver
|
||||
actions:
|
||||
- text: Get Started
|
||||
link: /hardware/overview/
|
||||
icon: right-arrow
|
||||
- text: Master Reference
|
||||
link: /reference/master-reference/
|
||||
variant: minimal
|
||||
---
|
||||
|
||||
import { CardGrid, Card, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
The Genpix SkyWalker-1 is a standalone USB 2.0 DVB-S satellite receiver built around a Cypress FX2LP microcontroller and Broadcom BCM4500 demodulator. This project documents its complete internal architecture through Linux kernel driver analysis, Ghidra firmware reverse engineering across five firmware images, Windows BDA driver source review, and custom firmware development.
|
||||
|
||||
The result is a fully open-source technical reference covering every vendor command, register, GPIO pin, and data path -- from the USB control transfers down to the I2C bit timing on the BCM4500's indirect register protocol.
|
||||
|
||||
**Supported modulations:** DVB-S (QPSK), Turbo QPSK, Turbo 8PSK, Turbo 16QAM, Digicipher II (Combo, Split I/Q, Offset QPSK), DSS (QPSK), and DVB-S BPSK. Symbol rates from 256 Ksps to 30 Msps across a 950--2150 MHz IF range.
|
||||
|
||||
<Aside type="note">
|
||||
DVB-S2 is **not supported**. The BCM4500 predates the DVB-S2 standard and contains no LDPC or BCH decoder hardware. This is a silicon limitation -- no firmware update can change it. See the [DVB-S2 Incompatibility](/driver/dvb-s2/) section for the full investigation.
|
||||
</Aside>
|
||||
|
||||
## Explore the Documentation
|
||||
|
||||
<CardGrid>
|
||||
<Card title="Hardware" icon="laptop">
|
||||
FX2LP + BCM4500 architecture, GPIO pin maps, RF specifications, and board block diagram.
|
||||
|
||||
[Hardware Overview](/hardware/overview/)
|
||||
</Card>
|
||||
<Card title="USB Interface" icon="puzzle">
|
||||
Vendor command reference, boot sequence, configuration status byte, and endpoint layout.
|
||||
|
||||
[USB Interface](/usb/interface/)
|
||||
</Card>
|
||||
<Card title="BCM4500" icon="setting">
|
||||
Demodulator I2C protocol, indirect register access, tuning sequence, and GPIF streaming path.
|
||||
|
||||
[Demodulator](/bcm4500/demodulator/)
|
||||
</Card>
|
||||
<Card title="LNB and DiSEqC" icon="rocket">
|
||||
LNB voltage and tone control, DiSEqC 1.0/1.2 protocol, legacy Dish Network switches.
|
||||
|
||||
[LNB Control](/lnb-diseqc/lnb-control/)
|
||||
</Card>
|
||||
<Card title="Firmware" icon="document">
|
||||
Version comparison across five firmware images, custom v3.01.0 development, and storage formats.
|
||||
|
||||
[Firmware Versions](/firmware/version-comparison/)
|
||||
</Card>
|
||||
<Card title="Tools" icon="seti">
|
||||
Python utilities for RAM loading, EEPROM flashing, transponder tuning, transport stream analysis, and hardware debugging.
|
||||
|
||||
[Firmware Loader](/tools/firmware-loader/)
|
||||
</Card>
|
||||
</CardGrid>
|
||||
|
||||
## Project Scope
|
||||
|
||||
This documentation covers the complete reverse engineering of the SkyWalker-1 hardware and firmware:
|
||||
|
||||
- **5 firmware images** decompiled in Ghidra: v2.06.04, Rev.2 v2.10.04, and three v2.13 sub-variants
|
||||
- **30 vendor USB commands** fully decoded with parameter formats and version-specific behavior
|
||||
- **Custom firmware** (v3.01.0) built from scratch with SDCC + fx2lib, including 7 new diagnostic commands
|
||||
- **13 Python tools** for firmware management, satellite tuning, transport stream analysis, and hardware debugging
|
||||
- **I2C STOP corruption bug** discovered and fixed through incremental debug modes on the FX2 controller
|
||||
|
||||
All source code, firmware binaries, analysis reports, and this documentation are available in the project repository.
|
||||
|
||||
## Support Genpix and the Leleka Foundation
|
||||
|
||||
Genpix Electronics is selling their remaining SkyWalker-1 inventory on eBay, with all net proceeds going to the [Leleka Foundation](https://www.leleka.org/) -- a US-based charitable organization providing medical and social support to individuals wounded and displaced by the conflict in Ukraine.
|
||||
|
||||
If you'd like to own one of these capable little receivers and support a good cause at the same time, check the [eBay listing from the official Genpix seller](https://www.ebay.com/itm/196142001978). New-in-box units ship via USPS Priority Mail. Thank you to Genpix for building the SkyWalker-1 and for directing their remaining stock toward something meaningful.
|
||||
---
|
||||
title: SkyWalker-1 Documentation
|
||||
description: Reverse-engineered technical documentation for the Genpix SkyWalker-1 DVB-S USB 2.0 satellite receiver.
|
||||
template: splash
|
||||
hero:
|
||||
title: SkyWalker-1
|
||||
tagline: Open-source reverse-engineered documentation for the Genpix SkyWalker-1 DVB-S USB 2.0 satellite receiver
|
||||
image:
|
||||
file: ../../assets/skywalker-1-hero.jpg
|
||||
alt: Genpix Electronics SkyWalker-1 DVB-S USB satellite receiver
|
||||
actions:
|
||||
- text: Get Started
|
||||
link: /hardware/overview/
|
||||
icon: right-arrow
|
||||
- text: Master Reference
|
||||
link: /reference/master-reference/
|
||||
variant: minimal
|
||||
---
|
||||
|
||||
import { CardGrid, Card, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
The Genpix SkyWalker-1 is a standalone USB 2.0 DVB-S satellite receiver built around a Cypress FX2LP microcontroller and Broadcom BCM4500 demodulator. This project documents its complete internal architecture through Linux kernel driver analysis, Ghidra firmware reverse engineering across five firmware images, Windows BDA driver source review, and custom firmware development.
|
||||
|
||||
The result is a fully open-source technical reference covering every vendor command, register, GPIO pin, and data path -- from the USB control transfers down to the I2C bit timing on the BCM4500's indirect register protocol.
|
||||
|
||||
**Supported modulations:** DVB-S (QPSK), Turbo QPSK, Turbo 8PSK, Turbo 16QAM, Digicipher II (Combo, Split I/Q, Offset QPSK), DSS (QPSK), and DVB-S BPSK. Symbol rates from 256 Ksps to 30 Msps across a 950--2150 MHz IF range.
|
||||
|
||||
<Aside type="note">
|
||||
DVB-S2 is **not supported**. The BCM4500 predates the DVB-S2 standard and contains no LDPC or BCH decoder hardware. This is a silicon limitation -- no firmware update can change it. See the [DVB-S2 Incompatibility](/driver/dvb-s2/) section for the full investigation.
|
||||
</Aside>
|
||||
|
||||
## Explore the Documentation
|
||||
|
||||
<CardGrid>
|
||||
<Card title="Hardware" icon="laptop">
|
||||
FX2LP + BCM4500 architecture, GPIO pin maps, RF specifications, and board block diagram.
|
||||
|
||||
[Hardware Overview](/hardware/overview/)
|
||||
</Card>
|
||||
<Card title="USB Interface" icon="puzzle">
|
||||
Vendor command reference, boot sequence, configuration status byte, and endpoint layout.
|
||||
|
||||
[USB Interface](/usb/interface/)
|
||||
</Card>
|
||||
<Card title="BCM4500" icon="setting">
|
||||
Demodulator I2C protocol, indirect register access, tuning sequence, and GPIF streaming path.
|
||||
|
||||
[Demodulator](/bcm4500/demodulator/)
|
||||
</Card>
|
||||
<Card title="LNB and DiSEqC" icon="rocket">
|
||||
LNB voltage and tone control, DiSEqC 1.0/1.2 protocol, legacy Dish Network switches.
|
||||
|
||||
[LNB Control](/lnb-diseqc/lnb-control/)
|
||||
</Card>
|
||||
<Card title="Firmware" icon="document">
|
||||
Version comparison across five firmware images, custom v3.01.0 development, and storage formats.
|
||||
|
||||
[Firmware Versions](/firmware/version-comparison/)
|
||||
</Card>
|
||||
<Card title="Tools" icon="seti">
|
||||
Python utilities for RAM loading, EEPROM flashing, transponder tuning, transport stream analysis, and hardware debugging.
|
||||
|
||||
[Firmware Loader](/tools/firmware-loader/)
|
||||
</Card>
|
||||
</CardGrid>
|
||||
|
||||
## Project Scope
|
||||
|
||||
This documentation covers the complete reverse engineering of the SkyWalker-1 hardware and firmware:
|
||||
|
||||
- **5 firmware images** decompiled in Ghidra: v2.06.04, Rev.2 v2.10.04, and three v2.13 sub-variants
|
||||
- **30 vendor USB commands** fully decoded with parameter formats and version-specific behavior
|
||||
- **Custom firmware** (v3.01.0) built from scratch with SDCC + fx2lib, including 7 new diagnostic commands
|
||||
- **13 Python tools** for firmware management, satellite tuning, transport stream analysis, and hardware debugging
|
||||
- **I2C STOP corruption bug** discovered and fixed through incremental debug modes on the FX2 controller
|
||||
|
||||
All source code, firmware binaries, analysis reports, and this documentation are available in the project repository.
|
||||
|
||||
## Support Genpix and the Leleka Foundation
|
||||
|
||||
Genpix Electronics is selling their remaining SkyWalker-1 inventory on eBay, with all net proceeds going to the [Leleka Foundation](https://www.leleka.org/) -- a US-based charitable organization providing medical and social support to individuals wounded and displaced by the conflict in Ukraine.
|
||||
|
||||
If you'd like to own one of these capable little receivers and support a good cause at the same time, check the [eBay listing from the official Genpix seller](https://www.ebay.com/itm/196142001978). New-in-box units ship via USPS Priority Mail. Thank you to Genpix for building the SkyWalker-1 and for directing their remaining stock toward something meaningful.
|
||||
|
|
|
|||
|
|
@ -1,273 +1,273 @@
|
|||
---
|
||||
title: DiSEqC Protocol
|
||||
description: DiSEqC 1.0/1.2 implementation with Manchester-encoded GPIO bit-bang, timing analysis, and command framing.
|
||||
---
|
||||
|
||||
import { Steps, Badge, Aside, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
The SkyWalker-1 implements DiSEqC (Digital Satellite Equipment Control) via Timer2-based GPIO bit-bang on the FX2 microcontroller. The algorithm is identical across all firmware versions -- only the data pin assignment changes per PCB revision.
|
||||
|
||||
[Download DiSEqC PDF](/downloads/DiSEqC%20for%20the%20Skywalker-1.pdf)
|
||||
|
||||
## Protocol Diagrams
|
||||
|
||||
The following diagrams document the Genpix BDA driver's DiSEqC extended property interface:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Signal Architecture
|
||||
|
||||
The firmware does **not** generate the 22 kHz carrier directly. GPIO P0.3 gates an external 22 kHz oscillator circuit on the PCB:
|
||||
|
||||
```
|
||||
FX2 Firmware External Hardware Coax Cable
|
||||
+------------------+ +--------------------+ +------------------+
|
||||
| P0.3 (carrier) |---->| 22 kHz oscillator |---->| LNB power line |
|
||||
| (enable/disable) | | (gated by P0.3) | | (13V/18V + tone) |
|
||||
| | | | | |
|
||||
| P0.x (data bit) | | (internal firmware | | |
|
||||
| (firmware only) | | logic only) | | |
|
||||
+------------------+ +--------------------+ +------------------+
|
||||
```
|
||||
|
||||
The data pin (P0.7 / P0.4 / P0.0 depending on version) is used only internally by the firmware's Manchester encoding logic. It controls whether the carrier gate signal is cut short or held for the full bit period.
|
||||
|
||||
## Timer2 Configuration
|
||||
|
||||
All firmware versions configure Timer2 identically during USB descriptor setup:
|
||||
|
||||
| Parameter | Value | Notes |
|
||||
|-----------|-------|-------|
|
||||
| T2CON | `0x04` | Auto-reload mode, timer running |
|
||||
| RCAP2H | `0xF8` | Reload high byte |
|
||||
| RCAP2L | `0x2F` | Reload low byte (reload = 63535) |
|
||||
| CKCON.T2M | `0` | Timer2 clock = 48 MHz / 12 = 4 MHz |
|
||||
|
||||
**Tick period calculation:**
|
||||
|
||||
```
|
||||
FX2 master clock = 48 MHz
|
||||
CKCON.T2M = 0 -> Timer2 clock = 48 MHz / 12 = 4 MHz
|
||||
Count per overflow = 65536 - 63535 = 2001
|
||||
Tick period = 2001 / 4,000,000 = 500.25 us ~ 500 us
|
||||
Tick frequency ~ 2.0 kHz
|
||||
```
|
||||
|
||||
Timer2 runs continuously from power-on and is never stopped or reconfigured. It serves as a stable 500 us timebase for all DiSEqC operations.
|
||||
|
||||
## Manchester Encoding
|
||||
|
||||
Each DiSEqC bit consists of 3 Timer2 ticks (3 x 500 us = 1.5 ms):
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Data '0'">
|
||||
**Data '0'** -- 2/3 tone, 1/3 silence (1.0 ms carrier + 0.5 ms silence):
|
||||
|
||||
```
|
||||
Tick 1 Tick 2 Tick 3
|
||||
(500 us) (500 us) (500 us)
|
||||
P0.3: _____|========|========|________|
|
||||
^tone ON ^tone OFF
|
||||
(setup gap) (1.0 ms carrier) (0.5 ms silence)
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem label="Data '1'">
|
||||
**Data '1'** -- 1/3 tone, 2/3 silence (0.5 ms carrier + 1.0 ms silence):
|
||||
|
||||
```
|
||||
Tick 1 Tick 2 Tick 3
|
||||
(500 us) (500 us) (500 us)
|
||||
P0.3: _____|========|________|________|
|
||||
^tone ON ^tone OFF early
|
||||
(setup gap) (0.5 ms carrier) (1.0 ms silence)
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Bit Symbol Implementation
|
||||
|
||||
Decompiled from Rev.2 `FUN_CODE_213c`:
|
||||
|
||||
```c title="DiSEqC bit symbol function"
|
||||
void diseqc_bit_symbol(void) {
|
||||
wait_TF2(); // Tick 1: inter-bit gap (500 us)
|
||||
P0 |= 0x08; // P0.3 = 1 -> 22 kHz carrier ON
|
||||
wait_TF2(); // Tick 2: carrier period (500 us)
|
||||
if (data_pin != 0) { // If data = '1':
|
||||
P0 &= 0xF7; // P0.3 = 0 -> carrier OFF (short pulse)
|
||||
}
|
||||
wait_TF2(); // Tick 3: final period (500 us)
|
||||
P0 &= 0xF7; // P0.3 = 0 -> carrier always OFF at end
|
||||
}
|
||||
```
|
||||
|
||||
The `wait_TF2()` function is the lowest-level timing primitive -- a busy-wait on the Timer2 overflow flag:
|
||||
|
||||
```c title="Timer2 tick wait (identical across all versions)"
|
||||
void wait_TF2(void) {
|
||||
while (TF2 == 0) {} // Poll Timer2 overflow flag
|
||||
TF2 = 0; // Clear flag for next tick
|
||||
}
|
||||
```
|
||||
|
||||
## Byte Transmission
|
||||
|
||||
Each DiSEqC byte is 9 bits: 8 data bits (MSB first) + 1 odd parity bit.
|
||||
|
||||
```c title="DiSEqC byte transmit (from Rev.2 FUN_CODE_07d1)"
|
||||
void diseqc_send_byte(char first_byte, byte data) {
|
||||
byte ones_count = 0;
|
||||
if (first_byte == 0) TF2 = 0; // Sync timer on first byte
|
||||
|
||||
for (char i = 8; i > 0; i--) { // 8 bits, MSB first
|
||||
if (data & 0x80) {
|
||||
data_pin = 1; // Set data = '1'
|
||||
diseqc_bit_symbol();
|
||||
ones_count++;
|
||||
} else {
|
||||
data_pin = 0; // Set data = '0'
|
||||
diseqc_bit_symbol();
|
||||
}
|
||||
data <<= 1; // Next bit
|
||||
}
|
||||
|
||||
data_pin = ~ones_count & 1; // Odd parity: '1' if even count
|
||||
diseqc_bit_symbol(); // Transmit parity bit
|
||||
}
|
||||
```
|
||||
|
||||
**Timing per byte:** 9 bits x 1.5 ms = 13.5 ms
|
||||
|
||||
## DiSEqC Command Sequence
|
||||
|
||||
The full command flow for sending a DiSEqC message via vendor command `0x8D` (`SEND_DISEQC_COMMAND`):
|
||||
|
||||
<Steps>
|
||||
|
||||
1. **Read wLength** from USB SETUP packet (`0xE6BE`) to determine message byte count
|
||||
|
||||
2. **Disable 22 kHz carrier** by clearing P0.3 (ensuring a clean start state)
|
||||
|
||||
3. **Pre-transmission delay** of 15 Timer2 ticks (7.5 ms) for LNB voltage settling
|
||||
|
||||
4. **Transmit message bytes** if wLength > 0: iterate through EP0BUF, sending each byte via Manchester-encoded bit-bang (8 data bits + odd parity, 3 Timer2 ticks per bit)
|
||||
|
||||
5. **Or send tone burst** if wLength == 0:
|
||||
- `wValue == 0`: Tone Burst A (25 Timer2 ticks = 12.5 ms of unmodulated carrier)
|
||||
- `wValue != 0`: Tone Burst B (transmitted as `0xFF` byte pattern through the bit-bang function)
|
||||
|
||||
</Steps>
|
||||
|
||||
## USB Command Format
|
||||
|
||||
### Full DiSEqC Message (3-6 bytes)
|
||||
|
||||
```
|
||||
USB SETUP:
|
||||
bmRequestType = 0x40 (vendor, host-to-device)
|
||||
bRequest = 0x8D (SEND_DISEQC_COMMAND)
|
||||
wValue = msg[0] (framing byte, typically 0xE0 or 0xE1)
|
||||
wIndex = 0x0000
|
||||
wLength = message length (3-6)
|
||||
|
||||
EP0 Data:
|
||||
Byte 0: Framing byte (e.g., 0xE0 = command from master, no reply expected)
|
||||
Byte 1: Address byte (e.g., 0x10 = any LNB, 0x11 = LNB 1)
|
||||
Byte 2: Command byte (e.g., 0x38 = Write N0, committed switch port)
|
||||
Byte 3: Data byte 0 (optional, port selection bits)
|
||||
Byte 4: Data byte 1 (optional)
|
||||
Byte 5: Data byte 2 (optional)
|
||||
```
|
||||
|
||||
### Tone Burst (Mini DiSEqC)
|
||||
|
||||
```
|
||||
USB SETUP:
|
||||
bmRequestType = 0x40
|
||||
bRequest = 0x8D
|
||||
wValue = 0x00 (Burst A) or 0x01 (Burst B)
|
||||
wIndex = 0x0000
|
||||
wLength = 0 (zero length signals tone burst mode)
|
||||
```
|
||||
|
||||
## Windows BDA Driver Interface
|
||||
|
||||
The Windows driver exposes DiSEqC through a BDA extended property GUID:
|
||||
|
||||
```c title="BDA Extended Property GUID"
|
||||
// {0B5221EB-F4C4-4976-B959-EF74427464D9}
|
||||
#define STATIC_KSPROPSETID_BdaExtendedProperty \
|
||||
0x0B5221EB, 0xF4C4, 0x4976, 0xB9, 0x59, 0xEF, 0x74, 0x42, 0x74, 0x64, 0xD9
|
||||
```
|
||||
|
||||
The DiSEqC command structure:
|
||||
|
||||
```c title="DISEQC_COMMAND structure"
|
||||
typedef enum enSimpleToneBurst {
|
||||
SEC_MINI_A,
|
||||
SEC_MINI_B
|
||||
} SIMPLE_TONE_BURST;
|
||||
|
||||
typedef struct __DISEQC_COMMAND {
|
||||
UCHAR ucMessage[6]; // Framing, Address, Command, Data[0..2]
|
||||
UCHAR ucMessageLength; // 3-6 for DiSEqC; 1 for tone burst
|
||||
} DISEQC_COMMAND;
|
||||
```
|
||||
|
||||
For tone burst commands, set `ucMessageLength = 1` and `ucMessage[0]` to either `SEC_MINI_A` (0x00) or `SEC_MINI_B` (0x01).
|
||||
|
||||
## Data Pin Assignment Per Version
|
||||
|
||||
The DiSEqC algorithm is identical across all firmware versions. Only the data pin changes per PCB revision:
|
||||
|
||||
| Firmware Version | Data Pin | Carrier Pin | Byte Transmit Function | Bit Symbol Function | Timer Wait |
|
||||
|------------------|----------|-------------|----------------------|--------------------|-----------:|
|
||||
| v2.06 | **P0.7** | P0.3 | `0x2098` | `0x23B5` | `0x24C6` |
|
||||
| Rev.2 v2.10 | **P0.4** | P0.3 | `FUN_CODE_07d1` | `FUN_CODE_213c` | `FUN_CODE_225f` |
|
||||
| v2.13 FW1 | **P0.0** | P0.3 | `FUN_CODE_2060` | `FUN_CODE_22f3` | `func_0x2431` |
|
||||
| Custom v3.01.0 | **P0.7** | P0.3 | `diseqc_tone_burst()` | (inline) | (inline) |
|
||||
|
||||
<Aside type="note">
|
||||
The custom v3.01.0 firmware uses the v2.06 pin assignment (P0.7 for data) since it targets the same original PCB revision. If flashing custom firmware to a Rev.2 board, the DiSEqC data pin would need to be changed to P0.4.
|
||||
</Aside>
|
||||
|
||||
## CPU Clock Compensation
|
||||
|
||||
The delay function used before DiSEqC transmission adjusts its loop count based on the FX2 CPU clock speed:
|
||||
|
||||
```c title="Clock-aware delay function"
|
||||
void delay(byte high, byte low) {
|
||||
byte clkspd = CPUCS & 0x18; // CPUCS[4:3] = clock speed bits
|
||||
if (clkspd == 0x00) { // 12 MHz: halve the count
|
||||
// Adjust high:low /= 2
|
||||
} else if (clkspd == 0x10) { // 48 MHz: double the count
|
||||
// Adjust high:low *= 2
|
||||
}
|
||||
// 24 MHz (0x08): use count as-is
|
||||
while (high:low > 0) {
|
||||
wait_TF2();
|
||||
high:low--;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The pre-DiSEqC delay call is `delay(0, 0x0F)` = 15 ticks x 500 us = **7.5 ms**. This allows the LNB voltage to stabilize before DiSEqC signaling begins.
|
||||
|
||||
## Complete Timing Summary
|
||||
|
||||
| Parameter | Value | Source |
|
||||
|-----------|-------|--------|
|
||||
| Timer2 clock | 4 MHz (48 MHz / 12) | CKCON default, T2M=0 |
|
||||
| Timer2 reload | `0xF82F` | RCAP2H:RCAP2L |
|
||||
| Tick period | 500.25 us | (65536 - 63535) / 4 MHz |
|
||||
| Bit period | 1.5 ms (3 ticks) | DiSEqC Manchester encoding |
|
||||
| Byte period | 13.5 ms (9 bits) | 8 data + 1 parity |
|
||||
| Tone burst duration | 12.5 ms (25 ticks) | Mini-command A/B |
|
||||
| Pre-TX settling delay | 7.5 ms (15 ticks) | Voltage stabilization |
|
||||
| Data '0' | 1.0 ms tone + 0.5 ms silence | 2/3 duty cycle |
|
||||
| Data '1' | 0.5 ms tone + 1.0 ms silence | 1/3 duty cycle |
|
||||
| Carrier frequency | 22 kHz (external oscillator) | Gated by P0.3 |
|
||||
| 3-byte DiSEqC message | ~48 ms total | 7.5 ms settle + 3 x 13.5 ms |
|
||||
| 6-byte DiSEqC message | ~88.5 ms total | 7.5 ms settle + 6 x 13.5 ms |
|
||||
---
|
||||
title: DiSEqC Protocol
|
||||
description: DiSEqC 1.0/1.2 implementation with Manchester-encoded GPIO bit-bang, timing analysis, and command framing.
|
||||
---
|
||||
|
||||
import { Steps, Badge, Aside, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
The SkyWalker-1 implements DiSEqC (Digital Satellite Equipment Control) via Timer2-based GPIO bit-bang on the FX2 microcontroller. The algorithm is identical across all firmware versions -- only the data pin assignment changes per PCB revision.
|
||||
|
||||
[Download DiSEqC PDF](/downloads/DiSEqC%20for%20the%20Skywalker-1.pdf)
|
||||
|
||||
## Protocol Diagrams
|
||||
|
||||
The following diagrams document the Genpix BDA driver's DiSEqC extended property interface:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Signal Architecture
|
||||
|
||||
The firmware does **not** generate the 22 kHz carrier directly. GPIO P0.3 gates an external 22 kHz oscillator circuit on the PCB:
|
||||
|
||||
```
|
||||
FX2 Firmware External Hardware Coax Cable
|
||||
+------------------+ +--------------------+ +------------------+
|
||||
| P0.3 (carrier) |---->| 22 kHz oscillator |---->| LNB power line |
|
||||
| (enable/disable) | | (gated by P0.3) | | (13V/18V + tone) |
|
||||
| | | | | |
|
||||
| P0.x (data bit) | | (internal firmware | | |
|
||||
| (firmware only) | | logic only) | | |
|
||||
+------------------+ +--------------------+ +------------------+
|
||||
```
|
||||
|
||||
The data pin (P0.7 / P0.4 / P0.0 depending on version) is used only internally by the firmware's Manchester encoding logic. It controls whether the carrier gate signal is cut short or held for the full bit period.
|
||||
|
||||
## Timer2 Configuration
|
||||
|
||||
All firmware versions configure Timer2 identically during USB descriptor setup:
|
||||
|
||||
| Parameter | Value | Notes |
|
||||
|-----------|-------|-------|
|
||||
| T2CON | `0x04` | Auto-reload mode, timer running |
|
||||
| RCAP2H | `0xF8` | Reload high byte |
|
||||
| RCAP2L | `0x2F` | Reload low byte (reload = 63535) |
|
||||
| CKCON.T2M | `0` | Timer2 clock = 48 MHz / 12 = 4 MHz |
|
||||
|
||||
**Tick period calculation:**
|
||||
|
||||
```
|
||||
FX2 master clock = 48 MHz
|
||||
CKCON.T2M = 0 -> Timer2 clock = 48 MHz / 12 = 4 MHz
|
||||
Count per overflow = 65536 - 63535 = 2001
|
||||
Tick period = 2001 / 4,000,000 = 500.25 us ~ 500 us
|
||||
Tick frequency ~ 2.0 kHz
|
||||
```
|
||||
|
||||
Timer2 runs continuously from power-on and is never stopped or reconfigured. It serves as a stable 500 us timebase for all DiSEqC operations.
|
||||
|
||||
## Manchester Encoding
|
||||
|
||||
Each DiSEqC bit consists of 3 Timer2 ticks (3 x 500 us = 1.5 ms):
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Data '0'">
|
||||
**Data '0'** -- 2/3 tone, 1/3 silence (1.0 ms carrier + 0.5 ms silence):
|
||||
|
||||
```
|
||||
Tick 1 Tick 2 Tick 3
|
||||
(500 us) (500 us) (500 us)
|
||||
P0.3: _____|========|========|________|
|
||||
^tone ON ^tone OFF
|
||||
(setup gap) (1.0 ms carrier) (0.5 ms silence)
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem label="Data '1'">
|
||||
**Data '1'** -- 1/3 tone, 2/3 silence (0.5 ms carrier + 1.0 ms silence):
|
||||
|
||||
```
|
||||
Tick 1 Tick 2 Tick 3
|
||||
(500 us) (500 us) (500 us)
|
||||
P0.3: _____|========|________|________|
|
||||
^tone ON ^tone OFF early
|
||||
(setup gap) (0.5 ms carrier) (1.0 ms silence)
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Bit Symbol Implementation
|
||||
|
||||
Decompiled from Rev.2 `FUN_CODE_213c`:
|
||||
|
||||
```c title="DiSEqC bit symbol function"
|
||||
void diseqc_bit_symbol(void) {
|
||||
wait_TF2(); // Tick 1: inter-bit gap (500 us)
|
||||
P0 |= 0x08; // P0.3 = 1 -> 22 kHz carrier ON
|
||||
wait_TF2(); // Tick 2: carrier period (500 us)
|
||||
if (data_pin != 0) { // If data = '1':
|
||||
P0 &= 0xF7; // P0.3 = 0 -> carrier OFF (short pulse)
|
||||
}
|
||||
wait_TF2(); // Tick 3: final period (500 us)
|
||||
P0 &= 0xF7; // P0.3 = 0 -> carrier always OFF at end
|
||||
}
|
||||
```
|
||||
|
||||
The `wait_TF2()` function is the lowest-level timing primitive -- a busy-wait on the Timer2 overflow flag:
|
||||
|
||||
```c title="Timer2 tick wait (identical across all versions)"
|
||||
void wait_TF2(void) {
|
||||
while (TF2 == 0) {} // Poll Timer2 overflow flag
|
||||
TF2 = 0; // Clear flag for next tick
|
||||
}
|
||||
```
|
||||
|
||||
## Byte Transmission
|
||||
|
||||
Each DiSEqC byte is 9 bits: 8 data bits (MSB first) + 1 odd parity bit.
|
||||
|
||||
```c title="DiSEqC byte transmit (from Rev.2 FUN_CODE_07d1)"
|
||||
void diseqc_send_byte(char first_byte, byte data) {
|
||||
byte ones_count = 0;
|
||||
if (first_byte == 0) TF2 = 0; // Sync timer on first byte
|
||||
|
||||
for (char i = 8; i > 0; i--) { // 8 bits, MSB first
|
||||
if (data & 0x80) {
|
||||
data_pin = 1; // Set data = '1'
|
||||
diseqc_bit_symbol();
|
||||
ones_count++;
|
||||
} else {
|
||||
data_pin = 0; // Set data = '0'
|
||||
diseqc_bit_symbol();
|
||||
}
|
||||
data <<= 1; // Next bit
|
||||
}
|
||||
|
||||
data_pin = ~ones_count & 1; // Odd parity: '1' if even count
|
||||
diseqc_bit_symbol(); // Transmit parity bit
|
||||
}
|
||||
```
|
||||
|
||||
**Timing per byte:** 9 bits x 1.5 ms = 13.5 ms
|
||||
|
||||
## DiSEqC Command Sequence
|
||||
|
||||
The full command flow for sending a DiSEqC message via vendor command `0x8D` (`SEND_DISEQC_COMMAND`):
|
||||
|
||||
<Steps>
|
||||
|
||||
1. **Read wLength** from USB SETUP packet (`0xE6BE`) to determine message byte count
|
||||
|
||||
2. **Disable 22 kHz carrier** by clearing P0.3 (ensuring a clean start state)
|
||||
|
||||
3. **Pre-transmission delay** of 15 Timer2 ticks (7.5 ms) for LNB voltage settling
|
||||
|
||||
4. **Transmit message bytes** if wLength > 0: iterate through EP0BUF, sending each byte via Manchester-encoded bit-bang (8 data bits + odd parity, 3 Timer2 ticks per bit)
|
||||
|
||||
5. **Or send tone burst** if wLength == 0:
|
||||
- `wValue == 0`: Tone Burst A (25 Timer2 ticks = 12.5 ms of unmodulated carrier)
|
||||
- `wValue != 0`: Tone Burst B (transmitted as `0xFF` byte pattern through the bit-bang function)
|
||||
|
||||
</Steps>
|
||||
|
||||
## USB Command Format
|
||||
|
||||
### Full DiSEqC Message (3-6 bytes)
|
||||
|
||||
```
|
||||
USB SETUP:
|
||||
bmRequestType = 0x40 (vendor, host-to-device)
|
||||
bRequest = 0x8D (SEND_DISEQC_COMMAND)
|
||||
wValue = msg[0] (framing byte, typically 0xE0 or 0xE1)
|
||||
wIndex = 0x0000
|
||||
wLength = message length (3-6)
|
||||
|
||||
EP0 Data:
|
||||
Byte 0: Framing byte (e.g., 0xE0 = command from master, no reply expected)
|
||||
Byte 1: Address byte (e.g., 0x10 = any LNB, 0x11 = LNB 1)
|
||||
Byte 2: Command byte (e.g., 0x38 = Write N0, committed switch port)
|
||||
Byte 3: Data byte 0 (optional, port selection bits)
|
||||
Byte 4: Data byte 1 (optional)
|
||||
Byte 5: Data byte 2 (optional)
|
||||
```
|
||||
|
||||
### Tone Burst (Mini DiSEqC)
|
||||
|
||||
```
|
||||
USB SETUP:
|
||||
bmRequestType = 0x40
|
||||
bRequest = 0x8D
|
||||
wValue = 0x00 (Burst A) or 0x01 (Burst B)
|
||||
wIndex = 0x0000
|
||||
wLength = 0 (zero length signals tone burst mode)
|
||||
```
|
||||
|
||||
## Windows BDA Driver Interface
|
||||
|
||||
The Windows driver exposes DiSEqC through a BDA extended property GUID:
|
||||
|
||||
```c title="BDA Extended Property GUID"
|
||||
// {0B5221EB-F4C4-4976-B959-EF74427464D9}
|
||||
#define STATIC_KSPROPSETID_BdaExtendedProperty \
|
||||
0x0B5221EB, 0xF4C4, 0x4976, 0xB9, 0x59, 0xEF, 0x74, 0x42, 0x74, 0x64, 0xD9
|
||||
```
|
||||
|
||||
The DiSEqC command structure:
|
||||
|
||||
```c title="DISEQC_COMMAND structure"
|
||||
typedef enum enSimpleToneBurst {
|
||||
SEC_MINI_A,
|
||||
SEC_MINI_B
|
||||
} SIMPLE_TONE_BURST;
|
||||
|
||||
typedef struct __DISEQC_COMMAND {
|
||||
UCHAR ucMessage[6]; // Framing, Address, Command, Data[0..2]
|
||||
UCHAR ucMessageLength; // 3-6 for DiSEqC; 1 for tone burst
|
||||
} DISEQC_COMMAND;
|
||||
```
|
||||
|
||||
For tone burst commands, set `ucMessageLength = 1` and `ucMessage[0]` to either `SEC_MINI_A` (0x00) or `SEC_MINI_B` (0x01).
|
||||
|
||||
## Data Pin Assignment Per Version
|
||||
|
||||
The DiSEqC algorithm is identical across all firmware versions. Only the data pin changes per PCB revision:
|
||||
|
||||
| Firmware Version | Data Pin | Carrier Pin | Byte Transmit Function | Bit Symbol Function | Timer Wait |
|
||||
|------------------|----------|-------------|----------------------|--------------------|-----------:|
|
||||
| v2.06 | **P0.7** | P0.3 | `0x2098` | `0x23B5` | `0x24C6` |
|
||||
| Rev.2 v2.10 | **P0.4** | P0.3 | `FUN_CODE_07d1` | `FUN_CODE_213c` | `FUN_CODE_225f` |
|
||||
| v2.13 FW1 | **P0.0** | P0.3 | `FUN_CODE_2060` | `FUN_CODE_22f3` | `func_0x2431` |
|
||||
| Custom v3.01.0 | **P0.7** | P0.3 | `diseqc_tone_burst()` | (inline) | (inline) |
|
||||
|
||||
<Aside type="note">
|
||||
The custom v3.01.0 firmware uses the v2.06 pin assignment (P0.7 for data) since it targets the same original PCB revision. If flashing custom firmware to a Rev.2 board, the DiSEqC data pin would need to be changed to P0.4.
|
||||
</Aside>
|
||||
|
||||
## CPU Clock Compensation
|
||||
|
||||
The delay function used before DiSEqC transmission adjusts its loop count based on the FX2 CPU clock speed:
|
||||
|
||||
```c title="Clock-aware delay function"
|
||||
void delay(byte high, byte low) {
|
||||
byte clkspd = CPUCS & 0x18; // CPUCS[4:3] = clock speed bits
|
||||
if (clkspd == 0x00) { // 12 MHz: halve the count
|
||||
// Adjust high:low /= 2
|
||||
} else if (clkspd == 0x10) { // 48 MHz: double the count
|
||||
// Adjust high:low *= 2
|
||||
}
|
||||
// 24 MHz (0x08): use count as-is
|
||||
while (high:low > 0) {
|
||||
wait_TF2();
|
||||
high:low--;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The pre-DiSEqC delay call is `delay(0, 0x0F)` = 15 ticks x 500 us = **7.5 ms**. This allows the LNB voltage to stabilize before DiSEqC signaling begins.
|
||||
|
||||
## Complete Timing Summary
|
||||
|
||||
| Parameter | Value | Source |
|
||||
|-----------|-------|--------|
|
||||
| Timer2 clock | 4 MHz (48 MHz / 12) | CKCON default, T2M=0 |
|
||||
| Timer2 reload | `0xF82F` | RCAP2H:RCAP2L |
|
||||
| Tick period | 500.25 us | (65536 - 63535) / 4 MHz |
|
||||
| Bit period | 1.5 ms (3 ticks) | DiSEqC Manchester encoding |
|
||||
| Byte period | 13.5 ms (9 bits) | 8 data + 1 parity |
|
||||
| Tone burst duration | 12.5 ms (25 ticks) | Mini-command A/B |
|
||||
| Pre-TX settling delay | 7.5 ms (15 ticks) | Voltage stabilization |
|
||||
| Data '0' | 1.0 ms tone + 0.5 ms silence | 2/3 duty cycle |
|
||||
| Data '1' | 0.5 ms tone + 1.0 ms silence | 1/3 duty cycle |
|
||||
| Carrier frequency | 22 kHz (external oscillator) | Gated by P0.3 |
|
||||
| 3-byte DiSEqC message | ~48 ms total | 7.5 ms settle + 3 x 13.5 ms |
|
||||
| 6-byte DiSEqC message | ~88.5 ms total | 7.5 ms settle + 6 x 13.5 ms |
|
||||
|
|
|
|||
|
|
@ -1,106 +1,106 @@
|
|||
---
|
||||
title: Legacy Dish Network Switch
|
||||
description: SET_DN_SWITCH (0x8F) protocol for Dish Network SW21/SW44 legacy switches.
|
||||
---
|
||||
|
||||
import { Badge, Aside, Steps } from '@astrojs/starlight/components';
|
||||
|
||||
The SkyWalker-1 supports legacy Dish Network satellite switches (SW21, SW44) through vendor command `0x8F` (`SET_DN_SWITCH`). This protocol predates DiSEqC and uses a simpler serial bit-bang scheme on a shared GPIO pin.
|
||||
|
||||
## Protocol Overview
|
||||
|
||||
The legacy Dish Network switch protocol sends a 7-bit serial command via **GPIO P0.4** (the same pin used for LNB voltage selection). The command is bit-banged LSB-first with fixed timing delays.
|
||||
|
||||
<Aside type="note">
|
||||
The 8th bit (bit 7, value `0x80`) of the original Dish Network switch command controls LNB voltage polarity and is **not** sent via this protocol. Instead, the kernel driver sends it separately via `SET_LNB_VOLTAGE` (command `0x8B`). This split between data bits and voltage is handled by the `send_legacy_dish_cmd` callback in the kernel driver.
|
||||
</Aside>
|
||||
|
||||
## USB Command Format
|
||||
|
||||
```
|
||||
USB SETUP:
|
||||
bmRequestType = 0x40 (vendor, host-to-device)
|
||||
bRequest = 0x8F (SET_DN_SWITCH)
|
||||
wValue = 7-bit switch command (bits 0-6)
|
||||
wIndex = 0x0000
|
||||
wLength = 0
|
||||
```
|
||||
|
||||
## Bit-Bang Sequence
|
||||
|
||||
The firmware transmits the 7-bit command using the following GPIO sequence:
|
||||
|
||||
<Steps>
|
||||
|
||||
1. **Start pulse**: Assert P0.4 HIGH, delay approximately 32 CPU cycles (~0.67 us at 48 MHz)
|
||||
|
||||
2. **Gap**: De-assert P0.4 LOW, delay approximately 8 CPU cycles (~0.17 us at 48 MHz)
|
||||
|
||||
3. **Data bits**: Shift out 7 bits LSB-first via P0.4, with approximately 8-cycle delays between each bit transition
|
||||
|
||||
4. **End**: P0.4 returns to its previous LNB voltage state
|
||||
|
||||
</Steps>
|
||||
|
||||
## Switch Position Mapping
|
||||
|
||||
The legacy protocol supports the SW21 (2-input, 1-output) and SW44 (4-input, 4-output) switches. The 7-bit command encodes the desired input port and satellite selection:
|
||||
|
||||
| Switch Type | Bit Pattern | Function |
|
||||
|-------------|-------------|----------|
|
||||
| SW21 | <Badge text="Port A" variant="success" /> `0bxxxxxxx` with port bit = 0 | Select satellite A input |
|
||||
| SW21 | <Badge text="Port B" variant="note" /> `0bxxxxxxx` with port bit = 1 | Select satellite B input |
|
||||
| SW44 | Multiple bit fields | Select one of 4 inputs to one of 4 outputs |
|
||||
|
||||
The exact bit-field layout is proprietary to Dish Network. The kernel driver constructs the command value in `dishnetwork_send_legacy_command()` based on the requested switch position.
|
||||
|
||||
## Timing Characteristics
|
||||
|
||||
The legacy switch protocol is significantly simpler and faster than DiSEqC:
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| Start pulse width | ~0.67 us (32 cycles at 48 MHz) |
|
||||
| Inter-bit gap | ~0.17 us (8 cycles at 48 MHz) |
|
||||
| Total command time | ~5-10 us for 7 bits |
|
||||
| GPIO pin | P0.4 (shared with LNB voltage) |
|
||||
|
||||
<Aside type="caution">
|
||||
Because P0.4 is shared between the legacy switch protocol and LNB voltage selection, a switch command briefly disrupts the LNB voltage level. The switch must complete before the voltage is returned to its correct state. The kernel driver handles this coordination automatically.
|
||||
</Aside>
|
||||
|
||||
## Kernel Driver Integration
|
||||
|
||||
The Linux kernel driver implements legacy Dish Network support through a dedicated callback:
|
||||
|
||||
```c title="Legacy Dish Network command callback"
|
||||
// Registered as .send_legacy_dish_cmd in dvb_usb_device_properties
|
||||
static int gp8psk_send_legacy_dish_cmd(struct dvb_frontend *fe, unsigned long cmd)
|
||||
{
|
||||
struct dvb_usb_adapter *adap = fe->dvb->priv;
|
||||
struct dvb_usb_device *d = adap->dev;
|
||||
|
||||
// Bit 7 (0x80) controls voltage, sent separately
|
||||
gp8psk_usb_out_op(d, SET_LNB_VOLTAGE, (cmd >> 7) & 1, 0, NULL, 0);
|
||||
|
||||
// Bits 0-6 sent via SET_DN_SWITCH
|
||||
gp8psk_usb_out_op(d, SET_DN_SWITCH, cmd & 0x7F, 0, NULL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Comparison with DiSEqC
|
||||
|
||||
| Feature | Legacy Dish Protocol | DiSEqC 1.0 |
|
||||
|---------|---------------------|------------|
|
||||
| Standard | Proprietary (Dish Network) | EUTELSAT standard |
|
||||
| Encoding | Serial bit-bang, LSB-first | Manchester encoding, MSB-first |
|
||||
| Carrier | None (direct GPIO) | 22 kHz modulated |
|
||||
| Message length | 7 bits | 3-6 bytes (24-54 bits + parity) |
|
||||
| Timing | ~5-10 us total | ~48-88 ms total |
|
||||
| Switch support | SW21, SW44 | DiSEqC 1.0/1.1/1.2 compatible switches |
|
||||
| GPIO pin | P0.4 (shared with voltage) | P0.x (data) + P0.3 (carrier) |
|
||||
| Vendor command | `0x8F` | `0x8D` |
|
||||
|
||||
For modern satellite installations, DiSEqC is the preferred switch protocol. The legacy Dish Network protocol exists for backward compatibility with older SW21 and SW44 switches commonly found in North American installations. See the [DiSEqC Protocol](/lnb-diseqc/diseqc-protocol/) page for the full DiSEqC implementation.
|
||||
---
|
||||
title: Legacy Dish Network Switch
|
||||
description: SET_DN_SWITCH (0x8F) protocol for Dish Network SW21/SW44 legacy switches.
|
||||
---
|
||||
|
||||
import { Badge, Aside, Steps } from '@astrojs/starlight/components';
|
||||
|
||||
The SkyWalker-1 supports legacy Dish Network satellite switches (SW21, SW44) through vendor command `0x8F` (`SET_DN_SWITCH`). This protocol predates DiSEqC and uses a simpler serial bit-bang scheme on a shared GPIO pin.
|
||||
|
||||
## Protocol Overview
|
||||
|
||||
The legacy Dish Network switch protocol sends a 7-bit serial command via **GPIO P0.4** (the same pin used for LNB voltage selection). The command is bit-banged LSB-first with fixed timing delays.
|
||||
|
||||
<Aside type="note">
|
||||
The 8th bit (bit 7, value `0x80`) of the original Dish Network switch command controls LNB voltage polarity and is **not** sent via this protocol. Instead, the kernel driver sends it separately via `SET_LNB_VOLTAGE` (command `0x8B`). This split between data bits and voltage is handled by the `send_legacy_dish_cmd` callback in the kernel driver.
|
||||
</Aside>
|
||||
|
||||
## USB Command Format
|
||||
|
||||
```
|
||||
USB SETUP:
|
||||
bmRequestType = 0x40 (vendor, host-to-device)
|
||||
bRequest = 0x8F (SET_DN_SWITCH)
|
||||
wValue = 7-bit switch command (bits 0-6)
|
||||
wIndex = 0x0000
|
||||
wLength = 0
|
||||
```
|
||||
|
||||
## Bit-Bang Sequence
|
||||
|
||||
The firmware transmits the 7-bit command using the following GPIO sequence:
|
||||
|
||||
<Steps>
|
||||
|
||||
1. **Start pulse**: Assert P0.4 HIGH, delay approximately 32 CPU cycles (~0.67 us at 48 MHz)
|
||||
|
||||
2. **Gap**: De-assert P0.4 LOW, delay approximately 8 CPU cycles (~0.17 us at 48 MHz)
|
||||
|
||||
3. **Data bits**: Shift out 7 bits LSB-first via P0.4, with approximately 8-cycle delays between each bit transition
|
||||
|
||||
4. **End**: P0.4 returns to its previous LNB voltage state
|
||||
|
||||
</Steps>
|
||||
|
||||
## Switch Position Mapping
|
||||
|
||||
The legacy protocol supports the SW21 (2-input, 1-output) and SW44 (4-input, 4-output) switches. The 7-bit command encodes the desired input port and satellite selection:
|
||||
|
||||
| Switch Type | Bit Pattern | Function |
|
||||
|-------------|-------------|----------|
|
||||
| SW21 | <Badge text="Port A" variant="success" /> `0bxxxxxxx` with port bit = 0 | Select satellite A input |
|
||||
| SW21 | <Badge text="Port B" variant="note" /> `0bxxxxxxx` with port bit = 1 | Select satellite B input |
|
||||
| SW44 | Multiple bit fields | Select one of 4 inputs to one of 4 outputs |
|
||||
|
||||
The exact bit-field layout is proprietary to Dish Network. The kernel driver constructs the command value in `dishnetwork_send_legacy_command()` based on the requested switch position.
|
||||
|
||||
## Timing Characteristics
|
||||
|
||||
The legacy switch protocol is significantly simpler and faster than DiSEqC:
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| Start pulse width | ~0.67 us (32 cycles at 48 MHz) |
|
||||
| Inter-bit gap | ~0.17 us (8 cycles at 48 MHz) |
|
||||
| Total command time | ~5-10 us for 7 bits |
|
||||
| GPIO pin | P0.4 (shared with LNB voltage) |
|
||||
|
||||
<Aside type="caution">
|
||||
Because P0.4 is shared between the legacy switch protocol and LNB voltage selection, a switch command briefly disrupts the LNB voltage level. The switch must complete before the voltage is returned to its correct state. The kernel driver handles this coordination automatically.
|
||||
</Aside>
|
||||
|
||||
## Kernel Driver Integration
|
||||
|
||||
The Linux kernel driver implements legacy Dish Network support through a dedicated callback:
|
||||
|
||||
```c title="Legacy Dish Network command callback"
|
||||
// Registered as .send_legacy_dish_cmd in dvb_usb_device_properties
|
||||
static int gp8psk_send_legacy_dish_cmd(struct dvb_frontend *fe, unsigned long cmd)
|
||||
{
|
||||
struct dvb_usb_adapter *adap = fe->dvb->priv;
|
||||
struct dvb_usb_device *d = adap->dev;
|
||||
|
||||
// Bit 7 (0x80) controls voltage, sent separately
|
||||
gp8psk_usb_out_op(d, SET_LNB_VOLTAGE, (cmd >> 7) & 1, 0, NULL, 0);
|
||||
|
||||
// Bits 0-6 sent via SET_DN_SWITCH
|
||||
gp8psk_usb_out_op(d, SET_DN_SWITCH, cmd & 0x7F, 0, NULL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Comparison with DiSEqC
|
||||
|
||||
| Feature | Legacy Dish Protocol | DiSEqC 1.0 |
|
||||
|---------|---------------------|------------|
|
||||
| Standard | Proprietary (Dish Network) | EUTELSAT standard |
|
||||
| Encoding | Serial bit-bang, LSB-first | Manchester encoding, MSB-first |
|
||||
| Carrier | None (direct GPIO) | 22 kHz modulated |
|
||||
| Message length | 7 bits | 3-6 bytes (24-54 bits + parity) |
|
||||
| Timing | ~5-10 us total | ~48-88 ms total |
|
||||
| Switch support | SW21, SW44 | DiSEqC 1.0/1.1/1.2 compatible switches |
|
||||
| GPIO pin | P0.4 (shared with voltage) | P0.x (data) + P0.3 (carrier) |
|
||||
| Vendor command | `0x8F` | `0x8D` |
|
||||
|
||||
For modern satellite installations, DiSEqC is the preferred switch protocol. The legacy Dish Network protocol exists for backward compatibility with older SW21 and SW44 switches commonly found in North American installations. See the [DiSEqC Protocol](/lnb-diseqc/diseqc-protocol/) page for the full DiSEqC implementation.
|
||||
|
|
|
|||
|
|
@ -1,127 +1,127 @@
|
|||
---
|
||||
title: LNB Control
|
||||
description: LNB voltage selection (13V/18V), 22 kHz tone generation, extra volt mode, and current limits.
|
||||
---
|
||||
|
||||
import { Badge, Aside, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
The SkyWalker-1 controls LNB power and band selection through dedicated GPIO pins on the FX2 microcontroller. No I2C transactions are involved in basic LNB control -- voltage and tone are driven directly by port pins.
|
||||
|
||||
## LNB Voltage (13V / 18V)
|
||||
|
||||
LNB voltage selects the polarization of the received signal. It is controlled via **GPIO P0.4** using vendor command `0x8B` (`SET_LNB_VOLTAGE`).
|
||||
|
||||
| wValue | Voltage | GPIO P0.4 | Polarization |
|
||||
|--------|---------|-----------|--------------|
|
||||
| `0x00` | 13V | LOW | Vertical / Circular-Right |
|
||||
| `0x01` | 18V | HIGH | Horizontal / Circular-Left |
|
||||
|
||||
The Linux kernel driver sets the voltage during the tuning sequence based on the requested polarization:
|
||||
|
||||
```c title="Kernel driver polarization mapping"
|
||||
// From gp8psk-fe.c set_voltage callback
|
||||
case SEC_VOLTAGE_13: // Vertical / Circular-R
|
||||
gp8psk_usb_out_op(d, SET_LNB_VOLTAGE, 0, 0, NULL, 0);
|
||||
break;
|
||||
case SEC_VOLTAGE_18: // Horizontal / Circular-L
|
||||
gp8psk_usb_out_op(d, SET_LNB_VOLTAGE, 1, 0, NULL, 0);
|
||||
break;
|
||||
```
|
||||
|
||||
The configuration status byte (returned by `GET_8PSK_CONFIG`, command `0x80`) reflects the current voltage selection in **bit 5** (`0x20`, `bmSEL18V`).
|
||||
|
||||
## Extra Volt Mode (+1V Boost)
|
||||
|
||||
For long cable runs where voltage drop is significant, the SkyWalker-1 supports a +1V boost mode (13V becomes 14V, 18V becomes 19V). This is controlled via vendor command `0x94` (`USE_EXTRA_VOLT`).
|
||||
|
||||
| wValue | XRAM 0xE0B6 | Result |
|
||||
|--------|-------------|--------|
|
||||
| `0x00` | `0x62` | Normal voltage (13V / 18V) |
|
||||
| `0x01` | `0x6A` | Boosted voltage (14V / 19V) |
|
||||
|
||||
The difference between the two XRAM values is **bit 3** (`0x08`), which enables the boost on the LNB power regulator circuitry.
|
||||
|
||||
<Aside type="caution">
|
||||
The LNB power output has firm current limits. The maximum **non-continuous** draw is 750 mA (for short bursts of several minutes). For permanent continuous load, do not exceed **450 mA** to avoid overheating the onboard regulator. Exceeding these limits risks thermal damage to the SkyWalker-1 board.
|
||||
</Aside>
|
||||
|
||||
The kernel driver exposes extra volt mode through the `enable_high_lnb_voltage` callback in the DVB frontend properties.
|
||||
|
||||
## 22 kHz Tone Generation
|
||||
|
||||
The 22 kHz tone selects the high or low band on a universal LNB. It is controlled via **GPIO P0.3** using vendor command `0x8C` (`SET_22KHZ_TONE`).
|
||||
|
||||
| wValue | State | GPIO P0.3 | LNB Band |
|
||||
|--------|-------|-----------|----------|
|
||||
| `0x00` | OFF | LOW | Low band (9.75 GHz LO on universal LNB) |
|
||||
| `0x01` | ON | HIGH | High band (10.6 GHz LO on universal LNB) |
|
||||
|
||||
<Aside type="note">
|
||||
P0.3 does not generate the 22 kHz carrier directly. Instead, it **gates an external 22 kHz oscillator circuit** on the PCB. When P0.3 is HIGH, the oscillator output is coupled onto the coaxial cable's DC power line alongside the 13V/18V LNB voltage. When P0.3 is LOW, the oscillator is disabled and no tone is present.
|
||||
</Aside>
|
||||
|
||||
The configuration status byte tracks the tone state in **bit 4** (`0x10`, `bm22kHz`).
|
||||
|
||||
## LNB Power Supply Enable
|
||||
|
||||
Before any LNB control is possible, the LNB power supply itself must be enabled via vendor command `0x8A` (`START_INTERSIL`). The kernel driver does this during the boot sequence if bit 2 (`bmIntersilOn`) of the configuration status byte is not already set.
|
||||
|
||||
| wValue | Action | Config Bit |
|
||||
|--------|--------|------------|
|
||||
| `0x01` | Enable LNB power supply | Sets `bmIntersilOn` (bit 2) |
|
||||
| `0x00` | Disable LNB power supply | Clears `bmIntersilOn` (bit 2) |
|
||||
|
||||
In the custom v3.01.0 firmware, enabling the LNB supply also configures the output enable masks for the LNB control GPIO pins:
|
||||
|
||||
```c title="START_INTERSIL handler in custom firmware"
|
||||
case START_INTERSIL:
|
||||
if (wval) {
|
||||
OEA |= (PIN_22KHZ | PIN_LNB_VOLT | PIN_DISEQC);
|
||||
config_status |= BM_INTERSIL;
|
||||
} else {
|
||||
config_status &= ~BM_INTERSIL;
|
||||
}
|
||||
```
|
||||
|
||||
## GPIO Pin Assignments
|
||||
|
||||
The LNB control pins are consistent across firmware versions, though their exact initialization differs:
|
||||
|
||||
| GPIO Pin | Function | Direction | Init State |
|
||||
|----------|----------|-----------|------------|
|
||||
| P0.1 | Power supply enable | Output | LOW (off) |
|
||||
| P0.2 | Power supply disable | Output | HIGH (active disable) |
|
||||
| P0.3 | 22 kHz tone gate | Output | LOW (tone off) |
|
||||
| P0.4 | LNB voltage select | Output | LOW (13V) |
|
||||
| P0.5 | BCM4500 hardware reset | Output | LOW (reset asserted) |
|
||||
|
||||
The initial GPIO state after power-on is `IOA = 0x84` (P0.7 and P0.2 HIGH, all others LOW), with the output enable mask `OEA = 0xBE` (P0.1 through P0.5 and P0.7 as outputs).
|
||||
|
||||
## LNB GPIO Debug Commands
|
||||
|
||||
Three vendor commands provide low-level GPIO access for LNB debugging:
|
||||
|
||||
| Command | Name | Purpose |
|
||||
|---------|------|---------|
|
||||
| `0x96` | `SET_LNB_GPIO_MODE` | Configure LNB GPIO output enables |
|
||||
| `0x97` | `SET_GPIO_PINS` | Direct write to LNB GPIO pins via `wValue` bitmap |
|
||||
| `0x98` | `GET_GPIO_STATUS` | Read LNB feedback GPIO pin (1 byte) |
|
||||
|
||||
These commands access Port B (XRAM-mapped IOB) rather than Port A. In v2.06 and v2.13, `0x97` controls IOB bits 1 and 2 for LNB, while `0x96` configures IOB bit 3 as the GPIO mode select. Rev.2 uses IOB bit 4 instead.
|
||||
|
||||
<Aside type="note">
|
||||
These debug GPIO commands are not used by the Linux kernel driver during normal operation. They are available for diagnostic tools and custom applications that need direct hardware-level LNB control.
|
||||
</Aside>
|
||||
|
||||
## Complete LNB Configuration Sequence
|
||||
|
||||
During a typical tune operation, the kernel driver configures the LNB in this order:
|
||||
|
||||
```
|
||||
1. SET_LNB_VOLTAGE (0x8B) -- Select 13V or 18V based on polarization
|
||||
2. SET_22KHZ_TONE (0x8C) -- Select low or high band
|
||||
3. SEND_DISEQC (0x8D) -- Switch multi-port switch if needed
|
||||
4. TUNE_8PSK (0x86) -- Send tuning parameters
|
||||
```
|
||||
|
||||
The voltage and tone are set before any DiSEqC commands because the LNB must be powered and in the correct band before the switch can respond. See the [DiSEqC Protocol](/lnb-diseqc/diseqc-protocol/) page for details on switch control, and the [Tuning Protocol](/bcm4500/tuning-protocol/) for the complete tune flow.
|
||||
---
|
||||
title: LNB Control
|
||||
description: LNB voltage selection (13V/18V), 22 kHz tone generation, extra volt mode, and current limits.
|
||||
---
|
||||
|
||||
import { Badge, Aside, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
The SkyWalker-1 controls LNB power and band selection through dedicated GPIO pins on the FX2 microcontroller. No I2C transactions are involved in basic LNB control -- voltage and tone are driven directly by port pins.
|
||||
|
||||
## LNB Voltage (13V / 18V)
|
||||
|
||||
LNB voltage selects the polarization of the received signal. It is controlled via **GPIO P0.4** using vendor command `0x8B` (`SET_LNB_VOLTAGE`).
|
||||
|
||||
| wValue | Voltage | GPIO P0.4 | Polarization |
|
||||
|--------|---------|-----------|--------------|
|
||||
| `0x00` | 13V | LOW | Vertical / Circular-Right |
|
||||
| `0x01` | 18V | HIGH | Horizontal / Circular-Left |
|
||||
|
||||
The Linux kernel driver sets the voltage during the tuning sequence based on the requested polarization:
|
||||
|
||||
```c title="Kernel driver polarization mapping"
|
||||
// From gp8psk-fe.c set_voltage callback
|
||||
case SEC_VOLTAGE_13: // Vertical / Circular-R
|
||||
gp8psk_usb_out_op(d, SET_LNB_VOLTAGE, 0, 0, NULL, 0);
|
||||
break;
|
||||
case SEC_VOLTAGE_18: // Horizontal / Circular-L
|
||||
gp8psk_usb_out_op(d, SET_LNB_VOLTAGE, 1, 0, NULL, 0);
|
||||
break;
|
||||
```
|
||||
|
||||
The configuration status byte (returned by `GET_8PSK_CONFIG`, command `0x80`) reflects the current voltage selection in **bit 5** (`0x20`, `bmSEL18V`).
|
||||
|
||||
## Extra Volt Mode (+1V Boost)
|
||||
|
||||
For long cable runs where voltage drop is significant, the SkyWalker-1 supports a +1V boost mode (13V becomes 14V, 18V becomes 19V). This is controlled via vendor command `0x94` (`USE_EXTRA_VOLT`).
|
||||
|
||||
| wValue | XRAM 0xE0B6 | Result |
|
||||
|--------|-------------|--------|
|
||||
| `0x00` | `0x62` | Normal voltage (13V / 18V) |
|
||||
| `0x01` | `0x6A` | Boosted voltage (14V / 19V) |
|
||||
|
||||
The difference between the two XRAM values is **bit 3** (`0x08`), which enables the boost on the LNB power regulator circuitry.
|
||||
|
||||
<Aside type="caution">
|
||||
The LNB power output has firm current limits. The maximum **non-continuous** draw is 750 mA (for short bursts of several minutes). For permanent continuous load, do not exceed **450 mA** to avoid overheating the onboard regulator. Exceeding these limits risks thermal damage to the SkyWalker-1 board.
|
||||
</Aside>
|
||||
|
||||
The kernel driver exposes extra volt mode through the `enable_high_lnb_voltage` callback in the DVB frontend properties.
|
||||
|
||||
## 22 kHz Tone Generation
|
||||
|
||||
The 22 kHz tone selects the high or low band on a universal LNB. It is controlled via **GPIO P0.3** using vendor command `0x8C` (`SET_22KHZ_TONE`).
|
||||
|
||||
| wValue | State | GPIO P0.3 | LNB Band |
|
||||
|--------|-------|-----------|----------|
|
||||
| `0x00` | OFF | LOW | Low band (9.75 GHz LO on universal LNB) |
|
||||
| `0x01` | ON | HIGH | High band (10.6 GHz LO on universal LNB) |
|
||||
|
||||
<Aside type="note">
|
||||
P0.3 does not generate the 22 kHz carrier directly. Instead, it **gates an external 22 kHz oscillator circuit** on the PCB. When P0.3 is HIGH, the oscillator output is coupled onto the coaxial cable's DC power line alongside the 13V/18V LNB voltage. When P0.3 is LOW, the oscillator is disabled and no tone is present.
|
||||
</Aside>
|
||||
|
||||
The configuration status byte tracks the tone state in **bit 4** (`0x10`, `bm22kHz`).
|
||||
|
||||
## LNB Power Supply Enable
|
||||
|
||||
Before any LNB control is possible, the LNB power supply itself must be enabled via vendor command `0x8A` (`START_INTERSIL`). The kernel driver does this during the boot sequence if bit 2 (`bmIntersilOn`) of the configuration status byte is not already set.
|
||||
|
||||
| wValue | Action | Config Bit |
|
||||
|--------|--------|------------|
|
||||
| `0x01` | Enable LNB power supply | Sets `bmIntersilOn` (bit 2) |
|
||||
| `0x00` | Disable LNB power supply | Clears `bmIntersilOn` (bit 2) |
|
||||
|
||||
In the custom v3.01.0 firmware, enabling the LNB supply also configures the output enable masks for the LNB control GPIO pins:
|
||||
|
||||
```c title="START_INTERSIL handler in custom firmware"
|
||||
case START_INTERSIL:
|
||||
if (wval) {
|
||||
OEA |= (PIN_22KHZ | PIN_LNB_VOLT | PIN_DISEQC);
|
||||
config_status |= BM_INTERSIL;
|
||||
} else {
|
||||
config_status &= ~BM_INTERSIL;
|
||||
}
|
||||
```
|
||||
|
||||
## GPIO Pin Assignments
|
||||
|
||||
The LNB control pins are consistent across firmware versions, though their exact initialization differs:
|
||||
|
||||
| GPIO Pin | Function | Direction | Init State |
|
||||
|----------|----------|-----------|------------|
|
||||
| P0.1 | Power supply enable | Output | LOW (off) |
|
||||
| P0.2 | Power supply disable | Output | HIGH (active disable) |
|
||||
| P0.3 | 22 kHz tone gate | Output | LOW (tone off) |
|
||||
| P0.4 | LNB voltage select | Output | LOW (13V) |
|
||||
| P0.5 | BCM4500 hardware reset | Output | LOW (reset asserted) |
|
||||
|
||||
The initial GPIO state after power-on is `IOA = 0x84` (P0.7 and P0.2 HIGH, all others LOW), with the output enable mask `OEA = 0xBE` (P0.1 through P0.5 and P0.7 as outputs).
|
||||
|
||||
## LNB GPIO Debug Commands
|
||||
|
||||
Three vendor commands provide low-level GPIO access for LNB debugging:
|
||||
|
||||
| Command | Name | Purpose |
|
||||
|---------|------|---------|
|
||||
| `0x96` | `SET_LNB_GPIO_MODE` | Configure LNB GPIO output enables |
|
||||
| `0x97` | `SET_GPIO_PINS` | Direct write to LNB GPIO pins via `wValue` bitmap |
|
||||
| `0x98` | `GET_GPIO_STATUS` | Read LNB feedback GPIO pin (1 byte) |
|
||||
|
||||
These commands access Port B (XRAM-mapped IOB) rather than Port A. In v2.06 and v2.13, `0x97` controls IOB bits 1 and 2 for LNB, while `0x96` configures IOB bit 3 as the GPIO mode select. Rev.2 uses IOB bit 4 instead.
|
||||
|
||||
<Aside type="note">
|
||||
These debug GPIO commands are not used by the Linux kernel driver during normal operation. They are available for diagnostic tools and custom applications that need direct hardware-level LNB control.
|
||||
</Aside>
|
||||
|
||||
## Complete LNB Configuration Sequence
|
||||
|
||||
During a typical tune operation, the kernel driver configures the LNB in this order:
|
||||
|
||||
```
|
||||
1. SET_LNB_VOLTAGE (0x8B) -- Select 13V or 18V based on polarization
|
||||
2. SET_22KHZ_TONE (0x8C) -- Select low or high band
|
||||
3. SEND_DISEQC (0x8D) -- Switch multi-port switch if needed
|
||||
4. TUNE_8PSK (0x86) -- Send tuning parameters
|
||||
```
|
||||
|
||||
The voltage and tone are set before any DiSEqC commands because the LNB must be powered and in the correct band before the switch can respond. See the [DiSEqC Protocol](/lnb-diseqc/diseqc-protocol/) page for details on switch control, and the [Tuning Protocol](/bcm4500/tuning-protocol/) for the complete tune flow.
|
||||
|
|
|
|||
|
|
@ -1,65 +1,65 @@
|
|||
---
|
||||
title: Sources
|
||||
description: Primary sources, analysis documents, and community references for the SkyWalker-1 reverse engineering project.
|
||||
---
|
||||
|
||||
# Sources and Bibliography
|
||||
|
||||
All technical content in this documentation was derived from the following primary sources. No public BCM4500 datasheet with complete register definitions exists; register addresses, functions, and protocols were determined through firmware disassembly, I2C bus captures, driver source analysis, and behavioral testing.
|
||||
|
||||
---
|
||||
|
||||
## Firmware Analysis
|
||||
|
||||
Five firmware images were decompiled and disassembled using Ghidra (8051 processor module):
|
||||
|
||||
| Firmware | Ghidra Port | Source |
|
||||
|----------|-------------|--------|
|
||||
| v2.06.04 | 8193 | Extracted from SkyWalker-1 EEPROM via `eeprom_dump.py` |
|
||||
| Rev.2 v2.10.04 | 8197 | Extracted from Rev.2 hardware EEPROM |
|
||||
| v2.13.01 (FW1) | 8194 | Extracted from `SW1_update_2_13_x.exe` via `wine_memdump.py` |
|
||||
| v2.13.02 (FW2) | 8195 | Extracted from `SW1_update_2_13_x.exe` via `wine_memdump.py` |
|
||||
| v2.13.03 (FW3) | 8196 | Extracted from `SW1_update_2_13_x.exe` via `wine_memdump.py` |
|
||||
|
||||
Binary dumps are in `firmware-dump/`.
|
||||
|
||||
## Driver Source
|
||||
|
||||
| Source | Path / Location |
|
||||
|--------|----------------|
|
||||
| Linux kernel 6.16.5 | `drivers/media/usb/dvb-usb/gp8psk.c`, `gp8psk.h`, `gp8psk-fe.c`, `gp8psk-fe.h` |
|
||||
| Linux kernel | `drivers/media/usb/dvb-usb/dvb-usb-firmware.c` |
|
||||
| Windows BDA driver | `SkyWalker1_Final_Release/Source/SkyWalker1Control.cpp` |
|
||||
| Windows BDA driver | `SkyWalker1_Final_Release/Include/SkyWalker1Control.h`, `SkyWalker1CommonDef.h` |
|
||||
|
||||
## Hardware Documentation
|
||||
|
||||
- BCM4500 Datasheet: [DatasheetQ](https://html.datasheetq.com/pdf-html/885700/Broadcom/2page/BCM4500.html), [Elcodis](https://elcodis.com/parts/5786421/BCM4500.html)
|
||||
- BCM4501 Product Page: [Broadcom](https://www.broadcom.com/products/broadband/set-top-box/bcm4501)
|
||||
- Cypress CY7C68013A (FX2LP) Technical Reference Manual
|
||||
- Genpix Electronics: https://www.genpix-electronics.com/index.php?act=viewDoc&docId=9
|
||||
- Genpix SkyWalker-3 specifications: https://www.genpix-electronics.com/what-is-skywalker-3.html
|
||||
- Device `dmesg` output from running SkyWalker-1 hardware
|
||||
|
||||
## Analysis Reports
|
||||
|
||||
Internal analysis documents produced during this project (in `docs/` directory):
|
||||
|
||||
1. `gp8psk-driver-analysis.md` -- Linux kernel driver analysis
|
||||
2. `firmware-analysis-v206-vs-v213.md` -- v2.06 vs v2.13 firmware comparison
|
||||
3. `rev2-deep-analysis.md` -- Rev.2 deep function inventory (107 functions)
|
||||
4. `gpif-streaming-analysis.md` -- GPIF/MPEG-2 streaming path
|
||||
5. `tuning-protocol-analysis.md` -- TUNE_8PSK protocol deep dive
|
||||
6. `vendor-commands-unknown.md` -- Vendor command decode (0x8F, 0x91--0x98)
|
||||
7. `kernel-fw01-analysis.md` -- Kernel firmware format and EEPROM boot
|
||||
8. `firmware-dump/fw_v213_comparison_report.md` -- v2.13 sub-variant comparison
|
||||
9. `dvb-s2-investigation.md` -- DVB-S2 incompatibility investigation
|
||||
10. `docs/boot-debug-findings.md` -- Boot/I2C debugging findings
|
||||
11. `docs/diseqc/diseqc-skywalker-1.md` -- DiSEqC Windows BDA interface
|
||||
12. `firmware/skywalker1.c` -- Custom firmware v3.01.0 source
|
||||
|
||||
## Community References
|
||||
|
||||
- [LinuxTV mailing list: BCM4500 and DVB-S2](https://www.mail-archive.com/linux-dvb@linuxtv.org/msg24808.html)
|
||||
- [SatelliteGuys: Turbo 8PSK card reverse engineering](https://www.satelliteguys.us/xen/threads/another-turbo-8psk-card.246879/)
|
||||
- [SatelliteGuys: Genpix SkyWalker-1 discussion](https://www.satelliteguys.us/xen/threads/genpix-skywalker-1.214196/)
|
||||
---
|
||||
title: Sources
|
||||
description: Primary sources, analysis documents, and community references for the SkyWalker-1 reverse engineering project.
|
||||
---
|
||||
|
||||
# Sources and Bibliography
|
||||
|
||||
All technical content in this documentation was derived from the following primary sources. No public BCM4500 datasheet with complete register definitions exists; register addresses, functions, and protocols were determined through firmware disassembly, I2C bus captures, driver source analysis, and behavioral testing.
|
||||
|
||||
---
|
||||
|
||||
## Firmware Analysis
|
||||
|
||||
Five firmware images were decompiled and disassembled using Ghidra (8051 processor module):
|
||||
|
||||
| Firmware | Ghidra Port | Source |
|
||||
|----------|-------------|--------|
|
||||
| v2.06.04 | 8193 | Extracted from SkyWalker-1 EEPROM via `eeprom_dump.py` |
|
||||
| Rev.2 v2.10.04 | 8197 | Extracted from Rev.2 hardware EEPROM |
|
||||
| v2.13.01 (FW1) | 8194 | Extracted from `SW1_update_2_13_x.exe` via `wine_memdump.py` |
|
||||
| v2.13.02 (FW2) | 8195 | Extracted from `SW1_update_2_13_x.exe` via `wine_memdump.py` |
|
||||
| v2.13.03 (FW3) | 8196 | Extracted from `SW1_update_2_13_x.exe` via `wine_memdump.py` |
|
||||
|
||||
Binary dumps are in `firmware-dump/`.
|
||||
|
||||
## Driver Source
|
||||
|
||||
| Source | Path / Location |
|
||||
|--------|----------------|
|
||||
| Linux kernel 6.16.5 | `drivers/media/usb/dvb-usb/gp8psk.c`, `gp8psk.h`, `gp8psk-fe.c`, `gp8psk-fe.h` |
|
||||
| Linux kernel | `drivers/media/usb/dvb-usb/dvb-usb-firmware.c` |
|
||||
| Windows BDA driver | `SkyWalker1_Final_Release/Source/SkyWalker1Control.cpp` |
|
||||
| Windows BDA driver | `SkyWalker1_Final_Release/Include/SkyWalker1Control.h`, `SkyWalker1CommonDef.h` |
|
||||
|
||||
## Hardware Documentation
|
||||
|
||||
- BCM4500 Datasheet: [DatasheetQ](https://html.datasheetq.com/pdf-html/885700/Broadcom/2page/BCM4500.html), [Elcodis](https://elcodis.com/parts/5786421/BCM4500.html)
|
||||
- BCM4501 Product Page: [Broadcom](https://www.broadcom.com/products/broadband/set-top-box/bcm4501)
|
||||
- Cypress CY7C68013A (FX2LP) Technical Reference Manual
|
||||
- Genpix Electronics: https://www.genpix-electronics.com/index.php?act=viewDoc&docId=9
|
||||
- Genpix SkyWalker-3 specifications: https://www.genpix-electronics.com/what-is-skywalker-3.html
|
||||
- Device `dmesg` output from running SkyWalker-1 hardware
|
||||
|
||||
## Analysis Reports
|
||||
|
||||
Internal analysis documents produced during this project (in `docs/` directory):
|
||||
|
||||
1. `gp8psk-driver-analysis.md` -- Linux kernel driver analysis
|
||||
2. `firmware-analysis-v206-vs-v213.md` -- v2.06 vs v2.13 firmware comparison
|
||||
3. `rev2-deep-analysis.md` -- Rev.2 deep function inventory (107 functions)
|
||||
4. `gpif-streaming-analysis.md` -- GPIF/MPEG-2 streaming path
|
||||
5. `tuning-protocol-analysis.md` -- TUNE_8PSK protocol deep dive
|
||||
6. `vendor-commands-unknown.md` -- Vendor command decode (0x8F, 0x91--0x98)
|
||||
7. `kernel-fw01-analysis.md` -- Kernel firmware format and EEPROM boot
|
||||
8. `firmware-dump/fw_v213_comparison_report.md` -- v2.13 sub-variant comparison
|
||||
9. `dvb-s2-investigation.md` -- DVB-S2 incompatibility investigation
|
||||
10. `docs/boot-debug-findings.md` -- Boot/I2C debugging findings
|
||||
11. `docs/diseqc/diseqc-skywalker-1.md` -- DiSEqC Windows BDA interface
|
||||
12. `firmware/skywalker1.c` -- Custom firmware v3.01.0 source
|
||||
|
||||
## Community References
|
||||
|
||||
- [LinuxTV mailing list: BCM4500 and DVB-S2](https://www.mail-archive.com/linux-dvb@linuxtv.org/msg24808.html)
|
||||
- [SatelliteGuys: Turbo 8PSK card reverse engineering](https://www.satelliteguys.us/xen/threads/another-turbo-8psk-card.246879/)
|
||||
- [SatelliteGuys: Genpix SkyWalker-1 discussion](https://www.satelliteguys.us/xen/threads/genpix-skywalker-1.214196/)
|
||||
|
|
|
|||
|
|
@ -1,95 +1,95 @@
|
|||
---
|
||||
title: Arc Survey
|
||||
description: Automated multi-satellite orbital arc survey with motor control and carrier catalog aggregation.
|
||||
---
|
||||
|
||||
import { Aside, Steps } from '@astrojs/starlight/components';
|
||||
|
||||
The `arc_survey.py` tool automates a complete satellite census across the GEO orbital arc.
|
||||
It points the dish motor to each orbital longitude, runs a full six-stage carrier survey,
|
||||
saves individual catalogs, and produces an aggregated sky map.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Survey 4 specific North American slots
|
||||
python tools/arc_survey.py --observer-lon -96.8 --slots "97W,99W,101W,103W"
|
||||
|
||||
# Survey a 60-degree arc at 3-degree intervals
|
||||
python tools/arc_survey.py --observer-lon -96.8 --arc -120 -60 --step 3
|
||||
|
||||
# List common NA orbital positions
|
||||
python tools/arc_survey.py --list-slots
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
<Steps>
|
||||
1. **Parse orbital slot list** from CLI, JSON file, or arc range
|
||||
2. **For each slot**: send USALS GotoX command to motor
|
||||
3. **Wait for settle** (scales with angular distance, minimum 15 seconds)
|
||||
4. **Run six-stage survey**: coarse sweep → peak detection → fine sweep → blind scan → TS sample → catalog
|
||||
5. **Save per-slot catalog** to `~/.skywalker1/surveys/`
|
||||
6. **Save arc survey state** to `~/.skywalker1/arc-surveys/` (for resume)
|
||||
7. **Print aggregated summary** with per-slot carrier counts
|
||||
</Steps>
|
||||
|
||||
## Resume Support
|
||||
|
||||
The tool saves state after every completed slot. If interrupted (Ctrl-C, power loss, etc.):
|
||||
|
||||
```bash
|
||||
python tools/arc_survey.py --resume ~/.skywalker1/arc-surveys/arc-survey-2026-02-17.json
|
||||
```
|
||||
|
||||
Already-surveyed slots are skipped. The survey continues from where it left off.
|
||||
|
||||
## Slot Specification
|
||||
|
||||
<Aside type="tip" title="Slot formats">
|
||||
Slots accept multiple formats: `97W`, `97.5W`, `3E`, `-97`, `-97.5`
|
||||
</Aside>
|
||||
|
||||
**CLI list:**
|
||||
```bash
|
||||
--slots "97W,99W,101W,103W,105W"
|
||||
```
|
||||
|
||||
**JSON file:**
|
||||
```json
|
||||
[
|
||||
{"name": "97W", "lon": -97.0},
|
||||
{"name": "99W", "lon": -99.0},
|
||||
{"name": "Galaxy 16", "lon": -99.0}
|
||||
]
|
||||
```
|
||||
|
||||
**Arc range:**
|
||||
```bash
|
||||
--arc -120 -60 --step 3
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
| Flag | Default | Description |
|
||||
|---|---|---|
|
||||
| `--observer-lon` | (required) | Observer longitude (negative = west) |
|
||||
| `--observer-lat` | 0.0 | Observer latitude |
|
||||
| `--slots` | — | Comma-separated slot list |
|
||||
| `--file` | — | JSON file with slot definitions |
|
||||
| `--arc` | — | Start/stop longitude for range scan |
|
||||
| `--step` | 3.0 | Degrees between arc positions |
|
||||
| `--coarse-step` | 5.0 | MHz step for coarse spectrum sweep |
|
||||
| `--settle-time` | 15 | Minimum motor settle time (seconds) |
|
||||
| `--resume` | — | Resume from saved state file |
|
||||
| `--list-slots` | — | Print common NA slots and exit |
|
||||
|
||||
## Output Files
|
||||
|
||||
| Location | Content |
|
||||
|---|---|
|
||||
| `~/.skywalker1/surveys/arc-DATE-SLOT.json` | Per-slot carrier catalog |
|
||||
| `~/.skywalker1/arc-surveys/arc-survey-DATE.json` | Arc survey state (for resume) |
|
||||
|
||||
The per-slot catalogs are standard `CarrierCatalog` JSON files, compatible with
|
||||
the diff and comparison tools in `carrier_catalog.py`.
|
||||
---
|
||||
title: Arc Survey
|
||||
description: Automated multi-satellite orbital arc survey with motor control and carrier catalog aggregation.
|
||||
---
|
||||
|
||||
import { Aside, Steps } from '@astrojs/starlight/components';
|
||||
|
||||
The `arc_survey.py` tool automates a complete satellite census across the GEO orbital arc.
|
||||
It points the dish motor to each orbital longitude, runs a full six-stage carrier survey,
|
||||
saves individual catalogs, and produces an aggregated sky map.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Survey 4 specific North American slots
|
||||
python tools/arc_survey.py --observer-lon -96.8 --slots "97W,99W,101W,103W"
|
||||
|
||||
# Survey a 60-degree arc at 3-degree intervals
|
||||
python tools/arc_survey.py --observer-lon -96.8 --arc -120 -60 --step 3
|
||||
|
||||
# List common NA orbital positions
|
||||
python tools/arc_survey.py --list-slots
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
<Steps>
|
||||
1. **Parse orbital slot list** from CLI, JSON file, or arc range
|
||||
2. **For each slot**: send USALS GotoX command to motor
|
||||
3. **Wait for settle** (scales with angular distance, minimum 15 seconds)
|
||||
4. **Run six-stage survey**: coarse sweep → peak detection → fine sweep → blind scan → TS sample → catalog
|
||||
5. **Save per-slot catalog** to `~/.skywalker1/surveys/`
|
||||
6. **Save arc survey state** to `~/.skywalker1/arc-surveys/` (for resume)
|
||||
7. **Print aggregated summary** with per-slot carrier counts
|
||||
</Steps>
|
||||
|
||||
## Resume Support
|
||||
|
||||
The tool saves state after every completed slot. If interrupted (Ctrl-C, power loss, etc.):
|
||||
|
||||
```bash
|
||||
python tools/arc_survey.py --resume ~/.skywalker1/arc-surveys/arc-survey-2026-02-17.json
|
||||
```
|
||||
|
||||
Already-surveyed slots are skipped. The survey continues from where it left off.
|
||||
|
||||
## Slot Specification
|
||||
|
||||
<Aside type="tip" title="Slot formats">
|
||||
Slots accept multiple formats: `97W`, `97.5W`, `3E`, `-97`, `-97.5`
|
||||
</Aside>
|
||||
|
||||
**CLI list:**
|
||||
```bash
|
||||
--slots "97W,99W,101W,103W,105W"
|
||||
```
|
||||
|
||||
**JSON file:**
|
||||
```json
|
||||
[
|
||||
{"name": "97W", "lon": -97.0},
|
||||
{"name": "99W", "lon": -99.0},
|
||||
{"name": "Galaxy 16", "lon": -99.0}
|
||||
]
|
||||
```
|
||||
|
||||
**Arc range:**
|
||||
```bash
|
||||
--arc -120 -60 --step 3
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
| Flag | Default | Description |
|
||||
|---|---|---|
|
||||
| `--observer-lon` | (required) | Observer longitude (negative = west) |
|
||||
| `--observer-lat` | 0.0 | Observer latitude |
|
||||
| `--slots` | — | Comma-separated slot list |
|
||||
| `--file` | — | JSON file with slot definitions |
|
||||
| `--arc` | — | Start/stop longitude for range scan |
|
||||
| `--step` | 3.0 | Degrees between arc positions |
|
||||
| `--coarse-step` | 5.0 | MHz step for coarse spectrum sweep |
|
||||
| `--settle-time` | 15 | Minimum motor settle time (seconds) |
|
||||
| `--resume` | — | Resume from saved state file |
|
||||
| `--list-slots` | — | Print common NA slots and exit |
|
||||
|
||||
## Output Files
|
||||
|
||||
| Location | Content |
|
||||
|---|---|
|
||||
| `~/.skywalker1/surveys/arc-DATE-SLOT.json` | Per-slot carrier catalog |
|
||||
| `~/.skywalker1/arc-surveys/arc-survey-DATE.json` | Arc survey state (for resume) |
|
||||
|
||||
The per-slot catalogs are standard `CarrierCatalog` JSON files, compatible with
|
||||
the diff and comparison tools in `carrier_catalog.py`.
|
||||
|
|
|
|||
|
|
@ -1,75 +1,75 @@
|
|||
---
|
||||
title: Beacon Logger
|
||||
description: Long-term satellite signal logging for propagation research, rain fade analysis, and link budget validation.
|
||||
---
|
||||
|
||||
import { Aside, Steps } from '@astrojs/starlight/components';
|
||||
|
||||
The `beacon_logger.py` tool locks onto a stable satellite transponder and records SNR, AGC levels,
|
||||
and signal power at configurable intervals. Multi-day datasets reveal rain fade events, diurnal
|
||||
thermal effects, antenna mount drift, and LNB gain variations.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Log a Ku-band transponder for 1 hour, CSV output
|
||||
python tools/beacon_logger.py --freq 1265000 --sr 20000000 --output beacon.csv
|
||||
|
||||
# 24-hour unattended logging with per-minute statistics
|
||||
python tools/beacon_logger.py --freq 1265000 --sr 20000000 \
|
||||
--output beacon-24h.csv --json-output stats.jsonl --duration 86400
|
||||
```
|
||||
|
||||
<Aside type="note" title="Frequency units">
|
||||
The `--freq` parameter is in **kHz** (IF frequency). For a Ku-band transponder at 12015 MHz
|
||||
with a universal LNB at LO 10750 MHz, the IF is 12015 − 10750 = 1265 MHz = **1265000 kHz**.
|
||||
</Aside>
|
||||
|
||||
## Features
|
||||
|
||||
- **Auto-relock**: If signal is lost (weather, dish movement), automatically retunes and relocks
|
||||
- **Statistics per interval**: min/max/mean/stddev of SNR over each reporting window
|
||||
- **Dual output**: Raw per-sample CSV + per-interval JSONL statistics
|
||||
- **Signal handlers**: Clean shutdown on SIGTERM/SIGINT, no data loss
|
||||
- **Systemd integration**: `--generate-systemd` prints a ready-to-use unit file
|
||||
|
||||
## Options
|
||||
|
||||
| Flag | Default | Description |
|
||||
|---|---|---|
|
||||
| `--freq` | (required) | IF frequency in kHz |
|
||||
| `--sr` | 20000000 | Symbol rate in sps |
|
||||
| `--mod` | qpsk | Modulation type |
|
||||
| `--fec` | auto | FEC rate |
|
||||
| `--output` | — | CSV output file (raw samples) |
|
||||
| `--json-output` | — | JSONL file (per-interval statistics) |
|
||||
| `--duration` | 3600 | Logging duration in seconds |
|
||||
| `--sample-interval` | 1.0 | Seconds between samples |
|
||||
| `--report-interval` | 60 | Seconds between summary reports |
|
||||
| `--pol` | — | LNB polarization (H/V) |
|
||||
| `--band` | — | LNB band (low/high) |
|
||||
| `--daemon` | — | Suppress stdout for background operation |
|
||||
| `--generate-systemd` | — | Print systemd unit file and exit |
|
||||
|
||||
## Systemd Daemon Mode
|
||||
|
||||
```bash
|
||||
# Generate and install the service
|
||||
python tools/beacon_logger.py --generate-systemd \
|
||||
--freq 1265000 --sr 20000000 \
|
||||
--output /var/log/skywalker/beacon.csv \
|
||||
--duration 999999 > /tmp/beacon-logger.service
|
||||
|
||||
sudo cp /tmp/beacon-logger.service /etc/systemd/system/
|
||||
sudo systemctl enable --now beacon-logger
|
||||
```
|
||||
|
||||
## Applications
|
||||
|
||||
| Use Case | What to Measure | Interval |
|
||||
|---|---|---|
|
||||
| Rain fade | SNR drops during precipitation | 1 Hz |
|
||||
| LNB thermal drift | AGC shift over temperature cycle | 10s |
|
||||
| Antenna mount stability | Slow SNR decay over days | 60s |
|
||||
| Link budget validation | Long-term average vs. predicted | 60s |
|
||||
| Ionospheric scintillation | Rapid AGC fluctuations | 1 Hz |
|
||||
---
|
||||
title: Beacon Logger
|
||||
description: Long-term satellite signal logging for propagation research, rain fade analysis, and link budget validation.
|
||||
---
|
||||
|
||||
import { Aside, Steps } from '@astrojs/starlight/components';
|
||||
|
||||
The `beacon_logger.py` tool locks onto a stable satellite transponder and records SNR, AGC levels,
|
||||
and signal power at configurable intervals. Multi-day datasets reveal rain fade events, diurnal
|
||||
thermal effects, antenna mount drift, and LNB gain variations.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Log a Ku-band transponder for 1 hour, CSV output
|
||||
python tools/beacon_logger.py --freq 1265000 --sr 20000000 --output beacon.csv
|
||||
|
||||
# 24-hour unattended logging with per-minute statistics
|
||||
python tools/beacon_logger.py --freq 1265000 --sr 20000000 \
|
||||
--output beacon-24h.csv --json-output stats.jsonl --duration 86400
|
||||
```
|
||||
|
||||
<Aside type="note" title="Frequency units">
|
||||
The `--freq` parameter is in **kHz** (IF frequency). For a Ku-band transponder at 12015 MHz
|
||||
with a universal LNB at LO 10750 MHz, the IF is 12015 − 10750 = 1265 MHz = **1265000 kHz**.
|
||||
</Aside>
|
||||
|
||||
## Features
|
||||
|
||||
- **Auto-relock**: If signal is lost (weather, dish movement), automatically retunes and relocks
|
||||
- **Statistics per interval**: min/max/mean/stddev of SNR over each reporting window
|
||||
- **Dual output**: Raw per-sample CSV + per-interval JSONL statistics
|
||||
- **Signal handlers**: Clean shutdown on SIGTERM/SIGINT, no data loss
|
||||
- **Systemd integration**: `--generate-systemd` prints a ready-to-use unit file
|
||||
|
||||
## Options
|
||||
|
||||
| Flag | Default | Description |
|
||||
|---|---|---|
|
||||
| `--freq` | (required) | IF frequency in kHz |
|
||||
| `--sr` | 20000000 | Symbol rate in sps |
|
||||
| `--mod` | qpsk | Modulation type |
|
||||
| `--fec` | auto | FEC rate |
|
||||
| `--output` | — | CSV output file (raw samples) |
|
||||
| `--json-output` | — | JSONL file (per-interval statistics) |
|
||||
| `--duration` | 3600 | Logging duration in seconds |
|
||||
| `--sample-interval` | 1.0 | Seconds between samples |
|
||||
| `--report-interval` | 60 | Seconds between summary reports |
|
||||
| `--pol` | — | LNB polarization (H/V) |
|
||||
| `--band` | — | LNB band (low/high) |
|
||||
| `--daemon` | — | Suppress stdout for background operation |
|
||||
| `--generate-systemd` | — | Print systemd unit file and exit |
|
||||
|
||||
## Systemd Daemon Mode
|
||||
|
||||
```bash
|
||||
# Generate and install the service
|
||||
python tools/beacon_logger.py --generate-systemd \
|
||||
--freq 1265000 --sr 20000000 \
|
||||
--output /var/log/skywalker/beacon.csv \
|
||||
--duration 999999 > /tmp/beacon-logger.service
|
||||
|
||||
sudo cp /tmp/beacon-logger.service /etc/systemd/system/
|
||||
sudo systemctl enable --now beacon-logger
|
||||
```
|
||||
|
||||
## Applications
|
||||
|
||||
| Use Case | What to Measure | Interval |
|
||||
|---|---|---|
|
||||
| Rain fade | SNR drops during precipitation | 1 Hz |
|
||||
| LNB thermal drift | AGC shift over temperature cycle | 10s |
|
||||
| Antenna mount stability | Slow SNR decay over days | 60s |
|
||||
| Link budget validation | Long-term average vs. predicted | 60s |
|
||||
| Ionospheric scintillation | Rapid AGC fluctuations | 1 Hz |
|
||||
|
|
|
|||
|
|
@ -1,259 +1,259 @@
|
|||
---
|
||||
title: Debugging Tools
|
||||
description: Hardware diagnostic tools for boot testing, I2C bus debugging, and firmware extraction.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Steps, Aside, Badge } from '@astrojs/starlight/components';
|
||||
|
||||
The `tools/` directory includes a suite of diagnostic scripts developed during the reverse engineering of the SkyWalker-1. These tools were instrumental in isolating the I2C STOP corruption bug in the FX2 controller and verifying correct BCM4500 initialization.
|
||||
|
||||
All debugging tools require `pyusb`, root access, and the kernel `dvb_usb_gp8psk` module to be unloaded.
|
||||
|
||||
```bash
|
||||
pip install pyusb
|
||||
sudo modprobe -r dvb_usb_gp8psk gp8psk_fe
|
||||
```
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Boot Tests">
|
||||
|
||||
## Boot Testing Tools
|
||||
|
||||
Two scripts test the BCM4500 boot sequence using debug modes in the custom v3.01.0 firmware.
|
||||
|
||||
### test_boot.py -- Full Boot Verification
|
||||
|
||||
Runs a complete BOOT_8PSK sequence and reports success or failure with detailed register readback.
|
||||
|
||||
```bash
|
||||
sudo python3 tools/test_boot.py
|
||||
```
|
||||
|
||||
**What it does:**
|
||||
|
||||
<Steps>
|
||||
1. Reads firmware version via `GET_FW_VERS` (0x92)
|
||||
2. Reads config status via `GET_8PSK_CONFIG` (0x80)
|
||||
3. Sends `BOOT_8PSK` (0x89, wValue=1) with a 10-second timeout
|
||||
4. Decodes the 3-byte response: config status, boot stage, probe byte
|
||||
5. On success: reads BCM4500 direct registers (0xA2--0xA8), indirect registers (pages 0x00--0x0F), I2C diagnostic data, signal strength, and lock status
|
||||
6. On failure: runs I2C bus scan (0xB4), attempts raw I2C reads to BCM4500
|
||||
</Steps>
|
||||
|
||||
**Boot stage codes:**
|
||||
|
||||
| Stage | Code | Meaning |
|
||||
|-------|------|---------|
|
||||
| `NOT_STARTED` | 0x00 | Boot not attempted |
|
||||
| `GPIO_SETUP` | 0x01 | GPIO pins configured |
|
||||
| `PWR_SETTLED` | 0x02 | Power-on delay complete |
|
||||
| `I2C_PROBE` | 0x03 | Probing BCM4500 on I2C |
|
||||
| `INIT_BLK0` | 0x04 | Writing init block 0 |
|
||||
| `INIT_BLK1` | 0x05 | Writing init block 1 |
|
||||
| `INIT_BLK2` | 0x06 | Writing init block 2 |
|
||||
| `COMPLETE` | 0xFF | Boot finished |
|
||||
|
||||
**Config status flags after successful boot:**
|
||||
|
||||
| Flag | Bit | Expected |
|
||||
|------|-----|----------|
|
||||
| `bm8pskStarted` | 0x01 | <Badge text="ON" variant="success" /> |
|
||||
| `bm8pskFW_Loaded` | 0x02 | <Badge text="ON" variant="success" /> |
|
||||
|
||||
### test_boot_debug.py -- Incremental Stage Testing
|
||||
|
||||
Sends debug boot modes (wValue=0x80 through 0x83) one at a time to isolate which stage of the boot sequence fails. Can test a single stage or run all sequentially.
|
||||
|
||||
```bash title="Run all debug stages"
|
||||
sudo python3 tools/test_boot_debug.py
|
||||
```
|
||||
|
||||
```bash title="Test only stage 0x82"
|
||||
sudo python3 tools/test_boot_debug.py 0x82
|
||||
```
|
||||
|
||||
**Debug modes:**
|
||||
|
||||
| wValue | Action | Tests |
|
||||
|--------|--------|-------|
|
||||
| `0x80` | No-op: return current state | Firmware responsive? |
|
||||
| `0x81` | GPIO setup + power + delays (no I2C) | Power rails working? |
|
||||
| `0x82` | GPIO + I2C bus reset + BCM4500 probe | I2C communication working? |
|
||||
| `0x83` | GPIO + I2C probe + write init block 0 | Register writes accepted? |
|
||||
|
||||
Each mode returns a 3-byte response: config status, boot stage, and probe byte. The tool reports timing (ms) for each stage and checks if the device is still responsive after failures.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="I2C Tests">
|
||||
|
||||
## I2C Debugging Suite
|
||||
|
||||
Three scripts that implement a progressive isolation methodology to identify I2C bus faults. These were developed in sequence, each informed by the results of the previous one.
|
||||
|
||||
### Methodology
|
||||
|
||||
<Steps>
|
||||
1. **Broad scan** (`test_i2c_debug.py`) -- Power on the BCM4500 and probe the entire I2C bus. Does the chip respond at any address?
|
||||
2. **Isolation** (`test_i2c_isolate.py`) -- Determine whether the fault is in the BCM4500, the I2C bus, or the FX2 controller. Test the chip when already powered vs. after a reset.
|
||||
3. **Pinpoint** (`test_i2c_pinpoint.py`) -- Identify the exact operation that corrupts communication. Compare I2C read-only, GPIO+reset, and GPIO+bus-reset+probe modes.
|
||||
</Steps>
|
||||
|
||||
### test_i2c_debug.py -- Bus Discovery
|
||||
|
||||
Powers on the BCM4500 via GPIO-only mode (0x81), then runs progressively detailed diagnostics.
|
||||
|
||||
```bash
|
||||
sudo python3 tools/test_i2c_debug.py
|
||||
```
|
||||
|
||||
**Test sequence:**
|
||||
|
||||
| Step | Action | Purpose |
|
||||
|------|--------|---------|
|
||||
| 1 | Send mode 0x81 (GPIO power on) | Start BCM4500 without any I2C |
|
||||
| 2 | I2C bus scan (0xB4) immediately | Check for devices right after power-on |
|
||||
| 3 | Wait 500 ms, scan again | Maybe chip needs more time? |
|
||||
| 4 | Raw I2C reads to candidate addresses | Try 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x11, 0x68, 0x69, 0x60, 0x61 |
|
||||
| 5 | Check I2C controller state | Observe bus health |
|
||||
| 6 | Send mode 0x82 after 1s delay | Test probe with extra settling time |
|
||||
|
||||
**Candidate addresses** cover different BCM4500 pin-strap configurations and common demodulator/tuner addresses.
|
||||
|
||||
### test_i2c_isolate.py -- Fault Isolation
|
||||
|
||||
Determines whether the failure is caused by `bcm_direct_read`, re-reset timing, or the I2C bus reset (bmSTOP).
|
||||
|
||||
```bash
|
||||
sudo python3 tools/test_i2c_isolate.py
|
||||
```
|
||||
|
||||
**Test sequence:**
|
||||
|
||||
| Test | Action | Question |
|
||||
|------|--------|----------|
|
||||
| **A** | Power on (0x81), wait 1s, raw read | Is BCM4500 alive from cold start? |
|
||||
| **B** | Run mode 0x82 (re-resets + probe) | Does `bcm_direct_read` work after reset? |
|
||||
| **C** | Immediately raw read after 0x82 | Is I2C function itself broken? |
|
||||
| **D** | Raw reads at 100, 200, 500, 1000, 2000 ms | Is it a timing issue? |
|
||||
| **E** | Re-power (0x81), wait, then 0x82 | Repeatable? |
|
||||
|
||||
### test_i2c_pinpoint.py -- Root Cause Identification
|
||||
|
||||
The definitive test that identified the I2C STOP corruption bug. Compares three modes on a chip that is already proven alive.
|
||||
|
||||
```bash
|
||||
sudo python3 tools/test_i2c_pinpoint.py
|
||||
```
|
||||
|
||||
**Test sequence:**
|
||||
|
||||
<Steps>
|
||||
1. Power on BCM4500 via mode 0x81, confirm alive with raw read
|
||||
2. **Mode 0x84**: `bcm_direct_read` only (no GPIO, chip already powered) -- tests if the read function itself is broken
|
||||
3. **Mode 0x85**: GPIO + reset + power but NO I2C bus reset (no `bmSTOP`) -- tests if re-reset breaks things
|
||||
4. **Mode 0x82**: GPIO + I2C `bmSTOP` + reset + power + probe -- the mode that was failing
|
||||
</Steps>
|
||||
|
||||
**Interpretation:**
|
||||
|
||||
| Result Pattern | Diagnosis |
|
||||
|----------------|-----------|
|
||||
| 0x84 works, 0x85 works, 0x82 fails | `bmSTOP` (I2C bus reset) is the culprit |
|
||||
| 0x84 fails | `bcm_direct_read` has a bug |
|
||||
| 0x85 fails | Re-reset timing needs more delay |
|
||||
|
||||
<Aside type="note">
|
||||
This test conclusively proved that the spurious `I2CS |= bmSTOP` in the boot sequence was corrupting the FX2 I2C controller. Mode 0x84 succeeded immediately after 0x82 failed, proving the BCM4500 was alive the entire time -- only the FX2 controller was in a bad state. See [I2C STOP Corruption Bug](/i2c/stop-corruption-bug/) for the full analysis.
|
||||
</Aside>
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Memory Extraction">
|
||||
|
||||
## Windows Memory Dump (wine_memdump.py)
|
||||
|
||||
Extracts firmware from the Genpix Windows driver/updater by running it under Wine and dumping process memory. The Windows updater executable contains embedded firmware images that are unpacked into memory at runtime.
|
||||
|
||||
<Aside type="note">
|
||||
This tool requires **Wine** to be installed. It reads `/proc/PID/mem` and `/proc/PID/maps`, which requires running as the same user as the Wine process or as root.
|
||||
</Aside>
|
||||
|
||||
```bash
|
||||
sudo python3 tools/wine_memdump.py SkyWalkerUpdater.exe
|
||||
```
|
||||
|
||||
```bash title="Attach to an already-running Wine process"
|
||||
sudo python3 tools/wine_memdump.py SkyWalkerUpdater.exe --skip-launch
|
||||
```
|
||||
|
||||
```bash title="Custom output directory and wait time"
|
||||
sudo python3 tools/wine_memdump.py SkyWalkerUpdater.exe -o dumps/ --wait 5
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `EXE` | Windows PE executable to run under Wine (positional, required) |
|
||||
| `-o, --output-dir DIR` | Output directory for dumps (default: `.`) |
|
||||
| `--wait SECONDS` | Seconds to wait after launch for unpacking (default: `3`) |
|
||||
| `--skip-launch` | Attach to an already-running Wine process |
|
||||
|
||||
### How It Works
|
||||
|
||||
<Steps>
|
||||
1. Launches the `.exe` under Wine with `WINEDEBUG=-all` to suppress noise
|
||||
2. Waits for the process to unpack (configurable delay)
|
||||
3. Finds the Wine process PID by scanning `/proc`
|
||||
4. Reads `/proc/PID/maps` to identify readable memory regions
|
||||
5. Reads each region from `/proc/PID/mem` into a contiguous dump
|
||||
6. Saves the full dump (`wine_memdump.bin`) and region map (`wine_memdump_regions.txt`)
|
||||
7. Searches the dump for firmware signatures
|
||||
</Steps>
|
||||
|
||||
### Firmware Signature Search
|
||||
|
||||
The tool searches for 7 categories of signatures:
|
||||
|
||||
| Search | Pattern | Purpose |
|
||||
|--------|---------|---------|
|
||||
| C2 EEPROM headers | `C2 C0 09 03 02` | Find embedded firmware images |
|
||||
| FX2 init sequence | `78 7F E4 F6 D8 FD 75 81` | FX2 RAM clear/init code |
|
||||
| RAM clear pattern | `78 7F E4 F6 D8 FD` | Partial match variant |
|
||||
| C2 load records | LEN + addr `0x0000` + `LJMP` | Record chain starts |
|
||||
| VID references | `C0 09` near `03 02` | VID/PID byte pairs |
|
||||
| Version strings | `2.13`, `SkyWalker`, `Genpix`, `8PSK`, etc. | Text markers |
|
||||
| USB transfer setup | `0x40 0xA0`, `0x40 0x83`, `0x40 0x84` | WinUSB vendor request patterns |
|
||||
|
||||
Each hit is reported with the dump offset, virtual address, containing memory region, and context bytes.
|
||||
|
||||
### Output Files
|
||||
|
||||
| File | Contents |
|
||||
|------|----------|
|
||||
| `wine_memdump.bin` | Full concatenated memory dump |
|
||||
| `wine_memdump_regions.txt` | Region map with virtual addresses, permissions, and dump offsets |
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Diagnostic Command Reference
|
||||
|
||||
The custom v3.01.0 firmware adds these diagnostic vendor commands used by the test scripts:
|
||||
|
||||
| Command | Code | Direction | Purpose |
|
||||
|---------|------|-----------|---------|
|
||||
| `I2C_BUS_SCAN` | 0xB4 | IN | Probe all 128 I2C addresses, return 16-byte bitmap |
|
||||
| `I2C_RAW_READ` | 0xB5 | IN | Read from any I2C device (wValue=addr, wIndex=reg) |
|
||||
| `I2C_DIAG` | 0xB6 | IN | Step-by-step indirect register read with intermediate values |
|
||||
| `RAW_DEMOD_READ` | 0xB1 | IN | Read any BCM4500 indirect register |
|
||||
| `RAW_DEMOD_WRITE` | 0xB2 | OUT | Write any BCM4500 indirect register |
|
||||
|
||||
These commands are only available when running the custom firmware. Stock firmware will STALL on these command codes.
|
||||
|
||||
## See Also
|
||||
|
||||
- [I2C STOP Corruption Bug](/i2c/stop-corruption-bug/) -- the bug these tools helped discover
|
||||
- [Boot Sequence](/usb/boot-sequence/) -- the initialization flow being tested
|
||||
- [Custom Firmware v3.01.0](/firmware/custom-v301/) -- the firmware that enables debug modes
|
||||
- [I2C Bus Architecture](/i2c/bus-architecture/) -- I2C protocol and controller details
|
||||
- [Firmware Loader](/tools/firmware-loader/) -- load custom firmware before running debug tools
|
||||
---
|
||||
title: Debugging Tools
|
||||
description: Hardware diagnostic tools for boot testing, I2C bus debugging, and firmware extraction.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Steps, Aside, Badge } from '@astrojs/starlight/components';
|
||||
|
||||
The `tools/` directory includes a suite of diagnostic scripts developed during the reverse engineering of the SkyWalker-1. These tools were instrumental in isolating the I2C STOP corruption bug in the FX2 controller and verifying correct BCM4500 initialization.
|
||||
|
||||
All debugging tools require `pyusb`, root access, and the kernel `dvb_usb_gp8psk` module to be unloaded.
|
||||
|
||||
```bash
|
||||
pip install pyusb
|
||||
sudo modprobe -r dvb_usb_gp8psk gp8psk_fe
|
||||
```
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Boot Tests">
|
||||
|
||||
## Boot Testing Tools
|
||||
|
||||
Two scripts test the BCM4500 boot sequence using debug modes in the custom v3.01.0 firmware.
|
||||
|
||||
### test_boot.py -- Full Boot Verification
|
||||
|
||||
Runs a complete BOOT_8PSK sequence and reports success or failure with detailed register readback.
|
||||
|
||||
```bash
|
||||
sudo python3 tools/test_boot.py
|
||||
```
|
||||
|
||||
**What it does:**
|
||||
|
||||
<Steps>
|
||||
1. Reads firmware version via `GET_FW_VERS` (0x92)
|
||||
2. Reads config status via `GET_8PSK_CONFIG` (0x80)
|
||||
3. Sends `BOOT_8PSK` (0x89, wValue=1) with a 10-second timeout
|
||||
4. Decodes the 3-byte response: config status, boot stage, probe byte
|
||||
5. On success: reads BCM4500 direct registers (0xA2--0xA8), indirect registers (pages 0x00--0x0F), I2C diagnostic data, signal strength, and lock status
|
||||
6. On failure: runs I2C bus scan (0xB4), attempts raw I2C reads to BCM4500
|
||||
</Steps>
|
||||
|
||||
**Boot stage codes:**
|
||||
|
||||
| Stage | Code | Meaning |
|
||||
|-------|------|---------|
|
||||
| `NOT_STARTED` | 0x00 | Boot not attempted |
|
||||
| `GPIO_SETUP` | 0x01 | GPIO pins configured |
|
||||
| `PWR_SETTLED` | 0x02 | Power-on delay complete |
|
||||
| `I2C_PROBE` | 0x03 | Probing BCM4500 on I2C |
|
||||
| `INIT_BLK0` | 0x04 | Writing init block 0 |
|
||||
| `INIT_BLK1` | 0x05 | Writing init block 1 |
|
||||
| `INIT_BLK2` | 0x06 | Writing init block 2 |
|
||||
| `COMPLETE` | 0xFF | Boot finished |
|
||||
|
||||
**Config status flags after successful boot:**
|
||||
|
||||
| Flag | Bit | Expected |
|
||||
|------|-----|----------|
|
||||
| `bm8pskStarted` | 0x01 | <Badge text="ON" variant="success" /> |
|
||||
| `bm8pskFW_Loaded` | 0x02 | <Badge text="ON" variant="success" /> |
|
||||
|
||||
### test_boot_debug.py -- Incremental Stage Testing
|
||||
|
||||
Sends debug boot modes (wValue=0x80 through 0x83) one at a time to isolate which stage of the boot sequence fails. Can test a single stage or run all sequentially.
|
||||
|
||||
```bash title="Run all debug stages"
|
||||
sudo python3 tools/test_boot_debug.py
|
||||
```
|
||||
|
||||
```bash title="Test only stage 0x82"
|
||||
sudo python3 tools/test_boot_debug.py 0x82
|
||||
```
|
||||
|
||||
**Debug modes:**
|
||||
|
||||
| wValue | Action | Tests |
|
||||
|--------|--------|-------|
|
||||
| `0x80` | No-op: return current state | Firmware responsive? |
|
||||
| `0x81` | GPIO setup + power + delays (no I2C) | Power rails working? |
|
||||
| `0x82` | GPIO + I2C bus reset + BCM4500 probe | I2C communication working? |
|
||||
| `0x83` | GPIO + I2C probe + write init block 0 | Register writes accepted? |
|
||||
|
||||
Each mode returns a 3-byte response: config status, boot stage, and probe byte. The tool reports timing (ms) for each stage and checks if the device is still responsive after failures.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="I2C Tests">
|
||||
|
||||
## I2C Debugging Suite
|
||||
|
||||
Three scripts that implement a progressive isolation methodology to identify I2C bus faults. These were developed in sequence, each informed by the results of the previous one.
|
||||
|
||||
### Methodology
|
||||
|
||||
<Steps>
|
||||
1. **Broad scan** (`test_i2c_debug.py`) -- Power on the BCM4500 and probe the entire I2C bus. Does the chip respond at any address?
|
||||
2. **Isolation** (`test_i2c_isolate.py`) -- Determine whether the fault is in the BCM4500, the I2C bus, or the FX2 controller. Test the chip when already powered vs. after a reset.
|
||||
3. **Pinpoint** (`test_i2c_pinpoint.py`) -- Identify the exact operation that corrupts communication. Compare I2C read-only, GPIO+reset, and GPIO+bus-reset+probe modes.
|
||||
</Steps>
|
||||
|
||||
### test_i2c_debug.py -- Bus Discovery
|
||||
|
||||
Powers on the BCM4500 via GPIO-only mode (0x81), then runs progressively detailed diagnostics.
|
||||
|
||||
```bash
|
||||
sudo python3 tools/test_i2c_debug.py
|
||||
```
|
||||
|
||||
**Test sequence:**
|
||||
|
||||
| Step | Action | Purpose |
|
||||
|------|--------|---------|
|
||||
| 1 | Send mode 0x81 (GPIO power on) | Start BCM4500 without any I2C |
|
||||
| 2 | I2C bus scan (0xB4) immediately | Check for devices right after power-on |
|
||||
| 3 | Wait 500 ms, scan again | Maybe chip needs more time? |
|
||||
| 4 | Raw I2C reads to candidate addresses | Try 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x11, 0x68, 0x69, 0x60, 0x61 |
|
||||
| 5 | Check I2C controller state | Observe bus health |
|
||||
| 6 | Send mode 0x82 after 1s delay | Test probe with extra settling time |
|
||||
|
||||
**Candidate addresses** cover different BCM4500 pin-strap configurations and common demodulator/tuner addresses.
|
||||
|
||||
### test_i2c_isolate.py -- Fault Isolation
|
||||
|
||||
Determines whether the failure is caused by `bcm_direct_read`, re-reset timing, or the I2C bus reset (bmSTOP).
|
||||
|
||||
```bash
|
||||
sudo python3 tools/test_i2c_isolate.py
|
||||
```
|
||||
|
||||
**Test sequence:**
|
||||
|
||||
| Test | Action | Question |
|
||||
|------|--------|----------|
|
||||
| **A** | Power on (0x81), wait 1s, raw read | Is BCM4500 alive from cold start? |
|
||||
| **B** | Run mode 0x82 (re-resets + probe) | Does `bcm_direct_read` work after reset? |
|
||||
| **C** | Immediately raw read after 0x82 | Is I2C function itself broken? |
|
||||
| **D** | Raw reads at 100, 200, 500, 1000, 2000 ms | Is it a timing issue? |
|
||||
| **E** | Re-power (0x81), wait, then 0x82 | Repeatable? |
|
||||
|
||||
### test_i2c_pinpoint.py -- Root Cause Identification
|
||||
|
||||
The definitive test that identified the I2C STOP corruption bug. Compares three modes on a chip that is already proven alive.
|
||||
|
||||
```bash
|
||||
sudo python3 tools/test_i2c_pinpoint.py
|
||||
```
|
||||
|
||||
**Test sequence:**
|
||||
|
||||
<Steps>
|
||||
1. Power on BCM4500 via mode 0x81, confirm alive with raw read
|
||||
2. **Mode 0x84**: `bcm_direct_read` only (no GPIO, chip already powered) -- tests if the read function itself is broken
|
||||
3. **Mode 0x85**: GPIO + reset + power but NO I2C bus reset (no `bmSTOP`) -- tests if re-reset breaks things
|
||||
4. **Mode 0x82**: GPIO + I2C `bmSTOP` + reset + power + probe -- the mode that was failing
|
||||
</Steps>
|
||||
|
||||
**Interpretation:**
|
||||
|
||||
| Result Pattern | Diagnosis |
|
||||
|----------------|-----------|
|
||||
| 0x84 works, 0x85 works, 0x82 fails | `bmSTOP` (I2C bus reset) is the culprit |
|
||||
| 0x84 fails | `bcm_direct_read` has a bug |
|
||||
| 0x85 fails | Re-reset timing needs more delay |
|
||||
|
||||
<Aside type="note">
|
||||
This test conclusively proved that the spurious `I2CS |= bmSTOP` in the boot sequence was corrupting the FX2 I2C controller. Mode 0x84 succeeded immediately after 0x82 failed, proving the BCM4500 was alive the entire time -- only the FX2 controller was in a bad state. See [I2C STOP Corruption Bug](/i2c/stop-corruption-bug/) for the full analysis.
|
||||
</Aside>
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Memory Extraction">
|
||||
|
||||
## Windows Memory Dump (wine_memdump.py)
|
||||
|
||||
Extracts firmware from the Genpix Windows driver/updater by running it under Wine and dumping process memory. The Windows updater executable contains embedded firmware images that are unpacked into memory at runtime.
|
||||
|
||||
<Aside type="note">
|
||||
This tool requires **Wine** to be installed. It reads `/proc/PID/mem` and `/proc/PID/maps`, which requires running as the same user as the Wine process or as root.
|
||||
</Aside>
|
||||
|
||||
```bash
|
||||
sudo python3 tools/wine_memdump.py SkyWalkerUpdater.exe
|
||||
```
|
||||
|
||||
```bash title="Attach to an already-running Wine process"
|
||||
sudo python3 tools/wine_memdump.py SkyWalkerUpdater.exe --skip-launch
|
||||
```
|
||||
|
||||
```bash title="Custom output directory and wait time"
|
||||
sudo python3 tools/wine_memdump.py SkyWalkerUpdater.exe -o dumps/ --wait 5
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `EXE` | Windows PE executable to run under Wine (positional, required) |
|
||||
| `-o, --output-dir DIR` | Output directory for dumps (default: `.`) |
|
||||
| `--wait SECONDS` | Seconds to wait after launch for unpacking (default: `3`) |
|
||||
| `--skip-launch` | Attach to an already-running Wine process |
|
||||
|
||||
### How It Works
|
||||
|
||||
<Steps>
|
||||
1. Launches the `.exe` under Wine with `WINEDEBUG=-all` to suppress noise
|
||||
2. Waits for the process to unpack (configurable delay)
|
||||
3. Finds the Wine process PID by scanning `/proc`
|
||||
4. Reads `/proc/PID/maps` to identify readable memory regions
|
||||
5. Reads each region from `/proc/PID/mem` into a contiguous dump
|
||||
6. Saves the full dump (`wine_memdump.bin`) and region map (`wine_memdump_regions.txt`)
|
||||
7. Searches the dump for firmware signatures
|
||||
</Steps>
|
||||
|
||||
### Firmware Signature Search
|
||||
|
||||
The tool searches for 7 categories of signatures:
|
||||
|
||||
| Search | Pattern | Purpose |
|
||||
|--------|---------|---------|
|
||||
| C2 EEPROM headers | `C2 C0 09 03 02` | Find embedded firmware images |
|
||||
| FX2 init sequence | `78 7F E4 F6 D8 FD 75 81` | FX2 RAM clear/init code |
|
||||
| RAM clear pattern | `78 7F E4 F6 D8 FD` | Partial match variant |
|
||||
| C2 load records | LEN + addr `0x0000` + `LJMP` | Record chain starts |
|
||||
| VID references | `C0 09` near `03 02` | VID/PID byte pairs |
|
||||
| Version strings | `2.13`, `SkyWalker`, `Genpix`, `8PSK`, etc. | Text markers |
|
||||
| USB transfer setup | `0x40 0xA0`, `0x40 0x83`, `0x40 0x84` | WinUSB vendor request patterns |
|
||||
|
||||
Each hit is reported with the dump offset, virtual address, containing memory region, and context bytes.
|
||||
|
||||
### Output Files
|
||||
|
||||
| File | Contents |
|
||||
|------|----------|
|
||||
| `wine_memdump.bin` | Full concatenated memory dump |
|
||||
| `wine_memdump_regions.txt` | Region map with virtual addresses, permissions, and dump offsets |
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Diagnostic Command Reference
|
||||
|
||||
The custom v3.01.0 firmware adds these diagnostic vendor commands used by the test scripts:
|
||||
|
||||
| Command | Code | Direction | Purpose |
|
||||
|---------|------|-----------|---------|
|
||||
| `I2C_BUS_SCAN` | 0xB4 | IN | Probe all 128 I2C addresses, return 16-byte bitmap |
|
||||
| `I2C_RAW_READ` | 0xB5 | IN | Read from any I2C device (wValue=addr, wIndex=reg) |
|
||||
| `I2C_DIAG` | 0xB6 | IN | Step-by-step indirect register read with intermediate values |
|
||||
| `RAW_DEMOD_READ` | 0xB1 | IN | Read any BCM4500 indirect register |
|
||||
| `RAW_DEMOD_WRITE` | 0xB2 | OUT | Write any BCM4500 indirect register |
|
||||
|
||||
These commands are only available when running the custom firmware. Stock firmware will STALL on these command codes.
|
||||
|
||||
## See Also
|
||||
|
||||
- [I2C STOP Corruption Bug](/i2c/stop-corruption-bug/) -- the bug these tools helped discover
|
||||
- [Boot Sequence](/usb/boot-sequence/) -- the initialization flow being tested
|
||||
- [Custom Firmware v3.01.0](/firmware/custom-v301/) -- the firmware that enables debug modes
|
||||
- [I2C Bus Architecture](/i2c/bus-architecture/) -- I2C protocol and controller details
|
||||
- [Firmware Loader](/tools/firmware-loader/) -- load custom firmware before running debug tools
|
||||
|
|
|
|||
|
|
@ -1,266 +1,266 @@
|
|||
---
|
||||
title: EEPROM Utilities
|
||||
description: EEPROM read, write, probe, and verification tools for the FX2 configuration EEPROM.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Steps, Aside, Badge } from '@astrojs/starlight/components';
|
||||
|
||||
Three tools manage the SkyWalker-1's I2C EEPROM (24Cxx-family at address `0x51`): `eeprom_write.py` for flashing firmware images, `eeprom_dump.py` for reading EEPROM contents, and `eeprom_probe.py` for testing I2C addressing methods.
|
||||
|
||||
The EEPROM stores the Cypress FX2 boot firmware in C2 IIC format. The FX2 loads this firmware automatically on every power-up. Writing incorrect data to the EEPROM will prevent the device from enumerating on USB.
|
||||
|
||||
<Aside type="danger">
|
||||
**Writing incorrect firmware to the EEPROM can brick the device.** The SkyWalker-1 boots exclusively from this EEPROM on power-up. A corrupted image means no USB enumeration -- the device will appear dead until the EEPROM is reprogrammed using either the FX2 boot ROM's `0xA0` vendor request (if accessible) or an external I2C programmer.
|
||||
|
||||
Always back up the current EEPROM contents before flashing.
|
||||
</Aside>
|
||||
|
||||
```bash
|
||||
pip install pyusb
|
||||
```
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Flash (eeprom_write.py)">
|
||||
|
||||
## EEPROM Flash Tool
|
||||
|
||||
`eeprom_write.py` is the full-featured EEPROM management tool with four subcommands.
|
||||
|
||||
### Subcommands
|
||||
|
||||
| Subcommand | Purpose |
|
||||
|------------|---------|
|
||||
| `info` | Parse and display C2 header from a `.bin` file (offline, no device needed) |
|
||||
| `backup` | Dump current EEPROM contents to a file |
|
||||
| `verify` | Compare a `.bin` file against current EEPROM contents |
|
||||
| `flash` | Write a C2 firmware image to the EEPROM |
|
||||
|
||||
### info -- Inspect a C2 Image File
|
||||
|
||||
Parse and display the C2 header and load records from a firmware binary without connecting to the device.
|
||||
|
||||
```bash
|
||||
python3 tools/eeprom_write.py info firmware.bin
|
||||
```
|
||||
|
||||
```
|
||||
C2 Image: firmware.bin
|
||||
File size: 9512 bytes
|
||||
========================================
|
||||
|
||||
Header:
|
||||
Format: C2 (Large EEPROM, code loads to internal RAM)
|
||||
VID: 0x09C0 (Genpix)
|
||||
PID: 0x0203 (SkyWalker-1)
|
||||
DID: 0x0000
|
||||
Config: 0x40 (400kHz I2C)
|
||||
|
||||
Load Records:
|
||||
[0] 1023 bytes -> 0x0000-0x03FE [02 18 8d 78 7f e4 f6 d8...]
|
||||
[1] 1023 bytes -> 0x03FF-0x07FD [...]
|
||||
...
|
||||
[9] 115 bytes -> 0x23F7-0x2469 [...]
|
||||
[10] END MARKER -> entry point: 0xE600
|
||||
|
||||
Total firmware: 9472 bytes in 10 segments
|
||||
Entry point: 0xE600 (LJMP target after boot)
|
||||
|
||||
EEPROM footprint: 9512 bytes (0x2528)
|
||||
```
|
||||
|
||||
### backup -- Read EEPROM to File
|
||||
|
||||
```bash
|
||||
sudo python3 tools/eeprom_write.py backup -o my_backup.bin
|
||||
```
|
||||
|
||||
```bash title="Specify read size"
|
||||
sudo python3 tools/eeprom_write.py backup -o my_backup.bin --max-size 16384
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `-o, --output FILE` | Output file (default: `skywalker1_eeprom.bin`) |
|
||||
| `--max-size BYTES` | Maximum bytes to read (default: `16384`) |
|
||||
|
||||
### verify -- Compare Image Against EEPROM
|
||||
|
||||
Read the EEPROM and compare byte-by-byte against a local `.bin` file. Reports any mismatches with offset and expected vs. actual values.
|
||||
|
||||
```bash
|
||||
sudo python3 tools/eeprom_write.py verify firmware.bin
|
||||
```
|
||||
|
||||
Exit code `0` = match, `1` = mismatch.
|
||||
|
||||
### flash -- Write Image to EEPROM
|
||||
|
||||
The flash workflow includes built-in safety measures: image validation, VID/PID check, automatic backup, countdown timer, write verification.
|
||||
|
||||
<Steps>
|
||||
1. **Validate** the C2 image file (header format, VID/PID match, record integrity)
|
||||
2. **Connect** to the SkyWalker-1 and check device VID/PID against the image
|
||||
3. **Backup** the current EEPROM contents to a timestamped file
|
||||
4. **Wait** 3 seconds with a countdown (Ctrl-C to abort)
|
||||
5. **Write** the image in 16-byte page-aligned chunks with 10 ms write cycle delays
|
||||
6. **Verify** by reading back and comparing every byte
|
||||
</Steps>
|
||||
|
||||
```bash title="Flash with all safety checks"
|
||||
sudo python3 tools/eeprom_write.py flash firmware.bin
|
||||
```
|
||||
|
||||
```bash title="Dry run (shows what would happen, writes nothing)"
|
||||
sudo python3 tools/eeprom_write.py flash firmware.bin --dry-run
|
||||
```
|
||||
|
||||
```bash title="Skip backup (not recommended)"
|
||||
sudo python3 tools/eeprom_write.py flash firmware.bin --no-backup
|
||||
```
|
||||
|
||||
```bash title="Force flash with VID/PID mismatch"
|
||||
sudo python3 tools/eeprom_write.py flash firmware.bin --force
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `FILE` | C2 firmware image (positional, required) |
|
||||
| `--dry-run` | Validate and show plan without writing |
|
||||
| `--no-backup` | Skip pre-flash EEPROM backup |
|
||||
| `--force` | Override VID/PID mismatch check |
|
||||
|
||||
### EEPROM Write Parameters
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| I2C slave address | `0x51` (7-bit) |
|
||||
| Vendor command (write) | `I2C_WRITE` (`0x83`) |
|
||||
| Vendor command (read) | `I2C_READ` (`0x84`) |
|
||||
| Page size | 16 bytes (conservative for 24Cxx) |
|
||||
| Write cycle time | 10 ms per page |
|
||||
| Maximum image size | 16,384 bytes (16 KB) |
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Dump (eeprom_dump.py)">
|
||||
|
||||
## EEPROM Dump Tool
|
||||
|
||||
`eeprom_dump.py` is a simpler read-only tool focused on extracting and analyzing EEPROM contents.
|
||||
|
||||
```bash
|
||||
sudo python3 tools/eeprom_dump.py -o eeprom.bin
|
||||
```
|
||||
|
||||
```bash title="Extract flat binary + 64K Ghidra image"
|
||||
sudo python3 tools/eeprom_dump.py -o eeprom.bin --extract
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `-o, --output FILE` | Output file (default: `skywalker1_eeprom.bin`) |
|
||||
| `--extract` | Extract firmware as flat binary + full 64K image |
|
||||
| `--max-size BYTES` | Maximum EEPROM size to read (default: `16384`) |
|
||||
|
||||
### Auto-Detect End of Data
|
||||
|
||||
The tool detects the end of valid data by watching for consecutive `0xFF` chunks (4 or more = end of programmed region). This avoids reading the entire EEPROM when only the first few KB contain firmware.
|
||||
|
||||
### Extract Mode (--extract)
|
||||
|
||||
When `--extract` is specified, the tool additionally produces:
|
||||
|
||||
- **`*_flat.bin`** -- firmware code extracted from C2 records, stored as a flat binary covering only the used address range
|
||||
- **`*_full64k.bin`** -- full 64 KB memory image (unused regions filled with `0xFF`), suitable for loading in Ghidra at base address `0x0000`
|
||||
|
||||
### Output
|
||||
|
||||
```
|
||||
Genpix SkyWalker-1 EEPROM Dump
|
||||
========================================
|
||||
Found device: Bus 1 Addr 12
|
||||
|
||||
Reading EEPROM (max 16384 bytes)...
|
||||
End of data at 0x2600 (0xFF padding)
|
||||
Read 9728 bytes total
|
||||
Saved raw EEPROM to: eeprom.bin
|
||||
|
||||
========================================
|
||||
EEPROM Header:
|
||||
Format: C2 (Large EEPROM, code loads to internal RAM)
|
||||
VID: 0x09C0 (Genpix)
|
||||
PID: 0x0203 (SkyWalker-1)
|
||||
DID: 0x0000
|
||||
Config: 0x40 (400kHz I2C)
|
||||
|
||||
Load Records:
|
||||
[0] 1023 bytes -> 0x0000-0x03FE [02 18 8d 78 7f e4 f6 d8...]
|
||||
...
|
||||
[10] END MARKER -> entry point: 0xE600
|
||||
|
||||
Total firmware: 9472 bytes in 10 records
|
||||
Entry point: 0xE600 (LJMP target after boot)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Probe (eeprom_probe.py)">
|
||||
|
||||
## EEPROM Address Probe
|
||||
|
||||
`eeprom_probe.py` is a diagnostic script that tests different I2C addressing methods to determine how the Genpix firmware's `I2C_READ`/`I2C_WRITE` commands interpret `wValue` and `wIndex` parameters for EEPROM access.
|
||||
|
||||
This is a developer tool used during reverse engineering. It runs 7 different addressing approaches and checks each result against known reference bytes from the EEPROM header.
|
||||
|
||||
```bash
|
||||
sudo python3 tools/eeprom_probe.py
|
||||
```
|
||||
|
||||
### Test Approaches
|
||||
|
||||
| Approach | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| 1 | `I2C_WRITE data=[addr_h, addr_l]` then `I2C_READ` | Set EEPROM offset via write data |
|
||||
| 2 | `wValue=addr, wIndex=slave` | Reversed parameter mapping |
|
||||
| 3 | `wValue=slave, wIndex=addr` | Standard Genpix mapping |
|
||||
| 4 | `I2C_READ wIndex=offset` | Offset in wIndex field |
|
||||
| 5 | `wValue=(slave<<8\|offset)` | Packed slave + offset |
|
||||
| 6 | `wValue=offset (no slave)` | Offset only, no slave address |
|
||||
| 7 | Larger reads (64 bytes) | Verify sequential data across page boundaries |
|
||||
|
||||
### Known Reference Bytes
|
||||
|
||||
The script compares against two known patterns:
|
||||
|
||||
- **Offset 0x0000**: `C2 C0 09 03 02 00 00 40` (C2 header: marker, VID, PID, DID, config)
|
||||
- **Offset 0x0008**: `03 FF 00 00 02 18 8D 30` (first load record)
|
||||
|
||||
<Aside type="note">
|
||||
This tool confirmed that the Genpix firmware uses `wValue=slave_address` (0x51) and `wIndex=eeprom_offset` for the `I2C_READ` (0x84) command. Approach 4 is the correct protocol.
|
||||
</Aside>
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Cypress C2 Boot Format
|
||||
|
||||
The EEPROM stores firmware in the Cypress C2 IIC second-stage boot format:
|
||||
|
||||
| Field | Offset | Size | Description |
|
||||
|-------|--------|------|-------------|
|
||||
| Marker | 0 | 1 | `0xC2` (large EEPROM, external memory) |
|
||||
| VID | 1 | 2 | USB Vendor ID, little-endian (`0x09C0`) |
|
||||
| PID | 3 | 2 | USB Product ID, little-endian (`0x0203`) |
|
||||
| DID | 5 | 2 | Device ID, little-endian |
|
||||
| Config | 7 | 1 | Boot config (`0x40` = 400 kHz I2C) |
|
||||
| Records | 8+ | var | Code segments: `[len_hi][len_lo][addr_hi][addr_lo][data...]` |
|
||||
| End | last | 4 | `[0x80][0x01][entry_hi][entry_lo]` (entry point = `0xE600`) |
|
||||
|
||||
For a full description of the storage format, see [Firmware Storage Formats](/firmware/storage-formats/).
|
||||
|
||||
## See Also
|
||||
|
||||
- [Firmware Loader](/tools/firmware-loader/) -- RAM-based firmware loading (non-destructive)
|
||||
- [Storage Formats](/firmware/storage-formats/) -- C2 format, hexline, and FW02 chunk details
|
||||
- [Version Comparison](/firmware/version-comparison/) -- differences across firmware versions
|
||||
- [I2C Bus Architecture](/i2c/bus-architecture/) -- I2C protocol details
|
||||
---
|
||||
title: EEPROM Utilities
|
||||
description: EEPROM read, write, probe, and verification tools for the FX2 configuration EEPROM.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Steps, Aside, Badge } from '@astrojs/starlight/components';
|
||||
|
||||
Three tools manage the SkyWalker-1's I2C EEPROM (24Cxx-family at address `0x51`): `eeprom_write.py` for flashing firmware images, `eeprom_dump.py` for reading EEPROM contents, and `eeprom_probe.py` for testing I2C addressing methods.
|
||||
|
||||
The EEPROM stores the Cypress FX2 boot firmware in C2 IIC format. The FX2 loads this firmware automatically on every power-up. Writing incorrect data to the EEPROM will prevent the device from enumerating on USB.
|
||||
|
||||
<Aside type="danger">
|
||||
**Writing incorrect firmware to the EEPROM can brick the device.** The SkyWalker-1 boots exclusively from this EEPROM on power-up. A corrupted image means no USB enumeration -- the device will appear dead until the EEPROM is reprogrammed using either the FX2 boot ROM's `0xA0` vendor request (if accessible) or an external I2C programmer.
|
||||
|
||||
Always back up the current EEPROM contents before flashing.
|
||||
</Aside>
|
||||
|
||||
```bash
|
||||
pip install pyusb
|
||||
```
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Flash (eeprom_write.py)">
|
||||
|
||||
## EEPROM Flash Tool
|
||||
|
||||
`eeprom_write.py` is the full-featured EEPROM management tool with four subcommands.
|
||||
|
||||
### Subcommands
|
||||
|
||||
| Subcommand | Purpose |
|
||||
|------------|---------|
|
||||
| `info` | Parse and display C2 header from a `.bin` file (offline, no device needed) |
|
||||
| `backup` | Dump current EEPROM contents to a file |
|
||||
| `verify` | Compare a `.bin` file against current EEPROM contents |
|
||||
| `flash` | Write a C2 firmware image to the EEPROM |
|
||||
|
||||
### info -- Inspect a C2 Image File
|
||||
|
||||
Parse and display the C2 header and load records from a firmware binary without connecting to the device.
|
||||
|
||||
```bash
|
||||
python3 tools/eeprom_write.py info firmware.bin
|
||||
```
|
||||
|
||||
```
|
||||
C2 Image: firmware.bin
|
||||
File size: 9512 bytes
|
||||
========================================
|
||||
|
||||
Header:
|
||||
Format: C2 (Large EEPROM, code loads to internal RAM)
|
||||
VID: 0x09C0 (Genpix)
|
||||
PID: 0x0203 (SkyWalker-1)
|
||||
DID: 0x0000
|
||||
Config: 0x40 (400kHz I2C)
|
||||
|
||||
Load Records:
|
||||
[0] 1023 bytes -> 0x0000-0x03FE [02 18 8d 78 7f e4 f6 d8...]
|
||||
[1] 1023 bytes -> 0x03FF-0x07FD [...]
|
||||
...
|
||||
[9] 115 bytes -> 0x23F7-0x2469 [...]
|
||||
[10] END MARKER -> entry point: 0xE600
|
||||
|
||||
Total firmware: 9472 bytes in 10 segments
|
||||
Entry point: 0xE600 (LJMP target after boot)
|
||||
|
||||
EEPROM footprint: 9512 bytes (0x2528)
|
||||
```
|
||||
|
||||
### backup -- Read EEPROM to File
|
||||
|
||||
```bash
|
||||
sudo python3 tools/eeprom_write.py backup -o my_backup.bin
|
||||
```
|
||||
|
||||
```bash title="Specify read size"
|
||||
sudo python3 tools/eeprom_write.py backup -o my_backup.bin --max-size 16384
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `-o, --output FILE` | Output file (default: `skywalker1_eeprom.bin`) |
|
||||
| `--max-size BYTES` | Maximum bytes to read (default: `16384`) |
|
||||
|
||||
### verify -- Compare Image Against EEPROM
|
||||
|
||||
Read the EEPROM and compare byte-by-byte against a local `.bin` file. Reports any mismatches with offset and expected vs. actual values.
|
||||
|
||||
```bash
|
||||
sudo python3 tools/eeprom_write.py verify firmware.bin
|
||||
```
|
||||
|
||||
Exit code `0` = match, `1` = mismatch.
|
||||
|
||||
### flash -- Write Image to EEPROM
|
||||
|
||||
The flash workflow includes built-in safety measures: image validation, VID/PID check, automatic backup, countdown timer, write verification.
|
||||
|
||||
<Steps>
|
||||
1. **Validate** the C2 image file (header format, VID/PID match, record integrity)
|
||||
2. **Connect** to the SkyWalker-1 and check device VID/PID against the image
|
||||
3. **Backup** the current EEPROM contents to a timestamped file
|
||||
4. **Wait** 3 seconds with a countdown (Ctrl-C to abort)
|
||||
5. **Write** the image in 16-byte page-aligned chunks with 10 ms write cycle delays
|
||||
6. **Verify** by reading back and comparing every byte
|
||||
</Steps>
|
||||
|
||||
```bash title="Flash with all safety checks"
|
||||
sudo python3 tools/eeprom_write.py flash firmware.bin
|
||||
```
|
||||
|
||||
```bash title="Dry run (shows what would happen, writes nothing)"
|
||||
sudo python3 tools/eeprom_write.py flash firmware.bin --dry-run
|
||||
```
|
||||
|
||||
```bash title="Skip backup (not recommended)"
|
||||
sudo python3 tools/eeprom_write.py flash firmware.bin --no-backup
|
||||
```
|
||||
|
||||
```bash title="Force flash with VID/PID mismatch"
|
||||
sudo python3 tools/eeprom_write.py flash firmware.bin --force
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `FILE` | C2 firmware image (positional, required) |
|
||||
| `--dry-run` | Validate and show plan without writing |
|
||||
| `--no-backup` | Skip pre-flash EEPROM backup |
|
||||
| `--force` | Override VID/PID mismatch check |
|
||||
|
||||
### EEPROM Write Parameters
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| I2C slave address | `0x51` (7-bit) |
|
||||
| Vendor command (write) | `I2C_WRITE` (`0x83`) |
|
||||
| Vendor command (read) | `I2C_READ` (`0x84`) |
|
||||
| Page size | 16 bytes (conservative for 24Cxx) |
|
||||
| Write cycle time | 10 ms per page |
|
||||
| Maximum image size | 16,384 bytes (16 KB) |
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Dump (eeprom_dump.py)">
|
||||
|
||||
## EEPROM Dump Tool
|
||||
|
||||
`eeprom_dump.py` is a simpler read-only tool focused on extracting and analyzing EEPROM contents.
|
||||
|
||||
```bash
|
||||
sudo python3 tools/eeprom_dump.py -o eeprom.bin
|
||||
```
|
||||
|
||||
```bash title="Extract flat binary + 64K Ghidra image"
|
||||
sudo python3 tools/eeprom_dump.py -o eeprom.bin --extract
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `-o, --output FILE` | Output file (default: `skywalker1_eeprom.bin`) |
|
||||
| `--extract` | Extract firmware as flat binary + full 64K image |
|
||||
| `--max-size BYTES` | Maximum EEPROM size to read (default: `16384`) |
|
||||
|
||||
### Auto-Detect End of Data
|
||||
|
||||
The tool detects the end of valid data by watching for consecutive `0xFF` chunks (4 or more = end of programmed region). This avoids reading the entire EEPROM when only the first few KB contain firmware.
|
||||
|
||||
### Extract Mode (--extract)
|
||||
|
||||
When `--extract` is specified, the tool additionally produces:
|
||||
|
||||
- **`*_flat.bin`** -- firmware code extracted from C2 records, stored as a flat binary covering only the used address range
|
||||
- **`*_full64k.bin`** -- full 64 KB memory image (unused regions filled with `0xFF`), suitable for loading in Ghidra at base address `0x0000`
|
||||
|
||||
### Output
|
||||
|
||||
```
|
||||
Genpix SkyWalker-1 EEPROM Dump
|
||||
========================================
|
||||
Found device: Bus 1 Addr 12
|
||||
|
||||
Reading EEPROM (max 16384 bytes)...
|
||||
End of data at 0x2600 (0xFF padding)
|
||||
Read 9728 bytes total
|
||||
Saved raw EEPROM to: eeprom.bin
|
||||
|
||||
========================================
|
||||
EEPROM Header:
|
||||
Format: C2 (Large EEPROM, code loads to internal RAM)
|
||||
VID: 0x09C0 (Genpix)
|
||||
PID: 0x0203 (SkyWalker-1)
|
||||
DID: 0x0000
|
||||
Config: 0x40 (400kHz I2C)
|
||||
|
||||
Load Records:
|
||||
[0] 1023 bytes -> 0x0000-0x03FE [02 18 8d 78 7f e4 f6 d8...]
|
||||
...
|
||||
[10] END MARKER -> entry point: 0xE600
|
||||
|
||||
Total firmware: 9472 bytes in 10 records
|
||||
Entry point: 0xE600 (LJMP target after boot)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Probe (eeprom_probe.py)">
|
||||
|
||||
## EEPROM Address Probe
|
||||
|
||||
`eeprom_probe.py` is a diagnostic script that tests different I2C addressing methods to determine how the Genpix firmware's `I2C_READ`/`I2C_WRITE` commands interpret `wValue` and `wIndex` parameters for EEPROM access.
|
||||
|
||||
This is a developer tool used during reverse engineering. It runs 7 different addressing approaches and checks each result against known reference bytes from the EEPROM header.
|
||||
|
||||
```bash
|
||||
sudo python3 tools/eeprom_probe.py
|
||||
```
|
||||
|
||||
### Test Approaches
|
||||
|
||||
| Approach | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| 1 | `I2C_WRITE data=[addr_h, addr_l]` then `I2C_READ` | Set EEPROM offset via write data |
|
||||
| 2 | `wValue=addr, wIndex=slave` | Reversed parameter mapping |
|
||||
| 3 | `wValue=slave, wIndex=addr` | Standard Genpix mapping |
|
||||
| 4 | `I2C_READ wIndex=offset` | Offset in wIndex field |
|
||||
| 5 | `wValue=(slave<<8\|offset)` | Packed slave + offset |
|
||||
| 6 | `wValue=offset (no slave)` | Offset only, no slave address |
|
||||
| 7 | Larger reads (64 bytes) | Verify sequential data across page boundaries |
|
||||
|
||||
### Known Reference Bytes
|
||||
|
||||
The script compares against two known patterns:
|
||||
|
||||
- **Offset 0x0000**: `C2 C0 09 03 02 00 00 40` (C2 header: marker, VID, PID, DID, config)
|
||||
- **Offset 0x0008**: `03 FF 00 00 02 18 8D 30` (first load record)
|
||||
|
||||
<Aside type="note">
|
||||
This tool confirmed that the Genpix firmware uses `wValue=slave_address` (0x51) and `wIndex=eeprom_offset` for the `I2C_READ` (0x84) command. Approach 4 is the correct protocol.
|
||||
</Aside>
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Cypress C2 Boot Format
|
||||
|
||||
The EEPROM stores firmware in the Cypress C2 IIC second-stage boot format:
|
||||
|
||||
| Field | Offset | Size | Description |
|
||||
|-------|--------|------|-------------|
|
||||
| Marker | 0 | 1 | `0xC2` (large EEPROM, external memory) |
|
||||
| VID | 1 | 2 | USB Vendor ID, little-endian (`0x09C0`) |
|
||||
| PID | 3 | 2 | USB Product ID, little-endian (`0x0203`) |
|
||||
| DID | 5 | 2 | Device ID, little-endian |
|
||||
| Config | 7 | 1 | Boot config (`0x40` = 400 kHz I2C) |
|
||||
| Records | 8+ | var | Code segments: `[len_hi][len_lo][addr_hi][addr_lo][data...]` |
|
||||
| End | last | 4 | `[0x80][0x01][entry_hi][entry_lo]` (entry point = `0xE600`) |
|
||||
|
||||
For a full description of the storage format, see [Firmware Storage Formats](/firmware/storage-formats/).
|
||||
|
||||
## See Also
|
||||
|
||||
- [Firmware Loader](/tools/firmware-loader/) -- RAM-based firmware loading (non-destructive)
|
||||
- [Storage Formats](/firmware/storage-formats/) -- C2 format, hexline, and FW02 chunk details
|
||||
- [Version Comparison](/firmware/version-comparison/) -- differences across firmware versions
|
||||
- [I2C Bus Architecture](/i2c/bus-architecture/) -- I2C protocol details
|
||||
|
|
|
|||
|
|
@ -1,255 +1,255 @@
|
|||
---
|
||||
title: Firmware Loader
|
||||
description: RAM firmware loader and firmware dump/probe utilities for the Genpix SkyWalker-1.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Steps, Aside, Badge } from '@astrojs/starlight/components';
|
||||
|
||||
Two complementary tools handle firmware on the SkyWalker-1: `fw_load.py` loads firmware into FX2 RAM for testing, and `fw_dump.py` extracts firmware and device information from a running device.
|
||||
|
||||
Both tools require `pyusb` and typically need root access (or appropriate udev rules) to communicate with the USB device.
|
||||
|
||||
```bash
|
||||
pip install pyusb
|
||||
```
|
||||
|
||||
<Aside type="danger">
|
||||
**RAM loading is non-destructive.** The `fw_load.py` tool writes to volatile FX2 RAM only -- it never touches the EEPROM. Power-cycling the device restores the factory-programmed firmware. For permanent EEPROM programming, see [EEPROM Utilities](/tools/eeprom-utilities/).
|
||||
</Aside>
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Loading (fw_load.py)">
|
||||
|
||||
## RAM Firmware Loader
|
||||
|
||||
`fw_load.py` loads firmware into the Cypress FX2 internal/external RAM via the standard `0xA0` vendor request built into the FX2 silicon boot ROM. This is the primary tool for firmware development: load, test, power-cycle to revert.
|
||||
|
||||
### Subcommands
|
||||
|
||||
| Subcommand | Purpose |
|
||||
|------------|---------|
|
||||
| `load` | Load a firmware file into FX2 RAM |
|
||||
| `reset` | Halt and restart the FX2 CPU |
|
||||
| `read` | Read and hex-dump FX2 RAM contents |
|
||||
|
||||
### load -- Write Firmware to RAM
|
||||
|
||||
<Steps>
|
||||
1. **Halt the CPU** -- writes `0x01` to the CPUCS register at `0xE600`
|
||||
2. **Write code segments** into RAM in 64-byte chunks via vendor request `0xA0`
|
||||
3. **Start the CPU** -- writes `0x00` to CPUCS, triggering USB re-enumeration
|
||||
</Steps>
|
||||
|
||||
**Supported file formats:**
|
||||
|
||||
| Extension | Format | Load Address |
|
||||
|-----------|--------|-------------|
|
||||
| `.ihx`, `.hex` | Intel HEX | Addresses embedded in file |
|
||||
| `.bix`, `.bin` | Raw binary | `0x0000` (start of internal RAM) |
|
||||
|
||||
**Options:**
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `FILE` | Firmware file path (positional, required) |
|
||||
| `--no-reset` | Load segments without halting/starting the CPU |
|
||||
| `--wait SECONDS` | Wait for USB re-enumeration after load |
|
||||
| `-v, --verbose` | Show per-chunk transfer progress |
|
||||
| `--force` | Allow loading to devices with unknown VID/PID |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash title="Load custom firmware and wait for re-enumeration"
|
||||
sudo python3 tools/fw_load.py load firmware/build/skywalker1.ihx --wait 3
|
||||
```
|
||||
|
||||
```bash title="Load raw binary with verbose output"
|
||||
sudo python3 tools/fw_load.py load firmware.bix -v --wait 5
|
||||
```
|
||||
|
||||
```bash title="Load without CPU reset (write segments only)"
|
||||
sudo python3 tools/fw_load.py load firmware.ihx --no-reset
|
||||
```
|
||||
|
||||
**Typical output:**
|
||||
|
||||
```
|
||||
SkyWalker-1 RAM Firmware Loader
|
||||
========================================
|
||||
|
||||
Firmware: firmware/build/skywalker1.ihx
|
||||
Segments: 3
|
||||
Total size: 3072 bytes
|
||||
Address: 0x0000 - 0x0BFF
|
||||
|
||||
Found SkyWalker-1: Bus 1 Addr 12 (VID 0x09C0 PID 0x0203)
|
||||
|
||||
[1/3] Halting CPU (CPUCS = 0x01)...
|
||||
CPU halted
|
||||
|
||||
[2/3] Loading 3 segment(s) into RAM...
|
||||
0x0000-0x03FF (1024 bytes)
|
||||
0x0400-0x07FF (1024 bytes)
|
||||
0x0800-0x0BFF (1024 bytes)
|
||||
|
||||
3072 bytes loaded
|
||||
|
||||
[3/3] Starting CPU (CPUCS = 0x00)...
|
||||
CPU released
|
||||
|
||||
Firmware is running. The device will re-enumerate
|
||||
with new USB descriptors if the firmware does so.
|
||||
```
|
||||
|
||||
### reset -- Restart the FX2 CPU
|
||||
|
||||
Halts and restarts the CPU without loading new code. Useful for triggering USB re-enumeration when the device is in a bad state.
|
||||
|
||||
```bash
|
||||
sudo python3 tools/fw_load.py reset --wait 3
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--wait SECONDS` | Wait for re-enumeration after reset |
|
||||
| `--force` | Allow reset on unknown VID/PID |
|
||||
|
||||
### read -- Hex-Dump FX2 RAM
|
||||
|
||||
Read memory contents from a running FX2. Useful for verifying loaded firmware or inspecting register state.
|
||||
|
||||
```bash title="Read first 256 bytes of internal RAM"
|
||||
sudo python3 tools/fw_load.py read --addr 0x0000 --len 256
|
||||
```
|
||||
|
||||
```bash title="Check CPUCS register"
|
||||
sudo python3 tools/fw_load.py read --addr 0xe600 --len 1
|
||||
```
|
||||
|
||||
```bash title="Dump to file"
|
||||
sudo python3 tools/fw_load.py read --addr 0x0000 --len 8192 -o ram_dump.bin
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--addr ADDR` | Start address in hex (default: `0x0000`) |
|
||||
| `--len LENGTH` | Bytes to read (default: `256`) |
|
||||
| `-o, --output FILE` | Save raw bytes to file |
|
||||
| `--force` | Allow read on unknown VID/PID |
|
||||
|
||||
### Device Detection
|
||||
|
||||
The loader searches for devices in this order:
|
||||
|
||||
1. **SkyWalker-1** -- VID `0x09C0`, PID `0x0203`
|
||||
2. **Bare Cypress FX2** -- VID `0x04B4`, PID `0x8613` (unprogrammed/blank EEPROM)
|
||||
|
||||
Use `--force` to override VID/PID checks for devices with non-standard descriptors.
|
||||
|
||||
<Aside type="tip">
|
||||
**Recovery procedure:** If the device is completely hung (not responding to vendor commands), you can still recover it using `fw_load.py`. The FX2's `0xA0` vendor request is handled by the boot ROM in silicon, not by user firmware. Even a hung 8051 CPU can be halted and reprogrammed via this mechanism.
|
||||
</Aside>
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Dumping (fw_dump.py)">
|
||||
|
||||
## Firmware Probe and Dump Tool
|
||||
|
||||
`fw_dump.py` queries device information, dumps FX2 RAM contents, and scans for undocumented vendor commands.
|
||||
|
||||
### Options
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--info` | Query and display device information |
|
||||
| `--dump FILE` | Dump FX2 RAM to a binary file |
|
||||
| `--scan` | Brute-force scan all vendor commands (0x00--0xFF) |
|
||||
| `--start ADDR` | RAM dump start address (default: `0x0000`) |
|
||||
| `--size SIZE` | RAM dump size in bytes (default: `0x2000` = 8 KB) |
|
||||
| `--external` | Attempt to dump external RAM (64 KB) |
|
||||
|
||||
Running with no flags defaults to `--info --scan`.
|
||||
|
||||
### Device Info (--info)
|
||||
|
||||
Queries all known Genpix vendor commands and displays firmware version, build date, USB speed, serial number, and the 8PSK configuration status byte with decoded flags.
|
||||
|
||||
```bash
|
||||
sudo python3 tools/fw_dump.py --info
|
||||
```
|
||||
|
||||
```
|
||||
=== Genpix SkyWalker-1 Device Info ===
|
||||
|
||||
FW Version: 2.06.4 (0x020604)
|
||||
FW Build: 2007-07-13
|
||||
BCD Version: 0206
|
||||
Vendor: Genpix
|
||||
Product: SkyWalker-1
|
||||
USB Speed: High (480Mbps)
|
||||
Serial: 00000000
|
||||
Config: 0x03
|
||||
[ ON] 8PSK Started
|
||||
[ ON] BCM4500 FW Loaded
|
||||
[off] Intersil LNB On
|
||||
[off] DVB Mode
|
||||
[off] 22kHz Tone
|
||||
[off] 18V Selected
|
||||
[off] DC Tuned
|
||||
[off] Armed (streaming)
|
||||
```
|
||||
|
||||
### Vendor Command Scan (--scan)
|
||||
|
||||
Sends vendor IN requests to every command index from `0x00` to `0xFF` and reports which ones return data. Commands already documented in the reference are labeled `[KNOWN]`; unexpected responses are flagged `[NEW!]`.
|
||||
|
||||
```bash
|
||||
sudo python3 tools/fw_dump.py --scan
|
||||
```
|
||||
|
||||
### RAM Dump (--dump)
|
||||
|
||||
Reads FX2 internal RAM (8 KB at `0x0000`--`0x1FFF`) or external RAM (up to 64 KB with `--external`) via the standard `0xA0` vendor request.
|
||||
|
||||
```bash title="Dump internal RAM"
|
||||
sudo python3 tools/fw_dump.py --dump internal_ram.bin
|
||||
```
|
||||
|
||||
```bash title="Dump external RAM (64 KB)"
|
||||
sudo python3 tools/fw_dump.py --dump external_ram.bin --external
|
||||
```
|
||||
|
||||
```bash title="Dump specific range"
|
||||
sudo python3 tools/fw_dump.py --dump region.bin --start 0x1000 --size 0x800
|
||||
```
|
||||
|
||||
The tool performs a quick analysis of the dump, reporting non-`0xFF` byte count and checking for the standard FX2 reset vector (`LJMP` at address `0x0000`).
|
||||
|
||||
<Aside type="note">
|
||||
RAM readback may fail if the running firmware has disabled or overridden the `0xA0` vendor request. Stock Genpix firmware supports `0xA0` readback; some custom configurations may not. If you see consecutive read failures, the device firmware may need to be reloaded first.
|
||||
</Aside>
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Kernel Driver Conflict
|
||||
|
||||
The Linux `dvb_usb_gp8psk` module auto-loads when the SkyWalker-1 enumerates on USB. It will race with these tools for device access.
|
||||
|
||||
**Before using any tool**, either blacklist the module or unload it:
|
||||
|
||||
```bash title="Unload for current session"
|
||||
sudo modprobe -r dvb_usb_gp8psk gp8psk_fe
|
||||
```
|
||||
|
||||
```bash title="Permanent blacklist"
|
||||
echo -e "blacklist dvb_usb_gp8psk\nblacklist gp8psk_fe" | \
|
||||
sudo tee /etc/modprobe.d/blacklist-gp8psk.conf
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [EEPROM Utilities](/tools/eeprom-utilities/) -- for permanent firmware flashing
|
||||
- [Boot Sequence](/usb/boot-sequence/) -- what happens after firmware loads
|
||||
- [Custom Firmware v3.01.0](/firmware/custom-v301/) -- the open-source replacement firmware
|
||||
- [Vendor Commands](/usb/vendor-commands/) -- command reference used by these tools
|
||||
---
|
||||
title: Firmware Loader
|
||||
description: RAM firmware loader and firmware dump/probe utilities for the Genpix SkyWalker-1.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Steps, Aside, Badge } from '@astrojs/starlight/components';
|
||||
|
||||
Two complementary tools handle firmware on the SkyWalker-1: `fw_load.py` loads firmware into FX2 RAM for testing, and `fw_dump.py` extracts firmware and device information from a running device.
|
||||
|
||||
Both tools require `pyusb` and typically need root access (or appropriate udev rules) to communicate with the USB device.
|
||||
|
||||
```bash
|
||||
pip install pyusb
|
||||
```
|
||||
|
||||
<Aside type="danger">
|
||||
**RAM loading is non-destructive.** The `fw_load.py` tool writes to volatile FX2 RAM only -- it never touches the EEPROM. Power-cycling the device restores the factory-programmed firmware. For permanent EEPROM programming, see [EEPROM Utilities](/tools/eeprom-utilities/).
|
||||
</Aside>
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Loading (fw_load.py)">
|
||||
|
||||
## RAM Firmware Loader
|
||||
|
||||
`fw_load.py` loads firmware into the Cypress FX2 internal/external RAM via the standard `0xA0` vendor request built into the FX2 silicon boot ROM. This is the primary tool for firmware development: load, test, power-cycle to revert.
|
||||
|
||||
### Subcommands
|
||||
|
||||
| Subcommand | Purpose |
|
||||
|------------|---------|
|
||||
| `load` | Load a firmware file into FX2 RAM |
|
||||
| `reset` | Halt and restart the FX2 CPU |
|
||||
| `read` | Read and hex-dump FX2 RAM contents |
|
||||
|
||||
### load -- Write Firmware to RAM
|
||||
|
||||
<Steps>
|
||||
1. **Halt the CPU** -- writes `0x01` to the CPUCS register at `0xE600`
|
||||
2. **Write code segments** into RAM in 64-byte chunks via vendor request `0xA0`
|
||||
3. **Start the CPU** -- writes `0x00` to CPUCS, triggering USB re-enumeration
|
||||
</Steps>
|
||||
|
||||
**Supported file formats:**
|
||||
|
||||
| Extension | Format | Load Address |
|
||||
|-----------|--------|-------------|
|
||||
| `.ihx`, `.hex` | Intel HEX | Addresses embedded in file |
|
||||
| `.bix`, `.bin` | Raw binary | `0x0000` (start of internal RAM) |
|
||||
|
||||
**Options:**
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `FILE` | Firmware file path (positional, required) |
|
||||
| `--no-reset` | Load segments without halting/starting the CPU |
|
||||
| `--wait SECONDS` | Wait for USB re-enumeration after load |
|
||||
| `-v, --verbose` | Show per-chunk transfer progress |
|
||||
| `--force` | Allow loading to devices with unknown VID/PID |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash title="Load custom firmware and wait for re-enumeration"
|
||||
sudo python3 tools/fw_load.py load firmware/build/skywalker1.ihx --wait 3
|
||||
```
|
||||
|
||||
```bash title="Load raw binary with verbose output"
|
||||
sudo python3 tools/fw_load.py load firmware.bix -v --wait 5
|
||||
```
|
||||
|
||||
```bash title="Load without CPU reset (write segments only)"
|
||||
sudo python3 tools/fw_load.py load firmware.ihx --no-reset
|
||||
```
|
||||
|
||||
**Typical output:**
|
||||
|
||||
```
|
||||
SkyWalker-1 RAM Firmware Loader
|
||||
========================================
|
||||
|
||||
Firmware: firmware/build/skywalker1.ihx
|
||||
Segments: 3
|
||||
Total size: 3072 bytes
|
||||
Address: 0x0000 - 0x0BFF
|
||||
|
||||
Found SkyWalker-1: Bus 1 Addr 12 (VID 0x09C0 PID 0x0203)
|
||||
|
||||
[1/3] Halting CPU (CPUCS = 0x01)...
|
||||
CPU halted
|
||||
|
||||
[2/3] Loading 3 segment(s) into RAM...
|
||||
0x0000-0x03FF (1024 bytes)
|
||||
0x0400-0x07FF (1024 bytes)
|
||||
0x0800-0x0BFF (1024 bytes)
|
||||
|
||||
3072 bytes loaded
|
||||
|
||||
[3/3] Starting CPU (CPUCS = 0x00)...
|
||||
CPU released
|
||||
|
||||
Firmware is running. The device will re-enumerate
|
||||
with new USB descriptors if the firmware does so.
|
||||
```
|
||||
|
||||
### reset -- Restart the FX2 CPU
|
||||
|
||||
Halts and restarts the CPU without loading new code. Useful for triggering USB re-enumeration when the device is in a bad state.
|
||||
|
||||
```bash
|
||||
sudo python3 tools/fw_load.py reset --wait 3
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--wait SECONDS` | Wait for re-enumeration after reset |
|
||||
| `--force` | Allow reset on unknown VID/PID |
|
||||
|
||||
### read -- Hex-Dump FX2 RAM
|
||||
|
||||
Read memory contents from a running FX2. Useful for verifying loaded firmware or inspecting register state.
|
||||
|
||||
```bash title="Read first 256 bytes of internal RAM"
|
||||
sudo python3 tools/fw_load.py read --addr 0x0000 --len 256
|
||||
```
|
||||
|
||||
```bash title="Check CPUCS register"
|
||||
sudo python3 tools/fw_load.py read --addr 0xe600 --len 1
|
||||
```
|
||||
|
||||
```bash title="Dump to file"
|
||||
sudo python3 tools/fw_load.py read --addr 0x0000 --len 8192 -o ram_dump.bin
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--addr ADDR` | Start address in hex (default: `0x0000`) |
|
||||
| `--len LENGTH` | Bytes to read (default: `256`) |
|
||||
| `-o, --output FILE` | Save raw bytes to file |
|
||||
| `--force` | Allow read on unknown VID/PID |
|
||||
|
||||
### Device Detection
|
||||
|
||||
The loader searches for devices in this order:
|
||||
|
||||
1. **SkyWalker-1** -- VID `0x09C0`, PID `0x0203`
|
||||
2. **Bare Cypress FX2** -- VID `0x04B4`, PID `0x8613` (unprogrammed/blank EEPROM)
|
||||
|
||||
Use `--force` to override VID/PID checks for devices with non-standard descriptors.
|
||||
|
||||
<Aside type="tip">
|
||||
**Recovery procedure:** If the device is completely hung (not responding to vendor commands), you can still recover it using `fw_load.py`. The FX2's `0xA0` vendor request is handled by the boot ROM in silicon, not by user firmware. Even a hung 8051 CPU can be halted and reprogrammed via this mechanism.
|
||||
</Aside>
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Dumping (fw_dump.py)">
|
||||
|
||||
## Firmware Probe and Dump Tool
|
||||
|
||||
`fw_dump.py` queries device information, dumps FX2 RAM contents, and scans for undocumented vendor commands.
|
||||
|
||||
### Options
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--info` | Query and display device information |
|
||||
| `--dump FILE` | Dump FX2 RAM to a binary file |
|
||||
| `--scan` | Brute-force scan all vendor commands (0x00--0xFF) |
|
||||
| `--start ADDR` | RAM dump start address (default: `0x0000`) |
|
||||
| `--size SIZE` | RAM dump size in bytes (default: `0x2000` = 8 KB) |
|
||||
| `--external` | Attempt to dump external RAM (64 KB) |
|
||||
|
||||
Running with no flags defaults to `--info --scan`.
|
||||
|
||||
### Device Info (--info)
|
||||
|
||||
Queries all known Genpix vendor commands and displays firmware version, build date, USB speed, serial number, and the 8PSK configuration status byte with decoded flags.
|
||||
|
||||
```bash
|
||||
sudo python3 tools/fw_dump.py --info
|
||||
```
|
||||
|
||||
```
|
||||
=== Genpix SkyWalker-1 Device Info ===
|
||||
|
||||
FW Version: 2.06.4 (0x020604)
|
||||
FW Build: 2007-07-13
|
||||
BCD Version: 0206
|
||||
Vendor: Genpix
|
||||
Product: SkyWalker-1
|
||||
USB Speed: High (480Mbps)
|
||||
Serial: 00000000
|
||||
Config: 0x03
|
||||
[ ON] 8PSK Started
|
||||
[ ON] BCM4500 FW Loaded
|
||||
[off] Intersil LNB On
|
||||
[off] DVB Mode
|
||||
[off] 22kHz Tone
|
||||
[off] 18V Selected
|
||||
[off] DC Tuned
|
||||
[off] Armed (streaming)
|
||||
```
|
||||
|
||||
### Vendor Command Scan (--scan)
|
||||
|
||||
Sends vendor IN requests to every command index from `0x00` to `0xFF` and reports which ones return data. Commands already documented in the reference are labeled `[KNOWN]`; unexpected responses are flagged `[NEW!]`.
|
||||
|
||||
```bash
|
||||
sudo python3 tools/fw_dump.py --scan
|
||||
```
|
||||
|
||||
### RAM Dump (--dump)
|
||||
|
||||
Reads FX2 internal RAM (8 KB at `0x0000`--`0x1FFF`) or external RAM (up to 64 KB with `--external`) via the standard `0xA0` vendor request.
|
||||
|
||||
```bash title="Dump internal RAM"
|
||||
sudo python3 tools/fw_dump.py --dump internal_ram.bin
|
||||
```
|
||||
|
||||
```bash title="Dump external RAM (64 KB)"
|
||||
sudo python3 tools/fw_dump.py --dump external_ram.bin --external
|
||||
```
|
||||
|
||||
```bash title="Dump specific range"
|
||||
sudo python3 tools/fw_dump.py --dump region.bin --start 0x1000 --size 0x800
|
||||
```
|
||||
|
||||
The tool performs a quick analysis of the dump, reporting non-`0xFF` byte count and checking for the standard FX2 reset vector (`LJMP` at address `0x0000`).
|
||||
|
||||
<Aside type="note">
|
||||
RAM readback may fail if the running firmware has disabled or overridden the `0xA0` vendor request. Stock Genpix firmware supports `0xA0` readback; some custom configurations may not. If you see consecutive read failures, the device firmware may need to be reloaded first.
|
||||
</Aside>
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Kernel Driver Conflict
|
||||
|
||||
The Linux `dvb_usb_gp8psk` module auto-loads when the SkyWalker-1 enumerates on USB. It will race with these tools for device access.
|
||||
|
||||
**Before using any tool**, either blacklist the module or unload it:
|
||||
|
||||
```bash title="Unload for current session"
|
||||
sudo modprobe -r dvb_usb_gp8psk gp8psk_fe
|
||||
```
|
||||
|
||||
```bash title="Permanent blacklist"
|
||||
echo -e "blacklist dvb_usb_gp8psk\nblacklist gp8psk_fe" | \
|
||||
sudo tee /etc/modprobe.d/blacklist-gp8psk.conf
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [EEPROM Utilities](/tools/eeprom-utilities/) -- for permanent firmware flashing
|
||||
- [Boot Sequence](/usb/boot-sequence/) -- what happens after firmware loads
|
||||
- [Custom Firmware v3.01.0](/firmware/custom-v301/) -- the open-source replacement firmware
|
||||
- [Vendor Commands](/usb/vendor-commands/) -- command reference used by these tools
|
||||
|
|
|
|||
|
|
@ -1,131 +1,131 @@
|
|||
---
|
||||
title: Hydrogen 21 cm Radiometer
|
||||
description: Detect neutral hydrogen emission at 1420.405 MHz using the SkyWalker-1 as an L-band radiometer.
|
||||
---
|
||||
|
||||
import { Aside, Card, CardGrid, Steps, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
The `h21cm.py` tool turns the SkyWalker-1 into a hydrogen line radiometer. Neutral hydrogen
|
||||
atoms emit radiation at **1420.405 MHz** when the electron's spin flips relative to the proton —
|
||||
the most fundamental spectral line in radio astronomy, and it falls directly in the IF range.
|
||||
|
||||
## Antenna Setup
|
||||
|
||||
The SkyWalker-1 normally receives satellite TV through an LNB (Low Noise Block downconverter) mounted
|
||||
at the focal point of a dish. For hydrogen line work, the LNB must be **removed or bypassed entirely** —
|
||||
it would block the signal. An LNB's waveguide feed is dimensioned for Ku-band wavelengths (~2.5 cm)
|
||||
and its internal filters reject everything outside the 10.7-12.75 GHz range. At 1420 MHz (wavelength
|
||||
~21 cm), nothing gets through.
|
||||
|
||||
Instead, connect an L-band antenna directly to the SkyWalker-1's F-connector with coaxial cable.
|
||||
The tool disables LNB power automatically, so there's no voltage on the cable.
|
||||
|
||||
### Antenna Options
|
||||
|
||||
<CardGrid>
|
||||
<Card title="Horn Antenna" icon="rocket">
|
||||
The classic radio astronomy choice. A tin-can "cantenna" or sheet-metal pyramidal horn provides
|
||||
10-15 dBi gain with predictable, calculable performance. Easy to build from hardware store materials.
|
||||
A circular waveguide horn from a ~15 cm diameter can works well at 1420 MHz.
|
||||
</Card>
|
||||
<Card title="Dish + L-Band Feed" icon="star">
|
||||
Reuse your satellite dish — replace the LNB with a 1420 MHz feed (dipole + reflector, or a small
|
||||
horn at the focal point). The dish surface accuracy matters less at 21 cm wavelength than at Ku-band,
|
||||
so even mesh dishes work fine. This gives the highest gain of any option here.
|
||||
</Card>
|
||||
<Card title="Helical Antenna" icon="setting">
|
||||
A helical antenna is circularly polarized and offers good gain in a compact package. Hydrogen emission
|
||||
is unpolarized, so a circularly-polarized antenna captures half the power (~3 dB penalty vs linear),
|
||||
but helix construction is forgiving and well-documented for L-band.
|
||||
</Card>
|
||||
<Card title="Patch Antenna" icon="open-book">
|
||||
Commercial L-band patch antennas (GPS antennas at 1575 MHz are close) are compact and cheap. Lower
|
||||
gain than the other options (~5-7 dBi), and narrow bandwidth may not cover the full hydrogen emission
|
||||
profile. Fine for a first detection attempt.
|
||||
</Card>
|
||||
</CardGrid>
|
||||
|
||||
<Aside type="note" title="Impedance mismatch">
|
||||
The SkyWalker-1's F-connector is 75Ω. Most L-band antennas and amateur radio feedlines are 50Ω.
|
||||
The resulting 1.5:1 VSWR costs about **0.2 dB** of mismatch loss — negligible for AGC power detection.
|
||||
No matching network is needed. Use an SMA-to-F adapter if your antenna has an SMA connector.
|
||||
</Aside>
|
||||
|
||||
### Cable and Connectors
|
||||
|
||||
Run 75Ω coax (RG-6 is standard satellite TV cable) from the antenna to the SkyWalker-1. At 1420 MHz,
|
||||
cable loss matters more than impedance mismatch — keep runs under 10 meters if possible. RG-6 loses
|
||||
roughly **0.2 dB per meter** at 1.4 GHz (about 6 dB per 100 feet), so a 10m run costs ~2 dB.
|
||||
Shorter is better.
|
||||
|
||||
If your antenna uses 50Ω connectors (SMA, N-type), a simple adapter to F-type is fine. The 0.2 dB
|
||||
impedance mismatch is far less than a meter of extra cable.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Single sweep, 8x averaging for best sensitivity
|
||||
python tools/h21cm.py --averages 8
|
||||
|
||||
# One-hour drift scan with CSV output
|
||||
python tools/h21cm.py --drift --duration 3600 --averages 4 --output h21cm-data.csv
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
<Steps>
|
||||
1. **LNB power is disabled** — direct input mode, no frequency conversion
|
||||
2. **Sweeps 1418-1422 MHz** (configurable) at 0.5 MHz steps
|
||||
3. **Measures AGC power** at each step — the BCM4500 responds to any RF energy
|
||||
4. **Estimates baseline** from the band edges (where no hydrogen is expected)
|
||||
5. **Calculates excess power** above baseline — the hydrogen emission
|
||||
6. **Computes Doppler velocity** for each frequency bin
|
||||
</Steps>
|
||||
|
||||
The velocity axis maps frequency to radial velocity via:
|
||||
|
||||
**v = c × (1420.405 − f_observed) / 1420.405**
|
||||
|
||||
Positive velocity = hydrogen moving away (lower frequency). The ~200 km/s spread
|
||||
in a typical observation maps the rotation curve of the Milky Way.
|
||||
|
||||
## Options
|
||||
|
||||
| Flag | Default | Description |
|
||||
|---|---|---|
|
||||
| `--center` | 1420.405 | Center frequency in MHz |
|
||||
| `--span` | 4.0 | Frequency span in MHz |
|
||||
| `--step` | 0.5 | Frequency step in MHz |
|
||||
| `--dwell` | 50 | Integration time per step in ms |
|
||||
| `--averages` | 1 | Number of sweeps to average (4-16 recommended) |
|
||||
| `--output` | — | CSV output file |
|
||||
| `--control` | — | Include control band comparison |
|
||||
| `--drift` | — | Enable drift scan mode |
|
||||
| `--duration` | 3600 | Drift scan duration in seconds |
|
||||
| `--interval` | 60 | Seconds between drift scans |
|
||||
| `--motor-step` | 0 | Motor steps between scans (declination scanning) |
|
||||
|
||||
## Sensitivity Notes
|
||||
|
||||
<Aside type="tip" title="Improving SNR">
|
||||
The hydrogen line is weak. The BCM4500's ~346 kHz resolution bandwidth is actually
|
||||
helpful here — it's wide enough to capture the broad galactic emission without
|
||||
excessive noise. Use `--averages 8` or higher and `--dwell 100` for best results.
|
||||
Each doubling of averages improves SNR by ~3 dB.
|
||||
</Aside>
|
||||
|
||||
The `--control` flag sweeps an adjacent band (1430-1434 MHz) where no hydrogen emission
|
||||
is expected. Comparing the two bands confirms that any detected bump is real signal,
|
||||
not system noise variation.
|
||||
|
||||
## CSV Output Format
|
||||
|
||||
| Column | Description |
|
||||
|---|---|
|
||||
| `timestamp` | ISO 8601 UTC timestamp |
|
||||
| `scan_num` | Scan number (drift mode only) |
|
||||
| `freq_mhz` | Frequency in MHz |
|
||||
| `power_db` | Raw power in dB (relative) |
|
||||
| `excess_db` | Power above baseline |
|
||||
| `velocity_km_s` | Doppler velocity in km/s |
|
||||
| `baseline_db` | Estimated noise floor |
|
||||
---
|
||||
title: Hydrogen 21 cm Radiometer
|
||||
description: Detect neutral hydrogen emission at 1420.405 MHz using the SkyWalker-1 as an L-band radiometer.
|
||||
---
|
||||
|
||||
import { Aside, Card, CardGrid, Steps, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
The `h21cm.py` tool turns the SkyWalker-1 into a hydrogen line radiometer. Neutral hydrogen
|
||||
atoms emit radiation at **1420.405 MHz** when the electron's spin flips relative to the proton —
|
||||
the most fundamental spectral line in radio astronomy, and it falls directly in the IF range.
|
||||
|
||||
## Antenna Setup
|
||||
|
||||
The SkyWalker-1 normally receives satellite TV through an LNB (Low Noise Block downconverter) mounted
|
||||
at the focal point of a dish. For hydrogen line work, the LNB must be **removed or bypassed entirely** —
|
||||
it would block the signal. An LNB's waveguide feed is dimensioned for Ku-band wavelengths (~2.5 cm)
|
||||
and its internal filters reject everything outside the 10.7-12.75 GHz range. At 1420 MHz (wavelength
|
||||
~21 cm), nothing gets through.
|
||||
|
||||
Instead, connect an L-band antenna directly to the SkyWalker-1's F-connector with coaxial cable.
|
||||
The tool disables LNB power automatically, so there's no voltage on the cable.
|
||||
|
||||
### Antenna Options
|
||||
|
||||
<CardGrid>
|
||||
<Card title="Horn Antenna" icon="rocket">
|
||||
The classic radio astronomy choice. A tin-can "cantenna" or sheet-metal pyramidal horn provides
|
||||
10-15 dBi gain with predictable, calculable performance. Easy to build from hardware store materials.
|
||||
A circular waveguide horn from a ~15 cm diameter can works well at 1420 MHz.
|
||||
</Card>
|
||||
<Card title="Dish + L-Band Feed" icon="star">
|
||||
Reuse your satellite dish — replace the LNB with a 1420 MHz feed (dipole + reflector, or a small
|
||||
horn at the focal point). The dish surface accuracy matters less at 21 cm wavelength than at Ku-band,
|
||||
so even mesh dishes work fine. This gives the highest gain of any option here.
|
||||
</Card>
|
||||
<Card title="Helical Antenna" icon="setting">
|
||||
A helical antenna is circularly polarized and offers good gain in a compact package. Hydrogen emission
|
||||
is unpolarized, so a circularly-polarized antenna captures half the power (~3 dB penalty vs linear),
|
||||
but helix construction is forgiving and well-documented for L-band.
|
||||
</Card>
|
||||
<Card title="Patch Antenna" icon="open-book">
|
||||
Commercial L-band patch antennas (GPS antennas at 1575 MHz are close) are compact and cheap. Lower
|
||||
gain than the other options (~5-7 dBi), and narrow bandwidth may not cover the full hydrogen emission
|
||||
profile. Fine for a first detection attempt.
|
||||
</Card>
|
||||
</CardGrid>
|
||||
|
||||
<Aside type="note" title="Impedance mismatch">
|
||||
The SkyWalker-1's F-connector is 75Ω. Most L-band antennas and amateur radio feedlines are 50Ω.
|
||||
The resulting 1.5:1 VSWR costs about **0.2 dB** of mismatch loss — negligible for AGC power detection.
|
||||
No matching network is needed. Use an SMA-to-F adapter if your antenna has an SMA connector.
|
||||
</Aside>
|
||||
|
||||
### Cable and Connectors
|
||||
|
||||
Run 75Ω coax (RG-6 is standard satellite TV cable) from the antenna to the SkyWalker-1. At 1420 MHz,
|
||||
cable loss matters more than impedance mismatch — keep runs under 10 meters if possible. RG-6 loses
|
||||
roughly **0.2 dB per meter** at 1.4 GHz (about 6 dB per 100 feet), so a 10m run costs ~2 dB.
|
||||
Shorter is better.
|
||||
|
||||
If your antenna uses 50Ω connectors (SMA, N-type), a simple adapter to F-type is fine. The 0.2 dB
|
||||
impedance mismatch is far less than a meter of extra cable.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Single sweep, 8x averaging for best sensitivity
|
||||
python tools/h21cm.py --averages 8
|
||||
|
||||
# One-hour drift scan with CSV output
|
||||
python tools/h21cm.py --drift --duration 3600 --averages 4 --output h21cm-data.csv
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
<Steps>
|
||||
1. **LNB power is disabled** — direct input mode, no frequency conversion
|
||||
2. **Sweeps 1418-1422 MHz** (configurable) at 0.5 MHz steps
|
||||
3. **Measures AGC power** at each step — the BCM4500 responds to any RF energy
|
||||
4. **Estimates baseline** from the band edges (where no hydrogen is expected)
|
||||
5. **Calculates excess power** above baseline — the hydrogen emission
|
||||
6. **Computes Doppler velocity** for each frequency bin
|
||||
</Steps>
|
||||
|
||||
The velocity axis maps frequency to radial velocity via:
|
||||
|
||||
**v = c × (1420.405 − f_observed) / 1420.405**
|
||||
|
||||
Positive velocity = hydrogen moving away (lower frequency). The ~200 km/s spread
|
||||
in a typical observation maps the rotation curve of the Milky Way.
|
||||
|
||||
## Options
|
||||
|
||||
| Flag | Default | Description |
|
||||
|---|---|---|
|
||||
| `--center` | 1420.405 | Center frequency in MHz |
|
||||
| `--span` | 4.0 | Frequency span in MHz |
|
||||
| `--step` | 0.5 | Frequency step in MHz |
|
||||
| `--dwell` | 50 | Integration time per step in ms |
|
||||
| `--averages` | 1 | Number of sweeps to average (4-16 recommended) |
|
||||
| `--output` | — | CSV output file |
|
||||
| `--control` | — | Include control band comparison |
|
||||
| `--drift` | — | Enable drift scan mode |
|
||||
| `--duration` | 3600 | Drift scan duration in seconds |
|
||||
| `--interval` | 60 | Seconds between drift scans |
|
||||
| `--motor-step` | 0 | Motor steps between scans (declination scanning) |
|
||||
|
||||
## Sensitivity Notes
|
||||
|
||||
<Aside type="tip" title="Improving SNR">
|
||||
The hydrogen line is weak. The BCM4500's ~346 kHz resolution bandwidth is actually
|
||||
helpful here — it's wide enough to capture the broad galactic emission without
|
||||
excessive noise. Use `--averages 8` or higher and `--dwell 100` for best results.
|
||||
Each doubling of averages improves SNR by ~3 dB.
|
||||
</Aside>
|
||||
|
||||
The `--control` flag sweeps an adjacent band (1430-1434 MHz) where no hydrogen emission
|
||||
is expected. Comparing the two bands confirms that any detected bump is real signal,
|
||||
not system noise variation.
|
||||
|
||||
## CSV Output Format
|
||||
|
||||
| Column | Description |
|
||||
|---|---|
|
||||
| `timestamp` | ISO 8601 UTC timestamp |
|
||||
| `scan_num` | Scan number (drift mode only) |
|
||||
| `freq_mhz` | Frequency in MHz |
|
||||
| `power_db` | Raw power in dB (relative) |
|
||||
| `excess_db` | Power above baseline |
|
||||
| `velocity_km_s` | Doppler velocity in km/s |
|
||||
| `baseline_db` | Estimated noise floor |
|
||||
|
|
|
|||
|
|
@ -1,112 +1,112 @@
|
|||
---
|
||||
title: MCP Server
|
||||
description: Model Context Protocol server that exposes the SkyWalker-1 hardware API to LLMs for autonomous RF exploration.
|
||||
---
|
||||
|
||||
import { Aside, Steps } from '@astrojs/starlight/components';
|
||||
|
||||
The `skywalker-mcp` package wraps the entire SkyWalker-1 Python API as an MCP (Model Context Protocol)
|
||||
server, making every hardware function accessible to LLMs. This enables natural-language signal analysis,
|
||||
autonomous RF exploration, and scheduled observation campaigns.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
cd mcp/skywalker-mcp
|
||||
uv sync
|
||||
```
|
||||
|
||||
## Running
|
||||
|
||||
```bash
|
||||
# Local development
|
||||
uv run --directory mcp/skywalker-mcp skywalker-mcp
|
||||
|
||||
# Add to Claude Code
|
||||
claude mcp add skywalker-mcp -- uv run --directory mcp/skywalker-mcp skywalker-mcp
|
||||
```
|
||||
|
||||
## Tools (20)
|
||||
|
||||
### Device Status
|
||||
| Tool | Description |
|
||||
|---|---|
|
||||
| `get_device_status` | Firmware version, config bits, USB speed, serial, last error |
|
||||
| `get_signal_quality` | SNR, AGC, power, lock status |
|
||||
| `get_stream_diagnostics` | Poll count, overflows, sync loss |
|
||||
|
||||
### Spectrum & Tuning
|
||||
| Tool | Description |
|
||||
|---|---|
|
||||
| `sweep_spectrum` | Full-band power measurement with peak detection |
|
||||
| `tune_frequency` | Tune to specific freq/modulation/FEC, read signal |
|
||||
| `run_blind_scan` | Symbol rate sweep at single frequency |
|
||||
|
||||
### Survey & Catalog
|
||||
| Tool | Description |
|
||||
|---|---|
|
||||
| `run_carrier_survey` | Six-stage pipeline: sweep → peaks → blind → TS → catalog |
|
||||
| `compare_surveys` | Diff two saved catalogs for changes |
|
||||
| `list_surveys` | List saved survey files with metadata |
|
||||
|
||||
### Dish Motor
|
||||
| Tool | Description |
|
||||
|---|---|
|
||||
| `move_dish` | Halt, east, west, goto slot, USALS GotoX (continuous drive requires explicit opt-in) |
|
||||
| `jog_dish` | Small steps (1-30) + signal quality readback |
|
||||
| `store_position` | Save current position to memory slot |
|
||||
|
||||
### LNB & I2C
|
||||
| Tool | Description |
|
||||
|---|---|
|
||||
| `set_lnb_config` | Voltage (13V/18V), 22 kHz tone, power off |
|
||||
| `scan_i2c_bus` | Enumerate all I2C devices |
|
||||
| `read_i2c_register` | Read single byte from I2C address |
|
||||
|
||||
### Transport Stream & Identification
|
||||
| Tool | Description |
|
||||
|---|---|
|
||||
| `capture_transport_stream` | Capture + parse PAT/PMT/SDT for service names |
|
||||
| `identify_frequency` | Look up frequency against allocation tables |
|
||||
|
||||
## Resources
|
||||
|
||||
| URI | Description |
|
||||
|---|---|
|
||||
| `skywalker://status` | Live device state (firmware, config, signal) |
|
||||
| `skywalker://catalog/latest` | Most recent survey catalog as JSON |
|
||||
| `skywalker://allocations/lband` | L-band frequency allocation table |
|
||||
| `skywalker://modulations` | Supported modulations and FEC rates |
|
||||
|
||||
## Prompts
|
||||
|
||||
| Prompt | Description |
|
||||
|---|---|
|
||||
| `explore_rf_environment` | Strategy for autonomous RF discovery |
|
||||
| `hydrogen_line_observation` | Guided 21 cm observation procedure |
|
||||
|
||||
## Architecture
|
||||
|
||||
<Aside type="note" title="Thread safety">
|
||||
The BCM4500 demodulator cannot handle overlapping USB control transfers. The MCP server
|
||||
uses the same `DeviceBridge` pattern as the TUI — a `threading.RLock` serializes all
|
||||
hardware access. Async MCP handlers use `asyncio.to_thread()` to avoid blocking the
|
||||
event loop during USB I/O.
|
||||
</Aside>
|
||||
|
||||
The server uses FastMCP's lifespan pattern: the USB device opens on server startup and
|
||||
closes on shutdown. All tools receive the device bridge through the lifespan context.
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
# Verify the server starts and can talk to hardware
|
||||
claude -p "What firmware version is loaded?" \
|
||||
--mcp-config .mcp.json \
|
||||
--allowedTools "mcp__skywalker-mcp__*"
|
||||
|
||||
# Run a spectrum sweep via natural language
|
||||
claude -p "Sweep the full IF band and tell me what you find" \
|
||||
--mcp-config .mcp.json \
|
||||
--allowedTools "mcp__skywalker-mcp__*"
|
||||
```
|
||||
---
|
||||
title: MCP Server
|
||||
description: Model Context Protocol server that exposes the SkyWalker-1 hardware API to LLMs for autonomous RF exploration.
|
||||
---
|
||||
|
||||
import { Aside, Steps } from '@astrojs/starlight/components';
|
||||
|
||||
The `skywalker-mcp` package wraps the entire SkyWalker-1 Python API as an MCP (Model Context Protocol)
|
||||
server, making every hardware function accessible to LLMs. This enables natural-language signal analysis,
|
||||
autonomous RF exploration, and scheduled observation campaigns.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
cd mcp/skywalker-mcp
|
||||
uv sync
|
||||
```
|
||||
|
||||
## Running
|
||||
|
||||
```bash
|
||||
# Local development
|
||||
uv run --directory mcp/skywalker-mcp skywalker-mcp
|
||||
|
||||
# Add to Claude Code
|
||||
claude mcp add skywalker-mcp -- uv run --directory mcp/skywalker-mcp skywalker-mcp
|
||||
```
|
||||
|
||||
## Tools (20)
|
||||
|
||||
### Device Status
|
||||
| Tool | Description |
|
||||
|---|---|
|
||||
| `get_device_status` | Firmware version, config bits, USB speed, serial, last error |
|
||||
| `get_signal_quality` | SNR, AGC, power, lock status |
|
||||
| `get_stream_diagnostics` | Poll count, overflows, sync loss |
|
||||
|
||||
### Spectrum & Tuning
|
||||
| Tool | Description |
|
||||
|---|---|
|
||||
| `sweep_spectrum` | Full-band power measurement with peak detection |
|
||||
| `tune_frequency` | Tune to specific freq/modulation/FEC, read signal |
|
||||
| `run_blind_scan` | Symbol rate sweep at single frequency |
|
||||
|
||||
### Survey & Catalog
|
||||
| Tool | Description |
|
||||
|---|---|
|
||||
| `run_carrier_survey` | Six-stage pipeline: sweep → peaks → blind → TS → catalog |
|
||||
| `compare_surveys` | Diff two saved catalogs for changes |
|
||||
| `list_surveys` | List saved survey files with metadata |
|
||||
|
||||
### Dish Motor
|
||||
| Tool | Description |
|
||||
|---|---|
|
||||
| `move_dish` | Halt, east, west, goto slot, USALS GotoX (continuous drive requires explicit opt-in) |
|
||||
| `jog_dish` | Small steps (1-30) + signal quality readback |
|
||||
| `store_position` | Save current position to memory slot |
|
||||
|
||||
### LNB & I2C
|
||||
| Tool | Description |
|
||||
|---|---|
|
||||
| `set_lnb_config` | Voltage (13V/18V), 22 kHz tone, power off |
|
||||
| `scan_i2c_bus` | Enumerate all I2C devices |
|
||||
| `read_i2c_register` | Read single byte from I2C address |
|
||||
|
||||
### Transport Stream & Identification
|
||||
| Tool | Description |
|
||||
|---|---|
|
||||
| `capture_transport_stream` | Capture + parse PAT/PMT/SDT for service names |
|
||||
| `identify_frequency` | Look up frequency against allocation tables |
|
||||
|
||||
## Resources
|
||||
|
||||
| URI | Description |
|
||||
|---|---|
|
||||
| `skywalker://status` | Live device state (firmware, config, signal) |
|
||||
| `skywalker://catalog/latest` | Most recent survey catalog as JSON |
|
||||
| `skywalker://allocations/lband` | L-band frequency allocation table |
|
||||
| `skywalker://modulations` | Supported modulations and FEC rates |
|
||||
|
||||
## Prompts
|
||||
|
||||
| Prompt | Description |
|
||||
|---|---|
|
||||
| `explore_rf_environment` | Strategy for autonomous RF discovery |
|
||||
| `hydrogen_line_observation` | Guided 21 cm observation procedure |
|
||||
|
||||
## Architecture
|
||||
|
||||
<Aside type="note" title="Thread safety">
|
||||
The BCM4500 demodulator cannot handle overlapping USB control transfers. The MCP server
|
||||
uses the same `DeviceBridge` pattern as the TUI — a `threading.RLock` serializes all
|
||||
hardware access. Async MCP handlers use `asyncio.to_thread()` to avoid blocking the
|
||||
event loop during USB I/O.
|
||||
</Aside>
|
||||
|
||||
The server uses FastMCP's lifespan pattern: the USB device opens on server startup and
|
||||
closes on shutdown. All tools receive the device bridge through the lifespan context.
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
# Verify the server starts and can talk to hardware
|
||||
claude -p "What firmware version is loaded?" \
|
||||
--mcp-config .mcp.json \
|
||||
--allowedTools "mcp__skywalker-mcp__*"
|
||||
|
||||
# Run a spectrum sweep via natural language
|
||||
claude -p "Sweep the full IF band and tell me what you find" \
|
||||
--mcp-config .mcp.json \
|
||||
--allowedTools "mcp__skywalker-mcp__*"
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,107 +1,107 @@
|
|||
---
|
||||
title: Motor Control
|
||||
description: DiSEqC 1.2 positioner motor control with USALS GotoX, stored positions, and live signal feedback for dish alignment.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Steps, Aside, Badge } from '@astrojs/starlight/components';
|
||||
|
||||
Command-line tool for driving a DiSEqC 1.2 positioner motor through the SkyWalker-1. Supports continuous jog, stored positions, USALS GotoX calculations, and an interactive keyboard-driven mode with live signal feedback for hands-free dish alignment.
|
||||
|
||||
<Badge text="Requires custom firmware v3.03.0+" variant="caution" />
|
||||
|
||||
## Usage
|
||||
|
||||
```bash title="Interactive motor jog with live signal"
|
||||
sudo python3 tools/motor.py interactive
|
||||
```
|
||||
|
||||
```bash title="Drive to a stored position"
|
||||
sudo python3 tools/motor.py goto 3
|
||||
```
|
||||
|
||||
```bash title="USALS GotoX (Es'hail-2 from central Texas)"
|
||||
sudo python3 tools/motor.py gotox --sat 25.9 --lon -97.5
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `halt` | Emergency stop the motor |
|
||||
| `east [--steps N]` | Drive east (continuous or N steps) |
|
||||
| `west [--steps N]` | Drive west (continuous or N steps) |
|
||||
| `goto SLOT` | Recall stored position (0 = reference) |
|
||||
| `store SLOT` | Store current position in slot (1-255) |
|
||||
| `gotox --sat LON --lon LON` | USALS GotoX with calculated rotation angle |
|
||||
| `limit east\|west` | Set east or west movement limit |
|
||||
| `nolimits` | Disable movement limits |
|
||||
| `raw HEX` | Send raw DiSEqC bytes (e.g. `E0 31 60`) |
|
||||
| `interactive` | Keyboard-driven jog with live signal display |
|
||||
|
||||
## Interactive Mode
|
||||
|
||||
The `interactive` subcommand turns the terminal into a real-time motor controller optimized for dish alignment. No menus, no prompts — just directional input with instant signal feedback.
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| <kbd>Left</kbd> / <kbd>h</kbd> | Jog west |
|
||||
| <kbd>Right</kbd> / <kbd>l</kbd> | Jog east |
|
||||
| <kbd>Space</kbd> | Halt motor |
|
||||
| <kbd>1</kbd>–<kbd>9</kbd> | Recall stored position |
|
||||
| <kbd>s</kbd> | Store current position (prompts for slot) |
|
||||
| <kbd>g</kbd> | USALS GotoX (prompts for coordinates) |
|
||||
| <kbd>q</kbd> | Quit (motor auto-halts on exit) |
|
||||
|
||||
Signal readings update at ~2 Hz, showing SNR, AGC power, and lock status. The display uses ANSI color to distinguish locked (green) from unlocked (red) states.
|
||||
|
||||
### Safety Features
|
||||
|
||||
- **30-second auto-halt** — continuous jog stops automatically after 30 seconds to prevent mechanical damage
|
||||
- **Exit handler** — motor is halted via `atexit` if the process terminates unexpectedly
|
||||
- **Limit enforcement** — DiSEqC 1.2 hardware limits are respected when set
|
||||
|
||||
<Aside type="caution">
|
||||
The motor continues to drive until explicitly halted. If the SkyWalker-1 loses USB power during a jog, the motor will keep moving until it hits a hardware limit. Always set east/west limits before extended use.
|
||||
</Aside>
|
||||
|
||||
## USALS GotoX
|
||||
|
||||
The `gotox` subcommand calculates the optimal motor rotation angle using the USALS (Universal Satellites Automatic Location System) algorithm and sends a DiSEqC 1.3 GotoX command.
|
||||
|
||||
```bash title="Calculate and drive to Es'hail-2 (QO-100)"
|
||||
sudo python3 tools/motor.py gotox --sat 25.9 --lon -97.5
|
||||
# Output: Angle: 72.4 deg East — sending GotoX
|
||||
```
|
||||
|
||||
The angle calculation accounts for:
|
||||
- Observer longitude (negative = West)
|
||||
- Satellite longitude (positive = East)
|
||||
- Geostationary orbit geometry at 35,786 km altitude
|
||||
|
||||
<Aside type="tip">
|
||||
For QO-100 reception from North America, the motor angle is extreme (~70+ degrees East). Verify your positioner has sufficient travel range before issuing the command.
|
||||
</Aside>
|
||||
|
||||
## DiSEqC 1.2 Protocol
|
||||
|
||||
All motor commands use DiSEqC 1.2 (EN 50494) framing transmitted through the SkyWalker-1's Manchester encoder. The firmware generates the 22 kHz modulated waveform with correct timing:
|
||||
|
||||
| Command | DiSEqC Bytes | Description |
|
||||
|---------|-------------|-------------|
|
||||
| Halt | `E0 31 60` | Stop motor movement |
|
||||
| Drive East | `E0 31 68 00` | Continuous east (00 = no step limit) |
|
||||
| Drive West | `E0 31 69 00` | Continuous west |
|
||||
| Store Position | `E0 31 6A NN` | Save current position to slot NN |
|
||||
| Goto Position | `E0 31 6B NN` | Drive to stored position NN |
|
||||
| Set East Limit | `E0 31 66 00` | Set current position as east limit |
|
||||
| Set West Limit | `E0 31 66 01` | Set current position as west limit |
|
||||
| Disable Limits | `E0 31 63` | Remove movement limits |
|
||||
| GotoX | `E0 31 6E HH LL` | USALS rotation (angle encoded as 2 bytes) |
|
||||
|
||||
The `raw` subcommand accepts any hex string for advanced DiSEqC messaging beyond the built-in commands.
|
||||
|
||||
## See Also
|
||||
|
||||
- [SkyWalker TUI — Motor Screen](/tools/tui/#motor-control) — graphical motor control with signal gauge
|
||||
- [DiSEqC Protocol](/lnb-diseqc/diseqc-protocol/) — protocol specification and Manchester encoding details
|
||||
- [QO-100 DATV Reception](/guides/qo100-datv/) — using motor control for QO-100 dish pointing
|
||||
---
|
||||
title: Motor Control
|
||||
description: DiSEqC 1.2 positioner motor control with USALS GotoX, stored positions, and live signal feedback for dish alignment.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Steps, Aside, Badge } from '@astrojs/starlight/components';
|
||||
|
||||
Command-line tool for driving a DiSEqC 1.2 positioner motor through the SkyWalker-1. Supports continuous jog, stored positions, USALS GotoX calculations, and an interactive keyboard-driven mode with live signal feedback for hands-free dish alignment.
|
||||
|
||||
<Badge text="Requires custom firmware v3.03.0+" variant="caution" />
|
||||
|
||||
## Usage
|
||||
|
||||
```bash title="Interactive motor jog with live signal"
|
||||
sudo python3 tools/motor.py interactive
|
||||
```
|
||||
|
||||
```bash title="Drive to a stored position"
|
||||
sudo python3 tools/motor.py goto 3
|
||||
```
|
||||
|
||||
```bash title="USALS GotoX (Es'hail-2 from central Texas)"
|
||||
sudo python3 tools/motor.py gotox --sat 25.9 --lon -97.5
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `halt` | Emergency stop the motor |
|
||||
| `east [--steps N]` | Drive east (continuous or N steps) |
|
||||
| `west [--steps N]` | Drive west (continuous or N steps) |
|
||||
| `goto SLOT` | Recall stored position (0 = reference) |
|
||||
| `store SLOT` | Store current position in slot (1-255) |
|
||||
| `gotox --sat LON --lon LON` | USALS GotoX with calculated rotation angle |
|
||||
| `limit east\|west` | Set east or west movement limit |
|
||||
| `nolimits` | Disable movement limits |
|
||||
| `raw HEX` | Send raw DiSEqC bytes (e.g. `E0 31 60`) |
|
||||
| `interactive` | Keyboard-driven jog with live signal display |
|
||||
|
||||
## Interactive Mode
|
||||
|
||||
The `interactive` subcommand turns the terminal into a real-time motor controller optimized for dish alignment. No menus, no prompts — just directional input with instant signal feedback.
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| <kbd>Left</kbd> / <kbd>h</kbd> | Jog west |
|
||||
| <kbd>Right</kbd> / <kbd>l</kbd> | Jog east |
|
||||
| <kbd>Space</kbd> | Halt motor |
|
||||
| <kbd>1</kbd>–<kbd>9</kbd> | Recall stored position |
|
||||
| <kbd>s</kbd> | Store current position (prompts for slot) |
|
||||
| <kbd>g</kbd> | USALS GotoX (prompts for coordinates) |
|
||||
| <kbd>q</kbd> | Quit (motor auto-halts on exit) |
|
||||
|
||||
Signal readings update at ~2 Hz, showing SNR, AGC power, and lock status. The display uses ANSI color to distinguish locked (green) from unlocked (red) states.
|
||||
|
||||
### Safety Features
|
||||
|
||||
- **30-second auto-halt** — continuous jog stops automatically after 30 seconds to prevent mechanical damage
|
||||
- **Exit handler** — motor is halted via `atexit` if the process terminates unexpectedly
|
||||
- **Limit enforcement** — DiSEqC 1.2 hardware limits are respected when set
|
||||
|
||||
<Aside type="caution">
|
||||
The motor continues to drive until explicitly halted. If the SkyWalker-1 loses USB power during a jog, the motor will keep moving until it hits a hardware limit. Always set east/west limits before extended use.
|
||||
</Aside>
|
||||
|
||||
## USALS GotoX
|
||||
|
||||
The `gotox` subcommand calculates the optimal motor rotation angle using the USALS (Universal Satellites Automatic Location System) algorithm and sends a DiSEqC 1.3 GotoX command.
|
||||
|
||||
```bash title="Calculate and drive to Es'hail-2 (QO-100)"
|
||||
sudo python3 tools/motor.py gotox --sat 25.9 --lon -97.5
|
||||
# Output: Angle: 72.4 deg East — sending GotoX
|
||||
```
|
||||
|
||||
The angle calculation accounts for:
|
||||
- Observer longitude (negative = West)
|
||||
- Satellite longitude (positive = East)
|
||||
- Geostationary orbit geometry at 35,786 km altitude
|
||||
|
||||
<Aside type="tip">
|
||||
For QO-100 reception from North America, the motor angle is extreme (~70+ degrees East). Verify your positioner has sufficient travel range before issuing the command.
|
||||
</Aside>
|
||||
|
||||
## DiSEqC 1.2 Protocol
|
||||
|
||||
All motor commands use DiSEqC 1.2 (EN 50494) framing transmitted through the SkyWalker-1's Manchester encoder. The firmware generates the 22 kHz modulated waveform with correct timing:
|
||||
|
||||
| Command | DiSEqC Bytes | Description |
|
||||
|---------|-------------|-------------|
|
||||
| Halt | `E0 31 60` | Stop motor movement |
|
||||
| Drive East | `E0 31 68 00` | Continuous east (00 = no step limit) |
|
||||
| Drive West | `E0 31 69 00` | Continuous west |
|
||||
| Store Position | `E0 31 6A NN` | Save current position to slot NN |
|
||||
| Goto Position | `E0 31 6B NN` | Drive to stored position NN |
|
||||
| Set East Limit | `E0 31 66 00` | Set current position as east limit |
|
||||
| Set West Limit | `E0 31 66 01` | Set current position as west limit |
|
||||
| Disable Limits | `E0 31 63` | Remove movement limits |
|
||||
| GotoX | `E0 31 6E HH LL` | USALS rotation (angle encoded as 2 bytes) |
|
||||
|
||||
The `raw` subcommand accepts any hex string for advanced DiSEqC messaging beyond the built-in commands.
|
||||
|
||||
## See Also
|
||||
|
||||
- [SkyWalker TUI — Motor Screen](/tools/tui/#motor-control) — graphical motor control with signal gauge
|
||||
- [DiSEqC Protocol](/lnb-diseqc/diseqc-protocol/) — protocol specification and Manchester encoding details
|
||||
- [QO-100 DATV Reception](/guides/qo100-datv/) — using motor control for QO-100 dish pointing
|
||||
|
|
|
|||
|
|
@ -1,260 +1,260 @@
|
|||
---
|
||||
title: RF Test Bench
|
||||
description: Automated CW injection testing with NanoVNA, HMC472A digital attenuator, and SkyWalker-1 receiver.
|
||||
---
|
||||
|
||||
import { Aside, Badge, Card, CardGrid, Steps, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
<Badge text="tools/rf_testbench.py" variant="note" />
|
||||
|
||||
The `rf_testbench.py` tool turns a NanoVNA, an HMC472A digital attenuator, and the SkyWalker-1 into
|
||||
an automated RF test bench. It injects CW signals at known frequencies and power levels, then
|
||||
measures the receiver's response — characterizing AGC linearity, IF band flatness, frequency
|
||||
accuracy, sensitivity, and BPSK mode 9 behavior.
|
||||
|
||||
## Hardware Setup
|
||||
|
||||
<Steps>
|
||||
1. **NanoVNA CH0 output** (SMA) connects to a **DC blocker** (SMA inline, required)
|
||||
2. **DC blocker output** connects to the **HMC472A RF IN** (SMA)
|
||||
3. **HMC472A RF OUT** (SMA) connects via **SMA-to-F adapter** to the **SkyWalker-1 F-connector**
|
||||
4. **HMC472A ESP32-S2 controller** connected to your network (WiFi) — reachable at `http://attenuator.local`
|
||||
5. **NanoVNA** connected via USB (for mcnanovna automation) or operated via touchscreen (manual mode)
|
||||
</Steps>
|
||||
|
||||
```
|
||||
NanoVNA CH0 ──→ DC Blocker ──→ HMC472A (0-31.5 dB) ──→ SMA-to-F ──→ SkyWalker-1
|
||||
(SMA) (SMA) REST API control adapter (F-type)
|
||||
http://attenuator.local
|
||||
```
|
||||
|
||||
### Components
|
||||
|
||||
| Component | Purpose | Notes |
|
||||
|-----------|---------|-------|
|
||||
| NanoVNA-H (9 kHz-1.5 GHz) | CW signal source | Output ~-15 dBm at max power. Overlaps SkyWalker-1 IF band at 950-1500 MHz |
|
||||
| DC Blocker (SMA inline) | Protect NanoVNA from LNB voltage | Required — even though the tool disables LNB power, this prevents accidental damage |
|
||||
| HMC472A attenuator module | Precision level control | 0-31.5 dB in 0.5 dB steps, controlled via ESP32-S2 REST API |
|
||||
| SMA-to-F adapter | Connector transition | 50-to-75 ohm mismatch is ~0.2 dB — negligible |
|
||||
|
||||
<Aside type="danger" title="DC blocker is required">
|
||||
The SkyWalker-1 can supply 13-18V DC through the F-connector for LNB power. Although `rf_testbench.py`
|
||||
disables LNB power on startup, a bug, power glitch, or running a different tool without disconnecting
|
||||
could send DC voltage backward through the signal path. The DC blocker prevents this from reaching
|
||||
the HMC472A and NanoVNA.
|
||||
</Aside>
|
||||
|
||||
### HMC472A Attenuator
|
||||
|
||||
The [HMC472A digital attenuator](https://hmc472.l.zmesh.systems/) provides programmable signal level
|
||||
control via its ESP32-S2 REST API:
|
||||
|
||||
- **Range**: 0 to 31.5 dB in 0.5 dB steps (64 discrete settings)
|
||||
- **Bandwidth**: DC to 3.8 GHz (covers the full SkyWalker-1 IF range)
|
||||
- **Insertion loss**: 1.4-1.9 dB typical
|
||||
- **Control**: HTTP REST — `POST /set {"attenuation_db": 10.5}`
|
||||
- **Switching speed**: 60 ns (faster than any measurement cycle)
|
||||
|
||||
The tool communicates with the attenuator at `http://attenuator.local` by default. Override with
|
||||
`--attenuator http://10.0.0.50` if your device has a different address.
|
||||
|
||||
### NanoVNA Frequency Overlap
|
||||
|
||||
The NanoVNA-H (HW3.7) covers 9 kHz to 1.5 GHz. The SkyWalker-1's IF range is 950-2150 MHz.
|
||||
The **overlapping usable range is 950-1500 MHz** — the lower portion of the IF band. This is
|
||||
sufficient for characterizing the tuner and AGC, and includes the 1420 MHz hydrogen line region.
|
||||
|
||||
For testing above 1500 MHz, a different signal source (bladeRF, signal generator) would be needed.
|
||||
|
||||
## Calibration
|
||||
|
||||
Before running quantitative tests, characterize the signal path loss:
|
||||
|
||||
<Steps>
|
||||
1. Disconnect the SkyWalker-1 end of the cable
|
||||
2. Connect: **NanoVNA CH0** → DC blocker → HMC472A (set to 0 dB) → cable → **NanoVNA CH1**
|
||||
3. Run an S21 sweep from 950 to 1500 MHz using mcnanovna or the NanoVNA touchscreen
|
||||
4. Export as CSV with columns `freq_mhz` and `s21_db` (or `frequency_hz` and `loss_db`)
|
||||
5. Pass to `rf_testbench.py` with `--cal path_loss.csv`
|
||||
</Steps>
|
||||
|
||||
The tool interpolates the measured path loss at each test frequency and subtracts it from AGC
|
||||
readings. Without a calibration file, raw AGC values are still reported — useful for relative
|
||||
measurements but not calibrated to absolute power.
|
||||
|
||||
<Aside type="tip" title="HMC472A insertion loss">
|
||||
The HMC472A adds 1.4-1.9 dB of insertion loss even at 0 dB attenuation setting. The calibration
|
||||
sweep captures this automatically since the signal passes through the attenuator during the S21
|
||||
measurement.
|
||||
</Aside>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **SkyWalker-1** with [custom firmware v3.02+](/firmware/custom-v302/) (for `tune_monitor` command)
|
||||
- **HMC472A** attenuator with ESP32-S2 controller on the network
|
||||
- **NanoVNA-H** (manual mode works with any VNA; auto mode requires [mcnanovna](https://git.supported.systems/rf/mcnanovna))
|
||||
- **Python 3.10+** with `pyusb` installed
|
||||
- **DC blocker** (SMA inline)
|
||||
- **SMA-to-F adapter**
|
||||
|
||||
## Test Descriptions
|
||||
|
||||
### AGC Power Linearity
|
||||
|
||||
```bash
|
||||
python tools/rf_testbench.py agc-linearity --freq 1200
|
||||
```
|
||||
|
||||
Injects CW at a fixed frequency while sweeping the HMC472A from 0 to 31.5 dB in 0.5 dB steps.
|
||||
At each attenuation level, the SkyWalker-1 reports AGC1, AGC2, and derived power. This maps the
|
||||
**AGC transfer function** — how the receiver's automatic gain control responds to known changes
|
||||
in input power.
|
||||
|
||||
The output shows whether the AGC is linear, where it saturates, and its effective dynamic range.
|
||||
With 64 measurement points across 31.5 dB, the resolution is high enough to reveal nonlinearities
|
||||
in the BCM3440 tuner's gain control loop.
|
||||
|
||||
### IF Band Flatness
|
||||
|
||||
```bash
|
||||
python tools/rf_testbench.py band-flatness --start 950 --stop 1500 --step 10
|
||||
```
|
||||
|
||||
Sweeps the NanoVNA CW frequency across the IF band while keeping the HMC472A at a fixed
|
||||
attenuation (10 dB default). At each frequency, the SkyWalker-1 tunes and reads AGC power.
|
||||
|
||||
The result reveals:
|
||||
- **Tuner gain slope**: the BCM3440 may have more gain at some frequencies than others
|
||||
- **Passband ripple**: resonances or nulls in the IF filter chain
|
||||
- **Cable/path frequency response**: if a calibration file is loaded, this is subtracted out
|
||||
|
||||
### Frequency Accuracy
|
||||
|
||||
```bash
|
||||
python tools/rf_testbench.py freq-accuracy --freqs 1000,1100,1200,1300,1400
|
||||
```
|
||||
|
||||
At each test frequency, the NanoVNA injects CW while the SkyWalker-1 runs a narrow spectrum
|
||||
sweep (+/- 5 MHz) around the expected frequency. The detected power peak is compared against the
|
||||
injected frequency.
|
||||
|
||||
This characterizes the **BCM3440 tuner's frequency accuracy** — how much the actual tuned
|
||||
frequency differs from the commanded frequency. The error may be systematic (constant offset)
|
||||
or frequency-dependent.
|
||||
|
||||
### Minimum Detectable Signal
|
||||
|
||||
```bash
|
||||
python tools/rf_testbench.py mds --freq 1200
|
||||
```
|
||||
|
||||
First measures the noise floor with maximum attenuation (31.5 dB). Then injects CW and steps
|
||||
the HMC472A from 0 dB upward in 1 dB increments until the signal drops below 3-sigma above
|
||||
the noise floor.
|
||||
|
||||
The attenuation level where the signal disappears, combined with the NanoVNA output power
|
||||
(~-15 dBm), gives an approximate **minimum detectable signal level** in dBm.
|
||||
|
||||
### BPSK Mode 9 CW Probe
|
||||
|
||||
```bash
|
||||
python tools/rf_testbench.py bpsk-probe --freq 1200
|
||||
```
|
||||
|
||||
An exploratory test that tunes the SkyWalker-1 in **BPSK mode (index 9)** — the same Viterbi
|
||||
rate 1/2 K=7 inner FEC used by GOES LRIT. A CW carrier has no modulation, so the demodulator
|
||||
shouldn't acquire lock, but the AGC and carrier recovery behavior is informative.
|
||||
|
||||
Tests several symbol rates (293,883 sps matching LRIT, plus 500K, 1M, and 5M) and compares
|
||||
against QPSK mode 0 at the same frequency. This establishes a baseline for what mode 9 reports
|
||||
with an unmodulated carrier — useful context for future modulated-signal experiments with a
|
||||
bladeRF.
|
||||
|
||||
## Options
|
||||
|
||||
| Flag | Default | Description |
|
||||
|------|---------|-------------|
|
||||
| `--attenuator` | `http://attenuator.local` | HMC472A REST API base URL |
|
||||
| `--nanovna` | `auto` | NanoVNA control: `auto` (mcnanovna) or `manual` (prompted) |
|
||||
| `--cal` | — | Path loss calibration CSV file |
|
||||
| `--settle` | 200 | Settle time in ms after changing attenuation |
|
||||
| `--output` / `-o` | — | CSV output file |
|
||||
| `--verbose` / `-v` | — | Show raw USB traffic |
|
||||
|
||||
### Per-Test Options
|
||||
|
||||
| Test | Flag | Default | Description |
|
||||
|------|------|---------|-------------|
|
||||
| `agc-linearity` | `--freq` | 1200 | Test frequency in MHz |
|
||||
| `band-flatness` | `--start` | 950 | Start frequency in MHz |
|
||||
| `band-flatness` | `--stop` | 1500 | Stop frequency in MHz |
|
||||
| `band-flatness` | `--step` | 10 | Frequency step in MHz |
|
||||
| `freq-accuracy` | `--freqs` | 1000,1100,1200,1300,1400 | Comma-separated test frequencies |
|
||||
| `mds` | `--freq` | 1200 | Test frequency in MHz |
|
||||
| `bpsk-probe` | `--freq` | 1200 | Test frequency in MHz |
|
||||
|
||||
## CSV Output Format
|
||||
|
||||
All tests write the same CSV format when `--output` is specified:
|
||||
|
||||
| Column | Description |
|
||||
|--------|-------------|
|
||||
| `timestamp` | ISO 8601 UTC timestamp |
|
||||
| `test_name` | Test identifier (agc_linearity, band_flatness, freq_accuracy, mds, bpsk_probe) |
|
||||
| `freq_mhz` | Frequency in MHz |
|
||||
| `atten_db` | HMC472A attenuation setting in dB |
|
||||
| `agc1` | BCM3440 AGC1 register value |
|
||||
| `agc2` | BCM3440 AGC2 register value |
|
||||
| `power_db` | Derived power estimate in dB (relative) |
|
||||
| `snr_raw` | Raw SNR register value |
|
||||
| `snr_db` | SNR in dB |
|
||||
| `locked` | Demodulator lock status |
|
||||
| `lock_raw` | Raw lock status byte |
|
||||
| `status` | Status byte |
|
||||
| `notes` | Test-specific metadata |
|
||||
|
||||
## Interpreting Results
|
||||
|
||||
### AGC Linearity Curves
|
||||
|
||||
A well-behaved AGC should show a roughly linear relationship between attenuation (dB) and AGC
|
||||
register value. Look for:
|
||||
|
||||
- **Linear region**: Where AGC tracks input power changes 1:1 in dB — this is the useful
|
||||
measurement range
|
||||
- **Saturation**: Where adding more signal doesn't change AGC — the tuner's front end is
|
||||
compressing
|
||||
- **Noise floor**: Where reducing signal doesn't change AGC — the receiver's internal noise
|
||||
dominates
|
||||
|
||||
### Band Flatness
|
||||
|
||||
Ideal response is flat across the band. In practice:
|
||||
- **1-3 dB variation** across 950-1500 MHz is typical for a consumer-grade tuner
|
||||
- **Sharp dips** may indicate cable resonances or connector issues
|
||||
- **Systematic slope** (gain increasing or decreasing with frequency) is common and can be
|
||||
corrected in post-processing
|
||||
|
||||
### Frequency Error
|
||||
|
||||
Consumer satellite tuners typically have **50-200 kHz frequency accuracy**. A consistent offset
|
||||
suggests LO error in the BCM3440. Frequency-dependent error suggests tuning nonlinearity.
|
||||
|
||||
## Mock Mode
|
||||
|
||||
Run with `SKYWALKER_MOCK=1` for testing without hardware:
|
||||
|
||||
```bash
|
||||
SKYWALKER_MOCK=1 python tools/rf_testbench.py agc-linearity --freq 1200 --nanovna manual
|
||||
```
|
||||
|
||||
Mock mode uses built-in simulated responses for the SkyWalker-1 and HMC472A. The NanoVNA prompts
|
||||
are skipped. Useful for verifying command structure and CSV output format.
|
||||
|
||||
## See Also
|
||||
|
||||
- [Spectrum Analysis](/tools/spectrum-analysis/) — frequency sweep techniques
|
||||
- [Hydrogen 21 cm](/tools/h21cm/) — direct L-band input mode (same RF path concept)
|
||||
- [Signal Monitoring](/bcm4500/signal-monitoring/) — AGC and SNR register details
|
||||
- [HMC472A Documentation](https://hmc472.l.zmesh.systems/) — attenuator module reference
|
||||
- [Applications & Use Cases](/guides/applications/) — RF test and measurement context
|
||||
---
|
||||
title: RF Test Bench
|
||||
description: Automated CW injection testing with NanoVNA, HMC472A digital attenuator, and SkyWalker-1 receiver.
|
||||
---
|
||||
|
||||
import { Aside, Badge, Card, CardGrid, Steps, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
<Badge text="tools/rf_testbench.py" variant="note" />
|
||||
|
||||
The `rf_testbench.py` tool turns a NanoVNA, an HMC472A digital attenuator, and the SkyWalker-1 into
|
||||
an automated RF test bench. It injects CW signals at known frequencies and power levels, then
|
||||
measures the receiver's response — characterizing AGC linearity, IF band flatness, frequency
|
||||
accuracy, sensitivity, and BPSK mode 9 behavior.
|
||||
|
||||
## Hardware Setup
|
||||
|
||||
<Steps>
|
||||
1. **NanoVNA CH0 output** (SMA) connects to a **DC blocker** (SMA inline, required)
|
||||
2. **DC blocker output** connects to the **HMC472A RF IN** (SMA)
|
||||
3. **HMC472A RF OUT** (SMA) connects via **SMA-to-F adapter** to the **SkyWalker-1 F-connector**
|
||||
4. **HMC472A ESP32-S2 controller** connected to your network (WiFi) — reachable at `http://attenuator.local`
|
||||
5. **NanoVNA** connected via USB (for mcnanovna automation) or operated via touchscreen (manual mode)
|
||||
</Steps>
|
||||
|
||||
```
|
||||
NanoVNA CH0 ──→ DC Blocker ──→ HMC472A (0-31.5 dB) ──→ SMA-to-F ──→ SkyWalker-1
|
||||
(SMA) (SMA) REST API control adapter (F-type)
|
||||
http://attenuator.local
|
||||
```
|
||||
|
||||
### Components
|
||||
|
||||
| Component | Purpose | Notes |
|
||||
|-----------|---------|-------|
|
||||
| NanoVNA-H (9 kHz-1.5 GHz) | CW signal source | Output ~-15 dBm at max power. Overlaps SkyWalker-1 IF band at 950-1500 MHz |
|
||||
| DC Blocker (SMA inline) | Protect NanoVNA from LNB voltage | Required — even though the tool disables LNB power, this prevents accidental damage |
|
||||
| HMC472A attenuator module | Precision level control | 0-31.5 dB in 0.5 dB steps, controlled via ESP32-S2 REST API |
|
||||
| SMA-to-F adapter | Connector transition | 50-to-75 ohm mismatch is ~0.2 dB — negligible |
|
||||
|
||||
<Aside type="danger" title="DC blocker is required">
|
||||
The SkyWalker-1 can supply 13-18V DC through the F-connector for LNB power. Although `rf_testbench.py`
|
||||
disables LNB power on startup, a bug, power glitch, or running a different tool without disconnecting
|
||||
could send DC voltage backward through the signal path. The DC blocker prevents this from reaching
|
||||
the HMC472A and NanoVNA.
|
||||
</Aside>
|
||||
|
||||
### HMC472A Attenuator
|
||||
|
||||
The [HMC472A digital attenuator](https://hmc472.l.zmesh.systems/) provides programmable signal level
|
||||
control via its ESP32-S2 REST API:
|
||||
|
||||
- **Range**: 0 to 31.5 dB in 0.5 dB steps (64 discrete settings)
|
||||
- **Bandwidth**: DC to 3.8 GHz (covers the full SkyWalker-1 IF range)
|
||||
- **Insertion loss**: 1.4-1.9 dB typical
|
||||
- **Control**: HTTP REST — `POST /set {"attenuation_db": 10.5}`
|
||||
- **Switching speed**: 60 ns (faster than any measurement cycle)
|
||||
|
||||
The tool communicates with the attenuator at `http://attenuator.local` by default. Override with
|
||||
`--attenuator http://10.0.0.50` if your device has a different address.
|
||||
|
||||
### NanoVNA Frequency Overlap
|
||||
|
||||
The NanoVNA-H (HW3.7) covers 9 kHz to 1.5 GHz. The SkyWalker-1's IF range is 950-2150 MHz.
|
||||
The **overlapping usable range is 950-1500 MHz** — the lower portion of the IF band. This is
|
||||
sufficient for characterizing the tuner and AGC, and includes the 1420 MHz hydrogen line region.
|
||||
|
||||
For testing above 1500 MHz, a different signal source (bladeRF, signal generator) would be needed.
|
||||
|
||||
## Calibration
|
||||
|
||||
Before running quantitative tests, characterize the signal path loss:
|
||||
|
||||
<Steps>
|
||||
1. Disconnect the SkyWalker-1 end of the cable
|
||||
2. Connect: **NanoVNA CH0** → DC blocker → HMC472A (set to 0 dB) → cable → **NanoVNA CH1**
|
||||
3. Run an S21 sweep from 950 to 1500 MHz using mcnanovna or the NanoVNA touchscreen
|
||||
4. Export as CSV with columns `freq_mhz` and `s21_db` (or `frequency_hz` and `loss_db`)
|
||||
5. Pass to `rf_testbench.py` with `--cal path_loss.csv`
|
||||
</Steps>
|
||||
|
||||
The tool interpolates the measured path loss at each test frequency and subtracts it from AGC
|
||||
readings. Without a calibration file, raw AGC values are still reported — useful for relative
|
||||
measurements but not calibrated to absolute power.
|
||||
|
||||
<Aside type="tip" title="HMC472A insertion loss">
|
||||
The HMC472A adds 1.4-1.9 dB of insertion loss even at 0 dB attenuation setting. The calibration
|
||||
sweep captures this automatically since the signal passes through the attenuator during the S21
|
||||
measurement.
|
||||
</Aside>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **SkyWalker-1** with [custom firmware v3.02+](/firmware/custom-v302/) (for `tune_monitor` command)
|
||||
- **HMC472A** attenuator with ESP32-S2 controller on the network
|
||||
- **NanoVNA-H** (manual mode works with any VNA; auto mode requires [mcnanovna](https://git.supported.systems/rf/mcnanovna))
|
||||
- **Python 3.10+** with `pyusb` installed
|
||||
- **DC blocker** (SMA inline)
|
||||
- **SMA-to-F adapter**
|
||||
|
||||
## Test Descriptions
|
||||
|
||||
### AGC Power Linearity
|
||||
|
||||
```bash
|
||||
python tools/rf_testbench.py agc-linearity --freq 1200
|
||||
```
|
||||
|
||||
Injects CW at a fixed frequency while sweeping the HMC472A from 0 to 31.5 dB in 0.5 dB steps.
|
||||
At each attenuation level, the SkyWalker-1 reports AGC1, AGC2, and derived power. This maps the
|
||||
**AGC transfer function** — how the receiver's automatic gain control responds to known changes
|
||||
in input power.
|
||||
|
||||
The output shows whether the AGC is linear, where it saturates, and its effective dynamic range.
|
||||
With 64 measurement points across 31.5 dB, the resolution is high enough to reveal nonlinearities
|
||||
in the BCM3440 tuner's gain control loop.
|
||||
|
||||
### IF Band Flatness
|
||||
|
||||
```bash
|
||||
python tools/rf_testbench.py band-flatness --start 950 --stop 1500 --step 10
|
||||
```
|
||||
|
||||
Sweeps the NanoVNA CW frequency across the IF band while keeping the HMC472A at a fixed
|
||||
attenuation (10 dB default). At each frequency, the SkyWalker-1 tunes and reads AGC power.
|
||||
|
||||
The result reveals:
|
||||
- **Tuner gain slope**: the BCM3440 may have more gain at some frequencies than others
|
||||
- **Passband ripple**: resonances or nulls in the IF filter chain
|
||||
- **Cable/path frequency response**: if a calibration file is loaded, this is subtracted out
|
||||
|
||||
### Frequency Accuracy
|
||||
|
||||
```bash
|
||||
python tools/rf_testbench.py freq-accuracy --freqs 1000,1100,1200,1300,1400
|
||||
```
|
||||
|
||||
At each test frequency, the NanoVNA injects CW while the SkyWalker-1 runs a narrow spectrum
|
||||
sweep (+/- 5 MHz) around the expected frequency. The detected power peak is compared against the
|
||||
injected frequency.
|
||||
|
||||
This characterizes the **BCM3440 tuner's frequency accuracy** — how much the actual tuned
|
||||
frequency differs from the commanded frequency. The error may be systematic (constant offset)
|
||||
or frequency-dependent.
|
||||
|
||||
### Minimum Detectable Signal
|
||||
|
||||
```bash
|
||||
python tools/rf_testbench.py mds --freq 1200
|
||||
```
|
||||
|
||||
First measures the noise floor with maximum attenuation (31.5 dB). Then injects CW and steps
|
||||
the HMC472A from 0 dB upward in 1 dB increments until the signal drops below 3-sigma above
|
||||
the noise floor.
|
||||
|
||||
The attenuation level where the signal disappears, combined with the NanoVNA output power
|
||||
(~-15 dBm), gives an approximate **minimum detectable signal level** in dBm.
|
||||
|
||||
### BPSK Mode 9 CW Probe
|
||||
|
||||
```bash
|
||||
python tools/rf_testbench.py bpsk-probe --freq 1200
|
||||
```
|
||||
|
||||
An exploratory test that tunes the SkyWalker-1 in **BPSK mode (index 9)** — the same Viterbi
|
||||
rate 1/2 K=7 inner FEC used by GOES LRIT. A CW carrier has no modulation, so the demodulator
|
||||
shouldn't acquire lock, but the AGC and carrier recovery behavior is informative.
|
||||
|
||||
Tests several symbol rates (293,883 sps matching LRIT, plus 500K, 1M, and 5M) and compares
|
||||
against QPSK mode 0 at the same frequency. This establishes a baseline for what mode 9 reports
|
||||
with an unmodulated carrier — useful context for future modulated-signal experiments with a
|
||||
bladeRF.
|
||||
|
||||
## Options
|
||||
|
||||
| Flag | Default | Description |
|
||||
|------|---------|-------------|
|
||||
| `--attenuator` | `http://attenuator.local` | HMC472A REST API base URL |
|
||||
| `--nanovna` | `auto` | NanoVNA control: `auto` (mcnanovna) or `manual` (prompted) |
|
||||
| `--cal` | — | Path loss calibration CSV file |
|
||||
| `--settle` | 200 | Settle time in ms after changing attenuation |
|
||||
| `--output` / `-o` | — | CSV output file |
|
||||
| `--verbose` / `-v` | — | Show raw USB traffic |
|
||||
|
||||
### Per-Test Options
|
||||
|
||||
| Test | Flag | Default | Description |
|
||||
|------|------|---------|-------------|
|
||||
| `agc-linearity` | `--freq` | 1200 | Test frequency in MHz |
|
||||
| `band-flatness` | `--start` | 950 | Start frequency in MHz |
|
||||
| `band-flatness` | `--stop` | 1500 | Stop frequency in MHz |
|
||||
| `band-flatness` | `--step` | 10 | Frequency step in MHz |
|
||||
| `freq-accuracy` | `--freqs` | 1000,1100,1200,1300,1400 | Comma-separated test frequencies |
|
||||
| `mds` | `--freq` | 1200 | Test frequency in MHz |
|
||||
| `bpsk-probe` | `--freq` | 1200 | Test frequency in MHz |
|
||||
|
||||
## CSV Output Format
|
||||
|
||||
All tests write the same CSV format when `--output` is specified:
|
||||
|
||||
| Column | Description |
|
||||
|--------|-------------|
|
||||
| `timestamp` | ISO 8601 UTC timestamp |
|
||||
| `test_name` | Test identifier (agc_linearity, band_flatness, freq_accuracy, mds, bpsk_probe) |
|
||||
| `freq_mhz` | Frequency in MHz |
|
||||
| `atten_db` | HMC472A attenuation setting in dB |
|
||||
| `agc1` | BCM3440 AGC1 register value |
|
||||
| `agc2` | BCM3440 AGC2 register value |
|
||||
| `power_db` | Derived power estimate in dB (relative) |
|
||||
| `snr_raw` | Raw SNR register value |
|
||||
| `snr_db` | SNR in dB |
|
||||
| `locked` | Demodulator lock status |
|
||||
| `lock_raw` | Raw lock status byte |
|
||||
| `status` | Status byte |
|
||||
| `notes` | Test-specific metadata |
|
||||
|
||||
## Interpreting Results
|
||||
|
||||
### AGC Linearity Curves
|
||||
|
||||
A well-behaved AGC should show a roughly linear relationship between attenuation (dB) and AGC
|
||||
register value. Look for:
|
||||
|
||||
- **Linear region**: Where AGC tracks input power changes 1:1 in dB — this is the useful
|
||||
measurement range
|
||||
- **Saturation**: Where adding more signal doesn't change AGC — the tuner's front end is
|
||||
compressing
|
||||
- **Noise floor**: Where reducing signal doesn't change AGC — the receiver's internal noise
|
||||
dominates
|
||||
|
||||
### Band Flatness
|
||||
|
||||
Ideal response is flat across the band. In practice:
|
||||
- **1-3 dB variation** across 950-1500 MHz is typical for a consumer-grade tuner
|
||||
- **Sharp dips** may indicate cable resonances or connector issues
|
||||
- **Systematic slope** (gain increasing or decreasing with frequency) is common and can be
|
||||
corrected in post-processing
|
||||
|
||||
### Frequency Error
|
||||
|
||||
Consumer satellite tuners typically have **50-200 kHz frequency accuracy**. A consistent offset
|
||||
suggests LO error in the BCM3440. Frequency-dependent error suggests tuning nonlinearity.
|
||||
|
||||
## Mock Mode
|
||||
|
||||
Run with `SKYWALKER_MOCK=1` for testing without hardware:
|
||||
|
||||
```bash
|
||||
SKYWALKER_MOCK=1 python tools/rf_testbench.py agc-linearity --freq 1200 --nanovna manual
|
||||
```
|
||||
|
||||
Mock mode uses built-in simulated responses for the SkyWalker-1 and HMC472A. The NanoVNA prompts
|
||||
are skipped. Useful for verifying command structure and CSV output format.
|
||||
|
||||
## See Also
|
||||
|
||||
- [Spectrum Analysis](/tools/spectrum-analysis/) — frequency sweep techniques
|
||||
- [Hydrogen 21 cm](/tools/h21cm/) — direct L-band input mode (same RF path concept)
|
||||
- [Signal Monitoring](/bcm4500/signal-monitoring/) — AGC and SNR register details
|
||||
- [HMC472A Documentation](https://hmc472.l.zmesh.systems/) — attenuator module reference
|
||||
- [Applications & Use Cases](/guides/applications/) — RF test and measurement context
|
||||
|
|
|
|||
|
|
@ -1,202 +1,202 @@
|
|||
---
|
||||
title: Safety Testing
|
||||
description: Hamilton adversarial test suite for firmware safety validation — operator error, invalid inputs, state machine violations, and rapid-fire stress.
|
||||
---
|
||||
|
||||
import { Badge, Aside, Steps, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
The Hamilton adversarial test suite (`test_hamilton.py`) validates that the firmware survives every kind of wrong input an operator or buggy host software could send. Named after Margaret Hamilton's methodology at MIT Instrumentation Laboratory — she tested "what if the astronaut pushes the wrong button?" and found bugs that saved Apollo missions. <Badge text="v3.05" variant="success" />
|
||||
|
||||
```bash
|
||||
sudo python3 tools/test_hamilton.py
|
||||
```
|
||||
|
||||
<Aside type="note">
|
||||
Requires `pyusb`, root access, and firmware v3.05.0+ loaded. The device must have the BCM4500 booted before running. The test handles boot/shutdown sequences internally.
|
||||
</Aside>
|
||||
|
||||
## Test Philosophy
|
||||
|
||||
Traditional hardware testing validates the happy path — correct inputs producing correct outputs. Adversarial testing validates the *unhappy* path: what happens when the host sends garbage? The firmware must never:
|
||||
|
||||
1. **Hang** — every code path must terminate in bounded time
|
||||
2. **Corrupt state** — invalid inputs must not leave the device in an inconsistent state
|
||||
3. **Fire the watchdog** — the main loop must stay responsive
|
||||
4. **Become unresponsive** — the device must accept new commands after any error
|
||||
|
||||
Each test follows the same pattern:
|
||||
|
||||
<Steps>
|
||||
1. Read `last_error` before the operation
|
||||
2. Send the invalid/abusive command
|
||||
3. Verify the device is still alive (`GET_FW_VERS` returns `3.05.0`)
|
||||
4. Read `last_error` after — check it matches expectations
|
||||
</Steps>
|
||||
|
||||
## Test Categories
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="DiSEqC">
|
||||
|
||||
### Category 1: DiSEqC Message Abuse
|
||||
|
||||
Tests the DiSEqC messaging path with invalid parameters. DiSEqC uses Timer2-based Manchester encoding on the 22 kHz carrier — a stuck timer would hang the firmware without the v3.05.0 `diseqc_wait_ticks()` timeout fix.
|
||||
|
||||
| Test | Input | Expected |
|
||||
|------|-------|----------|
|
||||
| 1a. Tone burst B | `wValue=1` | `ERR_NOT_SUPPORTED` (0x0A) |
|
||||
| 1b. Tone burst 0xFF | `wValue=0xFF` | `ERR_NOT_SUPPORTED` (0x0A) |
|
||||
| 1c. Too short | 2-byte payload | No hang |
|
||||
| 1d. Too long | 8-byte payload | No hang |
|
||||
| 1e. Empty | 0-byte payload | No hang |
|
||||
| 1f. Recovery | Valid 4-byte message | No hang, correct execution |
|
||||
| 1g. Motor halt | DiSEqC 1.2 `E0 31 60` (no motor connected) | No hang |
|
||||
| 1h. Drive 255 steps | DiSEqC 1.2 `E0 31 68 FF` | No hang |
|
||||
| 1i. Bogus USALS | DiSEqC 1.2 `E0 31 6E FF FF` | No hang |
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Tune">
|
||||
|
||||
### Category 2: Tune Parameter Abuse
|
||||
|
||||
Sends tuning commands with out-of-range parameters. The BCM4500 demodulator receives these values directly over I2C — the firmware must not hang regardless of what the host sends.
|
||||
|
||||
| Test | Input | Expected |
|
||||
|------|-------|----------|
|
||||
| 2a. Zero symbol rate | `SR=0` | No hang |
|
||||
| 2b. Max symbol rate | `SR=0xFFFFFFFF` | No hang |
|
||||
| 2c. Zero frequency | `freq=0` | No hang |
|
||||
| 2d. Max frequency | `freq=0xFFFFFFFF` | No hang |
|
||||
| 2e. Invalid modulation | `mod=0xFF` | No hang |
|
||||
| 2f. Invalid FEC | `fec=0xFF` | No hang |
|
||||
| 2g. Truncated (4 bytes) | 4 of 10 bytes sent | `ERR_EP0_TIMEOUT` (0x07) |
|
||||
| 2h. Single byte | 1 of 10 bytes sent | `ERR_EP0_TIMEOUT` (0x07) |
|
||||
| 2i. All zeros | 10 zero bytes | No hang |
|
||||
| 2j. All 0xFF | 10 × `0xFF` | No hang |
|
||||
|
||||
Tests 2g and 2h specifically validate the `ep0_wait_data()` + `EP0BCL` payload length check. The firmware waits for the EP0 data phase, but when the host sends fewer bytes than expected, the timeout fires and the command is safely aborted.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="I2C">
|
||||
|
||||
### Category 3: I2C Address Space Abuse
|
||||
|
||||
Sends I2C reads and writes to non-existent or invalid addresses. Before v3.05.0, the bus scan command (`0xB4`) used bare `while (!(I2CS & bmDONE))` loops that would hang if any address didn't ACK.
|
||||
|
||||
| Test | Input | Expected |
|
||||
|------|-------|----------|
|
||||
| 3a. Addr 0x7F | Non-existent I2C device | `ERR_I2C_NAK` (0x02) |
|
||||
| 3b. Addr 0x00 | General call address | No hang |
|
||||
| 3c. Page 0xFF | BCM4500 indirect read, invalid page | No hang |
|
||||
| 3d. Count=0 | Multi-reg read with zero count | No hang |
|
||||
| 3e. Count=255 | Multi-reg read exceeding 64 max | No hang (clamped) |
|
||||
| 3f. Write to 0x7F | Raw I2C write to non-existent device | No hang |
|
||||
| 3g. Reserved reg | BCM4500 direct read of reg 0xFF | No hang |
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="State Machine">
|
||||
|
||||
### Category 4: State Machine Violations
|
||||
|
||||
Tests operations in the wrong order — the classic "what if the astronaut pushes the wrong button?" scenario.
|
||||
|
||||
| Test | Scenario | Expected |
|
||||
|------|----------|----------|
|
||||
| 4a. Double boot | `BOOT_8PSK(1)` when already booted | No hang |
|
||||
| 4b. Tune with BCM off | Power off BCM4500, then tune | `ERR_BCM_NOT_READY` (0x04) |
|
||||
| 4c. Signal read, BCM off | Signal monitor with demod powered down | No hang |
|
||||
| 4d. Bus scan, BCM off | I2C bus scan with demod powered down | No hang |
|
||||
| 4e. Hotplug, BCM off | Force hotplug rescan with demod off | No hang |
|
||||
| 4f. Recovery | Re-boot BCM4500 after power cycle | `STARTED \| FW_LOADED` |
|
||||
| 4g. Arm + disarm | Arm streaming then immediately disarm | No hang |
|
||||
| 4h. Disarm, not armed | Disarm when already disarmed | No hang |
|
||||
| 4i. Rapid boot toggle | Off → on → off → on in quick succession | No hang |
|
||||
|
||||
Test 4b is the most important: it proves the `do_tune()` early guard works. Without the v3.05.0 fix, tuning with a powered-off BCM4500 would attempt I2C writes to a device that isn't there, potentially hanging on the bus.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Boundaries">
|
||||
|
||||
### Category 5: Boundary and Buffer Abuse
|
||||
|
||||
Tests USB transfer size mismatches — requesting more or fewer bytes than the firmware produces.
|
||||
|
||||
| Test | Scenario | Expected |
|
||||
|------|----------|----------|
|
||||
| 5a. 0 bytes from GET_CONFIG | Request 0 bytes (firmware sends 1) | No hang |
|
||||
| 5b. 64 bytes from GET_CONFIG | Request 64 bytes (firmware sends 1) | No hang |
|
||||
| 5c. 64 bytes from GET_LAST_ERROR | Request 64 bytes (firmware sends 1) | No hang |
|
||||
| 5d. 1 byte from GET_FW_VERS | Request 1 byte (firmware sends 6) | No hang |
|
||||
| 5e. 1 byte from GET_STREAM_DIAG | Request 1 byte (firmware sends 12) | No hang |
|
||||
| 5f. STREAM_DIAG wval=0xFFFF | Non-standard wValue | No hang |
|
||||
| 5g. HOTPLUG wval=0xFFFF | Non-standard wValue | No hang |
|
||||
|
||||
The USB layer handles size mismatches — the firmware writes its full response to EP0BUF regardless of how many bytes the host requested. The host-side libusb may return an `Overflow` error, but the device remains stable.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Stress">
|
||||
|
||||
### Category 6: Rapid-Fire Stress
|
||||
|
||||
Sends many commands in quick succession to test for timing-dependent failures.
|
||||
|
||||
| Test | Operations | Typical Time |
|
||||
|------|-----------|--------------|
|
||||
| 6a. Config reads | 200 × `GET_CONFIG` | ~9 ms |
|
||||
| 6b. Error reads | 50 × `GET_LAST_ERROR` | ~2 ms |
|
||||
| 6c. Signal monitors | 30 × `SIGNAL_MONITOR` | ~276 ms |
|
||||
| 6d. Voltage toggles | 40 × `SET_LNB_VOLTAGE` | ~2 ms |
|
||||
| 6e. DiSEqC messages | 10 × 4-byte DiSEqC switch | ~1,473 ms |
|
||||
|
||||
Single-byte vendor commands (config, error) complete in under 0.05 ms. Signal monitors are slower (~9 ms each) because they perform multiple I2C reads. DiSEqC messages are the slowest due to Timer2-based bit-bang encoding.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Invalid Cmds">
|
||||
|
||||
### Category 7: Invalid Vendor Commands
|
||||
|
||||
Sends vendor command codes that don't exist in the firmware.
|
||||
|
||||
| Test | Command | Expected |
|
||||
|------|---------|----------|
|
||||
| 7a-f. Unknown codes | `0xFF`, `0x01`, `0x50`, `0xFE`, `0x00`, `0x79` | USB STALL |
|
||||
| 7g. Wrong direction | `GET_CONFIG` as OUT | No hang |
|
||||
| 7h. Oversized payload | 64-byte payload to `GET_CONFIG` | No hang |
|
||||
|
||||
Unknown commands correctly return a USB STALL (the EP0 error response per USB spec). The firmware's `handle_vendorcommand()` returns FALSE for unrecognized request codes, which causes fx2lib to send the STALL handshake.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Results
|
||||
|
||||
The full test suite runs in under 30 seconds on hardware:
|
||||
|
||||
```
|
||||
================================================================
|
||||
HAMILTON ADVERSARIAL TEST — FINAL RESULTS
|
||||
-----------------------------------------
|
||||
Tests passed: 55
|
||||
Tests failed: 0
|
||||
Device alive: True
|
||||
Final error: 0x08 [GPIF_TIMEOUT]
|
||||
Watchdog fired: No
|
||||
Verdict: PASS
|
||||
================================================================
|
||||
```
|
||||
|
||||
<Aside type="tip">
|
||||
The final `GPIF_TIMEOUT` error is expected — it comes from the arm/disarm toggle test (4g). The GPIF state machine hadn't fully started before it was told to stop. The error code correctly reports what happened, and the device remains fully operational.
|
||||
</Aside>
|
||||
|
||||
## Running the Tests
|
||||
|
||||
The test can be run after any firmware change to verify safety properties are preserved:
|
||||
|
||||
```bash
|
||||
# Load firmware and run tests
|
||||
sudo python3 tools/fw_load.py load firmware/build/skywalker1.ihx --wait 5
|
||||
sudo python3 tools/test_hamilton.py
|
||||
```
|
||||
|
||||
The test automatically boots the BCM4500, runs all 55 tests, and powers everything down. If any test fails, the device state and error code are reported. If the device becomes unresponsive, the test halts immediately and reports which operation killed it.
|
||||
---
|
||||
title: Safety Testing
|
||||
description: Hamilton adversarial test suite for firmware safety validation — operator error, invalid inputs, state machine violations, and rapid-fire stress.
|
||||
---
|
||||
|
||||
import { Badge, Aside, Steps, Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
The Hamilton adversarial test suite (`test_hamilton.py`) validates that the firmware survives every kind of wrong input an operator or buggy host software could send. Named after Margaret Hamilton's methodology at MIT Instrumentation Laboratory — she tested "what if the astronaut pushes the wrong button?" and found bugs that saved Apollo missions. <Badge text="v3.05" variant="success" />
|
||||
|
||||
```bash
|
||||
sudo python3 tools/test_hamilton.py
|
||||
```
|
||||
|
||||
<Aside type="note">
|
||||
Requires `pyusb`, root access, and firmware v3.05.0+ loaded. The device must have the BCM4500 booted before running. The test handles boot/shutdown sequences internally.
|
||||
</Aside>
|
||||
|
||||
## Test Philosophy
|
||||
|
||||
Traditional hardware testing validates the happy path — correct inputs producing correct outputs. Adversarial testing validates the *unhappy* path: what happens when the host sends garbage? The firmware must never:
|
||||
|
||||
1. **Hang** — every code path must terminate in bounded time
|
||||
2. **Corrupt state** — invalid inputs must not leave the device in an inconsistent state
|
||||
3. **Fire the watchdog** — the main loop must stay responsive
|
||||
4. **Become unresponsive** — the device must accept new commands after any error
|
||||
|
||||
Each test follows the same pattern:
|
||||
|
||||
<Steps>
|
||||
1. Read `last_error` before the operation
|
||||
2. Send the invalid/abusive command
|
||||
3. Verify the device is still alive (`GET_FW_VERS` returns `3.05.0`)
|
||||
4. Read `last_error` after — check it matches expectations
|
||||
</Steps>
|
||||
|
||||
## Test Categories
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="DiSEqC">
|
||||
|
||||
### Category 1: DiSEqC Message Abuse
|
||||
|
||||
Tests the DiSEqC messaging path with invalid parameters. DiSEqC uses Timer2-based Manchester encoding on the 22 kHz carrier — a stuck timer would hang the firmware without the v3.05.0 `diseqc_wait_ticks()` timeout fix.
|
||||
|
||||
| Test | Input | Expected |
|
||||
|------|-------|----------|
|
||||
| 1a. Tone burst B | `wValue=1` | `ERR_NOT_SUPPORTED` (0x0A) |
|
||||
| 1b. Tone burst 0xFF | `wValue=0xFF` | `ERR_NOT_SUPPORTED` (0x0A) |
|
||||
| 1c. Too short | 2-byte payload | No hang |
|
||||
| 1d. Too long | 8-byte payload | No hang |
|
||||
| 1e. Empty | 0-byte payload | No hang |
|
||||
| 1f. Recovery | Valid 4-byte message | No hang, correct execution |
|
||||
| 1g. Motor halt | DiSEqC 1.2 `E0 31 60` (no motor connected) | No hang |
|
||||
| 1h. Drive 255 steps | DiSEqC 1.2 `E0 31 68 FF` | No hang |
|
||||
| 1i. Bogus USALS | DiSEqC 1.2 `E0 31 6E FF FF` | No hang |
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Tune">
|
||||
|
||||
### Category 2: Tune Parameter Abuse
|
||||
|
||||
Sends tuning commands with out-of-range parameters. The BCM4500 demodulator receives these values directly over I2C — the firmware must not hang regardless of what the host sends.
|
||||
|
||||
| Test | Input | Expected |
|
||||
|------|-------|----------|
|
||||
| 2a. Zero symbol rate | `SR=0` | No hang |
|
||||
| 2b. Max symbol rate | `SR=0xFFFFFFFF` | No hang |
|
||||
| 2c. Zero frequency | `freq=0` | No hang |
|
||||
| 2d. Max frequency | `freq=0xFFFFFFFF` | No hang |
|
||||
| 2e. Invalid modulation | `mod=0xFF` | No hang |
|
||||
| 2f. Invalid FEC | `fec=0xFF` | No hang |
|
||||
| 2g. Truncated (4 bytes) | 4 of 10 bytes sent | `ERR_EP0_TIMEOUT` (0x07) |
|
||||
| 2h. Single byte | 1 of 10 bytes sent | `ERR_EP0_TIMEOUT` (0x07) |
|
||||
| 2i. All zeros | 10 zero bytes | No hang |
|
||||
| 2j. All 0xFF | 10 × `0xFF` | No hang |
|
||||
|
||||
Tests 2g and 2h specifically validate the `ep0_wait_data()` + `EP0BCL` payload length check. The firmware waits for the EP0 data phase, but when the host sends fewer bytes than expected, the timeout fires and the command is safely aborted.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="I2C">
|
||||
|
||||
### Category 3: I2C Address Space Abuse
|
||||
|
||||
Sends I2C reads and writes to non-existent or invalid addresses. Before v3.05.0, the bus scan command (`0xB4`) used bare `while (!(I2CS & bmDONE))` loops that would hang if any address didn't ACK.
|
||||
|
||||
| Test | Input | Expected |
|
||||
|------|-------|----------|
|
||||
| 3a. Addr 0x7F | Non-existent I2C device | `ERR_I2C_NAK` (0x02) |
|
||||
| 3b. Addr 0x00 | General call address | No hang |
|
||||
| 3c. Page 0xFF | BCM4500 indirect read, invalid page | No hang |
|
||||
| 3d. Count=0 | Multi-reg read with zero count | No hang |
|
||||
| 3e. Count=255 | Multi-reg read exceeding 64 max | No hang (clamped) |
|
||||
| 3f. Write to 0x7F | Raw I2C write to non-existent device | No hang |
|
||||
| 3g. Reserved reg | BCM4500 direct read of reg 0xFF | No hang |
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="State Machine">
|
||||
|
||||
### Category 4: State Machine Violations
|
||||
|
||||
Tests operations in the wrong order — the classic "what if the astronaut pushes the wrong button?" scenario.
|
||||
|
||||
| Test | Scenario | Expected |
|
||||
|------|----------|----------|
|
||||
| 4a. Double boot | `BOOT_8PSK(1)` when already booted | No hang |
|
||||
| 4b. Tune with BCM off | Power off BCM4500, then tune | `ERR_BCM_NOT_READY` (0x04) |
|
||||
| 4c. Signal read, BCM off | Signal monitor with demod powered down | No hang |
|
||||
| 4d. Bus scan, BCM off | I2C bus scan with demod powered down | No hang |
|
||||
| 4e. Hotplug, BCM off | Force hotplug rescan with demod off | No hang |
|
||||
| 4f. Recovery | Re-boot BCM4500 after power cycle | `STARTED \| FW_LOADED` |
|
||||
| 4g. Arm + disarm | Arm streaming then immediately disarm | No hang |
|
||||
| 4h. Disarm, not armed | Disarm when already disarmed | No hang |
|
||||
| 4i. Rapid boot toggle | Off → on → off → on in quick succession | No hang |
|
||||
|
||||
Test 4b is the most important: it proves the `do_tune()` early guard works. Without the v3.05.0 fix, tuning with a powered-off BCM4500 would attempt I2C writes to a device that isn't there, potentially hanging on the bus.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Boundaries">
|
||||
|
||||
### Category 5: Boundary and Buffer Abuse
|
||||
|
||||
Tests USB transfer size mismatches — requesting more or fewer bytes than the firmware produces.
|
||||
|
||||
| Test | Scenario | Expected |
|
||||
|------|----------|----------|
|
||||
| 5a. 0 bytes from GET_CONFIG | Request 0 bytes (firmware sends 1) | No hang |
|
||||
| 5b. 64 bytes from GET_CONFIG | Request 64 bytes (firmware sends 1) | No hang |
|
||||
| 5c. 64 bytes from GET_LAST_ERROR | Request 64 bytes (firmware sends 1) | No hang |
|
||||
| 5d. 1 byte from GET_FW_VERS | Request 1 byte (firmware sends 6) | No hang |
|
||||
| 5e. 1 byte from GET_STREAM_DIAG | Request 1 byte (firmware sends 12) | No hang |
|
||||
| 5f. STREAM_DIAG wval=0xFFFF | Non-standard wValue | No hang |
|
||||
| 5g. HOTPLUG wval=0xFFFF | Non-standard wValue | No hang |
|
||||
|
||||
The USB layer handles size mismatches — the firmware writes its full response to EP0BUF regardless of how many bytes the host requested. The host-side libusb may return an `Overflow` error, but the device remains stable.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Stress">
|
||||
|
||||
### Category 6: Rapid-Fire Stress
|
||||
|
||||
Sends many commands in quick succession to test for timing-dependent failures.
|
||||
|
||||
| Test | Operations | Typical Time |
|
||||
|------|-----------|--------------|
|
||||
| 6a. Config reads | 200 × `GET_CONFIG` | ~9 ms |
|
||||
| 6b. Error reads | 50 × `GET_LAST_ERROR` | ~2 ms |
|
||||
| 6c. Signal monitors | 30 × `SIGNAL_MONITOR` | ~276 ms |
|
||||
| 6d. Voltage toggles | 40 × `SET_LNB_VOLTAGE` | ~2 ms |
|
||||
| 6e. DiSEqC messages | 10 × 4-byte DiSEqC switch | ~1,473 ms |
|
||||
|
||||
Single-byte vendor commands (config, error) complete in under 0.05 ms. Signal monitors are slower (~9 ms each) because they perform multiple I2C reads. DiSEqC messages are the slowest due to Timer2-based bit-bang encoding.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Invalid Cmds">
|
||||
|
||||
### Category 7: Invalid Vendor Commands
|
||||
|
||||
Sends vendor command codes that don't exist in the firmware.
|
||||
|
||||
| Test | Command | Expected |
|
||||
|------|---------|----------|
|
||||
| 7a-f. Unknown codes | `0xFF`, `0x01`, `0x50`, `0xFE`, `0x00`, `0x79` | USB STALL |
|
||||
| 7g. Wrong direction | `GET_CONFIG` as OUT | No hang |
|
||||
| 7h. Oversized payload | 64-byte payload to `GET_CONFIG` | No hang |
|
||||
|
||||
Unknown commands correctly return a USB STALL (the EP0 error response per USB spec). The firmware's `handle_vendorcommand()` returns FALSE for unrecognized request codes, which causes fx2lib to send the STALL handshake.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Results
|
||||
|
||||
The full test suite runs in under 30 seconds on hardware:
|
||||
|
||||
```
|
||||
================================================================
|
||||
HAMILTON ADVERSARIAL TEST — FINAL RESULTS
|
||||
-----------------------------------------
|
||||
Tests passed: 55
|
||||
Tests failed: 0
|
||||
Device alive: True
|
||||
Final error: 0x08 [GPIF_TIMEOUT]
|
||||
Watchdog fired: No
|
||||
Verdict: PASS
|
||||
================================================================
|
||||
```
|
||||
|
||||
<Aside type="tip">
|
||||
The final `GPIF_TIMEOUT` error is expected — it comes from the arm/disarm toggle test (4g). The GPIF state machine hadn't fully started before it was told to stop. The error code correctly reports what happened, and the device remains fully operational.
|
||||
</Aside>
|
||||
|
||||
## Running the Tests
|
||||
|
||||
The test can be run after any firmware change to verify safety properties are preserved:
|
||||
|
||||
```bash
|
||||
# Load firmware and run tests
|
||||
sudo python3 tools/fw_load.py load firmware/build/skywalker1.ihx --wait 5
|
||||
sudo python3 tools/test_hamilton.py
|
||||
```
|
||||
|
||||
The test automatically boots the BCM4500, runs all 55 tests, and powers everything down. If any test fails, the device state and error code are reported. If the device becomes unresponsive, the test halts immediately and reports which operation killed it.
|
||||
|
|
|
|||
|
|
@ -1,329 +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
|
||||
---
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,212 +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
|
||||
---
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,160 +1,160 @@
|
|||
---
|
||||
title: Carrier Survey
|
||||
description: Automated six-stage carrier detection, cataloging, and differential analysis across the full IF range or QO-100 narrowband transponder.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Steps, Aside, Badge, CardGrid, Card } from '@astrojs/starlight/components';
|
||||
|
||||
Automated carrier survey tool that sweeps the IF range, detects signals above the noise floor, identifies locked transponders with blind scan, and catalogs results with service name extraction from the transport stream. Supports full-band surveys, quick scans, QO-100 narrowband sweeps, and differential analysis between survey snapshots.
|
||||
|
||||
<Badge text="Requires custom firmware v3.03.0+" variant="caution" />
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash title="Full six-stage survey"
|
||||
sudo python3 tools/survey.py full-scan
|
||||
```
|
||||
|
||||
```bash title="Quick sweep (no blind scan)"
|
||||
sudo python3 tools/survey.py quick-scan
|
||||
```
|
||||
|
||||
```bash title="QO-100 narrowband survey"
|
||||
sudo python3 tools/survey.py qo100 --lnb-lo 9750
|
||||
```
|
||||
|
||||
```bash title="Compare two surveys"
|
||||
python3 tools/survey.py diff survey-old.json survey-new.json
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `full-scan` | Complete 6-stage survey with carrier identification |
|
||||
| `quick-scan` | Sweep + peak detection only (stages 1-2) |
|
||||
| `diff FILE1 FILE2` | Compare two survey catalogs |
|
||||
| `export FILE` | Export to CSV, JSON, or text |
|
||||
| `view [FILE]` | Display latest or specified survey |
|
||||
| `qo100 --lnb-lo LO` | QO-100 transponder survey with optimized parameters |
|
||||
|
||||
## Survey Pipeline
|
||||
|
||||
The full survey runs six stages sequentially. Each stage refines the previous stage's results.
|
||||
|
||||
<Steps>
|
||||
1. **Coarse sweep** — full IF range (950-2150 MHz default) at 5 MHz steps. Measures signal power at each frequency using the firmware's spectrum sweep command.
|
||||
|
||||
2. **Peak detection** — adaptive noise floor estimation using median + MAD (Median Absolute Deviation), followed by local maxima finding with -3 dB bandwidth estimation and peak merging.
|
||||
|
||||
3. **Fine sweep** — +/-10 MHz around each detected peak at 1 MHz steps. Refines center frequencies from the coarse sweep.
|
||||
|
||||
4. **Blind scan** — at each refined peak, tries symbol rates from 1-30 Msps in 1 Msps steps using the firmware's adaptive blind scan (AGC pre-check skips empty frequencies). Reports lock status with frequency and symbol rate.
|
||||
|
||||
5. **TS sample** — for locked carriers, captures 3 seconds of transport stream data and parses PAT, PMT, and SDT tables to extract program numbers, stream types, service names, and provider names.
|
||||
|
||||
6. **Catalog assembly** — aggregates all results into a `CarrierCatalog` with full metadata, saves to `~/.skywalker1/surveys/`.
|
||||
</Steps>
|
||||
|
||||
### Full Scan Options
|
||||
|
||||
| Flag | Default | Description |
|
||||
|------|---------|-------------|
|
||||
| `--start` | 950 | Start frequency (MHz) |
|
||||
| `--stop` | 2150 | Stop frequency (MHz) |
|
||||
| `--coarse-step` | 5.0 | Coarse sweep step (MHz) |
|
||||
| `--fine-step` | 1.0 | Fine sweep step (MHz) |
|
||||
| `--sr-min` | 1,000,000 | Min symbol rate (sps) |
|
||||
| `--sr-max` | 30,000,000 | Max symbol rate (sps) |
|
||||
| `--sr-step` | 1,000,000 | Symbol rate step (sps) |
|
||||
| `--pol` | — | Polarization label (H/V/L/R) |
|
||||
| `--band` | — | Band label (low/high) |
|
||||
| `--output` | auto | Output filename |
|
||||
|
||||
## Carrier Catalog
|
||||
|
||||
Survey results are stored as JSON in `~/.skywalker1/surveys/` with auto-generated filenames:
|
||||
|
||||
```
|
||||
~/.skywalker1/surveys/
|
||||
survey-2026-02-15-low-V.json
|
||||
survey-2026-02-16-high-H.json
|
||||
survey-2026-02-16-qo100-9750.json
|
||||
```
|
||||
|
||||
Each carrier entry records:
|
||||
- Frequency, symbol rate, modulation, FEC
|
||||
- Signal power, SNR, lock status
|
||||
- Detected services (names, types, PIDs)
|
||||
- Estimated bandwidth and carrier classification
|
||||
- First/last seen timestamps, scan count
|
||||
|
||||
### Differential Analysis
|
||||
|
||||
Compare two survey snapshots to detect changes over time:
|
||||
|
||||
```bash
|
||||
python3 tools/survey.py diff old-survey.json new-survey.json
|
||||
```
|
||||
|
||||
Output categorizes carriers as:
|
||||
|
||||
| Category | Description |
|
||||
|----------|-------------|
|
||||
| **NEW** | Carrier present in new survey but not old |
|
||||
| **MISSING** | Carrier present in old survey but not new |
|
||||
| **CHANGED** | Carrier exists in both but with differences (lock state, power >2 dB, modulation, services) |
|
||||
| **STABLE** | Carrier unchanged between surveys |
|
||||
|
||||
<Aside type="tip">
|
||||
Run surveys at the same time of day and with the same LNB configuration for meaningful differential analysis. Sun transit, rain fade, and LNB drift all affect signal levels.
|
||||
</Aside>
|
||||
|
||||
## QO-100 Mode
|
||||
|
||||
The `qo100` subcommand uses parameters optimized for the Es'hail-2 wideband transponder (10491-10499 MHz):
|
||||
|
||||
- **Step sizes**: 0.5 MHz coarse, 0.1 MHz fine (vs 5/1 MHz for broadcast)
|
||||
- **Symbol rate range**: 256 ksps - 2 Msps (vs 1-30 Msps for broadcast)
|
||||
- **Detection threshold**: 3.0 dB above noise floor (more sensitive)
|
||||
- **IF range**: auto-calculated from `--lnb-lo` parameter
|
||||
|
||||
```bash title="QO-100 with universal LNB low band"
|
||||
sudo python3 tools/survey.py qo100 --lnb-lo 9750
|
||||
# Scans IF: 741-749 MHz
|
||||
```
|
||||
|
||||
```bash title="QO-100 with modified LNB (9361 MHz LO)"
|
||||
sudo python3 tools/survey.py qo100 --lnb-lo 9361
|
||||
# Scans IF: 1130-1138 MHz
|
||||
```
|
||||
|
||||
<Aside type="caution">
|
||||
The BCM4500 demodulator has a minimum symbol rate of 256 ksps. QO-100 DATV stations transmitting below this rate will appear as detected energy in the sweep but cannot achieve signal lock. The survey distinguishes "carrier detected" from "carrier locked" in its output.
|
||||
</Aside>
|
||||
|
||||
## Signal Analysis
|
||||
|
||||
The survey engine uses enhanced signal analysis beyond the basic peak detection in `skywalker_lib`:
|
||||
|
||||
- **Adaptive noise floor** — median + MAD robust estimator, resistant to strong carriers biasing the baseline
|
||||
- **Bandwidth estimation** — walks from peak until -3 dB crossing on each side, with inter-bin interpolation
|
||||
- **Carrier classification** — heuristic mapping from estimated bandwidth to likely symbol rate range and modulation type
|
||||
- **Peak merging** — overlapping detections within one bandwidth are consolidated into a single carrier
|
||||
|
||||
## Export Formats
|
||||
|
||||
```bash title="Export to CSV"
|
||||
python3 tools/survey.py export survey.json --format csv --output carriers.csv
|
||||
```
|
||||
|
||||
```bash title="Export to formatted text"
|
||||
python3 tools/survey.py export survey.json --format text
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [SkyWalker TUI — Survey Screen](/tools/tui/#survey--qo-100) — graphical survey with spectrum plot and carrier table
|
||||
- [QO-100 DATV Reception](/guides/qo100-datv/) — complete guide to receiving amateur television via Es'hail-2
|
||||
- [Spectrum Analysis](/tools/spectrum-analysis/) — manual spectrum sweep techniques
|
||||
- [TS Analyzer](/tools/ts-analyzer/) — standalone transport stream analysis
|
||||
---
|
||||
title: Carrier Survey
|
||||
description: Automated six-stage carrier detection, cataloging, and differential analysis across the full IF range or QO-100 narrowband transponder.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Steps, Aside, Badge, CardGrid, Card } from '@astrojs/starlight/components';
|
||||
|
||||
Automated carrier survey tool that sweeps the IF range, detects signals above the noise floor, identifies locked transponders with blind scan, and catalogs results with service name extraction from the transport stream. Supports full-band surveys, quick scans, QO-100 narrowband sweeps, and differential analysis between survey snapshots.
|
||||
|
||||
<Badge text="Requires custom firmware v3.03.0+" variant="caution" />
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash title="Full six-stage survey"
|
||||
sudo python3 tools/survey.py full-scan
|
||||
```
|
||||
|
||||
```bash title="Quick sweep (no blind scan)"
|
||||
sudo python3 tools/survey.py quick-scan
|
||||
```
|
||||
|
||||
```bash title="QO-100 narrowband survey"
|
||||
sudo python3 tools/survey.py qo100 --lnb-lo 9750
|
||||
```
|
||||
|
||||
```bash title="Compare two surveys"
|
||||
python3 tools/survey.py diff survey-old.json survey-new.json
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `full-scan` | Complete 6-stage survey with carrier identification |
|
||||
| `quick-scan` | Sweep + peak detection only (stages 1-2) |
|
||||
| `diff FILE1 FILE2` | Compare two survey catalogs |
|
||||
| `export FILE` | Export to CSV, JSON, or text |
|
||||
| `view [FILE]` | Display latest or specified survey |
|
||||
| `qo100 --lnb-lo LO` | QO-100 transponder survey with optimized parameters |
|
||||
|
||||
## Survey Pipeline
|
||||
|
||||
The full survey runs six stages sequentially. Each stage refines the previous stage's results.
|
||||
|
||||
<Steps>
|
||||
1. **Coarse sweep** — full IF range (950-2150 MHz default) at 5 MHz steps. Measures signal power at each frequency using the firmware's spectrum sweep command.
|
||||
|
||||
2. **Peak detection** — adaptive noise floor estimation using median + MAD (Median Absolute Deviation), followed by local maxima finding with -3 dB bandwidth estimation and peak merging.
|
||||
|
||||
3. **Fine sweep** — +/-10 MHz around each detected peak at 1 MHz steps. Refines center frequencies from the coarse sweep.
|
||||
|
||||
4. **Blind scan** — at each refined peak, tries symbol rates from 1-30 Msps in 1 Msps steps using the firmware's adaptive blind scan (AGC pre-check skips empty frequencies). Reports lock status with frequency and symbol rate.
|
||||
|
||||
5. **TS sample** — for locked carriers, captures 3 seconds of transport stream data and parses PAT, PMT, and SDT tables to extract program numbers, stream types, service names, and provider names.
|
||||
|
||||
6. **Catalog assembly** — aggregates all results into a `CarrierCatalog` with full metadata, saves to `~/.skywalker1/surveys/`.
|
||||
</Steps>
|
||||
|
||||
### Full Scan Options
|
||||
|
||||
| Flag | Default | Description |
|
||||
|------|---------|-------------|
|
||||
| `--start` | 950 | Start frequency (MHz) |
|
||||
| `--stop` | 2150 | Stop frequency (MHz) |
|
||||
| `--coarse-step` | 5.0 | Coarse sweep step (MHz) |
|
||||
| `--fine-step` | 1.0 | Fine sweep step (MHz) |
|
||||
| `--sr-min` | 1,000,000 | Min symbol rate (sps) |
|
||||
| `--sr-max` | 30,000,000 | Max symbol rate (sps) |
|
||||
| `--sr-step` | 1,000,000 | Symbol rate step (sps) |
|
||||
| `--pol` | — | Polarization label (H/V/L/R) |
|
||||
| `--band` | — | Band label (low/high) |
|
||||
| `--output` | auto | Output filename |
|
||||
|
||||
## Carrier Catalog
|
||||
|
||||
Survey results are stored as JSON in `~/.skywalker1/surveys/` with auto-generated filenames:
|
||||
|
||||
```
|
||||
~/.skywalker1/surveys/
|
||||
survey-2026-02-15-low-V.json
|
||||
survey-2026-02-16-high-H.json
|
||||
survey-2026-02-16-qo100-9750.json
|
||||
```
|
||||
|
||||
Each carrier entry records:
|
||||
- Frequency, symbol rate, modulation, FEC
|
||||
- Signal power, SNR, lock status
|
||||
- Detected services (names, types, PIDs)
|
||||
- Estimated bandwidth and carrier classification
|
||||
- First/last seen timestamps, scan count
|
||||
|
||||
### Differential Analysis
|
||||
|
||||
Compare two survey snapshots to detect changes over time:
|
||||
|
||||
```bash
|
||||
python3 tools/survey.py diff old-survey.json new-survey.json
|
||||
```
|
||||
|
||||
Output categorizes carriers as:
|
||||
|
||||
| Category | Description |
|
||||
|----------|-------------|
|
||||
| **NEW** | Carrier present in new survey but not old |
|
||||
| **MISSING** | Carrier present in old survey but not new |
|
||||
| **CHANGED** | Carrier exists in both but with differences (lock state, power >2 dB, modulation, services) |
|
||||
| **STABLE** | Carrier unchanged between surveys |
|
||||
|
||||
<Aside type="tip">
|
||||
Run surveys at the same time of day and with the same LNB configuration for meaningful differential analysis. Sun transit, rain fade, and LNB drift all affect signal levels.
|
||||
</Aside>
|
||||
|
||||
## QO-100 Mode
|
||||
|
||||
The `qo100` subcommand uses parameters optimized for the Es'hail-2 wideband transponder (10491-10499 MHz):
|
||||
|
||||
- **Step sizes**: 0.5 MHz coarse, 0.1 MHz fine (vs 5/1 MHz for broadcast)
|
||||
- **Symbol rate range**: 256 ksps - 2 Msps (vs 1-30 Msps for broadcast)
|
||||
- **Detection threshold**: 3.0 dB above noise floor (more sensitive)
|
||||
- **IF range**: auto-calculated from `--lnb-lo` parameter
|
||||
|
||||
```bash title="QO-100 with universal LNB low band"
|
||||
sudo python3 tools/survey.py qo100 --lnb-lo 9750
|
||||
# Scans IF: 741-749 MHz
|
||||
```
|
||||
|
||||
```bash title="QO-100 with modified LNB (9361 MHz LO)"
|
||||
sudo python3 tools/survey.py qo100 --lnb-lo 9361
|
||||
# Scans IF: 1130-1138 MHz
|
||||
```
|
||||
|
||||
<Aside type="caution">
|
||||
The BCM4500 demodulator has a minimum symbol rate of 256 ksps. QO-100 DATV stations transmitting below this rate will appear as detected energy in the sweep but cannot achieve signal lock. The survey distinguishes "carrier detected" from "carrier locked" in its output.
|
||||
</Aside>
|
||||
|
||||
## Signal Analysis
|
||||
|
||||
The survey engine uses enhanced signal analysis beyond the basic peak detection in `skywalker_lib`:
|
||||
|
||||
- **Adaptive noise floor** — median + MAD robust estimator, resistant to strong carriers biasing the baseline
|
||||
- **Bandwidth estimation** — walks from peak until -3 dB crossing on each side, with inter-bin interpolation
|
||||
- **Carrier classification** — heuristic mapping from estimated bandwidth to likely symbol rate range and modulation type
|
||||
- **Peak merging** — overlapping detections within one bandwidth are consolidated into a single carrier
|
||||
|
||||
## Export Formats
|
||||
|
||||
```bash title="Export to CSV"
|
||||
python3 tools/survey.py export survey.json --format csv --output carriers.csv
|
||||
```
|
||||
|
||||
```bash title="Export to formatted text"
|
||||
python3 tools/survey.py export survey.json --format text
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [SkyWalker TUI — Survey Screen](/tools/tui/#survey--qo-100) — graphical survey with spectrum plot and carrier table
|
||||
- [QO-100 DATV Reception](/guides/qo100-datv/) — complete guide to receiving amateur television via Es'hail-2
|
||||
- [Spectrum Analysis](/tools/spectrum-analysis/) — manual spectrum sweep techniques
|
||||
- [TS Analyzer](/tools/ts-analyzer/) — standalone transport stream analysis
|
||||
|
|
|
|||
|
|
@ -1,262 +1,262 @@
|
|||
---
|
||||
title: TS Analyzer
|
||||
description: MPEG-2 Transport Stream analyzer for captured satellite data from the SkyWalker-1.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Steps, Aside, Badge } from '@astrojs/starlight/components';
|
||||
|
||||
`ts_analyze.py` parses and analyzes 188-byte MPEG-2 transport stream packets from `.ts` files captured by `tune.py`, piped from stdin, or any standard TS source. It covers PID distribution, PAT/PMT parsing, continuity counter verification, scrambling detection, bitrate estimation, and live stream monitoring.
|
||||
|
||||
Reference: ISO/IEC 13818-1 (MPEG-2 Systems).
|
||||
|
||||
## Subcommands
|
||||
|
||||
| Subcommand | Purpose |
|
||||
|------------|---------|
|
||||
| `analyze` | Full stream analysis with PID table, CC errors, bitrate (default) |
|
||||
| `pids` | Quick PID summary table |
|
||||
| `pat` | Parse and display the Program Association Table |
|
||||
| `pmt` | Parse and display Program Map Tables |
|
||||
| `dump` | Hex dump of individual TS packets |
|
||||
| `monitor` | Live stream monitoring with real-time statistics |
|
||||
|
||||
Passing a filename without a subcommand defaults to `analyze`.
|
||||
|
||||
## Global Options
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `-v, --verbose` | Show sync search details and debug info |
|
||||
|
||||
---
|
||||
|
||||
## analyze -- Full Stream Analysis
|
||||
|
||||
Comprehensive analysis of a TS file or stream: total packets, unique PIDs, TEI errors, scrambling, continuity counter errors, PCR-based bitrate, and a per-PID distribution table.
|
||||
|
||||
```bash
|
||||
python3 tools/ts_analyze.py capture.ts
|
||||
```
|
||||
|
||||
```bash title="Analyze first 10000 packets only"
|
||||
python3 tools/ts_analyze.py analyze capture.ts --max-packets 10000
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `INPUT` | TS file path or `-` for stdin |
|
||||
| `--max-packets N` | Limit analysis to N packets (0 = all) |
|
||||
|
||||
**Example output:**
|
||||
|
||||
```
|
||||
MPEG-2 Transport Stream Analysis
|
||||
============================================================
|
||||
File: capture.ts (52,428,800 bytes)
|
||||
|
||||
Total packets: 278,876
|
||||
Total bytes: 52,428,688
|
||||
Unique PIDs: 12
|
||||
TEI errors: 0
|
||||
Scrambled: 0
|
||||
Duration: 5.23s (from PCR)
|
||||
Bitrate: 80.21 Mbps (PCR-based)
|
||||
|
||||
============================================================
|
||||
PID Distribution
|
||||
============================================================
|
||||
PID Count % CC Err Name
|
||||
--- ----- -- ------ ----
|
||||
0x0000 1,394 0.50% - PAT
|
||||
0x0001 278 0.10% - CAT
|
||||
0x0010 556 0.20% - NIT/ST
|
||||
0x0011 556 0.20% - SDT/BAT/ST
|
||||
0x0100 139,438 50.00% -
|
||||
0x0101 83,663 29.99% -
|
||||
0x0102 5,576 2.00% -
|
||||
0x1FFF 47,415 17.01% - Null
|
||||
```
|
||||
|
||||
### What It Detects
|
||||
|
||||
| Metric | Description |
|
||||
|--------|-------------|
|
||||
| **PID count** | Packet count per Program ID |
|
||||
| **TEI** | Transport Error Indicator flag in the TS header |
|
||||
| **Scrambling** | Transport scrambling control bits (even/odd key) |
|
||||
| **CC errors** | Continuity counter discontinuities per PID |
|
||||
| **Bitrate** | Calculated from PCR timestamps when available |
|
||||
|
||||
---
|
||||
|
||||
## pids -- Quick PID Table
|
||||
|
||||
Faster variant of `analyze` that only counts packets per PID without CC checking or PCR extraction.
|
||||
|
||||
```bash
|
||||
python3 tools/ts_analyze.py pids capture.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## pat -- Program Association Table
|
||||
|
||||
Parses PID 0x0000 to extract the PAT, showing the transport stream ID and which PMT PID corresponds to each program number.
|
||||
|
||||
```bash
|
||||
python3 tools/ts_analyze.py pat capture.ts
|
||||
```
|
||||
|
||||
```
|
||||
Program Association Table (PAT)
|
||||
==================================================
|
||||
Transport Stream ID: 0x0001 (1)
|
||||
Version: 3
|
||||
Programs: 2
|
||||
|
||||
Program PMT PID Note
|
||||
------- ------- ----
|
||||
0 0x0010 NIT
|
||||
1 0x0100
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## pmt -- Program Map Tables
|
||||
|
||||
Parses the PAT first to discover PMT PIDs, then parses each PMT to show the elementary streams (video, audio, data) in each program.
|
||||
|
||||
```bash
|
||||
python3 tools/ts_analyze.py pmt capture.ts
|
||||
```
|
||||
|
||||
```
|
||||
Program Map Tables
|
||||
============================================================
|
||||
Transport Stream ID: 0x0001
|
||||
|
||||
Program 1 (PMT PID 0x0100, version 2)
|
||||
PCR PID: 0x0101
|
||||
Streams:
|
||||
Type PID Description
|
||||
---- --- -----------
|
||||
0x02 0x0101 MPEG-2 Video (13818-2)
|
||||
0x04 0x0102 MPEG-2 Audio (13818-3)
|
||||
0x06 0x0103 PES Private Data
|
||||
```
|
||||
|
||||
### Known Stream Types
|
||||
|
||||
The tool recognizes these standard stream type identifiers:
|
||||
|
||||
| Code | Description |
|
||||
|------|-------------|
|
||||
| 0x01 | MPEG-1 Video |
|
||||
| 0x02 | MPEG-2 Video |
|
||||
| 0x03 | MPEG-1 Audio |
|
||||
| 0x04 | MPEG-2 Audio |
|
||||
| 0x06 | PES Private Data |
|
||||
| 0x0F | MPEG-2 AAC Audio |
|
||||
| 0x1B | H.264/AVC Video |
|
||||
| 0x24 | H.265/HEVC Video |
|
||||
| 0x81 | AC-3 Audio (ATSC) |
|
||||
|
||||
---
|
||||
|
||||
## dump -- Hex Dump Packets
|
||||
|
||||
Print detailed per-packet information including header fields, adaptation field flags, PCR values, and raw hex dump.
|
||||
|
||||
```bash title="Dump first 5 packets"
|
||||
python3 tools/ts_analyze.py dump capture.ts --count 5
|
||||
```
|
||||
|
||||
```bash title="Dump packets for a specific PID"
|
||||
python3 tools/ts_analyze.py dump capture.ts --pid 0x0100 --count 3
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--pid PID` | Filter by PID (hex `0x100` or decimal `256`) |
|
||||
| `--count N` | Maximum packets to dump (default: `10`) |
|
||||
|
||||
**Example output:**
|
||||
|
||||
```
|
||||
Packet #1
|
||||
PID: 0x0000 (PAT)
|
||||
TEI: 0 PUSI: 1 Priority: 0
|
||||
Scrambling: none Adaptation: payload only CC: 5
|
||||
Hex:
|
||||
0000: 47 40 00 15 00 00 B0 0D 00 01 C1 00 00 00 00 E0 G@..............
|
||||
0010: 10 00 01 E1 00 78 A0 B4 FF FF FF FF FF FF FF FF .....x..........
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## monitor -- Live Stream Monitoring
|
||||
|
||||
Real-time monitoring of a TS stream, reporting new PIDs as they appear, continuity errors, TEI errors, and bitrate.
|
||||
|
||||
```bash title="Monitor from a live stream pipe"
|
||||
sudo python3 tools/tune.py stream --stdout | python3 tools/ts_analyze.py monitor -
|
||||
```
|
||||
|
||||
```bash title="Monitor a growing file"
|
||||
python3 tools/ts_analyze.py monitor capture.ts
|
||||
```
|
||||
|
||||
The monitor prints events as they occur (new PIDs, errors) and updates a status line on stderr every second with running statistics.
|
||||
|
||||
```
|
||||
MPEG-2 TS Live Monitor
|
||||
============================================================
|
||||
Ctrl-C to stop
|
||||
|
||||
[ 0.0s] New PID: 0x0000 (PAT)
|
||||
[ 0.0s] New PID: 0x1FFF (Null)
|
||||
[ 0.1s] New PID: 0x0100
|
||||
[ 0.1s] New PID: 0x0101
|
||||
[ 0.2s] New PID: 0x0102
|
||||
278,876 pkts 52,428,688 bytes 80.21 Mbps PIDs: 12 CCerr:0 TEI:0 (5s)
|
||||
|
||||
Monitor Summary
|
||||
========================================
|
||||
Duration: 5.2s
|
||||
Packets: 278,876
|
||||
Bytes: 52,428,688
|
||||
Unique PIDs: 12
|
||||
CC errors: 0
|
||||
TEI errors: 0
|
||||
Avg bitrate: 80.21 Mbps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Capturing a Transport Stream
|
||||
|
||||
<Aside type="tip">
|
||||
To capture a TS stream for analysis, first tune to a transponder with `tune.py`, then capture with the `stream` subcommand:
|
||||
|
||||
```bash title="Tune and capture"
|
||||
sudo python3 tools/tune.py tune 12520 27500 --pol H --band high
|
||||
sudo python3 tools/tune.py stream -o capture.ts --duration 30
|
||||
```
|
||||
|
||||
```bash title="Pipe directly to analyzer"
|
||||
sudo python3 tools/tune.py stream --stdout | python3 tools/ts_analyze.py monitor -
|
||||
```
|
||||
</Aside>
|
||||
|
||||
## Sync Detection
|
||||
|
||||
The analyzer requires at least 3 consecutive sync bytes (`0x47`) at 188-byte intervals to confirm alignment. If the file starts with non-TS data (e.g., leading garbage from a partial USB transfer), the tool will skip ahead until sync is found.
|
||||
|
||||
When using `-v` (verbose), the sync search offset is reported. A non-zero sync offset may indicate that the capture started mid-packet or contains a non-TS preamble.
|
||||
|
||||
## See Also
|
||||
|
||||
- [Tuning Tool](/tools/tuning/) -- capture TS data from a transponder
|
||||
- [GPIF Streaming](/bcm4500/gpif-streaming/) -- how TS data flows from BCM4500 through USB
|
||||
- [Vendor Commands](/usb/vendor-commands/) -- ARM_TRANSFER (0x85) starts/stops the stream
|
||||
---
|
||||
title: TS Analyzer
|
||||
description: MPEG-2 Transport Stream analyzer for captured satellite data from the SkyWalker-1.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Steps, Aside, Badge } from '@astrojs/starlight/components';
|
||||
|
||||
`ts_analyze.py` parses and analyzes 188-byte MPEG-2 transport stream packets from `.ts` files captured by `tune.py`, piped from stdin, or any standard TS source. It covers PID distribution, PAT/PMT parsing, continuity counter verification, scrambling detection, bitrate estimation, and live stream monitoring.
|
||||
|
||||
Reference: ISO/IEC 13818-1 (MPEG-2 Systems).
|
||||
|
||||
## Subcommands
|
||||
|
||||
| Subcommand | Purpose |
|
||||
|------------|---------|
|
||||
| `analyze` | Full stream analysis with PID table, CC errors, bitrate (default) |
|
||||
| `pids` | Quick PID summary table |
|
||||
| `pat` | Parse and display the Program Association Table |
|
||||
| `pmt` | Parse and display Program Map Tables |
|
||||
| `dump` | Hex dump of individual TS packets |
|
||||
| `monitor` | Live stream monitoring with real-time statistics |
|
||||
|
||||
Passing a filename without a subcommand defaults to `analyze`.
|
||||
|
||||
## Global Options
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `-v, --verbose` | Show sync search details and debug info |
|
||||
|
||||
---
|
||||
|
||||
## analyze -- Full Stream Analysis
|
||||
|
||||
Comprehensive analysis of a TS file or stream: total packets, unique PIDs, TEI errors, scrambling, continuity counter errors, PCR-based bitrate, and a per-PID distribution table.
|
||||
|
||||
```bash
|
||||
python3 tools/ts_analyze.py capture.ts
|
||||
```
|
||||
|
||||
```bash title="Analyze first 10000 packets only"
|
||||
python3 tools/ts_analyze.py analyze capture.ts --max-packets 10000
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `INPUT` | TS file path or `-` for stdin |
|
||||
| `--max-packets N` | Limit analysis to N packets (0 = all) |
|
||||
|
||||
**Example output:**
|
||||
|
||||
```
|
||||
MPEG-2 Transport Stream Analysis
|
||||
============================================================
|
||||
File: capture.ts (52,428,800 bytes)
|
||||
|
||||
Total packets: 278,876
|
||||
Total bytes: 52,428,688
|
||||
Unique PIDs: 12
|
||||
TEI errors: 0
|
||||
Scrambled: 0
|
||||
Duration: 5.23s (from PCR)
|
||||
Bitrate: 80.21 Mbps (PCR-based)
|
||||
|
||||
============================================================
|
||||
PID Distribution
|
||||
============================================================
|
||||
PID Count % CC Err Name
|
||||
--- ----- -- ------ ----
|
||||
0x0000 1,394 0.50% - PAT
|
||||
0x0001 278 0.10% - CAT
|
||||
0x0010 556 0.20% - NIT/ST
|
||||
0x0011 556 0.20% - SDT/BAT/ST
|
||||
0x0100 139,438 50.00% -
|
||||
0x0101 83,663 29.99% -
|
||||
0x0102 5,576 2.00% -
|
||||
0x1FFF 47,415 17.01% - Null
|
||||
```
|
||||
|
||||
### What It Detects
|
||||
|
||||
| Metric | Description |
|
||||
|--------|-------------|
|
||||
| **PID count** | Packet count per Program ID |
|
||||
| **TEI** | Transport Error Indicator flag in the TS header |
|
||||
| **Scrambling** | Transport scrambling control bits (even/odd key) |
|
||||
| **CC errors** | Continuity counter discontinuities per PID |
|
||||
| **Bitrate** | Calculated from PCR timestamps when available |
|
||||
|
||||
---
|
||||
|
||||
## pids -- Quick PID Table
|
||||
|
||||
Faster variant of `analyze` that only counts packets per PID without CC checking or PCR extraction.
|
||||
|
||||
```bash
|
||||
python3 tools/ts_analyze.py pids capture.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## pat -- Program Association Table
|
||||
|
||||
Parses PID 0x0000 to extract the PAT, showing the transport stream ID and which PMT PID corresponds to each program number.
|
||||
|
||||
```bash
|
||||
python3 tools/ts_analyze.py pat capture.ts
|
||||
```
|
||||
|
||||
```
|
||||
Program Association Table (PAT)
|
||||
==================================================
|
||||
Transport Stream ID: 0x0001 (1)
|
||||
Version: 3
|
||||
Programs: 2
|
||||
|
||||
Program PMT PID Note
|
||||
------- ------- ----
|
||||
0 0x0010 NIT
|
||||
1 0x0100
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## pmt -- Program Map Tables
|
||||
|
||||
Parses the PAT first to discover PMT PIDs, then parses each PMT to show the elementary streams (video, audio, data) in each program.
|
||||
|
||||
```bash
|
||||
python3 tools/ts_analyze.py pmt capture.ts
|
||||
```
|
||||
|
||||
```
|
||||
Program Map Tables
|
||||
============================================================
|
||||
Transport Stream ID: 0x0001
|
||||
|
||||
Program 1 (PMT PID 0x0100, version 2)
|
||||
PCR PID: 0x0101
|
||||
Streams:
|
||||
Type PID Description
|
||||
---- --- -----------
|
||||
0x02 0x0101 MPEG-2 Video (13818-2)
|
||||
0x04 0x0102 MPEG-2 Audio (13818-3)
|
||||
0x06 0x0103 PES Private Data
|
||||
```
|
||||
|
||||
### Known Stream Types
|
||||
|
||||
The tool recognizes these standard stream type identifiers:
|
||||
|
||||
| Code | Description |
|
||||
|------|-------------|
|
||||
| 0x01 | MPEG-1 Video |
|
||||
| 0x02 | MPEG-2 Video |
|
||||
| 0x03 | MPEG-1 Audio |
|
||||
| 0x04 | MPEG-2 Audio |
|
||||
| 0x06 | PES Private Data |
|
||||
| 0x0F | MPEG-2 AAC Audio |
|
||||
| 0x1B | H.264/AVC Video |
|
||||
| 0x24 | H.265/HEVC Video |
|
||||
| 0x81 | AC-3 Audio (ATSC) |
|
||||
|
||||
---
|
||||
|
||||
## dump -- Hex Dump Packets
|
||||
|
||||
Print detailed per-packet information including header fields, adaptation field flags, PCR values, and raw hex dump.
|
||||
|
||||
```bash title="Dump first 5 packets"
|
||||
python3 tools/ts_analyze.py dump capture.ts --count 5
|
||||
```
|
||||
|
||||
```bash title="Dump packets for a specific PID"
|
||||
python3 tools/ts_analyze.py dump capture.ts --pid 0x0100 --count 3
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--pid PID` | Filter by PID (hex `0x100` or decimal `256`) |
|
||||
| `--count N` | Maximum packets to dump (default: `10`) |
|
||||
|
||||
**Example output:**
|
||||
|
||||
```
|
||||
Packet #1
|
||||
PID: 0x0000 (PAT)
|
||||
TEI: 0 PUSI: 1 Priority: 0
|
||||
Scrambling: none Adaptation: payload only CC: 5
|
||||
Hex:
|
||||
0000: 47 40 00 15 00 00 B0 0D 00 01 C1 00 00 00 00 E0 G@..............
|
||||
0010: 10 00 01 E1 00 78 A0 B4 FF FF FF FF FF FF FF FF .....x..........
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## monitor -- Live Stream Monitoring
|
||||
|
||||
Real-time monitoring of a TS stream, reporting new PIDs as they appear, continuity errors, TEI errors, and bitrate.
|
||||
|
||||
```bash title="Monitor from a live stream pipe"
|
||||
sudo python3 tools/tune.py stream --stdout | python3 tools/ts_analyze.py monitor -
|
||||
```
|
||||
|
||||
```bash title="Monitor a growing file"
|
||||
python3 tools/ts_analyze.py monitor capture.ts
|
||||
```
|
||||
|
||||
The monitor prints events as they occur (new PIDs, errors) and updates a status line on stderr every second with running statistics.
|
||||
|
||||
```
|
||||
MPEG-2 TS Live Monitor
|
||||
============================================================
|
||||
Ctrl-C to stop
|
||||
|
||||
[ 0.0s] New PID: 0x0000 (PAT)
|
||||
[ 0.0s] New PID: 0x1FFF (Null)
|
||||
[ 0.1s] New PID: 0x0100
|
||||
[ 0.1s] New PID: 0x0101
|
||||
[ 0.2s] New PID: 0x0102
|
||||
278,876 pkts 52,428,688 bytes 80.21 Mbps PIDs: 12 CCerr:0 TEI:0 (5s)
|
||||
|
||||
Monitor Summary
|
||||
========================================
|
||||
Duration: 5.2s
|
||||
Packets: 278,876
|
||||
Bytes: 52,428,688
|
||||
Unique PIDs: 12
|
||||
CC errors: 0
|
||||
TEI errors: 0
|
||||
Avg bitrate: 80.21 Mbps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Capturing a Transport Stream
|
||||
|
||||
<Aside type="tip">
|
||||
To capture a TS stream for analysis, first tune to a transponder with `tune.py`, then capture with the `stream` subcommand:
|
||||
|
||||
```bash title="Tune and capture"
|
||||
sudo python3 tools/tune.py tune 12520 27500 --pol H --band high
|
||||
sudo python3 tools/tune.py stream -o capture.ts --duration 30
|
||||
```
|
||||
|
||||
```bash title="Pipe directly to analyzer"
|
||||
sudo python3 tools/tune.py stream --stdout | python3 tools/ts_analyze.py monitor -
|
||||
```
|
||||
</Aside>
|
||||
|
||||
## Sync Detection
|
||||
|
||||
The analyzer requires at least 3 consecutive sync bytes (`0x47`) at 188-byte intervals to confirm alignment. If the file starts with non-TS data (e.g., leading garbage from a partial USB transfer), the tool will skip ahead until sync is found.
|
||||
|
||||
When using `-v` (verbose), the sync search offset is reported. A non-zero sync offset may indicate that the capture started mid-packet or contains a non-TS preamble.
|
||||
|
||||
## See Also
|
||||
|
||||
- [Tuning Tool](/tools/tuning/) -- capture TS data from a transponder
|
||||
- [GPIF Streaming](/bcm4500/gpif-streaming/) -- how TS data flows from BCM4500 through USB
|
||||
- [Vendor Commands](/usb/vendor-commands/) -- ARM_TRANSFER (0x85) starts/stops the stream
|
||||
|
|
|
|||
|
|
@ -1,314 +1,314 @@
|
|||
---
|
||||
title: SkyWalker TUI
|
||||
description: Interactive terminal dashboard for spectrum analysis, transponder scanning, signal monitoring, L-band analysis, carrier tracking, device management, transport stream analysis, and hardware configuration.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Steps, Aside, Badge } from '@astrojs/starlight/components';
|
||||
|
||||
The SkyWalker TUI is a full-screen terminal dashboard built on [Textual](https://textual.textualize.io/) that wraps all ten operating modes into a single interactive interface. Five RF signal modes (Spectrum, Scan, Monitor, L-Band, Track), Device management, Transport Stream analysis, hardware Config, DiSEqC Motor control, and an automated Carrier Survey with QO-100 DATV support — all with sidebar navigation, F-key shortcuts, real-time widget updates, and a dark/light theme toggle.
|
||||
|
||||
<Badge text="Requires custom firmware v3.03.0+" variant="caution" />
|
||||
|
||||

|
||||
|
||||
## Installation
|
||||
|
||||
The TUI is distributed as the `skywalker-tui` package from the `tui/` directory in the repository.
|
||||
|
||||
```bash title="Install with uv"
|
||||
cd tui && uv sync
|
||||
```
|
||||
|
||||
```bash title="Run with hardware"
|
||||
sudo uv run skywalker-tui
|
||||
```
|
||||
|
||||
```bash title="Run in demo mode (no hardware)"
|
||||
uv run skywalker-tui --demo
|
||||
```
|
||||
|
||||
### CLI Flags
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--demo` | Synthetic signal data — no SkyWalker-1 USB device needed |
|
||||
| `--no-splash` | Skip the startup splash screen |
|
||||
| `--verbose, -v` | Verbose USB logging (hardware mode only) |
|
||||
| `MODE` | Initial mode: `spectrum` (default), `scan`, `monitor`, `lband`, `track`, `device`, `stream`, `config`, `motor`, `survey` |
|
||||
|
||||
<Aside type="tip">
|
||||
Demo mode generates realistic-looking synthetic data for every screen, making it useful for familiarization, documentation, and development without satellite hardware.
|
||||
</Aside>
|
||||
|
||||
---
|
||||
|
||||
## Keyboard Shortcuts
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| <kbd>F1</kbd> | Spectrum analyzer |
|
||||
| <kbd>F2</kbd> | Transponder scanner |
|
||||
| <kbd>F3</kbd> | Signal monitor |
|
||||
| <kbd>F4</kbd> | L-Band analyzer |
|
||||
| <kbd>F5</kbd> | Carrier tracker |
|
||||
| <kbd>F6</kbd> | Device management |
|
||||
| <kbd>F7</kbd> | Transport stream |
|
||||
| <kbd>F8</kbd> | Hardware config |
|
||||
| <kbd>F9</kbd> | Motor control |
|
||||
| <kbd>F10</kbd> | Carrier survey |
|
||||
| <kbd>d</kbd> | Toggle dark/light theme |
|
||||
| <kbd>q</kbd> | Quit |
|
||||
| <kbd>Ctrl+W</kbd> | Easter egg |
|
||||
| <kbd>Esc</kbd> | Dismiss overlay / skip splash |
|
||||
|
||||
The sidebar buttons mirror the F-key shortcuts — click or press to switch modes. The active mode is highlighted in the sidebar.
|
||||
|
||||
---
|
||||
|
||||
## Screens
|
||||
|
||||
Each mode is a self-contained screen with its own widgets, workers, and layout. Switching modes pauses the previous screen's polling and starts the new one.
|
||||
|
||||
### Spectrum <Badge text="F1" variant="note" />
|
||||
|
||||
Sweep spectrum analyzer across the 950–2150 MHz IF range. Renders a bar chart of signal power at each frequency step with a scrolling waterfall display below.
|
||||
|
||||

|
||||
|
||||
- Bar chart with color-coded signal levels (blue → green → yellow → red)
|
||||
- Waterfall display showing power over time
|
||||
- Peak detection markers above the noise floor
|
||||
- Configurable sweep range, step size, and dwell time
|
||||
|
||||
### Scan <Badge text="F2" variant="note" />
|
||||
|
||||
Three-phase automated transponder search: coarse sweep, fine sweep around peaks, and blind scan at each refined candidate across a range of symbol rates.
|
||||
|
||||

|
||||
|
||||
<Steps>
|
||||
1. **Coarse sweep** at 10 MHz steps to identify candidate carriers
|
||||
2. **Fine sweep** at 2 MHz steps around each detected peak
|
||||
3. **Blind scan** at each refined peak, trying symbol rates from min to max
|
||||
</Steps>
|
||||
|
||||
- Progress indicator for each phase
|
||||
- Results table with frequency, symbol rate, and lock status
|
||||
- Supports Ku-band, C-band, and custom LO configurations
|
||||
|
||||
### Monitor <Badge text="F3" variant="note" />
|
||||
|
||||
Real-time signal strength display for hands-free dish alignment. Tunes to a single frequency and polls continuously with visual feedback.
|
||||
|
||||

|
||||
|
||||
- Signal gauge with lock/unlock status indicator
|
||||
- Sparkline history of recent signal strength samples
|
||||
- SNR and AGC readouts
|
||||
- Designed for dish alignment — visual feedback without needing to read numbers
|
||||
|
||||
### L-Band <Badge text="F4" variant="note" />
|
||||
|
||||
Direct RF input analysis in the 950–2150 MHz range with LNB power disabled. Annotates known L-band frequency allocations.
|
||||
|
||||

|
||||
|
||||
- Same sweep engine as Spectrum mode, but with `lnb_lo=0` and LNB voltage off
|
||||
- Frequency allocation overlays for Amateur 23cm, GNSS, Inmarsat, Iridium, MetSat
|
||||
- Detects carrier presence regardless of modulation compatibility
|
||||
|
||||
<Aside type="caution">
|
||||
Connect an L-band antenna or filtered preamp directly to the F-type input. LNB power is explicitly disabled in this mode to protect non-LNB equipment.
|
||||
</Aside>
|
||||
|
||||
### Track <Badge text="F5" variant="note" />
|
||||
|
||||
Carrier/beacon tracker with timestamped logging. Tracks a single frequency and detects lock/unlock transitions over time.
|
||||
|
||||

|
||||
|
||||
- Radar scope display showing signal history
|
||||
- Sparkline for signal power trend
|
||||
- Event log with timestamped lock/unlock transitions
|
||||
- Frequency drift detection
|
||||
|
||||
### Device <Badge text="F6" variant="note" />
|
||||
|
||||
Firmware management, EEPROM operations, and hardware diagnostics. An identity panel at the top always shows firmware version, serial number, USB speed, and config register flags.
|
||||
|
||||

|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Firmware">
|
||||
- FX2 RAM read at arbitrary addresses rendered as scrollable hex dump
|
||||
- CPU Halt / Start controls for the Cypress FX2 microcontroller
|
||||
- Non-destructive — RAM contents revert on power cycle
|
||||
</TabItem>
|
||||
<TabItem label="EEPROM">
|
||||
- Read full 16 KB EEPROM with progress bar
|
||||
- One-click backup to timestamped `.bin` file
|
||||
- Flash with full safety state machine:
|
||||
|
||||
<Steps>
|
||||
1. **C2 validation** — magic byte, VID/PID match, END marker, record parsing
|
||||
2. **Auto-backup** — saves current EEPROM to timestamped file before any write
|
||||
3. **3-second countdown** — ABORT button auto-focused (accidental keypress = cancel)
|
||||
4. **Page write** — 16-byte pages with progress bar and circuit breaker (3 consecutive errors = abort)
|
||||
5. **Byte-verify** — full readback with diff highlighting in hex view
|
||||
</Steps>
|
||||
</TabItem>
|
||||
<TabItem label="Diagnostics">
|
||||
- Boot test with mode selector (0x80–0x85) showing stage progression
|
||||
- I2C bus scan identifying BCM4500, EEPROM, tuner, and LNB controller
|
||||
- BCM4500 register dump rendered as hex view
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<Aside type="danger">
|
||||
EEPROM flash is the only operation that can brick the device. The safety state machine prevents accidental writes, but a power loss during write will corrupt the boot image. The verify step detects partial writes — if it fails, **do not power cycle** until the flash is retried successfully.
|
||||
</Aside>
|
||||
|
||||
### Stream <Badge text="F7" variant="note" />
|
||||
|
||||
Live MPEG-2 Transport Stream capture and analysis. Reads raw 188-byte TS packets from the SkyWalker-1 bulk endpoint, parses them in real time, and displays PID distribution statistics alongside a hierarchical PSI program structure tree.
|
||||
|
||||

|
||||
|
||||
- PID distribution table with count, percentage, CC errors, and known PID names
|
||||
- PAT/PMT tree display showing transport stream ID, program numbers, PMT PIDs, and elementary stream types (video, audio, data)
|
||||
- Aggregate stats: total packets, bytes, unique PIDs, CC errors, duration
|
||||
- Capture-to-file mode for saving raw `.ts` files to disk
|
||||
- Auto-starts monitoring in demo mode
|
||||
|
||||
<Aside type="tip">
|
||||
The stream monitor batches UI updates at 500ms intervals to keep the interface responsive even at high packet rates. The USB bulk endpoint is always safely disarmed when monitoring stops.
|
||||
</Aside>
|
||||
|
||||
### Config <Badge text="F8" variant="note" />
|
||||
|
||||
LNB power control, DiSEqC switching, and modulation/FEC configuration — all the hardware settings in one place.
|
||||
|
||||

|
||||
|
||||
<Tabs>
|
||||
<TabItem label="LNB Control">
|
||||
- Power On / Off toggle
|
||||
- Voltage selection: 13V (vertical) / 18V (horizontal), with optional +1V boost
|
||||
- 22 kHz tone toggle for high-band LNB switching
|
||||
- Current draw warning (450 mA continuous max)
|
||||
</TabItem>
|
||||
<TabItem label="DiSEqC Switch">
|
||||
- Port selector (1–4) sending committed commands
|
||||
- Tone burst A/B for legacy switches
|
||||
- Raw hex message input (3–6 byte DiSEqC 1.0/1.2 commands) with validation
|
||||
</TabItem>
|
||||
<TabItem label="Modulation / FEC">
|
||||
- Modulation type dropdown: QPSK, Turbo QPSK, Turbo 8PSK, DCII Combo, DCII Split I/Q, DCII Offset QPSK, DSS
|
||||
- FEC rate dropdown filtered per modulation group
|
||||
- Symbol rate (256 Ksps – 30 Msps) and frequency inputs
|
||||
- Tune button applies settings to the BCM4500 demodulator
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Motor Control <Badge text="F9" variant="note" />
|
||||
|
||||
DiSEqC 1.2 positioner motor control with three-column layout: jog/halt controls, stored positions, and USALS GotoX calculator. A live signal bar at the bottom provides real-time SNR, power, lock status, and motor position feedback for hands-free dish alignment.
|
||||
|
||||

|
||||
|
||||
- Continuous jog (east/west) with keyboard arrows or button controls
|
||||
- 3x3 stored position grid for quick satellite recall
|
||||
- USALS GotoX calculator with observer longitude input and satellite presets (QO-100, Galaxy 19, AMC-1)
|
||||
- 30-second auto-halt safety timer on continuous jog
|
||||
- Motor automatically halts when switching away from the screen
|
||||
- Live signal gauge updates at 2 Hz for alignment feedback
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| <kbd>Left</kbd> | Jog west |
|
||||
| <kbd>Right</kbd> | Jog east |
|
||||
| <kbd>Space</kbd> | Halt motor |
|
||||
|
||||
<Aside type="caution">
|
||||
The motor continues to drive until explicitly halted or the 30-second safety timer triggers. Always set east/west limits via the Limits panel before extended use.
|
||||
</Aside>
|
||||
|
||||
### Survey + QO-100 <Badge text="F10" variant="note" />
|
||||
|
||||
Automated carrier survey with two tabs: Full Band sweep across the entire IF range, and a QO-100 DATV tab with parameters optimized for the Es'hail-2 wideband transponder.
|
||||
|
||||

|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Full Band">
|
||||
Six-stage survey pipeline: coarse sweep, peak detection, fine sweep, blind scan, TS sample, and catalog assembly. Results display as a spectrum plot and frequency table showing detected carriers with their symbol rate, modulation, lock status, and service names.
|
||||
|
||||
- Configurable start/stop frequency and step sizes
|
||||
- Full scan and quick scan modes
|
||||
- Carrier table with real-time status
|
||||
- Results auto-saved to `~/.skywalker1/surveys/`
|
||||
</TabItem>
|
||||
<TabItem label="QO-100 DATV">
|
||||
Narrowband sweep tuned for Es'hail-2's wideband transponder (10491-10499 MHz). Uses tighter frequency steps (0.5/0.1 MHz vs 5/1 MHz), lower symbol rate range (256 ksps - 2 Msps), and a more sensitive detection threshold (3.0 dB).
|
||||
|
||||
- LNB local oscillator input for IF calculation
|
||||
- Known QO-100 station frequencies displayed
|
||||
- Info panel explaining BCM4500 symbol rate limitations
|
||||
- Carriers classified as "detected" vs "locked"
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
## Easter Eggs
|
||||
|
||||
### Dark Side Toggle
|
||||
|
||||
Press <kbd>d</kbd> to toggle between dark and light themes. Switching to dark mode triggers a notification:
|
||||
|
||||
> *"Welcome to the Dark Side."*
|
||||
> — The Force is strong with this one
|
||||
|
||||

|
||||
|
||||
Switching back to light mode:
|
||||
|
||||
> *"The Force awakens."*
|
||||
> — A New Hope
|
||||
|
||||
### Star Wars
|
||||
|
||||
Press <kbd>Ctrl+W</kbd> to open the Star Wars overlay. It attempts to connect to `towel.blinkenlights.nl:23` for the classic ASCII Star Wars telnet animation. If the connection fails (port 23 is blocked on many networks), a built-in opening crawl plays instead — complete with a SkyWalker-1 themed storyline and Star Destroyer ASCII art.
|
||||
|
||||

|
||||
|
||||
Press <kbd>Esc</kbd> to close the overlay and return to the main dashboard.
|
||||
|
||||
### Kitty Cat
|
||||
|
||||
When running inside the [Kitty terminal](https://sw.kovidgoyal.net/kitty/), the splash screen adds a subtle 🐱 to the title bar. A small nod to the terminal that made it all possible.
|
||||
|
||||
---
|
||||
|
||||
## Splash Screen
|
||||
|
||||
On startup, the TUI displays a randomly selected piece of ASCII art from the [16colo.rs / Mistigris](https://16colo.rs/) archives — pre-baked as ANSI half-block art for instant rendering. The splash auto-dismisses after 5 seconds or on any keypress.
|
||||
|
||||

|
||||
|
||||
Use `--no-splash` to skip it.
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [SkyWalker RF Tool](/tools/skywalker/) — CLI version of the RF modes (text-only output, scriptable)
|
||||
- [Spectrum Analysis](/tools/spectrum-analysis/) — practical how-to guides for each operating mode
|
||||
- [Signal Monitoring](/bcm4500/signal-monitoring/) — SIGNAL_MONITOR register protocol and data format
|
||||
- [Tuning Tool](/tools/tuning/) — primary tuning, LNB control, and transport stream capture
|
||||
- [EEPROM Utilities](/tools/eeprom-utilities/) — standalone CLI for firmware updates (same safety protocol as F6 Device screen)
|
||||
- [TS Stream Analyzer](/tools/ts-analyzer/) — standalone TS packet analyzer (same engine as F7 Stream screen)
|
||||
- [Motor Control](/tools/motor/) — standalone CLI for DiSEqC 1.2 motor control (same engine as F9 Motor screen)
|
||||
- [Carrier Survey](/tools/survey/) — standalone CLI for automated carrier survey (same engine as F10 Survey screen)
|
||||
- [QO-100 DATV Reception](/guides/qo100-datv/) — complete guide to Es'hail-2 amateur television reception
|
||||
- [RF Coverage](/hardware/rf-coverage/) — frequency coverage and antenna considerations
|
||||
---
|
||||
title: SkyWalker TUI
|
||||
description: Interactive terminal dashboard for spectrum analysis, transponder scanning, signal monitoring, L-band analysis, carrier tracking, device management, transport stream analysis, and hardware configuration.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Steps, Aside, Badge } from '@astrojs/starlight/components';
|
||||
|
||||
The SkyWalker TUI is a full-screen terminal dashboard built on [Textual](https://textual.textualize.io/) that wraps all ten operating modes into a single interactive interface. Five RF signal modes (Spectrum, Scan, Monitor, L-Band, Track), Device management, Transport Stream analysis, hardware Config, DiSEqC Motor control, and an automated Carrier Survey with QO-100 DATV support — all with sidebar navigation, F-key shortcuts, real-time widget updates, and a dark/light theme toggle.
|
||||
|
||||
<Badge text="Requires custom firmware v3.03.0+" variant="caution" />
|
||||
|
||||

|
||||
|
||||
## Installation
|
||||
|
||||
The TUI is distributed as the `skywalker-tui` package from the `tui/` directory in the repository.
|
||||
|
||||
```bash title="Install with uv"
|
||||
cd tui && uv sync
|
||||
```
|
||||
|
||||
```bash title="Run with hardware"
|
||||
sudo uv run skywalker-tui
|
||||
```
|
||||
|
||||
```bash title="Run in demo mode (no hardware)"
|
||||
uv run skywalker-tui --demo
|
||||
```
|
||||
|
||||
### CLI Flags
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--demo` | Synthetic signal data — no SkyWalker-1 USB device needed |
|
||||
| `--no-splash` | Skip the startup splash screen |
|
||||
| `--verbose, -v` | Verbose USB logging (hardware mode only) |
|
||||
| `MODE` | Initial mode: `spectrum` (default), `scan`, `monitor`, `lband`, `track`, `device`, `stream`, `config`, `motor`, `survey` |
|
||||
|
||||
<Aside type="tip">
|
||||
Demo mode generates realistic-looking synthetic data for every screen, making it useful for familiarization, documentation, and development without satellite hardware.
|
||||
</Aside>
|
||||
|
||||
---
|
||||
|
||||
## Keyboard Shortcuts
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| <kbd>F1</kbd> | Spectrum analyzer |
|
||||
| <kbd>F2</kbd> | Transponder scanner |
|
||||
| <kbd>F3</kbd> | Signal monitor |
|
||||
| <kbd>F4</kbd> | L-Band analyzer |
|
||||
| <kbd>F5</kbd> | Carrier tracker |
|
||||
| <kbd>F6</kbd> | Device management |
|
||||
| <kbd>F7</kbd> | Transport stream |
|
||||
| <kbd>F8</kbd> | Hardware config |
|
||||
| <kbd>F9</kbd> | Motor control |
|
||||
| <kbd>F10</kbd> | Carrier survey |
|
||||
| <kbd>d</kbd> | Toggle dark/light theme |
|
||||
| <kbd>q</kbd> | Quit |
|
||||
| <kbd>Ctrl+W</kbd> | Easter egg |
|
||||
| <kbd>Esc</kbd> | Dismiss overlay / skip splash |
|
||||
|
||||
The sidebar buttons mirror the F-key shortcuts — click or press to switch modes. The active mode is highlighted in the sidebar.
|
||||
|
||||
---
|
||||
|
||||
## Screens
|
||||
|
||||
Each mode is a self-contained screen with its own widgets, workers, and layout. Switching modes pauses the previous screen's polling and starts the new one.
|
||||
|
||||
### Spectrum <Badge text="F1" variant="note" />
|
||||
|
||||
Sweep spectrum analyzer across the 950–2150 MHz IF range. Renders a bar chart of signal power at each frequency step with a scrolling waterfall display below.
|
||||
|
||||

|
||||
|
||||
- Bar chart with color-coded signal levels (blue → green → yellow → red)
|
||||
- Waterfall display showing power over time
|
||||
- Peak detection markers above the noise floor
|
||||
- Configurable sweep range, step size, and dwell time
|
||||
|
||||
### Scan <Badge text="F2" variant="note" />
|
||||
|
||||
Three-phase automated transponder search: coarse sweep, fine sweep around peaks, and blind scan at each refined candidate across a range of symbol rates.
|
||||
|
||||

|
||||
|
||||
<Steps>
|
||||
1. **Coarse sweep** at 10 MHz steps to identify candidate carriers
|
||||
2. **Fine sweep** at 2 MHz steps around each detected peak
|
||||
3. **Blind scan** at each refined peak, trying symbol rates from min to max
|
||||
</Steps>
|
||||
|
||||
- Progress indicator for each phase
|
||||
- Results table with frequency, symbol rate, and lock status
|
||||
- Supports Ku-band, C-band, and custom LO configurations
|
||||
|
||||
### Monitor <Badge text="F3" variant="note" />
|
||||
|
||||
Real-time signal strength display for hands-free dish alignment. Tunes to a single frequency and polls continuously with visual feedback.
|
||||
|
||||

|
||||
|
||||
- Signal gauge with lock/unlock status indicator
|
||||
- Sparkline history of recent signal strength samples
|
||||
- SNR and AGC readouts
|
||||
- Designed for dish alignment — visual feedback without needing to read numbers
|
||||
|
||||
### L-Band <Badge text="F4" variant="note" />
|
||||
|
||||
Direct RF input analysis in the 950–2150 MHz range with LNB power disabled. Annotates known L-band frequency allocations.
|
||||
|
||||

|
||||
|
||||
- Same sweep engine as Spectrum mode, but with `lnb_lo=0` and LNB voltage off
|
||||
- Frequency allocation overlays for Amateur 23cm, GNSS, Inmarsat, Iridium, MetSat
|
||||
- Detects carrier presence regardless of modulation compatibility
|
||||
|
||||
<Aside type="caution">
|
||||
Connect an L-band antenna or filtered preamp directly to the F-type input. LNB power is explicitly disabled in this mode to protect non-LNB equipment.
|
||||
</Aside>
|
||||
|
||||
### Track <Badge text="F5" variant="note" />
|
||||
|
||||
Carrier/beacon tracker with timestamped logging. Tracks a single frequency and detects lock/unlock transitions over time.
|
||||
|
||||

|
||||
|
||||
- Radar scope display showing signal history
|
||||
- Sparkline for signal power trend
|
||||
- Event log with timestamped lock/unlock transitions
|
||||
- Frequency drift detection
|
||||
|
||||
### Device <Badge text="F6" variant="note" />
|
||||
|
||||
Firmware management, EEPROM operations, and hardware diagnostics. An identity panel at the top always shows firmware version, serial number, USB speed, and config register flags.
|
||||
|
||||

|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Firmware">
|
||||
- FX2 RAM read at arbitrary addresses rendered as scrollable hex dump
|
||||
- CPU Halt / Start controls for the Cypress FX2 microcontroller
|
||||
- Non-destructive — RAM contents revert on power cycle
|
||||
</TabItem>
|
||||
<TabItem label="EEPROM">
|
||||
- Read full 16 KB EEPROM with progress bar
|
||||
- One-click backup to timestamped `.bin` file
|
||||
- Flash with full safety state machine:
|
||||
|
||||
<Steps>
|
||||
1. **C2 validation** — magic byte, VID/PID match, END marker, record parsing
|
||||
2. **Auto-backup** — saves current EEPROM to timestamped file before any write
|
||||
3. **3-second countdown** — ABORT button auto-focused (accidental keypress = cancel)
|
||||
4. **Page write** — 16-byte pages with progress bar and circuit breaker (3 consecutive errors = abort)
|
||||
5. **Byte-verify** — full readback with diff highlighting in hex view
|
||||
</Steps>
|
||||
</TabItem>
|
||||
<TabItem label="Diagnostics">
|
||||
- Boot test with mode selector (0x80–0x85) showing stage progression
|
||||
- I2C bus scan identifying BCM4500, EEPROM, tuner, and LNB controller
|
||||
- BCM4500 register dump rendered as hex view
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<Aside type="danger">
|
||||
EEPROM flash is the only operation that can brick the device. The safety state machine prevents accidental writes, but a power loss during write will corrupt the boot image. The verify step detects partial writes — if it fails, **do not power cycle** until the flash is retried successfully.
|
||||
</Aside>
|
||||
|
||||
### Stream <Badge text="F7" variant="note" />
|
||||
|
||||
Live MPEG-2 Transport Stream capture and analysis. Reads raw 188-byte TS packets from the SkyWalker-1 bulk endpoint, parses them in real time, and displays PID distribution statistics alongside a hierarchical PSI program structure tree.
|
||||
|
||||

|
||||
|
||||
- PID distribution table with count, percentage, CC errors, and known PID names
|
||||
- PAT/PMT tree display showing transport stream ID, program numbers, PMT PIDs, and elementary stream types (video, audio, data)
|
||||
- Aggregate stats: total packets, bytes, unique PIDs, CC errors, duration
|
||||
- Capture-to-file mode for saving raw `.ts` files to disk
|
||||
- Auto-starts monitoring in demo mode
|
||||
|
||||
<Aside type="tip">
|
||||
The stream monitor batches UI updates at 500ms intervals to keep the interface responsive even at high packet rates. The USB bulk endpoint is always safely disarmed when monitoring stops.
|
||||
</Aside>
|
||||
|
||||
### Config <Badge text="F8" variant="note" />
|
||||
|
||||
LNB power control, DiSEqC switching, and modulation/FEC configuration — all the hardware settings in one place.
|
||||
|
||||

|
||||
|
||||
<Tabs>
|
||||
<TabItem label="LNB Control">
|
||||
- Power On / Off toggle
|
||||
- Voltage selection: 13V (vertical) / 18V (horizontal), with optional +1V boost
|
||||
- 22 kHz tone toggle for high-band LNB switching
|
||||
- Current draw warning (450 mA continuous max)
|
||||
</TabItem>
|
||||
<TabItem label="DiSEqC Switch">
|
||||
- Port selector (1–4) sending committed commands
|
||||
- Tone burst A/B for legacy switches
|
||||
- Raw hex message input (3–6 byte DiSEqC 1.0/1.2 commands) with validation
|
||||
</TabItem>
|
||||
<TabItem label="Modulation / FEC">
|
||||
- Modulation type dropdown: QPSK, Turbo QPSK, Turbo 8PSK, DCII Combo, DCII Split I/Q, DCII Offset QPSK, DSS
|
||||
- FEC rate dropdown filtered per modulation group
|
||||
- Symbol rate (256 Ksps – 30 Msps) and frequency inputs
|
||||
- Tune button applies settings to the BCM4500 demodulator
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Motor Control <Badge text="F9" variant="note" />
|
||||
|
||||
DiSEqC 1.2 positioner motor control with three-column layout: jog/halt controls, stored positions, and USALS GotoX calculator. A live signal bar at the bottom provides real-time SNR, power, lock status, and motor position feedback for hands-free dish alignment.
|
||||
|
||||

|
||||
|
||||
- Continuous jog (east/west) with keyboard arrows or button controls
|
||||
- 3x3 stored position grid for quick satellite recall
|
||||
- USALS GotoX calculator with observer longitude input and satellite presets (QO-100, Galaxy 19, AMC-1)
|
||||
- 30-second auto-halt safety timer on continuous jog
|
||||
- Motor automatically halts when switching away from the screen
|
||||
- Live signal gauge updates at 2 Hz for alignment feedback
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| <kbd>Left</kbd> | Jog west |
|
||||
| <kbd>Right</kbd> | Jog east |
|
||||
| <kbd>Space</kbd> | Halt motor |
|
||||
|
||||
<Aside type="caution">
|
||||
The motor continues to drive until explicitly halted or the 30-second safety timer triggers. Always set east/west limits via the Limits panel before extended use.
|
||||
</Aside>
|
||||
|
||||
### Survey + QO-100 <Badge text="F10" variant="note" />
|
||||
|
||||
Automated carrier survey with two tabs: Full Band sweep across the entire IF range, and a QO-100 DATV tab with parameters optimized for the Es'hail-2 wideband transponder.
|
||||
|
||||

|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Full Band">
|
||||
Six-stage survey pipeline: coarse sweep, peak detection, fine sweep, blind scan, TS sample, and catalog assembly. Results display as a spectrum plot and frequency table showing detected carriers with their symbol rate, modulation, lock status, and service names.
|
||||
|
||||
- Configurable start/stop frequency and step sizes
|
||||
- Full scan and quick scan modes
|
||||
- Carrier table with real-time status
|
||||
- Results auto-saved to `~/.skywalker1/surveys/`
|
||||
</TabItem>
|
||||
<TabItem label="QO-100 DATV">
|
||||
Narrowband sweep tuned for Es'hail-2's wideband transponder (10491-10499 MHz). Uses tighter frequency steps (0.5/0.1 MHz vs 5/1 MHz), lower symbol rate range (256 ksps - 2 Msps), and a more sensitive detection threshold (3.0 dB).
|
||||
|
||||
- LNB local oscillator input for IF calculation
|
||||
- Known QO-100 station frequencies displayed
|
||||
- Info panel explaining BCM4500 symbol rate limitations
|
||||
- Carriers classified as "detected" vs "locked"
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
## Easter Eggs
|
||||
|
||||
### Dark Side Toggle
|
||||
|
||||
Press <kbd>d</kbd> to toggle between dark and light themes. Switching to dark mode triggers a notification:
|
||||
|
||||
> *"Welcome to the Dark Side."*
|
||||
> — The Force is strong with this one
|
||||
|
||||

|
||||
|
||||
Switching back to light mode:
|
||||
|
||||
> *"The Force awakens."*
|
||||
> — A New Hope
|
||||
|
||||
### Star Wars
|
||||
|
||||
Press <kbd>Ctrl+W</kbd> to open the Star Wars overlay. It attempts to connect to `towel.blinkenlights.nl:23` for the classic ASCII Star Wars telnet animation. If the connection fails (port 23 is blocked on many networks), a built-in opening crawl plays instead — complete with a SkyWalker-1 themed storyline and Star Destroyer ASCII art.
|
||||
|
||||

|
||||
|
||||
Press <kbd>Esc</kbd> to close the overlay and return to the main dashboard.
|
||||
|
||||
### Kitty Cat
|
||||
|
||||
When running inside the [Kitty terminal](https://sw.kovidgoyal.net/kitty/), the splash screen adds a subtle 🐱 to the title bar. A small nod to the terminal that made it all possible.
|
||||
|
||||
---
|
||||
|
||||
## Splash Screen
|
||||
|
||||
On startup, the TUI displays a randomly selected piece of ASCII art from the [16colo.rs / Mistigris](https://16colo.rs/) archives — pre-baked as ANSI half-block art for instant rendering. The splash auto-dismisses after 5 seconds or on any keypress.
|
||||
|
||||

|
||||
|
||||
Use `--no-splash` to skip it.
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [SkyWalker RF Tool](/tools/skywalker/) — CLI version of the RF modes (text-only output, scriptable)
|
||||
- [Spectrum Analysis](/tools/spectrum-analysis/) — practical how-to guides for each operating mode
|
||||
- [Signal Monitoring](/bcm4500/signal-monitoring/) — SIGNAL_MONITOR register protocol and data format
|
||||
- [Tuning Tool](/tools/tuning/) — primary tuning, LNB control, and transport stream capture
|
||||
- [EEPROM Utilities](/tools/eeprom-utilities/) — standalone CLI for firmware updates (same safety protocol as F6 Device screen)
|
||||
- [TS Stream Analyzer](/tools/ts-analyzer/) — standalone TS packet analyzer (same engine as F7 Stream screen)
|
||||
- [Motor Control](/tools/motor/) — standalone CLI for DiSEqC 1.2 motor control (same engine as F9 Motor screen)
|
||||
- [Carrier Survey](/tools/survey/) — standalone CLI for automated carrier survey (same engine as F10 Survey screen)
|
||||
- [QO-100 DATV Reception](/guides/qo100-datv/) — complete guide to Es'hail-2 amateur television reception
|
||||
- [RF Coverage](/hardware/rf-coverage/) — frequency coverage and antenna considerations
|
||||
|
|
|
|||
|
|
@ -1,293 +1,293 @@
|
|||
---
|
||||
title: Tuning Tool
|
||||
description: DVB-S tuning, signal monitoring, LNB control, DiSEqC switching, and transport stream capture.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Steps, Aside, Badge } from '@astrojs/starlight/components';
|
||||
|
||||
`tune.py` is the primary user-facing tool for operating the SkyWalker-1 as a satellite receiver. It handles the complete signal chain: powering the demodulator, configuring LNB voltage and tone, sending DiSEqC switch commands, tuning to a transponder, monitoring signal quality, and capturing MPEG-2 transport stream data.
|
||||
|
||||
```bash
|
||||
pip install pyusb
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
| Subcommand | Purpose |
|
||||
|------------|---------|
|
||||
| `status` | Show device config, firmware version, and signal info |
|
||||
| `tune` | Tune to a transponder frequency |
|
||||
| `stream` | Capture MPEG-2 transport stream data |
|
||||
| `diseqc` | Send DiSEqC switch commands |
|
||||
| `lnb` | Control LNB voltage and 22 kHz tone |
|
||||
|
||||
Running with no subcommand defaults to `status`.
|
||||
|
||||
## Global Options
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `-v, --verbose` | Show raw USB control transfer traffic |
|
||||
| `--json` | Output machine-readable JSON (where supported) |
|
||||
|
||||
---
|
||||
|
||||
## status -- Device Information
|
||||
|
||||
Displays the current device state: firmware version, configuration status flags, signal lock, and SNR.
|
||||
|
||||
```bash
|
||||
sudo python3 tools/tune.py status
|
||||
```
|
||||
|
||||
```
|
||||
Genpix SkyWalker-1 Status
|
||||
==================================================
|
||||
|
||||
USB: Bus 1 Addr 12 (VID 0x09C0, PID 0x0203)
|
||||
FW: 2.06.4 (built 2007-07-13)
|
||||
|
||||
Config: 0x07
|
||||
[ ON] 8PSK Started
|
||||
[ ON] BCM4500 FW Loaded
|
||||
[ ON] LNB Power On
|
||||
[off] DVB Mode
|
||||
[off] 22 kHz Tone
|
||||
[off] 18V Selected
|
||||
[off] DC Tuned
|
||||
[off] Armed (streaming)
|
||||
|
||||
Signal Lock: LOCKED
|
||||
SNR: 8.2 dB (raw 0x0823)
|
||||
Quality: [################------------------------] 42.3%
|
||||
```
|
||||
|
||||
Use `--json` for programmatic access to all fields.
|
||||
|
||||
---
|
||||
|
||||
## tune -- Transponder Tuning
|
||||
|
||||
The 8-step tuning sequence: check status, boot demodulator, enable LNB power, set voltage (polarization), set extra voltage, set 22 kHz tone (band), send TUNE_8PSK command, wait for signal lock.
|
||||
|
||||
### Required Arguments
|
||||
|
||||
| Argument | Description |
|
||||
|----------|-------------|
|
||||
| `FREQ` | Transponder downlink frequency in MHz (e.g., `12520`) |
|
||||
| `SR` | Symbol rate in ksps (e.g., `27500`) |
|
||||
|
||||
### Options
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--pol H/V/L/R` | Polarization: H (horizontal/18V), V (vertical/13V), L (left circular/18V), R (right circular/13V) |
|
||||
| `--band low/high` | LNB band: low (tone off, 9750 MHz LO) or high (tone on, 10600 MHz LO) |
|
||||
| `--lnb-lo MHZ` | Override LNB local oscillator frequency (default: 9750 low, 10600 high) |
|
||||
| `--mod TYPE` | Modulation type (default: `qpsk`) |
|
||||
| `--fec RATE` | FEC rate (default: `auto`) |
|
||||
| `--timeout SECONDS` | Signal lock timeout (default: `10`) |
|
||||
| `--extra-volt` | Enable +1V LNB voltage boost for long cables |
|
||||
| `--json` | Output JSON result |
|
||||
|
||||
### Modulation Types
|
||||
|
||||
| Name | Description | FEC Group |
|
||||
|------|-------------|-----------|
|
||||
| `qpsk` | DVB-S QPSK | dvbs |
|
||||
| `turbo-qpsk` | Turbo-coded QPSK | turbo |
|
||||
| `turbo-8psk` | Turbo-coded 8PSK | turbo |
|
||||
| `turbo-16qam` | Turbo-coded 16QAM | turbo-16qam |
|
||||
| `dcii-combo` | Digicipher II Combo | dcii |
|
||||
| `dcii-i` | Digicipher II I-stream | dcii |
|
||||
| `dcii-q` | Digicipher II Q-stream | dcii |
|
||||
| `dcii-oqpsk` | Digicipher II Offset QPSK | dcii |
|
||||
| `dss` | DSS QPSK | dvbs |
|
||||
| `bpsk` | DVB BPSK | dvbs |
|
||||
|
||||
### FEC Rates by Group
|
||||
|
||||
| Group | Available Rates |
|
||||
|-------|----------------|
|
||||
| **dvbs** | `1/2`, `2/3`, `3/4`, `5/6`, `7/8`, `auto`, `none` |
|
||||
| **turbo** | `1/2`, `2/3`, `3/4`, `5/6`, `auto` |
|
||||
| **turbo-16qam** | `3/4`, `auto` |
|
||||
| **dcii** | `1/2`, `2/3`, `6/7`, `3/4`, `5/11`, `1/2+`, `2/3+`, `6/7+`, `3/4+`, `auto` |
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="DVB-S">
|
||||
|
||||
```bash title="Standard DVB-S QPSK, Ku-band high"
|
||||
sudo python3 tools/tune.py tune 12520 27500 --pol H --band high
|
||||
```
|
||||
|
||||
```bash title="DVB-S with explicit FEC"
|
||||
sudo python3 tools/tune.py tune 11836 27500 --pol V --band low --fec 3/4
|
||||
```
|
||||
|
||||
```bash title="C-band with custom LNB LO"
|
||||
sudo python3 tools/tune.py tune 3960 26667 --pol V --lnb-lo 5150
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Turbo">
|
||||
|
||||
```bash title="Turbo 8PSK"
|
||||
sudo python3 tools/tune.py tune 12224 20000 --pol V --band high --mod turbo-8psk --fec 2/3
|
||||
```
|
||||
|
||||
```bash title="Turbo QPSK with auto FEC"
|
||||
sudo python3 tools/tune.py tune 12090 20000 --pol H --band high --mod turbo-qpsk
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="DCII">
|
||||
|
||||
```bash title="Digicipher II Combo"
|
||||
sudo python3 tools/tune.py tune 12224 20000 --pol V --band high --mod dcii-combo --fec auto
|
||||
```
|
||||
|
||||
```bash title="DCII Split I-stream"
|
||||
sudo python3 tools/tune.py tune 12224 20000 --pol V --band high --mod dcii-i --fec 6/7
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Tuning Output
|
||||
|
||||
```
|
||||
Tuning SkyWalker-1
|
||||
==================================================
|
||||
Downlink: 12520 MHz
|
||||
LNB LO: 10600 MHz
|
||||
IF Frequency: 1920.0 MHz (1920000 kHz)
|
||||
Symbol Rate: 27500 ksps (27500000 sps)
|
||||
Modulation: DVB-S QPSK (index 0)
|
||||
FEC: auto (index 5)
|
||||
Polarization: Horizontal (18V)
|
||||
Band: high (22kHz on)
|
||||
|
||||
[1/8] Config status: 0x07
|
||||
[2/8] Demodulator already running
|
||||
[3/8] LNB power already on
|
||||
[4/8] Setting LNB voltage: 18V
|
||||
[5/8] Extra voltage: off
|
||||
[6/8] 22 kHz tone: ON
|
||||
[7/8] Sending TUNE_8PSK...
|
||||
[8/8] Waiting for signal lock (timeout 10s)...
|
||||
...
|
||||
|
||||
LOCKED
|
||||
SNR: 8.2 dB (raw 0x0823)
|
||||
Quality: [################------------------------] 42.3%
|
||||
```
|
||||
|
||||
<Aside type="tip">
|
||||
The IF frequency is computed as `downlink - LNB_LO`. It must fall within 950--2150 MHz or the tool will warn. If you see negative IF values, your LNB LO is set higher than the downlink frequency -- check your LNB type.
|
||||
</Aside>
|
||||
|
||||
---
|
||||
|
||||
## stream -- Transport Stream Capture
|
||||
|
||||
Captures MPEG-2 transport stream data from EP2 bulk endpoint after the device is tuned and locked. Requires a prior successful `tune` command (signal must be locked).
|
||||
|
||||
```bash title="Capture to file for 60 seconds"
|
||||
sudo python3 tools/tune.py stream -o capture.ts --duration 60
|
||||
```
|
||||
|
||||
```bash title="Pipe directly to VLC"
|
||||
sudo python3 tools/tune.py stream --stdout | vlc -
|
||||
```
|
||||
|
||||
```bash title="Pipe to ffmpeg for recording"
|
||||
sudo python3 tools/tune.py stream --stdout | ffmpeg -i pipe: -c copy output.mp4
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `-o, --output FILE` | Write TS data to file |
|
||||
| `--stdout` | Write TS data to stdout (for piping) |
|
||||
| `--duration SECONDS` | Capture duration (default: until Ctrl-C) |
|
||||
|
||||
The stream status is written to stderr when using `--stdout`, so it does not contaminate the TS data pipe.
|
||||
|
||||
<Aside type="tip">
|
||||
Capture a short sample and analyze it with [ts_analyze.py](/tools/ts-analyzer/) to verify PIDs, check for continuity errors, and identify the programs in the transport stream.
|
||||
</Aside>
|
||||
|
||||
---
|
||||
|
||||
## diseqc -- Switch Commands
|
||||
|
||||
Send DiSEqC 1.0 port selection, mini-DiSEqC tone bursts, or raw DiSEqC messages.
|
||||
|
||||
```bash title="Select DiSEqC 1.0 port 1"
|
||||
sudo python3 tools/tune.py diseqc --port 1
|
||||
```
|
||||
|
||||
```bash title="Send tone burst A"
|
||||
sudo python3 tools/tune.py diseqc --tone-burst A
|
||||
```
|
||||
|
||||
```bash title="Send raw DiSEqC message"
|
||||
sudo python3 tools/tune.py diseqc --raw E0 10 38 F0
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--port 1-4` | DiSEqC 1.0 committed switch port |
|
||||
| `--tone-burst A/B` | Mini-DiSEqC tone burst |
|
||||
| `--raw HH HH ...` | Raw DiSEqC bytes in hex (3--6 bytes) |
|
||||
|
||||
For details on the DiSEqC signaling implementation, see [DiSEqC Protocol](/lnb-diseqc/diseqc-protocol/).
|
||||
|
||||
---
|
||||
|
||||
## lnb -- LNB Control
|
||||
|
||||
Direct control of LNB voltage, 22 kHz tone, and power supply. Running with no flags shows the current LNB state.
|
||||
|
||||
```bash title="Show LNB status"
|
||||
sudo python3 tools/tune.py lnb
|
||||
```
|
||||
|
||||
```bash title="Set voltage and tone"
|
||||
sudo python3 tools/tune.py lnb --voltage 18 --tone on
|
||||
```
|
||||
|
||||
```bash title="Power cycle the LNB"
|
||||
sudo python3 tools/tune.py lnb --power off
|
||||
sudo python3 tools/tune.py lnb --power on
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--voltage 13/18` | LNB voltage (13V = vertical, 18V = horizontal) |
|
||||
| `--tone on/off` | 22 kHz tone (on = high band, off = low band) |
|
||||
| `--extra-volt` | Enable +1V boost (13V becomes 14V, 18V becomes 19V) |
|
||||
| `--power on/off` | LNB power supply on/off |
|
||||
|
||||
<Aside type="caution">
|
||||
The LNB can supply up to 750 mA non-continuous, but sustained loads should stay below 450 mA to avoid overheating the on-board regulator. Use `--extra-volt` only when needed for long cable runs.
|
||||
</Aside>
|
||||
|
||||
---
|
||||
|
||||
## Signal Quality Metrics
|
||||
|
||||
| Metric | Source | Description |
|
||||
|--------|--------|-------------|
|
||||
| **SNR** | `GET_SIGNAL_STRENGTH` (0x87) | Signal-to-noise ratio in dB, read as 16-bit value (dBu * 256 units) |
|
||||
| **Lock** | `GET_SIGNAL_LOCK` (0x90) | BCM4500 register 0xA4, bit 5 indicates signal lock |
|
||||
| **Quality %** | Computed | SNR scaled: `snr_raw * 17` maps to 0--100%, saturating at SNR >= `0x0F00` |
|
||||
|
||||
For details on the signal monitoring protocol, see [Signal Monitoring](/bcm4500/signal-monitoring/).
|
||||
|
||||
## See Also
|
||||
|
||||
- [Tuning Protocol](/bcm4500/tuning-protocol/) -- firmware-level TUNE_8PSK command format
|
||||
- [LNB Control](/lnb-diseqc/lnb-control/) -- hardware-level voltage and tone control
|
||||
- [TS Analyzer](/tools/ts-analyzer/) -- analyze captured transport streams
|
||||
- [Vendor Commands](/usb/vendor-commands/) -- complete USB command reference
|
||||
---
|
||||
title: Tuning Tool
|
||||
description: DVB-S tuning, signal monitoring, LNB control, DiSEqC switching, and transport stream capture.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Steps, Aside, Badge } from '@astrojs/starlight/components';
|
||||
|
||||
`tune.py` is the primary user-facing tool for operating the SkyWalker-1 as a satellite receiver. It handles the complete signal chain: powering the demodulator, configuring LNB voltage and tone, sending DiSEqC switch commands, tuning to a transponder, monitoring signal quality, and capturing MPEG-2 transport stream data.
|
||||
|
||||
```bash
|
||||
pip install pyusb
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
| Subcommand | Purpose |
|
||||
|------------|---------|
|
||||
| `status` | Show device config, firmware version, and signal info |
|
||||
| `tune` | Tune to a transponder frequency |
|
||||
| `stream` | Capture MPEG-2 transport stream data |
|
||||
| `diseqc` | Send DiSEqC switch commands |
|
||||
| `lnb` | Control LNB voltage and 22 kHz tone |
|
||||
|
||||
Running with no subcommand defaults to `status`.
|
||||
|
||||
## Global Options
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `-v, --verbose` | Show raw USB control transfer traffic |
|
||||
| `--json` | Output machine-readable JSON (where supported) |
|
||||
|
||||
---
|
||||
|
||||
## status -- Device Information
|
||||
|
||||
Displays the current device state: firmware version, configuration status flags, signal lock, and SNR.
|
||||
|
||||
```bash
|
||||
sudo python3 tools/tune.py status
|
||||
```
|
||||
|
||||
```
|
||||
Genpix SkyWalker-1 Status
|
||||
==================================================
|
||||
|
||||
USB: Bus 1 Addr 12 (VID 0x09C0, PID 0x0203)
|
||||
FW: 2.06.4 (built 2007-07-13)
|
||||
|
||||
Config: 0x07
|
||||
[ ON] 8PSK Started
|
||||
[ ON] BCM4500 FW Loaded
|
||||
[ ON] LNB Power On
|
||||
[off] DVB Mode
|
||||
[off] 22 kHz Tone
|
||||
[off] 18V Selected
|
||||
[off] DC Tuned
|
||||
[off] Armed (streaming)
|
||||
|
||||
Signal Lock: LOCKED
|
||||
SNR: 8.2 dB (raw 0x0823)
|
||||
Quality: [################------------------------] 42.3%
|
||||
```
|
||||
|
||||
Use `--json` for programmatic access to all fields.
|
||||
|
||||
---
|
||||
|
||||
## tune -- Transponder Tuning
|
||||
|
||||
The 8-step tuning sequence: check status, boot demodulator, enable LNB power, set voltage (polarization), set extra voltage, set 22 kHz tone (band), send TUNE_8PSK command, wait for signal lock.
|
||||
|
||||
### Required Arguments
|
||||
|
||||
| Argument | Description |
|
||||
|----------|-------------|
|
||||
| `FREQ` | Transponder downlink frequency in MHz (e.g., `12520`) |
|
||||
| `SR` | Symbol rate in ksps (e.g., `27500`) |
|
||||
|
||||
### Options
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--pol H/V/L/R` | Polarization: H (horizontal/18V), V (vertical/13V), L (left circular/18V), R (right circular/13V) |
|
||||
| `--band low/high` | LNB band: low (tone off, 9750 MHz LO) or high (tone on, 10600 MHz LO) |
|
||||
| `--lnb-lo MHZ` | Override LNB local oscillator frequency (default: 9750 low, 10600 high) |
|
||||
| `--mod TYPE` | Modulation type (default: `qpsk`) |
|
||||
| `--fec RATE` | FEC rate (default: `auto`) |
|
||||
| `--timeout SECONDS` | Signal lock timeout (default: `10`) |
|
||||
| `--extra-volt` | Enable +1V LNB voltage boost for long cables |
|
||||
| `--json` | Output JSON result |
|
||||
|
||||
### Modulation Types
|
||||
|
||||
| Name | Description | FEC Group |
|
||||
|------|-------------|-----------|
|
||||
| `qpsk` | DVB-S QPSK | dvbs |
|
||||
| `turbo-qpsk` | Turbo-coded QPSK | turbo |
|
||||
| `turbo-8psk` | Turbo-coded 8PSK | turbo |
|
||||
| `turbo-16qam` | Turbo-coded 16QAM | turbo-16qam |
|
||||
| `dcii-combo` | Digicipher II Combo | dcii |
|
||||
| `dcii-i` | Digicipher II I-stream | dcii |
|
||||
| `dcii-q` | Digicipher II Q-stream | dcii |
|
||||
| `dcii-oqpsk` | Digicipher II Offset QPSK | dcii |
|
||||
| `dss` | DSS QPSK | dvbs |
|
||||
| `bpsk` | DVB BPSK | dvbs |
|
||||
|
||||
### FEC Rates by Group
|
||||
|
||||
| Group | Available Rates |
|
||||
|-------|----------------|
|
||||
| **dvbs** | `1/2`, `2/3`, `3/4`, `5/6`, `7/8`, `auto`, `none` |
|
||||
| **turbo** | `1/2`, `2/3`, `3/4`, `5/6`, `auto` |
|
||||
| **turbo-16qam** | `3/4`, `auto` |
|
||||
| **dcii** | `1/2`, `2/3`, `6/7`, `3/4`, `5/11`, `1/2+`, `2/3+`, `6/7+`, `3/4+`, `auto` |
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="DVB-S">
|
||||
|
||||
```bash title="Standard DVB-S QPSK, Ku-band high"
|
||||
sudo python3 tools/tune.py tune 12520 27500 --pol H --band high
|
||||
```
|
||||
|
||||
```bash title="DVB-S with explicit FEC"
|
||||
sudo python3 tools/tune.py tune 11836 27500 --pol V --band low --fec 3/4
|
||||
```
|
||||
|
||||
```bash title="C-band with custom LNB LO"
|
||||
sudo python3 tools/tune.py tune 3960 26667 --pol V --lnb-lo 5150
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Turbo">
|
||||
|
||||
```bash title="Turbo 8PSK"
|
||||
sudo python3 tools/tune.py tune 12224 20000 --pol V --band high --mod turbo-8psk --fec 2/3
|
||||
```
|
||||
|
||||
```bash title="Turbo QPSK with auto FEC"
|
||||
sudo python3 tools/tune.py tune 12090 20000 --pol H --band high --mod turbo-qpsk
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="DCII">
|
||||
|
||||
```bash title="Digicipher II Combo"
|
||||
sudo python3 tools/tune.py tune 12224 20000 --pol V --band high --mod dcii-combo --fec auto
|
||||
```
|
||||
|
||||
```bash title="DCII Split I-stream"
|
||||
sudo python3 tools/tune.py tune 12224 20000 --pol V --band high --mod dcii-i --fec 6/7
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Tuning Output
|
||||
|
||||
```
|
||||
Tuning SkyWalker-1
|
||||
==================================================
|
||||
Downlink: 12520 MHz
|
||||
LNB LO: 10600 MHz
|
||||
IF Frequency: 1920.0 MHz (1920000 kHz)
|
||||
Symbol Rate: 27500 ksps (27500000 sps)
|
||||
Modulation: DVB-S QPSK (index 0)
|
||||
FEC: auto (index 5)
|
||||
Polarization: Horizontal (18V)
|
||||
Band: high (22kHz on)
|
||||
|
||||
[1/8] Config status: 0x07
|
||||
[2/8] Demodulator already running
|
||||
[3/8] LNB power already on
|
||||
[4/8] Setting LNB voltage: 18V
|
||||
[5/8] Extra voltage: off
|
||||
[6/8] 22 kHz tone: ON
|
||||
[7/8] Sending TUNE_8PSK...
|
||||
[8/8] Waiting for signal lock (timeout 10s)...
|
||||
...
|
||||
|
||||
LOCKED
|
||||
SNR: 8.2 dB (raw 0x0823)
|
||||
Quality: [################------------------------] 42.3%
|
||||
```
|
||||
|
||||
<Aside type="tip">
|
||||
The IF frequency is computed as `downlink - LNB_LO`. It must fall within 950--2150 MHz or the tool will warn. If you see negative IF values, your LNB LO is set higher than the downlink frequency -- check your LNB type.
|
||||
</Aside>
|
||||
|
||||
---
|
||||
|
||||
## stream -- Transport Stream Capture
|
||||
|
||||
Captures MPEG-2 transport stream data from EP2 bulk endpoint after the device is tuned and locked. Requires a prior successful `tune` command (signal must be locked).
|
||||
|
||||
```bash title="Capture to file for 60 seconds"
|
||||
sudo python3 tools/tune.py stream -o capture.ts --duration 60
|
||||
```
|
||||
|
||||
```bash title="Pipe directly to VLC"
|
||||
sudo python3 tools/tune.py stream --stdout | vlc -
|
||||
```
|
||||
|
||||
```bash title="Pipe to ffmpeg for recording"
|
||||
sudo python3 tools/tune.py stream --stdout | ffmpeg -i pipe: -c copy output.mp4
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `-o, --output FILE` | Write TS data to file |
|
||||
| `--stdout` | Write TS data to stdout (for piping) |
|
||||
| `--duration SECONDS` | Capture duration (default: until Ctrl-C) |
|
||||
|
||||
The stream status is written to stderr when using `--stdout`, so it does not contaminate the TS data pipe.
|
||||
|
||||
<Aside type="tip">
|
||||
Capture a short sample and analyze it with [ts_analyze.py](/tools/ts-analyzer/) to verify PIDs, check for continuity errors, and identify the programs in the transport stream.
|
||||
</Aside>
|
||||
|
||||
---
|
||||
|
||||
## diseqc -- Switch Commands
|
||||
|
||||
Send DiSEqC 1.0 port selection, mini-DiSEqC tone bursts, or raw DiSEqC messages.
|
||||
|
||||
```bash title="Select DiSEqC 1.0 port 1"
|
||||
sudo python3 tools/tune.py diseqc --port 1
|
||||
```
|
||||
|
||||
```bash title="Send tone burst A"
|
||||
sudo python3 tools/tune.py diseqc --tone-burst A
|
||||
```
|
||||
|
||||
```bash title="Send raw DiSEqC message"
|
||||
sudo python3 tools/tune.py diseqc --raw E0 10 38 F0
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--port 1-4` | DiSEqC 1.0 committed switch port |
|
||||
| `--tone-burst A/B` | Mini-DiSEqC tone burst |
|
||||
| `--raw HH HH ...` | Raw DiSEqC bytes in hex (3--6 bytes) |
|
||||
|
||||
For details on the DiSEqC signaling implementation, see [DiSEqC Protocol](/lnb-diseqc/diseqc-protocol/).
|
||||
|
||||
---
|
||||
|
||||
## lnb -- LNB Control
|
||||
|
||||
Direct control of LNB voltage, 22 kHz tone, and power supply. Running with no flags shows the current LNB state.
|
||||
|
||||
```bash title="Show LNB status"
|
||||
sudo python3 tools/tune.py lnb
|
||||
```
|
||||
|
||||
```bash title="Set voltage and tone"
|
||||
sudo python3 tools/tune.py lnb --voltage 18 --tone on
|
||||
```
|
||||
|
||||
```bash title="Power cycle the LNB"
|
||||
sudo python3 tools/tune.py lnb --power off
|
||||
sudo python3 tools/tune.py lnb --power on
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--voltage 13/18` | LNB voltage (13V = vertical, 18V = horizontal) |
|
||||
| `--tone on/off` | 22 kHz tone (on = high band, off = low band) |
|
||||
| `--extra-volt` | Enable +1V boost (13V becomes 14V, 18V becomes 19V) |
|
||||
| `--power on/off` | LNB power supply on/off |
|
||||
|
||||
<Aside type="caution">
|
||||
The LNB can supply up to 750 mA non-continuous, but sustained loads should stay below 450 mA to avoid overheating the on-board regulator. Use `--extra-volt` only when needed for long cable runs.
|
||||
</Aside>
|
||||
|
||||
---
|
||||
|
||||
## Signal Quality Metrics
|
||||
|
||||
| Metric | Source | Description |
|
||||
|--------|--------|-------------|
|
||||
| **SNR** | `GET_SIGNAL_STRENGTH` (0x87) | Signal-to-noise ratio in dB, read as 16-bit value (dBu * 256 units) |
|
||||
| **Lock** | `GET_SIGNAL_LOCK` (0x90) | BCM4500 register 0xA4, bit 5 indicates signal lock |
|
||||
| **Quality %** | Computed | SNR scaled: `snr_raw * 17` maps to 0--100%, saturating at SNR >= `0x0F00` |
|
||||
|
||||
For details on the signal monitoring protocol, see [Signal Monitoring](/bcm4500/signal-monitoring/).
|
||||
|
||||
## See Also
|
||||
|
||||
- [Tuning Protocol](/bcm4500/tuning-protocol/) -- firmware-level TUNE_8PSK command format
|
||||
- [LNB Control](/lnb-diseqc/lnb-control/) -- hardware-level voltage and tone control
|
||||
- [TS Analyzer](/tools/ts-analyzer/) -- analyze captured transport streams
|
||||
- [Vendor Commands](/usb/vendor-commands/) -- complete USB command reference
|
||||
|
|
|
|||
|
|
@ -1,135 +1,135 @@
|
|||
---
|
||||
title: Boot Sequence
|
||||
description: Complete power-on boot flow from USB enumeration through BCM4500 demodulator initialization.
|
||||
---
|
||||
|
||||
import { Steps, Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
## EEPROM Boot (Hardware)
|
||||
|
||||
The SkyWalker-1 firmware is stored in a 24Cxx-family I2C EEPROM in Cypress C2 format. On power-up, the FX2 boot ROM reads this firmware automatically -- no host interaction is required.
|
||||
|
||||
### C2 EEPROM Format
|
||||
|
||||
| Offset | Size | Field | SkyWalker-1 Value |
|
||||
|--------|------|-------|-------------------|
|
||||
| 0 | 1 | Marker | 0xC2 (external memory, large code model) |
|
||||
| 1 | 2 | VID (LE) | 0x09C0 |
|
||||
| 3 | 2 | PID (LE) | 0x0203 |
|
||||
| 5 | 2 | DID (LE) | 0x0000 |
|
||||
| 7 | 1 | Config | 0x40 (400 kHz I2C) |
|
||||
|
||||
Code segments follow the header: 2-byte length (BE) + 2-byte target address (BE) + data. Maximum segment size is 1023 bytes (FX2 I2C boot ROM buffer limit). All SkyWalker-1 variants use 10 segments.
|
||||
|
||||
The terminator is 0x80xx (high bit set) + 2-byte entry point address (0xE600 = CPUCS register).
|
||||
|
||||
## Kernel Driver Boot Flow
|
||||
|
||||
After USB enumeration, the Linux `dvb_usb_gp8psk` driver executes this initialization sequence:
|
||||
|
||||
<Steps>
|
||||
1. **GET_8PSK_CONFIG (0x80)** -- Read the [configuration status byte](/usb/config-status/). Check bit 0 (`bm8pskStarted`).
|
||||
|
||||
2. **BOOT_8PSK (0x89, wValue=1)** -- If not started, power on the BCM4500 demodulator. Then read the firmware version via **GET_FW_VERS (0x92)**.
|
||||
|
||||
3. **LOAD_BCM4500 (0x88)** -- If bit 1 (`bm8pskFW_Loaded`) is clear, load BCM4500 firmware. This only applies to Rev.1 Warm (PID 0x0201). On SkyWalker-1, this bit is always set and 0x88 returns STALL.
|
||||
|
||||
4. **START_INTERSIL (0x8A, wValue=1)** -- If bit 2 (`bmIntersilOn`) is clear, enable the LNB power supply.
|
||||
|
||||
5. **SET_DVB_MODE (0x8E, wValue=1)** -- Attempt to set DVB mode. This STALLs on all SkyWalker-1 firmware versions (the command is a no-op).
|
||||
|
||||
6. **ARM_TRANSFER (0x85, wValue=0)** -- Abort any pending MPEG-2 transfer to ensure a clean state.
|
||||
|
||||
7. **Device ready for tuning** -- The kernel driver reports the frontend as available.
|
||||
</Steps>
|
||||
|
||||
## BCM4500 Demodulator Boot (BOOT_8PSK, 0x89)
|
||||
|
||||
The BOOT_8PSK command handler powers on the BCM4500 demodulator and writes three initialization register blocks via I2C. This sequence was reverse-engineered from stock v2.06 firmware (`FUN_CODE_1D4F` + `FUN_CODE_0ddd`) and re-implemented in custom firmware v3.01.0.
|
||||
|
||||
<Steps>
|
||||
1. **Assert BCM4500 RESET** -- Drive P0.5 LOW. This holds the BCM4500's digital logic in reset while power is applied.
|
||||
|
||||
2. **Power on** -- Set P0.1 HIGH (power enable), P0.2 LOW (power disable off). The SkyWalker-1 has complementary power control pins.
|
||||
|
||||
3. **Wait for power settle** -- 30 ms delay. The power supply must reach regulation before releasing reset.
|
||||
|
||||
4. **Release RESET** -- Drive P0.5 HIGH. The BCM4500 begins its internal power-on reset (POR) and mask ROM boot sequence.
|
||||
|
||||
5. **Wait for BCM4500 POR** -- 50 ms delay. The chip needs time for its internal oscillator to stabilize and mask ROM to execute.
|
||||
|
||||
6. **I2C probe** -- Read direct register 0xA2 (status) at I2C address 0x08 to verify the chip is alive and responding. If this fails, boot aborts.
|
||||
|
||||
7. **Write init block 0** -- 7 bytes to indirect page 0, starting at register 0x06. Data: `{06 0b 17 38 9f d9 80}`.
|
||||
|
||||
8. **Write init block 1** -- 8 bytes to indirect page 0, starting at register 0x07. Data: `{07 09 39 4f 00 65 b7 10}`.
|
||||
|
||||
9. **Write init block 2** -- 3 bytes to indirect page 0, starting at register 0x0F. Data: `{0f 0c 09}`.
|
||||
|
||||
10. **Set config_status** -- OR in `BM_STARTED | BM_FW_LOADED` (0x03). Subsequent vendor commands check this flag before operating.
|
||||
</Steps>
|
||||
|
||||
Each init block is written using the BCM4500 [indirect register protocol](/bcm4500/demodulator/): page select (0xA6 = 0x00), data to 0xA7 (multi-byte with auto-increment), trailing zero to 0xA7, then commit (0xA8 = 0x03). The firmware polls 0xA8 until the command completes before proceeding.
|
||||
|
||||
**Total boot time**: approximately 90 ms (30 ms power settle + 50 ms POR delay + ~10 ms I2C transactions).
|
||||
|
||||
### Boot Results
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Boot time | ~90 ms total |
|
||||
| config_status after boot | 0x03 (STARTED + FW_LOADED) |
|
||||
| Direct registers 0xA2-0xA8 | All return 0x02 (powered, not locked -- expected without signal) |
|
||||
| Signal lock | 0x00 (no lock -- no satellite signal present) |
|
||||
|
||||
<Aside type="danger" title="I2C STOP Corruption During Boot">
|
||||
A critical bug was discovered during custom firmware development: sending `I2CS |= bmSTOP` when no I2C transaction is active (before the probe step) corrupts the FX2 I2C controller state, causing all subsequent I2C operations to fail.
|
||||
|
||||
The fix is to simply omit the spurious STOP condition. If the I2C bus is idle (as it should be after power-on), a new START condition succeeds without any bus reset.
|
||||
|
||||
See [I2C STOP Corruption Bug](/i2c/stop-corruption-bug/) for the complete root cause analysis.
|
||||
</Aside>
|
||||
|
||||
## Firmware Version Identification
|
||||
|
||||
The kernel reads the firmware version on boot via GET_FW_VERS (0x92) and logs it:
|
||||
|
||||
```
|
||||
gp8psk: FW Version = 2.06.4 (0x20604) Build 2007/07/13
|
||||
```
|
||||
|
||||
Kernel revision constants for hardware detection:
|
||||
|
||||
```c title="gp8psk-fe.h"
|
||||
GP8PSK_FW_REV1 = 0x020604 // v2.06.4
|
||||
GP8PSK_FW_REV2 = 0x020704 // v2.07.4
|
||||
```
|
||||
|
||||
If `fw_vers >= GP8PSK_FW_REV2`, the kernel enables Rev.2-specific code paths. The v2.10 and v2.13 firmwares are newer than either constant and trigger Rev.2 behavior.
|
||||
|
||||
## FX2 CPUCS Recovery
|
||||
|
||||
The FX2's CPUCS register at 0xE600 is accessible via the standard vendor request `bRequest=0xA0` (RAM read/write), which is handled by the FX2 boot ROM in silicon -- not by user firmware. This means firmware can be reloaded over a completely hung device without a physical USB unplug:
|
||||
|
||||
```bash title="Recover a hung device"
|
||||
sudo python3 tools/fw_load.py load firmware/build/skywalker1.ihx --wait 3
|
||||
```
|
||||
|
||||
Writing 0x01 to CPUCS halts the CPU. New code is written to RAM. Writing 0x00 restarts it. The device re-enumerates with the new firmware.
|
||||
|
||||
## Custom Debug Boot Modes (v3.01.0)
|
||||
|
||||
The custom firmware extends BOOT_8PSK (0x89) with incremental debug modes for isolating boot failures:
|
||||
|
||||
| wValue | Action | Purpose |
|
||||
|--------|--------|---------|
|
||||
| 0x80 | No-op: return `config_status` and `boot_stage` | Status check without side effects |
|
||||
| 0x81 | GPIO + power + delays only (no I2C) | Test power sequencing |
|
||||
| 0x82 | GPIO + power + I2C probe | Test I2C after power-on |
|
||||
| 0x83 | GPIO + power + probe + init block 0 | Test first register write |
|
||||
| 0x84 | I2C probe only (chip already powered) | Isolate I2C from power sequencing |
|
||||
| 0x85 | Same as 0x82 without bmSTOP | Confirmed the STOP corruption bug |
|
||||
| 0x01 | Full boot (production) | Normal operation |
|
||||
| 0x00 | Shutdown | Power down demodulator |
|
||||
|
||||
These modes were instrumental in identifying the [I2C STOP corruption bug](/i2c/stop-corruption-bug/) -- mode 0x82 (with bmSTOP) failed while mode 0x85 (without bmSTOP) succeeded, pinpointing the exact cause.
|
||||
---
|
||||
title: Boot Sequence
|
||||
description: Complete power-on boot flow from USB enumeration through BCM4500 demodulator initialization.
|
||||
---
|
||||
|
||||
import { Steps, Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
## EEPROM Boot (Hardware)
|
||||
|
||||
The SkyWalker-1 firmware is stored in a 24Cxx-family I2C EEPROM in Cypress C2 format. On power-up, the FX2 boot ROM reads this firmware automatically -- no host interaction is required.
|
||||
|
||||
### C2 EEPROM Format
|
||||
|
||||
| Offset | Size | Field | SkyWalker-1 Value |
|
||||
|--------|------|-------|-------------------|
|
||||
| 0 | 1 | Marker | 0xC2 (external memory, large code model) |
|
||||
| 1 | 2 | VID (LE) | 0x09C0 |
|
||||
| 3 | 2 | PID (LE) | 0x0203 |
|
||||
| 5 | 2 | DID (LE) | 0x0000 |
|
||||
| 7 | 1 | Config | 0x40 (400 kHz I2C) |
|
||||
|
||||
Code segments follow the header: 2-byte length (BE) + 2-byte target address (BE) + data. Maximum segment size is 1023 bytes (FX2 I2C boot ROM buffer limit). All SkyWalker-1 variants use 10 segments.
|
||||
|
||||
The terminator is 0x80xx (high bit set) + 2-byte entry point address (0xE600 = CPUCS register).
|
||||
|
||||
## Kernel Driver Boot Flow
|
||||
|
||||
After USB enumeration, the Linux `dvb_usb_gp8psk` driver executes this initialization sequence:
|
||||
|
||||
<Steps>
|
||||
1. **GET_8PSK_CONFIG (0x80)** -- Read the [configuration status byte](/usb/config-status/). Check bit 0 (`bm8pskStarted`).
|
||||
|
||||
2. **BOOT_8PSK (0x89, wValue=1)** -- If not started, power on the BCM4500 demodulator. Then read the firmware version via **GET_FW_VERS (0x92)**.
|
||||
|
||||
3. **LOAD_BCM4500 (0x88)** -- If bit 1 (`bm8pskFW_Loaded`) is clear, load BCM4500 firmware. This only applies to Rev.1 Warm (PID 0x0201). On SkyWalker-1, this bit is always set and 0x88 returns STALL.
|
||||
|
||||
4. **START_INTERSIL (0x8A, wValue=1)** -- If bit 2 (`bmIntersilOn`) is clear, enable the LNB power supply.
|
||||
|
||||
5. **SET_DVB_MODE (0x8E, wValue=1)** -- Attempt to set DVB mode. This STALLs on all SkyWalker-1 firmware versions (the command is a no-op).
|
||||
|
||||
6. **ARM_TRANSFER (0x85, wValue=0)** -- Abort any pending MPEG-2 transfer to ensure a clean state.
|
||||
|
||||
7. **Device ready for tuning** -- The kernel driver reports the frontend as available.
|
||||
</Steps>
|
||||
|
||||
## BCM4500 Demodulator Boot (BOOT_8PSK, 0x89)
|
||||
|
||||
The BOOT_8PSK command handler powers on the BCM4500 demodulator and writes three initialization register blocks via I2C. This sequence was reverse-engineered from stock v2.06 firmware (`FUN_CODE_1D4F` + `FUN_CODE_0ddd`) and re-implemented in custom firmware v3.01.0.
|
||||
|
||||
<Steps>
|
||||
1. **Assert BCM4500 RESET** -- Drive P0.5 LOW. This holds the BCM4500's digital logic in reset while power is applied.
|
||||
|
||||
2. **Power on** -- Set P0.1 HIGH (power enable), P0.2 LOW (power disable off). The SkyWalker-1 has complementary power control pins.
|
||||
|
||||
3. **Wait for power settle** -- 30 ms delay. The power supply must reach regulation before releasing reset.
|
||||
|
||||
4. **Release RESET** -- Drive P0.5 HIGH. The BCM4500 begins its internal power-on reset (POR) and mask ROM boot sequence.
|
||||
|
||||
5. **Wait for BCM4500 POR** -- 50 ms delay. The chip needs time for its internal oscillator to stabilize and mask ROM to execute.
|
||||
|
||||
6. **I2C probe** -- Read direct register 0xA2 (status) at I2C address 0x08 to verify the chip is alive and responding. If this fails, boot aborts.
|
||||
|
||||
7. **Write init block 0** -- 7 bytes to indirect page 0, starting at register 0x06. Data: `{06 0b 17 38 9f d9 80}`.
|
||||
|
||||
8. **Write init block 1** -- 8 bytes to indirect page 0, starting at register 0x07. Data: `{07 09 39 4f 00 65 b7 10}`.
|
||||
|
||||
9. **Write init block 2** -- 3 bytes to indirect page 0, starting at register 0x0F. Data: `{0f 0c 09}`.
|
||||
|
||||
10. **Set config_status** -- OR in `BM_STARTED | BM_FW_LOADED` (0x03). Subsequent vendor commands check this flag before operating.
|
||||
</Steps>
|
||||
|
||||
Each init block is written using the BCM4500 [indirect register protocol](/bcm4500/demodulator/): page select (0xA6 = 0x00), data to 0xA7 (multi-byte with auto-increment), trailing zero to 0xA7, then commit (0xA8 = 0x03). The firmware polls 0xA8 until the command completes before proceeding.
|
||||
|
||||
**Total boot time**: approximately 90 ms (30 ms power settle + 50 ms POR delay + ~10 ms I2C transactions).
|
||||
|
||||
### Boot Results
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Boot time | ~90 ms total |
|
||||
| config_status after boot | 0x03 (STARTED + FW_LOADED) |
|
||||
| Direct registers 0xA2-0xA8 | All return 0x02 (powered, not locked -- expected without signal) |
|
||||
| Signal lock | 0x00 (no lock -- no satellite signal present) |
|
||||
|
||||
<Aside type="danger" title="I2C STOP Corruption During Boot">
|
||||
A critical bug was discovered during custom firmware development: sending `I2CS |= bmSTOP` when no I2C transaction is active (before the probe step) corrupts the FX2 I2C controller state, causing all subsequent I2C operations to fail.
|
||||
|
||||
The fix is to simply omit the spurious STOP condition. If the I2C bus is idle (as it should be after power-on), a new START condition succeeds without any bus reset.
|
||||
|
||||
See [I2C STOP Corruption Bug](/i2c/stop-corruption-bug/) for the complete root cause analysis.
|
||||
</Aside>
|
||||
|
||||
## Firmware Version Identification
|
||||
|
||||
The kernel reads the firmware version on boot via GET_FW_VERS (0x92) and logs it:
|
||||
|
||||
```
|
||||
gp8psk: FW Version = 2.06.4 (0x20604) Build 2007/07/13
|
||||
```
|
||||
|
||||
Kernel revision constants for hardware detection:
|
||||
|
||||
```c title="gp8psk-fe.h"
|
||||
GP8PSK_FW_REV1 = 0x020604 // v2.06.4
|
||||
GP8PSK_FW_REV2 = 0x020704 // v2.07.4
|
||||
```
|
||||
|
||||
If `fw_vers >= GP8PSK_FW_REV2`, the kernel enables Rev.2-specific code paths. The v2.10 and v2.13 firmwares are newer than either constant and trigger Rev.2 behavior.
|
||||
|
||||
## FX2 CPUCS Recovery
|
||||
|
||||
The FX2's CPUCS register at 0xE600 is accessible via the standard vendor request `bRequest=0xA0` (RAM read/write), which is handled by the FX2 boot ROM in silicon -- not by user firmware. This means firmware can be reloaded over a completely hung device without a physical USB unplug:
|
||||
|
||||
```bash title="Recover a hung device"
|
||||
sudo python3 tools/fw_load.py load firmware/build/skywalker1.ihx --wait 3
|
||||
```
|
||||
|
||||
Writing 0x01 to CPUCS halts the CPU. New code is written to RAM. Writing 0x00 restarts it. The device re-enumerates with the new firmware.
|
||||
|
||||
## Custom Debug Boot Modes (v3.01.0)
|
||||
|
||||
The custom firmware extends BOOT_8PSK (0x89) with incremental debug modes for isolating boot failures:
|
||||
|
||||
| wValue | Action | Purpose |
|
||||
|--------|--------|---------|
|
||||
| 0x80 | No-op: return `config_status` and `boot_stage` | Status check without side effects |
|
||||
| 0x81 | GPIO + power + delays only (no I2C) | Test power sequencing |
|
||||
| 0x82 | GPIO + power + I2C probe | Test I2C after power-on |
|
||||
| 0x83 | GPIO + power + probe + init block 0 | Test first register write |
|
||||
| 0x84 | I2C probe only (chip already powered) | Isolate I2C from power sequencing |
|
||||
| 0x85 | Same as 0x82 without bmSTOP | Confirmed the STOP corruption bug |
|
||||
| 0x01 | Full boot (production) | Normal operation |
|
||||
| 0x00 | Shutdown | Power down demodulator |
|
||||
|
||||
These modes were instrumental in identifying the [I2C STOP corruption bug](/i2c/stop-corruption-bug/) -- mode 0x82 (with bmSTOP) failed while mode 0x85 (without bmSTOP) succeeded, pinpointing the exact cause.
|
||||
|
|
|
|||
|
|
@ -1,79 +1,79 @@
|
|||
---
|
||||
title: Configuration Status Byte
|
||||
description: Bit-field breakdown of the configuration status byte returned by GET_8PSK_CONFIG (0x80).
|
||||
---
|
||||
|
||||
import { Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
The configuration status byte is returned by the GET_8PSK_CONFIG vendor command (0x80) and reflects the current device state. The kernel driver checks these bits during initialization to determine which boot steps have already completed.
|
||||
|
||||
## Bit Field Map
|
||||
|
||||
| Bit | Mask | Name | Meaning |
|
||||
|-----|------|------|---------|
|
||||
| 7 | 0x80 | bmArmed | MPEG-2 stream transfer armed / GPIF active |
|
||||
| 6 | 0x40 | bmDCtuned | DC offset tuning complete (set for DCII modes) |
|
||||
| 5 | 0x20 | bmSEL18V | 18V LNB voltage selected (else 13V) |
|
||||
| 4 | 0x10 | bm22kHz | 22 kHz tone active |
|
||||
| 3 | 0x08 | bmDVBmode | DVB mode enabled |
|
||||
| 2 | 0x04 | bmIntersilOn | LNB power supply enabled |
|
||||
| 1 | 0x02 | bm8pskFW_Loaded | BCM4500 firmware loaded |
|
||||
| 0 | 0x01 | bm8pskStarted | Device booted and running |
|
||||
|
||||
## IRAM Storage Address
|
||||
|
||||
The status byte is stored at a different IRAM address depending on firmware version:
|
||||
|
||||
| Firmware | IRAM Address |
|
||||
|----------|-------------|
|
||||
| v2.06 | 0x6D |
|
||||
| Rev.2 v2.10.4 | 0x4E |
|
||||
| v2.13 | 0x4F |
|
||||
|
||||
## Bit Details
|
||||
|
||||
### Bit 0: bm8pskStarted <Badge text="0x01" variant="note" />
|
||||
|
||||
Set when BOOT_8PSK (0x89, wValue=1) completes successfully. The kernel driver checks this bit first. If clear, it sends BOOT_8PSK to power on the demodulator.
|
||||
|
||||
### Bit 1: bm8pskFW_Loaded <Badge text="0x02" variant="note" />
|
||||
|
||||
Indicates that the BCM4500 firmware has been loaded. On the SkyWalker-1, the BCM4500 runs from internal mask ROM, so this bit is **always set** after boot. The kernel driver checks this to decide whether to send LOAD_BCM4500 (0x88), which STALLs on the SkyWalker-1 since it is unnecessary.
|
||||
|
||||
### Bit 2: bmIntersilOn <Badge text="0x04" variant="note" />
|
||||
|
||||
Set when START_INTERSIL (0x8A, wValue=1) enables the LNB power supply. The name "Intersil" refers to the LNB voltage regulator IC manufacturer.
|
||||
|
||||
### Bit 3: bmDVBmode <Badge text="0x08" variant="note" />
|
||||
|
||||
Set when DVB-S mode is enabled via SET_DVB_MODE (0x8E). On SkyWalker-1, this command STALLs -- the bit is managed internally by the tuning dispatch logic.
|
||||
|
||||
### Bit 4: bm22kHz <Badge text="0x10" variant="note" />
|
||||
|
||||
Reflects the current state of the 22 kHz tone (SET_22KHZ_TONE, 0x8C). Set when the tone is active (high band), clear when inactive (low band).
|
||||
|
||||
### Bit 5: bmSEL18V <Badge text="0x20" variant="note" />
|
||||
|
||||
Reflects the current LNB voltage selection (SET_LNB_VOLTAGE, 0x8B). Set for 18V (horizontal/circular-left), clear for 13V (vertical/circular-right).
|
||||
|
||||
### Bit 6: bmDCtuned <Badge text="0x40" variant="note" />
|
||||
|
||||
Set when tuning to a Digicipher II (DCII) modulation mode. Cleared for all other modulation types (DVB-S, Turbo, DSS, BPSK). The tuning dispatch logic manages this bit during TUNE_8PSK (0x86) processing.
|
||||
|
||||
### Bit 7: bmArmed <Badge text="0x80" variant="note" />
|
||||
|
||||
Set when ARM_TRANSFER (0x85, wValue=1) starts the MPEG-2 transport stream. Cleared when ARM_TRANSFER (0x85, wValue=0) stops it. While set, the [GPIF engine](/bcm4500/gpif-streaming/) is continuously reading data from the BCM4500 into the EP2 FIFO.
|
||||
|
||||
## Typical Values
|
||||
|
||||
| State | Value | Bits Set |
|
||||
|-------|-------|----------|
|
||||
| After power-on (before boot) | 0x00 | None |
|
||||
| After BOOT_8PSK | 0x03 | bm8pskStarted + bm8pskFW_Loaded |
|
||||
| After LNB enable + 18V + tone | 0x37 | Started + FW + Intersil + 18V + 22kHz |
|
||||
| Streaming DVB-S | 0xB7 | Above + bmArmed |
|
||||
| Streaming DCII | 0xF7 | Above + bmDCtuned |
|
||||
|
||||
<Aside type="note" title="Kernel Driver Behavior">
|
||||
The kernel driver reads this byte during initialization and uses it to skip already-completed steps. On a warm reboot (without USB disconnect), some bits may already be set from the previous session, causing the driver to skip BOOT_8PSK, START_INTERSIL, or LOAD_BCM4500 as appropriate.
|
||||
</Aside>
|
||||
---
|
||||
title: Configuration Status Byte
|
||||
description: Bit-field breakdown of the configuration status byte returned by GET_8PSK_CONFIG (0x80).
|
||||
---
|
||||
|
||||
import { Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
The configuration status byte is returned by the GET_8PSK_CONFIG vendor command (0x80) and reflects the current device state. The kernel driver checks these bits during initialization to determine which boot steps have already completed.
|
||||
|
||||
## Bit Field Map
|
||||
|
||||
| Bit | Mask | Name | Meaning |
|
||||
|-----|------|------|---------|
|
||||
| 7 | 0x80 | bmArmed | MPEG-2 stream transfer armed / GPIF active |
|
||||
| 6 | 0x40 | bmDCtuned | DC offset tuning complete (set for DCII modes) |
|
||||
| 5 | 0x20 | bmSEL18V | 18V LNB voltage selected (else 13V) |
|
||||
| 4 | 0x10 | bm22kHz | 22 kHz tone active |
|
||||
| 3 | 0x08 | bmDVBmode | DVB mode enabled |
|
||||
| 2 | 0x04 | bmIntersilOn | LNB power supply enabled |
|
||||
| 1 | 0x02 | bm8pskFW_Loaded | BCM4500 firmware loaded |
|
||||
| 0 | 0x01 | bm8pskStarted | Device booted and running |
|
||||
|
||||
## IRAM Storage Address
|
||||
|
||||
The status byte is stored at a different IRAM address depending on firmware version:
|
||||
|
||||
| Firmware | IRAM Address |
|
||||
|----------|-------------|
|
||||
| v2.06 | 0x6D |
|
||||
| Rev.2 v2.10.4 | 0x4E |
|
||||
| v2.13 | 0x4F |
|
||||
|
||||
## Bit Details
|
||||
|
||||
### Bit 0: bm8pskStarted <Badge text="0x01" variant="note" />
|
||||
|
||||
Set when BOOT_8PSK (0x89, wValue=1) completes successfully. The kernel driver checks this bit first. If clear, it sends BOOT_8PSK to power on the demodulator.
|
||||
|
||||
### Bit 1: bm8pskFW_Loaded <Badge text="0x02" variant="note" />
|
||||
|
||||
Indicates that the BCM4500 firmware has been loaded. On the SkyWalker-1, the BCM4500 runs from internal mask ROM, so this bit is **always set** after boot. The kernel driver checks this to decide whether to send LOAD_BCM4500 (0x88), which STALLs on the SkyWalker-1 since it is unnecessary.
|
||||
|
||||
### Bit 2: bmIntersilOn <Badge text="0x04" variant="note" />
|
||||
|
||||
Set when START_INTERSIL (0x8A, wValue=1) enables the LNB power supply. The name "Intersil" refers to the LNB voltage regulator IC manufacturer.
|
||||
|
||||
### Bit 3: bmDVBmode <Badge text="0x08" variant="note" />
|
||||
|
||||
Set when DVB-S mode is enabled via SET_DVB_MODE (0x8E). On SkyWalker-1, this command STALLs -- the bit is managed internally by the tuning dispatch logic.
|
||||
|
||||
### Bit 4: bm22kHz <Badge text="0x10" variant="note" />
|
||||
|
||||
Reflects the current state of the 22 kHz tone (SET_22KHZ_TONE, 0x8C). Set when the tone is active (high band), clear when inactive (low band).
|
||||
|
||||
### Bit 5: bmSEL18V <Badge text="0x20" variant="note" />
|
||||
|
||||
Reflects the current LNB voltage selection (SET_LNB_VOLTAGE, 0x8B). Set for 18V (horizontal/circular-left), clear for 13V (vertical/circular-right).
|
||||
|
||||
### Bit 6: bmDCtuned <Badge text="0x40" variant="note" />
|
||||
|
||||
Set when tuning to a Digicipher II (DCII) modulation mode. Cleared for all other modulation types (DVB-S, Turbo, DSS, BPSK). The tuning dispatch logic manages this bit during TUNE_8PSK (0x86) processing.
|
||||
|
||||
### Bit 7: bmArmed <Badge text="0x80" variant="note" />
|
||||
|
||||
Set when ARM_TRANSFER (0x85, wValue=1) starts the MPEG-2 transport stream. Cleared when ARM_TRANSFER (0x85, wValue=0) stops it. While set, the [GPIF engine](/bcm4500/gpif-streaming/) is continuously reading data from the BCM4500 into the EP2 FIFO.
|
||||
|
||||
## Typical Values
|
||||
|
||||
| State | Value | Bits Set |
|
||||
|-------|-------|----------|
|
||||
| After power-on (before boot) | 0x00 | None |
|
||||
| After BOOT_8PSK | 0x03 | bm8pskStarted + bm8pskFW_Loaded |
|
||||
| After LNB enable + 18V + tone | 0x37 | Started + FW + Intersil + 18V + 22kHz |
|
||||
| Streaming DVB-S | 0xB7 | Above + bmArmed |
|
||||
| Streaming DCII | 0xF7 | Above + bmDCtuned |
|
||||
|
||||
<Aside type="note" title="Kernel Driver Behavior">
|
||||
The kernel driver reads this byte during initialization and uses it to skip already-completed steps. On a warm reboot (without USB disconnect), some bits may already be set from the previous session, causing the driver to skip BOOT_8PSK, START_INTERSIL, or LOAD_BCM4500 as appropriate.
|
||||
</Aside>
|
||||
|
|
|
|||
|
|
@ -1,105 +1,105 @@
|
|||
---
|
||||
title: USB Interface
|
||||
description: USB VID/PID table, endpoint map, descriptors, and warm boot behavior for the SkyWalker-1.
|
||||
---
|
||||
|
||||
import { Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
## VID/PID Table
|
||||
|
||||
All Genpix products share USB Vendor ID `0x09C0`:
|
||||
|
||||
| PID | Product | State | Notes |
|
||||
|-----|---------|-------|-------|
|
||||
| 0x0200 | 8PSK-to-USB2 Rev.1 | <Badge text="Cold" variant="caution" /> | Requires FW01 upload to RAM |
|
||||
| 0x0201 | 8PSK-to-USB2 Rev.1 | <Badge text="Warm" variant="success" /> | Requires FW02 (BCM4500 firmware) |
|
||||
| 0x0202 | 8PSK-to-USB2 Rev.2 | <Badge text="Warm" variant="success" /> | Boots from EEPROM |
|
||||
| 0x0203 | **SkyWalker-1** | <Badge text="Warm" variant="success" /> | Boots from EEPROM |
|
||||
| 0x0204 | SkyWalker-1 (alternate) | <Badge text="Warm" variant="success" /> | Boots from EEPROM |
|
||||
| 0x0205 | SkyWalker-2 | -- | Not in kernel 6.16.5 |
|
||||
| 0x0206 | SkyWalker CW3K | <Badge text="Warm" variant="success" /> | Requires CW3K_INIT (0x9D) |
|
||||
|
||||
PID `0x0203` was added to the Linux kernel `dvb_usb_gp8psk` device table after v6.6.1.
|
||||
|
||||
## Endpoint Map
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Control endpoint | EP0 (default pipe, vendor requests) |
|
||||
| Bulk IN endpoint | EP2 (address 0x82) -- MPEG-2 transport stream |
|
||||
| Generic bulk CTRL endpoint | 0x01 (BCM4500 FW02 upload, Rev.1 only) |
|
||||
|
||||
## Streaming Properties
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| URB count | 7 |
|
||||
| URB buffer size | 8192 bytes each |
|
||||
| Stream type | USB_BULK |
|
||||
| FX2 controller type | CYPRESS_FX2 |
|
||||
|
||||
## EP2 Endpoint Configuration
|
||||
|
||||
```c title="EP2CFG Register (0xE610)"
|
||||
EP2CFG = 0xE2; // valid=1, dir=IN, type=BULK, size=512, buf=DOUBLE
|
||||
```
|
||||
|
||||
| Bit | Value | Meaning |
|
||||
|-----|-------|---------|
|
||||
| 7 (VALID) | 1 | Endpoint enabled |
|
||||
| 6 (DIR) | 1 | IN (device to host) |
|
||||
| 5:4 (TYPE) | 10 | Bulk transfer |
|
||||
| 3 (SIZE) | 0 | 512-byte packets |
|
||||
| 1:0 (BUF) | 10 | Double-buffered |
|
||||
|
||||
EP4, EP6, and EP8 are disabled (`&= ~bmVALID`). Only EP2 is used for data streaming.
|
||||
|
||||
## Warm Boot Behavior
|
||||
|
||||
The SkyWalker-1 (PID 0x0203) enumerates directly as a "warm" device. The DVB-USB framework skips firmware download when `cold_ids` is NULL. No host-side firmware files are required.
|
||||
|
||||
| Device | PID | Needs FW01? | Needs FW02? | Boot Source |
|
||||
|--------|-----|-------------|-------------|-------------|
|
||||
| Rev.1 Cold | 0x0200 | Yes | -- | RAM (empty) |
|
||||
| Rev.1 Warm | 0x0201 | No | Yes | RAM (FW01 loaded) |
|
||||
| Rev.2 | 0x0202 | No | No | EEPROM |
|
||||
| SkyWalker-1 | 0x0203 | No | No | EEPROM |
|
||||
| SkyWalker CW3K | 0x0206 | No | No | EEPROM |
|
||||
|
||||
<Aside type="note" title="Firmware Files Not Available">
|
||||
The firmware files `dvb-usb-gp8psk-01.fw` and `dvb-usb-gp8psk-02.fw` were never open-sourced or included in the `linux-firmware` package. They are only needed for the older Rev.1 hardware (PID 0x0200/0x0201), not for the SkyWalker-1.
|
||||
</Aside>
|
||||
|
||||
## USB Control Transfer Protocol
|
||||
|
||||
All vendor commands use USB control transfers with these common parameters:
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| bmRequestType | `USB_TYPE_VENDOR` (0x40 for OUT, 0xC0 for IN) |
|
||||
| Timeout | 2000 ms |
|
||||
| Retry | Up to 3 attempts for IN operations if partial data received |
|
||||
| Data buffer maximum | 80 bytes (kernel driver) |
|
||||
|
||||
See [Vendor Commands](/usb/vendor-commands/) for the complete command reference.
|
||||
|
||||
## Kernel Driver Modules
|
||||
|
||||
The Linux kernel uses two modules for the SkyWalker-1:
|
||||
|
||||
| Module | Function |
|
||||
|--------|----------|
|
||||
| `dvb_usb_gp8psk` | USB transport layer, device management, firmware loading |
|
||||
| `gp8psk_fe` | DVB frontend operations (demodulation, tuning, signal status) |
|
||||
|
||||
<Aside type="tip" title="Blacklisting for Development">
|
||||
When developing or testing custom firmware, blacklist both kernel modules to prevent the driver from racing with your test tools:
|
||||
|
||||
```
|
||||
# /etc/modprobe.d/blacklist-gp8psk.conf
|
||||
blacklist dvb_usb_gp8psk
|
||||
blacklist gp8psk_fe
|
||||
```
|
||||
|
||||
Then unload: `sudo modprobe -r dvb_usb_gp8psk gp8psk_fe`
|
||||
</Aside>
|
||||
---
|
||||
title: USB Interface
|
||||
description: USB VID/PID table, endpoint map, descriptors, and warm boot behavior for the SkyWalker-1.
|
||||
---
|
||||
|
||||
import { Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
## VID/PID Table
|
||||
|
||||
All Genpix products share USB Vendor ID `0x09C0`:
|
||||
|
||||
| PID | Product | State | Notes |
|
||||
|-----|---------|-------|-------|
|
||||
| 0x0200 | 8PSK-to-USB2 Rev.1 | <Badge text="Cold" variant="caution" /> | Requires FW01 upload to RAM |
|
||||
| 0x0201 | 8PSK-to-USB2 Rev.1 | <Badge text="Warm" variant="success" /> | Requires FW02 (BCM4500 firmware) |
|
||||
| 0x0202 | 8PSK-to-USB2 Rev.2 | <Badge text="Warm" variant="success" /> | Boots from EEPROM |
|
||||
| 0x0203 | **SkyWalker-1** | <Badge text="Warm" variant="success" /> | Boots from EEPROM |
|
||||
| 0x0204 | SkyWalker-1 (alternate) | <Badge text="Warm" variant="success" /> | Boots from EEPROM |
|
||||
| 0x0205 | SkyWalker-2 | -- | Not in kernel 6.16.5 |
|
||||
| 0x0206 | SkyWalker CW3K | <Badge text="Warm" variant="success" /> | Requires CW3K_INIT (0x9D) |
|
||||
|
||||
PID `0x0203` was added to the Linux kernel `dvb_usb_gp8psk` device table after v6.6.1.
|
||||
|
||||
## Endpoint Map
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Control endpoint | EP0 (default pipe, vendor requests) |
|
||||
| Bulk IN endpoint | EP2 (address 0x82) -- MPEG-2 transport stream |
|
||||
| Generic bulk CTRL endpoint | 0x01 (BCM4500 FW02 upload, Rev.1 only) |
|
||||
|
||||
## Streaming Properties
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| URB count | 7 |
|
||||
| URB buffer size | 8192 bytes each |
|
||||
| Stream type | USB_BULK |
|
||||
| FX2 controller type | CYPRESS_FX2 |
|
||||
|
||||
## EP2 Endpoint Configuration
|
||||
|
||||
```c title="EP2CFG Register (0xE610)"
|
||||
EP2CFG = 0xE2; // valid=1, dir=IN, type=BULK, size=512, buf=DOUBLE
|
||||
```
|
||||
|
||||
| Bit | Value | Meaning |
|
||||
|-----|-------|---------|
|
||||
| 7 (VALID) | 1 | Endpoint enabled |
|
||||
| 6 (DIR) | 1 | IN (device to host) |
|
||||
| 5:4 (TYPE) | 10 | Bulk transfer |
|
||||
| 3 (SIZE) | 0 | 512-byte packets |
|
||||
| 1:0 (BUF) | 10 | Double-buffered |
|
||||
|
||||
EP4, EP6, and EP8 are disabled (`&= ~bmVALID`). Only EP2 is used for data streaming.
|
||||
|
||||
## Warm Boot Behavior
|
||||
|
||||
The SkyWalker-1 (PID 0x0203) enumerates directly as a "warm" device. The DVB-USB framework skips firmware download when `cold_ids` is NULL. No host-side firmware files are required.
|
||||
|
||||
| Device | PID | Needs FW01? | Needs FW02? | Boot Source |
|
||||
|--------|-----|-------------|-------------|-------------|
|
||||
| Rev.1 Cold | 0x0200 | Yes | -- | RAM (empty) |
|
||||
| Rev.1 Warm | 0x0201 | No | Yes | RAM (FW01 loaded) |
|
||||
| Rev.2 | 0x0202 | No | No | EEPROM |
|
||||
| SkyWalker-1 | 0x0203 | No | No | EEPROM |
|
||||
| SkyWalker CW3K | 0x0206 | No | No | EEPROM |
|
||||
|
||||
<Aside type="note" title="Firmware Files Not Available">
|
||||
The firmware files `dvb-usb-gp8psk-01.fw` and `dvb-usb-gp8psk-02.fw` were never open-sourced or included in the `linux-firmware` package. They are only needed for the older Rev.1 hardware (PID 0x0200/0x0201), not for the SkyWalker-1.
|
||||
</Aside>
|
||||
|
||||
## USB Control Transfer Protocol
|
||||
|
||||
All vendor commands use USB control transfers with these common parameters:
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| bmRequestType | `USB_TYPE_VENDOR` (0x40 for OUT, 0xC0 for IN) |
|
||||
| Timeout | 2000 ms |
|
||||
| Retry | Up to 3 attempts for IN operations if partial data received |
|
||||
| Data buffer maximum | 80 bytes (kernel driver) |
|
||||
|
||||
See [Vendor Commands](/usb/vendor-commands/) for the complete command reference.
|
||||
|
||||
## Kernel Driver Modules
|
||||
|
||||
The Linux kernel uses two modules for the SkyWalker-1:
|
||||
|
||||
| Module | Function |
|
||||
|--------|----------|
|
||||
| `dvb_usb_gp8psk` | USB transport layer, device management, firmware loading |
|
||||
| `gp8psk_fe` | DVB frontend operations (demodulation, tuning, signal status) |
|
||||
|
||||
<Aside type="tip" title="Blacklisting for Development">
|
||||
When developing or testing custom firmware, blacklist both kernel modules to prevent the driver from racing with your test tools:
|
||||
|
||||
```
|
||||
# /etc/modprobe.d/blacklist-gp8psk.conf
|
||||
blacklist dvb_usb_gp8psk
|
||||
blacklist gp8psk_fe
|
||||
```
|
||||
|
||||
Then unload: `sudo modprobe -r dvb_usb_gp8psk gp8psk_fe`
|
||||
</Aside>
|
||||
|
|
|
|||
|
|
@ -1,191 +1,191 @@
|
|||
---
|
||||
title: Vendor Commands
|
||||
description: Complete USB vendor command reference with bRequest codes, parameters, and firmware version compatibility.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
All vendor commands use USB control transfers with `USB_TYPE_VENDOR` (bmRequestType bit 6 set). The vendor command dispatcher at CODE:0056 validates `bRequest` in the range 0x80--0x9D (30 entries for v2.06/v2.13) or 0x80--0x9A (27 entries for Rev.2) and dispatches via an indexed jump table at CODE:0076.
|
||||
|
||||
<Aside type="note" title="Status Key">
|
||||
<Badge text="OK" variant="success" /> = Implemented and functional. <Badge text="STALL" variant="danger" /> = Routes to stall handler (endpoint stall returned). <Badge text="Proto" variant="caution" /> = Partial/prototype implementation. <Badge text="N/A" variant="note" /> = Command index out of range (Rev.2 only supports 0x80--0x9A). <Badge text="Changed" variant="caution" /> = Implementation differs between versions.
|
||||
</Aside>
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Documented Commands">
|
||||
|
||||
## Stock Command Table (0x80--0x9D)
|
||||
|
||||
| Cmd | Name | Dir | wValue | wIndex | wLength | Purpose | v2.06 | Rev.2 | v2.13 |
|
||||
|-----|------|-----|--------|--------|---------|---------|-------|-------|-------|
|
||||
| 0x80 | GET_8PSK_CONFIG | IN | 0 | 0 | 1 | Read [configuration status byte](/usb/config-status/) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x81 | SET_8PSK_CONFIG | OUT | varies | 0 | 0 | Set config (reserved) | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> |
|
||||
| 0x82 | (reserved) | -- | -- | -- | -- | Reserved | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> |
|
||||
| 0x83 | I2C_WRITE | OUT | dev_addr | reg_addr | N | Write to I2C device | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x84 | I2C_READ | IN | dev_addr | reg_addr | N | Read from I2C device | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x85 | ARM_TRANSFER | OUT | 0/1 | 0 | 0 | Start (1) / stop (0) MPEG-2 stream | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x86 | TUNE_8PSK | OUT | 0 | 0 | 10 | Set [tuning parameters](/bcm4500/tuning-protocol/) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x87 | GET_SIGNAL_STRENGTH | IN | 0 | 0 | 6 | Read [SNR and diagnostics](/bcm4500/signal-monitoring/) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="Changed" variant="caution" /> |
|
||||
| 0x88 | LOAD_BCM4500 | OUT | 1 | 0 | 0 | Initiate BCM4500 FW download | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> |
|
||||
| 0x89 | BOOT_8PSK | IN | 0/1 | 0 | 1 | Power on (1) / off (0) demodulator | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x8A | START_INTERSIL | IN | 0/1 | 0 | 1 | Enable (1) / disable (0) LNB supply | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x8B | SET_LNB_VOLTAGE | OUT | 0/1 | 0 | 0 | 13V (0) or 18V (1) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x8C | SET_22KHZ_TONE | OUT | 0/1 | 0 | 0 | Tone off (0) or on (1) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x8D | SEND_DISEQC_COMMAND | OUT | msg[0] | 0 | len | DiSEqC message or tone burst | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x8E | SET_DVB_MODE | OUT | 1 | 0 | 0 | Enable DVB-S mode | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> |
|
||||
| 0x8F | SET_DN_SWITCH | OUT | cmd7bit | 0 | 0 | Legacy Dish Network switch protocol | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x90 | GET_SIGNAL_LOCK | IN | 0 | 0 | 1 | Read [signal lock status](/bcm4500/signal-monitoring/) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x92 | GET_FW_VERS | IN | 0 | 0 | 6 | Read firmware version + build date | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x93 | GET_SERIAL_NUMBER | IN | 0 | 0 | 4 | Read 4-byte serial from EEPROM | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x94 | USE_EXTRA_VOLT | OUT | 0/1 | 0 | 0 | Enable +1V LNB boost (14V/19V) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x95 | GET_FPGA_VERS | IN | 0 | 0 | 1 | Read EEPROM hardware/platform ID | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
|
||||
### Detailed Parameter Formats
|
||||
|
||||
**0x87 GET_SIGNAL_STRENGTH**: Returns 6 bytes. Bytes 0--1 contain a 16-bit SNR value (little-endian, dBu x 256 units). Bytes 2--5 are reserved/diagnostic BCM4500 registers. Version differences: v2.06 polls 3 registers (0xA2, 0xA8, 0xA4) up to 6 times; v2.13 consolidates to 1 register with a simplified poll.
|
||||
|
||||
**0x8D SEND_DISEQC_COMMAND**: When `wLength > 0`, the payload is a standard DiSEqC message (3--6 bytes) with `wValue` set to `msg[0]` (framing byte, typically 0xE0 or 0xE1). When `wLength == 0` and `wValue == 0`, tone burst A is sent. When `wLength == 0` and `wValue != 0`, tone burst B is sent.
|
||||
|
||||
**0x8F SET_DN_SWITCH**: `wValue` carries a 7-bit Dish Network switch command (LSB-first), bit-banged on GPIO P0.4 with specific timing. The 8th bit (0x80) of the original switch command selects LNB voltage and is sent separately via SET_LNB_VOLTAGE.
|
||||
|
||||
**0x92 GET_FW_VERS**: Returns 6 bytes of hardcoded constants:
|
||||
|
||||
```c title="GET_FW_VERS Response Format"
|
||||
Byte 0: version minor_minor (e.g., 0x04)
|
||||
Byte 1: version minor (e.g., 0x06)
|
||||
Byte 2: version major (e.g., 0x02)
|
||||
Byte 3: build day (e.g., 0x0D = 13)
|
||||
Byte 4: build month (e.g., 0x07 = July)
|
||||
Byte 5: build year - 2000 (e.g., 0x07 = 2007)
|
||||
|
||||
Full version = byte[2] << 16 | byte[1] << 8 | byte[0]
|
||||
Build date = (2000 + byte[5]) / byte[4] / byte[3]
|
||||
```
|
||||
|
||||
**0x93 GET_SERIAL_NUMBER**: Returns 4 bytes read from I2C EEPROM at device address 0x51 (7-bit), extracted at 8-bit intervals using a shift/rotate routine.
|
||||
|
||||
**0x94 USE_EXTRA_VOLT**: `wValue=1` writes 0x6A to XRAM 0xE0B6; `wValue=0` writes 0x62. The difference is bit 3 (0x08), which controls the voltage boost on the LNB power regulator.
|
||||
|
||||
**0x95 GET_FPGA_VERS**: Reads from I2C EEPROM at 0x51. Despite the name, there is no FPGA on the SkyWalker-1 -- this returns a hardware platform ID. v2.06 reads EEPROM offset 0x31 (2 bytes); v2.13/Rev.2 read offset 0x00 (1 byte).
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Debug / Internal Commands">
|
||||
|
||||
## Debug Commands (0x91, 0x96--0x98)
|
||||
|
||||
These commands are not used by any driver (Linux or Windows). They appear to be manufacturing/debug interfaces.
|
||||
|
||||
| Cmd | Name | Dir | wValue | wLength | Purpose | v2.06 | Rev.2 | v2.13 |
|
||||
|-----|------|-----|--------|---------|---------|-------|-------|-------|
|
||||
| 0x91 | I2C_ADDR_ADJUST | IN | 0=dec, 1=inc | 1 | Inc/dec internal IRAM counter | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x96 | SET_LNB_GPIO_MODE | OUT | 0/1 | 0 | Configure LNB GPIO output enables | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x97 | SET_GPIO_PINS | OUT | bitmap | 0 | Direct write to LNB GPIO pins | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x98 | GET_GPIO_STATUS | IN | 0 | 1 | Read LNB feedback GPIO pin | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
|
||||
### 0x91 I2C_ADDR_ADJUST
|
||||
|
||||
Increments (`wValue != 0`) or decrements (`wValue == 0`) an internal IRAM counter and returns its current value (1 byte). The counter lives at IRAM 0x66 (v2.06) or IRAM 0x18 (v2.13/Rev.2). Likely used for I2C address or tuner register index adjustment during development.
|
||||
|
||||
### 0x96 SET_LNB_GPIO_MODE
|
||||
|
||||
Configures GPIO output enable registers for the LNB voltage regulator hardware:
|
||||
|
||||
| Mode | v2.06/v2.13 | Rev.2 |
|
||||
|------|-------------|-------|
|
||||
| Default (wValue=0) | OEB=0xF0 | OEB=0xE7, OEA=0x9E |
|
||||
| Active (wValue=1) | IOB=(IOB & 0xF7) OR 0x06; OEB=0xFE | IOB.4 clear; P0.6, P0.0 set; OEA OR= 0x41 |
|
||||
|
||||
### 0x97 SET_GPIO_PINS
|
||||
|
||||
Direct GPIO pin write for LNB control:
|
||||
|
||||
| wValue Bit | v2.06/v2.13 Target | Rev.2 Target |
|
||||
|-----------|-------------------|-------------|
|
||||
| bit 1 | IOB.1 (Port B) | P0.6 (Port A) |
|
||||
| bit 2 | IOB.2 (Port B) | P0.0 (Port A) |
|
||||
| bit 3 | IOB.3 (Port B) | IOB.4 (Port B) |
|
||||
|
||||
### 0x98 GET_GPIO_STATUS
|
||||
|
||||
Returns 1 byte (0 or 1) from a single GPIO input pin -- likely an LNB overcurrent or power-good feedback signal:
|
||||
|
||||
| Version | Pin Read |
|
||||
|---------|----------|
|
||||
| v2.06/v2.13 | IOB.0 (Port B bit 0) |
|
||||
| Rev.2 | P0.5 (Port A bit 5) |
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Extended Commands (v2.13+)">
|
||||
|
||||
## Extended Commands (0x99--0x9D)
|
||||
|
||||
| Cmd | Name | Dir | wValue | wLength | Purpose | v2.06 | Rev.2 | v2.13 |
|
||||
|-----|------|-----|--------|---------|---------|-------|-------|-------|
|
||||
| 0x99 | GET_DEMOD_STATUS | IN | 0 | 1 | Read BCM4500 register 0xF9 | <Badge text="STALL" variant="danger" /> | <Badge text="Proto" variant="caution" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x9A | INIT_DEMOD | OUT | 0 | 0 | Trigger demod re-init (3 attempts) | <Badge text="STALL" variant="danger" /> | <Badge text="Proto" variant="caution" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x9B | (reserved) | -- | -- | -- | Reserved | <Badge text="STALL" variant="danger" /> | <Badge text="N/A" variant="note" /> | <Badge text="STALL" variant="danger" /> |
|
||||
| 0x9C | DELAY_COMMAND | OUT | delay | 0 | Host-controlled tuning delay + poll | <Badge text="STALL" variant="danger" /> | <Badge text="N/A" variant="note" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x9D | CW3K_INIT / SET_MODE_FLAG | OUT | 0/1 | 0 | CW3K init or conditional demod reset | <Badge text="OK" variant="success" /> | <Badge text="N/A" variant="note" /> | <Badge text="Changed" variant="caution" /> |
|
||||
|
||||
### Driver Usage Notes
|
||||
|
||||
- The Linux driver only sends LOAD_BCM4500 (0x88) for Rev.1 Warm (PID 0x0201). On SkyWalker-1, `bm8pskFW_Loaded` is already set and 0x88 routes to STALL.
|
||||
- The Linux driver only sends CW3K_INIT (0x9D) for SkyWalker CW3K (PID 0x0206).
|
||||
- Rev.2 supports only commands 0x80--0x9A (27 entries). Commands 0x9B--0x9D are out of range and produce undefined behavior.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Custom Firmware">
|
||||
|
||||
## Custom Firmware Commands (0xB0--0xB9)
|
||||
|
||||
Commands added in custom firmware v3.01.0--v3.02.0 for development, diagnostics, and signal monitoring:
|
||||
|
||||
| Cmd | Name | Dir | wValue | wIndex | wLength | Purpose |
|
||||
|-----|------|-----|--------|--------|---------|---------|
|
||||
| 0xB0 | SPECTRUM_SWEEP | OUT | 0 | 0 | 10 | Step through freq range, read SNR at each step |
|
||||
| 0xB1 | RAW_DEMOD_READ | IN | reg | 0 | 1 | Read BCM4500 indirect register |
|
||||
| 0xB2 | RAW_DEMOD_WRITE | OUT | reg | data | 0 | Write BCM4500 indirect register |
|
||||
| 0xB3 | BLIND_SCAN | OUT | 0 | 0 | 16 | Try symbol rates at given freq, report lock |
|
||||
| 0xB4 | I2C_BUS_SCAN | IN | 0 | 0 | 16 | Probe all 7-bit addresses, return 16-byte bitmap |
|
||||
| 0xB5 | I2C_RAW_READ | IN | addr7 | reg | N | Combined write-read from any I2C device |
|
||||
| 0xB6 | I2C_DIAG | IN | page | 0 | 8 | Step-by-step indirect register diagnostic |
|
||||
|
||||
### Parameter Formats
|
||||
|
||||
**0xB0 SPECTRUM_SWEEP**: 10-byte EP0 payload: `[start_freq(u32 LE kHz), stop_freq(u32 LE kHz), step_khz(u16 LE)]`. Programs BCM4500 at each frequency step, reads SNR, packs u16 LE results into EP2 bulk FIFO.
|
||||
|
||||
**0xB3 BLIND_SCAN**: 16-byte EP0 payload: `[freq_khz(u32 LE), sr_min(u32 LE sps), sr_max(u32 LE sps), sr_step(u32 LE sps)]`. Returns 8 bytes on lock `[freq_khz(4) + sr_locked(4)]` or 1 byte 0x00 if no lock found.
|
||||
|
||||
**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>
|
||||
|
||||
## Vendor Command Dispatch Mechanism
|
||||
|
||||
The dispatch logic at CODE:0056 (identical address across all stock versions):
|
||||
|
||||
```
|
||||
1. Check bmRequestType bit 6 -> vendor request?
|
||||
2. Read bRequest from SETUPDAT[1]
|
||||
3. Subtract 0x80 (command base offset)
|
||||
4. Compare against maximum: < 0x1E (v2.06/v2.13) or < 0x1B (Rev.2)
|
||||
5. If in range: double the index (2 bytes per AJMP) -> JMP @A+DPTR
|
||||
6. If out of range: route to STALL handler
|
||||
```
|
||||
|
||||
The jump table at CODE:0076 contains 2-byte AJMP instruction targets, one per command from 0x80 upward.
|
||||
---
|
||||
title: Vendor Commands
|
||||
description: Complete USB vendor command reference with bRequest codes, parameters, and firmware version compatibility.
|
||||
---
|
||||
|
||||
import { Tabs, TabItem, Badge, Aside } from '@astrojs/starlight/components';
|
||||
|
||||
All vendor commands use USB control transfers with `USB_TYPE_VENDOR` (bmRequestType bit 6 set). The vendor command dispatcher at CODE:0056 validates `bRequest` in the range 0x80--0x9D (30 entries for v2.06/v2.13) or 0x80--0x9A (27 entries for Rev.2) and dispatches via an indexed jump table at CODE:0076.
|
||||
|
||||
<Aside type="note" title="Status Key">
|
||||
<Badge text="OK" variant="success" /> = Implemented and functional. <Badge text="STALL" variant="danger" /> = Routes to stall handler (endpoint stall returned). <Badge text="Proto" variant="caution" /> = Partial/prototype implementation. <Badge text="N/A" variant="note" /> = Command index out of range (Rev.2 only supports 0x80--0x9A). <Badge text="Changed" variant="caution" /> = Implementation differs between versions.
|
||||
</Aside>
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Documented Commands">
|
||||
|
||||
## Stock Command Table (0x80--0x9D)
|
||||
|
||||
| Cmd | Name | Dir | wValue | wIndex | wLength | Purpose | v2.06 | Rev.2 | v2.13 |
|
||||
|-----|------|-----|--------|--------|---------|---------|-------|-------|-------|
|
||||
| 0x80 | GET_8PSK_CONFIG | IN | 0 | 0 | 1 | Read [configuration status byte](/usb/config-status/) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x81 | SET_8PSK_CONFIG | OUT | varies | 0 | 0 | Set config (reserved) | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> |
|
||||
| 0x82 | (reserved) | -- | -- | -- | -- | Reserved | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> |
|
||||
| 0x83 | I2C_WRITE | OUT | dev_addr | reg_addr | N | Write to I2C device | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x84 | I2C_READ | IN | dev_addr | reg_addr | N | Read from I2C device | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x85 | ARM_TRANSFER | OUT | 0/1 | 0 | 0 | Start (1) / stop (0) MPEG-2 stream | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x86 | TUNE_8PSK | OUT | 0 | 0 | 10 | Set [tuning parameters](/bcm4500/tuning-protocol/) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x87 | GET_SIGNAL_STRENGTH | IN | 0 | 0 | 6 | Read [SNR and diagnostics](/bcm4500/signal-monitoring/) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="Changed" variant="caution" /> |
|
||||
| 0x88 | LOAD_BCM4500 | OUT | 1 | 0 | 0 | Initiate BCM4500 FW download | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> |
|
||||
| 0x89 | BOOT_8PSK | IN | 0/1 | 0 | 1 | Power on (1) / off (0) demodulator | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x8A | START_INTERSIL | IN | 0/1 | 0 | 1 | Enable (1) / disable (0) LNB supply | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x8B | SET_LNB_VOLTAGE | OUT | 0/1 | 0 | 0 | 13V (0) or 18V (1) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x8C | SET_22KHZ_TONE | OUT | 0/1 | 0 | 0 | Tone off (0) or on (1) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x8D | SEND_DISEQC_COMMAND | OUT | msg[0] | 0 | len | DiSEqC message or tone burst | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x8E | SET_DVB_MODE | OUT | 1 | 0 | 0 | Enable DVB-S mode | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> | <Badge text="STALL" variant="danger" /> |
|
||||
| 0x8F | SET_DN_SWITCH | OUT | cmd7bit | 0 | 0 | Legacy Dish Network switch protocol | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x90 | GET_SIGNAL_LOCK | IN | 0 | 0 | 1 | Read [signal lock status](/bcm4500/signal-monitoring/) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x92 | GET_FW_VERS | IN | 0 | 0 | 6 | Read firmware version + build date | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x93 | GET_SERIAL_NUMBER | IN | 0 | 0 | 4 | Read 4-byte serial from EEPROM | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x94 | USE_EXTRA_VOLT | OUT | 0/1 | 0 | 0 | Enable +1V LNB boost (14V/19V) | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x95 | GET_FPGA_VERS | IN | 0 | 0 | 1 | Read EEPROM hardware/platform ID | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
|
||||
### Detailed Parameter Formats
|
||||
|
||||
**0x87 GET_SIGNAL_STRENGTH**: Returns 6 bytes. Bytes 0--1 contain a 16-bit SNR value (little-endian, dBu x 256 units). Bytes 2--5 are reserved/diagnostic BCM4500 registers. Version differences: v2.06 polls 3 registers (0xA2, 0xA8, 0xA4) up to 6 times; v2.13 consolidates to 1 register with a simplified poll.
|
||||
|
||||
**0x8D SEND_DISEQC_COMMAND**: When `wLength > 0`, the payload is a standard DiSEqC message (3--6 bytes) with `wValue` set to `msg[0]` (framing byte, typically 0xE0 or 0xE1). When `wLength == 0` and `wValue == 0`, tone burst A is sent. When `wLength == 0` and `wValue != 0`, tone burst B is sent.
|
||||
|
||||
**0x8F SET_DN_SWITCH**: `wValue` carries a 7-bit Dish Network switch command (LSB-first), bit-banged on GPIO P0.4 with specific timing. The 8th bit (0x80) of the original switch command selects LNB voltage and is sent separately via SET_LNB_VOLTAGE.
|
||||
|
||||
**0x92 GET_FW_VERS**: Returns 6 bytes of hardcoded constants:
|
||||
|
||||
```c title="GET_FW_VERS Response Format"
|
||||
Byte 0: version minor_minor (e.g., 0x04)
|
||||
Byte 1: version minor (e.g., 0x06)
|
||||
Byte 2: version major (e.g., 0x02)
|
||||
Byte 3: build day (e.g., 0x0D = 13)
|
||||
Byte 4: build month (e.g., 0x07 = July)
|
||||
Byte 5: build year - 2000 (e.g., 0x07 = 2007)
|
||||
|
||||
Full version = byte[2] << 16 | byte[1] << 8 | byte[0]
|
||||
Build date = (2000 + byte[5]) / byte[4] / byte[3]
|
||||
```
|
||||
|
||||
**0x93 GET_SERIAL_NUMBER**: Returns 4 bytes read from I2C EEPROM at device address 0x51 (7-bit), extracted at 8-bit intervals using a shift/rotate routine.
|
||||
|
||||
**0x94 USE_EXTRA_VOLT**: `wValue=1` writes 0x6A to XRAM 0xE0B6; `wValue=0` writes 0x62. The difference is bit 3 (0x08), which controls the voltage boost on the LNB power regulator.
|
||||
|
||||
**0x95 GET_FPGA_VERS**: Reads from I2C EEPROM at 0x51. Despite the name, there is no FPGA on the SkyWalker-1 -- this returns a hardware platform ID. v2.06 reads EEPROM offset 0x31 (2 bytes); v2.13/Rev.2 read offset 0x00 (1 byte).
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Debug / Internal Commands">
|
||||
|
||||
## Debug Commands (0x91, 0x96--0x98)
|
||||
|
||||
These commands are not used by any driver (Linux or Windows). They appear to be manufacturing/debug interfaces.
|
||||
|
||||
| Cmd | Name | Dir | wValue | wLength | Purpose | v2.06 | Rev.2 | v2.13 |
|
||||
|-----|------|-----|--------|---------|---------|-------|-------|-------|
|
||||
| 0x91 | I2C_ADDR_ADJUST | IN | 0=dec, 1=inc | 1 | Inc/dec internal IRAM counter | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x96 | SET_LNB_GPIO_MODE | OUT | 0/1 | 0 | Configure LNB GPIO output enables | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x97 | SET_GPIO_PINS | OUT | bitmap | 0 | Direct write to LNB GPIO pins | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x98 | GET_GPIO_STATUS | IN | 0 | 1 | Read LNB feedback GPIO pin | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> | <Badge text="OK" variant="success" /> |
|
||||
|
||||
### 0x91 I2C_ADDR_ADJUST
|
||||
|
||||
Increments (`wValue != 0`) or decrements (`wValue == 0`) an internal IRAM counter and returns its current value (1 byte). The counter lives at IRAM 0x66 (v2.06) or IRAM 0x18 (v2.13/Rev.2). Likely used for I2C address or tuner register index adjustment during development.
|
||||
|
||||
### 0x96 SET_LNB_GPIO_MODE
|
||||
|
||||
Configures GPIO output enable registers for the LNB voltage regulator hardware:
|
||||
|
||||
| Mode | v2.06/v2.13 | Rev.2 |
|
||||
|------|-------------|-------|
|
||||
| Default (wValue=0) | OEB=0xF0 | OEB=0xE7, OEA=0x9E |
|
||||
| Active (wValue=1) | IOB=(IOB & 0xF7) OR 0x06; OEB=0xFE | IOB.4 clear; P0.6, P0.0 set; OEA OR= 0x41 |
|
||||
|
||||
### 0x97 SET_GPIO_PINS
|
||||
|
||||
Direct GPIO pin write for LNB control:
|
||||
|
||||
| wValue Bit | v2.06/v2.13 Target | Rev.2 Target |
|
||||
|-----------|-------------------|-------------|
|
||||
| bit 1 | IOB.1 (Port B) | P0.6 (Port A) |
|
||||
| bit 2 | IOB.2 (Port B) | P0.0 (Port A) |
|
||||
| bit 3 | IOB.3 (Port B) | IOB.4 (Port B) |
|
||||
|
||||
### 0x98 GET_GPIO_STATUS
|
||||
|
||||
Returns 1 byte (0 or 1) from a single GPIO input pin -- likely an LNB overcurrent or power-good feedback signal:
|
||||
|
||||
| Version | Pin Read |
|
||||
|---------|----------|
|
||||
| v2.06/v2.13 | IOB.0 (Port B bit 0) |
|
||||
| Rev.2 | P0.5 (Port A bit 5) |
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Extended Commands (v2.13+)">
|
||||
|
||||
## Extended Commands (0x99--0x9D)
|
||||
|
||||
| Cmd | Name | Dir | wValue | wLength | Purpose | v2.06 | Rev.2 | v2.13 |
|
||||
|-----|------|-----|--------|---------|---------|-------|-------|-------|
|
||||
| 0x99 | GET_DEMOD_STATUS | IN | 0 | 1 | Read BCM4500 register 0xF9 | <Badge text="STALL" variant="danger" /> | <Badge text="Proto" variant="caution" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x9A | INIT_DEMOD | OUT | 0 | 0 | Trigger demod re-init (3 attempts) | <Badge text="STALL" variant="danger" /> | <Badge text="Proto" variant="caution" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x9B | (reserved) | -- | -- | -- | Reserved | <Badge text="STALL" variant="danger" /> | <Badge text="N/A" variant="note" /> | <Badge text="STALL" variant="danger" /> |
|
||||
| 0x9C | DELAY_COMMAND | OUT | delay | 0 | Host-controlled tuning delay + poll | <Badge text="STALL" variant="danger" /> | <Badge text="N/A" variant="note" /> | <Badge text="OK" variant="success" /> |
|
||||
| 0x9D | CW3K_INIT / SET_MODE_FLAG | OUT | 0/1 | 0 | CW3K init or conditional demod reset | <Badge text="OK" variant="success" /> | <Badge text="N/A" variant="note" /> | <Badge text="Changed" variant="caution" /> |
|
||||
|
||||
### Driver Usage Notes
|
||||
|
||||
- The Linux driver only sends LOAD_BCM4500 (0x88) for Rev.1 Warm (PID 0x0201). On SkyWalker-1, `bm8pskFW_Loaded` is already set and 0x88 routes to STALL.
|
||||
- The Linux driver only sends CW3K_INIT (0x9D) for SkyWalker CW3K (PID 0x0206).
|
||||
- Rev.2 supports only commands 0x80--0x9A (27 entries). Commands 0x9B--0x9D are out of range and produce undefined behavior.
|
||||
|
||||
</TabItem>
|
||||
<TabItem label="Custom Firmware">
|
||||
|
||||
## Custom Firmware Commands (0xB0--0xB9)
|
||||
|
||||
Commands added in custom firmware v3.01.0--v3.02.0 for development, diagnostics, and signal monitoring:
|
||||
|
||||
| Cmd | Name | Dir | wValue | wIndex | wLength | Purpose |
|
||||
|-----|------|-----|--------|--------|---------|---------|
|
||||
| 0xB0 | SPECTRUM_SWEEP | OUT | 0 | 0 | 10 | Step through freq range, read SNR at each step |
|
||||
| 0xB1 | RAW_DEMOD_READ | IN | reg | 0 | 1 | Read BCM4500 indirect register |
|
||||
| 0xB2 | RAW_DEMOD_WRITE | OUT | reg | data | 0 | Write BCM4500 indirect register |
|
||||
| 0xB3 | BLIND_SCAN | OUT | 0 | 0 | 16 | Try symbol rates at given freq, report lock |
|
||||
| 0xB4 | I2C_BUS_SCAN | IN | 0 | 0 | 16 | Probe all 7-bit addresses, return 16-byte bitmap |
|
||||
| 0xB5 | I2C_RAW_READ | IN | addr7 | reg | N | Combined write-read from any I2C device |
|
||||
| 0xB6 | I2C_DIAG | IN | page | 0 | 8 | Step-by-step indirect register diagnostic |
|
||||
|
||||
### Parameter Formats
|
||||
|
||||
**0xB0 SPECTRUM_SWEEP**: 10-byte EP0 payload: `[start_freq(u32 LE kHz), stop_freq(u32 LE kHz), step_khz(u16 LE)]`. Programs BCM4500 at each frequency step, reads SNR, packs u16 LE results into EP2 bulk FIFO.
|
||||
|
||||
**0xB3 BLIND_SCAN**: 16-byte EP0 payload: `[freq_khz(u32 LE), sr_min(u32 LE sps), sr_max(u32 LE sps), sr_step(u32 LE sps)]`. Returns 8 bytes on lock `[freq_khz(4) + sr_locked(4)]` or 1 byte 0x00 if no lock found.
|
||||
|
||||
**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>
|
||||
|
||||
## Vendor Command Dispatch Mechanism
|
||||
|
||||
The dispatch logic at CODE:0056 (identical address across all stock versions):
|
||||
|
||||
```
|
||||
1. Check bmRequestType bit 6 -> vendor request?
|
||||
2. Read bRequest from SETUPDAT[1]
|
||||
3. Subtract 0x80 (command base offset)
|
||||
4. Compare against maximum: < 0x1E (v2.06/v2.13) or < 0x1B (Rev.2)
|
||||
5. If in range: double the index (2 bytes per AJMP) -> JMP @A+DPTR
|
||||
6. If out of range: route to STALL handler
|
||||
```
|
||||
|
||||
The jump table at CODE:0076 contains 2-byte AJMP instruction targets, one per command from 0x80 upward.
|
||||
|
|
|
|||
|
|
@ -1,69 +1,69 @@
|
|||
/* Teal/Steel Engineering Theme for SkyWalker-1 Docs */
|
||||
|
||||
:root {
|
||||
--sl-color-accent-low: #0d3b3b;
|
||||
--sl-color-accent: #1a8a8a;
|
||||
--sl-color-accent-high: #b8e6e6;
|
||||
--sl-hue: 180;
|
||||
--sl-color-white: #f0f4f5;
|
||||
--sl-color-gray-1: #dde4e6;
|
||||
--sl-color-gray-2: #b8c4c8;
|
||||
--sl-color-gray-3: #7a8f96;
|
||||
--sl-color-gray-4: #4a5c63;
|
||||
--sl-color-gray-5: #2a3a40;
|
||||
--sl-color-gray-6: #1a2a30;
|
||||
--sl-color-black: #0e1519;
|
||||
}
|
||||
|
||||
:root[data-theme='light'] {
|
||||
--sl-color-accent-low: #d0f0f0;
|
||||
--sl-color-accent: #0d7377;
|
||||
--sl-color-accent-high: #043d3f;
|
||||
--sl-color-white: #1a2a30;
|
||||
--sl-color-gray-1: #2a3a40;
|
||||
--sl-color-gray-2: #4a5c63;
|
||||
--sl-color-gray-3: #7a8f96;
|
||||
--sl-color-gray-4: #b8c4c8;
|
||||
--sl-color-gray-5: #dde4e6;
|
||||
--sl-color-gray-6: #eef2f3;
|
||||
--sl-color-black: #f5f8f9;
|
||||
}
|
||||
|
||||
/* Ensure link colors use teal */
|
||||
:root {
|
||||
--sl-color-text-accent: var(--sl-color-accent);
|
||||
}
|
||||
|
||||
/* Table styling for hardware register tables */
|
||||
table {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: var(--sl-color-gray-6);
|
||||
}
|
||||
|
||||
/* Code block enhancement */
|
||||
.expressive-code {
|
||||
--ec-brdCol: var(--sl-color-gray-5);
|
||||
}
|
||||
|
||||
/* Badge color overrides for hardware status */
|
||||
.sl-badge--success {
|
||||
--sl-color-bg-badge: #0d5e3a;
|
||||
color: #a8e6cf;
|
||||
}
|
||||
|
||||
.sl-badge--danger {
|
||||
--sl-color-bg-badge: #5e1a0d;
|
||||
color: #e6b8a8;
|
||||
}
|
||||
|
||||
/* Mobile horizontal scroll for wide tables */
|
||||
@media (max-width: 50rem) {
|
||||
table {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
}
|
||||
/* Teal/Steel Engineering Theme for SkyWalker-1 Docs */
|
||||
|
||||
:root {
|
||||
--sl-color-accent-low: #0d3b3b;
|
||||
--sl-color-accent: #1a8a8a;
|
||||
--sl-color-accent-high: #b8e6e6;
|
||||
--sl-hue: 180;
|
||||
--sl-color-white: #f0f4f5;
|
||||
--sl-color-gray-1: #dde4e6;
|
||||
--sl-color-gray-2: #b8c4c8;
|
||||
--sl-color-gray-3: #7a8f96;
|
||||
--sl-color-gray-4: #4a5c63;
|
||||
--sl-color-gray-5: #2a3a40;
|
||||
--sl-color-gray-6: #1a2a30;
|
||||
--sl-color-black: #0e1519;
|
||||
}
|
||||
|
||||
:root[data-theme='light'] {
|
||||
--sl-color-accent-low: #d0f0f0;
|
||||
--sl-color-accent: #0d7377;
|
||||
--sl-color-accent-high: #043d3f;
|
||||
--sl-color-white: #1a2a30;
|
||||
--sl-color-gray-1: #2a3a40;
|
||||
--sl-color-gray-2: #4a5c63;
|
||||
--sl-color-gray-3: #7a8f96;
|
||||
--sl-color-gray-4: #b8c4c8;
|
||||
--sl-color-gray-5: #dde4e6;
|
||||
--sl-color-gray-6: #eef2f3;
|
||||
--sl-color-black: #f5f8f9;
|
||||
}
|
||||
|
||||
/* Ensure link colors use teal */
|
||||
:root {
|
||||
--sl-color-text-accent: var(--sl-color-accent);
|
||||
}
|
||||
|
||||
/* Table styling for hardware register tables */
|
||||
table {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: var(--sl-color-gray-6);
|
||||
}
|
||||
|
||||
/* Code block enhancement */
|
||||
.expressive-code {
|
||||
--ec-brdCol: var(--sl-color-gray-5);
|
||||
}
|
||||
|
||||
/* Badge color overrides for hardware status */
|
||||
.sl-badge--success {
|
||||
--sl-color-bg-badge: #0d5e3a;
|
||||
color: #a8e6cf;
|
||||
}
|
||||
|
||||
.sl-badge--danger {
|
||||
--sl-color-bg-badge: #5e1a0d;
|
||||
color: #e6b8a8;
|
||||
}
|
||||
|
||||
/* Mobile horizontal scroll for wide tables */
|
||||
@media (max-width: 50rem) {
|
||||
table {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue