Add alternative operating modes: spectrum, scan, monitor, lband, track

Firmware v3.02.0 adds three new vendor commands:
- 0xB7 SIGNAL_MONITOR: fast 8-byte combined signal read
- 0xB8 TUNE_MONITOR: tune + dwell + read in one round-trip
- 0xB9 MULTI_REG_READ: batch read up to 64 indirect registers

New tools/skywalker.py provides five modes that use the BCM4500's
AGC registers as a crude power detector across 950-2150 MHz IF,
even without demodulator lock:
- spectrum: sweep analyzer with ASCII/waterfall/matplotlib display
- scan: automated transponder scanner (sweep + peak detect + blind scan)
- monitor: real-time signal strength for dish alignment
- lband: direct input analyzer with L-band allocation annotations
- track: carrier/beacon tracker with CSV/JSON logging and drift detection

Extracts shared SkyWalker1 class and constants into skywalker_lib.py;
tune.py now imports from the shared library.
This commit is contained in:
Ryan Malloy 2026-02-12 17:29:00 -07:00
parent b21f4957f6
commit 23055f34ab
4 changed files with 1756 additions and 312 deletions

View file

@ -2,8 +2,9 @@
* Genpix SkyWalker-1 Custom Firmware
* For Cypress CY7C68013A (FX2LP) + Broadcom BCM4500 demodulator
*
* Stock-compatible vendor commands (0x80-0x94) plus new
* spectrum sweep, raw demod access, and blind scan commands (0xB0-0xB3).
* Stock-compatible vendor commands (0x80-0x94) plus custom
* spectrum sweep, raw demod access, blind scan (0xB0-0xB3),
* hardware diagnostics (0xB4-0xB6), and signal monitoring (0xB7-0xB9).
*
* SDCC + fx2lib toolchain. Loaded into FX2 RAM for testing.
*/
@ -53,6 +54,9 @@
#define RAW_DEMOD_READ 0xB1
#define RAW_DEMOD_WRITE 0xB2
#define BLIND_SCAN 0xB3
#define SIGNAL_MONITOR 0xB7
#define TUNE_MONITOR 0xB8
#define MULTI_REG_READ 0xB9
/* configuration status byte bits */
#define BM_STARTED 0x01
@ -85,6 +89,9 @@ volatile __bit got_sud;
static __xdata BYTE i2c_buf[16];
static __xdata BYTE i2c_rd[8];
/* TUNE_MONITOR result buffer: filled by OUT phase, returned by IN phase */
static __xdata BYTE tm_result[10];
/*
* BCM4500 register initialization data extracted from stock v2.06 firmware.
* FUN_CODE_0ddd writes these 3 blocks to BCM4500 indirect registers (page 0)
@ -1055,8 +1062,8 @@ BOOL handle_vendorcommand(BYTE cmd) {
/* 0x92: GET_FW_VERS -- return firmware version and build date */
case GET_FW_VERS:
EP0BUF[0] = 0x00; /* patch -> version 3.01.0 */
EP0BUF[1] = 0x01; /* minor */
EP0BUF[0] = 0x00; /* patch -> version 3.02.0 */
EP0BUF[1] = 0x02; /* minor */
EP0BUF[2] = 0x03; /* major */
EP0BUF[3] = 0x0C; /* day = 12 */
EP0BUF[4] = 0x02; /* month = 2 */
@ -1216,6 +1223,90 @@ BOOL handle_vendorcommand(BYTE cmd) {
return TRUE;
}
/* 0xB7: SIGNAL_MONITOR -- fast combined signal read (8 bytes)
* Returns SNR(2) + AGC1(2) + AGC2(2) + lock(1) + status(1)
* in a single USB transfer instead of 3 separate reads. */
case SIGNAL_MONITOR: {
BYTE sm_val;
/* SNR: indirect regs 0x00-0x01 */
bcm_indirect_read(0x00, &EP0BUF[0]);
bcm_indirect_read(0x01, &EP0BUF[1]);
/* AGC1: indirect regs 0x02-0x03 */
bcm_indirect_read(0x02, &EP0BUF[2]);
bcm_indirect_read(0x03, &EP0BUF[3]);
/* AGC2: indirect regs 0x04-0x05 */
bcm_indirect_read(0x04, &EP0BUF[4]);
bcm_indirect_read(0x05, &EP0BUF[5]);
/* Lock register (direct 0xA4) */
sm_val = 0;
bcm_direct_read(BCM_REG_LOCK, &sm_val);
EP0BUF[6] = sm_val;
/* Status register (direct 0xA2) */
sm_val = 0;
bcm_direct_read(BCM_REG_STATUS, &sm_val);
EP0BUF[7] = sm_val;
EP0BCH = 0;
EP0BCL = 8;
return TRUE;
}
/* 0xB8: TUNE_MONITOR -- tune + dwell + read in one round-trip
* OUT phase (0x40): receive 10-byte tune payload, tune, dwell, read signal
* IN phase (0xC0): return stored 10-byte result
* wValue = dwell time in ms (1-255) */
case TUNE_MONITOR: {
if (SETUPDAT[0] & 0x80) {
/* IN phase: return stored result from previous OUT phase */
BYTE ti;
for (ti = 0; ti < 10; ti++)
EP0BUF[ti] = tm_result[ti];
EP0BCH = 0;
EP0BCL = 10;
} else {
/* OUT phase: tune, dwell, measure */
BYTE dwell = (BYTE)wval;
EP0BCL = 0;
SYNCDELAY;
while (EP0CS & bmEPBUSY)
;
do_tune();
if (dwell > 0)
delay(dwell);
/* Read signal into result buffer */
bcm_indirect_read(0x00, &tm_result[0]);
bcm_indirect_read(0x01, &tm_result[1]);
bcm_indirect_read(0x02, &tm_result[2]);
bcm_indirect_read(0x03, &tm_result[3]);
bcm_indirect_read(0x04, &tm_result[4]);
bcm_indirect_read(0x05, &tm_result[5]);
tm_result[6] = 0;
bcm_direct_read(BCM_REG_LOCK, &tm_result[6]);
tm_result[7] = 0;
bcm_direct_read(BCM_REG_STATUS, &tm_result[7]);
tm_result[8] = dwell;
tm_result[9] = (BYTE)(wval >> 8);
}
return TRUE;
}
/* 0xB9: MULTI_REG_READ -- batch read of contiguous indirect registers
* wValue = start register, wIndex = count (1-64)
* Returns count bytes, one per register */
case MULTI_REG_READ: {
BYTE start_reg = (BYTE)wval;
BYTE count = (BYTE)SETUP_INDEX();
BYTE mi;
if (count == 0 || count > 64)
count = 1;
for (mi = 0; mi < count; mi++) {
EP0BUF[mi] = 0;
bcm_indirect_read(start_reg + mi, &EP0BUF[mi]);
}
EP0BCH = 0;
EP0BCL = count;
return TRUE;
}
default:
return FALSE;
}