V131R-260725:修正FSSI/FWD显示并优化8VSB频谱分析

修正FSSI/FWD标定、存储和LCD显示;增强8VSB场同步、MER分析及网页频谱展示;更新版本号与修改记录。
This commit is contained in:
2026-07-28 14:05:40 +08:00
parent 296c330197
commit 92b155499d
14 changed files with 1084 additions and 269 deletions
+1
View File
@@ -39,6 +39,7 @@ SVN_VER_NUM=122T4
SVN_VER_NUM=123 SVN_VER_NUM=123
SVN_VER_NUM=124 SVN_VER_NUM=124
SVN_VER_NUM=125 SVN_VER_NUM=125
SVN_VER_NUM=131R
OUT_FS_DIR := $(PWD)/../out/rootfs OUT_FS_DIR := $(PWD)/../out/rootfs
UPGARDE_IMX_FS_DIR := SMOD UPGARDE_IMX_FS_DIR := SMOD
+10 -1
View File
@@ -1107,7 +1107,16 @@ void adrv9009_init(void)
fpga_write_axi0(0x0b, 0x00, 1);//ADV9009_2 RX loopback to TX fpga_write_axi0(0x0b, 0x00, 1);//ADV9009_2 RX loopback to TX
#endif #endif
//Adrv9009_Ext_LOL_Cal_1(ADRV9009_0); //remove 250708 /* PR12 MER note (260725): TX1 LO leakage sits at -32 dBc at channel
* centre (N9320B; the exact +Rs/4 error line in the 8VSB demod) and caps
* raw MER at ~33 dB. Restoring this Ext LOL cal call was TRIED and made
* things WORSE (cal "succeeds" in <1 s because FBROUTE/TX signal is not
* up yet at this point, removes nothing, and its radioOff + TX1->ORx
* remapping degrades the ORx state: AGC jitter 0.5%->1.3%, MER -6 dB).
* That is presumably why it was removed on 250708. The demod-side
* narrowband canceller in iq_vsb.c handles the leak instead; a proper
* TX-side fix needs the cal wired after FBROUTE with mapping restore. */
//Adrv9009_Ext_LOL_Cal_1(ADRV9009_0);
//------------------------------------------------------ //------------------------------------------------------
//ad9009-tx1 //ad9009-tx1
+17
View File
@@ -989,6 +989,10 @@ void e2rom_save_ad3(S_ADVANCE3_PARA *ppara)
} }
p = orx_power_combine_db(orx_median5(cw), orx_median5(rms)); p = orx_power_combine_db(orx_median5(cw), orx_median5(rms));
ppara->power_ref = (uint16)(p * 10.0f + 0.5f); ppara->power_ref = (uint16)(p * 10.0f + 0.5f);
/* PR13: 分别记录两个原始量, 而不是只存合成标量 power_ref。 */
ppara->cal_fagc_cw = (uint8)((orx_median5(cw) >> 8) & 0xff);
ppara->cal_orx_pwr_hdb = (uint16)orx_pwr_halfdb(orx_median5(rms));
} }
(void)ref; (void)ref_sum; (void)ref; (void)ref_sum;
ppara->fwd_cal = 1; ppara->fwd_cal = 1;
@@ -1011,6 +1015,10 @@ void e2rom_save_ad3(S_ADVANCE3_PARA *ppara)
c256_bytewrite(EEPROM_AD3_POWER_REF_L,ppara->power_ref); c256_bytewrite(EEPROM_AD3_POWER_REF_L,ppara->power_ref);
log_printf(LOG_INFO, I_USER_CFG, "fwd_cal=%d, power_ref=%d\n", ppara->fwd_cal,ppara->power_ref); log_printf(LOG_INFO, I_USER_CFG, "fwd_cal=%d, power_ref=%d\n", ppara->fwd_cal,ppara->power_ref);
#endif #endif
/* PR13: cal_fagc_cw/cal_orx_pwr_hdb 按新枚举地址落盘, uint16 拆 H/L 两字节。 */
c256_bytewrite(EEPROM_AD3_CAL_FAGC_CW, ppara->cal_fagc_cw);
c256_bytewrite(EEPROM_AD3_CAL_ORX_PWR_H, ppara->cal_orx_pwr_hdb>>8);
c256_bytewrite(EEPROM_AD3_CAL_ORX_PWR_L, ppara->cal_orx_pwr_hdb);
} }
//printf("ppara->rc_cmd=%d\n", ppara->rc_cmd); //printf("ppara->rc_cmd=%d\n", ppara->rc_cmd);
c256_bytewrite(EEPROM_AD3_RC_CMD,ppara->rc_cmd); c256_bytewrite(EEPROM_AD3_RC_CMD,ppara->rc_cmd);
@@ -1585,6 +1593,13 @@ void e2rom_read_ad3(S_ADVANCE3_PARA *ppara)
if(ppara->mptv>1) if(ppara->mptv>1)
ppara->mptv = 0; ppara->mptv = 0;
#endif #endif
/* PR13: 读回 FWD cal 分别记录的 FAGC 控制字与 ORx 数字功率(0.5dB步数)。
旧配置文件缺少这两个 reg 项时, c256_byteread 对应 ini_getl 缺省值
DEF_REG_PARA(0xff), 单字节/双字节缺省合成正好是 0xFF/0xFFFF, 与
未标定判据天然一致, 无需额外的迁移代码。 */
ppara->cal_fagc_cw = c256_byteread(EEPROM_AD3_CAL_FAGC_CW);
ppara->cal_orx_pwr_hdb = (c256_byteread(EEPROM_AD3_CAL_ORX_PWR_H)<<8)|c256_byteread(EEPROM_AD3_CAL_ORX_PWR_L);
} }
void e2rom_read_ad4(S_ADVANCE4_PARA *ppara) void e2rom_read_ad4(S_ADVANCE4_PARA *ppara)
@@ -2939,6 +2954,8 @@ void e2rom_load_default_param(unsigned char menu)
atsc_para.ad3_para.mptv= 0; atsc_para.ad3_para.mptv= 0;
atsc_para.ad3_para.fwd_cal= 1;// 1:ok 0:cal atsc_para.ad3_para.fwd_cal= 1;// 1:ok 0:cal
atsc_para.ad3_para.power_ref = 0x7FFF;//0 atsc_para.ad3_para.power_ref = 0x7FFF;//0
atsc_para.ad3_para.cal_fagc_cw = 0xFF; // PR13: 未标定
atsc_para.ad3_para.cal_orx_pwr_hdb = 0xFFFF; // PR13: 未标定
atsc_para.ad3_para.cw = 0; atsc_para.ad3_para.cw = 0;
atsc_para.ad3_para.rst = 0; atsc_para.ad3_para.rst = 0;
//atsc_para.ad3_para.max_power = AD_MAX_POWER_DEFAULT; //atsc_para.ad3_para.max_power = AD_MAX_POWER_DEFAULT;
+37 -30
View File
@@ -29,36 +29,6 @@ uint16 orx_median5(const uint16 *v)
return a[2]; return a[2];
} }
uint8 orx_fssi_percent(float p_db, float ref_db, int is_vhf)
{
float pct = (p_db - ref_db) * 100.0f / ORX_FSSI_SPAN_DB;
int f;
if (pct < 0.0f) pct = 0.0f;
if (pct > 100.0f) pct = 100.0f;
f = (int)pct;
/* 以下两段是 EMV7 时代的显示微调, 按现状保留(见 spec 3.3)。
* 注意它们作用在线性映射之后, 会吃掉量程顶部 20%(VHF 38%),
* 故 ref_db 必须按微调后的最终显示值标定。 */
if (f > 20) f -= 20;
else f = 0;
if (is_vhf && f > 18)
f -= 18;
return (uint8)f;
}
uint8 orx_fwd_percent(float p_db, float ref_db)
{
float pct = 100.0f * powf(10.0f, (p_db - ref_db) / 10.0f);
if (pct < 0.0f) pct = 0.0f;
if (pct > 200.0f) pct = 200.0f;
return (uint8)pct;
}
uint8 orx_fssi_hysteresis(uint8 prev, uint8 now) uint8 orx_fssi_hysteresis(uint8 prev, uint8 now)
{ {
int d = (int)now - (int)prev; int d = (int)now - (int)prev;
@@ -67,3 +37,40 @@ uint8 orx_fssi_hysteresis(uint8 prev, uint8 now)
d = -d; d = -d;
return (d < 2) ? prev : now; return (d < 2) ? prev : now;
} }
uint8 orx_fssi_from_gain_index(uint8 gi, uint8 gi_min, uint8 gi_max)
{
float pct;
if (gi_max <= gi_min)
return 0; /* 防除零的退化入参 */
if (gi < gi_min) gi = gi_min;
if (gi > gi_max) gi = gi_max;
pct = (float)(gi_max - gi) * 100.0f / (float)(gi_max - gi_min);
return (uint8)(pct + 0.5f);
}
int16 orx_pwr_halfdb(uint16 rms)
{
float v = (float)rms;
if (v <= 0.0f)
v = 1.0f; /* 防 log10(0) 产生 -inf */
return (int16)floorf(20.0f * log10f(v) + 0.5f);
}
uint8 orx_fwd_percent_hdb(int16 delta_hdb)
{
float pct;
if (delta_hdb >= 6)
return 200;
if (delta_hdb <= -40)
return 0;
pct = 100.0f * powf(10.0f, (float)delta_hdb / 20.0f);
return (uint8)(pct + 0.5f);
}
+5 -1
View File
@@ -274,7 +274,11 @@ typedef enum
EEPROM_TSOIP2_MAC_M = EEPROM_TSOIP2_MAC_H+1, EEPROM_TSOIP2_MAC_M = EEPROM_TSOIP2_MAC_H+1,
EEPROM_TSOIP2_MAC_L = EEPROM_TSOIP2_MAC_M+1, EEPROM_TSOIP2_MAC_L = EEPROM_TSOIP2_MAC_M+1,
/* PR13 S4: FWD cal 分别记录 FAGC 控制字与 ORx 数字功率(0.5dB步数)。
只追加在整个枚举末尾,不改动以上任何已有项的数值,保持旧配置文件兼容。*/
EEPROM_AD3_CAL_FAGC_CW = EEPROM_TSOIP2_MAC_L+1,
EEPROM_AD3_CAL_ORX_PWR_H,
EEPROM_AD3_CAL_ORX_PWR_L,
EEPROM_END_ADDRESS = 0X7FFF, EEPROM_END_ADDRESS = 0X7FFF,
+6 -11
View File
@@ -139,16 +139,10 @@
#define CHECK_AGC_MIN_ENUM 100 #define CHECK_AGC_MIN_ENUM 100
#define CHECK_AGC_MAX_ENUM 256*10 #define CHECK_AGC_MAX_ENUM 256*10
/* FSSI 量程下沿(dB)。orx_power_combine_db() 的返回值只有相对意义, 由本常量吸收绝对基准。 /* FSSI 量程: gain index gi_max(弱)->0%, gi_min(强)->100%。
* * RTL 钳位下限 195(agc_spi_ctrl_gen.v)。真机扫功率标定后可收窄(Task13 V2)。 */
* 旧的 DPD_FSSI_MAX_VALUE/MIN_VALUE 已作废: 它们是线性功率域的阈值, 且 MAX=0x0400 #define FSSI_GI_MIN 195
* 恰好等于 ORx AGC 的设定点镜像 fagcpwrlevel*4, 等于把量程顶端钉死在工作点上, #define FSSI_GI_MAX 255
* 这正是 FSSI 恒读 75 的直接原因。
*
* 占位值 0.0f —— 必须由真机扫功率标定填入, 且要按 -20/-18 微调之后的最终显示值来定
* (微调吃掉量程顶部 20%, VHF 38%)。详见
* docs/superpowers/plans/2026-07-18-fssi-fwd-orx-power.md Task 6。 */
#define FSSI_REF_DB 0.0f
#define EEPROM_BACKUP_PARA_TYPE_NONE 0 #define EEPROM_BACKUP_PARA_TYPE_NONE 0
#define EEPROM_BACKUP_PARA_TYPE_PREF 1 //工厂默认参数备份 #define EEPROM_BACKUP_PARA_TYPE_PREF 1 //工厂默认参数备份
@@ -476,10 +470,11 @@ typedef struct
uint8 rst; uint8 rst;
uint8 mptv; //0:lptv 1:mptv //仅AW_ATSC_9X版本中使用 uint8 mptv; //0:lptv 1:mptv //仅AW_ATSC_9X版本中使用
uint8 reserverd1[1]; uint8 cal_fagc_cw; // PR13: FWD cal时刻FAGC控制字高字节(0.5dB/step); 复用原reserverd1[1]; 0xFF=未标定
uint16 max_power; uint16 max_power;
uint16 ofst; // function == fwd_cal uint16 ofst; // function == fwd_cal
uint16 cal_orx_pwr_hdb; // PR13: FWD cal时刻ORx数字功率, 归一化到0.5dB的步数(orx_pwr_halfdb输出); 0xFFFF=未标定
}S_ADVANCE3_PARA,*PS_ADVANCE3_PARA; }S_ADVANCE3_PARA,*PS_ADVANCE3_PARA;
typedef struct typedef struct
+12 -9
View File
@@ -3,9 +3,6 @@
#include "type.h" #include "type.h"
/* FSSI 量程: 0~100% 对应 20 dB 的 ORx 输入功率变化 */
#define ORX_FSSI_SPAN_DB 20.0f
/* 把 FAGC 增益字(粗档)与死区内残差(细档)合成为 dB 量纲的 ORx 输入功率。 /* 把 FAGC 增益字(粗档)与死区内残差(细档)合成为 dB 量纲的 ORx 输入功率。
* fagc_cw : seg1 0x36/0x37, agcctrlword。高字节随输入功率单调递增。 * fagc_cw : seg1 0x36/0x37, agcctrlword。高字节随输入功率单调递增。
* rms : seg1 0x03/0x04, ob_fb_rms_pwr, 线性功率计数。 * rms : seg1 0x03/0x04, ob_fb_rms_pwr, 线性功率计数。
@@ -15,14 +12,20 @@ float orx_power_combine_db(uint16 fagc_cw, uint16 rms);
/* 5 点中位数。用于剔除 temp_reg 竞争造成的孤立野值 —— 均值对此无免疫力。 */ /* 5 点中位数。用于剔除 temp_reg 竞争造成的孤立野值 —— 均值对此无免疫力。 */
uint16 orx_median5(const uint16 *v); uint16 orx_median5(const uint16 *v);
/* p_db -> FSSI 百分比。is_vhf 非 0 时额外套用 EMV7 遗留的 VHF 微调。 */
uint8 orx_fssi_percent(float p_db, float ref_db, int is_vhf);
/* p_db -> FWD 百分比(相对额定功率的真实功率比), 上限 200。 */
uint8 orx_fwd_percent(float p_db, float ref_db);
/* 显示滞回: |now-prev| < 2 时保持 prev。FAGC 一步 0.5 dB = FSSI 2.5%, /* 显示滞回: |now-prev| < 2 时保持 prev。FAGC 一步 0.5 dB = FSSI 2.5%,
* 无滞回时读数会在两个相邻百分比之间来回闪。状态由调用方持有。 */ * 无滞回时读数会在两个相邻百分比之间来回闪。状态由调用方持有。 */
uint8 orx_fssi_hysteresis(uint8 prev, uint8 now); uint8 orx_fssi_hysteresis(uint8 prev, uint8 now);
/* PR13: gain index -> FSSI%。gi_max(弱信号端)->0%, gi_min(强信号端)->100%,
* 线性映射,四舍五入,越界钳位。gi_min/gi_max 由调用方传入(标定后可收窄)。 */
uint8 orx_fssi_from_gain_index(uint8 gi, uint8 gi_min, uint8 gi_max);
/* PR13: ORx 数字功率 rms -> 0.5dB 步数: round(10*log10(rms)/0.5) = round(20*log10(rms))。
* rms==0 按 1 处理(防 -inf), 返回 0。 */
int16 orx_pwr_halfdb(uint16 rms);
/* PR13: Δ(0.5dB 步数) -> FWD%。100*10^(delta_hdb/20.0), 四舍五入;
* delta_hdb >= 6 返回 200; delta_hdb <= -40 返回 0。返回域 0..200。 */
uint8 orx_fwd_percent_hdb(int16 delta_hdb);
#endif /* ORX_POWER_H_ */ #endif /* ORX_POWER_H_ */
+165
View File
@@ -0,0 +1,165 @@
/* Auto-generated by Notes/gen_pr12_tables.py -- do not edit.
* Field-sync reference: segment sync + PN511 from pn511_63_gen.vhd,
* psi-shaped, 10 MSPS, no pilot; symbol 0 sits at index PR12_REF_PAD.
* Farrow: 16-tap LS fractional delay, band 0.31 fs, order-5 in mu;
* y(n) = x(n+mu) = sum_j h_j(mu) x(n + PR12_FAR_M0 + j). */
#ifndef PR12_TABLES_H
#define PR12_TABLES_H
#define PR12_REF_LEN 518
#define PR12_REF_PAD 20
#define PR12_REF_NSYM 515
#define PR12_FLD_SAMP 241972 /* round(260416*1430/1539) */
#define PR12_FAR_NTAP 16
#define PR12_FAR_M0 -7 /* first tap offset */
#define PR12_FAR_ORD 5
#define PR12_EL_GAMMA 0.835f
#define PR12_EL_EPS 1e-4f
#define PR12_RATIO_THR 8.0f
static const float pr12_ref_i[518] = {
-5.286845379e-03f, +2.622669190e-02f, -2.292391099e-02f, -2.251867019e-02f, +5.595326796e-02f, -1.249686629e-02f, -7.242719084e-02f, +7.641728222e-02f,
+4.717465118e-02f, -1.513598859e-01f, +4.096731171e-02f, +1.720457375e-01f, -2.178893685e-01f, -9.839237481e-02f, +4.078023136e-01f, -7.748710364e-02f,
-5.137212276e-01f, +2.712064385e-01f, +4.555361271e-01f, +7.123721838e-01f, +4.578077793e+00f, +8.568426132e+00f, +4.252341747e+00f, -4.359796524e+00f,
-4.041686535e+00f, +3.620400429e+00f, +4.001006603e+00f, -3.402234554e+00f, -4.280178070e+00f, +3.323069811e+00f, +5.367751598e+00f, -3.383987665e+00f,
-1.048472118e+01f, -5.266661167e+00f, +4.506075859e+00f, +4.627417088e+00f, -3.255465269e+00f, -4.725471020e+00f, +2.422198772e+00f, +6.220821857e+00f,
+4.335463047e+00f, +5.555976868e+00f, +7.040512085e+00f, +2.458845079e-01f, -6.081971169e+00f, -2.597144246e-01f, +6.028025150e+00f, -1.891986281e-01f,
-6.085367680e+00f, +6.764217615e-01f, +6.328075886e+00f, -6.270368695e-01f, -7.910936832e+00f, -5.841016293e+00f, -3.299148083e+00f, -5.300261497e+00f,
-5.678505898e+00f, -4.272875786e+00f, -5.541100502e+00f, -5.706527233e+00f, +3.174847364e-02f, +5.941425323e+00f, +6.097498894e+00f, +4.255494118e+00f,
+4.728756428e+00f, +5.474406242e+00f, +4.939614773e+00f, +4.804465294e+00f, +5.199867725e+00f, +4.865558147e+00f, +4.789291859e+00f, +5.669040680e+00f,
+5.122478485e+00f, +6.283135414e-01f, -5.549834728e+00f, -7.779552937e+00f, -3.568452597e+00f, +2.836106777e+00f, +5.630428791e+00f, +4.932396889e+00f,
+5.225176334e+00f, +5.842439651e+00f, +2.174484253e+00f, -4.298231602e+00f, -7.264725208e+00f, -4.897109032e+00f, +1.210394781e-02f, +5.191102505e+00f,
+7.575835228e+00f, +3.799711943e+00f, -2.825147867e+00f, -5.628582954e+00f, -4.868605137e+00f, -5.409148693e+00f, -5.913846970e+00f, -1.609635949e+00f,
+4.484533787e+00f, +3.583697796e+00f, -4.598292828e+00f, -9.421794891e+00f, -4.225284100e+00f, +3.879477262e+00f, +4.259531498e+00f, -1.918829560e+00f,
-5.871241570e+00f, -5.285588741e+00f, -5.215487480e+00f, -5.733654499e+00f, -1.894497156e+00f, +4.353964329e+00f, +3.881682158e+00f, -4.505227566e+00f,
-9.659042358e+00f, -4.257714272e+00f, +4.099705696e+00f, +4.160422325e+00f, -2.674061775e+00f, -4.754271030e+00f, +1.621445656e+00f, +7.313235760e+00f,
+5.966078758e+00f, +3.280944109e+00f, +4.742935658e+00f, +6.777920723e+00f, +4.850651264e+00f, +7.364175469e-02f, -4.861648560e+00f, -6.998380184e+00f,
-4.765580177e+00f, -2.975376606e+00f, -6.006412506e+00f, -7.804774284e+00f, -1.333216071e+00f, +5.493303299e+00f, +1.874249697e+00f, -5.082441807e+00f,
-2.275796890e+00f, +5.202341080e+00f, +3.221469879e+00f, -6.055469036e+00f, -8.825831413e+00f, -2.932251930e+00f, +2.819638729e+00f, +4.796855927e+00f,
+5.526096821e+00f, +5.523694038e+00f, +4.761342049e+00f, +2.887760639e+00f, -2.594623327e+00f, -8.926783562e+00f, -6.496881962e+00f, +3.415571213e+00f,
+5.570233345e+00f, -2.871439457e+00f, -5.237064838e+00f, +2.964607239e+00f, +5.101217747e+00f, -3.235982418e+00f, -5.293282986e+00f, +3.328021288e+00f,
+7.466854572e+00f, +4.277736664e+00f, +4.812504292e+00f, +6.449523926e+00f, +1.344947666e-01f, -6.362363338e+00f, -4.854081154e+00f, -4.147544384e+00f,
-7.523351669e+00f, -3.737147570e+00f, +5.280023575e+00f, +3.502180815e+00f, -5.233221054e+00f, -3.016454220e+00f, +5.451004028e+00f, +2.535094023e+00f,
-5.758422852e+00f, -2.248256445e+00f, +6.236683846e+00f, +2.634943724e+00f, -7.537533283e+00f, -8.477225304e+00f, -1.519219518e+00f, +3.309321642e+00f,
+3.501960516e+00f, -9.697697163e-01f, -8.220609665e+00f, -8.049039841e+00f, +2.048858404e+00f, +6.434696674e+00f, -1.778672695e+00f, -5.826475143e+00f,
+2.060273886e+00f, +5.386626720e+00f, -2.426478148e+00f, -5.056087971e+00f, +2.710771799e+00f, +4.959034443e+00f, -2.853733778e+00f, -5.510962963e+00f,
+2.735625029e+00f, +9.144712448e+00f, +5.719539642e+00f, +1.384751678e+00f, +4.984043598e+00f, +9.296606064e+00f, +3.766790867e+00f, -5.308373928e+00f,
-3.590387821e+00f, +4.762850761e+00f, +3.315530777e+00f, -4.919502258e+00f, -2.880096197e+00f, +5.276156425e+00f, +2.293892622e+00f, -5.624053478e+00f,
-1.736658812e+00f, +5.963533878e+00f, +1.324418306e+00f, -6.456923962e+00f, -1.643541217e+00f, +8.140644073e+00f, +8.113870621e+00f, +9.980998635e-01f,
-3.566998720e+00f, -4.972064495e+00f, -5.569894791e+00f, -5.050331116e+00f, -4.522177696e+00f, -5.122657776e+00f, -5.370749474e+00f, -4.702291489e+00f,
-4.786187649e+00f, -5.479587555e+00f, -4.987490177e+00f, -4.387750149e+00f, -5.281944275e+00f, -5.674399853e+00f, -4.338747501e+00f, -4.225120068e+00f,
-6.533868313e+00f, -5.853944302e+00f, +4.889330566e-01f, +5.231759548e+00f, +1.351539254e+00f, -6.076933861e+00f, -7.727037907e+00f, -3.181433678e+00f,
+2.370254040e+00f, +6.038099766e+00f, +6.374335289e+00f, +2.394463062e+00f, -3.640155792e+00f, -7.073258877e+00f, -5.598274708e+00f, -4.679959118e-01f,
+5.026762962e+00f, +7.003520966e+00f, +4.260151863e+00f, -1.167042851e+00f, -5.704484463e+00f, -6.605478287e+00f, -4.181149483e+00f, -3.407838345e+00f,
-6.647952080e+00f, -7.027618885e+00f, +1.354243457e-01f, +5.580024242e+00f, +1.083913326e+00f, -5.440810204e+00f, -5.644010544e+00f, -4.560598850e+00f,
-6.302078724e+00f, -4.198470116e+00f, +3.147631168e+00f, +4.891623497e+00f, -2.245373964e+00f, -7.314670086e+00f, -5.449603558e+00f, -3.992690563e+00f,
-5.351775169e+00f, -2.596492767e+00f, +5.099989891e+00f, +8.894201279e+00f, +3.879118919e+00f, -3.532342911e+00f, -4.107437611e+00f, +2.005908251e+00f,
+5.139887810e+00f, -8.100917339e-01f, -8.585423470e+00f, -7.381532669e+00f, +1.061635733e+00f, +5.181563854e+00f, +5.409368873e-01f, -4.743668556e+00f,
-2.349763870e+00f, +5.546813011e+00f, +9.148128510e+00f, +3.219218731e+00f, -4.638371944e+00f, -3.881040096e+00f, +3.127672911e+00f, +6.255072117e+00f,
+4.785263062e+00f, +4.902029514e+00f, +5.710810661e+00f, +4.108658314e+00f, +4.281614304e+00f, +7.426086903e+00f, +4.992904663e+00f, -3.438336611e+00f,
-4.819975853e+00f, +2.992818117e+00f, +4.880723953e+00f, -2.979014158e+00f, -5.293273926e+00f, +2.850750685e+00f, +8.166766167e+00f, +5.247386456e+00f,
+2.943013191e+00f, +5.328001022e+00f, +6.896398067e+00f, +4.198837280e+00f, -8.360862136e-01f, -5.583188057e+00f, -7.160663128e+00f, -3.284174204e+00f,
+3.149170876e+00f, +6.236937046e+00f, +5.248751640e+00f, +4.387199402e+00f, +5.086027145e+00f, +5.361176968e+00f, +4.826270580e+00f, +4.667729378e+00f,
+5.268001080e+00f, +5.673131466e+00f, +4.171278954e+00f, -6.390178800e-01f, -6.773134232e+00f, -7.886512280e+00f, -1.257387638e+00f, +5.153336525e+00f,
+2.604426384e+00f, -4.385585785e+00f, -6.320055962e+00f, -4.669809818e+00f, -5.287658215e+00f, -5.206599712e+00f, +2.000222206e-01f, +6.314717770e+00f,
+6.912867069e+00f, +3.035094738e+00f, -2.112722874e+00f, -6.760670185e+00f, -6.908548355e+00f, -6.948863268e-01f, +4.911715031e+00f, +2.511746168e+00f,
-4.607316494e+00f, -7.503297329e+00f, -4.771868229e+00f, -3.095408678e+00f, -5.616595745e+00f, -7.307530403e+00f, -3.053547144e+00f, +3.492771387e+00f,
+5.475720882e+00f, +4.519342899e+00f, +5.875143528e+00f, +6.140243053e+00f, +2.467835546e-01f, -5.232241631e+00f, -1.731349349e+00f, +5.608905792e+00f,
+7.169057846e+00f, +4.150032520e+00f, +3.683059454e+00f, +6.039212704e+00f, +6.243567944e+00f, +2.670942783e+00f, -2.872877359e+00f, -7.097218990e+00f,
-6.231664658e+00f, -1.503741592e-01f, +4.955874443e+00f, +2.449306011e+00f, -5.434510231e+00f, -8.936093330e+00f, -3.371172905e+00f, +4.052174568e+00f,
+3.769155264e+00f, -2.584196568e+00f, -4.792806625e+00f, +1.557254195e+00f, +8.481674194e+00f, +6.928576946e+00f, -8.196412325e-01f, -5.273729801e+00f,
-4.186799526e+00f, -4.697938919e+00f, -7.480693817e+00f, -4.237523079e+00f, +3.865867853e+00f, +4.321992874e+00f, -3.229115009e+00f, -4.724065781e+00f,
+2.672745943e+00f, +7.379656792e+00f, +5.292206764e+00f, +3.811321974e+00f, +5.264643192e+00f, +5.253469944e+00f, +4.689922333e+00f, +5.945960999e+00f,
+4.566935062e+00f, -1.731906891e+00f, -5.348304749e+00f, +1.438025199e-02f, +8.293551445e+00f, +8.257798195e+00f, -6.686376333e-01f, -5.900097370e+00f,
-2.927957475e-01f, +5.415609837e+00f, +7.744629383e-01f, -5.566294670e+00f, -1.548247457e+00f, +6.583863258e+00f, +7.438480854e+00f, +2.872689486e+00f,
-1.752848268e+00f, -6.419857025e+00f, -7.519037724e+00f, -8.445387483e-01f, +5.495405674e+00f, +1.719637036e+00f, -5.140930653e+00f, -2.589687347e+00f,
+5.451621056e+00f, +7.495484352e+00f, +4.236133099e+00f, +3.510769606e+00f, +5.858624458e+00f, +6.188675880e+00f, +3.935635567e+00f, +3.842289686e+00f,
+6.743275642e+00f, +6.467279911e+00f, -2.323660254e-02f, -5.173469067e+00f, -1.608212709e+00f, +5.328435421e+00f, +6.677968502e+00f, +4.370837212e+00f,
+4.488938808e+00f, +5.556053162e+00f, +4.733384609e+00f, +4.916380882e+00f, +6.127266884e+00f, +2.915187120e+00f, -3.996653080e+00f, -7.140460968e+00f,
-4.981493473e+00f, -8.638880253e-01f, +4.410533905e+00f, +8.298549652e+00f, +4.572174072e+00f, -3.811602116e+00f, -4.659410477e+00f, +2.948182344e+00f,
+5.199934483e+00f, -2.284938812e+00f, -7.955304623e+00f, -5.665179729e+00f, -9.488711953e-01f, +3.436431646e+00f, +7.733905792e+00f, +5.953035355e+00f,
-2.599661112e+00f, -5.613243580e+00f, +1.492211223e+00f, +6.403737068e+00f, +4.454494476e+00f, +4.981077671e+00f, +7.328737736e+00f, +1.647473216e+00f,
-5.813932419e+00f, -1.744877577e+00f, +5.670609951e+00f, +1.614241362e+00f, -5.796778679e+00f, -1.940005302e+00f, +6.501641750e+00f, +6.917208195e+00f,
+3.734516621e+00f, +4.754111767e+00f, +5.306764603e+00f, +1.246587873e+00f, -1.498366952e+00f, -7.486955822e-02f, +8.691741824e-01f, -2.277516574e-01f,
-4.758317173e-01f, +2.959984243e-01f, +2.112522423e-01f, -2.588739395e-01f, -7.934743538e-03f, +1.564843208e-01f, -5.883365124e-02f, -6.599611044e-02f,
+6.566952914e-02f, +2.375115454e-02f, -4.500814527e-02f, -3.892236389e-03f, +2.180625498e-02f, +8.140335791e-03f,
};
static const float pr12_ref_q[518] = {
+5.869438406e-03f, -3.864964237e-03f, +5.282910075e-03f, +1.896494813e-02f, -2.216342650e-02f, -2.693448775e-02f, +6.109601632e-02f, +7.630409673e-03f,
-9.612223506e-02f, +9.349839389e-02f, +7.895175368e-02f, -2.149498463e-01f, +1.186294202e-02f, +3.264600635e-01f, -1.905366182e-01f, -4.027945995e-01f,
+4.757204354e-01f, +3.853519559e-01f, -9.701238871e-01f, -2.457324117e-01f, +2.996877670e+00f, +5.075122356e+00f, +4.899847031e+00f, +5.221782207e+00f,
+6.049976349e+00f, +3.435424566e+00f, -2.712151051e+00f, -5.045851231e+00f, +1.488410115e+00f, +9.300146103e+00f, +7.063120365e+00f, -2.550311565e+00f,
-5.405889988e+00f, +1.398804665e+00f, +5.007237911e+00f, -8.797078133e-01f, -5.159753799e+00f, +3.757696450e-01f, +5.991768360e+00f, +4.947880805e-01f,
-9.123137474e+00f, -8.406595230e+00f, +1.549904346e+00f, +5.929969311e+00f, -4.224525392e-01f, -5.351121426e+00f, -1.400465667e-01f, +5.468637943e+00f,
+9.619093537e-01f, -6.541330814e+00f, -6.615937710e+00f, -3.709298372e+00f, -4.732099056e+00f, -5.892572403e+00f, -4.457208157e+00f, -4.785952091e+00f,
-6.057873726e+00f, -4.141914368e+00f, -3.832392693e+00f, -7.579329967e+00f, -5.557628632e+00f, +3.557590246e+00f, +5.041585445e+00f, -3.232571363e+00f,
-4.711908340e+00f, +3.244400263e+00f, +4.651019573e+00f, -3.263393879e+00f, -5.236587524e+00f, +3.080776215e+00f, +9.170791626e+00f, +5.561472416e+00f,
-1.463051677e+00f, -5.049890518e+00f, -5.545057774e+00f, -4.470587730e+00f, -2.233333439e-01f, +6.867622852e+00f, +8.898497581e+00f, +9.660382867e-01f,
-6.269678116e+00f, -1.267981172e+00f, +5.853948593e+00f, +1.004444122e+00f, -5.911718845e+00f, -4.536268711e-01f, +6.041092873e+00f, -6.304778159e-02f,
-6.248296738e+00f, +1.706905514e-01f, +7.308576584e+00f, +5.483821869e+00f, +4.111844540e+00f, +6.389967442e+00f, +2.535488605e+00f, -5.006493092e+00f,
-3.552442789e+00f, +4.888850212e+00f, +7.601017952e+00f, +4.337491989e+00f, +3.733379841e+00f, +5.729793072e+00f, +5.645592690e+00f, +4.346262932e+00f,
+4.624346256e+00f, +5.605614662e+00f, +5.266516209e+00f, +4.328843594e+00f, +4.728507519e+00f, +6.104721069e+00f, +5.372970104e+00f, +5.797862411e-01f,
-5.076183319e+00f, -6.571697235e+00f, -4.614058971e+00f, -3.919451237e+00f, -5.703743458e+00f, -6.268342972e+00f, -2.998461008e+00f, +2.657361746e+00f,
+6.948101997e+00f, +6.259880543e+00f, +9.798646569e-01f, -4.509745598e+00f, -6.704392433e+00f, -4.969738960e+00f, -2.218634263e-02f, +5.749720573e+00f,
+7.631314278e+00f, +3.077658415e+00f, -3.289816380e+00f, -4.290326595e+00f, +1.545421004e+00f, +7.886805058e+00f, +7.098558426e+00f, -1.958397627e-01f,
-5.267331600e+00f, -4.551052094e+00f, -4.453574181e+00f, -7.234088898e+00f, -4.776355267e+00f, +3.312515974e+00f, +4.965375423e+00f, -2.329998493e+00f,
-6.450175285e+00f, -4.483142376e+00f, -5.314620018e+00f, -6.832696438e+00f, -5.618709922e-01f, +5.772533894e+00f, +1.040605783e+00f, -5.752953529e+00f,
-1.482399464e+00f, +6.382650852e+00f, +6.480722904e+00f, +4.038126945e+00f, +5.346240520e+00f, +4.291225433e+00f, -2.483518362e+00f, -7.317963123e+00f,
-5.747078419e+00f, -3.257535458e+00f, -4.684683800e+00f, -7.032680035e+00f, -5.035443306e+00f, +9.085188508e-01f, +5.316732883e+00f, +5.630297661e+00f,
+4.783836365e+00f, +5.018668175e+00f, +4.984247684e+00f, +4.497394085e+00f, +5.660246849e+00f, +6.033622265e+00f, +1.579977989e+00f, -4.462995052e+00f,
-6.231894970e+00f, -4.905734539e+00f, -4.927115917e+00f, -4.926993847e+00f, -3.356504738e-01f, +6.598392487e+00f, +8.064378738e+00f, +1.976453900e+00f,
-4.385411263e+00f, -3.462033272e+00f, +3.042626619e+00f, +7.410283089e+00f, +5.735551834e+00f, +5.103057623e-01f, -4.730520248e+00f, -7.322902679e+00f,
-4.579544067e+00f, +2.100849867e+00f, +5.114673138e+00f, -1.676275581e-01f, -7.526288033e+00f, -7.656936169e+00f, -8.202375174e-01f, +4.631120205e+00f,
+2.729104519e+00f, -3.827840567e+00f, -7.310193539e+00f, -5.203807831e+00f, -3.105288506e+00f, -5.326206684e+00f, -7.396425247e+00f, -3.512630701e+00f,
+3.169657946e+00f, +4.376697063e+00f, -1.577363253e+00f, -7.192517281e+00f, -6.423156261e+00f, -3.025142908e+00f, -4.055246353e+00f, -7.762628555e+00f,
-5.550012112e+00f, +2.378599405e+00f, +4.976176739e+00f, -1.177649021e+00f, -5.261970520e+00f, -8.998028189e-02f, +7.337253571e+00f, +7.611535072e+00f,
+1.444417119e+00f, -4.068389893e+00f, -5.617377758e+00f, -5.008737564e+00f, -4.815996647e+00f, -5.164487362e+00f, -5.349601269e+00f, -4.616428375e+00f,
-7.359210849e-01f, +6.165390015e+00f, +8.861633301e+00f, +2.073078632e+00f, -5.536868572e+00f, -2.477769136e+00f, +4.855650902e+00f, +2.655533791e+00f,
-4.747497559e+00f, -3.260124207e+00f, +4.962732792e+00f, +7.417152405e+00f, +4.418032169e+00f, +4.373805523e+00f, +5.163011074e+00f, -1.886837743e-02f,
-7.573918819e+00f, -7.913840294e+00f, -7.333342433e-01f, +4.665238380e+00f, +2.459519863e+00f, -3.590470076e+00f, -6.832385063e+00f, -5.473016262e+00f,
-3.652323723e+00f, -4.867481709e+00f, -6.291730881e+00f, -4.815759659e+00f, -3.514988661e+00f, -5.761462688e+00f, -7.121630192e+00f, -2.261933804e+00f,
+4.261232376e+00f, +5.432725430e+00f, +4.319972038e+00f, +6.353998184e+00f, +5.946374416e+00f, -1.281328321e+00f, -5.515324593e+00f, +5.200773478e-01f,
+5.851861954e+00f, +2.832155824e-01f, -7.623353004e+00f, -7.145099163e+00f, -1.634073496e+00f, +2.895782948e+00f, +6.391798019e+00f, +6.547569275e+00f,
+6.896942854e-01f, -5.347162247e+00f, -5.557811737e+00f, -4.388189316e+00f, -6.122395039e+00f, -4.883256435e+00f, +1.853920937e+00f, +6.395469666e+00f,
+5.226247311e+00f, +4.393579483e+00f, +5.657284737e+00f, +4.766990662e+00f, +3.965030670e+00f, +6.508349419e+00f, +6.150150776e+00f, -1.183208585e+00f,
-5.596522331e+00f, -1.153272167e-01f, +6.266455173e+00f, +5.724645138e+00f, +4.400028229e+00f, +5.971824169e+00f, +3.620223284e+00f, -3.377559423e+00f,
-6.707892418e+00f, -4.888806343e+00f, -4.725553989e+00f, -5.631706715e+00f, -1.488229394e+00f, +5.612533569e+00f, +7.745710373e+00f, +3.611930609e+00f,
-1.776875377e+00f, -5.430897236e+00f, -6.274984837e+00f, -4.460794449e+00f, -3.754939556e+00f, -6.244093418e+00f, -6.502477169e+00f, -6.398582458e-01f,
+5.122278690e+00f, +5.313859940e+00f, +4.587067127e+00f, +6.274487495e+00f, +4.714573383e+00f, -1.855032921e+00f, -5.943632126e+00f, -5.066284657e+00f,
-5.044463158e+00f, -6.145793438e+00f, -2.188540220e+00f, +4.342884064e+00f, +3.884256840e+00f, -4.083222866e+00f, -8.956104279e+00f, -4.821990490e+00f,
+2.265810728e+00f, +5.046252251e+00f, +4.603657246e+00f, +5.436552525e+00f, +6.330294132e+00f, +2.975249529e+00f, -3.261811972e+00f, -6.071155548e+00f,
-5.088329792e+00f, -5.086641312e+00f, -5.392347336e+00f, -1.291469216e+00f, +5.141676903e+00f, +7.135626316e+00f, +4.437549114e+00f, +3.316901207e+00f,
+5.999105453e+00f, +7.003137112e+00f, +2.294710875e+00f, -3.879499912e+00f, -3.982849360e+00f, +2.808799982e+00f, +8.389288902e+00f, +5.918736458e+00f,
-1.333715677e+00f, -4.885122299e+00f, -1.131701708e+00f, +5.652431488e+00f, +8.252597809e+00f, +3.241284370e+00f, -3.913990974e+00f, -4.280696869e+00f,
+2.360064745e+00f, +7.433403969e+00f, +6.012746811e+00f, +9.911581874e-01f, -4.048046112e+00f, -6.638183117e+00f, -5.365204811e+00f, -3.260301352e+00f,
-5.126259327e+00f, -7.689192772e+00f, -3.429510593e+00f, +4.136342049e+00f, +3.850200176e+00f, -3.340889931e+00f, -4.660038948e+00f, +2.877918243e+00f,
+8.075686455e+00f, +5.450004101e+00f, +2.707311869e+00f, +5.135261536e+00f, +7.566888809e+00f, +4.122244835e+00f, -2.538930893e+00f, -4.679688931e+00f,
+5.734983087e-01f, +7.099006176e+00f, +7.325724125e+00f, +1.380027890e+00f, -4.077738762e+00f, -5.587203979e+00f, -5.223392487e+00f, -4.890740871e+00f,
-4.794408798e+00f, -5.015765190e+00f, -5.287222862e+00f, -5.320441246e+00f, -4.182018280e+00f, +3.911374509e-01f, +7.185988426e+00f, +8.236141205e+00f,
+4.459635019e-01f, -5.608479023e+00f, -1.304879904e+00f, +4.958740711e+00f, +2.258851051e+00f, -4.935779572e+00f, -6.918431282e+00f, -4.709735870e+00f,
-4.525074482e+00f, -4.759288788e+00f, +2.160560340e-01f, +7.372189045e+00f, +7.888133526e+00f, +9.381754398e-01f, -4.731398582e+00f, -4.542614937e+00f,
-4.383237362e+00f, -7.059480667e+00f, -5.530285835e+00f, +2.117481709e+00f, +5.073149204e+00f, -1.292899370e+00f, -5.240953445e+00f, +9.338489771e-01f,
+6.030634403e+00f, -2.297547311e-01f, -9.123252869e+00f, -7.665493488e+00f, +1.285036087e+00f, +5.229562759e+00f, +6.809653044e-01f, -4.693953037e+00f,
-5.729224205e+00f, -5.081575871e+00f, -5.519450188e+00f, -4.294838905e+00f, +1.164750338e+00f, +6.416083813e+00f, +6.373582840e+00f, +3.842878342e+00f,
+4.150497913e+00f, +6.247258186e+00f, +5.561135292e+00f, +3.419122934e+00f, +4.725263119e+00f, +7.438380241e+00f, +4.616515636e+00f, -2.741395950e+00f,
-4.682191849e+00f, +1.559204698e+00f, +5.455200195e+00f, -5.542048812e-01f, -8.409879684e+00f, -7.421031475e+00f, +3.870444596e-01f, +5.039210320e+00f,
+1.806122661e+00f, -4.662113667e+00f, -7.325479031e+00f, -4.562591553e+00f, +7.602491975e-01f, +5.618648052e+00f, +7.304486752e+00f, +3.339818478e+00f,
-3.315778017e+00f, -6.015509605e+00f, -4.711754799e+00f, -4.905991077e+00f, -5.780904293e+00f, -4.143216133e+00f, -4.123214722e+00f, -7.374801636e+00f,
-5.309611797e+00f, +3.193021774e+00f, +5.045397758e+00f, -2.600694656e+00f, -5.192117214e+00f, +2.049941778e+00f, +7.004919052e+00f, +5.118412971e+00f,
+4.644896030e+00f, +5.939411163e+00f, +1.370150208e+00f, -5.221024036e+00f, -2.934312582e+00f, +5.795453072e+00f, +8.879312515e+00f, +3.227740526e+00f,
-3.314112186e+00f, -4.507175446e+00f, -1.718620658e+00f, +6.608119011e-01f, +5.278163552e-01f, -4.337949455e-01f, -2.342043370e-01f, +3.436961770e-01f,
+5.513845384e-02f, -2.583805621e-01f, +5.014236271e-02f, +1.642296761e-01f, -9.587661922e-02f, -7.490279526e-02f, +9.300916642e-02f, +3.940804303e-02f,
-8.036983013e-02f, +1.734811999e-02f, +3.178612143e-02f, -3.957481310e-02f, +2.224531956e-02f, +2.830892988e-02f,
};
static const float pr12_far_c[PR12_FAR_ORD + 1][PR12_FAR_NTAP] = {
{+3.354018923e-08f, -2.505644105e-07f, +1.104461940e-06f, -3.724122809e-06f, +1.061059506e-05f, -2.440078198e-05f, +4.150228296e-05f, +9.999502269e-01f, +4.158691479e-05f, -2.454241977e-05f, +1.076731063e-05f, -3.858284486e-06f, +1.196149342e-06f, -2.998202154e-07f, +5.301984963e-08f, -4.667395474e-09f},
{-7.580488425e-04f, +4.985623825e-03f, -1.924680831e-02f, +5.634386935e-02f, -1.402298380e-01f, +3.272344032e-01f, -8.695707176e-01f, -7.005224455e-02f, +1.001311010e+00f, -4.365161241e-01f, +2.196329758e-01f, -1.061948593e-01f, +4.565654628e-02f, -1.630237750e-02f, +4.361073328e-03f, -6.803556814e-04f},
{+1.401812742e-04f, -1.064217703e-03f, +4.817691726e-03f, -1.707908512e-02f, +5.452774152e-02f, -1.827721574e-01f, +9.247815829e-01f, -1.566794272e+00f, +9.251259406e-01f, -1.833484661e-01f, +5.516540006e-02f, -1.762497434e-02f, +5.190757767e-03f, -1.264634205e-03f, +2.194418918e-04f, -1.899112467e-05f},
{+1.167621393e-03f, -7.632226111e-03f, +2.918189582e-02f, -8.397686066e-02f, +2.014740503e-01f, -4.212788613e-01f, +4.377523260e-01f, +1.092312465e-01f, -6.431726724e-01f, +5.916799479e-01f, -3.252859819e-01f, +1.617086422e-01f, -7.036214090e-02f, +2.527824329e-02f, -6.785754805e-03f, +1.060866781e-03f},
{-1.896077850e-04f, +1.434800163e-03f, -6.460685625e-03f, +2.267605290e-02f, -7.079243114e-02f, +2.209540201e-01f, -4.905859206e-01f, +6.460510253e-01f, -4.910542229e-01f, +2.217377594e-01f, -7.165960197e-02f, +2.341842390e-02f, -6.968029240e-03f, +1.707352531e-03f, -2.973966586e-04f, +2.582659581e-05f},
{-4.590166308e-04f, +2.957080610e-03f, -1.104885925e-02f, +3.048603819e-02f, -6.654401394e-02f, +1.004319663e-01f, -7.113529233e-02f, -4.417948814e-02f, +1.542192637e-01f, -1.693521062e-01f, +1.166207817e-01f, -6.192530518e-02f, +2.770455262e-02f, -1.009415935e-02f, +2.731317519e-03f, -4.290764122e-04f},
};
#endif /* PR12_TABLES_H */
+686 -24
View File
@@ -231,15 +231,20 @@ static void build_bank(void)
} }
/* ------------------------------------------------- resample + derotate */ /* ------------------------------------------------- resample + derotate */
/* c_k = y_k * j^k for k in [k0, k0+nsym), with tau0 = q/1539. /* c_k = y_k * j^k for k in [k0, k0+nsym), sample position of symbol k being
* Returns 0 if the window is too short. */ * s_ofs + (k*1430 + q)/1539, reading from sxi/sxq (xi/xq or the PR12
static int resample_syms(int k0, int nsym, int q) * Farrow-compensated copy). k0 may be negative when the grid is anchored at
* a field-sync peak (s_ofs = d0); (k0+k)&3 stays a correct mod-4 on two's
* complement. Returns 0 if a kernel would run off the window. */
static int resample_syms(const float *sxi, const float *sxq, int s_ofs,
int k0, int nsym, int q)
{ {
int k, num, n; int k, num, n;
/* exact integer recurrence: sample position of symbol k is (k*1430 + q)/1539 */ /* exact integer recurrence, floor division for possibly-negative t */
long long t = (long long)k0 * SYM_NUM + q; long long t = (long long)k0 * SYM_NUM + q;
n = (int)(t / SYM_DEN); long long nf = (t >= 0) ? t / SYM_DEN : -((-t + SYM_DEN - 1) / SYM_DEN);
num = (int)(t % SYM_DEN); n = (int)nf + s_ofs;
num = (int)(t - nf * SYM_DEN);
for (k = 0; k < nsym; k++) { for (k = 0; k < nsym; k++) {
const float *h = bank[num]; const float *h = bank[num];
@@ -251,7 +256,7 @@ static int resample_syms(int k0, int nsym, int q)
/* Four independent accumulators. A single chain of `yi += h[m]*x[m]` is /* Four independent accumulators. A single chain of `yi += h[m]*x[m]` is
* latency-bound: every VFP fmla waits on the previous one (~8 cycles), which * latency-bound: every VFP fmla waits on the previous one (~8 cycles), which
* is what made the demod take 1.8 s per window on the A7. PSI_TAPS is 36 = 4*9. */ * is what made the demod take 1.8 s per window on the A7. PSI_TAPS is 36 = 4*9. */
{ const float *pi = xi + base, *pq = xq + base; { const float *pi = sxi + base, *pq = sxq + base;
float i0 = 0, i1 = 0, i2 = 0, i3 = 0, q0 = 0, q1 = 0, q2 = 0, q3 = 0; float i0 = 0, i1 = 0, i2 = 0, i3 = 0, q0 = 0, q1 = 0, q2 = 0, q3 = 0;
for (m = 0; m < PSI_TAPS; m += 4) { for (m = 0; m < PSI_TAPS; m += 4) {
i0 += h[m + 0] * pi[m + 0]; q0 += h[m + 0] * pq[m + 0]; i0 += h[m + 0] * pi[m + 0]; q0 += h[m + 0] * pq[m + 0];
@@ -388,7 +393,7 @@ static float probe(int q, int k0, int nsym)
{ {
q %= SYM_DEN; q %= SYM_DEN;
if (q < 0) q += SYM_DEN; if (q < 0) q += SYM_DEN;
if (!resample_syms(k0, nsym, q)) return -99.0f; if (!resample_syms(xi, xq, 0, k0, nsym, q)) return -99.0f;
apply_theta(nsym, theta_of(nsym)); apply_theta(nsym, theta_of(nsym));
return slice_snr(ci, nsym, NULL, NULL); return slice_snr(ci, nsym, NULL, NULL);
} }
@@ -573,6 +578,619 @@ static int ms(struct timespec a, struct timespec b)
return (int)((b.tv_sec - a.tv_sec) * 1000 + (b.tv_nsec - a.tv_nsec) / 1000000); return (int)((b.tv_sec - a.tv_sec) * 1000 + (b.tv_nsec - a.tv_nsec) / 1000000);
} }
/* ================================================================ PR12 =====
* Field-sync-driven timing: FFT correlation against segsync+PN511 -> peak d0
* -> early-late gate (dL/dR at +-1 sample) -> mu -> 16-tap Farrow fractional
* delay -> the unchanged demod anchored at d0 with q = 0.
* PC reference: Notes/vsb_pr12_reference.py; tables: inc/pr12_tables.h.
* Runs IN PARALLEL with find_tau for cross-validation (Task12.md section 3.6);
* snr_db_pub stays on the old chain until the switch. */
#include "pr12_tables.h"
#define FFTN 8192
#define CORR_STEP (FFTN - PR12_REF_LEN + 1) /* valid lags per block */
#define CORR_MARGIN (PR12_REF_LEN + 40)
static float fs_absr2[NSAMP]; /* |R|^2 per lag, 1 MB */
static float fft_re[FFTN], fft_im[FFTN];
static float refF_re[FFTN], refF_im[FFTN];
static float tw_re[FFTN / 2], tw_im[FFTN / 2];
static float xfi[NSAMP], xfq[NSAMP]; /* Farrow-compensated copy */
static int pr12_ready;
static void fft_c(float *re, float *im, int inv)
{
int i, j, len;
for (i = 1, j = 0; i < FFTN; i++) { /* bit reversal */
int bit = FFTN >> 1;
for (; j & bit; bit >>= 1) j ^= bit;
j |= bit;
if (i < j) {
float t;
t = re[i]; re[i] = re[j]; re[j] = t;
t = im[i]; im[i] = im[j]; im[j] = t;
}
}
for (len = 2; len <= FFTN; len <<= 1) {
int half = len >> 1, step = FFTN / len;
for (i = 0; i < FFTN; i += len) {
for (j = 0; j < half; j++) {
int wi = j * step;
float wr = tw_re[wi], wm = inv ? -tw_im[wi] : tw_im[wi];
float ur = re[i + j], ui = im[i + j];
float vr = re[i + j + half] * wr - im[i + j + half] * wm;
float vi = re[i + j + half] * wm + im[i + j + half] * wr;
re[i + j] = ur + vr; im[i + j] = ui + vi;
re[i + j + half] = ur - vr; im[i + j + half] = ui - vi;
}
}
}
if (inv) {
float s = 1.0f / FFTN;
for (i = 0; i < FFTN; i++) { re[i] *= s; im[i] *= s; }
}
}
static void pr12_init(void)
{
int i;
for (i = 0; i < FFTN / 2; i++) {
double a = -2.0 * M_PI * i / FFTN;
tw_re[i] = (float)cos(a); tw_im[i] = (float)sin(a);
}
for (i = 0; i < FFTN; i++) {
refF_re[i] = (i < PR12_REF_LEN) ? pr12_ref_i[i] : 0.0f;
refF_im[i] = (i < PR12_REF_LEN) ? pr12_ref_q[i] : 0.0f;
}
fft_c(refF_re, refF_im, 0);
pr12_ready = 1;
}
/* overlap-save cross-correlation; returns lag m0 of ref[0] (peak), or -1 */
static int fsync_corr_c(float *ratio_out)
{
int b, i, m0 = -1, nvalid = NSAMP - CORR_MARGIN;
double sum2 = 0.0;
float peak2 = 0.0f;
for (b = 0; b * CORR_STEP < NSAMP; b++) {
int base = b * CORR_STEP;
for (i = 0; i < FFTN; i++) {
int s = base + i;
fft_re[i] = (s < NSAMP) ? xi[s] : 0.0f;
fft_im[i] = (s < NSAMP) ? xq[s] : 0.0f;
}
fft_c(fft_re, fft_im, 0);
for (i = 0; i < FFTN; i++) { /* X * conj(REF) */
float ar = fft_re[i], ai = fft_im[i];
float br = refF_re[i], bi = refF_im[i];
fft_re[i] = ar * br + ai * bi;
fft_im[i] = ai * br - ar * bi;
}
fft_c(fft_re, fft_im, 1);
for (i = 0; i < CORR_STEP && base + i < NSAMP; i++)
fs_absr2[base + i] = fft_re[i] * fft_re[i] + fft_im[i] * fft_im[i];
}
for (i = 40; i < nvalid; i++) {
sum2 += fs_absr2[i];
if (fs_absr2[i] > peak2) { peak2 = fs_absr2[i]; m0 = i; }
}
*ratio_out = (sum2 > 0.0 && m0 >= 0)
? sqrtf(peak2 / (float)(sum2 / (nvalid - 40))) : 0.0f;
return m0;
}
/* second complete peak at m0 +- 241972 (present in ~8 % of windows); info only */
static int pr12_second_peak(int m0, int *spacing)
{
int c, best = -1;
float b2 = 0.25f * fs_absr2[m0];
static const int sgn[2] = {-1, 1};
for (c = 0; c < 2; c++) {
int lo = m0 + sgn[c] * PR12_FLD_SAMP - 5, i;
if (lo < 40 || lo + 10 >= NSAMP - CORR_MARGIN) continue;
for (i = lo; i <= lo + 10; i++)
if (fs_absr2[i] > b2) { b2 = fs_absr2[i]; best = i; }
}
if (best >= 0 && spacing) *spacing = abs(best - m0);
return best;
}
/* |R| at fractional symbol-0 position d: Farrow-shift the received segment,
* then a direct dot product against the reference (16.6k MAC, negligible) */
static float frac_corr_c(double d)
{
int di = (int)floor(d), m0, i, j;
float mu = (float)(d - di), hr[PR12_FAR_NTAP];
double ar = 0.0, ai = 0.0;
if (mu > 0.5f) { di++; mu -= 1.0f; }
m0 = di - PR12_REF_PAD;
if (m0 + PR12_FAR_M0 < 0 ||
m0 + PR12_REF_LEN + PR12_FAR_M0 + PR12_FAR_NTAP > NSAMP) return 0.0f;
for (j = 0; j < PR12_FAR_NTAP; j++) { /* Horner over mu */
float h = pr12_far_c[PR12_FAR_ORD][j];
for (i = PR12_FAR_ORD - 1; i >= 0; i--) h = h * mu + pr12_far_c[i][j];
hr[j] = h;
}
for (i = 0; i < PR12_REF_LEN; i++) {
const float *pi = xi + m0 + i + PR12_FAR_M0;
const float *pq = xq + m0 + i + PR12_FAR_M0;
float sr = 0.0f, sq = 0.0f;
for (j = 0; j < PR12_FAR_NTAP; j++) { sr += hr[j] * pi[j]; sq += hr[j] * pq[j]; }
ar += (double)pr12_ref_i[i] * sr + (double)pr12_ref_q[i] * sq;
ai += (double)pr12_ref_i[i] * sq - (double)pr12_ref_q[i] * sr;
}
return (float)sqrt(ar * ar + ai * ai);
}
/* parabolic init on |R(d0-1)|,|R(d0)|,|R(d0+1)|, then dL-dR iterations.
* Returns mu in (-0.5, 0.5]; *di_out is the re-centred integer position. */
static float early_late_c(int d0, int *di_out, int *it_out)
{
int m0 = d0 - PR12_REF_PAD, it, r;
float Rm = sqrtf(fs_absr2[m0 - 1]), R0 = sqrtf(fs_absr2[m0]);
float Rp = sqrtf(fs_absr2[m0 + 1]);
float den = Rm - 2.0f * R0 + Rp, mu, e = 1.0f;
mu = (fabsf(den) > 1e-20f) ? 0.5f * (Rm - Rp) / den : 0.0f;
if (mu > 0.75f) mu = 0.75f;
if (mu < -0.75f) mu = -0.75f;
for (it = 0; it < 8; it++) {
float dL = frac_corr_c((double)d0 + mu - 1.0);
float dR = frac_corr_c((double)d0 + mu + 1.0);
e = (dL - dR) / (dL + dR + 1e-20f);
if (fabsf(e) < PR12_EL_EPS) { it++; break; }
mu -= PR12_EL_GAMMA * e;
}
r = (int)lroundf(mu);
*di_out = d0 + r;
*it_out = it;
return mu - (float)r;
}
/* decision SNR at pos = di + (k*1430 + q)/1539 (q folded so 0 <= q < 1539) */
static float probe_fs(int di, int q, int k0, int nsym)
{
while (q < 0) { q += SYM_DEN; di--; }
while (q >= SYM_DEN) { q -= SYM_DEN; di++; }
if (!resample_syms(xi, xq, di, k0, nsym, q)) return -99.0f;
apply_theta(nsym, theta_of(nsym));
return slice_snr(ci, nsym, NULL, NULL);
}
/* Final polish: +-8/1539 micro-search on decision SNR (16384-symbol probes)
* around the early-late mu, parabolic refine. Anchored to THIS window's own
* field sync -- never warm-started across windows (find_tau's hard lesson). */
static float snr_trim_c(int di, float mu_el, int k_lo)
{
int q_el = (int)lroundf(mu_el * (float)SYM_DEN);
int j, jb = 0, k0 = k_lo + 2000;
float best = -1e30f, s[17], mu;
for (j = -8; j <= 8; j++) {
s[j + 8] = probe_fs(di, q_el + j, k0, 16384);
if (s[j + 8] > best) { best = s[j + 8]; jb = j; }
}
mu = (float)(q_el + jb) / (float)SYM_DEN;
if (jb > -8 && jb < 8) {
float y0 = s[jb + 7], y1 = s[jb + 8], y2 = s[jb + 9];
float den = y0 - 2.0f * y1 + y2, dd;
dd = (fabsf(den) > 1e-12f) ? 0.5f * (y0 - y2) / den : 0.0f;
if (dd > 1.0f) dd = 1.0f;
if (dd < -1.0f) dd = -1.0f;
mu += dd / (float)SYM_DEN;
}
return mu;
}
/* x(n+mu) over the whole window into xfi/xfq (constant mu -> plain 16-tap FIR;
* 4 accumulators for the same VFP-latency reason as resample_syms) */
static void farrow_apply(float mu)
{
int i, j;
float h[PR12_FAR_NTAP];
for (j = 0; j < PR12_FAR_NTAP; j++) {
float v = pr12_far_c[PR12_FAR_ORD][j];
for (i = PR12_FAR_ORD - 1; i >= 0; i--) v = v * mu + pr12_far_c[i][j];
h[j] = v;
}
for (i = 0; i < 8; i++) { xfi[i] = xi[i]; xfq[i] = xq[i]; }
for (i = NSAMP - 8; i < NSAMP; i++) { xfi[i] = xi[i]; xfq[i] = xq[i]; }
for (i = 8; i < NSAMP - 8; i++) {
const float *pi = xi + i + PR12_FAR_M0, *pq = xq + i + PR12_FAR_M0;
float i0 = 0, i1 = 0, i2 = 0, i3 = 0, q0 = 0, q1 = 0, q2 = 0, q3 = 0;
for (j = 0; j < PR12_FAR_NTAP; j += 4) {
i0 += h[j + 0] * pi[j + 0]; q0 += h[j + 0] * pq[j + 0];
i1 += h[j + 1] * pi[j + 1]; q1 += h[j + 1] * pq[j + 1];
i2 += h[j + 2] * pi[j + 2]; q2 += h[j + 2] * pq[j + 2];
i3 += h[j + 3] * pi[j + 3]; q3 += h[j + 3] * pq[j + 3];
}
xfi[i] = (i0 + i1) + (i2 + i3);
xfq[i] = (q0 + q1) + (q2 + q3);
}
}
/* Error power spectrum: the frequency distribution of the raw decision error
* e_k = r_k - slice(r_k), Welch-averaged (Hann, 8192-symbol blocks reusing the
* field-sync FFT). e_k is the real I-path statistic at symbol rate; the
* one-sided error PSD (0..Rs/2 cyc/sym) is spread across the FULL occupied
* band +-3.0005 MHz = (Rs/4)(1+beta), the psi Nyquist edge -- the same band
* the blue signal trace fills -- with the midpoint (f_cyc = 0.25, the carrier
* LO-leak line) landing at 0 MHz. Normalised so the curve's mean equals
* -snr_raw (consistent with the SNR raw readout), with the real distortion
* structure (leak spike, ISI tilt) riding on top. */
#define ERR_NPTS 256
#define ERR_BAND_MHZ 3.5f /* display half-span (covers the +-3.0 band + roll-off) */
struct pr12_result {
int ok; /* field sync found and demod ran */
int di, d2, el_it, segoff, spacing, nsym;
float mu, mu_el, ratio, snr, snr_raw, theta, tau_cmp, seg_ratio, line_db;
float err_f0, err_df, err_db[ERR_NPTS];
int ms_corr, ms_el, ms_trim, ms_far, ms_dem;
};
/*
* Error power spectrum in the SAME channel-baseband frame as the blue signal
* PSD, so the two curves line up bin-for-bin. Driven by the POST-EQUALIZER
* statistic (`eqstat` = the 11-tap DD equalizer output, the one whose decision
* SNR is the published snr_db), NOT the raw statistic. From it, per symbol:
* d_k = slice(eqstat_k) (decision)
* e_k = eqstat_k - d_k (post-EQ decision error)
* Both the ideal and the error are re-modulated back into the channel baseband
* xhat(t) = sum_k (d_k+1.25)(-j)^k psi(t-t_k) (t_k = di + k/sps)
* ehat(t) = sum_k e_k (-j)^k psi(t-t_k)
* and Welch-FFT'd. out = 10log10(|FFT ehat|^2 / inband-flat(|FFT xhat|^2)),
* i.e. the residual-error density relative to the signal flat density -- the
* same "dB rel in-band flat density" axis as the blue trace. Band edges land
* at +-3.0 where the signal rolls off; the total under the curve is -snr_db
* (the equalized MER), not -snr_raw. */
#define ERR_REC 65536 /* samples reconstructed / analysed */
#define ERR_BLK 8192 /* Welch block (reuses the field-sync FFT size = FFTN) */
static float xh_re[NSAMP], xh_im[NSAMP]; /* reconstruction scratch, 2 MB */
static double sig_acc[ERR_BLK], er_acc[ERR_BLK];
static const float MJR[4] = {1.0f, 0.0f, -1.0f, 0.0f}; /* Re((-j)^k) */
static const float MJI[4] = {0.0f, -1.0f, 0.0f, 1.0f}; /* Im((-j)^k) */
/* Re-modulate a per-symbol amplitude into the channel baseband xh[] over
* [base-24, base+ERR_REC+24) using psi (the real SRRC, Nyquist Rs/4, beta
* 0.1152 -- the pulse the RTL shaper vsb_preq_srrc emits), at 10 MSPS.
* mode 0 (ideal): a = d_k + 1.25 (decision + pilot)
* mode 1 (error): a = eqstat_k - d_k (post-EQ decision error) */
static void err_reconstruct(const float *eqstat, int di, int k_lo, int nsym,
int base, int mode)
{
const float inv_sps = 1539.0f / 1430.0f;
int m, n;
for (n = base - 24; n < base + ERR_REC + 24; n++) { xh_re[n] = 0.0f; xh_im[n] = 0.0f; }
for (m = 0; m < nsym; m++) {
int kk = (k_lo + m) & 3;
float d = slice1(eqstat[m]);
float a = mode ? (eqstat[m] - d) : (d + PILOT_DC);
float br = a * MJR[kk], bi = a * MJI[kk];
float pos = (float)di + (float)(k_lo + m) * SPS;
int n0 = (int)pos, dn;
if (n0 < base - 24 || n0 > base + ERR_REC + 24) continue;
for (dn = -19; dn <= 19; dn++) {
int nn = n0 + dn;
float p;
if (nn < base - 24 || nn >= base + ERR_REC + 24) continue;
p = psi_lut(((float)nn - pos) * inv_sps);
xh_re[nn] += br * p; xh_im[nn] += bi * p;
}
}
}
static void err_welch(int base, int nblk, double *acc)
{
int b, k;
for (k = 0; k < ERR_BLK; k++) acc[k] = 0.0;
for (b = 0; b < nblk; b++) {
const float *hr_ = xh_re + base + b * ERR_BLK;
const float *hi_ = xh_im + base + b * ERR_BLK;
for (k = 0; k < ERR_BLK; k++) {
float w = 0.5f - 0.5f * cosf(2.0f * (float)M_PI * k / (ERR_BLK - 1));
fft_re[k] = hr_[k] * w; fft_im[k] = hi_[k] * w;
}
fft_c(fft_re, fft_im, 0);
for (k = 0; k < ERR_BLK; k++)
acc[k] += (double)fft_re[k] * fft_re[k] + (double)fft_im[k] * fft_im[k];
}
}
/*
* Error signal spectrum matching the equalized MER (snr_db). Reshape the
* post-EQ decisions to the ideal baseband and the post-EQ decision error
* (eqstat_k - d_k) to an error baseband -- both via the SRRC, both band-limited
* to the signal band, exactly "reshaped decisions minus the equalized IQ".
* out = 10log10(|FFT error|^2 / inband-flat(|FFT ideal|^2)) -- the residual
* density relative to the signal flat density, on the same channel-frame axis
* as the blue trace. Its integral is -snr_db (the equalized MER, e.g. -45),
* because it is built from the equalizer's own decision error, not the raw IQ. */
static void err_spectrum(const float *eqstat, int di, int k_lo, int nsym,
float snr_db_eq, float *f0, float *df, float *out)
{
int base, k, j, nblk, nflat = 0;
double flat = 0.0;
*f0 = -ERR_BAND_MHZ;
*df = (2.0f * ERR_BAND_MHZ) / (float)(ERR_NPTS - 1);
for (j = 0; j < ERR_NPTS; j++) out[j] = -99.0f;
if (nsym < 4 * ERR_BLK / 3) return;
base = di + (int)((float)k_lo * SPS) + 32;
if (base < 32) base = 32;
if (base + ERR_REC + 32 > NSAMP) base = NSAMP - ERR_REC - 32;
if (base < 32) return;
nblk = ERR_REC / ERR_BLK;
err_reconstruct(eqstat, di, k_lo, nsym, base, 0); /* ideal -> flat ref */
err_welch(base, nblk, sig_acc);
{ float binhz = 1.0e7f / ERR_BLK;
for (k = 0; k < ERR_BLK; k++) {
float f = ((k < ERR_BLK / 2) ? k : k - ERR_BLK) * binhz;
if (f > -2.5e6f && f < 2.5e6f) { flat += sig_acc[k]; nflat++; }
}
if (nflat) flat /= nflat;
}
if (!(flat > 0.0)) return;
err_reconstruct(eqstat, di, k_lo, nsym, base, 1); /* post-EQ error */
err_welch(base, nblk, er_acc);
for (j = 0; j < ERR_NPTS; j++) {
float fmhz = *f0 + (float)j * (*df);
int kb = (int)(fmhz * 1.0e6f / (1.0e7f / ERR_BLK) + (fmhz >= 0 ? 0.5f : -0.5f));
double p; int kk, cnt = 0;
if (kb < 0) kb += ERR_BLK;
p = 0.0;
for (kk = kb - 1; kk <= kb + 1; kk++) {
int idx = ((kk % ERR_BLK) + ERR_BLK) % ERR_BLK;
p += er_acc[idx]; cnt++;
}
p = (cnt > 0) ? p / cnt : flat * 1e-9;
if (!(p > 0.0)) p = flat * 1e-9;
out[j] = 10.0f * log10f((float)(p / flat));
}
/* Anchor the curve's power integral to -snr_db (the equalized MER): the
* reconstruction's absolute scale drifts a few dB from residual leak / edge
* effects, so rescale the whole trace by a constant so that
* 10log10(mean over the occupied band of 10^(out/10)) == -snr_db. Shape
* (where the error concentrates) is preserved; the total now matches the
* SNR the demod reports. */
if (snr_db_eq > 0.0f && snr_db_eq < 90.0f) {
double acc = 0.0; int nb = 0;
for (j = 0; j < ERR_NPTS; j++) {
float fmhz = *f0 + (float)j * (*df);
if (fmhz > -3.0f && fmhz < 3.0f) { acc += pow(10.0, out[j] / 10.0); nb++; }
}
if (nb > 0 && acc > 0.0) {
float cur = 10.0f * log10f((float)(acc / nb)); /* current integral dB */
float shift = -snr_db_eq - cur;
for (j = 0; j < ERR_NPTS; j++) out[j] += shift;
}
}
}
/*
* Decision-directed cancellation of a narrowband interferer at +Rs/4 in the
* c domain (channel-centre leakage -- e.g. TX LO leak -- lands there after
* the j^k derotation; its real part stamps the exact cos(pi*l/2) texture on
* the error autocorrelation). e_k = r_k - slice(r_k); the complex envelope
* W_k/2 = LP(e_k * (-j)^(k0+k)) over LC_LEN symbols (+-17.9 kHz, best in the
* PC sweep -- harmless on clean signals, +0.02 dB on the synthetic control);
* subtract 2*Re{LP * j^(k0+k)} and let the caller re-slice.
* Returns the removed line power in dB relative to Sum(d^2) (diagnostic).
*/
#define LC_LEN 301
static float lc_er[NSYM_MAX], lc_ei[NSYM_MAX];
static float line_cancel(float *r, float *q, int n, int k0)
{
double sr = 0.0, si = 0.0, removed = 0.0, sig = 0.0;
int k, half = LC_LEN / 2;
if (n < 4 * LC_LEN) return 0.0f;
for (k = 0; k < n; k++) {
float e = r[k] - slice1(r[k]);
int kk = (k0 + k) & 3;
/* e * (-j)^(k0+k): (-j)^kk = JR[kk] - j*JI[kk] */
lc_er[k] = e * JR[kk];
lc_ei[k] = -e * JI[kk];
}
/* centre-aligned running boxcar over [k-half, k+half], clamped at ends */
{ int lo = 0, hi = 0;
for (k = 0; k < n; k++) {
int wlo = k - half, whi = k + half + 1, kk = (k0 + k) & 3, cnt;
float wr, wi, l, d;
if (wlo < 0) wlo = 0;
if (whi > n) whi = n;
while (hi < whi) { sr += lc_er[hi]; si += lc_ei[hi]; hi++; }
while (lo < wlo) { sr -= lc_er[lo]; si -= lc_ei[lo]; lo++; }
cnt = hi - lo;
wr = (float)(sr / cnt);
wi = (float)(si / cnt);
/* complex line sample 2*(wr + j wi)*j^(k0+k): subtract its real
* part from the decision statistic and its imaginary part from
* the (normalised) quadrature so the equalizer sees a clean c */
l = 2.0f * (wr * JR[kk] - wi * JI[kk]);
if (q) q[k] -= 2.0f * (wr * JI[kk] + wi * JR[kk]);
d = slice1(r[k]);
sig += (double)d * d;
r[k] -= l;
removed += (double)l * l;
}
}
return (removed > 0.0 && sig > 0.0)
? 10.0f * log10f((float)(removed / sig)) : -99.0f;
}
/*
* T-spaced 11-tap complex LS equalizer, decision-directed, retrained per
* window (22 real unknowns; normal equations + Gaussian elimination). After
* the line canceller the residual floor (~-39 dB) is even-lag LINEAR ISI
* (observation-path ripple); this recovers it to ~43 dB (PC, half/half
* held-out). Real ATSC receivers/analyzers measure MER post-equalizer.
* Trains on every 2nd symbol; overfit bias with 22 dof over 70k samples is
* < 0.001 dB. Output written to out[] (edges copied through), returns SNR.
*/
#define EQ_M 5
#define EQ_NT (2 * EQ_M + 1)
#define EQ_NU (2 * EQ_NT)
static float eq_snr(const float *cr, const float *cq, int n, float *out)
{
static double xtx[EQ_NU][EQ_NU], xty[EQ_NU];
double w[EQ_NU], sd = 0.0, se = 0.0;
int k, i, j;
if (n < 100 * EQ_NT) return -99.0f;
memset(xtx, 0, sizeof(xtx));
memset(xty, 0, sizeof(xty));
for (k = EQ_M; k < n - EQ_M; k += 2) { /* stride-2 training */
double v[EQ_NU], d = slice1(cr[k]);
for (j = 0; j < EQ_NT; j++) {
v[2 * j] = cr[k - (j - EQ_M)];
v[2 * j + 1] = -cq[k - (j - EQ_M)];
}
for (i = 0; i < EQ_NU; i++) {
xty[i] += v[i] * d;
for (j = i; j < EQ_NU; j++) xtx[i][j] += v[i] * v[j];
}
}
for (i = 0; i < EQ_NU; i++) /* symmetrise + ridge */
for (j = 0; j < i; j++) xtx[i][j] = xtx[j][i];
for (i = 0; i < EQ_NU; i++) xtx[i][i] += 1e-6 * xtx[i][i];
/* Gaussian elimination with partial pivoting */
for (i = 0; i < EQ_NU; i++) {
int p = i;
double f;
for (j = i + 1; j < EQ_NU; j++)
if (fabs(xtx[j][i]) > fabs(xtx[p][i])) p = j;
if (fabs(xtx[p][i]) < 1e-12) return -99.0f;
if (p != i) {
for (j = 0; j < EQ_NU; j++) { f = xtx[i][j]; xtx[i][j] = xtx[p][j]; xtx[p][j] = f; }
f = xty[i]; xty[i] = xty[p]; xty[p] = f;
}
for (j = i + 1; j < EQ_NU; j++) {
f = xtx[j][i] / xtx[i][i];
if (f != 0.0) {
int m;
for (m = i; m < EQ_NU; m++) xtx[j][m] -= f * xtx[i][m];
xty[j] -= f * xty[i];
}
}
}
for (i = EQ_NU - 1; i >= 0; i--) {
double acc = xty[i];
for (j = i + 1; j < EQ_NU; j++) acc -= xtx[i][j] * w[j];
w[i] = acc / xtx[i][i];
}
for (k = 0; k < EQ_M; k++) out[k] = cr[k];
for (k = n - EQ_M; k < n; k++) out[k] = cr[k];
for (k = EQ_M; k < n - EQ_M; k++) {
float acc = 0.0f, d, e;
for (j = 0; j < EQ_NT; j++)
acc += (float)w[2 * j] * cr[k - (j - EQ_M)]
- (float)w[2 * j + 1] * cq[k - (j - EQ_M)];
out[k] = acc;
d = slice1(acc);
e = acc - d;
sd += (double)d * d;
se += (double)e * e;
}
return (se > 0.0) ? 10.0f * log10f((float)(sd / se)) : 99.0f;
}
static void pr12_run(struct pr12_result *R, float tau_old)
{
struct timespec t0, t1, t2, t3, t4, t5;
int m0, d0, di, it, k_lo, k_hi, nsym, off = 0, d2m;
float ratio, mu_el, mu, th, dc, scale, snr_raw, snr;
double dd;
memset(R, 0, sizeof(*R));
R->d2 = -1;
if (!pr12_ready) pr12_init();
now(&t0);
m0 = fsync_corr_c(&ratio);
now(&t1);
R->ratio = ratio;
R->ms_corr = ms(t0, t1);
if (m0 < 0 || ratio < PR12_RATIO_THR) return; /* no field sync: old chain only */
d2m = pr12_second_peak(m0, &R->spacing);
if (d2m >= 0) R->d2 = d2m + PR12_REF_PAD;
d0 = m0 + PR12_REF_PAD;
mu_el = early_late_c(d0, &di, &it);
now(&t2);
R->ms_el = ms(t1, t2);
k_lo = (int)ceilf((float)(GUARD - di) / SPS);
k_hi = (int)((float)(NSAMP - GUARD - di) / SPS);
mu = snr_trim_c(di, mu_el, k_lo);
now(&t3);
R->ms_trim = ms(t2, t3);
farrow_apply(mu);
now(&t4);
R->ms_far = ms(t3, t4);
nsym = k_hi - k_lo;
if (nsym > NSYM_USE) nsym = NSYM_USE;
if (nsym < 8 * SEG_LEN) return;
if (!resample_syms(xfi, xfq, di, k_lo, nsym, 0)) return;
th = theta_of(nsym);
apply_theta(nsym, th);
snr_raw = slice_snr(ci, nsym, &dc, &scale);
{ float inv = 1.0f / scale; /* quadrature to same scale */
int i2;
for (i2 = 0; i2 < nsym; i2++) cq[i2] *= inv;
}
gain_track(ci, nsym);
R->line_db = line_cancel(ci, cq, nsym, k_lo); /* narrowband +Rs/4 canceller */
{ float s_eq = eq_snr(ci, cq, nsym, lc_er); /* 11-tap DD equalizer -> lc_er */
const float *eqstat;
if (s_eq > -90.0f) {
snr = s_eq;
eqstat = lc_er; /* equalized statistic */
} else {
double sd = 0.0, se = 0.0;
int i;
for (i = 0; i < nsym; i++) {
float d = slice1(ci[i]);
sd += (double)d * d;
se += (double)(ci[i] - d) * (ci[i] - d);
}
snr = (se > 0.0) ? 10.0f * log10f((float)(sd / se)) : 99.0f;
eqstat = ci; /* EQ failed: fall back to tracked ci */
}
/* error spectrum matching the equalized MER: reshaped decisions minus
* the equalized signal = the post-EQ decision error, band-limited to
* the signal band, integral = -snr_db. */
/* DISABLED on request (2026-07-25): yellow error trace off — uncomment
* the call below (and the "err" JSON block + the HTML draw block) to
* restore. */
/* err_spectrum(eqstat, di, k_lo, nsym, snr, &R->err_f0, &R->err_df, R->err_db); */
(void)eqstat;
}
now(&t5);
R->ms_dem = ms(t4, t5);
R->ok = 1;
R->di = di;
R->mu = mu;
R->mu_el = mu_el;
R->el_it = it;
R->snr = snr;
R->snr_raw = snr_raw;
R->theta = th;
R->nsym = nsym;
R->seg_ratio = seg_sync(ci, nsym, &off);
R->segoff = off;
/* lattice distance to the old chain's tau (both describe the same symbol
* grid; they must agree modulo one symbol period = SPS samples) */
dd = fmod((double)di + (double)mu - (double)tau_old, (double)SPS);
if (dd < 0.0) dd += (double)SPS;
if (dd > 0.5 * (double)SPS) dd = (double)SPS - dd;
R->tau_cmp = (float)dd;
}
/* ============================================================= entry point */ /* ============================================================= entry point */
void iq_vsb_analyse(const unsigned char *win, int wid) void iq_vsb_analyse(const unsigned char *win, int wid)
{ {
@@ -582,6 +1200,9 @@ void iq_vsb_analyse(const unsigned char *win, int wid)
static char json[64 * 1024]; static char json[64 * 1024];
float th, dc, scale, snr, snr_raw, gjit = 0.0f, qi = 0.0f, coff, ratio, eye_w, minh, tau; float th, dc, scale, snr, snr_raw, gjit = 0.0f, qi = 0.0f, coff, ratio, eye_w, minh, tau;
float snr_head, snr_head_raw;
int sync_head;
struct pr12_result pr;
int q; int q;
double e = 0.0, s1 = 0.0, lsum[8], lsq[8]; double e = 0.0, s1 = 0.0, lsum[8], lsq[8];
int i, j, nsym, k0, off = 0, pos, sync, eye_open = 0, lcnt[8]; int i, j, nsym, k0, off = 0, pos, sync, eye_open = 0, lcnt[8];
@@ -613,7 +1234,7 @@ void iq_vsb_analyse(const unsigned char *win, int wid)
nsym = (int)((NSAMP - 2 * GUARD) / SPS) - 20; nsym = (int)((NSAMP - 2 * GUARD) / SPS) - 20;
if (nsym > NSYM_USE) nsym = NSYM_USE; if (nsym > NSYM_USE) nsym = NSYM_USE;
if (!resample_syms(k0, nsym, q)) return; if (!resample_syms(xi, xq, 0, k0, nsym, q)) return;
th = theta_of(nsym); th = theta_of(nsym);
apply_theta(nsym, th); apply_theta(nsym, th);
@@ -712,42 +1333,83 @@ void iq_vsb_analyse(const unsigned char *win, int wid)
} }
eye_w = (float)eye_open * 2.0f / (float)EYE_COLS; /* in UI */ eye_w = (float)eye_open * 2.0f / (float)EYE_COLS; /* in UI */
/* PR12 chain runs last: its trim probes and gain_track reuse ci[]/gtrk[],
* which the old chain (incl. build_eye) must be completely done with. */
pr12_run(&pr, tau);
/* Task12 stage-2 switch: the headline SNR (web tsnr + spectrum-page card)
* comes from the field-sync chain whenever it locked; the find_tau chain
* is the fallback and still drives the eye diagram. */
{ int sync_fs = pr.ok && pr.seg_ratio > 10.0f && pr.snr > 5.0f;
if (sync_fs) { snr_head = pr.snr; snr_head_raw = pr.snr_raw; sync_head = 1; }
else { snr_head = snr; snr_head_raw = snr_raw; sync_head = sync; }
}
pos = snprintf(json, sizeof(json), pos = snprintf(json, sizeof(json),
"{\"ts\":%ld,\"wid\":%d,\"nsym\":%d,\"sync\":%s," "{\"ts\":%ld,\"wid\":%d,\"nsym\":%d,\"sync\":%s,"
"\"snr_db\":%.2f,\"snr_raw_db\":%.2f,\"gain_jitter_pct\":%.2f," "\"snr_db\":%.2f,\"snr_raw_db\":%.2f,\"snr_legacy_db\":%.2f,\"gain_jitter_pct\":%.2f,"
"\"q_isi_db\":%.2f,\"tau\":%.4f,\"theta_deg\":%.2f," "\"q_isi_db\":%.2f,\"tau\":%.4f,\"theta_deg\":%.2f,"
"\"carrier_off_hz\":%.3f,\"segsync_ratio\":%.1f,\"segsync_off\":%d," "\"carrier_off_hz\":%.3f,\"segsync_ratio\":%.1f,\"segsync_off\":%d,"
"\"fs\":{\"sync\":%s,\"d0\":%d,\"mu\":%.4f,\"mu_el\":%.4f,\"ratio\":%.1f,"
"\"snr_db\":%.2f,\"snr_raw_db\":%.2f,\"segoff\":%d,\"tau_cmp\":%.2e,"
"\"d2\":%d,\"spacing\":%d,\"el_it\":%d,\"line_db\":%.1f},"
"\"levels\":[%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f]," "\"levels\":[%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f],"
"\"sigma\":[%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f]," "\"sigma\":[%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f],"
"\"eye_h\":[%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f],\"eye_w_ui\":%.3f," "\"eye_h\":[%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f],\"eye_w_ui\":%.3f,"
"\"eye\":{\"cols\":%d,\"rows\":%d,\"ui\":2.0,\"amp\":%.1f,\"log\":true,\"data\":\"%s\"}}\n", "\"eye\":{\"cols\":%d,\"rows\":%d,\"ui\":2.0,\"amp\":%.1f,\"log\":true,\"data\":\"%s\"}",
(long)time(NULL), wid, nsym, sync ? "true" : "false", (long)time(NULL), wid, nsym, sync_head ? "true" : "false",
snr, snr_raw, 100.0f * gjit, qi, tau, th * 180.0f / (float)M_PI, coff, ratio, off, snr_head, snr_head_raw, snr, 100.0f * gjit, qi, tau, th * 180.0f / (float)M_PI, coff, ratio, off,
pr.ok ? "true" : "false", pr.di, pr.mu, pr.mu_el, pr.ratio,
pr.snr, pr.snr_raw, pr.segoff, pr.tau_cmp,
pr.d2, pr.spacing, pr.el_it, pr.line_db,
lev[0], lev[1], lev[2], lev[3], lev[4], lev[5], lev[6], lev[7], lev[0], lev[1], lev[2], lev[3], lev[4], lev[5], lev[6], lev[7],
sig[0], sig[1], sig[2], sig[3], sig[4], sig[5], sig[6], sig[7], sig[0], sig[1], sig[2], sig[3], sig[4], sig[5], sig[6], sig[7],
eyeh[0], eyeh[1], eyeh[2], eyeh[3], eyeh[4], eyeh[5], eyeh[6], eye_w, eyeh[0], eyeh[1], eyeh[2], eyeh[3], eyeh[4], eyeh[5], eyeh[6], eye_w,
EYE_COLS, EYE_ROWS, EYE_AMP, b64buf); EYE_COLS, EYE_ROWS, EYE_AMP, b64buf);
/* append the error power spectrum (yellow web trace), then close the root */
/* DISABLED on request (2026-07-25): drop the leading `0 &&` to restore */
if (0 && pr.ok && pos > 0 && pos < (int)sizeof(json) - 2048) {
int jj;
pos += snprintf(json + pos, sizeof(json) - pos,
",\"err\":{\"f0\":%.4f,\"df\":%.5f,\"db\":[",
pr.err_f0, pr.err_df);
for (jj = 0; jj < ERR_NPTS; jj++)
pos += snprintf(json + pos, sizeof(json) - pos, "%s%.1f",
jj ? "," : "", pr.err_db[jj]);
pos += snprintf(json + pos, sizeof(json) - pos, "]}}\n");
} else {
pos += snprintf(json + pos, sizeof(json) - pos, "}\n");
}
{ FILE *f = fopen("/tmp/iq_eye.json.tmp", "w"); { FILE *f = fopen("/tmp/iq_eye.json.tmp", "w");
if (f) { fwrite(json, 1, pos, f); fclose(f); if (f) { fwrite(json, 1, pos, f); fclose(f);
rename("/tmp/iq_eye.json.tmp", "/tmp/iq_eye.json"); } rename("/tmp/iq_eye.json.tmp", "/tmp/iq_eye.json"); }
} }
if (sync) { if (sync_head) {
pthread_mutex_lock(&snr_mtx); pthread_mutex_lock(&snr_mtx);
snr_db_pub = snr; snr_stamp = time(NULL); snr_db_pub = snr_head; snr_stamp = time(NULL);
pthread_mutex_unlock(&snr_mtx); pthread_mutex_unlock(&snr_mtx);
} }
minh = eyeh[0]; minh = eyeh[0];
for (j = 1; j < 7; j++) if (eyeh[j] < minh) minh = eyeh[j]; for (j = 1; j < 7; j++) if (eyeh[j] < minh) minh = eyeh[j];
if(iq_vsb_log_allowed()) printf("[iq_vsb] win %d: SNR %.2f dB (raw %.2f, ORx AGC jitter %.2f%%) tau %.4f theta %+.1f deg coff %+.3f Hz "
{ "segsync %.1f@%d eye_w %.3f UI min_eye_h %.2f %s "
printf("[iq_vsb] win %d: SNR %.2f dB (raw %.2f, ORx AGC jitter %.2f%%) tau %.4f theta %+.1f deg coff %+.3f Hz " "[ms: unpack %d tau %d demod %d stats %d eye %d total %d]\n",
"segsync %.1f@%d eye_w %.3f UI min_eye_h %.2f %s " wid, snr, snr_raw, 100.0f * gjit, tau, th * 180.0f / (float)M_PI, coff, ratio, off, eye_w,
"[ms: unpack %d tau %d demod %d stats %d eye %d total %d]\n", minh, sync ? "SYNC" : "NOSYNC",
wid, snr, snr_raw, 100.0f * gjit, tau, th * 180.0f / (float)M_PI, coff, ratio, off, eye_w, ms(t0, t1), ms(t1, t2), ms(t2, t3), ms(t3, t4), ms(t4, t5), ms(t0, t5));
minh, sync ? "SYNC" : "NOSYNC", if (pr.ok)
ms(t0, t1), ms(t1, t2), ms(t2, t3), ms(t3, t4), ms(t4, t5), ms(t0, t5)); printf("[pr12] win %d: FS SNR %.2f dB (raw %.2f, line %.1f dB) d0 %d mu %+.4f (el %+.4f, %d it) "
} "ratio %.1f segsync %.1f@%d tau_cmp %.1e d2 %d spacing %d PUBLISH=%s "
"[ms: corr %d el %d trim %d farrow %d demod %d]\n",
wid, pr.snr, pr.snr_raw, pr.line_db, pr.di, pr.mu, pr.mu_el, pr.el_it,
pr.ratio, pr.seg_ratio, pr.segoff, pr.tau_cmp, pr.d2, pr.spacing,
(snr_head == pr.snr) ? "FS" : "LEGACY",
pr.ms_corr, pr.ms_el, pr.ms_trim, pr.ms_far, pr.ms_dem);
else
printf("[pr12] win %d: no field sync (ratio %.1f < %.1f), LEGACY fallback\n",
wid, pr.ratio, PR12_RATIO_THR);
} }
+72 -100
View File
@@ -814,26 +814,19 @@ void lcd_write_prompt(const PROMPT_POS *ppos,PMENU_S pmenu)
lcd_write_string(ppos->line,ppos->pos[pmenu->item_index],PROMPT_CHAR); lcd_write_string(ppos->line,ppos->pos[pmenu->item_index],PROMPT_CHAR);
} }
/* 取一次 ORx 输入功率(dB)。 /* 取一次 ORx gain index(=255-cw_hi), 5 点中位数原子读。
* *
* ORx RF-AGC 是闭环: dpd_rx_rfagc_1x.vhd:129 把 mean(I^2+Q^2) 伺服到 * ORx RF-AGC 闭环下(dpd_rx_rfagc_1x.vhd:179-187): 输入升高 -> 实测高于设定点
* fagcpwrlevel*64 = 16384, 且 dz=232 的死区(同文件 :29,:176)把偏离夹在 2.00dB 内。 * -> agcctrlword(cw_hi) 递增 -> gain_index = 255-cw_hi 递减 -> 增益降低。
* 因此 ob_fb_rms_pwr 恒被钉在 fagcpwrlevel*4 ≈ 1024, 单独用它不可能跟随输入功率 —— * 5 点中位数对孤立野值(板上 i2cget 或其它 master 插入造成)免疫, 均值不免疫。 */
* 输入功率的信息全部转移到了增益字上。 static uint8 orx_gain_index_now(void)
*
* 粗档取 FAGC 增益字(0x36/0x37, 0.5dB/step), 细档取死区内残差(0x03/0x04), 两者相加。
* 各取 5 次中位数: 加锁挡不住板上 i2cget 或其它 master 插入,
* 中位数对孤立野值免疫, 均值不免疫(这正是原 FWD 炸掉的机理)。 */
static float orx_input_power_db(void)
{ {
uint16 cw[5], rms[5]; uint16 cw[5];
int i; int i;
for (i = 0; i < 5; i++) { for (i = 0; i < 5; i++)
rms[i] = fpga_word_read_lh(0x01, 0x03, 0x04); /* ob_fb_rms_pwr */ cw[i] = fpga_word_read_hl(0x01, 0x36, 0x37);
cw[i] = fpga_word_read_hl(0x01, 0x36, 0x37); /* agcctrlword */ return (uint8)(255 - ((orx_median5(cw) >> 8) & 0xff));
}
return orx_power_combine_db(orx_median5(cw), orx_median5(rms));
} }
static void calc_fwd(void) static void calc_fwd(void)
@@ -899,32 +892,26 @@ static void calc_fwd(void)
} }
} }
#else #else
float p_db; int16 delta_hdb;
uint16 cw[5], rms[5];
int i;
uint8 cw_hi;
if( !atsc_para.ad3_para.power_ref ) { /* 未标定(S4 缺省标记, cal_fagc_cw==0xFF, 见 task-s4-report.md)时 FWD 显示 0 */
if (atsc_para.ad3_para.cal_fagc_cw == 0xff) {
atsc_para.log_para.fwd = 0; atsc_para.log_para.fwd = 0;
return; return;
} }
p_db = orx_input_power_db(); for (i = 0; i < 5; i++) {
rms[i] = fpga_word_read_lh(0x01, 0x03, 0x04);
/* power_ref 存的是额定功率处的 p_db, 0.1 dB/LSB 定点 */ cw[i] = fpga_word_read_hl(0x01, 0x36, 0x37);
atsc_para.log_para.fwd =
orx_fwd_percent(p_db, (float)atsc_para.ad3_para.power_ref / 10.0f);
/* 标定用日志, 标定完成后删除 */
{
static uint8 dbg_cnt = 0;
if(++dbg_cnt >= 5){ dbg_cnt = 0;
uint16 cw = fpga_word_read_hl(0x01, 0x36, 0x37);
uint16 rms = fpga_word_read_lh(0x01, 0x03, 0x04);
printf("[FSSI] cw_hi=%u rms=%u p_db=%.2f FWD=%d%% A_FSSI=%d B_FSSI=%d (ref=%d rf=%d)\n",
(cw >> 8) & 0xff, rms, p_db, atsc_para.log_para.fwd,
st_extend_state.a_fssi, st_extend_state.b_fssi,
atsc_para.ad3_para.power_ref, atsc_para.rf_para.rf);
}
} }
cw_hi = (uint8)((orx_median5(cw) >> 8) & 0xff);
delta_hdb = (int16)((int16)cw_hi + orx_pwr_halfdb(orx_median5(rms)))
- (int16)((int16)atsc_para.ad3_para.cal_fagc_cw
+ (int16)atsc_para.ad3_para.cal_orx_pwr_hdb);
atsc_para.log_para.fwd = orx_fwd_percent_hdb(delta_hdb);
#endif #endif
} }
void get_tsnr_limd_uimd(uint16 *ptsnr, uint8 *plmid, uint8 *pumid) void get_tsnr_limd_uimd(uint16 *ptsnr, uint8 *plmid, uint8 *pumid)
@@ -4178,20 +4165,12 @@ static void refresh_ad3(void *parg,void *parv)
#endif #endif
} }
static uint8 ad_fssi_calc(float p_db)
{
int is_vhf = (atsc_para.rf_para.txfreq <= FREQ_MAX_V) ? 1 : 0;
/* -20 / VHF -18 两段 EMV7 遗留显示微调在 orx_fssi_percent() 内保留 */
return orx_fssi_percent(p_db, FSSI_REF_DB, is_vhf);
}
//" PDT CFR ADPC FSSI " //" PDT CFR ADPC FSSI "
static void refresh_ad4(void *parg,void *parv) static void refresh_ad4(void *parg,void *parv)
{ {
PMENU_S pmenu = (PMENU_S)parg; PMENU_S pmenu = (PMENU_S)parg;
const PROMPT_POS prompt_pos = {LINE_1,{1,7,13,22}}; const PROMPT_POS prompt_pos = {LINE_1,{1,7,13,22}};
PS_ADVANCE4_PARA ppara = (PS_ADVANCE4_PARA)para_middle; PS_ADVANCE4_PARA ppara = (PS_ADVANCE4_PARA)para_middle;
float p_db;
uint8 data; uint8 data;
static uint32 tickbak=0; static uint32 tickbak=0;
@@ -4234,68 +4213,62 @@ static void refresh_ad4(void *parg,void *parv)
break;*/ break;*/
} }
#if 1 //需添加A:FSSI和B:FSSI ; FSSI 单源: update_fssi_state() 是唯一计算点,
//需添加A:FSSI和B:FSSI //本函数只读 st_extend_state.a_fssi/b_fssi 显示, 不再自己取数/换算。
if(check_delay_over(&tickbak,50)==TRUE) //ab_flag=TRUE -> 当前选中 B 路(set_ab_switch(1)); FALSE -> A 路(set_ab_switch(0))。
{ //05 3a 发1 为A 发0 为B if(atsc_para.system_para.ab_mode==1)
if(++ppara->ab_sec>16)
{
ppara->ab_flag = ppara->ab_flag?FALSE:TRUE;
ppara->ab_sec = 0;
}
set_ab_switch(ppara->ab_flag);
p_db = orx_input_power_db();
ppara->fssi = ad_fssi_calc(p_db);
}
if(ppara->ab_flag)
{ {
st_extend_state.a_fssi=ppara->fssi; if(check_delay_over(&tickbak,50)==TRUE)
if((ppara->fssi > 75) || (ppara->fssi < 20)) { // 0x23 IQB_UPD: 1=B, 0=A (E_SELECT_A=0/E_SELECT_B=1)
if(++ppara->ab_sec>16)
{
ppara->ab_flag = ppara->ab_flag?FALSE:TRUE;
ppara->ab_sec = 0;
}
set_ab_switch(ppara->ab_flag);
}
if(ppara->ab_flag)
{ {
if(ppara->ab_sec&1) ppara->fssi = st_extend_state.b_fssi;
sprintf(out_str,"B:%d%%!",ppara->fssi); //250307 feedback, need to change A->B if((ppara->fssi > 75) || (ppara->fssi < 20))
{
if(ppara->ab_sec&1)
sprintf(out_str,"B:%d%%!",ppara->fssi);
else
return;
}
else else
return; {
sprintf(out_str,"B:%d%%",ppara->fssi);
}
} }
else else
{ {
sprintf(out_str,"B:%d%%",ppara->fssi); ppara->fssi = st_extend_state.a_fssi;
if((ppara->fssi > 75) || (ppara->fssi < 20))
{
if(ppara->ab_sec&1)
sprintf(out_str,"A:%d%%!",ppara->fssi);
else
return;
}
else
{
sprintf(out_str,"A:%d%%",ppara->fssi);
}
} }
} }
else else
{ {
st_extend_state.b_fssi=ppara->fssi; //ab_mode!=1(出厂默认 0): update_fssi_state() 只更新 a_fssi, b_fssi 恒 0,
//不做 A/B 轮换、也不再周期性 set_ab_switch() 打扰 DPD 反馈通路,
//固定显示 A 路(S5 fix1, 对应评审 C2/I1)。
ppara->fssi = st_extend_state.a_fssi;
if((ppara->fssi > 75) || (ppara->fssi < 20)) if((ppara->fssi > 75) || (ppara->fssi < 20))
{ sprintf(out_str,"A:%d%%!",ppara->fssi);
if(ppara->ab_sec&1)
sprintf(out_str,"A:%d%%!",ppara->fssi); //250307 feedback, need to change B->A
else
return;
}
else else
{
sprintf(out_str,"A:%d%%",ppara->fssi); sprintf(out_str,"A:%d%%",ppara->fssi);
}
} }
#else
set_ab_switch(0); // 0 : A
//printf("ad4 AB - A\n");
p_db = orx_input_power_db();
ppara->fssi = ad_fssi_calc(p_db);
st_extend_state.a_fssi = ppara->fssi;
if((ppara->fssi > 75) || (ppara->fssi < 20))
{
sprintf(out_str,"%d%%!",ppara->fssi);
}
else
{
sprintf(out_str,"%d%%",ppara->fssi);
}
#endif
lcd_write_string(LINE_2,prompt_pos.pos[3]+1,out_str); lcd_write_string(LINE_2,prompt_pos.pos[3]+1,out_str);
} }
@@ -14560,11 +14533,10 @@ static void sec_proc_func(void)
static void update_fssi_state(void) static void update_fssi_state(void)
{ {
//05 3a 发1 为A ; 发0 为B // 0x23 IQB_UPD: 1=B, 0=A (E_SELECT_A=0/E_SELECT_B=1)
uint8 a_fssi; uint8 a_fssi;
uint8 b_fssi; uint8 b_fssi;
uint8 data; uint8 data;
float p_db;
static uint16 sec_cnt=0; static uint16 sec_cnt=0;
static uint8 ab_flg =0; static uint8 ab_flg =0;
@@ -14608,10 +14580,10 @@ static void update_fssi_state(void)
if(ab_flg){ if(ab_flg){
//B //B
set_ab_switch(E_SELECT_B); set_ab_switch(E_SELECT_B);
p_db = orx_input_power_db();
{ /* 显示滞回: FAGC 一步 0.5dB = FSSI 2.5%, 无滞回会来回闪 */ { /* 显示滞回: FAGC 一步 0.5dB = FSSI 2.5%, 无滞回会来回闪 */
static uint8 b_prev = 0; static uint8 b_prev = 0;
b_prev = orx_fssi_hysteresis(b_prev, ad_fssi_calc(p_db)); b_prev = orx_fssi_hysteresis(b_prev,
orx_fssi_from_gain_index(orx_gain_index_now(), FSSI_GI_MIN, FSSI_GI_MAX));
st_extend_state.b_fssi = b_prev; st_extend_state.b_fssi = b_prev;
} }
} }
@@ -14619,10 +14591,10 @@ static void update_fssi_state(void)
//delay_ms(9); //delay_ms(9);
//A //A
set_ab_switch(E_SELECT_A); set_ab_switch(E_SELECT_A);
p_db = orx_input_power_db();
{ {
static uint8 a_prev = 0; static uint8 a_prev = 0;
a_prev = orx_fssi_hysteresis(a_prev, ad_fssi_calc(p_db)); a_prev = orx_fssi_hysteresis(a_prev,
orx_fssi_from_gain_index(orx_gain_index_now(), FSSI_GI_MIN, FSSI_GI_MAX));
st_extend_state.a_fssi = a_prev; st_extend_state.a_fssi = a_prev;
} }
} }
@@ -14631,10 +14603,10 @@ static void update_fssi_state(void)
else{ else{
set_ab_switch(0); set_ab_switch(0);
//printf("AB switch A\n"); //printf("AB switch A\n");
p_db = orx_input_power_db();
{ {
static uint8 a_prev2 = 0; static uint8 a_prev2 = 0;
a_prev2 = orx_fssi_hysteresis(a_prev2, ad_fssi_calc(p_db)); a_prev2 = orx_fssi_hysteresis(a_prev2,
orx_fssi_from_gain_index(orx_gain_index_now(), FSSI_GI_MIN, FSSI_GI_MAX));
st_extend_state.a_fssi = a_prev2; st_extend_state.a_fssi = a_prev2;
} }
//printf("[%s,%d] only A_FSSI=%d\n",__FUNCTION__,__LINE__,st_extend_state.a_fssi); //printf("[%s,%d] only A_FSSI=%d\n",__FUNCTION__,__LINE__,st_extend_state.a_fssi);
+8 -7
View File
@@ -109,9 +109,10 @@ function SetFormDefaults() {
var rawFwd = Number(form[10]); var rawFwd = Number(form[10]);
cf.fwd.value = (ab_modenum==1) ? "N/A" : (rawFwd + " %"); cf.fwd.value = (ab_modenum==1) ? "N/A" : (rawFwd + " %");
// SNR. Half-dB from the app, plus the fixed +10 dB display offset. // SNR. Half-dB from the app, shown as-is (PR12 removed the old fixed
// +10 dB display offset; this is the true measured MER).
var tmpdata = Number(form[11]); var tmpdata = Number(form[11]);
var snrStr = (tmpdata/2 + 10).toFixed(1); var snrStr = (tmpdata/2).toFixed(1);
cf.tsnr.value = (ab_modenum==1) ? "N/A" : snrStr; cf.tsnr.value = (ab_modenum==1) ? "N/A" : snrStr;
// IMD. Half-dB from the app, plus the fixed +11 dB display offset. // IMD. Half-dB from the app, plus the fixed +11 dB display offset.
@@ -330,8 +331,8 @@ function highlightProblemTableRows() {
// SNR, LIMD, UIMD: same error thresholds as summary cards / updateStatusColors // SNR, LIMD, UIMD: same error thresholds as summary cards / updateStatusColors
var id = inp.id || ''; var id = inp.id || '';
if (id === 'tsnr' && v !== '' && v !== 'N/A') { if (id === 'tsnr' && v !== '' && v !== 'N/A') {
var snrNum = parseFloat(v); // carries the +10 dB display offset var snrNum = parseFloat(v); // true measured dB (PR12: no display offset)
if (snrNum < 25) { tr.classList.add('row-has-error'); break; } if (snrNum < 15) { tr.classList.add('row-has-error'); break; }
} }
if (id === 'l_imd' && v !== '' && v !== 'N/A') { if (id === 'l_imd' && v !== '' && v !== 'N/A') {
if (parseFloat(v) < 47) { tr.classList.add('row-has-error'); break; } if (parseFloat(v) < 47) { tr.classList.add('row-has-error'); break; }
@@ -381,9 +382,9 @@ function updateStatusColors() {
// Main Status details: LIMD, UIMD, SNR – same rules as summary cards (green/red/warn by range) // Main Status details: LIMD, UIMD, SNR – same rules as summary cards (green/red/warn by range)
var tsnrEl = document.getElementById("tsnr"); var tsnrEl = document.getElementById("tsnr");
if(tsnrEl && tsnrEl.value !== "" && tsnrEl.value !== "N/A") { if(tsnrEl && tsnrEl.value !== "" && tsnrEl.value !== "N/A") {
var snrVal = parseFloat(tsnrEl.value); // already carries the +10 dB display offset var snrVal = parseFloat(tsnrEl.value); // true measured dB (PR12: no display offset)
if(snrVal >= 37) tsnrEl.style.color = color_ok; if(snrVal >= 27) tsnrEl.style.color = color_ok;
else if(snrVal >= 30) tsnrEl.style.color = color_warn; else if(snrVal >= 20) tsnrEl.style.color = color_warn;
else tsnrEl.style.color = color_err; else tsnrEl.style.color = color_err;
} else if(tsnrEl) tsnrEl.style.color = ""; } else if(tsnrEl) tsnrEl.style.color = "";
+6 -4
View File
@@ -154,7 +154,7 @@ function SetFormDefaults(){
var queryData = "sys_cfg=&sysnet=&imxsystime=&web_title=&system_a1_key=&ntp_server=&imx_timezone=&"; var queryData = "sys_cfg=&sysnet=&imxsystime=&web_title=&system_a1_key=&ntp_server=&imx_timezone=&";
} else { } else {
// Remote mode: show LOADING indicator // Remote mode: show LOADING indicator
$('#para_set_id, #net_set_id, #sys_rtc_id, #sysreset_bt_id, #upgrade_imx_id, #key_set_id, #ntp_set_id, #timezone_set_id') $('#para_set_id, #net_set_id, #sys_rtc_id, #sysreset_bt_id, #upgrade_imx_id, #upgrade_fpga_id, #key_set_id, #ntp_set_id, #timezone_set_id')
.attr("disabled", true) .attr("disabled", true)
.val("Loading..."); .val("Loading...");
var queryData = "sys_cfg=&sysnet=&imxsystime=&web_title=&system_a1_key=&ntp_server=&imx_timezone=&"; var queryData = "sys_cfg=&sysnet=&imxsystime=&web_title=&system_a1_key=&ntp_server=&imx_timezone=&";
@@ -279,14 +279,16 @@ function SetFormDefaults(){
var currentCtlValue = Number(cf0.ctl.value); var currentCtlValue = Number(cf0.ctl.value);
if(currentCtlValue == 1) { if(currentCtlValue == 1) {
// Local mode: keep buttons disabled // Local mode: keep buttons disabled
$('#para_set_id, #net_set_id, #sys_rtc_id, #sysreset_bt_id, #upgrade_imx_id, #key_set_id, #ntp_set_id, #timezone_set_id') $('#para_set_id, #net_set_id, #sys_rtc_id, #sysreset_bt_id, #upgrade_imx_id, #upgrade_fpga_id, #key_set_id, #ntp_set_id, #timezone_set_id')
.attr("disabled", true) .attr("disabled", true)
.val("Local Mode"); .val("Local Mode");
} else { } else {
// Remote mode: enable buttons // Remote mode: enable buttons
$('#para_set_id, #net_set_id, #sys_rtc_id, #sysreset_bt_id, #upgrade_imx_id, #key_set_id, #ntp_set_id, #timezone_set_id') $('#para_set_id, #net_set_id, #sys_rtc_id, #sysreset_bt_id, #upgrade_imx_id, #upgrade_fpga_id, #key_set_id, #ntp_set_id, #timezone_set_id')
.attr("disabled", false) .attr("disabled", false)
.val("Set"); .val("Set");
$('#upgrade_imx_id').val("Upgrade");
$('#upgrade_fpga_id').val("Upgrade FPGA");
} }
if (typeof setFormSnapshotUpdate === 'function') setFormSnapshotUpdate(); if (typeof setFormSnapshotUpdate === 'function') setFormSnapshotUpdate();
}).fail(function() { }).fail(function() {
@@ -1770,7 +1772,7 @@ function set_timezone() {
<div id="divfpga" style="padding: 20px;"> <div id="divfpga" style="padding: 20px;">
<span id="LFpga"></span> <span id="LFpga"></span>
<input name="FpgaMcs" type="file" id="FpgaMcs" accept=".mcs"/> <input name="FpgaMcs" type="file" id="FpgaMcs" accept=".mcs"/>
<input type="button" class="btn-modern" alt="FPGA UPGRADE" value="Upgrade FPGA" id="upgrade_fpga_id" onclick='uploadFpgaFile()'> <input type="button" class="btn-modern" alt="FPGA UPGRADE" value="Upgrade FPGA" disabled id="upgrade_fpga_id" onclick='uploadFpgaFile()'>
</div> </div>
</form> </form>
<iframe name="myIframeFpga" id="myIframeFpga" style="display: none;"></iframe> <iframe name="myIframeFpga" id="myIframeFpga" style="display: none;"></iframe>
+56 -82
View File
@@ -78,15 +78,21 @@
eyeRestart(); eyeRestart();
} }
/* PR12: the Refresh/Pause/Peak-hold toolbar and the status lines were
* removed on request, so these helpers tolerate the elements being absent
* (fixed 1 s refresh, never paused, no peak-hold). */
function elChecked(id){ var e = document.getElementById(id); return !!(e && e.checked); }
function specRateMs(){ var e = document.getElementById('spec_rate'); return e ? Number(e.value) : 1000; }
function specRestart(){ function specRestart(){
if (SPEC_TIMER) clearInterval(SPEC_TIMER); if (SPEC_TIMER) clearInterval(SPEC_TIMER);
if (document.getElementById('spec_pause').checked) { setState('paused'); return; } if (elChecked('spec_pause')) { setState('paused'); return; }
var ms = Number(document.getElementById('spec_rate').value);
specFetch(); specFetch();
SPEC_TIMER = setInterval(specFetch, ms); SPEC_TIMER = setInterval(specFetch, specRateMs());
} }
function setState(s){ document.getElementById('spec_state').innerText = s; } function setState(s){ var e = document.getElementById('spec_state'); if (e) e.innerText = s; }
function setEyeState(s){ var e = document.getElementById('eye_state'); if (e) e.innerText = s; }
function specFetch(){ function specFetch(){
$.ajax({url: 'iq_spectrum.cgi?t=' + Date.now(), dataType: 'json', cache: false}) $.ajax({url: 'iq_spectrum.cgi?t=' + Date.now(), dataType: 'json', cache: false})
@@ -106,18 +112,8 @@
} }
function updateReadouts(d){ function updateReadouts(d){
document.getElementById('rd_shoulder').innerText = d.shoulder.toFixed(1); /* PR12: the readout cards under the spectrum were removed; d.shoulder
document.getElementById('rd_limd').innerText = (d.limd === undefined) ? '--' : d.limd.toFixed(1); * and d.pilot are still drawn on the plot itself in drawSpectrum(). */
document.getElementById('rd_uimd').innerText = (d.uimd === undefined) ? '--' : d.uimd.toFixed(1);
document.getElementById('rd_pilot').innerText = d.pilot.toFixed(4);
document.getElementById('rd_peak').innerText = d.peak.toFixed(1);
document.getElementById('rd_rbw').innerText = d.rbw_khz.toFixed(2);
document.getElementById('rd_pkts').innerText = d.pkts + '/' + d.npkt;
document.getElementById('rd_ms').innerText = d.ms.toFixed(2);
document.getElementById('rd_pkts').style.color = (d.pkts === d.npkt) ? '' : '#ef4444';
var fld = document.getElementById('rd_field');
fld.innerText = (d.ms > 24.2) ? 'yes' : 'no'; // one ATSC data field = 24.2 ms
fld.style.color = (d.ms > 24.2) ? '#22c55e' : '#ef4444';
} }
@@ -128,54 +124,37 @@
* polls at the same rate as the spectrum and redraws only when the window id changes. * polls at the same rate as the spectrum and redraws only when the window id changes.
*/ */
var EYE_TIMER = null, eyeWid = -1, eyeFails = 0; var EYE_TIMER = null, eyeWid = -1, eyeFails = 0;
var eyeLastData = null; // newest iq_eye.json, for the in-band noise line
function eyeRestart(){ function eyeRestart(){
if (EYE_TIMER) clearInterval(EYE_TIMER); if (EYE_TIMER) clearInterval(EYE_TIMER);
if (document.getElementById('spec_pause').checked) return; if (elChecked('spec_pause')) return;
eyeFetch(); eyeFetch();
EYE_TIMER = setInterval(eyeFetch, Number(document.getElementById('spec_rate').value)); EYE_TIMER = setInterval(eyeFetch, specRateMs());
} }
function eyeFetch(){ function eyeFetch(){
$.ajax({url: 'iq_eye.cgi?t=' + Date.now(), dataType: 'json', cache: false}) $.ajax({url: 'iq_eye.cgi?t=' + Date.now(), dataType: 'json', cache: false})
.done(function(d){ .done(function(d){
var st = document.getElementById('eye_state');
if (!d || d.error){ if (!d || d.error){
if (d && d.redirect) { window.location.href = d.redirect; return; } if (d && d.redirect) { window.location.href = d.redirect; return; }
st.innerText = (d && d.error === 'nodata') ? 'no window demodulated yet' : 'error'; setEyeState((d && d.error === 'nodata') ? 'no window demodulated yet' : 'error');
return; return;
} }
if (!d.sync){ st.innerText = 'no symbol lock'; return; } if (!d.sync){ setEyeState('no symbol lock'); return; }
eyeFails = 0; eyeFails = 0;
eyeLastData = d;
eyeReadouts(d); eyeReadouts(d);
if (d.wid !== eyeWid){ eyeWid = d.wid; drawEye(d); } if (d.wid !== eyeWid){ eyeWid = d.wid; drawEye(d); }
st.innerText = 'window ' + d.wid + ' | ' + d.nsym + ' symbols | updated ' + setEyeState('window ' + d.wid + ' | ' + d.nsym + ' symbols');
new Date(d.ts * 1000).toLocaleTimeString();
}) })
.fail(function(){ eyeFails++; .fail(function(){ eyeFails++; setEyeState('fetch failed (' + eyeFails + ')'); });
document.getElementById('eye_state').innerText = 'fetch failed (' + eyeFails + ')'; });
} }
function eyeReadouts(d){ function eyeReadouts(d){
var minh = Math.min.apply(null, d.eye_h); /* PR12: only SNR/MER and SNR raw remain on request. */
var sAvg = d.sigma.reduce(function(a,b){ return a + b; }, 0) / d.sigma.length;
document.getElementById('rd_snr').innerText = d.snr_db.toFixed(2); document.getElementById('rd_snr').innerText = d.snr_db.toFixed(2);
document.getElementById('rd_eyeh').innerText = minh.toFixed(2);
document.getElementById('rd_eyew').innerText = d.eye_w_ui.toFixed(3);
document.getElementById('rd_tau').innerText = d.tau.toFixed(4);
document.getElementById('rd_segsync').innerText = d.segsync_ratio.toFixed(1);
document.getElementById('rd_coff').innerText = d.carrier_off_hz.toFixed(3);
document.getElementById('rd_snrraw').innerText = (d.snr_raw_db === undefined) ? '--' : d.snr_raw_db.toFixed(2); document.getElementById('rd_snrraw').innerText = (d.snr_raw_db === undefined) ? '--' : d.snr_raw_db.toFixed(2);
document.getElementById('rd_gjit').innerText = (d.gain_jitter_pct === undefined) ? '--' : d.gain_jitter_pct.toFixed(2);
var gj = document.getElementById('rd_gjit');
gj.style.color = (d.gain_jitter_pct < 1.0) ? '#22c55e' : (d.gain_jitter_pct < 4 ? '#f59e0b' : '#ef4444');
/* min(eye_h) should track 2 - 6*sigma. A big shortfall means ISI or non-Gaussian
* distortion -- something a single SNR number cannot show. Guard the absolute
* value too: once sigma is large, 2-6*sigma goes negative and a shut eye would
* otherwise pass the relative test. */
var el = document.getElementById('rd_eyeh');
el.style.color = (minh > 0.6 && minh > 2 - 6 * sAvg - 0.35) ? '#22c55e'
: (minh > 0.3 ? '#f59e0b' : '#ef4444');
var sn = document.getElementById('rd_snr'); var sn = document.getElementById('rd_snr');
sn.style.color = (d.snr_db >= 27) ? '#22c55e' : (d.snr_db >= 20 ? '#f59e0b' : '#ef4444'); sn.style.color = (d.snr_db >= 27) ? '#22c55e' : (d.snr_db >= 20 ? '#f59e0b' : '#ef4444');
} }
@@ -306,7 +285,7 @@
var fmin = d.f0, fmax = d.f0 + (d.n-1)*d.df; var fmin = d.f0, fmax = d.f0 + (d.n-1)*d.df;
var m = drawGrid(fmin, fmax), i; var m = drawGrid(fmin, fmax), i;
if (document.getElementById('spec_hold').checked){ if (elChecked('spec_hold')){
if (!specPeak || specPeak.length !== d.psd.length) specPeak = d.psd.slice(); if (!specPeak || specPeak.length !== d.psd.length) specPeak = d.psd.slice();
else for (i = 0; i < d.psd.length; i++) if (d.psd[i] > specPeak[i]) specPeak[i] = d.psd[i]; else for (i = 0; i < d.psd.length; i++) if (d.psd[i] > specPeak[i]) specPeak[i] = d.psd[i];
trace(m, specPeak, d.f0, d.df, fmin, fmax, '#f59e0b', 1); trace(m, specPeak, d.f0, d.df, fmin, fmax, '#f59e0b', 1);
@@ -321,11 +300,32 @@
m.g.fillStyle = '#f87171'; m.g.textAlign = 'left'; m.g.textBaseline = 'bottom'; m.g.fillStyle = '#f87171'; m.g.textAlign = 'left'; m.g.textBaseline = 'bottom';
m.g.fillText('shoulder ' + d.shoulder.toFixed(1) + ' dB', m.l+6, y-3); m.g.fillText('shoulder ' + d.shoulder.toFixed(1) + ' dB', m.l+6, y-3);
var xp = xOf(m, d.pilot, fmin, fmax); // pilot marker /* PR12: pilot vertical marker removed on request (it covered the pilot
m.g.strokeStyle = '#a3e635'; m.g.beginPath(); * peak). The pilot frequency is still available in d.pilot. */
m.g.moveTo(xp, m.t); m.g.lineTo(xp, m.h-m.b); m.g.stroke();
m.g.fillStyle = '#a3e635'; m.g.textAlign = 'left'; m.g.textBaseline = 'top'; /* PR12: error signal spectrum (yellow) = reshaped decisions subtracted
m.g.fillText('pilot ' + d.pilot.toFixed(3) + ' MHz', xp+4, m.t+2); * from the actual IQ, in the channel frame so it lines up with the blue
* trace. Full-band residual (shows in-band distortion AND out-of-band
* shoulder). Drawn only where it stays below the blue PSD -- any point
* above the signal breaks the segment (clip, not clamp). */
/* DISABLED on request (2026-07-25): drop the leading `false &&` to restore */
if (false && eyeLastData && eyeLastData.err && eyeLastData.err.db){
var er = eyeLastData.err, g2 = m.g, started = false, ei;
g2.strokeStyle = '#eab308'; g2.lineWidth = 1.4; g2.beginPath();
for (ei = 0; ei < er.db.length; ei++){
var ef = er.f0 + ei*er.df;
if (ef < fmin || ef > fmax){ started = false; continue; }
/* blue signal level at this frequency (nearest published bin) */
var bi = Math.round((ef - d.f0)/d.df);
var sig = (bi >= 0 && bi < d.psd.length) ? d.psd[bi] : 99;
if (er.db[ei] > sig){ started = false; continue; } /* above signal: hide */
var ex = xOf(m, ef, fmin, fmax),
ey = yOf(m, Math.max(Math.min(er.db[ei], Y_TOP), Y_BOT));
if (!started){ g2.moveTo(ex, ey); started = true; } else g2.lineTo(ex, ey);
}
g2.stroke();
/* PR12: yellow error trace has no text label on request */
}
} }
// Opens the modal - gets current username from server // Opens the modal - gets current username from server
@@ -672,49 +672,23 @@
<main class="app-main"> <main class="app-main">
<h2>ORx Receive Spectrum</h2> <h2>ORx Receive Spectrum</h2>
<div class="box"> <div class="box">
<div class="spec-toolbar"> <!-- PR12: Refresh/Pause/Peak-hold toolbar + status line removed on
<label>Refresh request (fixed 1 s refresh). Readout cards were removed earlier;
<select id="spec_rate" onchange="specRestart()"> the on-plot shoulder line and yellow error trace remain. -->
<option value="500">0.5 s</option>
<option value="1000" selected>1 s</option>
<option value="2000">2 s</option>
<option value="5000">5 s</option>
</select>
</label>
<label><input type="checkbox" id="spec_pause" onchange="specRestart()"> Pause</label>
<label><input type="checkbox" id="spec_hold" onchange="specPeak=null"> Peak hold</label>
<span id="spec_state">starting&hellip;</span>
</div>
<canvas id="spec_canvas"></canvas> <canvas id="spec_canvas"></canvas>
<div class="spec-readouts">
<div class="spec-readout"><span class="k">Shoulder</span><span class="v"><span id="rd_shoulder">--</span> dB</span></div>
<div class="spec-readout" title="10log10(P@+24kHz / P@3.1494MHz on the pilot side), 11 bins each. Raw dB, without the +11 dB offset the Status page adds."><span class="k">LIMD raw</span><span class="v"><span id="rd_limd">--</span> dB</span></div>
<div class="spec-readout" title="10log10(P@+24kHz / P@3.1494MHz opposite the pilot), 11 bins each. Raw dB, without the +11 dB offset the Status page adds."><span class="k">UIMD raw</span><span class="v"><span id="rd_uimd">--</span> dB</span></div>
<div class="spec-readout"><span class="k">Pilot</span><span class="v"><span id="rd_pilot">--</span> MHz</span></div>
<div class="spec-readout"><span class="k">Peak</span><span class="v"><span id="rd_peak">--</span> dB</span></div>
<div class="spec-readout"><span class="k">RBW</span><span class="v"><span id="rd_rbw">--</span> kHz</span></div>
<div class="spec-readout"><span class="k">Packets</span><span class="v" id="rd_pkts">--</span></div>
<div class="spec-readout"><span class="k">Contiguous</span><span class="v"><span id="rd_ms">--</span> ms</span></div>
<div class="spec-readout"><span class="k">&gt; 1 field</span><span class="v" id="rd_field">--</span></div>
</div>
</div> </div>
<h2>8VSB Eye Diagram</h2> <h2>8VSB Eye Diagram</h2>
<div class="box"> <div class="box">
<div class="spec-toolbar"> <!-- PR12: eye caption + status line removed on request. -->
<span style="font-size:13px;opacity:.75">Matched-filtered I rail, 2 symbol periods, log persistence</span>
<span id="eye_state">waiting for the demodulator&hellip;</span>
</div>
<canvas id="eye_canvas"></canvas> <canvas id="eye_canvas"></canvas>
<div class="spec-readouts"> <div class="spec-readouts">
<div class="spec-readout" title="MER, I-path definition: 10log10(sum d^2 / sum (r-d)^2)"><span class="k">SNR / MER</span><span class="v"><span id="rd_snr">--</span> dB</span></div> <div class="spec-readout" title="MER, I-path definition: 10log10(sum d^2 / sum (r-d)^2)"><span class="k">SNR / MER</span><span class="v"><span id="rd_snr">--</span> dB</span></div>
<div class="spec-readout" title="Smallest of the 7 eye openings at the decision instant (1%/99% percentiles); ideal 2.00"><span class="k">Min eye height</span><span class="v" id="rd_eyeh">--</span></div>
<div class="spec-readout" title="Time span over which the central eye stays open"><span class="k">Eye width</span><span class="v"><span id="rd_eyew">--</span> UI</span></div>
<div class="spec-readout" title="Sampling phase offset; re-solved for every window because the capture start is not symbol-locked"><span class="k">&tau;0</span><span class="v"><span id="rd_tau">--</span> sa</span></div>
<div class="spec-readout" title="Correlation peak / rms of the 4-symbol segment sync, period 832. Above 10 means locked"><span class="k">Seg sync</span><span class="v" id="rd_segsync">--</span></div>
<div class="spec-readout" title="Pilot phase drift across the window. TX and ORx share the LO, so this must be ~0"><span class="k">Carrier offset</span><span class="v"><span id="rd_coff">--</span> Hz</span></div>
<div class="spec-readout" title="MER before the ORx AGC gain steps are tracked out. The gap to SNR/MER is what the observation receiver costs you, not the transmitter."><span class="k">SNR raw</span><span class="v"><span id="rd_snrraw">--</span> dB</span></div> <div class="spec-readout" title="MER before the ORx AGC gain steps are tracked out. The gap to SNR/MER is what the observation receiver costs you, not the transmitter."><span class="k">SNR raw</span><span class="v"><span id="rd_snrraw">--</span> dB</span></div>
<div class="spec-readout" title="rms of the ORx RF AGC gain steps over the window (0.43 dB per gain index). This is a receiver artifact; a dead zone in dpd_rx_rfagc would remove it at the source."><span class="k">ORx AGC jitter</span><span class="v"><span id="rd_gjit">--</span> %</span></div> <!-- PR13: FSSI from the ORx FAGC gain index (255->0%, 195->100%).
Value comes from the shared 3 s dashboard poll (dashboard.js),
not from iq_eye.cgi. Shows "A / B" when FSSI refresh is on. -->
<div class="spec-readout" title="ORx front-end signal strength from the FAGC gain index (gain index 255 -> 0%, 195 -> 100%). A path; A / B when the FSSI refresh switch is on."><span class="k">FSSI</span><span class="v"><span id="rd_fssi">--</span> %</span></div>
</div> </div>
</div> </div>
</main> </main>
+3
View File
@@ -1,5 +1,8 @@
SMOD-ATSC 修改记录: SMOD-ATSC 修改记录:
V131R-260725:
1.修正FSSI/FWD显示问题;
V125-260722:(暂时发现DPD/FSSI/网页频谱显示都不正常,临时提交) V125-260722:(暂时发现DPD/FSSI/网页频谱显示都不正常,临时提交)
1.合并增加 Web 端 FPGA .mcs 在线升级功能,支持升级进度及错误状态反馈。 1.合并增加 Web 端 FPGA .mcs 在线升级功能,支持升级进度及错误状态反馈。
2.合并优化 FPGA 升级后的恢复流程,实现无需断电的 FPGA 配置、JESD 链路及 RF 热恢复。 2.合并优化 FPGA 升级后的恢复流程,实现无需断电的 FPGA 配置、JESD 链路及 RF 热恢复。