diff --git a/src/Makefile b/src/Makefile index 31b8c4c..5bef230 100755 --- a/src/Makefile +++ b/src/Makefile @@ -39,6 +39,7 @@ SVN_VER_NUM=122T4 SVN_VER_NUM=123 SVN_VER_NUM=124 SVN_VER_NUM=125 +SVN_VER_NUM=131R OUT_FS_DIR := $(PWD)/../out/rootfs UPGARDE_IMX_FS_DIR := SMOD diff --git a/src/atsc_app/adrv9009/adrv9009_config.c b/src/atsc_app/adrv9009/adrv9009_config.c index 69b22d1..566db96 100755 --- a/src/atsc_app/adrv9009/adrv9009_config.c +++ b/src/atsc_app/adrv9009/adrv9009_config.c @@ -1107,7 +1107,16 @@ void adrv9009_init(void) fpga_write_axi0(0x0b, 0x00, 1);//ADV9009_2 RX loopback to TX #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 diff --git a/src/atsc_app/bsp/eeprom.c b/src/atsc_app/bsp/eeprom.c index 4123dea..2ac4ee1 100755 --- a/src/atsc_app/bsp/eeprom.c +++ b/src/atsc_app/bsp/eeprom.c @@ -989,6 +989,10 @@ void e2rom_save_ad3(S_ADVANCE3_PARA *ppara) } p = orx_power_combine_db(orx_median5(cw), orx_median5(rms)); 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; 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); log_printf(LOG_INFO, I_USER_CFG, "fwd_cal=%d, power_ref=%d\n", ppara->fwd_cal,ppara->power_ref); #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); 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) ppara->mptv = 0; #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) @@ -2939,6 +2954,8 @@ void e2rom_load_default_param(unsigned char menu) atsc_para.ad3_para.mptv= 0; atsc_para.ad3_para.fwd_cal= 1;// 1:ok 0:cal 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.rst = 0; //atsc_para.ad3_para.max_power = AD_MAX_POWER_DEFAULT; diff --git a/src/atsc_app/bsp/orx_power.c b/src/atsc_app/bsp/orx_power.c index b59743d..93547d4 100755 --- a/src/atsc_app/bsp/orx_power.c +++ b/src/atsc_app/bsp/orx_power.c @@ -29,36 +29,6 @@ uint16 orx_median5(const uint16 *v) 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) { int d = (int)now - (int)prev; @@ -67,3 +37,40 @@ uint8 orx_fssi_hysteresis(uint8 prev, uint8 now) d = -d; 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); +} diff --git a/src/atsc_app/inc/eeprom.h b/src/atsc_app/inc/eeprom.h index 4e9ed14..2f0bf91 100755 --- a/src/atsc_app/inc/eeprom.h +++ b/src/atsc_app/inc/eeprom.h @@ -274,7 +274,11 @@ typedef enum EEPROM_TSOIP2_MAC_M = EEPROM_TSOIP2_MAC_H+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, diff --git a/src/atsc_app/inc/lcd_task.h b/src/atsc_app/inc/lcd_task.h index d2829be..415cee7 100755 --- a/src/atsc_app/inc/lcd_task.h +++ b/src/atsc_app/inc/lcd_task.h @@ -139,16 +139,10 @@ #define CHECK_AGC_MIN_ENUM 100 #define CHECK_AGC_MAX_ENUM 256*10 -/* FSSI 量程下沿(dB)。orx_power_combine_db() 的返回值只有相对意义, 由本常量吸收绝对基准。 - * - * 旧的 DPD_FSSI_MAX_VALUE/MIN_VALUE 已作废: 它们是线性功率域的阈值, 且 MAX=0x0400 - * 恰好等于 ORx AGC 的设定点镜像 fagcpwrlevel*4, 等于把量程顶端钉死在工作点上, - * 这正是 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 +/* FSSI 量程: gain index gi_max(弱)->0%, gi_min(强)->100%。 + * RTL 钳位下限 195(agc_spi_ctrl_gen.v)。真机扫功率标定后可收窄(Task13 V2)。 */ +#define FSSI_GI_MIN 195 +#define FSSI_GI_MAX 255 #define EEPROM_BACKUP_PARA_TYPE_NONE 0 #define EEPROM_BACKUP_PARA_TYPE_PREF 1 //工厂默认参数备份 @@ -476,10 +470,11 @@ typedef struct uint8 rst; 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 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; typedef struct diff --git a/src/atsc_app/inc/orx_power.h b/src/atsc_app/inc/orx_power.h index 5ea65a1..770211d 100755 --- a/src/atsc_app/inc/orx_power.h +++ b/src/atsc_app/inc/orx_power.h @@ -3,9 +3,6 @@ #include "type.h" -/* FSSI 量程: 0~100% 对应 20 dB 的 ORx 输入功率变化 */ -#define ORX_FSSI_SPAN_DB 20.0f - /* 把 FAGC 增益字(粗档)与死区内残差(细档)合成为 dB 量纲的 ORx 输入功率。 * fagc_cw : seg1 0x36/0x37, agcctrlword。高字节随输入功率单调递增。 * 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 竞争造成的孤立野值 —— 均值对此无免疫力。 */ 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%, * 无滞回时读数会在两个相邻百分比之间来回闪。状态由调用方持有。 */ 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_ */ diff --git a/src/atsc_app/inc/pr12_tables.h b/src/atsc_app/inc/pr12_tables.h new file mode 100755 index 0000000..22c24f1 --- /dev/null +++ b/src/atsc_app/inc/pr12_tables.h @@ -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 */ diff --git a/src/atsc_app/task/iq_vsb.c b/src/atsc_app/task/iq_vsb.c index 6dfbe64..43470ec 100755 --- a/src/atsc_app/task/iq_vsb.c +++ b/src/atsc_app/task/iq_vsb.c @@ -231,15 +231,20 @@ static void build_bank(void) } /* ------------------------------------------------- resample + derotate */ -/* c_k = y_k * j^k for k in [k0, k0+nsym), with tau0 = q/1539. - * Returns 0 if the window is too short. */ -static int resample_syms(int k0, int nsym, int q) +/* c_k = y_k * j^k for k in [k0, k0+nsym), sample position of symbol k being + * s_ofs + (k*1430 + q)/1539, reading from sxi/sxq (xi/xq or the PR12 + * 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; - /* 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; - n = (int)(t / SYM_DEN); - num = (int)(t % SYM_DEN); + long long nf = (t >= 0) ? t / SYM_DEN : -((-t + SYM_DEN - 1) / SYM_DEN); + n = (int)nf + s_ofs; + num = (int)(t - nf * SYM_DEN); for (k = 0; k < nsym; k++) { 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 * 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. */ - { 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; for (m = 0; m < PSI_TAPS; m += 4) { 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; 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)); 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); } +/* ================================================================ 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 */ 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]; 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; 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]; @@ -613,7 +1234,7 @@ void iq_vsb_analyse(const unsigned char *win, int wid) nsym = (int)((NSAMP - 2 * GUARD) / SPS) - 20; 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); 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 */ + /* 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), "{\"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," "\"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]," "\"sigma\":[%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.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", - (long)time(NULL), wid, nsym, sync ? "true" : "false", - snr, snr_raw, 100.0f * gjit, qi, tau, th * 180.0f / (float)M_PI, coff, ratio, off, + "\"eye\":{\"cols\":%d,\"rows\":%d,\"ui\":2.0,\"amp\":%.1f,\"log\":true,\"data\":\"%s\"}", + (long)time(NULL), wid, nsym, sync_head ? "true" : "false", + 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], 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, 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"); if (f) { fwrite(json, 1, pos, f); fclose(f); rename("/tmp/iq_eye.json.tmp", "/tmp/iq_eye.json"); } } - if (sync) { + if (sync_head) { 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); } minh = eyeh[0]; 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 " - "[ms: unpack %d tau %d demod %d stats %d eye %d total %d]\n", - wid, snr, snr_raw, 100.0f * gjit, tau, th * 180.0f / (float)M_PI, coff, ratio, off, eye_w, - minh, sync ? "SYNC" : "NOSYNC", - ms(t0, t1), ms(t1, t2), ms(t2, t3), ms(t3, t4), ms(t4, t5), ms(t0, t5)); - } + 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 " + "[ms: unpack %d tau %d demod %d stats %d eye %d total %d]\n", + wid, snr, snr_raw, 100.0f * gjit, tau, th * 180.0f / (float)M_PI, coff, ratio, off, eye_w, + minh, sync ? "SYNC" : "NOSYNC", + ms(t0, t1), ms(t1, t2), ms(t2, t3), ms(t3, t4), ms(t4, t5), ms(t0, t5)); + if (pr.ok) + 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); } diff --git a/src/atsc_app/task/lcd_task.c b/src/atsc_app/task/lcd_task.c index f022949..9151980 100755 --- a/src/atsc_app/task/lcd_task.c +++ b/src/atsc_app/task/lcd_task.c @@ -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); } -/* 取一次 ORx 输入功率(dB)。 +/* 取一次 ORx gain index(=255-cw_hi), 5 点中位数原子读。 * - * ORx RF-AGC 是闭环: dpd_rx_rfagc_1x.vhd:129 把 mean(I^2+Q^2) 伺服到 - * fagcpwrlevel*64 = 16384, 且 dz=232 的死区(同文件 :29,:176)把偏离夹在 2.00dB 内。 - * 因此 ob_fb_rms_pwr 恒被钉在 fagcpwrlevel*4 ≈ 1024, 单独用它不可能跟随输入功率 —— - * 输入功率的信息全部转移到了增益字上。 - * - * 粗档取 FAGC 增益字(0x36/0x37, 0.5dB/step), 细档取死区内残差(0x03/0x04), 两者相加。 - * 各取 5 次中位数: 加锁挡不住板上 i2cget 或其它 master 插入, - * 中位数对孤立野值免疫, 均值不免疫(这正是原 FWD 炸掉的机理)。 */ -static float orx_input_power_db(void) + * ORx RF-AGC 闭环下(dpd_rx_rfagc_1x.vhd:179-187): 输入升高 -> 实测高于设定点 + * -> agcctrlword(cw_hi) 递增 -> gain_index = 255-cw_hi 递减 -> 增益降低。 + * 5 点中位数对孤立野值(板上 i2cget 或其它 master 插入造成)免疫, 均值不免疫。 */ +static uint8 orx_gain_index_now(void) { - uint16 cw[5], rms[5]; + uint16 cw[5]; int 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); /* agcctrlword */ - } - return orx_power_combine_db(orx_median5(cw), orx_median5(rms)); + for (i = 0; i < 5; i++) + cw[i] = fpga_word_read_hl(0x01, 0x36, 0x37); + return (uint8)(255 - ((orx_median5(cw) >> 8) & 0xff)); } static void calc_fwd(void) @@ -899,32 +892,26 @@ static void calc_fwd(void) } } #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; return; } - p_db = orx_input_power_db(); - - /* power_ref 存的是额定功率处的 p_db, 0.1 dB/LSB 定点 */ - 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); - } + for (i = 0; i < 5; i++) { + rms[i] = fpga_word_read_lh(0x01, 0x03, 0x04); + cw[i] = fpga_word_read_hl(0x01, 0x36, 0x37); } - + 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 } void get_tsnr_limd_uimd(uint16 *ptsnr, uint8 *plmid, uint8 *pumid) @@ -4178,20 +4165,12 @@ static void refresh_ad3(void *parg,void *parv) #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 " static void refresh_ad4(void *parg,void *parv) { PMENU_S pmenu = (PMENU_S)parg; const PROMPT_POS prompt_pos = {LINE_1,{1,7,13,22}}; PS_ADVANCE4_PARA ppara = (PS_ADVANCE4_PARA)para_middle; - float p_db; uint8 data; static uint32 tickbak=0; @@ -4234,68 +4213,62 @@ static void refresh_ad4(void *parg,void *parv) break;*/ } -#if 1 - //需添加A:FSSI和B:FSSI - if(check_delay_over(&tickbak,50)==TRUE) - { //05 3a 发1 为A 发0 为B - 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) + //需添加A:FSSI和B:FSSI ; FSSI 单源: update_fssi_state() 是唯一计算点, + //本函数只读 st_extend_state.a_fssi/b_fssi 显示, 不再自己取数/换算。 + //ab_flag=TRUE -> 当前选中 B 路(set_ab_switch(1)); FALSE -> A 路(set_ab_switch(0))。 + if(atsc_para.system_para.ab_mode==1) { - st_extend_state.a_fssi=ppara->fssi; - if((ppara->fssi > 75) || (ppara->fssi < 20)) + if(check_delay_over(&tickbak,50)==TRUE) + { // 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) - sprintf(out_str,"B:%d%%!",ppara->fssi); //250307 feedback, need to change A->B + ppara->fssi = st_extend_state.b_fssi; + if((ppara->fssi > 75) || (ppara->fssi < 20)) + { + if(ppara->ab_sec&1) + sprintf(out_str,"B:%d%%!",ppara->fssi); + else + return; + } else - return; + { + sprintf(out_str,"B:%d%%",ppara->fssi); + } } 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 { - 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->ab_sec&1) - sprintf(out_str,"A:%d%%!",ppara->fssi); //250307 feedback, need to change B->A - else - return; - } + sprintf(out_str,"A:%d%%!",ppara->fssi); else - { 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); } @@ -14560,11 +14533,10 @@ static void sec_proc_func(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 b_fssi; uint8 data; - float p_db; static uint16 sec_cnt=0; static uint8 ab_flg =0; @@ -14608,10 +14580,10 @@ static void update_fssi_state(void) if(ab_flg){ //B set_ab_switch(E_SELECT_B); - p_db = orx_input_power_db(); { /* 显示滞回: FAGC 一步 0.5dB = FSSI 2.5%, 无滞回会来回闪 */ 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; } } @@ -14619,10 +14591,10 @@ static void update_fssi_state(void) //delay_ms(9); //A set_ab_switch(E_SELECT_A); - p_db = orx_input_power_db(); { 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; } } @@ -14631,10 +14603,10 @@ static void update_fssi_state(void) else{ set_ab_switch(0); //printf("AB switch A\n"); - p_db = orx_input_power_db(); { 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; } //printf("[%s,%d] only A_FSSI=%d\n",__FUNCTION__,__LINE__,st_extend_state.a_fssi); diff --git a/src/web/www/status_info.html b/src/web/www/status_info.html index 8821fb0..821cef3 100755 --- a/src/web/www/status_info.html +++ b/src/web/www/status_info.html @@ -109,9 +109,10 @@ function SetFormDefaults() { var rawFwd = Number(form[10]); 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 snrStr = (tmpdata/2 + 10).toFixed(1); + var snrStr = (tmpdata/2).toFixed(1); cf.tsnr.value = (ab_modenum==1) ? "N/A" : snrStr; // 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 var id = inp.id || ''; if (id === 'tsnr' && v !== '' && v !== 'N/A') { - var snrNum = parseFloat(v); // carries the +10 dB display offset - if (snrNum < 25) { tr.classList.add('row-has-error'); break; } + var snrNum = parseFloat(v); // true measured dB (PR12: no display offset) + if (snrNum < 15) { tr.classList.add('row-has-error'); break; } } if (id === 'l_imd' && v !== '' && v !== 'N/A') { 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) var tsnrEl = document.getElementById("tsnr"); if(tsnrEl && tsnrEl.value !== "" && tsnrEl.value !== "N/A") { - var snrVal = parseFloat(tsnrEl.value); // already carries the +10 dB display offset - if(snrVal >= 37) tsnrEl.style.color = color_ok; - else if(snrVal >= 30) tsnrEl.style.color = color_warn; + var snrVal = parseFloat(tsnrEl.value); // true measured dB (PR12: no display offset) + if(snrVal >= 27) tsnrEl.style.color = color_ok; + else if(snrVal >= 20) tsnrEl.style.color = color_warn; else tsnrEl.style.color = color_err; } else if(tsnrEl) tsnrEl.style.color = ""; diff --git a/src/web/www/sys_set.html b/src/web/www/sys_set.html index f86ae03..7d97595 100755 --- a/src/web/www/sys_set.html +++ b/src/web/www/sys_set.html @@ -154,7 +154,7 @@ function SetFormDefaults(){ var queryData = "sys_cfg=&sysnet=&imxsystime=&web_title=&system_a1_key=&ntp_server=&imx_timezone=&"; } else { // 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) .val("Loading..."); 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); if(currentCtlValue == 1) { // 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) .val("Local Mode"); } else { // 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) .val("Set"); + $('#upgrade_imx_id').val("Upgrade"); + $('#upgrade_fpga_id').val("Upgrade FPGA"); } if (typeof setFormSnapshotUpdate === 'function') setFormSnapshotUpdate(); }).fail(function() { @@ -1770,7 +1772,7 @@ function set_timezone() {