Compare commits
17
Commits
92b155499d
...
0da880ec86
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0da880ec86 | ||
|
|
f6e4f84c27 | ||
|
|
61c85f1fdc | ||
|
|
94a6772782 | ||
|
|
d76eff51c4 | ||
|
|
233866b5df | ||
|
|
2cf8ca7a5a | ||
|
|
46db56d45c | ||
|
|
9763f09b4d | ||
|
|
936a850c6b | ||
|
|
eebdd22256 | ||
|
|
8243380474 | ||
|
|
bded7b6836 | ||
|
|
33d8f4f428 | ||
|
|
47b782e976 | ||
|
|
fad8784550 | ||
|
|
f939716614 |
@@ -0,0 +1,108 @@
|
|||||||
|
# PR17:DPD结束后恢复ORx FAGC实时跟踪
|
||||||
|
|
||||||
|
## 1. 背景
|
||||||
|
|
||||||
|
当前冷启动流程会调用 `boot_cfg_fagc_update()`,向 FPGA 的
|
||||||
|
`FPGA_ADDR_DPD_CTRL3` 写入 `DPD_CTRL3_FAGC_UPDATE`,因此在未执行
|
||||||
|
DPD Update 时,ORx FAGC保持实时跟踪。
|
||||||
|
|
||||||
|
执行DPD Update后,FAGC在训练阶段工作一段时间,然后被锁定。DPD完整结束后,
|
||||||
|
当前流程没有恢复冷启动时的FAGC工作状态,导致后续ORx反馈射频电平发生较大变化时,
|
||||||
|
FAGC不能再次收敛。
|
||||||
|
|
||||||
|
## 2. 需求
|
||||||
|
|
||||||
|
DPD Update完整结束后,恢复与设备冷启动后相同的ORx FAGC实时跟踪状态。
|
||||||
|
|
||||||
|
要求:
|
||||||
|
|
||||||
|
- 只修改i.MX6应用代码。
|
||||||
|
- 不修改FPGA RTL。
|
||||||
|
- 不修改ORx DAGC逻辑。
|
||||||
|
- 不改变DPD训练期间现有的FAGC启停和锁定时序。
|
||||||
|
- 不触发新的DPD训练,不清除已经生成的DPD系数。
|
||||||
|
|
||||||
|
## 3. 修改方案
|
||||||
|
|
||||||
|
目标文件:
|
||||||
|
|
||||||
|
```text
|
||||||
|
imx6/src/atsc_app/bsp/dpd_proc.c
|
||||||
|
```
|
||||||
|
|
||||||
|
复用现有函数:
|
||||||
|
|
||||||
|
```c
|
||||||
|
boot_cfg_fagc_update();
|
||||||
|
```
|
||||||
|
|
||||||
|
该函数定义于:
|
||||||
|
|
||||||
|
```text
|
||||||
|
imx6/src/atsc_app/bsp/fpga.c
|
||||||
|
```
|
||||||
|
|
||||||
|
如果 `dpd_proc.c` 中没有函数声明,则增加:
|
||||||
|
|
||||||
|
```c
|
||||||
|
extern void boot_cfg_fagc_update(void);
|
||||||
|
```
|
||||||
|
|
||||||
|
在DPD Update完整成功结束、即将退出更新状态机的位置,调用:
|
||||||
|
|
||||||
|
```c
|
||||||
|
/* 恢复冷启动后的ORx FAGC实时跟踪状态。 */
|
||||||
|
boot_cfg_fagc_update();
|
||||||
|
|
||||||
|
log_printf(LOG_INFO, I_USER_CFG,
|
||||||
|
"[DPD] update complete, ORx FAGC tracking restored\n");
|
||||||
|
```
|
||||||
|
|
||||||
|
如果该完整结束出口当前执行以下最终清零:
|
||||||
|
|
||||||
|
```c
|
||||||
|
fpga_byte_write(0x05, FPGA_ADDR_DPD_CTRL3, 0);
|
||||||
|
```
|
||||||
|
|
||||||
|
则将这一次最终清零替换为 `boot_cfg_fagc_update()`。
|
||||||
|
|
||||||
|
必须保留phase 1、phase 7以及其他DPD训练阶段内部用于锁定FAGC的
|
||||||
|
`FPGA_ADDR_DPD_CTRL3 = 0` 操作。这些操作属于现有DPD收敛流程,不能替换。
|
||||||
|
|
||||||
|
如果工程中存在多套DPD Update状态机,应分别检查它们的完整成功结束出口,确保最终都恢复FAGC。
|
||||||
|
|
||||||
|
## 4. 修改后的工作状态
|
||||||
|
|
||||||
|
1. 冷启动、不执行DPD Update:行为不变,FAGC持续实时跟踪。
|
||||||
|
2. DPD训练期间:行为不变,FAGC先收敛,再按现有流程锁定。
|
||||||
|
3. DPD Update完整结束:重新置位 `DPD_CTRL3_FAGC_UPDATE`。
|
||||||
|
4. 后续ORx反馈电平超出RTL现有死区时,FAGC再次调整ADRV9009 ORx增益。
|
||||||
|
5. DAGC控制方式和已完成的DPD系数保持不变。
|
||||||
|
|
||||||
|
## 5. 注意事项
|
||||||
|
|
||||||
|
- `boot_cfg_fagc_update()` 只有在ADRV9009 SPI控制权属于FPGA时才会写入;DPD结束时应确认SPI控制权已经交还FPGA。
|
||||||
|
- 不增加 `DPD_CTRL3_FAGC_SLOWDN`,确保恢复状态与当前冷启动后的状态完全一致。
|
||||||
|
- 不触发 `DPD_START`。
|
||||||
|
- 只修改整个DPD Update的最终出口,不修改phase之间的临时控制操作。
|
||||||
|
|
||||||
|
## 6. 验证步骤
|
||||||
|
|
||||||
|
1. 冷启动设备,不执行DPD Update,记录FAGC控制字或ORx `gain_index`的工作状态。
|
||||||
|
2. 执行一次完整DPD Update。
|
||||||
|
3. 确认DPD结束后日志出现:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[DPD] update complete, ORx FAGC tracking restored
|
||||||
|
```
|
||||||
|
|
||||||
|
4. DPD结束后改变反馈链路衰减,使ORx输入电平变化超过现有约±1 dB死区。
|
||||||
|
5. 确认FAGC控制字及ADRV9009 ORx `gain_index`重新变化并最终稳定。
|
||||||
|
6. 确认DPD系数没有被清除或重新训练,发射输出保持正常。
|
||||||
|
|
||||||
|
## 7. 验收标准
|
||||||
|
|
||||||
|
- DPD Update结束后,FAGC工作状态与冷启动且未执行DPD时一致。
|
||||||
|
- 反馈射频电平明显变化后,FAGC能够重新收敛。
|
||||||
|
- DPD训练阶段原有的FAGC锁定逻辑不变。
|
||||||
|
- 不涉及RTL和DAGC修改。
|
||||||
Executable → Regular
+46
@@ -39,7 +39,53 @@ 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=125T
|
||||||
|
SVN_VER_NUM=130R
|
||||||
|
|
||||||
|
#1.修正之前版本FWD/FSSI等显示问题;
|
||||||
|
#2.已经支持网页升级FPGA功能;
|
||||||
SVN_VER_NUM=131R
|
SVN_VER_NUM=131R
|
||||||
|
SVN_VER_NUM=132
|
||||||
|
|
||||||
|
#132R
|
||||||
|
#1.调整软件中AB反馈口的使用习惯和历史型号兼容;
|
||||||
|
#2.修正TSOIP参数开机加载问题;
|
||||||
|
#3.调整网页相关布局和显示内容(标题/频谱图/SNR/FSSI显示等内容);
|
||||||
|
SVN_VER_NUM=132R
|
||||||
|
|
||||||
|
SVN_VER_NUM=132RT1
|
||||||
|
SVN_VER_NUM=132RT2
|
||||||
|
|
||||||
|
|
||||||
|
#133R
|
||||||
|
#1.修正升级或执行网页System Reset可能导致频谱无法正常恢复问题;
|
||||||
|
#2.恢复网页端的LIMD和UIMD的固定显示偏置;
|
||||||
|
#3.调整 FSSI Refresh 开启时,频谱图和眼图停止更新;
|
||||||
|
#4.修正login页面title未正确显示问题;
|
||||||
|
SVN_VER_NUM=133R
|
||||||
|
|
||||||
|
#134R
|
||||||
|
#1.对RF IN A/B做了调整,适配最新版本260805的FPGA;
|
||||||
|
SVN_VER_NUM=134R
|
||||||
|
|
||||||
|
#1.合并补充overview页面,支持登录后自动跳转到overview页面;
|
||||||
|
SVN_VER_NUM=135R
|
||||||
|
|
||||||
|
#未正式发布,存在开机后SNR LIMD/UIMD不更新和显示问题
|
||||||
|
SVN_VER_NUM=136R
|
||||||
|
|
||||||
|
#加入偏置 切换频点降功率
|
||||||
|
SVN_VER_NUM=136RT
|
||||||
|
|
||||||
|
#加入偏置 切换频点不降功率
|
||||||
|
SVN_VER_NUM=136RT2
|
||||||
|
|
||||||
|
#更新473-887
|
||||||
|
SVN_VER_NUM=136RT3
|
||||||
|
|
||||||
|
SVN_VER_NUM=137R
|
||||||
|
|
||||||
|
SVN_VER_NUM=138R
|
||||||
|
|
||||||
OUT_FS_DIR := $(PWD)/../out/rootfs
|
OUT_FS_DIR := $(PWD)/../out/rootfs
|
||||||
UPGARDE_IMX_FS_DIR := SMOD
|
UPGARDE_IMX_FS_DIR := SMOD
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
### Auther : F
|
### Auther : F
|
||||||
### Date : 2021-12-7
|
### Date : 2021-12-7
|
||||||
### Function: Start anywave work
|
### Function: Start anywave work
|
||||||
echo " "
|
echo " "
|
||||||
echo " "
|
echo " "
|
||||||
echo "--------------------------------"
|
echo "--------------------------------"
|
||||||
@@ -20,8 +20,13 @@ echo "clk prepare..."
|
|||||||
sleep 5
|
sleep 5
|
||||||
echo "clk rdy"
|
echo "clk rdy"
|
||||||
|
|
||||||
init_ad9528_reset_gpio.sh
|
if imx_cfgad9528 --check-ready; then
|
||||||
imx_cfgad9528
|
echo "AD9528 already configured and locked; preserve live RF clocks"
|
||||||
|
else
|
||||||
|
echo "AD9528 not ready; run cold-start reset and configuration"
|
||||||
|
init_ad9528_reset_gpio.sh
|
||||||
|
imx_cfgad9528
|
||||||
|
fi
|
||||||
|
|
||||||
mkdir -p /aw/anywave/dpd_log
|
mkdir -p /aw/anywave/dpd_log
|
||||||
|
|
||||||
@@ -38,7 +43,7 @@ echo "== web start =="
|
|||||||
cd /aw/anywave/web/sh && ./start_web.sh
|
cd /aw/anywave/web/sh && ./start_web.sh
|
||||||
|
|
||||||
MODULE_LOG_PATH=/var/module_log/
|
MODULE_LOG_PATH=/var/module_log/
|
||||||
mkdir -p $MODULE_LOG_PATH
|
mkdir -p $MODULE_LOG_PATH
|
||||||
|
|
||||||
echo 10 > /sys/class/gpio/export
|
echo 10 > /sys/class/gpio/export
|
||||||
echo 11 > /sys/class/gpio/export
|
echo 11 > /sys/class/gpio/export
|
||||||
@@ -148,15 +153,3 @@ echo " "
|
|||||||
|
|
||||||
|
|
||||||
start_and_longlog /usr/sbin logrotateStart $MODULE_LOG_PATH logrotateStart.log 0 &
|
start_and_longlog /usr/sbin logrotateStart $MODULE_LOG_PATH logrotateStart.log 0 &
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -101,10 +101,13 @@ all: imx_atsc_app
|
|||||||
rm -rf ~/nfs/imx_atsc_app
|
rm -rf ~/nfs/imx_atsc_app
|
||||||
cp imx_atsc_app ~/nfs/
|
cp imx_atsc_app ~/nfs/
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
cp imx_atsc_app ../../out/upgrade_imx_fs/SMOD/src/aw/anywave/app/
|
cp imx_atsc_app ../../out/upgrade_imx_fs/SMOD/src/aw/anywave/app/
|
||||||
cp imx_atsc_app ../../out/rootfs/aw/anywave/app/
|
cp imx_atsc_app ../../out/rootfs/aw/anywave/app/
|
||||||
@echo "\n\n\t ATSC_APP intall ok\n\n"
|
cp runme.sh ../../out/upgrade_imx_fs/SMOD/runme.sh
|
||||||
|
cp anywave_exc_start.sh ../../out/upgrade_imx_fs/SMOD/src/aw/anywave_exc_start.sh
|
||||||
|
cp anywave_exc_start.sh ../../out/rootfs/aw/anywave_exc_start.sh
|
||||||
|
@echo "\n\n\t ATSC_APP intall ok\n\n"
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf *.o ./imx_atsc_app
|
rm -rf *.o ./imx_atsc_app
|
||||||
|
|||||||
Executable → Regular
+7
-1
@@ -340,7 +340,13 @@ int adrv9009_cfg(int which_9009)
|
|||||||
TAL_ADC_STITCHING|
|
TAL_ADC_STITCHING|
|
||||||
|
|
||||||
TAL_ORX_LO_DELAY | //add 260518 ORx 专用初始化校准
|
TAL_ORX_LO_DELAY | //add 260518 ORx 专用初始化校准
|
||||||
TAL_ORX_QEC_INIT //add 260518 ORx 专用初始化校准
|
/*
|
||||||
|
* PR16 hardware A/B test: keep the one-shot cold-boot
|
||||||
|
* QEC init, but do not enable TAL_TRACK_ORX1_QEC.
|
||||||
|
* Forced tracking reached IRR=58 dB / 2099 updates,
|
||||||
|
* while the exported ORx IQ stream became all zero.
|
||||||
|
*/
|
||||||
|
TAL_ORX_QEC_INIT
|
||||||
;
|
;
|
||||||
uint8_t errorFlag = 0;
|
uint8_t errorFlag = 0;
|
||||||
taliseErr_t mykError = TAL_ERR_OK;
|
taliseErr_t mykError = TAL_ERR_OK;
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
### Auther : F
|
||||||
|
### Date : 2021-12-7
|
||||||
|
### Function: Start anywave work
|
||||||
|
echo " "
|
||||||
|
echo " "
|
||||||
|
echo "--------------------------------"
|
||||||
|
echo " ... HELLO , ANYWAVE ... "
|
||||||
|
echo "--------------------------------"
|
||||||
|
echo " "
|
||||||
|
echo " "
|
||||||
|
|
||||||
|
setnetmac.sh
|
||||||
|
|
||||||
|
ifconfig eth0 192.168.1.143
|
||||||
|
ifconfig eth1 192.168.2.143
|
||||||
|
route add default gw 192.168.1.1
|
||||||
|
|
||||||
|
echo "clk prepare..."
|
||||||
|
sleep 5
|
||||||
|
echo "clk rdy"
|
||||||
|
|
||||||
|
if imx_cfgad9528 --check-ready; then
|
||||||
|
echo "AD9528 already configured and locked; preserve live RF clocks"
|
||||||
|
else
|
||||||
|
echo "AD9528 not ready; run cold-start reset and configuration"
|
||||||
|
init_ad9528_reset_gpio.sh
|
||||||
|
imx_cfgad9528
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p /aw/anywave/dpd_log
|
||||||
|
|
||||||
|
### Path for web upgrade
|
||||||
|
mkdir -p /aw/upgrade
|
||||||
|
mkdir -p /aw/tmp
|
||||||
|
mkdir -p /aw/anywave/cfg
|
||||||
|
mkdir -p /aw/anywave/cfg/bkp
|
||||||
|
mkdir -p /aw/anywave/dpd_curve
|
||||||
|
mkdir -p /aw/anywave/lib
|
||||||
|
mkdir -p /aw/log
|
||||||
|
mkdir -p /etc/logrotate.d/
|
||||||
|
echo "== web start =="
|
||||||
|
cd /aw/anywave/web/sh && ./start_web.sh
|
||||||
|
|
||||||
|
MODULE_LOG_PATH=/var/module_log/
|
||||||
|
mkdir -p $MODULE_LOG_PATH
|
||||||
|
|
||||||
|
echo 10 > /sys/class/gpio/export
|
||||||
|
echo 11 > /sys/class/gpio/export
|
||||||
|
echo 12 > /sys/class/gpio/export
|
||||||
|
echo 13 > /sys/class/gpio/export
|
||||||
|
echo 14 > /sys/class/gpio/export
|
||||||
|
echo 15 > /sys/class/gpio/export
|
||||||
|
insmod /aw/anywave/lib/key-bsp.ko
|
||||||
|
|
||||||
|
#logconf
|
||||||
|
LOG_CONF_FILE=/etc/logrotate.d/log.conf
|
||||||
|
rm -f $LOG_CONF_FILE
|
||||||
|
set_log_conf()
|
||||||
|
{
|
||||||
|
echo "$1$2{
|
||||||
|
$3
|
||||||
|
daily
|
||||||
|
rotate 3
|
||||||
|
size 1K
|
||||||
|
dateext
|
||||||
|
extension .log
|
||||||
|
compress
|
||||||
|
notifempty
|
||||||
|
sharedscripts
|
||||||
|
create 644 root root
|
||||||
|
sharedscripts
|
||||||
|
postrotate
|
||||||
|
$4
|
||||||
|
endscript
|
||||||
|
}
|
||||||
|
" >> $LOG_CONF_FILE
|
||||||
|
}
|
||||||
|
|
||||||
|
start_and_log()
|
||||||
|
{
|
||||||
|
if [ $# -lt 4 ];then
|
||||||
|
echo 'Usage:start_and_log command_path command_with_param log_path log_name enable_output &'
|
||||||
|
echo "For example:"
|
||||||
|
echo "start_and_log /app ./app /var/module_log/ app.log 0 &"
|
||||||
|
echo "or start_and_log /app ./app /var/module_log/ app.log 1 &"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
set_log_conf $3 $4
|
||||||
|
cd $1 && $2 2>&1 | loglog $3 $4 $5 $6
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
set_longlog_conf()
|
||||||
|
{
|
||||||
|
echo "$1$2{
|
||||||
|
$3
|
||||||
|
daily
|
||||||
|
rotate 3
|
||||||
|
size 500K
|
||||||
|
dateext
|
||||||
|
extension .log
|
||||||
|
compress
|
||||||
|
notifempty
|
||||||
|
create 644 root root
|
||||||
|
sharedscripts
|
||||||
|
postrotate
|
||||||
|
$4
|
||||||
|
endscript
|
||||||
|
}
|
||||||
|
" >> $LOG_CONF_FILE
|
||||||
|
}
|
||||||
|
start_and_longlog()
|
||||||
|
{
|
||||||
|
if [ $# -lt 4 ];then
|
||||||
|
echo 'Usage:start_and_longlog command_path command_with_param log_path log_name enable_output &'
|
||||||
|
echo "For example:"
|
||||||
|
echo "start_and_longlog /app ./app /var/module_log/ app.log 0 &"
|
||||||
|
echo "or start_and_longlog /app ./app /var/module_log/ app.log 1 &"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
set_longlog_conf $3 $4
|
||||||
|
cd $1 && $2 2>&1 | loglog $3 $4 $5 $6
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
echo "== app start =="
|
||||||
|
#cd /aw/anywave/app/ && ./imx_atsc_app&
|
||||||
|
start_and_log /aw/anywave/app ./imx_atsc_app $MODULE_LOG_PATH imx_atsc_app.log 1 &
|
||||||
|
|
||||||
|
echo "== daemon_system start =="
|
||||||
|
#daemon_system
|
||||||
|
start_and_longlog /usr/sbin daemon_system $MODULE_LOG_PATH daemon_system.log 1 &
|
||||||
|
|
||||||
|
echo " "
|
||||||
|
echo "== start finished =="
|
||||||
|
echo " "
|
||||||
|
|
||||||
|
|
||||||
|
#----------daemon_app-------------
|
||||||
|
#ProcNumber=`ps -ef |grep -w daemon_app|grep -v grep|wc -l`
|
||||||
|
#if [ $ProcNumber -le 0 ];then
|
||||||
|
#/usr/sbin/daemon_app &
|
||||||
|
#echo "daemon_app first run"
|
||||||
|
#else
|
||||||
|
#echo "daemon_app have been run"
|
||||||
|
#fi
|
||||||
|
|
||||||
|
|
||||||
|
start_and_longlog /usr/sbin logrotateStart $MODULE_LOG_PATH logrotateStart.log 0 &
|
||||||
Executable → Regular
+3180
-3170
File diff suppressed because it is too large
Load Diff
Executable → Regular
+115
-28
@@ -85,6 +85,26 @@ static const int32 we_power[]=
|
|||||||
1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000
|
1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Hardware V1.1 VHF calibration, channels 2..13. */
|
||||||
|
static const uint16 we_power_v1_1_v[]=
|
||||||
|
{
|
||||||
|
143,98,66,38,28,52,52,59,62,64,66,74
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hardware V1.1 UHF calibration, channels 14..83 (473..887 MHz).
|
||||||
|
* Values are the raw WE register values measured with 563 MHz as 0 dB.
|
||||||
|
* 1 raw step = 0.05 dB; 300 = 0 dB. V1.1 may use values up to 720
|
||||||
|
* while V1.0 remains limited to 500.
|
||||||
|
*/
|
||||||
|
static const uint16 we_power_v1_1[]=
|
||||||
|
{
|
||||||
|
224,226,231,231,234,235,239,244,245,248,254,257,264,268,269,275,281,282,
|
||||||
|
292,292,295,301,304,312,317,322,329,335,340,345,347,350,354,358,361,366,
|
||||||
|
370,377,382,385,389,391,394,399,403,405,413,420,423,432,437,447,454,463,
|
||||||
|
480,495,512,528,547,565,590,614,635,662,687,715,500,500,500,500
|
||||||
|
};
|
||||||
|
|
||||||
/*auto ver
|
/*auto ver
|
||||||
{
|
{
|
||||||
-405, -361, -349, -314, -332, -303, -295, -285, -172, -257,
|
-405, -361, -349, -314, -332, -303, -295, -285, -172, -257,
|
||||||
@@ -192,6 +212,7 @@ void e2rom_save_sys(S_SYSTEM_PARA *ppara)
|
|||||||
|
|
||||||
c256_bytewrite(EEPROM_ADRV9009_ORX_MANU_GAIN, ppara->orx_manu_gain&0xff);
|
c256_bytewrite(EEPROM_ADRV9009_ORX_MANU_GAIN, ppara->orx_manu_gain&0xff);
|
||||||
c256_bytewrite(EEPROM_SPEC_ENABLE, ppara->spec_enable ? 1 : 0);
|
c256_bytewrite(EEPROM_SPEC_ENABLE, ppara->spec_enable ? 1 : 0);
|
||||||
|
c256_bytewrite(EEPROM_HARDWARE_VERSION, ppara->hardware_version);
|
||||||
}
|
}
|
||||||
void e2rom_save_power(S_RF_PARA *ppara)
|
void e2rom_save_power(S_RF_PARA *ppara)
|
||||||
{
|
{
|
||||||
@@ -398,18 +419,36 @@ void e2rom_save_wd(S_WD_PARA *ppara)
|
|||||||
c256_bytewrite(EEPROM_WD_ADDR+i,*pchar++);
|
c256_bytewrite(EEPROM_WD_ADDR+i,*pchar++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
uint16 get_we_power_max(void)
|
||||||
|
{
|
||||||
|
return (atsc_para.system_para.hardware_version == HARDWARE_VERSION_V1_1) ?
|
||||||
|
WE_POWER_MAX_V1_1 : WE_POWER_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16 e2rom_legacy_power_addr(void)
|
||||||
|
{
|
||||||
|
return (atsc_para.wl_para.exc_5x == 0) ?
|
||||||
|
EEPROM_CH2_POWER_ADDR : EEPROM_CH1_POWER_ADDR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16 e2rom_active_power_addr(void)
|
||||||
|
{
|
||||||
|
if(atsc_para.system_para.hardware_version == HARDWARE_VERSION_V1_1)
|
||||||
|
return EEPROM_CH_POWER_V1_1_ADDR;
|
||||||
|
|
||||||
|
return e2rom_legacy_power_addr();
|
||||||
|
}
|
||||||
|
|
||||||
void e2rom_save_leak(uint32 ch, S_WE_PARA *ppara)
|
void e2rom_save_leak(uint32 ch, S_WE_PARA *ppara)
|
||||||
{
|
{
|
||||||
if(atsc_para.wl_para.exc_5x==0)
|
uint16 base = e2rom_active_power_addr();
|
||||||
{
|
uint16 power_max = get_we_power_max();
|
||||||
c256_bytewrite(EEPROM_CH2_POWER_ADDR+ch*2+0,ppara->power>>8);
|
|
||||||
c256_bytewrite(EEPROM_CH2_POWER_ADDR+ch*2+1,ppara->power);
|
if(ppara->power > power_max)
|
||||||
}
|
ppara->power = power_max;
|
||||||
else
|
|
||||||
{
|
c256_bytewrite(base+ch*2+0,ppara->power>>8);
|
||||||
c256_bytewrite(EEPROM_CH1_POWER_ADDR+ch*2+0,ppara->power>>8);
|
c256_bytewrite(base+ch*2+1,ppara->power);
|
||||||
c256_bytewrite(EEPROM_CH1_POWER_ADDR+ch*2+1,ppara->power);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
void e2rom_save_we(S_WE_PARA *ppara)
|
void e2rom_save_we(S_WE_PARA *ppara)
|
||||||
{
|
{
|
||||||
@@ -1145,8 +1184,16 @@ void e2rom_read_system(S_SYSTEM_PARA *ppara)
|
|||||||
ppara->orx_manu_gain = orx_gain_max;
|
ppara->orx_manu_gain = orx_gain_max;
|
||||||
|
|
||||||
ppara->spec_enable = 1;
|
ppara->spec_enable = 1;
|
||||||
|
ppara->hardware_version = c256_byteread(EEPROM_HARDWARE_VERSION);
|
||||||
|
if(ppara->hardware_version >= HARDWARE_VERSION_END)
|
||||||
|
{
|
||||||
|
ppara->hardware_version = DEFAULT_HARDWARE_VERSION;
|
||||||
|
c256_bytewrite(EEPROM_HARDWARE_VERSION, ppara->hardware_version);
|
||||||
|
}
|
||||||
|
|
||||||
printf("load agc_att=%d %fdb adrv9009_att=%d orx_manu_gain=%d spec_enable=%d\n", ppara->agc_att, -0.5*ppara->agc_att, ppara->adrv9009_att, ppara->orx_manu_gain, ppara->spec_enable);
|
printf("load agc_att=%d %fdb adrv9009_att=%d orx_manu_gain=%d spec_enable=%d hardware=V1.%d\n",
|
||||||
|
ppara->agc_att, -0.5*ppara->agc_att, ppara->adrv9009_att,
|
||||||
|
ppara->orx_manu_gain, ppara->spec_enable, ppara->hardware_version);
|
||||||
}
|
}
|
||||||
static void bak_e2rom_read_rf(S_RF_PARA *ppara)
|
static void bak_e2rom_read_rf(S_RF_PARA *ppara)
|
||||||
{
|
{
|
||||||
@@ -2008,6 +2055,26 @@ static void e2rom_fac_power_rst_3x(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void e2rom_fac_power_rst_v1_1(void)
|
||||||
|
{
|
||||||
|
uint32 i;
|
||||||
|
uint16 power;
|
||||||
|
|
||||||
|
for(i=FREQ_MIN_V1; i<=FREQ_MAX_V3; i++)
|
||||||
|
{
|
||||||
|
power = we_power_v1_1_v[i-FREQ_MIN_V1];
|
||||||
|
c256_bytewrite(EEPROM_CH_POWER_V1_1_ADDR+i*2+0, power>>8);
|
||||||
|
c256_bytewrite(EEPROM_CH_POWER_V1_1_ADDR+i*2+1, power);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i=FREQ_MIN_U; i<=FREQ_MAX_U; i++)
|
||||||
|
{
|
||||||
|
power = we_power_v1_1[i-FREQ_MIN_U];
|
||||||
|
c256_bytewrite(EEPROM_CH_POWER_V1_1_ADDR+i*2+0, power>>8);
|
||||||
|
c256_bytewrite(EEPROM_CH_POWER_V1_1_ADDR+i*2+1, power);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static uint16 e2rom_read_fac_power_default_3x(uint32 chn)
|
static uint16 e2rom_read_fac_power_default_3x(uint32 chn)
|
||||||
{
|
{
|
||||||
uint16 power;
|
uint16 power;
|
||||||
@@ -2245,7 +2312,7 @@ static void e2rom_read_dpd_para(S_DPD_PARA *ppara)
|
|||||||
//gl default val 0x0370
|
//gl default val 0x0370
|
||||||
ppara->gl = c256_byteread_default(EEPROM_DPD_GAIN_LIMIT_H, 0x03)<<8 | c256_byteread_default(EEPROM_DPD_GAIN_LIMIT_L, 0x70);
|
ppara->gl = c256_byteread_default(EEPROM_DPD_GAIN_LIMIT_H, 0x03)<<8 | c256_byteread_default(EEPROM_DPD_GAIN_LIMIT_L, 0x70);
|
||||||
|
|
||||||
ppara->dpd_ctl = c256_byteread_default(EEPROM_DPD_CTL_TST, 0x03);
|
ppara->dpd_ctl = c256_byteread_default(EEPROM_DPD_CTL_TST, 0x07);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2401,11 +2468,13 @@ void e2rom_read_tsoip(int tsoip_index, S_TSOIP_CONFIG_PARA *ppara)
|
|||||||
log_printf(LOG_DEBUG,I_NOUSED,"TSOIP_%d ppara->receive_type=%s\n", tsoip_index+1, ppara->receive_type==0?"multi":"singl" );
|
log_printf(LOG_DEBUG,I_NOUSED,"TSOIP_%d ppara->receive_type=%s\n", tsoip_index+1, ppara->receive_type==0?"multi":"singl" );
|
||||||
|
|
||||||
ppara->fec = (c256_byteread(EEPROM_FEC_ADDR)<<8)|c256_byteread(EEPROM_FEC_ADDR+1);
|
ppara->fec = (c256_byteread(EEPROM_FEC_ADDR)<<8)|c256_byteread(EEPROM_FEC_ADDR+1);
|
||||||
if(ppara->fec >= 1) {
|
if(ppara->fec > 1) {
|
||||||
ppara->fec = 0;
|
ppara->fec = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ppara->delay = (c256_byteread(EEPROM_FEC_DLY_ADDR)<<8)|c256_byteread(EEPROM_FEC_DLY_ADDR+1);
|
ppara->delay = (c256_byteread(EEPROM_FEC_DLY_ADDR)<<8)|c256_byteread(EEPROM_FEC_DLY_ADDR+1);
|
||||||
|
ppara->row = (c256_byteread(EEPROM_ROW_ADDR)<<8)|c256_byteread(EEPROM_ROW_ADDR+1);
|
||||||
|
ppara->col = (c256_byteread(EEPROM_COL_ADDR)<<8)|c256_byteread(EEPROM_COL_ADDR+1);
|
||||||
ppara->sfn_mfn_sel = c256_byteread(EEPROM_SFN_MFN_SEL);
|
ppara->sfn_mfn_sel = c256_byteread(EEPROM_SFN_MFN_SEL);
|
||||||
if(ppara->sfn_mfn_sel != NET_SFN && ppara->sfn_mfn_sel != NET_MFN) {
|
if(ppara->sfn_mfn_sel != NET_SFN && ppara->sfn_mfn_sel != NET_MFN) {
|
||||||
ppara->sfn_mfn_sel = NET_SFN;
|
ppara->sfn_mfn_sel = NET_SFN;
|
||||||
@@ -2858,6 +2927,7 @@ void e2rom_load_default_param(unsigned char menu)
|
|||||||
atsc_para.system_para.gps = 0;
|
atsc_para.system_para.gps = 0;
|
||||||
atsc_para.system_para.test = TEST_OFF;
|
atsc_para.system_para.test = TEST_OFF;
|
||||||
atsc_para.system_para.spec_enable = 1;
|
atsc_para.system_para.spec_enable = 1;
|
||||||
|
atsc_para.system_para.hardware_version = DEFAULT_HARDWARE_VERSION;
|
||||||
//atsc_para.system_para.net = 1; //MFN;
|
//atsc_para.system_para.net = 1; //MFN;
|
||||||
atsc_para.system_para.ctrl = CTRL_REMOTE; // remote
|
atsc_para.system_para.ctrl = CTRL_REMOTE; // remote
|
||||||
|
|
||||||
@@ -3218,7 +3288,7 @@ void e2rom_load_default_param(unsigned char menu)
|
|||||||
atsc_para.dpd_para.phase6_minute=3;//TM
|
atsc_para.dpd_para.phase6_minute=3;//TM
|
||||||
atsc_para.dpd_para.wpp_th =3900;//6000
|
atsc_para.dpd_para.wpp_th =3900;//6000
|
||||||
atsc_para.dpd_para.gl =0x0370;//0x0A00
|
atsc_para.dpd_para.gl =0x0370;//0x0A00
|
||||||
atsc_para.dpd_para.dpd_ctl =3;
|
atsc_para.dpd_para.dpd_ctl =0x07;
|
||||||
//cttb_para.dpd_para.amp_sat_sqr=0;//该值由amp_sat转换而成
|
//cttb_para.dpd_para.amp_sat_sqr=0;//该值由amp_sat转换而成
|
||||||
e2rom_save_dpd_para(&atsc_para.dpd_para);
|
e2rom_save_dpd_para(&atsc_para.dpd_para);
|
||||||
//----------------------------
|
//----------------------------
|
||||||
@@ -3241,6 +3311,7 @@ void e2rom_load_default_param(unsigned char menu)
|
|||||||
|
|
||||||
case MENU_POWER:
|
case MENU_POWER:
|
||||||
atsc_para.rf_para.power = RF_POWER_ZERO;
|
atsc_para.rf_para.power = RF_POWER_ZERO;
|
||||||
|
e2rom_fac_power_rst_v1_1();
|
||||||
if(atsc_para.wl_para.exc_5x==0)
|
if(atsc_para.wl_para.exc_5x==0)
|
||||||
{
|
{
|
||||||
atsc_para.rf_para.db = DEFAULT_RF_POWER_DB_3X;
|
atsc_para.rf_para.db = DEFAULT_RF_POWER_DB_3X;
|
||||||
@@ -3268,27 +3339,42 @@ void e2rom_load_default_param(unsigned char menu)
|
|||||||
|
|
||||||
void e2rom_read_leak(uint32 chn,S_WE_PARA *ppara)
|
void e2rom_read_leak(uint32 chn,S_WE_PARA *ppara)
|
||||||
{
|
{
|
||||||
uint8 data = 0;
|
uint8 data;
|
||||||
|
uint16 base = e2rom_active_power_addr();
|
||||||
|
uint16 power_max = get_we_power_max();
|
||||||
|
|
||||||
if(atsc_para.wl_para.exc_5x==0)
|
data = c256_byteread(base+chn*2+0);
|
||||||
|
ppara->power = (data<<8)|c256_byteread(base+chn*2+1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Populate an upgraded unit's missing V1.1 entry from the measured
|
||||||
|
* V1.1 defaults and persist it in the independent V1.1 EEPROM table.
|
||||||
|
*/
|
||||||
|
if((ppara->power>power_max) &&
|
||||||
|
(atsc_para.system_para.hardware_version == HARDWARE_VERSION_V1_1))
|
||||||
{
|
{
|
||||||
data = c256_byteread(EEPROM_CH2_POWER_ADDR+chn*2+0);
|
if((chn>=FREQ_MIN_V1)&&(chn<=FREQ_MAX_V3))
|
||||||
ppara->power = (data<<8)|c256_byteread(EEPROM_CH2_POWER_ADDR+chn*2+1);
|
|
||||||
|
|
||||||
if(ppara->power>WE_POWER_MAX)
|
|
||||||
{
|
{
|
||||||
ppara->power = e2rom_read_fac_power_default_3x(chn);
|
ppara->power = we_power_v1_1_v[chn-FREQ_MIN_V1];
|
||||||
|
}
|
||||||
|
else if((chn>=FREQ_MIN_U)&&(chn<=FREQ_MAX_U))
|
||||||
|
{
|
||||||
|
ppara->power = we_power_v1_1[chn-FREQ_MIN_U];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ppara->power<=power_max)
|
||||||
|
{
|
||||||
|
c256_bytewrite(EEPROM_CH_POWER_V1_1_ADDR+chn*2+0,ppara->power>>8);
|
||||||
|
c256_bytewrite(EEPROM_CH_POWER_V1_1_ADDR+chn*2+1,ppara->power);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
data = c256_byteread(EEPROM_CH1_POWER_ADDR+chn*2+0);
|
|
||||||
ppara->power = (data<<8)|c256_byteread(EEPROM_CH1_POWER_ADDR+chn*2+1);
|
|
||||||
|
|
||||||
if(ppara->power>WE_POWER_MAX)
|
if(ppara->power>power_max)
|
||||||
{
|
{
|
||||||
|
if(atsc_para.wl_para.exc_5x==0)
|
||||||
|
ppara->power = e2rom_read_fac_power_default_3x(chn);
|
||||||
|
else
|
||||||
ppara->power = e2rom_read_fac_power_default_5x(chn);
|
ppara->power = e2rom_read_fac_power_default_5x(chn);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
extern void fpga_read_system(S_SYSTEM_PARA *ppara);
|
extern void fpga_read_system(S_SYSTEM_PARA *ppara);
|
||||||
@@ -3441,6 +3527,7 @@ void e2rom_read_all_para(void)
|
|||||||
e2rom_load_default_param(2);
|
e2rom_load_default_param(2);
|
||||||
e2rom_load_default_param(3);
|
e2rom_load_default_param(3);
|
||||||
|
|
||||||
|
e2rom_fac_power_rst_v1_1();
|
||||||
if(atsc_para.wl_para.exc_5x==0) //此处是将非当前标准的we功率也置为默认值.
|
if(atsc_para.wl_para.exc_5x==0) //此处是将非当前标准的we功率也置为默认值.
|
||||||
{
|
{
|
||||||
e2rom_fac_power_rst_5x();
|
e2rom_fac_power_rst_5x();
|
||||||
|
|||||||
+32
-8
@@ -80,11 +80,26 @@ void uart_debug(uint8 *pbuf,uint32 len)
|
|||||||
#define HMC472_STEP_MDB 500
|
#define HMC472_STEP_MDB 500
|
||||||
#define HMC472_STEPS_MAX 63
|
#define HMC472_STEPS_MAX 63
|
||||||
#define ADRV9009_ATT_BASE 8000
|
#define ADRV9009_ATT_BASE 8000
|
||||||
|
#define ADRV9009_ATT_MAX 40000
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The post-reset measurements were made in V1.0 software mode and established
|
||||||
|
* that LCD -11 dB produces 0 dBm at the 563 MHz reference point. Therefore
|
||||||
|
* V1.1 needs 11 dB additional attenuation for LCD 0 dB to map to the same
|
||||||
|
* physical 0 dBm output.
|
||||||
|
*/
|
||||||
|
#define V1_1_OUTPUT_GAIN_MDB 11000
|
||||||
|
|
||||||
static int32 g_last_total_mdb = -1;
|
static int32 g_last_total_mdb = -1;
|
||||||
static uint16 g_last_adrv9009_att = 0xFFFF;
|
static uint16 g_last_adrv9009_att = 0xFFFF;
|
||||||
static volatile uint8 g_adrv9009_ready = 0;
|
static volatile uint8 g_adrv9009_ready = 0;
|
||||||
|
|
||||||
|
static int32 hardware_output_gain_mdb(void)
|
||||||
|
{
|
||||||
|
return atsc_para.system_para.hardware_version == HARDWARE_VERSION_V1_1
|
||||||
|
? V1_1_OUTPUT_GAIN_MDB : 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Validate a stored power command only after the current channel's WE power
|
* Validate a stored power command only after the current channel's WE power
|
||||||
* has been loaded. A raw attenuation of zero is a valid physical maximum;
|
* has been loaded. A raw attenuation of zero is a valid physical maximum;
|
||||||
@@ -98,6 +113,7 @@ static uint8 normalize_new_power_rf_db(S_WE_PARA *pwe, S_RF_PARA *prf)
|
|||||||
raw_total_mdb = (int32)RAW_MAX_OUTPUT_MDB
|
raw_total_mdb = (int32)RAW_MAX_OUTPUT_MDB
|
||||||
- ((int32)prf->db + (int32)pwe->power - (int32)REF_POWER_UNITS)
|
- ((int32)prf->db + (int32)pwe->power - (int32)REF_POWER_UNITS)
|
||||||
* (int32)PWR_UNIT_TO_MDB;
|
* (int32)PWR_UNIT_TO_MDB;
|
||||||
|
raw_total_mdb += hardware_output_gain_mdb();
|
||||||
|
|
||||||
if(raw_total_mdb >= 0)
|
if(raw_total_mdb >= 0)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1057,6 +1073,7 @@ uint16 fpga_limit_power_target(S_WE_PARA *pwe, S_RF_PARA *prf, uint16 target_pow
|
|||||||
total_mdb = (int32)RAW_MAX_OUTPUT_MDB
|
total_mdb = (int32)RAW_MAX_OUTPUT_MDB
|
||||||
- ((int32)prf->db + (int32)pwe->power - (int32)REF_POWER_UNITS)
|
- ((int32)prf->db + (int32)pwe->power - (int32)REF_POWER_UNITS)
|
||||||
* (int32)PWR_UNIT_TO_MDB;
|
* (int32)PWR_UNIT_TO_MDB;
|
||||||
|
total_mdb += hardware_output_gain_mdb();
|
||||||
|
|
||||||
if(total_mdb <= 0)
|
if(total_mdb <= 0)
|
||||||
return prf->power;
|
return prf->power;
|
||||||
@@ -1087,12 +1104,13 @@ void fpga_save_power(S_WE_PARA *pwe,S_RF_PARA *prf)
|
|||||||
total_mdb = (int32)RAW_MAX_OUTPUT_MDB
|
total_mdb = (int32)RAW_MAX_OUTPUT_MDB
|
||||||
- ((int32)prf->db + (int32)pwe->power - (int32)REF_POWER_UNITS)
|
- ((int32)prf->db + (int32)pwe->power - (int32)REF_POWER_UNITS)
|
||||||
* (int32)PWR_UNIT_TO_MDB;
|
* (int32)PWR_UNIT_TO_MDB;
|
||||||
|
total_mdb += hardware_output_gain_mdb();
|
||||||
|
|
||||||
if(total_mdb < 0)
|
if(total_mdb < 0)
|
||||||
total_mdb = 0;
|
total_mdb = 0;
|
||||||
|
|
||||||
if(total_mdb > (int32)(ADRV9009_ATT_BASE + HMC472_STEPS_MAX * HMC472_STEP_MDB))
|
if(total_mdb > (int32)(ADRV9009_ATT_MAX + HMC472_STEPS_MAX * HMC472_STEP_MDB))
|
||||||
total_mdb = ADRV9009_ATT_BASE + HMC472_STEPS_MAX * HMC472_STEP_MDB;
|
total_mdb = ADRV9009_ATT_MAX + HMC472_STEPS_MAX * HMC472_STEP_MDB;
|
||||||
|
|
||||||
if(total_mdb >= (int32)ADRV9009_ATT_BASE)
|
if(total_mdb >= (int32)ADRV9009_ATT_BASE)
|
||||||
{
|
{
|
||||||
@@ -1100,7 +1118,13 @@ void fpga_save_power(S_WE_PARA *pwe,S_RF_PARA *prf)
|
|||||||
agc_att_val = (uint8)(hmc_extra / HMC472_STEP_MDB);
|
agc_att_val = (uint8)(hmc_extra / HMC472_STEP_MDB);
|
||||||
if(agc_att_val > HMC472_STEPS_MAX)
|
if(agc_att_val > HMC472_STEPS_MAX)
|
||||||
agc_att_val = HMC472_STEPS_MAX;
|
agc_att_val = HMC472_STEPS_MAX;
|
||||||
adrv9009_att_val = (uint16)(ADRV9009_ATT_BASE + (hmc_extra % HMC472_STEP_MDB));
|
|
||||||
|
/*
|
||||||
|
* Keep ADRV9009 near its 8000 mdB optimum while HMC472 has range.
|
||||||
|
* Once HMC472 reaches 63, use the remaining ADRV9009 range up to
|
||||||
|
* 40000 mdB instead of clipping the combined attenuation at 39500.
|
||||||
|
*/
|
||||||
|
adrv9009_att_val = (uint16)(total_mdb - agc_att_val * HMC472_STEP_MDB);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1141,6 +1165,8 @@ void fpga_save_power(S_WE_PARA *pwe,S_RF_PARA *prf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
g_last_total_mdb = total_mdb;
|
g_last_total_mdb = total_mdb;
|
||||||
|
printf("[power] hardware=V1.%d offset_mdb=%d\n",
|
||||||
|
atsc_para.system_para.hardware_version, hardware_output_gain_mdb());
|
||||||
|
|
||||||
printf("[power] we=%3d db=%3d total_mdb=%5d | agc_att=%d adrv_att=%d\n",
|
printf("[power] we=%3d db=%3d total_mdb=%5d | agc_att=%d adrv_att=%d\n",
|
||||||
pwe->power, prf->db, total_mdb, agc_att_val, adrv9009_att_val);
|
pwe->power, prf->db, total_mdb, agc_att_val, adrv9009_att_val);
|
||||||
@@ -1717,11 +1743,9 @@ void config_fpga_para(void)
|
|||||||
|
|
||||||
fpga_save_all_para(TRUE);
|
fpga_save_all_para(TRUE);
|
||||||
|
|
||||||
/* TSOIP ---20220214 s ---*/
|
/* TSOIP settings have already been restored by e2rom_read_all_para().
|
||||||
// PR03: force TSoIP unicast defaults (192.168.2.200/single) onto BOTH cores every boot
|
* Do not load defaults here: that routine also writes the defaults back to
|
||||||
extern void e2rom_load_default_param_t_tsoip(int tsoip_index, uint32 type);
|
* EEPROM and would erase the values set from the web page on every reboot. */
|
||||||
e2rom_load_default_param_t_tsoip(TSOIP_1, 0);
|
|
||||||
e2rom_load_default_param_t_tsoip(TSOIP_2, 0);
|
|
||||||
|
|
||||||
tsoip_byte_write(TSOIP_1, I2C_REG_ADDRES, 0x11);
|
tsoip_byte_write(TSOIP_1, I2C_REG_ADDRES, 0x11);
|
||||||
delay_ms(200);//delay_ms(6000);
|
delay_ms(200);//delay_ms(6000);
|
||||||
|
|||||||
Executable → Regular
+1
-2
@@ -980,8 +980,7 @@ void fpga_para_init(void)
|
|||||||
{
|
{
|
||||||
if(atsc_para.rf_para.rf && (adrv9009_set_tx_path_enable(1) != OK))
|
if(atsc_para.rf_para.rf && (adrv9009_set_tx_path_enable(1) != OK))
|
||||||
{
|
{
|
||||||
log_printf(LOG_ERR, I_SYS_STATUS,
|
log_printf(LOG_ERR, I_SYS_STATUS, "ADRV9009 TX enable failed, continue restoring requested RF state\n");
|
||||||
"ADRV9009 TX enable failed, continue restoring requested RF state\n");
|
|
||||||
}
|
}
|
||||||
fpga_boot_rf_hold_release();
|
fpga_boot_rf_hold_release();
|
||||||
}
|
}
|
||||||
|
|||||||
Executable → Regular
+27
@@ -52,6 +52,33 @@ uint8 orx_fssi_from_gain_index(uint8 gi, uint8 gi_min, uint8 gi_max)
|
|||||||
return (uint8)(pct + 0.5f);
|
return (uint8)(pct + 0.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8 orx_fssi_from_power(uint16 fagc_cw, uint16 rms, uint16 rms_ref,
|
||||||
|
uint8 gi_min, uint8 gi_max)
|
||||||
|
{
|
||||||
|
float span_db; /* 量程(dB): (gi_max-gi_min) 个 0.5 dB 档 */
|
||||||
|
float p_db; /* 相对设定点的 ORx 输入功率(dB) */
|
||||||
|
float pct;
|
||||||
|
|
||||||
|
if (gi_max <= gi_min)
|
||||||
|
return 0; /* 防除零的退化入参 */
|
||||||
|
if (rms_ref == 0)
|
||||||
|
rms_ref = 1; /* 防 log10(0); 调用方不应传 0 */
|
||||||
|
|
||||||
|
/* 粗档 0.5*cw_hi 与细档 10*log10(rms/rms_ref) 同向(见 orx_power_combine_db
|
||||||
|
* 的方向说明), 直接相加。rms==rms_ref 时细档为 0, 退化成纯 gain index 版。 */
|
||||||
|
p_db = orx_power_combine_db(fagc_cw, rms)
|
||||||
|
- 10.0f * log10f((float)rms_ref);
|
||||||
|
|
||||||
|
span_db = 0.5f * (float)(gi_max - gi_min);
|
||||||
|
|
||||||
|
pct = p_db * 100.0f / span_db;
|
||||||
|
if (pct <= 0.0f)
|
||||||
|
return 0;
|
||||||
|
if (pct >= 100.0f)
|
||||||
|
return 100;
|
||||||
|
return (uint8)(pct + 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
int16 orx_pwr_halfdb(uint16 rms)
|
int16 orx_pwr_halfdb(uint16 rms)
|
||||||
{
|
{
|
||||||
float v = (float)rms;
|
float v = (float)rms;
|
||||||
|
|||||||
@@ -45,6 +45,11 @@
|
|||||||
|
|
||||||
#define USE_UART_DEV_LINK 1
|
#define USE_UART_DEV_LINK 1
|
||||||
|
|
||||||
|
/* IQ spectrum/eye-analysis periodic diagnostics on the debug UART.
|
||||||
|
* Keep disabled in normal operation; set to 1 only when timing/packet
|
||||||
|
* continuity troubleshooting is required. */
|
||||||
|
#define IQ_ANALYSIS_UART_DEBUG 0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prefer the local key/LCD path when the single-core CPU is busy with IQ
|
* Prefer the local key/LCD path when the single-core CPU is busy with IQ
|
||||||
* analysis. Set to 0 to restore the original thread priorities.
|
* analysis. Set to 0 to restore the original thread priorities.
|
||||||
|
|||||||
+250
-242
@@ -1,242 +1,250 @@
|
|||||||
#ifndef _DPD_PROC_H_
|
#ifndef _DPD_PROC_H_
|
||||||
#define _DPD_PROC_H_
|
#define _DPD_PROC_H_
|
||||||
|
|
||||||
#include "type.h"
|
#include "type.h"
|
||||||
|
|
||||||
#define DPD_LOG_ENABLE 1
|
#define DPD_LOG_ENABLE 1
|
||||||
#define DPD_LOG_FILE "/aw/anywave/dpd_log/log"
|
#define DPD_LOG_FILE "/aw/anywave/dpd_log/log"
|
||||||
|
|
||||||
#define FPGA_ADDR_DPD_LOG_TRIG 0x15
|
#define FPGA_ADDR_DPD_LOG_TRIG 0x15
|
||||||
//----Write Only--------------
|
//----Write Only--------------
|
||||||
//ok
|
//ok
|
||||||
#define FPGA_ADDR_DPD_CTRL1 0x20
|
#define FPGA_ADDR_DPD_CTRL1 0x20
|
||||||
#define FPGA_ADDR_DPD_CTRL2 0x21
|
#define FPGA_ADDR_DPD_CTRL2 0x21
|
||||||
#define FPGA_ADDR_DPD_CTRL3 0x22
|
#define FPGA_ADDR_DPD_CTRL3 0x22
|
||||||
//ok
|
//ok
|
||||||
#define FPGA_ADDR_DPD_IQB_UPD 0x23
|
#define FPGA_ADDR_DPD_IQB_UPD 0x23
|
||||||
#define FPGA_ADDR_DPD_CFG_STEP 0x24
|
#define FPGA_ADDR_DPD_CFG_STEP 0x24
|
||||||
|
|
||||||
|
|
||||||
//wpp
|
//wpp
|
||||||
#define FPGA_ADDR_WPP_EN_CLR 0x0d
|
#define FPGA_ADDR_WPP_EN_CLR 0x0d
|
||||||
#define FPGA_ADDR_WPP_TH_L 0x0e
|
#define FPGA_ADDR_WPP_TH_L 0x0e
|
||||||
#define FPGA_ADDR_WPP_TH_H 0x0f
|
#define FPGA_ADDR_WPP_TH_H 0x0f
|
||||||
|
|
||||||
#define MASK_WPP_ENABLE (1)
|
#define MASK_WPP_ENABLE (1)
|
||||||
#define MASK_WPP_CLR (1<<1)
|
#define MASK_WPP_CLR (1<<1)
|
||||||
|
|
||||||
|
|
||||||
//#define FPGA_ADDR_DPD_SYNCOFFSET ??
|
//#define FPGA_ADDR_DPD_SYNCOFFSET ??
|
||||||
|
|
||||||
#define FPGA_ADDR_DPD_TEST_L 0x02
|
#define FPGA_ADDR_DPD_TEST_L 0x02
|
||||||
#define FPGA_ADDR_DPD_TEST_H 0x03
|
#define FPGA_ADDR_DPD_TEST_H 0x03
|
||||||
|
|
||||||
//----Read Only--------------
|
//----Read Only--------------
|
||||||
#define FPGA_ADDR_DPD_STATUS1 0x01
|
#define FPGA_ADDR_DPD_STATUS1 0x01
|
||||||
|
|
||||||
//---------------------------
|
//---------------------------
|
||||||
#define MASK_RFAGC_DOWN_LIMIT (1<<1)
|
#define MASK_RFAGC_DOWN_LIMIT (1<<1)
|
||||||
#define MASK_RFAGC_UP_LIMIT (1<<2)
|
#define MASK_RFAGC_UP_LIMIT (1<<2)
|
||||||
|
|
||||||
#define SPI_BLK_LEN 35//Byte
|
#define SPI_BLK_LEN 35//Byte
|
||||||
|
|
||||||
enum{
|
enum{
|
||||||
E_RFAGC_NORMAL=0,
|
E_RFAGC_NORMAL=0,
|
||||||
E_RFAGC_DOWN_LIMIT,
|
E_RFAGC_DOWN_LIMIT,
|
||||||
E_RFAGC_UP_LIMIT,
|
E_RFAGC_UP_LIMIT,
|
||||||
}E_AGC_ERR_CODE;
|
}E_AGC_ERR_CODE;
|
||||||
/*
|
/*
|
||||||
E_RFAGC_POWER_LOW,
|
E_RFAGC_POWER_LOW,
|
||||||
E_RFAGC_POWER_HIGH,
|
E_RFAGC_POWER_HIGH,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//ok
|
//ok
|
||||||
#define MASK_DPD_ERR (0x0001)
|
#define MASK_DPD_ERR (0x0001)
|
||||||
#define MASK_DPD_PWRHIGH (0x0001<<1)
|
#define MASK_DPD_PWRHIGH (0x0001<<1)
|
||||||
#define MASK_DPD_PWRLOW (0x0001<<2)
|
#define MASK_DPD_PWRLOW (0x0001<<2)
|
||||||
#define MASK_DPD_DAGC_TOOHIGH (0x0001<<3)
|
#define MASK_DPD_DAGC_TOOHIGH (0x0001<<3)
|
||||||
#define MASK_DPD_DAGC_TOOLOW (0x0001<<4)
|
#define MASK_DPD_DAGC_TOOLOW (0x0001<<4)
|
||||||
#define MASK_DPD_ERR_ABORT (0x0001<<5)
|
#define MASK_DPD_ERR_ABORT (0x0001<<5)
|
||||||
#define MASK_DPD_TRX_SYNC_OK (0x0001<<6)
|
#define MASK_DPD_TRX_SYNC_OK (0x0001<<6)
|
||||||
#define MASK_DPD_SAT_FLG (0x0001<<7)
|
#define MASK_DPD_SAT_FLG (0x0001<<7)
|
||||||
|
|
||||||
//ok
|
//ok
|
||||||
#define DPD_CTRL1_DPD_START (0x0001)
|
#define DPD_CTRL1_DPD_START (0x0001)
|
||||||
#define DPD_CTRL1_DPD_ALLCLR (0x0001<<1)
|
#define DPD_CTRL1_DPD_ALLCLR (0x0001<<1)
|
||||||
#define DPD_CTRL1_ALL_BYPS (0x0001<<2)
|
#define DPD_CTRL1_ALL_BYPS (0x0001<<2)
|
||||||
|
|
||||||
#define DPD_CTRL2_LIN_UPDATE (0x0001)
|
#define DPD_CTRL2_LIN_UPDATE (0x0001)
|
||||||
#define DPD_CTRL2_NONLIN_UPDATE (0x0001<<1)
|
#define DPD_CTRL2_NONLIN_UPDATE (0x0001<<1)
|
||||||
#define DPD_CTRL2_SET_NONLIN_UDP_MOD(x) (((x)&0x03)<<2) /*01 : POLY; 10: CRST*/
|
#define DPD_CTRL2_SET_NONLIN_UDP_MOD(x) (((x)&0x03)<<2) /*01 : POLY; 10: CRST*/
|
||||||
#define DPD_CTRL2_AMP_CHK (0x0001<<4)
|
#define DPD_CTRL2_AMP_CHK (0x0001<<4)
|
||||||
#define DPD_CTRL2_PRL_UPDATE (0x0001<<5)
|
#define DPD_CTRL2_PRL_UPDATE (0x0001<<5)
|
||||||
#define DPD_CTRL2_RXEQ_UPDATE (0x0001<<6)
|
#define DPD_CTRL2_RXEQ_UPDATE (0x0001<<6)
|
||||||
|
|
||||||
#define DPD_CTRL2_POLYESTI_UPDATE (0x0001<<7)
|
#define DPD_CTRL2_POLYESTI_UPDATE (0x0001<<7)
|
||||||
|
|
||||||
//ok
|
//ok
|
||||||
#define INTER_DISTORTION_SW_MAX (7)
|
#define INTER_DISTORTION_SW_MAX (7)
|
||||||
|
|
||||||
|
|
||||||
//ok
|
//ok
|
||||||
#define DPD_CTRL3_FAGC_UPDATE (0x0001)
|
#define DPD_CTRL3_FAGC_UPDATE (0x0001)
|
||||||
#define DPD_CTRL3_FAGC_SLOWDN (0x0001<<1)
|
#define DPD_CTRL3_FAGC_SLOWDN (0x0001<<1)
|
||||||
#define DPD_CTRL3_DAGC_UPDATE (0x0001<<2)
|
#define DPD_CTRL3_DAGC_UPDATE (0x0001<<2)
|
||||||
#define DPD_CTRL3_DAGC_SLOWDN (0x0001<<3)
|
#define DPD_CTRL3_DAGC_SLOWDN (0x0001<<3)
|
||||||
#define DPD_CTRL3_DCRM_UPDATE (0x0001<<4)
|
#define DPD_CTRL3_DCRM_UPDATE (0x0001<<4)
|
||||||
#define DPD_CTRL3_ROT_UPDATE (0x0001<<5)
|
#define DPD_CTRL3_ROT_UPDATE (0x0001<<5)
|
||||||
#define DPD_CTRL3_SYNC_UPDATE (0x0001<<6)
|
#define DPD_CTRL3_SYNC_UPDATE (0x0001<<6)
|
||||||
#define DPD_CTRL3_SYNC_RESTART (0x0001<<7)
|
#define DPD_CTRL3_SYNC_RESTART (0x0001<<7)
|
||||||
//ok
|
//ok
|
||||||
#define DPD_IQB_UPDATE (0x0001)
|
#define DPD_IQB_UPDATE (0x0001)
|
||||||
#define DPD_IQB_BP_MASK (0x0001<<1)
|
#define DPD_IQB_BP_MASK (0x0001<<1)
|
||||||
//#define DPD_IQB_SYNC_MASK (0x0001<<2)
|
//#define DPD_IQB_SYNC_MASK (0x0001<<2)
|
||||||
#define DPD_SYNC_MODE_MASK (0x0001<<2)
|
#define DPD_SYNC_MODE_MASK (0x0001<<2)
|
||||||
|
|
||||||
#define DPD_RF_SEL_BEFORE (0x0001<<3)
|
/*
|
||||||
|
* Hardware order is exchanged again: logical/silkscreen A -> rf_sel 0,
|
||||||
#define DPD_SET_100M (0x00)
|
* logical/silkscreen B -> rf_sel 1. Keep every RF input selection routed
|
||||||
#define DPD_SET_25M (0x01)
|
* through this mapping so DPD/FWD/FSSI use the same A/B interpretation.
|
||||||
|
*/
|
||||||
#define SET_DPDFAGC_REFPWR(x) ((x))
|
#define DPD_RF_SEL_HW_A 0U
|
||||||
#define DPD_SET_CRST_ADDR_STRUCT_2 ((0x01)<<17)
|
#define DPD_RF_SEL_HW_B 1U
|
||||||
#define DPD_SET_ERR_TH(x) (((x)&0xffff))
|
#define DPD_RF_SEL_VALUE(sel) (((sel) == E_SELECT_B) ? DPD_RF_SEL_HW_B : DPD_RF_SEL_HW_A)
|
||||||
#define DPD_SET_ERR_CNT_TH(x) (((x)&0xff)<<16)
|
#define DPD_RF_SEL_AFTER (0x0001<<3)
|
||||||
#define DPD_SET_DGAIN(x) (((x)&0xffff)<<2)
|
|
||||||
#define DPD_SET_FIX_DLY(x) (0x8000 & ((x)&0x7ff))
|
#define DPD_SET_100M (0x00)
|
||||||
#define DPD_SET_CRST_REFAD(x) ((x)[0] | ((x)[1]<<4) | ((x)[2]<<8) | ((x)[3]<<12) | ((x)[4]<<16) | ((x)[5]<<20))
|
#define DPD_SET_25M (0x01)
|
||||||
|
|
||||||
#define DPD_MAX_TX_AMP_SAT 65535
|
#define SET_DPDFAGC_REFPWR(x) ((x))
|
||||||
//#define TX_FREQ_IN_HZ 2550000000
|
#define DPD_SET_CRST_ADDR_STRUCT_2 ((0x01)<<17)
|
||||||
|
#define DPD_SET_ERR_TH(x) (((x)&0xffff))
|
||||||
|
#define DPD_SET_ERR_CNT_TH(x) (((x)&0xff)<<16)
|
||||||
#define DPD_SEG 5
|
#define DPD_SET_DGAIN(x) (((x)&0xffff)<<2)
|
||||||
|
#define DPD_SET_FIX_DLY(x) (0x8000 & ((x)&0x7ff))
|
||||||
#define MASK_DPD_DATA_AREA0_MODE ((1)&0xff)
|
#define DPD_SET_CRST_REFAD(x) ((x)[0] | ((x)[1]<<4) | ((x)[2]<<8) | ((x)[3]<<12) | ((x)[4]<<16) | ((x)[5]<<20))
|
||||||
#define MASK_DPD_DATA_AREA1_MODE ((1<<1)&0xff)
|
|
||||||
#define MASK_DPD_DATA_AREA2_MODE ((1<<2)&0xff)
|
#define DPD_MAX_TX_AMP_SAT 65535
|
||||||
#define MASK_DPD_DATA_AREA3_MODE ((1<<3)&0xff)
|
//#define TX_FREQ_IN_HZ 2550000000
|
||||||
|
|
||||||
//#define MASK_TO_FPGA_WRITE_ENABLE ((1))
|
|
||||||
#define MASK_TO_SPI_WR_RD_ENABLE (1)
|
#define DPD_SEG 5
|
||||||
#define MASK_TO_FPGA_REFRESH_DATA (1<<1)
|
|
||||||
#define MASK_TO_FPGA_CHECK_CLEAR (1<<2)
|
#define MASK_DPD_DATA_AREA0_MODE ((1)&0xff)
|
||||||
|
#define MASK_DPD_DATA_AREA1_MODE ((1<<1)&0xff)
|
||||||
enum{E_CHECK_FOR_RAED=0, E_CHECK_FOR_WIRTE=1};
|
#define MASK_DPD_DATA_AREA2_MODE ((1<<2)&0xff)
|
||||||
|
#define MASK_DPD_DATA_AREA3_MODE ((1<<3)&0xff)
|
||||||
typedef struct{
|
|
||||||
u32 area_fpga_w_check;
|
//#define MASK_TO_FPGA_WRITE_ENABLE ((1))
|
||||||
u32 area_fpga_r_check;
|
#define MASK_TO_SPI_WR_RD_ENABLE (1)
|
||||||
u32 area_imx_w_check;
|
#define MASK_TO_FPGA_REFRESH_DATA (1<<1)
|
||||||
u32 area_imx_r_check;
|
#define MASK_TO_FPGA_CHECK_CLEAR (1<<2)
|
||||||
}S_CHECK_SUM;
|
|
||||||
|
enum{E_CHECK_FOR_RAED=0, E_CHECK_FOR_WIRTE=1};
|
||||||
//--------------------------------------------
|
|
||||||
|
typedef struct{
|
||||||
/*Add Bit Ctrl*/
|
u32 area_fpga_w_check;
|
||||||
typedef struct{
|
u32 area_fpga_r_check;
|
||||||
uint8 iqb_upd:1;
|
u32 area_imx_w_check;
|
||||||
uint8 iqb_bp:1;
|
u32 area_imx_r_check;
|
||||||
uint8 sync_mode:1; /* !! sync_mode 与 re_sel 相反 */
|
}S_CHECK_SUM;
|
||||||
uint8 rf_sel:1; /* !! sync_mode 与 re_sel 相反 */
|
|
||||||
}S_DPD_REG23;
|
//--------------------------------------------
|
||||||
|
|
||||||
typedef union
|
/*Add Bit Ctrl*/
|
||||||
{
|
typedef struct{
|
||||||
S_DPD_REG23 dpd_bt;
|
uint8 iqb_upd:1;
|
||||||
uint8 u8val;
|
uint8 iqb_bp:1;
|
||||||
}U_DPDCTL_REG23;
|
uint8 sync_mode:1; /* !! sync_mode 与 re_sel 相反 */
|
||||||
#define TSNR_FIX 3.5*100
|
uint8 rf_sel:1; /* !! sync_mode 与 re_sel 相反 */
|
||||||
|
}S_DPD_REG23;
|
||||||
enum{E_READ=0,E_WRITE=1};
|
|
||||||
|
typedef union
|
||||||
typedef enum
|
{
|
||||||
{
|
S_DPD_REG23 dpd_bt;
|
||||||
DPD_STATE_OK=0,
|
uint8 u8val;
|
||||||
DPD_STATE_ERR=0x10,
|
}U_DPDCTL_REG23;
|
||||||
DPD_STATE_POWER_HIGH,
|
#define TSNR_FIX 3.5*100
|
||||||
DPD_STATE_POWER_LOW,
|
|
||||||
DPD_STATE_POWER_TOO_HIGH,
|
enum{E_READ=0,E_WRITE=1};
|
||||||
DPD_STATE_POWER_TOO_LOW,
|
|
||||||
DPD_STATE_ERR_OVER_RANGE,
|
typedef enum
|
||||||
DPD_STATE_SYNC_OK,
|
{
|
||||||
|
DPD_STATE_OK=0,
|
||||||
DPD_READ_CKECK_ERR,
|
DPD_STATE_ERR=0x10,
|
||||||
DPD_WRITE_CHECK_ERR,
|
DPD_STATE_POWER_HIGH,
|
||||||
|
DPD_STATE_POWER_LOW,
|
||||||
DPD_SYNC_TIMEOUT,
|
DPD_STATE_POWER_TOO_HIGH,
|
||||||
DPD_CHECK_WPP_ERR_EXIT_HOLD,
|
DPD_STATE_POWER_TOO_LOW,
|
||||||
|
DPD_STATE_ERR_OVER_RANGE,
|
||||||
//...
|
DPD_STATE_SYNC_OK,
|
||||||
|
|
||||||
DPD_IDLE_ERR=0x30,
|
DPD_READ_CKECK_ERR,
|
||||||
|
DPD_WRITE_CHECK_ERR,
|
||||||
|
|
||||||
DPD_PROC_EXIT = 0x99,
|
DPD_SYNC_TIMEOUT,
|
||||||
DPD_UPDATING,
|
DPD_CHECK_WPP_ERR_EXIT_HOLD,
|
||||||
DPD_SAVE_DATA = 0xAA,
|
|
||||||
DPD_LOAD_DATA = 0xBB,
|
//...
|
||||||
DPD_STATUS_UPDATING = 0xff
|
|
||||||
}E_DPD_STATE;
|
DPD_IDLE_ERR=0x30,
|
||||||
|
|
||||||
typedef enum{
|
|
||||||
E_DPD_NSAVE_NFLITER=0x00,//0x00
|
DPD_PROC_EXIT = 0x99,
|
||||||
E_DPD_NSAVE_FLITER =0x01,//0x01
|
DPD_UPDATING,
|
||||||
E_DPD_SAVE_NFLITER =0x10,//0x10
|
DPD_SAVE_DATA = 0xAA,
|
||||||
E_DPD_SAVE_FLITER =0x11,//0x11
|
DPD_LOAD_DATA = 0xBB,
|
||||||
}E_CTL_DPD;//cttb_para.wh_para.ctl_dpd
|
DPD_STATUS_UPDATING = 0xff
|
||||||
|
}E_DPD_STATE;
|
||||||
|
|
||||||
//
|
typedef enum{
|
||||||
typedef enum
|
E_DPD_NSAVE_NFLITER=0x00,//0x00
|
||||||
{
|
E_DPD_NSAVE_FLITER =0x01,//0x01
|
||||||
E_LOAD_TO_MCU,
|
E_DPD_SAVE_NFLITER =0x10,//0x10
|
||||||
E_LOAD_TO_FPGA,
|
E_DPD_SAVE_FLITER =0x11,//0x11
|
||||||
}E_DPD_CURVE_PROC;
|
}E_CTL_DPD;//cttb_para.wh_para.ctl_dpd
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
//
|
||||||
E_DPD_AREA0=0,
|
typedef enum
|
||||||
E_DPD_AREA1,
|
{
|
||||||
E_DPD_AREA2,
|
E_LOAD_TO_MCU,
|
||||||
E_DPD_AREA3,
|
E_LOAD_TO_FPGA,
|
||||||
E_DPD_AREA_FIRST_4K,
|
}E_DPD_CURVE_PROC;
|
||||||
}E_DPD_CURVE_AREA;
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
typedef struct
|
E_DPD_AREA0=0,
|
||||||
{
|
E_DPD_AREA1,
|
||||||
uint32 tx_data_iq_i;
|
E_DPD_AREA2,
|
||||||
uint32 rx_data_iq_i;
|
E_DPD_AREA3,
|
||||||
uint16 ob_wpp_max_i;
|
E_DPD_AREA_FIRST_4K,
|
||||||
uint16 txaveamp_i;
|
}E_DPD_CURVE_AREA;
|
||||||
uint16 txpow_cur_i;
|
|
||||||
uint16 rx_dgain_i;
|
|
||||||
uint16 rx_fgain_cw_i;
|
typedef struct
|
||||||
uint16 dpd_dynamic_gain_i; // 发端算出、传给收端归一化的增益字 (14bit, seg5 0x32/0x33)
|
{
|
||||||
uint16 ob_b_pwr_i; // 预失真前功率 (14bit, seg5 0x34/0x35)
|
uint32 tx_data_iq_i;
|
||||||
uint16 ob_a_pwr_i; // 预失真后功率 (14bit, seg5 0x36/0x37)
|
uint32 rx_data_iq_i;
|
||||||
}S_DPD_LOG_TYPE;
|
uint16 ob_wpp_max_i;
|
||||||
|
uint16 txaveamp_i;
|
||||||
void refresh_update_dpd(void *parg,void *parv);
|
uint16 txpow_cur_i;
|
||||||
int dpd_curve_proc(uint8 proc_mode);
|
uint16 rx_dgain_i;
|
||||||
|
uint16 rx_fgain_cw_i;
|
||||||
void dpd_exit(void);
|
uint16 dpd_dynamic_gain_i; // 发端算出、传给收端归一化的增益字 (14bit, seg5 0x32/0x33)
|
||||||
void dpd_set_clr(void);
|
uint16 ob_b_pwr_i; // 预失真前功率 (14bit, seg5 0x34/0x35)
|
||||||
uint8 get_fpga_dpd_check(void);
|
uint16 ob_a_pwr_i; // 预失真后功率 (14bit, seg5 0x36/0x37)
|
||||||
void dpd_boot_run_init(void);
|
}S_DPD_LOG_TYPE;
|
||||||
extern void set_dpd_unit_area_auto_inc(uint8 inc_flg);
|
|
||||||
extern void set_tsnr_update_flg(uint8 up_flg);
|
void refresh_update_dpd(void *parg,void *parv);
|
||||||
extern int update_tsnr_uimd_limd(uint8 rst_flg,uint8* p_phase);
|
int dpd_curve_proc(uint8 proc_mode);
|
||||||
extern uint8 get_tsnr_update_flg(void);
|
|
||||||
extern void remote_set_dpd_clr(void);
|
void dpd_exit(void);
|
||||||
extern uint8 conv_adpc_err_output(uint8 raw_dpd_err);
|
void dpd_set_clr(void);
|
||||||
|
uint8 get_fpga_dpd_check(void);
|
||||||
extern void clear_dpd_data_file(void);
|
void dpd_boot_run_init(void);
|
||||||
extern void set_ab_switch(E_AB_SEL ab_val);
|
extern void set_dpd_unit_area_auto_inc(uint8 inc_flg);
|
||||||
extern u32 read_fpga_area_u32_check_sum(u8 check_type);
|
extern void set_tsnr_update_flg(uint8 up_flg);
|
||||||
extern void clear_check_sum(S_CHECK_SUM *parea_check);
|
extern int update_tsnr_uimd_limd(uint8 rst_flg,uint8* p_phase);
|
||||||
extern void calc_area_u32_check_sum(u32 *pcheck, u8 *pbuf, u32 len);
|
extern uint8 get_tsnr_update_flg(void);
|
||||||
extern void calc_area_check_sum(u8 *pcheck, u8 *pbuf, u32 len);
|
extern void remote_set_dpd_clr(void);
|
||||||
#endif
|
extern uint8 conv_adpc_err_output(uint8 raw_dpd_err);
|
||||||
|
|
||||||
|
extern void clear_dpd_data_file(void);
|
||||||
|
extern void set_ab_switch(E_AB_SEL ab_val);
|
||||||
|
extern u32 read_fpga_area_u32_check_sum(u8 check_type);
|
||||||
|
extern void clear_check_sum(S_CHECK_SUM *parea_check);
|
||||||
|
extern void calc_area_u32_check_sum(u32 *pcheck, u8 *pbuf, u32 len);
|
||||||
|
extern void calc_area_check_sum(u8 *pcheck, u8 *pbuf, u32 len);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ typedef enum
|
|||||||
EEPROM_ADRV9009_ATT_L,
|
EEPROM_ADRV9009_ATT_L,
|
||||||
EEPROM_ADRV9009_ORX_MANU_GAIN,
|
EEPROM_ADRV9009_ORX_MANU_GAIN,
|
||||||
EEPROM_SPEC_ENABLE,
|
EEPROM_SPEC_ENABLE,
|
||||||
|
EEPROM_HARDWARE_VERSION,
|
||||||
|
|
||||||
|
|
||||||
EEPROM_CHANNEL_ADDR = 0x50, //RF 相关
|
EEPROM_CHANNEL_ADDR = 0x50, //RF 相关
|
||||||
@@ -188,6 +189,8 @@ typedef enum
|
|||||||
//工厂菜单当前WK为0x220, 工厂菜单power定义到0x300,中间预留部分空间
|
//工厂菜单当前WK为0x220, 工厂菜单power定义到0x300,中间预留部分空间
|
||||||
EEPROM_CH1_POWER_ADDR = 0x300, //约60多字节
|
EEPROM_CH1_POWER_ADDR = 0x300, //约60多字节
|
||||||
EEPROM_CH2_POWER_ADDR = 0X400, //1.42(3X+)功率控制字
|
EEPROM_CH2_POWER_ADDR = 0X400, //1.42(3X+)功率控制字
|
||||||
|
// V1.1独立频点WE表;0x500~0x5A7,未配置项回退到V1.0表
|
||||||
|
EEPROM_CH_POWER_V1_1_ADDR = 0x500,
|
||||||
|
|
||||||
EEPROM_PSTS_TS_EDIT = 0x600, //开关
|
EEPROM_PSTS_TS_EDIT = 0x600, //开关
|
||||||
EEPROM_PSTS_TSIDH, //tsid
|
EEPROM_PSTS_TSIDH, //tsid
|
||||||
|
|||||||
Executable → Regular
+4
-6
@@ -11,17 +11,15 @@ void iq_spec_set_enable(int enable);
|
|||||||
/*
|
/*
|
||||||
* Newest UIMD/LIMD measured on the imx6's own 10 MSPS ORx window.
|
* Newest UIMD/LIMD measured on the imx6's own 10 MSPS ORx window.
|
||||||
*
|
*
|
||||||
* Same taps as the FPGA's imd_imd_fft_meas -- centre bin at +24.414 kHz and the two
|
* PR19 uses three fixed 500 kHz measurement windows. P0 is the mean linear power
|
||||||
* shoulder bins at +-3.1494 MHz -- but averaged over 11 bins (+-12.2 kHz) each and
|
* from -0.25 to +0.25 MHz, PL is the mean from -3.5 to -3.0 MHz, and PR is the mean
|
||||||
* Welch-averaged over the whole 26.2 ms window. LIMD is always the shoulder on the
|
* from +3.0 to +3.5 MHz. The window spectra are Welch-averaged over 26.2 ms.
|
||||||
* pilot's side of the carrier; which one that is follows the measured pilot, so a
|
|
||||||
* spectral inversion cannot silently swap the two.
|
|
||||||
*
|
*
|
||||||
* The FPGA's own registers cannot be used any more: its 20-bit |DFT|^2 output
|
* The FPGA's own registers cannot be used any more: its 20-bit |DFT|^2 output
|
||||||
* quantises the shoulder bins to 0 once the shoulder is better than ~45 dB, and
|
* quantises the shoulder bins to 0 once the shoulder is better than ~45 dB, and
|
||||||
* lcd_task.c's `if (imd_l == 0) imd_l = 1` then reports a constant.
|
* lcd_task.c's `if (imd_l == 0) imd_l = 1` then reports a constant.
|
||||||
*
|
*
|
||||||
* limd_db/uimd_db are 10*log10(P_centre / P_shoulder), i.e. positive dB, no +11 dB
|
* limd_db/uimd_db are P0-PL and P0-PR respectively, i.e. positive dB, with no +11 dB
|
||||||
* display offset applied.
|
* display offset applied.
|
||||||
*
|
*
|
||||||
* Returns 1 and fills both pointers when a window newer than IQ_IMD_MAX_AGE seconds
|
* Returns 1 and fills both pointers when a window newer than IQ_IMD_MAX_AGE seconds
|
||||||
|
|||||||
Executable → Regular
+18
@@ -105,6 +105,16 @@
|
|||||||
#define WE_POWER_ZERO 300 //we屏 0db对应值
|
#define WE_POWER_ZERO 300 //we屏 0db对应值
|
||||||
#define WE_POWER_MIN 0
|
#define WE_POWER_MIN 0
|
||||||
#define WE_POWER_MAX 500
|
#define WE_POWER_MAX 500
|
||||||
|
#define WE_POWER_MAX_V1_1 720 //V1.1物理功率余量允许到+21dB
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
HARDWARE_VERSION_V1_0 = 0,
|
||||||
|
HARDWARE_VERSION_V1_1,
|
||||||
|
HARDWARE_VERSION_END
|
||||||
|
} HARDWARE_VERSION;
|
||||||
|
|
||||||
|
#define DEFAULT_HARDWARE_VERSION HARDWARE_VERSION_V1_1
|
||||||
|
|
||||||
#define OCXO_MAX 10 //单位0.1hz system菜单
|
#define OCXO_MAX 10 //单位0.1hz system菜单
|
||||||
#define OCXO_UNIT 106 //保存时的放大倍数
|
#define OCXO_UNIT 106 //保存时的放大倍数
|
||||||
@@ -144,6 +154,12 @@
|
|||||||
#define FSSI_GI_MIN 195
|
#define FSSI_GI_MIN 195
|
||||||
#define FSSI_GI_MAX 255
|
#define FSSI_GI_MAX 255
|
||||||
|
|
||||||
|
/* PR16: FAGC 功率设定点, 用作 FSSI 细档(死区内 rms 残差)的零点。
|
||||||
|
* PR11 §2: rms_target = fagcpwrlevel*4; 当前 fagcpwrlevel=256 -> 1024。
|
||||||
|
* rms 恰好等于它时, 新算法与旧的纯 gain index 版读数完全相同(见 orx_power.h)。
|
||||||
|
* 若日后改了 fagcpwrlevel, 这里要同步; 更准的做法是按 PR11 §3 用标定点实测中位数。 */
|
||||||
|
#define FSSI_RMS_REF 1024
|
||||||
|
|
||||||
#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 //工厂默认参数备份
|
||||||
#define EEPROM_BACKUP_PARA_TYPE_CUST 2 //客户手动备份
|
#define EEPROM_BACKUP_PARA_TYPE_CUST 2 //客户手动备份
|
||||||
@@ -372,6 +388,7 @@ typedef struct
|
|||||||
uint8 agc_att;
|
uint8 agc_att;
|
||||||
uint16 adrv9009_att;
|
uint16 adrv9009_att;
|
||||||
uint8 orx_manu_gain;
|
uint8 orx_manu_gain;
|
||||||
|
uint8 hardware_version;
|
||||||
}S_SYSTEM_PARA,*PS_SYSTEM_PARA;
|
}S_SYSTEM_PARA,*PS_SYSTEM_PARA;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -1359,6 +1376,7 @@ void e2rom_read_psts(S_PSTS_PARA *ppara);
|
|||||||
|
|
||||||
extern void e2rom_read_leak(uint32 chn,S_WE_PARA *ppara);
|
extern void e2rom_read_leak(uint32 chn,S_WE_PARA *ppara);
|
||||||
extern void e2rom_save_leak(uint32 UI_ch, S_WE_PARA *ppara);
|
extern void e2rom_save_leak(uint32 UI_ch, S_WE_PARA *ppara);
|
||||||
|
extern uint16 get_we_power_max(void);
|
||||||
extern void e2rom_save_ad(S_ADVANCE_PARA *ppara);
|
extern void e2rom_save_ad(S_ADVANCE_PARA *ppara);
|
||||||
extern void e2rom_save_ad2(S_ADVANCE2_PARA *ppara);
|
extern void e2rom_save_ad2(S_ADVANCE2_PARA *ppara);
|
||||||
extern void e2rom_save_ad3(S_ADVANCE3_PARA *ppara);
|
extern void e2rom_save_ad3(S_ADVANCE3_PARA *ppara);
|
||||||
|
|||||||
Executable → Regular
+20
@@ -20,6 +20,26 @@ uint8 orx_fssi_hysteresis(uint8 prev, uint8 now);
|
|||||||
* 线性映射,四舍五入,越界钳位。gi_min/gi_max 由调用方传入(标定后可收窄)。 */
|
* 线性映射,四舍五入,越界钳位。gi_min/gi_max 由调用方传入(标定后可收窄)。 */
|
||||||
uint8 orx_fssi_from_gain_index(uint8 gi, uint8 gi_min, uint8 gi_max);
|
uint8 orx_fssi_from_gain_index(uint8 gi, uint8 gi_min, uint8 gi_max);
|
||||||
|
|
||||||
|
/* PR16: 用「FAGC 增益字 + 死区内 rms 残差」合成的 FSSI%,替代只看 gain index 的版本。
|
||||||
|
*
|
||||||
|
* 为什么必须这样: dpd_rx_rfagc_1x.vhd 的 dz=232 是一个 2.00 dB 死区(4.6 个 gain
|
||||||
|
* step 宽), 注释原文 "the AGC only re-acquires when the ORx level genuinely moves
|
||||||
|
* more than +-1 dB"。所以只看 gain index 时, ±1 dB 以内的输入变化 gain index 一步
|
||||||
|
* 不动 —— A/B 两路差异小于 1 dB 时 b_fssi 会恒等于 a_fssi, 看着在刷新, 其实是在
|
||||||
|
* 复读 A。死区内增益冻结, 功率变化恰好完整落在 ob_fb_rms_pwr 上, 所以把 rms 残差
|
||||||
|
* 加回来就能看进死区。PR11 §4.2 早有明确要求(rms 残差必须保留浮点, 否则
|
||||||
|
* "丢失 FAGC 死区内的真实功率变化"), PR13 改用纯 gain index 时丢掉了这一半。
|
||||||
|
*
|
||||||
|
* fagc_cw : seg1 0x36/0x37 agcctrlword
|
||||||
|
* rms : seg1 0x03/0x04 ob_fb_rms_pwr
|
||||||
|
* rms_ref : FAGC 功率设定点(fagcpwrlevel*4; fagcpwrlevel=256 -> 1024)
|
||||||
|
* gi_min/gi_max: 与 orx_fssi_from_gain_index 同义, 仍决定量程
|
||||||
|
*
|
||||||
|
* 后向兼容: rms == rms_ref 时本函数与 orx_fssi_from_gain_index(255-cw_hi,...)
|
||||||
|
* 完全相等 —— 即 AGC 恰好收敛在设定点时读数不变, 只有死区内才产生新的分辨力。 */
|
||||||
|
uint8 orx_fssi_from_power(uint16 fagc_cw, uint16 rms, uint16 rms_ref,
|
||||||
|
uint8 gi_min, uint8 gi_max);
|
||||||
|
|
||||||
/* PR13: ORx 数字功率 rms -> 0.5dB 步数: round(10*log10(rms)/0.5) = round(20*log10(rms))。
|
/* PR13: ORx 数字功率 rms -> 0.5dB 步数: round(10*log10(rms)/0.5) = round(20*log10(rms))。
|
||||||
* rms==0 按 1 处理(防 -inf), 返回 0。 */
|
* rms==0 按 1 处理(防 -inf), 返回 0。 */
|
||||||
int16 orx_pwr_halfdb(uint16 rms);
|
int16 orx_pwr_halfdb(uint16 rms);
|
||||||
|
|||||||
@@ -225,6 +225,7 @@ typedef struct
|
|||||||
uint8 agc_att;
|
uint8 agc_att;
|
||||||
uint16 adrv9009_att;
|
uint16 adrv9009_att;
|
||||||
uint8 orx_manu_gain;
|
uint8 orx_manu_gain;
|
||||||
|
uint8 hardware_version;
|
||||||
}S_CGI_SET,*PS_CGI_SET;
|
}S_CGI_SET,*PS_CGI_SET;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
|
|||||||
+8
-3
@@ -755,13 +755,18 @@ int main(int argc, char *argv[])
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 1: // restart
|
case 1: // restart
|
||||||
log_printf(LOG_DEBUG,I_NOUSED,"\n\nrestart by daemon.\n");
|
/* Keep argument 1 compatible with the deployed daemon, but use the same
|
||||||
g_restart_app_flg = 1;
|
* no-power-off RF recovery path as a normal application start. A fresh
|
||||||
|
* process must rebuild the TALISE handle and the FPGA-side JESD link;
|
||||||
|
* g_restart_app_flg=1 skips adrv9009_init() and can leave TX unavailable. */
|
||||||
|
log_printf(LOG_DEBUG,I_NOUSED,
|
||||||
|
"\n\nrestart by daemon, use normal RF recovery path.\n");
|
||||||
|
g_restart_app_flg = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log_printf(LOG_INFO, I_SYS_STATUS, "====System start===k=%d= 260428\n", USE_KOREA_VERSION);
|
log_printf(LOG_INFO, I_SYS_STATUS, "====System start===k=%d= 2607281650\n", USE_KOREA_VERSION);
|
||||||
|
|
||||||
pr_soft_start_ver();
|
pr_soft_start_ver();
|
||||||
|
|
||||||
|
|||||||
Executable
+88
@@ -0,0 +1,88 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# The upgrade shell is forked by the running app and inherits every app socket.
|
||||||
|
# Re-exec after closing inherited non-stdio descriptors so the old process can
|
||||||
|
# release its ports cleanly. Keep upgrade diagnostics on the debug serial port.
|
||||||
|
if [ "$SMOD_FD_CLEAN" != "1" ]; then
|
||||||
|
export SMOD_FD_CLEAN=1
|
||||||
|
exec /bin/sh -c '
|
||||||
|
i=3
|
||||||
|
while [ $i -lt 256 ]; do
|
||||||
|
eval "exec ${i}>&-" 2>/dev/null
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
exec /bin/sh "$1" </dev/null >/dev/ttymxc0 2>&1
|
||||||
|
' smod_fd_clean "$0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Kernel and device-tree updates only take effect after an IMX reboot. Pure
|
||||||
|
# userspace packages keep the clock/RF hardware powered and restart only the
|
||||||
|
# application so RF can recover without a power cycle.
|
||||||
|
need_reboot=0
|
||||||
|
reboot_reason=""
|
||||||
|
|
||||||
|
if [ -f ./src/zImage ]; then
|
||||||
|
need_reboot=1
|
||||||
|
reboot_reason="kernel"
|
||||||
|
echo "kernel image found: reboot required"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f ./src/imx6ull-S-gpmi-1024x600c7.dtb ]; then
|
||||||
|
need_reboot=1
|
||||||
|
if [ -n "$reboot_reason" ]; then
|
||||||
|
reboot_reason="$reboot_reason+device-tree"
|
||||||
|
else
|
||||||
|
reboot_reason="device-tree"
|
||||||
|
fi
|
||||||
|
echo "device-tree image found: reboot required"
|
||||||
|
fi
|
||||||
|
|
||||||
|
killall logrotateStart daemon_system 2>/dev/null
|
||||||
|
sh remove.sh
|
||||||
|
sh cp.sh
|
||||||
|
sync
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
if [ "$need_reboot" = "1" ]; then
|
||||||
|
echo "boot component updated ($reboot_reason), reboot IMX now"
|
||||||
|
reboot
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# pthread_setname_np changes the app's /proc comm, so identify it by the exact
|
||||||
|
# executable path. Also match "(deleted)" after cp.sh replaces the running ELF.
|
||||||
|
find_app_pids()
|
||||||
|
{
|
||||||
|
for exe in /proc/[0-9]*/exe; do
|
||||||
|
target=`readlink "$exe" 2>/dev/null` || continue
|
||||||
|
case "$target" in
|
||||||
|
/aw/anywave/app/imx_atsc_app|/aw/anywave/app/imx_atsc_app\ \(deleted\))
|
||||||
|
echo "$exe" | sed 's#/proc/##;s#/exe##'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
old_pids=`find_app_pids`
|
||||||
|
[ -n "$old_pids" ] && kill $old_pids 2>/dev/null
|
||||||
|
i=0
|
||||||
|
while [ -n "`find_app_pids`" ] && [ $i -lt 15 ]; do
|
||||||
|
sleep 1
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
old_pids=`find_app_pids`
|
||||||
|
[ -n "$old_pids" ] && kill -9 $old_pids 2>/dev/null
|
||||||
|
while [ -n "`find_app_pids`" ]; do sleep 1; done
|
||||||
|
|
||||||
|
# Remove stale logging pipelines only after the old application has exited.
|
||||||
|
killall loglog 2>/dev/null
|
||||||
|
|
||||||
|
# Start without the legacy argument 1 so the normal no-power-off RF recovery
|
||||||
|
# path rebuilds the TALISE handle and FPGA-side JESD link.
|
||||||
|
sleep 2
|
||||||
|
cd /aw/anywave/app && nohup sh -c './imx_atsc_app 2>&1 | /usr/sbin/loglog /var/module_log/ imx_atsc_app.log 1' </dev/null >/dev/ttymxc0 2>&1 &
|
||||||
|
|
||||||
|
# Restore the system watchdog/housekeeper and log rotation. BusyBox on this
|
||||||
|
# target has no setsid, and /usr/sbin is not in the default PATH.
|
||||||
|
sleep 2
|
||||||
|
nohup sh -c '/usr/sbin/daemon_system 2>&1 | /usr/sbin/loglog /var/module_log/ daemon_system.log 1' >/dev/null 2>&1 &
|
||||||
|
nohup sh -c '/usr/sbin/logrotateStart 2>&1 | /usr/sbin/loglog /var/module_log/ logrotateStart.log 0' >/dev/null 2>&1 &
|
||||||
@@ -0,0 +1,341 @@
|
|||||||
|
# 不掉电恢复 ADRV9009 / JESD / 射频链路流程
|
||||||
|
|
||||||
|
## 1. 目的与适用范围
|
||||||
|
|
||||||
|
本文整理设备在不切断整机电源的情况下,完成以下操作后恢复射频输出的处理链路:
|
||||||
|
|
||||||
|
- IMX 应用程序升级后,结束旧进程并启动新进程;
|
||||||
|
- IMX 应用程序异常退出后,由脚本或守护进程重新启动;
|
||||||
|
- FPGA 在线升级并通过 IPROG 重新加载 bitstream;
|
||||||
|
- FPGA 已重新加载,但 ADRV9009 和 AD9528 仍保持上电运行。
|
||||||
|
|
||||||
|
这里的核心问题是:ADRV9009 已经处于运行态时,再次执行 `TALISE_resetDevice()`、`TALISE_initialize()` 和初始化校准,可能返回 `err10 / TALACT_ERR_RESET_FULL`,从而造成 JESD 或 TX 通道恢复失败。因此,不掉电恢复不能简单复用完整冷启动流程,必须区分“9009 仍存活”和“9009 确实处于冷启动状态”。
|
||||||
|
|
||||||
|
## 2. 硬件状态与基本原则
|
||||||
|
|
||||||
|
当前板卡上,FPGA、ADRV9009 和 AD9528 在 IMX 应用进程退出后仍保持供电。FPGA 在线重载时,9009 的器件时钟仍由 OCXO/AD9528 提供,因此 9009 CLKPLL 通常不会因为 FPGA 重载而失锁。
|
||||||
|
|
||||||
|
不掉电恢复遵循以下原则:
|
||||||
|
|
||||||
|
1. 9009 已经正常运行时,只重新建立软件句柄和 FPGA 侧 JESD 链路,不重新初始化 TALISE。
|
||||||
|
2. FPGA 重载后,重新写入 FPGA 静态寄存器、配置 JESD、复位 FPGA GT,并重新完成 SYNC/ILAS 握手。
|
||||||
|
3. 射频输出在链路恢复完成前保持关闭或隔离,完成 PLL、JESD、功率参数恢复后再打开。
|
||||||
|
4. FPGA 状态、9009 状态应分别判断,不能长期只依赖一个 FPGA `cfg_valid` 标志推断所有硬件状态。
|
||||||
|
|
||||||
|
## 3. 当前使用的状态标志
|
||||||
|
|
||||||
|
### 3.1 FPGA `cfg_valid`
|
||||||
|
|
||||||
|
FPGA segment 5、地址 `0x60` 的 bit0 被用作配置有效标志:
|
||||||
|
|
||||||
|
- `1`:应用已经完成过 FPGA/RF 配置,FPGA 自上次配置后没有重新加载;
|
||||||
|
- `0`:冷启动或 FPGA bitstream 已重新加载,需要恢复 FPGA 配置和 JESD 链路。
|
||||||
|
|
||||||
|
完成 `config_fpga_para()` 后,应用执行:
|
||||||
|
|
||||||
|
```c
|
||||||
|
fpga_byte_write(0x05, 0x60, 0x01);
|
||||||
|
g_rf_boot_done = 1;
|
||||||
|
```
|
||||||
|
|
||||||
|
实现位置:`bsp/fpga.c` 中的 `fpga_read_cfg_valid()` 及 `fpga_save_all_para()` 尾部。
|
||||||
|
|
||||||
|
### 3.2 `g_skip_talise_cfg`
|
||||||
|
|
||||||
|
`g_skip_talise_cfg` 控制是否跳过 ADRV9009 的完整 TALISE 初始化:
|
||||||
|
|
||||||
|
- `0`:冷启动,执行 reset、initialize、ARM 加载和初始化校准;
|
||||||
|
- `1`:不掉电恢复,仅执行 `TALISE_openHw()` 和状态读取,跳过完整初始化。
|
||||||
|
|
||||||
|
实现位置:`adrv9009/adrv9009_config.c`。
|
||||||
|
|
||||||
|
### 3.3 `g_rf_boot_done`
|
||||||
|
|
||||||
|
该标志表示本进程已经完成 RF 启动配置。运行时 FPGA `cfg_valid` 轮询只有在该标志置位后才启用,避免启动阶段误触发热恢复。
|
||||||
|
|
||||||
|
### 3.4 `g_hot_relink_busy`
|
||||||
|
|
||||||
|
热恢复重入保护。FPGA 升级完成路径和后台 `cfg_valid` 检测路径都可能触发恢复,使用该标志保证同一时间只有一个 `hot_reinit_rf()` 执行。
|
||||||
|
|
||||||
|
## 4. 统一恢复链路
|
||||||
|
|
||||||
|
不掉电恢复的逻辑链路如下:
|
||||||
|
|
||||||
|
```text
|
||||||
|
触发恢复
|
||||||
|
|
|
||||||
|
+-- IMX 应用升级/重启 ---------- FPGA、9009、AD9528保持上电
|
||||||
|
|
|
||||||
|
+-- FPGA 在线升级/IPROG -------- FPGA重载,9009、AD9528保持上电
|
||||||
|
|
|
||||||
|
v
|
||||||
|
停止 DPD 和可能访问 SPI/RF 的后台状态机
|
||||||
|
|
|
||||||
|
v
|
||||||
|
判断 FPGA 是否重载、9009 是否仍存活
|
||||||
|
|
|
||||||
|
+-- 9009存活:TALISE_openHw,跳过reset/initialize/cals
|
||||||
|
|
|
||||||
|
+-- 9009未运行:执行完整TALISE冷启动初始化
|
||||||
|
|
|
||||||
|
v
|
||||||
|
恢复 FPGA 静态配置和业务参数
|
||||||
|
|
|
||||||
|
v
|
||||||
|
配置 JESD TX/RX/FB + FPGA GT reset + SYNC/ILAS重握手
|
||||||
|
|
|
||||||
|
v
|
||||||
|
恢复频率、衰减、ORx增益、FB route、SPI控制权
|
||||||
|
|
|
||||||
|
v
|
||||||
|
恢复 FAGC、ORx测量、DPD曲线/自适应状态
|
||||||
|
|
|
||||||
|
v
|
||||||
|
检查 PLL、JESD、TX状态
|
||||||
|
|
|
||||||
|
+-- 成功:按保存的RF状态打开TX通道和物理RF开关
|
||||||
|
|
|
||||||
|
+-- 失败:保持RF关闭,记录错误并进入有限次数重试/告警
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. IMX 应用升级或 kill/restart 流程
|
||||||
|
|
||||||
|
### 5.1 推荐的进程重启顺序
|
||||||
|
|
||||||
|
应用升级脚本应按以下顺序执行:
|
||||||
|
|
||||||
|
1. 停止 `daemon_system` 和 `logrotateStart`,避免守护进程抢先拉起旧程序。
|
||||||
|
2. 执行升级包的 `remove.sh` 和 `cp.sh`,复制新应用及配套文件。
|
||||||
|
3. `sync`,确保文件写入完成。
|
||||||
|
4. 根据 `/proc/<pid>/exe` 找到所有旧 `imx_atsc_app` 进程,而不是仅依赖进程名。
|
||||||
|
5. 先发送普通终止信号,等待旧进程退出;超时后再发送强制终止信号。
|
||||||
|
6. 确认旧进程全部退出后,清理遗留的 `loglog` 管道进程。
|
||||||
|
7. 无参数启动新应用:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./imx_atsc_app
|
||||||
|
```
|
||||||
|
|
||||||
|
8. 重新启动 `daemon_system` 和 `logrotateStart`。
|
||||||
|
|
||||||
|
ATSC 守护进程已经改为无参数启动新应用。App 端同时保留对历史参数 `1` 的兼容,但会将其按普通启动处理,即保持 `g_restart_app_flg=0`,确保新进程重新建立 TALISE 句柄并重建 FPGA/JESD 链路。
|
||||||
|
|
||||||
|
### 5.2 新进程中的 RF 启动流程
|
||||||
|
|
||||||
|
新进程进入 `fpga_para_init()` 后:
|
||||||
|
|
||||||
|
1. 在恢复参数前启用 RF hold,确保初始化期间物理输出保持关闭。
|
||||||
|
2. 读取 FPGA `cfg_valid`。
|
||||||
|
3. 若 `cfg_valid=1`,认为 FPGA 和 9009 未掉电:
|
||||||
|
- 设置 `g_skip_talise_cfg=1`;
|
||||||
|
- 调用 `config_fpga_para()`;
|
||||||
|
- `adrv9009_init()` 仍负责恢复 FPGA 侧 JESD,但 `adrv9009_cfg()` 只轻挂接 9009;
|
||||||
|
- 完成后清除 `g_skip_talise_cfg`。
|
||||||
|
4. 恢复 AGC、功率、DPD 和 FAGC 参数。
|
||||||
|
5. 若保存的 `atsc_para.rf_para.rf=1`,调用 `adrv9009_set_tx_path_enable(1)`。
|
||||||
|
6. 等待射频稳定延时后,释放 RF hold,并恢复物理 RF 开关和衰减值。
|
||||||
|
|
||||||
|
相关代码:
|
||||||
|
|
||||||
|
- `bsp/isys_config.c`:`fpga_para_init()`;
|
||||||
|
- `bsp/fpga.c`:`config_fpga_para()`、`fpga_save_all_para()`;
|
||||||
|
- `adrv9009/adrv9009_config.c`:`adrv9009_init()`、`adrv9009_cfg()`;
|
||||||
|
- `bsp/fpga.c`:`fpga_boot_rf_hold_begin()`、`fpga_boot_rf_hold_release()`。
|
||||||
|
|
||||||
|
## 6. FPGA 在线升级后的恢复流程
|
||||||
|
|
||||||
|
### 6.1 FPGA 升级完成
|
||||||
|
|
||||||
|
Web 或 TCP 升级路径将 `.mcs` 写入 FPGA flash。升级完成后:
|
||||||
|
|
||||||
|
1. `check_fpga_upgrade_done()` 检测 DONE 状态;
|
||||||
|
2. `reset_fpga_system()` 触发 FPGA IPROG,加载新 bitstream;
|
||||||
|
3. FPGA 重载后 `cfg_valid` 自动清零;
|
||||||
|
4. 延时约 3 秒等待 FPGA 配置稳定;
|
||||||
|
5. 调用 `hot_reinit_rf(0)`,不重启 IMX,不重新初始化 9009。
|
||||||
|
|
||||||
|
除升级完成回调外,`imx_up_thr()` 每秒检查一次 `cfg_valid`。在本进程已经完成启动、且曾经读到过 `cfg_valid=1` 的前提下,连续两次读到 `0`,也会触发 `hot_reinit_rf(0)`。这是 FPGA 异常重载时的补偿路径。
|
||||||
|
|
||||||
|
### 6.2 `hot_reinit_rf()` 处理步骤
|
||||||
|
|
||||||
|
`task/upgrade.c` 中的 `hot_reinit_rf()` 执行:
|
||||||
|
|
||||||
|
1. 使用 `g_hot_relink_busy` 防止重复进入;
|
||||||
|
2. 记录升级前 DPD/ADPC 模式;
|
||||||
|
3. 设置 `atsc_adpc_force_stop_flag=TRUE`,停止 DPD 状态机访问 SPI/RF;
|
||||||
|
4. 等待约 1.5 秒,使后台处理退出临界区;
|
||||||
|
5. 如果参数 `reload_fpga=1`,先执行 IPROG 并等待约 3 秒;
|
||||||
|
6. 设置 `g_skip_talise_cfg=1`;
|
||||||
|
7. 调用 `config_fpga_para()`,恢复 FPGA、JESD 和射频参数;
|
||||||
|
8. 清除 `g_skip_talise_cfg`;
|
||||||
|
9. 重置 DPD 软件状态,恢复 FAGC/ORx 测量;
|
||||||
|
10. 触发 TSNR/ORx phase 7~10 后台流程;
|
||||||
|
11. 如果升级前 DPD 处于 HOLD,重新加载保存曲线;如果处于 UPDATE,重新启动自适应;
|
||||||
|
12. 清除热恢复 busy 标志。
|
||||||
|
|
||||||
|
## 7. ADRV9009 轻挂接流程
|
||||||
|
|
||||||
|
当 `g_skip_talise_cfg=1` 时,`adrv9009_cfg()` 执行:
|
||||||
|
|
||||||
|
1. 根据通道选择对应的 `taliseDeviceInfo`;
|
||||||
|
2. 重建 `p_myTaliseDevice_shm` 指针;
|
||||||
|
3. 复制 TALISE 初始化结构到本进程内存;
|
||||||
|
4. 调用 `TALISE_openHw()`,重新打开 HAL/SPI 访问;
|
||||||
|
5. 调用 `TALISE_getPllsLockStatus()` 读取 PLL 状态;
|
||||||
|
6. 直接返回,不执行以下冷启动操作:
|
||||||
|
- `TALISE_resetDevice()`;
|
||||||
|
- `TALISE_initialize()`;
|
||||||
|
- ARM firmware 重新加载;
|
||||||
|
- `TALISE_runInitCals()`;
|
||||||
|
- 其他只应在冷启动执行的校准。
|
||||||
|
|
||||||
|
该流程解决了“9009 仍在运行时重复 TALISE 初始化导致 err10”的问题。
|
||||||
|
|
||||||
|
## 8. JESD204B 重连流程
|
||||||
|
|
||||||
|
轻挂接只跳过 TALISE 侧完整初始化,`adrv9009_init()` 中 FPGA 侧链路配置仍继续执行:
|
||||||
|
|
||||||
|
1. 初始化 IMX 到 ADRV9009 的 SPI 设备;
|
||||||
|
2. 暂停或隔离射频输出;
|
||||||
|
3. 配置 FPGA JESD TX0;
|
||||||
|
4. 轻挂接 ADRV9009;
|
||||||
|
5. 配置 FPGA JESD FB0 和 RX0;
|
||||||
|
6. 写 FPGA AXI `0x78 = 0x111` 后清零,复位 TX/RX/FB GT;
|
||||||
|
7. 使能 JESD TX、RX 链路;
|
||||||
|
8. 恢复 RF route、FB route 和相关 FPGA AXI 寄存器;
|
||||||
|
9. 在 9009 framer 已开始发送后,再执行一次 RX/FB GT 晚复位:
|
||||||
|
|
||||||
|
```text
|
||||||
|
AXI 0x78 = 0x110
|
||||||
|
delay 2 ms
|
||||||
|
AXI 0x78 = 0x000
|
||||||
|
```
|
||||||
|
|
||||||
|
10. 读取 FPGA `0xBB` 检查同步状态;当前代码把 `0x07` 或 `0x03` 作为 FB 初始化正常的日志判据;
|
||||||
|
11. 恢复 ORx/RF monitor、DPD 和 FB route。
|
||||||
|
|
||||||
|
本链路使用 JESD204B Subclass 0 的 SYNC/ILAS 重握手,不依赖重新初始化 9009 CLKPLL。
|
||||||
|
|
||||||
|
## 9. 射频参数和输出恢复
|
||||||
|
|
||||||
|
FPGA/JESD 恢复过程中需要恢复以下运行参数:
|
||||||
|
|
||||||
|
- FPGA 调制和系统静态寄存器;
|
||||||
|
- 发射频率、功率和频偏;
|
||||||
|
- ADRV9009 TX attenuation;
|
||||||
|
- ORx manual gain;
|
||||||
|
- HMC472/FPGA AGC attenuation;
|
||||||
|
- ADRV9009 SPI 控制权,从 MCU 切回 FPGA;
|
||||||
|
- TX/RX/ORx RF route;
|
||||||
|
- DPD bypass/HOLD/UPDATE 状态;
|
||||||
|
- 保存的 DPD 曲线;
|
||||||
|
- FAGC、ORx RMS 和 TSNR 后台测量状态。
|
||||||
|
|
||||||
|
外部射频打开应放在恢复流程最后:
|
||||||
|
|
||||||
|
1. `adrv9009_set_tx_path_enable(1)` 调用 `TALISE_setRxTxEnable()` 打开 TX1;
|
||||||
|
2. 回读 `TALISE_getRxTxEnable()` 确认 TX 状态;
|
||||||
|
3. 打开 FPGA/物理 RF 开关;
|
||||||
|
4. 等待衰减稳定;
|
||||||
|
5. 恢复保存的输出功率或衰减设置。
|
||||||
|
|
||||||
|
如果用户保存的 RF 状态为 OFF,则恢复完成后仍必须保持 TX 和物理 RF 输出关闭。
|
||||||
|
|
||||||
|
## 10. 恢复成功判据
|
||||||
|
|
||||||
|
不能只以函数执行结束作为成功,建议至少检查以下项目:
|
||||||
|
|
||||||
|
| 检查项 | 建议判据 |
|
||||||
|
|---|---|
|
||||||
|
| 9009 SPI | scratch-pad 写入/回读一致 |
|
||||||
|
| 9009 PLL | CLKPLL、RFPLL 必须锁定;使用 AUXPLL 时同时检查 AUXPLL |
|
||||||
|
| 9009 ARM | ARM 状态正常,命令接口可响应 |
|
||||||
|
| 9009 TX | `TALISE_getRxTxEnable()` 回读包含期望的 TX 通道 |
|
||||||
|
| FPGA JESD | TX/RX/FB SYNC 状态符合当前设计 |
|
||||||
|
| FPGA `0xBB` | 当前代码期望恢复后为 `0x03` 或 `0x07` |
|
||||||
|
| FPGA config | `cfg_valid` 最终重新写为 `1` |
|
||||||
|
| ORx | `ob_fb`/FWD-FSSI 不应长期为 0、1 或饱和值 `0x7fff` |
|
||||||
|
| DPD | HOLD 曲线已重新加载,或 UPDATE 状态机已重新启动 |
|
||||||
|
| 射频输出 | 频谱仪测得中心频率、功率和肩部符合保存配置 |
|
||||||
|
|
||||||
|
建议日志中保留以下关键字,方便现场定位:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[PR10] boot: FPGA cfg_valid=...
|
||||||
|
[JESD-RELINK] light-attach 9009...
|
||||||
|
[HOT-RESTART] begin...
|
||||||
|
[HOT-RESTART] FAGC/ORx measurement restarted...
|
||||||
|
[HOT-RESTART] done
|
||||||
|
```
|
||||||
|
|
||||||
|
## 11. 当前实现的限制和待补强项
|
||||||
|
|
||||||
|
### 11.1 IMX `reboot` 与 AD9528 保持
|
||||||
|
|
||||||
|
当前板卡已确认 Linux `reboot` 可以重新进入 U-Boot 和内核。实测表明,异常不是 `reboot` 本身造成,而是启动脚本无条件重新配置 AD9528,打断了正在运行的参考时钟/SYSREF/JESD 关系。
|
||||||
|
|
||||||
|
启动脚本现在先调用 `imx_cfgad9528 --check-ready` 进行纯读检查:芯片型号、版本、PLL 配置、SYSREF 控制和 PLL2 锁定状态均正确时,完全不触碰 RESET 和 AD9528 寄存器;状态无效时才进入断电启动的复位+完整配置流程。因此无需依赖可能残留的文件标志。
|
||||||
|
|
||||||
|
### 11.2 升级包脚本同步保护
|
||||||
|
|
||||||
|
源码 `src/atsc_app/runme.sh` 和待打包目录 `out/upgrade_imx_fs/SMOD/runme.sh` 已统一为自动分流版本:包含 `zImage` 或指定 DTB 时执行 warm reboot;纯用户态升级则执行“kill 旧 App -> 启动新 App”。`make install` 会自动同步 `runme.sh` 和 `anywave_exc_start.sh`,避免后续构建重新引入旧版。生成 tar 包后仍应使用以下方式复查:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
tar -tzf SMOD*.tar.gz | grep runme.sh
|
||||||
|
tar -xOzf SMOD*.tar.gz SMOD/runme.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### 11.3 守护进程启动入口已统一
|
||||||
|
|
||||||
|
ATSC 守护进程使用 `/proc/<pid>/exe` 精确检测 `/aw/anywave/app/imx_atsc_app`,不再使用可能受线程改名影响的 `ps | grep`。守护拉起命令不再传入参数 `1`;App 仍兼容旧版守护程序传入的 `1`,但会进入与无参数启动相同的 RF 恢复流程。
|
||||||
|
|
||||||
|
### 11.4 `cfg_valid` 不能完全代表 9009 状态
|
||||||
|
|
||||||
|
应把 FPGA 和 9009 状态拆开判断:
|
||||||
|
|
||||||
|
| FPGA状态 | 9009状态 | 应执行流程 |
|
||||||
|
|---|---|---|
|
||||||
|
| 未重载 | 存活且PLL锁定 | 轻挂接,按需刷新FPGA/JESD和射频参数 |
|
||||||
|
| 已重载 | 存活且PLL锁定 | 跳过TALISE初始化,完整重建FPGA/JESD |
|
||||||
|
| 已重载 | 未初始化/无SPI响应 | 完整冷启动TALISE,再配置FPGA/JESD |
|
||||||
|
| 未重载 | 无SPI响应或PLL异常 | 保持RF关闭,尝试9009硬复位和完整初始化 |
|
||||||
|
|
||||||
|
当 warm reboot 保留 AD9528 时,App 仍需结合 FPGA/9009 状态选择 light-attach 或完整恢复;不能单独把 `cfg_valid` 当作整条 RF 链路有效的证明。
|
||||||
|
|
||||||
|
### 11.5 当前错误处理偏弱
|
||||||
|
|
||||||
|
现有 light-attach 会打印 PLL 状态,但未严格检查 API 返回值和锁定 bit;部分 JESD 状态异常也只是打印日志,恢复流程仍会继续。发布版本建议:
|
||||||
|
|
||||||
|
- 严格检查 `TALISE_openHw()`、PLL、TX enable 和 JESD 状态;
|
||||||
|
- 任一关键检查失败时保持物理 RF 关闭;
|
||||||
|
- 增加有限次数的 FPGA/JESD 重连重试;
|
||||||
|
- 重试失败后产生明确告警,禁止无效链路下打开射频。
|
||||||
|
|
||||||
|
### 11.6 热恢复后的 RF 最终状态需要统一收口
|
||||||
|
|
||||||
|
热恢复中存在 `set_pa_output(0)`,而对应的 `set_pa_output(1)` 已被注释。需要结合 FPGA `AXI 0x100` 的实际定义确认 `0` 是否代表正常调制路径。如果它确实是 PA 输出使能,则必须在所有检查成功后统一恢复,避免 JESD 已同步但内部 RF 数据仍被关闭。
|
||||||
|
|
||||||
|
## 12. 推荐的最终启动决策
|
||||||
|
|
||||||
|
推荐在应用启动时使用以下决策顺序:
|
||||||
|
|
||||||
|
```text
|
||||||
|
1. RF物理开关置OFF,启动RF hold
|
||||||
|
2. 检查FPGA cfg_valid
|
||||||
|
3. 检查9009 SPI、scratch-pad、PLL和ARM状态
|
||||||
|
4. 9009存活 -> light-attach
|
||||||
|
5. 9009未运行 -> full TALISE init
|
||||||
|
6. FPGA重载或新进程启动 -> 恢复FPGA参数并重建JESD
|
||||||
|
7. 恢复频率/衰减/ORx/DPD/FAGC
|
||||||
|
8. 校验PLL + JESD + TX状态
|
||||||
|
9. 成功 -> 按保存状态释放RF hold
|
||||||
|
10. 失败 -> RF保持OFF,重试或告警
|
||||||
|
```
|
||||||
|
|
||||||
|
这样可以让以下入口最终汇聚到同一套 RF 恢复状态机:
|
||||||
|
|
||||||
|
- IMX 应用升级后的 kill/restart;
|
||||||
|
- 守护进程异常重启应用;
|
||||||
|
- 命令行/Web/IMX 内核或 DTB 升级触发的 warm reboot;
|
||||||
|
- FPGA 在线升级/IPROG;
|
||||||
|
- FPGA 运行时异常重载。
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# ATSC ADRV9009 射频信号主链路
|
||||||
|
|
||||||
|
本文档按当前工程源码整理 FPGA 到激励器 RF 接口的实际信号路径。
|
||||||
|
|
||||||
|
## 射频信号主路径
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
INPUT["TS 流 / 测试数据"] --> MOD["FPGA ATSC 8VSB 调制"]
|
||||||
|
MOD --> DPD["FPGA DPD 与 IQ 处理"]
|
||||||
|
DPD --> ROUTE["打开 FPGA IQ 路由<br/>FBROUTE = 0x10"]
|
||||||
|
ROUTE --> JESD_TX["FPGA JESD204B TX0<br/>2 Lane · Subclass 0 · 无 SYSREF"]
|
||||||
|
JESD_TX --> DEF["ADRV9009 JESD Deframer"]
|
||||||
|
DEF --> DAC["ADRV9009 TX1 DAC 与插值滤波"]
|
||||||
|
DAC --> MIX["正交调制 + RF PLL 上变频"]
|
||||||
|
MIX --> TX_PATH{"ADRV9009 TX1 通道使能?"}
|
||||||
|
TX_PATH -->|"TALISE_setRxTxEnable(..., TAL_TX1)"| ADRV_ATT["ADRV9009 TX 衰减<br/>0~40000 mdB"]
|
||||||
|
TX_PATH -->|"TAL_TXOFF"| STOP1["芯片内部 TX 停止"]
|
||||||
|
|
||||||
|
ADRV_ATT --> HMC["HMC472 外部衰减<br/>0~63,每步 0.5 dB"]
|
||||||
|
HMC --> RF_SW{"板级 RF 开关?"}
|
||||||
|
RF_SW -->|"FPGA seg1:0x26 = 1"| RF_OUT["激励器 RF 输出接口"]
|
||||||
|
RF_SW -->|"FPGA seg1:0x26 = 0"| STOP2["外部 RF 接口隔离"]
|
||||||
|
|
||||||
|
WE["当前频点 WE"] --> POWER["总衰减计算"]
|
||||||
|
LCD["LCD / 网页输出功率"] --> POWER
|
||||||
|
HW["HardwareVersion<br/>V1.0:0 dB<br/>V1.1:当前代码 +11 dB 补偿"] --> POWER
|
||||||
|
POWER --> SPLIT["衰减分配<br/>ADRV9009 优选约 8 dB<br/>其余优先分配给 HMC472"]
|
||||||
|
SPLIT --> ADRV_ATT
|
||||||
|
SPLIT --> HMC
|
||||||
|
|
||||||
|
MIX -. "TX1 → ORx1 内部映射" .-> ORX["ADRV9009 ORx1"]
|
||||||
|
ORX --> FRAMER["ADRV9009 JESD Framer B"]
|
||||||
|
FRAMER --> FPGA_FB["FPGA 反馈 IQ"]
|
||||||
|
FPGA_FB --> FAGC["ORx FAGC"]
|
||||||
|
FPGA_FB --> DPD_FB["DPD 反馈计算"]
|
||||||
|
FPGA_FB --> QUALITY["SNR / 左右带肩计算"]
|
||||||
|
FAGC -. "调节 ORx 增益" .-> ORX
|
||||||
|
DPD_FB -. "更新预失真参数" .-> DPD
|
||||||
|
```
|
||||||
|
|
||||||
|
## 三层射频开关
|
||||||
|
|
||||||
|
1. `TALISE_radioOn()`:ADRV9009 ARM 进入 Radio On 状态。
|
||||||
|
2. `TALISE_setRxTxEnable(..., TAL_TX1)`:开启 ADRV9009 TX1 信号链。
|
||||||
|
3. FPGA `seg1:0x26 = 1`:接通板级物理 RF 输出开关。
|
||||||
|
|
||||||
|
三层状态以及功率衰减配置均正确后,外部 RF 接口才会稳定输出射频。
|
||||||
|
|
||||||
|
## 关键源码入口
|
||||||
|
|
||||||
|
- `bsp/fpga.c::fpga_save_all_para()`:恢复 FPGA、RF、WE、DPD 等参数。
|
||||||
|
- `adrv9009/adrv9009_config.c::adrv9009_init()`:初始化 ADRV9009 与 JESD 链路。
|
||||||
|
- `adrv9009/jesd204b_config.c::jesd204b_tx0_cfg()`:配置 FPGA JESD204B TX0。
|
||||||
|
- `adrv9009/adrv9009_config.c::adrv9009_set_tx_path_enable()`:控制 ADRV9009 TX1。
|
||||||
|
- `bsp/fpga.c::fpga_save_power()`:计算并分配 ADRV9009/HMC472 衰减。
|
||||||
|
- `bsp/fpga.c::fpga_set_rf_output_with_para()`:控制最终板级 RF 输出。
|
||||||
Executable → Regular
+50
-19
@@ -41,6 +41,7 @@
|
|||||||
#include "../inc/fpga.h"
|
#include "../inc/fpga.h"
|
||||||
#include "../inc/http_get.h"
|
#include "../inc/http_get.h"
|
||||||
#include "../inc/adrv9009_ext.h"
|
#include "../inc/adrv9009_ext.h"
|
||||||
|
#include "../inc/iq_rx.h"
|
||||||
|
|
||||||
extern uint32 adpc_source_udp;
|
extern uint32 adpc_source_udp;
|
||||||
extern uint32 adpc_source_web;
|
extern uint32 adpc_source_web;
|
||||||
@@ -113,8 +114,16 @@ static void http_load_psip(S_PSTS_PARA *ppara)
|
|||||||
void http_load_status(S_CGI_STATUS *ppara)
|
void http_load_status(S_CGI_STATUS *ppara)
|
||||||
{
|
{
|
||||||
int tmp_imd;
|
int tmp_imd;
|
||||||
|
uint16 measured_tsnr;
|
||||||
|
uint8 measured_l_imd;
|
||||||
|
uint8 measured_u_imd;
|
||||||
PS_LOG_PARA appara=&atsc_para.log_para;
|
PS_LOG_PARA appara=&atsc_para.log_para;
|
||||||
|
|
||||||
|
/* PR19 values are produced asynchronously by iq_rx. Read that source here
|
||||||
|
* instead of relying on log_para's once-per-second refresh, which is skipped
|
||||||
|
* while update_flag is set during a DPD update. */
|
||||||
|
get_tsnr_limd_uimd(&measured_tsnr, &measured_l_imd, &measured_u_imd);
|
||||||
|
|
||||||
ppara->input = appara->asi_input;
|
ppara->input = appara->asi_input;
|
||||||
ppara->status= appara->input_ok_flag?0:1;
|
ppara->status= appara->input_ok_flag?0:1;
|
||||||
|
|
||||||
@@ -145,32 +154,32 @@ void http_load_status(S_CGI_STATUS *ppara)
|
|||||||
|
|
||||||
//ppara->tsnr = atsc_para.log_para.tsnr;
|
//ppara->tsnr = atsc_para.log_para.tsnr;
|
||||||
if(atsc_para.wd_para.sa >= 0)
|
if(atsc_para.wd_para.sa >= 0)
|
||||||
ppara->tsnr = atsc_para.log_para.tsnr+atsc_para.wd_para.sa;
|
ppara->tsnr = measured_tsnr+atsc_para.wd_para.sa;
|
||||||
else
|
else
|
||||||
ppara->tsnr = atsc_para.log_para.tsnr-(u8)(atsc_para.wd_para.sa*-1);
|
ppara->tsnr = measured_tsnr-(u8)(atsc_para.wd_para.sa*-1);
|
||||||
|
|
||||||
//ppara->l_imd = atsc_para.log_para.l_imd;
|
//ppara->l_imd = atsc_para.log_para.l_imd;
|
||||||
if(atsc_para.wd_para.sb >= 0)
|
if(atsc_para.wd_para.sb >= 0)
|
||||||
ppara->l_imd = atsc_para.log_para.l_imd+atsc_para.wd_para.sb;
|
ppara->l_imd = measured_l_imd+atsc_para.wd_para.sb;
|
||||||
else{
|
else{
|
||||||
tmp_imd = atsc_para.log_para.l_imd - (int)(-1*atsc_para.wd_para.sb);
|
tmp_imd = measured_l_imd - (int)(-1*atsc_para.wd_para.sb);
|
||||||
if(tmp_imd<0)
|
if(tmp_imd<0)
|
||||||
ppara->l_imd = 0;
|
ppara->l_imd = 0;
|
||||||
else{
|
else{
|
||||||
int8 tmpdata = -1*atsc_para.wd_para.sb;
|
int8 tmpdata = -1*atsc_para.wd_para.sb;
|
||||||
ppara->l_imd = atsc_para.log_para.l_imd-(u8)(tmpdata);
|
ppara->l_imd = measured_l_imd-(u8)(tmpdata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//ppara->u_imd = atsc_para.log_para.u_imd;
|
//ppara->u_imd = atsc_para.log_para.u_imd;
|
||||||
if(atsc_para.wd_para.sc >= 0)
|
if(atsc_para.wd_para.sc >= 0)
|
||||||
ppara->u_imd = atsc_para.log_para.u_imd+atsc_para.wd_para.sc;
|
ppara->u_imd = measured_u_imd+atsc_para.wd_para.sc;
|
||||||
else{
|
else{
|
||||||
tmp_imd = atsc_para.log_para.u_imd - (int)(-1*atsc_para.wd_para.sc);
|
tmp_imd = measured_u_imd - (int)(-1*atsc_para.wd_para.sc);
|
||||||
if(tmp_imd<0)
|
if(tmp_imd<0)
|
||||||
ppara->u_imd = 0;
|
ppara->u_imd = 0;
|
||||||
else{
|
else{
|
||||||
ppara->u_imd = atsc_para.log_para.u_imd-(u8)(-1*atsc_para.wd_para.sc);
|
ppara->u_imd = measured_u_imd-(u8)(-1*atsc_para.wd_para.sc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ppara->syserr = atsc_para.log_para.syserr;
|
ppara->syserr = atsc_para.log_para.syserr;
|
||||||
@@ -272,6 +281,7 @@ void http_load_sys(S_CGI_SET *ppara)
|
|||||||
ppara->agc_att = atsc_para.system_para.agc_att;
|
ppara->agc_att = atsc_para.system_para.agc_att;
|
||||||
ppara->adrv9009_att = atsc_para.system_para.adrv9009_att;
|
ppara->adrv9009_att = atsc_para.system_para.adrv9009_att;
|
||||||
ppara->orx_manu_gain = atsc_para.system_para.orx_manu_gain;
|
ppara->orx_manu_gain = atsc_para.system_para.orx_manu_gain;
|
||||||
|
ppara->hardware_version = atsc_para.system_para.hardware_version;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ERR_TO_LCM
|
#if ERR_TO_LCM
|
||||||
@@ -881,7 +891,12 @@ void http_write_sys(S_CGI_SET *ppara)
|
|||||||
|
|
||||||
|
|
||||||
if(atsc_para.system_para.adpc<=ADPC_HOLD&&g_trigger_up_tsnr_flg == 0)// 无大小均衡
|
if(atsc_para.system_para.adpc<=ADPC_HOLD&&g_trigger_up_tsnr_flg == 0)// 无大小均衡
|
||||||
|
{
|
||||||
atsc_para.system_para.ab_mode = ppara->ab_mode;
|
atsc_para.system_para.ab_mode = ppara->ab_mode;
|
||||||
|
/* FSSI Refresh alternates A/B feedback paths. Pause IQ collection until
|
||||||
|
* the unit returns to the stable single-A path. */
|
||||||
|
iq_spec_set_enable(ppara->ab_mode == 0);
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------
|
//-----------------------------------------
|
||||||
uint8 adpc_bak;
|
uint8 adpc_bak;
|
||||||
@@ -975,6 +990,25 @@ void http_write_sys(S_CGI_SET *ppara)
|
|||||||
c256_bytewrite(EEPROM_ADRV9009_ATT_L, ppara->adrv9009_att&0xff);
|
c256_bytewrite(EEPROM_ADRV9009_ATT_L, ppara->adrv9009_att&0xff);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if(ppara->hardware_version < HARDWARE_VERSION_END &&
|
||||||
|
ppara->hardware_version != atsc_para.system_para.hardware_version)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* V1.1 -> V1.0 removes 11 dB of compensation. Always switch
|
||||||
|
* RF off before applying a hardware profile change.
|
||||||
|
*/
|
||||||
|
atsc_para.rf_para.rf = 0;
|
||||||
|
c256_bytewrite(EEPROM_RF_ADDR, 0);
|
||||||
|
fpga_set_rf_output(0);
|
||||||
|
atsc_para.system_para.hardware_version = ppara->hardware_version;
|
||||||
|
c256_bytewrite(EEPROM_HARDWARE_VERSION, ppara->hardware_version);
|
||||||
|
atsc_para.we_para.ch = atsc_para.rf_para.txfreq;
|
||||||
|
e2rom_read_leak(atsc_para.we_para.ch, &atsc_para.we_para);
|
||||||
|
fpga_save_we(&atsc_para.we_para);
|
||||||
|
log_printf(LOG_INFO, I_USER_CFG,
|
||||||
|
"hardware version changed to V1.%d; RF forced off; WE=%u reloaded\n",
|
||||||
|
ppara->hardware_version, atsc_para.we_para.power);
|
||||||
|
}
|
||||||
if(ppara->orx_manu_gain!=atsc_para.system_para.orx_manu_gain){
|
if(ppara->orx_manu_gain!=atsc_para.system_para.orx_manu_gain){
|
||||||
cfg_adrv_orx_manu_gain(ppara->orx_manu_gain);
|
cfg_adrv_orx_manu_gain(ppara->orx_manu_gain);
|
||||||
atsc_para.system_para.orx_manu_gain = ppara->orx_manu_gain;
|
atsc_para.system_para.orx_manu_gain = ppara->orx_manu_gain;
|
||||||
@@ -1055,15 +1089,14 @@ void http_write_sys(S_CGI_SET *ppara)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(ppara->sys_reset) {
|
if(ppara->sys_reset) {
|
||||||
log_printf(LOG_DEBUG, I_USER_CFG, "SYSTEM RESET NOW (IMX+FPGA)!!!!!!!!!!!!!\n");
|
/* A Web System Reset is an IMX warm reboot. Keep FPGA, AD9528 and
|
||||||
//复位 FPGA本身复位
|
* ADRV9009 powered so the normal boot path can preserve the RF link. */
|
||||||
fpga_byte_write(0x01,0xfe,0x5a);
|
log_printf(LOG_DEBUG, I_USER_CFG,
|
||||||
fpga_byte_write(0x01,0xfe,0xa5);
|
"WEB SYSTEM RESET NOW (IMX warm reboot, keep RF hardware)\n");
|
||||||
|
system("sync");
|
||||||
// [one@20220419 add imx reset]
|
system("reboot");
|
||||||
system("sync");
|
return;
|
||||||
system("reboot");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1623,5 +1656,3 @@ void http_save_change_para(void *p)
|
|||||||
|
|
||||||
/*****************************END**************************************/
|
/*****************************END**************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Executable → Regular
+66
-77
@@ -46,24 +46,23 @@
|
|||||||
#define IQ_WBYTES (IQ_NPKT * IQ_PAY) /* 1 MiB per window */
|
#define IQ_WBYTES (IQ_NPKT * IQ_PAY) /* 1 MiB per window */
|
||||||
|
|
||||||
/* Welch over the whole window. The FFT is 4096 points because at 10 MSPS that is
|
/* Welch over the whole window. The FFT is 4096 points because at 10 MSPS that is
|
||||||
* a 2441.40625 Hz bin -- bit for bit the same bin the FPGA's imd_imd_fft_meas uses
|
* a 2441.40625 Hz bin. The published trace is folded down to 1024 points (4 bins
|
||||||
* at 40 MHz/16384. So bins 10 / +1290 / -1290 land exactly on its cent/L/R taps and
|
* each) to keep the JSON small. */
|
||||||
* UIMD/LIMD can be recomputed here without redefining anything. The published trace
|
|
||||||
* is folded down to 1024 points (4 bins each) to keep the JSON small. */
|
|
||||||
#define IQ_FFT 4096
|
#define IQ_FFT 4096
|
||||||
#define IQ_SEGPKT (IQ_FFT / IQ_NF) /* 16 packets per segment */
|
#define IQ_SEGPKT (IQ_FFT / IQ_NF) /* 16 packets per segment */
|
||||||
#define IQ_NSEG (IQ_NPKT / IQ_SEGPKT)
|
#define IQ_NSEG (IQ_NPKT / IQ_SEGPKT)
|
||||||
#define IQ_OUT 1024 /* published trace points */
|
#define IQ_OUT 1024 /* published trace points */
|
||||||
#define IQ_FOLD (IQ_FFT / IQ_OUT)
|
#define IQ_FOLD (IQ_FFT / IQ_OUT)
|
||||||
|
|
||||||
/* imd_imd_fft_meas taps: cent = +24.414 kHz, L = +3.1494 MHz, R = -3.1494 MHz.
|
/* PR19 shoulder measurement windows, in MHz relative to the channel centre.
|
||||||
* HALF widens each tap to 11 bins (+-12.2 kHz): a single 2.4 kHz bin of a data
|
* Average linear power in each 500 kHz window before converting the ratios to dB. */
|
||||||
* signal is chi-square with 2 dof, i.e. +-8 dB of scatter, and the -3.1494 MHz bin
|
#define IMD_CENT_LO_MHZ (-0.25f)
|
||||||
* additionally sits on a narrow feature ~5 dB above its neighbours. */
|
#define IMD_CENT_HI_MHZ ( 0.25f)
|
||||||
#define IMD_CENT 10
|
#define IMD_LEFT_LO_MHZ (-3.50f)
|
||||||
#define IMD_L 1290
|
#define IMD_LEFT_HI_MHZ (-3.00f)
|
||||||
#define IMD_R (IQ_FFT - IMD_L)
|
#define IMD_RIGHT_LO_MHZ ( 3.00f)
|
||||||
#define IMD_HALF 5
|
#define IMD_RIGHT_HI_MHZ ( 3.50f)
|
||||||
|
#define IMD_BW_KHZ 500.0f
|
||||||
|
|
||||||
#define IQ_JSON "/tmp/iq_spectrum.json"
|
#define IQ_JSON "/tmp/iq_spectrum.json"
|
||||||
#define IQ_JSON_TMP "/tmp/iq_spectrum.json.tmp"
|
#define IQ_JSON_TMP "/tmp/iq_spectrum.json.tmp"
|
||||||
@@ -128,12 +127,6 @@ static int longest_run(const unsigned char *seen)
|
|||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cmp_float(const void *a, const void *b)
|
|
||||||
{
|
|
||||||
float x = *(const float *)a, y = *(const float *)b;
|
|
||||||
return (x < y) ? -1 : (x > y);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- newest UIMD/LIMD, published to lcd_task.c's get_tsnr_limd_uimd() ---- */
|
/* ---- newest UIMD/LIMD, published to lcd_task.c's get_tsnr_limd_uimd() ---- */
|
||||||
static pthread_mutex_t imd_mtx = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t imd_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||||
static float imd_l_db, imd_u_db;
|
static float imd_l_db, imd_u_db;
|
||||||
@@ -142,7 +135,6 @@ static time_t imd_stamp; /* 0 = nothing measured yet */
|
|||||||
int iq_get_imd(float *limd_db, float *uimd_db)
|
int iq_get_imd(float *limd_db, float *uimd_db)
|
||||||
{
|
{
|
||||||
int fresh;
|
int fresh;
|
||||||
if (!iq_spec_get_enable()) return 0;
|
|
||||||
pthread_mutex_lock(&imd_mtx);
|
pthread_mutex_lock(&imd_mtx);
|
||||||
fresh = imd_stamp && (time(NULL) - imd_stamp) <= IQ_IMD_MAX_AGE;
|
fresh = imd_stamp && (time(NULL) - imd_stamp) <= IQ_IMD_MAX_AGE;
|
||||||
if (fresh) { *limd_db = imd_l_db; *uimd_db = imd_u_db; }
|
if (fresh) { *limd_db = imd_l_db; *uimd_db = imd_u_db; }
|
||||||
@@ -169,22 +161,22 @@ void iq_spec_set_enable(int enable)
|
|||||||
|
|
||||||
iq_spec_enable = enable;
|
iq_spec_enable = enable;
|
||||||
if (!enable) {
|
if (!enable) {
|
||||||
|
/* FSSI A/B switching mixes two feedback paths, so no IQ window collected
|
||||||
|
* in this mode is valid as a stable single-path spectrum or eye. */
|
||||||
pthread_mutex_lock(&iq_mtx);
|
pthread_mutex_lock(&iq_mtx);
|
||||||
iq_ready = -1;
|
iq_ready = -1;
|
||||||
pthread_mutex_unlock(&iq_mtx);
|
pthread_mutex_unlock(&iq_mtx);
|
||||||
|
|
||||||
pthread_mutex_lock(&vsb_mtx);
|
pthread_mutex_lock(&vsb_mtx);
|
||||||
vsb_pending = 0;
|
vsb_pending = 0;
|
||||||
pthread_mutex_unlock(&vsb_mtx);
|
pthread_mutex_unlock(&vsb_mtx);
|
||||||
|
|
||||||
pthread_mutex_lock(&imd_mtx);
|
pthread_mutex_lock(&imd_mtx);
|
||||||
imd_stamp = 0;
|
imd_stamp = 0;
|
||||||
pthread_mutex_unlock(&imd_mtx);
|
pthread_mutex_unlock(&imd_mtx);
|
||||||
iq_vsb_clear_results();
|
iq_vsb_clear_results();
|
||||||
|
|
||||||
unlink(IQ_JSON);
|
unlink(IQ_JSON);
|
||||||
unlink(IQ_JSON_TMP);
|
unlink(IQ_JSON_TMP);
|
||||||
unlink("/tmp/iq_eye.json");
|
unlink("/tmp/iq_eye.json");
|
||||||
|
unlink("/tmp/iq_raw.bin");
|
||||||
}
|
}
|
||||||
printf("[iq_rx] spectrum %s\n", enable ? "enabled" : "disabled");
|
printf("[iq_rx] spectrum %s\n", enable ? "enabled" : "disabled");
|
||||||
}
|
}
|
||||||
@@ -203,10 +195,9 @@ static void *iq_vsb_thr(void *arg)
|
|||||||
while (!vsb_pending) pthread_cond_wait(&vsb_cond, &vsb_mtx);
|
while (!vsb_pending) pthread_cond_wait(&vsb_cond, &vsb_mtx);
|
||||||
pthread_mutex_unlock(&vsb_mtx);
|
pthread_mutex_unlock(&vsb_mtx);
|
||||||
|
|
||||||
if (!iq_spec_get_enable()) continue;
|
if (iq_spec_get_enable()) {
|
||||||
iq_vsb_analyse(vsb_win, vsb_wid);
|
iq_vsb_analyse(vsb_win, vsb_wid);
|
||||||
|
} else {
|
||||||
if (!iq_spec_get_enable()) {
|
|
||||||
iq_vsb_clear_results();
|
iq_vsb_clear_results();
|
||||||
unlink("/tmp/iq_eye.json");
|
unlink("/tmp/iq_eye.json");
|
||||||
}
|
}
|
||||||
@@ -218,13 +209,21 @@ static void *iq_vsb_thr(void *arg)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* mean power of the 2*half+1 bins centred on k, wrapping the FFT index */
|
/* Mean linear power of all FFT bins inside the inclusive signed-frequency range. */
|
||||||
static float band_power(const float *psd, int k, int half)
|
static float band_power_range(const float *psd, float f_lo_mhz, float f_hi_mhz)
|
||||||
{
|
{
|
||||||
float acc = 0.0f;
|
float acc = 0.0f;
|
||||||
int d;
|
const float df = IQ_FS_MHZ / IQ_FFT;
|
||||||
for (d = -half; d <= half; d++) acc += psd[(k + d) & (IQ_FFT - 1)];
|
int i, count = 0;
|
||||||
return acc / (2 * half + 1);
|
|
||||||
|
for (i = 0; i < IQ_FFT; i++) {
|
||||||
|
float f = (i < IQ_FFT / 2 ? i : i - IQ_FFT) * df;
|
||||||
|
if (f >= f_lo_mhz && f <= f_hi_mhz) {
|
||||||
|
acc += psd[i];
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count ? acc / count : 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -290,19 +289,12 @@ static void *iq_spec_thr(void *arg)
|
|||||||
}
|
}
|
||||||
if (nseg == 0) { pthread_mutex_lock(&iq_mtx); iq_ready = -1;
|
if (nseg == 0) { pthread_mutex_lock(&iq_mtx); iq_ready = -1;
|
||||||
pthread_mutex_unlock(&iq_mtx); continue; }
|
pthread_mutex_unlock(&iq_mtx); continue; }
|
||||||
if (!iq_spec_get_enable()) {
|
|
||||||
pthread_mutex_lock(&iq_mtx);
|
|
||||||
iq_ready = -1;
|
|
||||||
pthread_mutex_unlock(&iq_mtx);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
const float df = IQ_FS_MHZ / IQ_FFT; /* 2.44140625 kHz, == FPGA's 40MHz/16384 */
|
const float df = IQ_FS_MHZ / IQ_FFT; /* 2.44140625 kHz, == FPGA's 40MHz/16384 */
|
||||||
const float dfo = df * IQ_FOLD; /* published bin */
|
const float dfo = df * IQ_FOLD; /* published bin */
|
||||||
float flat = 0.0f, pk = 0.0f, pilot = 0.0f, shoulder = 0.0f;
|
float flat = 0.0f, pk = 0.0f, pilot = 0.0f;
|
||||||
float p_cent, p_hi, p_lo, limd, uimd;
|
float p_cent, p_hi, p_lo, p0_db, pl_db, pr_db, limd, uimd;
|
||||||
int nflat = 0, nsh = 0, pkbin = 0, pos = 0, j;
|
int nflat = 0, pkbin = 0, pos = 0, j;
|
||||||
|
|
||||||
for (i = 0; i < IQ_FFT; i++) {
|
for (i = 0; i < IQ_FFT; i++) {
|
||||||
float f = (i < IQ_FFT / 2 ? i : i - IQ_FFT) * df;
|
float f = (i < IQ_FFT / 2 ? i : i - IQ_FFT) * df;
|
||||||
@@ -313,16 +305,17 @@ static void *iq_spec_thr(void *arg)
|
|||||||
flat = (nflat ? flat / nflat : 1e-12f) + 1e-12f;
|
flat = (nflat ? flat / nflat : 1e-12f) + 1e-12f;
|
||||||
pilot = (pkbin < IQ_FFT / 2 ? pkbin : pkbin - IQ_FFT) * df;
|
pilot = (pkbin < IQ_FFT / 2 ? pkbin : pkbin - IQ_FFT) * df;
|
||||||
|
|
||||||
/* UIMD/LIMD on imd_imd_fft_meas's own taps, band-averaged.
|
/* PR19: P0 is the mean power in the centre 500 kHz; PL and PR are
|
||||||
* LIMD is by definition the shoulder on the pilot's side of the carrier,
|
* the mean powers in -3.5..-3.0 MHz and +3.0..+3.5 MHz. The reported
|
||||||
* so key it off the measured pilot rather than off a hard-coded sign: an
|
* left/right shoulders are P0-PL and P0-PR respectively. */
|
||||||
* inverted ORx spectrum then swaps the taps instead of the labels. */
|
p_cent = band_power_range(psd, IMD_CENT_LO_MHZ, IMD_CENT_HI_MHZ);
|
||||||
p_cent = band_power(psd, IMD_CENT, IMD_HALF);
|
p_lo = band_power_range(psd, IMD_LEFT_LO_MHZ, IMD_LEFT_HI_MHZ);
|
||||||
p_hi = band_power(psd, IMD_L, IMD_HALF); /* +3.1494 MHz */
|
p_hi = band_power_range(psd, IMD_RIGHT_LO_MHZ, IMD_RIGHT_HI_MHZ);
|
||||||
p_lo = band_power(psd, IMD_R, IMD_HALF); /* -3.1494 MHz */
|
|
||||||
if (pilot > 0) { float t = p_hi; p_hi = p_lo; p_lo = t; }
|
|
||||||
limd = 10.0f * log10f(p_cent / (p_lo + 1e-12f) + 1e-12f);
|
limd = 10.0f * log10f(p_cent / (p_lo + 1e-12f) + 1e-12f);
|
||||||
uimd = 10.0f * log10f(p_cent / (p_hi + 1e-12f) + 1e-12f);
|
uimd = 10.0f * log10f(p_cent / (p_hi + 1e-12f) + 1e-12f);
|
||||||
|
p0_db = 10.0f * log10f(p_cent / flat + 1e-12f);
|
||||||
|
pl_db = 10.0f * log10f(p_lo / flat + 1e-12f);
|
||||||
|
pr_db = 10.0f * log10f(p_hi / flat + 1e-12f);
|
||||||
|
|
||||||
pthread_mutex_lock(&imd_mtx);
|
pthread_mutex_lock(&imd_mtx);
|
||||||
imd_l_db = limd; imd_u_db = uimd; imd_stamp = time(NULL);
|
imd_l_db = limd; imd_u_db = uimd; imd_stamp = time(NULL);
|
||||||
@@ -337,25 +330,18 @@ static void *iq_spec_thr(void *arg)
|
|||||||
}
|
}
|
||||||
for (j = 0; j < IQ_OUT; j++) psd[j] = sh[j];
|
for (j = 0; j < IQ_OUT; j++) psd[j] = sh[j];
|
||||||
|
|
||||||
for (j = 0; j < IQ_OUT; j++) {
|
|
||||||
float f = (j < IQ_OUT / 2 ? j : j - IQ_OUT) * dfo;
|
|
||||||
float a = f < 0 ? -f : f;
|
|
||||||
if (a >= 3.5f && a <= 4.8f) sh[nsh++] = psd[j]; /* shoulder, clear of Nyquist */
|
|
||||||
}
|
|
||||||
if (nsh) {
|
|
||||||
qsort(sh, nsh, sizeof(float), cmp_float);
|
|
||||||
shoulder = sh[nsh / 2];
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = snprintf(json, sizeof(json),
|
pos = snprintf(json, sizeof(json),
|
||||||
"{\"wid\":%d,\"pkts\":%d,\"npkt\":%d,\"run\":%d,\"ms\":%.2f,"
|
"{\"wid\":%d,\"pkts\":%d,\"npkt\":%d,\"run\":%d,\"ms\":%.2f,"
|
||||||
"\"nseg\":%d,\"fs\":%.3f,\"n\":%d,\"f0\":%.6f,\"df\":%.6f,"
|
"\"nseg\":%d,\"fs\":%.3f,\"n\":%d,\"f0\":%.6f,\"df\":%.6f,"
|
||||||
"\"rbw_khz\":%.2f,\"pilot\":%.4f,\"peak\":%.2f,\"shoulder\":%.2f,"
|
"\"rbw_khz\":%.2f,\"pilot\":%.4f,\"peak\":%.2f,"
|
||||||
"\"limd\":%.2f,\"uimd\":%.2f,\"imd_rbw_khz\":%.3f,\"psd\":[",
|
"\"shoulder\":%.2f,\"lshoulder\":%.2f,\"rshoulder\":%.2f,"
|
||||||
|
"\"limd\":%.2f,\"uimd\":%.2f,\"p0_db\":%.2f,\"pl_db\":%.2f,\"pr_db\":%.2f,"
|
||||||
|
"\"imd_rbw_khz\":%.3f,\"psd\":[",
|
||||||
wid, npkt, IQ_NPKT, run, run * IQ_NF / (IQ_FS_MHZ * 1e3f),
|
wid, npkt, IQ_NPKT, run, run * IQ_NF / (IQ_FS_MHZ * 1e3f),
|
||||||
nseg, IQ_FS_MHZ, IQ_OUT, -IQ_FS_MHZ / 2.0f + 0.5f * (IQ_FOLD - 1) * df, dfo,
|
nseg, IQ_FS_MHZ, IQ_OUT, -IQ_FS_MHZ / 2.0f + 0.5f * (IQ_FOLD - 1) * df, dfo,
|
||||||
dfo * 1000.0f, pilot, 10.0f * log10f(pk / flat + 1e-12f), shoulder,
|
dfo * 1000.0f, pilot, 10.0f * log10f(pk / flat + 1e-12f),
|
||||||
limd, uimd, df * 1000.0f * (2 * IMD_HALF + 1));
|
limd < uimd ? limd : uimd, limd, uimd, limd, uimd,
|
||||||
|
p0_db, pl_db, pr_db, IMD_BW_KHZ);
|
||||||
/* emit fftshift-ed, i.e. ascending frequency from f0 */
|
/* emit fftshift-ed, i.e. ascending frequency from f0 */
|
||||||
for (j = 0; j < IQ_OUT && pos < (int)sizeof(json) - 16; j++) {
|
for (j = 0; j < IQ_OUT && pos < (int)sizeof(json) - 16; j++) {
|
||||||
int k = (j + IQ_OUT / 2) & (IQ_OUT - 1);
|
int k = (j + IQ_OUT / 2) & (IQ_OUT - 1);
|
||||||
@@ -364,12 +350,14 @@ static void *iq_spec_thr(void *arg)
|
|||||||
}
|
}
|
||||||
pos += snprintf(json + pos, sizeof(json) - pos, "]}\n");
|
pos += snprintf(json + pos, sizeof(json) - pos, "]}\n");
|
||||||
|
|
||||||
{ FILE *f = fopen(IQ_JSON_TMP, "w"); /* write+rename: the CGI never sees a partial file */
|
if (iq_spec_get_enable()) {
|
||||||
|
FILE *f = fopen(IQ_JSON_TMP, "w"); /* write+rename: the CGI never sees a partial file */
|
||||||
if (f) { fwrite(json, 1, pos, f); fclose(f); rename(IQ_JSON_TMP, IQ_JSON); }
|
if (f) { fwrite(json, 1, pos, f); fclose(f); rename(IQ_JSON_TMP, IQ_JSON); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* a full window is 26.21 ms = 1.08 ATSC fields; print only a short burst every 30s */
|
/* a full window is 26.21 ms = 1.08 ATSC fields; print only a short burst every 30s */
|
||||||
|
#if IQ_ANALYSIS_UART_DEBUG
|
||||||
{
|
{
|
||||||
static time_t log_burst_start = 0;
|
static time_t log_burst_start = 0;
|
||||||
static int log_burst_count = 0;
|
static int log_burst_count = 0;
|
||||||
@@ -386,7 +374,8 @@ static void *iq_spec_thr(void *arg)
|
|||||||
log_burst_count++;
|
log_burst_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (npkt == IQ_NPKT && ++nraw >= IQ_RAW_EVERY) {
|
#endif
|
||||||
|
if (iq_spec_get_enable() && npkt == IQ_NPKT && ++nraw >= IQ_RAW_EVERY) {
|
||||||
FILE *r = fopen("/tmp/iq_raw.bin", "wb"); /* whole contiguous window, for PC-side analysis */
|
FILE *r = fopen("/tmp/iq_raw.bin", "wb"); /* whole contiguous window, for PC-side analysis */
|
||||||
if (r) { fwrite(iq_win[buf], 1, IQ_WBYTES, r); fclose(r); }
|
if (r) { fwrite(iq_win[buf], 1, IQ_WBYTES, r); fclose(r); }
|
||||||
nraw = 0;
|
nraw = 0;
|
||||||
@@ -414,7 +403,7 @@ static void *iq_spec_thr(void *arg)
|
|||||||
|
|
||||||
void *iq_rx_thr(void *arg)
|
void *iq_rx_thr(void *arg)
|
||||||
{
|
{
|
||||||
int fd, n, cur = -1, buf = 0, npkt = 0, active = 0;
|
int fd, n, cur = -1, buf = 0, npkt = 0;
|
||||||
struct sockaddr_in a;
|
struct sockaddr_in a;
|
||||||
unsigned char pkt[2048];
|
unsigned char pkt[2048];
|
||||||
pthread_t spec_id;
|
pthread_t spec_id;
|
||||||
@@ -425,7 +414,7 @@ void *iq_rx_thr(void *arg)
|
|||||||
printf("[sched] iq_rx_thr nice 5 failed: %s\n", strerror(errno));
|
printf("[sched] iq_rx_thr nice 5 failed: %s\n", strerror(errno));
|
||||||
#endif
|
#endif
|
||||||
/* PR06 step6: the IQ frames are now injected by the FPGA into eth0 (management port, RMII2)
|
/* PR06 step6: the IQ frames are now injected by the FPGA into eth0 (management port, RMII2)
|
||||||
* and addressed to 192.168.1.143 -- eth0's OWN IP -- so the kernel hands them straight to
|
* and addressed to limited broadcast on eth0, so the kernel hands them straight to
|
||||||
* this UDP socket. This removes the old dependency on the external eth1 cable: eth0 is always
|
* this UDP socket. This removes the old dependency on the external eth1 cable: eth0 is always
|
||||||
* connected while you observe the spectrum via the web, so the internal FPGA->imx6 IQ transfer
|
* connected while you observe the spectrum via the web, so the internal FPGA->imx6 IQ transfer
|
||||||
* no longer needs eth1's PHY link to be up. (busybox has no bare `ip`; absolute path required.) */
|
* no longer needs eth1's PHY link to be up. (busybox has no bare `ip`; absolute path required.) */
|
||||||
@@ -466,25 +455,25 @@ void *iq_rx_thr(void *arg)
|
|||||||
memset(iq_seen[0], 0, IQ_NPKT);
|
memset(iq_seen[0], 0, IQ_NPKT);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int wid, idx;
|
int wid, idx;
|
||||||
|
n = recvfrom(fd, pkt, sizeof(pkt), 0, NULL, NULL);
|
||||||
if (!iq_spec_get_enable()) {
|
if (!iq_spec_get_enable()) {
|
||||||
active = 0;
|
cur = -1;
|
||||||
cur = -1; npkt = 0;
|
npkt = 0;
|
||||||
memset(iq_seen[buf], 0, IQ_NPKT);
|
|
||||||
sleep(1);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!active) {
|
|
||||||
while (recvfrom(fd, pkt, sizeof(pkt), MSG_DONTWAIT, NULL, NULL) > 0) {}
|
|
||||||
active = 1;
|
|
||||||
}
|
|
||||||
n = recvfrom(fd, pkt, sizeof(pkt), 0, NULL, NULL);
|
|
||||||
if (n < 8 + IQ_PAY) continue;
|
if (n < 8 + IQ_PAY) continue;
|
||||||
if (pkt[0] != 0x49 || pkt[1] != 0x51 || pkt[6] != 0x02) continue; /* 'IQ', fmt=2 */
|
if (pkt[0] != 0x49 || pkt[1] != 0x51 || pkt[6] != 0x02) continue; /* 'IQ', fmt=2 */
|
||||||
wid = pkt[2];
|
wid = pkt[2];
|
||||||
idx = ((pkt[4] & 0x03) << 8) | pkt[5];
|
idx = ((pkt[4] & 0x03) << 8) | pkt[5];
|
||||||
if (idx >= IQ_NPKT) continue;
|
if (idx >= IQ_NPKT) continue;
|
||||||
|
|
||||||
if (cur < 0) cur = wid;
|
/* After FSSI Refresh is turned off, wait for packet zero so the first
|
||||||
|
* analysed window cannot contain a stale or partial A/B interval. */
|
||||||
|
if (cur < 0) {
|
||||||
|
if (idx != 0) continue;
|
||||||
|
memset(iq_seen[buf], 0, IQ_NPKT);
|
||||||
|
cur = wid;
|
||||||
|
}
|
||||||
if (wid != cur) { /* window boundary: hand this one off */
|
if (wid != cur) { /* window boundary: hand this one off */
|
||||||
int other = buf ^ 1;
|
int other = buf ^ 1;
|
||||||
pthread_mutex_lock(&iq_mtx);
|
pthread_mutex_lock(&iq_mtx);
|
||||||
|
|||||||
Executable → Regular
+16
-8
@@ -41,6 +41,7 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
#include "iq_rx.h"
|
#include "iq_rx.h"
|
||||||
|
#include "../inc/config.h"
|
||||||
|
|
||||||
#ifndef M_PI
|
#ifndef M_PI
|
||||||
#define M_PI 3.14159265358979323846
|
#define M_PI 3.14159265358979323846
|
||||||
@@ -139,7 +140,6 @@ static int iq_vsb_log_allowed(void)
|
|||||||
int iq_get_snr(float *snr_db)
|
int iq_get_snr(float *snr_db)
|
||||||
{
|
{
|
||||||
int fresh;
|
int fresh;
|
||||||
if (!iq_spec_get_enable()) return 0;
|
|
||||||
pthread_mutex_lock(&snr_mtx);
|
pthread_mutex_lock(&snr_mtx);
|
||||||
fresh = snr_stamp && (time(NULL) - snr_stamp) <= IQ_SNR_MAX_AGE;
|
fresh = snr_stamp && (time(NULL) - snr_stamp) <= IQ_SNR_MAX_AGE;
|
||||||
if (fresh) *snr_db = snr_db_pub;
|
if (fresh) *snr_db = snr_db_pub;
|
||||||
@@ -1022,17 +1022,23 @@ static float line_cancel(float *r, float *q, int n, int k0)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* T-spaced 11-tap complex LS equalizer, decision-directed, retrained per
|
* T-spaced 41-tap complex LS equalizer, decision-directed, retrained per
|
||||||
* window (22 real unknowns; normal equations + Gaussian elimination). After
|
* window (82 real unknowns; normal equations + Gaussian elimination). After
|
||||||
* the line canceller the residual floor (~-39 dB) is even-lag LINEAR ISI
|
* 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
|
* (observation-path ripple); this recovers it to ~43 dB (PC, half/half
|
||||||
* held-out). Real ATSC receivers/analyzers measure MER post-equalizer.
|
* held-out). Real ATSC receivers/analyzers measure MER post-equalizer.
|
||||||
* Trains on every 2nd symbol; overfit bias with 22 dof over 70k samples is
|
* PR16 offline held-out sweep on four eth0 IQ windows:
|
||||||
* < 0.001 dB. Output written to out[] (edges copied through), returns SNR.
|
* EQ11=42.30..43.36 dB, EQ21=42.45..43.53 dB, EQ41=42.84..44.02 dB.
|
||||||
|
* PR16 stride sweep showed that using every 32nd symbol costs only
|
||||||
|
* 0.09..0.17 dB on four held-out windows while reducing the Cortex-A7 normal
|
||||||
|
* matrix work by 16x. The resulting ~4374 training rows for 82 unknowns are
|
||||||
|
* still safely overdetermined. Output written to out[] (edges copied through),
|
||||||
|
* returns SNR.
|
||||||
*/
|
*/
|
||||||
#define EQ_M 5
|
#define EQ_M 20
|
||||||
#define EQ_NT (2 * EQ_M + 1)
|
#define EQ_NT (2 * EQ_M + 1)
|
||||||
#define EQ_NU (2 * EQ_NT)
|
#define EQ_NU (2 * EQ_NT)
|
||||||
|
#define EQ_TRAIN_STRIDE 32
|
||||||
static float eq_snr(const float *cr, const float *cq, int n, float *out)
|
static float eq_snr(const float *cr, const float *cq, int n, float *out)
|
||||||
{
|
{
|
||||||
static double xtx[EQ_NU][EQ_NU], xty[EQ_NU];
|
static double xtx[EQ_NU][EQ_NU], xty[EQ_NU];
|
||||||
@@ -1042,7 +1048,7 @@ static float eq_snr(const float *cr, const float *cq, int n, float *out)
|
|||||||
if (n < 100 * EQ_NT) return -99.0f;
|
if (n < 100 * EQ_NT) return -99.0f;
|
||||||
memset(xtx, 0, sizeof(xtx));
|
memset(xtx, 0, sizeof(xtx));
|
||||||
memset(xty, 0, sizeof(xty));
|
memset(xty, 0, sizeof(xty));
|
||||||
for (k = EQ_M; k < n - EQ_M; k += 2) { /* stride-2 training */
|
for (k = EQ_M; k < n - EQ_M; k += EQ_TRAIN_STRIDE) {
|
||||||
double v[EQ_NU], d = slice1(cr[k]);
|
double v[EQ_NU], d = slice1(cr[k]);
|
||||||
for (j = 0; j < EQ_NT; j++) {
|
for (j = 0; j < EQ_NT; j++) {
|
||||||
v[2 * j] = cr[k - (j - EQ_M)];
|
v[2 * j] = cr[k - (j - EQ_M)];
|
||||||
@@ -1144,7 +1150,7 @@ static void pr12_run(struct pr12_result *R, float tau_old)
|
|||||||
}
|
}
|
||||||
gain_track(ci, nsym);
|
gain_track(ci, nsym);
|
||||||
R->line_db = line_cancel(ci, cq, nsym, k_lo); /* narrowband +Rs/4 canceller */
|
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 */
|
{ float s_eq = eq_snr(ci, cq, nsym, lc_er); /* 41-tap DD equalizer -> lc_er */
|
||||||
const float *eqstat;
|
const float *eqstat;
|
||||||
if (s_eq > -90.0f) {
|
if (s_eq > -90.0f) {
|
||||||
snr = s_eq;
|
snr = s_eq;
|
||||||
@@ -1395,6 +1401,7 @@ void iq_vsb_analyse(const unsigned char *win, int wid)
|
|||||||
|
|
||||||
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_ANALYSIS_UART_DEBUG
|
||||||
printf("[iq_vsb] win %d: SNR %.2f dB (raw %.2f, ORx AGC jitter %.2f%%) tau %.4f theta %+.1f deg coff %+.3f Hz "
|
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 "
|
"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",
|
"[ms: unpack %d tau %d demod %d stats %d eye %d total %d]\n",
|
||||||
@@ -1412,4 +1419,5 @@ void iq_vsb_analyse(const unsigned char *win, int wid)
|
|||||||
else
|
else
|
||||||
printf("[pr12] win %d: no field sync (ratio %.1f < %.1f), LEGACY fallback\n",
|
printf("[pr12] win %d: no field sync (ratio %.1f < %.1f), LEGACY fallback\n",
|
||||||
wid, pr.ratio, PR12_RATIO_THR);
|
wid, pr.ratio, PR12_RATIO_THR);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
Executable → Regular
+21
-5
@@ -770,7 +770,7 @@ int get_sys_cfg(char *pbuf,short bufMaxLen)
|
|||||||
|
|
||||||
//printf("adpc=%d\n", atsc_para.system_para.adpc);
|
//printf("adpc=%d\n", atsc_para.system_para.adpc);
|
||||||
|
|
||||||
sprintf(pbuf, "#%u#%u#%u#%u#%u#%s#%u#%u#%u#%u#%u#0x%04X#%u#%u#%u#%u",
|
sprintf(pbuf, "#%u#%u#%u#%u#%u#%s#%u#%u#%u#%u#%u#0x%04X#%u#%u#%u#%u#%u",
|
||||||
input, // 通道选择
|
input, // 通道选择
|
||||||
atsc_para.system_para.gps, // 时钟源
|
atsc_para.system_para.gps, // 时钟源
|
||||||
atsc_para.system_para.adpc>=ADPC_UPDATE3?ADPC_UPDATE3:atsc_para.system_para.adpc, //预校正
|
atsc_para.system_para.adpc>=ADPC_UPDATE3?ADPC_UPDATE3:atsc_para.system_para.adpc, //预校正
|
||||||
@@ -786,8 +786,14 @@ int get_sys_cfg(char *pbuf,short bufMaxLen)
|
|||||||
atsc_para.sn_para.eas_key_valid,
|
atsc_para.sn_para.eas_key_valid,
|
||||||
atsc_para.system_para.agc_att,
|
atsc_para.system_para.agc_att,
|
||||||
atsc_para.system_para.adrv9009_att,
|
atsc_para.system_para.adrv9009_att,
|
||||||
atsc_para.system_para.orx_manu_gain
|
atsc_para.system_para.orx_manu_gain,
|
||||||
|
atsc_para.dpd_para.dpd_ctl
|
||||||
);
|
);
|
||||||
|
{
|
||||||
|
size_t used = strlen(pbuf);
|
||||||
|
if(used < (size_t)bufMaxLen)
|
||||||
|
snprintf(pbuf + used, (size_t)bufMaxLen - used, "#%u", atsc_para.system_para.hardware_version);
|
||||||
|
}
|
||||||
|
|
||||||
#if CGI_PRINT
|
#if CGI_PRINT
|
||||||
//printf("[%s,%d] %s\n", __FUNCTION__,__LINE__,pbuf);
|
//printf("[%s,%d] %s\n", __FUNCTION__,__LINE__,pbuf);
|
||||||
@@ -888,6 +894,12 @@ int set_sys_cfg(char *pbuf, short bufMaxLen)
|
|||||||
p_cgi_sys_para->adrv9009_att = (uint16)atoi(paraBuf[13]);
|
p_cgi_sys_para->adrv9009_att = (uint16)atoi(paraBuf[13]);
|
||||||
#endif
|
#endif
|
||||||
p_cgi_sys_para->orx_manu_gain = (uint16)atoi(paraBuf[14]);
|
p_cgi_sys_para->orx_manu_gain = (uint16)atoi(paraBuf[14]);
|
||||||
|
if(paraNum > 15)
|
||||||
|
{
|
||||||
|
uint8 hardware_version = (uint8)atoi(paraBuf[15]);
|
||||||
|
if(hardware_version < HARDWARE_VERSION_END)
|
||||||
|
p_cgi_sys_para->hardware_version = hardware_version;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
@@ -1303,8 +1315,9 @@ int set_spec_cfg(char *pbuf,short bufMaxLen)
|
|||||||
if (!pbuf || bufMaxLen == 0) return -1;
|
if (!pbuf || bufMaxLen == 0) return -1;
|
||||||
atsc_para.system_para.spec_enable = 1;
|
atsc_para.system_para.spec_enable = 1;
|
||||||
c256_bytewrite(EEPROM_SPEC_ENABLE, atsc_para.system_para.spec_enable);
|
c256_bytewrite(EEPROM_SPEC_ENABLE, atsc_para.system_para.spec_enable);
|
||||||
iq_spec_set_enable(1);
|
iq_spec_set_enable(atsc_para.system_para.ab_mode == 0);
|
||||||
log_printf(LOG_INFO, I_USER_CFG, "spectrum enable fixed on\n");
|
log_printf(LOG_INFO, I_USER_CFG, "spectrum page enabled, IQ collection %s\n",
|
||||||
|
atsc_para.system_para.ab_mode == 0 ? "running" : "paused by FSSI Refresh");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1576,8 +1589,8 @@ int get_tsoip_systs1(char *pbuf, short bufMaxLen)
|
|||||||
((pnet->udp_rtp&0xff)<<16)|((pnet->packet_len&0xff)<<8)|(pnet->receive_type&0xff),
|
((pnet->udp_rtp&0xff)<<16)|((pnet->packet_len&0xff)<<8)|(pnet->receive_type&0xff),
|
||||||
pnet->fec,
|
pnet->fec,
|
||||||
pnet->delay,
|
pnet->delay,
|
||||||
pnet->col,
|
|
||||||
pnet->row,
|
pnet->row,
|
||||||
|
pnet->col,
|
||||||
pnet->dest_ip[0],
|
pnet->dest_ip[0],
|
||||||
(pnet->dest_ip[1]<<16)|(pnet->dest_ip[2]<<8)|pnet->dest_ip[3],
|
(pnet->dest_ip[1]<<16)|(pnet->dest_ip[2]<<8)|pnet->dest_ip[3],
|
||||||
pnet->dest_port,
|
pnet->dest_port,
|
||||||
@@ -1777,6 +1790,9 @@ int start_fpga_upgrade(char *value,short maxlen)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
log_printf(LOG_INFO, I_USER_CFG, "get FPGA .mcs upgrade cmd from web.\n");
|
log_printf(LOG_INFO, I_USER_CFG, "get FPGA .mcs upgrade cmd from web.\n");
|
||||||
|
/* Clear the previous request's terminal status before the asynchronous
|
||||||
|
* imx_up_thr starts. Otherwise the web poll can consume a stale DONE. */
|
||||||
|
fpga_up_write_status("PENDING", 0, 0);
|
||||||
set_fpga_up_flg(1);
|
set_fpga_up_flg(1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Executable → Regular
+53
-33
@@ -829,6 +829,27 @@ static uint8 orx_gain_index_now(void)
|
|||||||
return (uint8)(255 - ((orx_median5(cw) >> 8) & 0xff));
|
return (uint8)(255 - ((orx_median5(cw) >> 8) & 0xff));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* PR16: 取一次 FSSI% —— 增益字(粗) + ob_fb_rms_pwr(细), 两者各 5 点中位数。
|
||||||
|
*
|
||||||
|
* 只看 gain index 时 FAGC 的 2 dB 死区会把 ±1 dB 以内的输入变化整个吃掉
|
||||||
|
* (dpd_rx_rfagc_1x.vhd dz=232), A/B 差异小于 1 dB 时 b_fssi 会恒等于 a_fssi。
|
||||||
|
* 死区内增益冻结, 功率变化完整落在 rms 上, 所以把 rms 残差加回来。详见 orx_power.h。
|
||||||
|
*
|
||||||
|
* 读法照抄 eeprom.c 里已验证的那段: 两对寄存器交替读、各取中位数 —— 0x03/0x04
|
||||||
|
* 共用 temp_reg 高字节锁存器, 交替读才能拿到配对的一组。 */
|
||||||
|
static uint8 orx_fssi_now(void)
|
||||||
|
{
|
||||||
|
uint16 cw[5], rms[5];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 5; i++) {
|
||||||
|
rms[i] = fpga_word_read_lh(0x01, 0x03, 0x04);
|
||||||
|
cw[i] = fpga_word_read_hl(0x01, 0x36, 0x37);
|
||||||
|
}
|
||||||
|
return orx_fssi_from_power(orx_median5(cw), orx_median5(rms),
|
||||||
|
FSSI_RMS_REF, FSSI_GI_MIN, FSSI_GI_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
static void calc_fwd(void)
|
static void calc_fwd(void)
|
||||||
{
|
{
|
||||||
#if 0
|
#if 0
|
||||||
@@ -925,11 +946,14 @@ void get_tsnr_limd_uimd(uint16 *ptsnr, uint8 *plmid, uint8 *pumid)
|
|||||||
uint16 u16_tsnr;
|
uint16 u16_tsnr;
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
if(atsc_para.system_para.adpc_err==DPD_SAVE_DATA || atsc_para.system_para.adpc_err==DPD_LOAD_DATA)
|
|
||||||
return;
|
|
||||||
|
|
||||||
imd_l = last_l; imd_r = last_r; u16_tsnr = last_snr;
|
imd_l = last_l; imd_r = last_r; u16_tsnr = last_snr;
|
||||||
|
|
||||||
|
/* Always initialise outputs. IQ results are protected independently and
|
||||||
|
* remain valid while DPD save/load suppresses the normal log refresh. */
|
||||||
|
*plmid = (uint8)imd_l;
|
||||||
|
*pumid = (uint8)imd_r;
|
||||||
|
*ptsnr = u16_tsnr;
|
||||||
|
|
||||||
// The FPGA's imd_imd_fft_meas registers (0x05:0x17/0x18/0x19) used to be read here.
|
// The FPGA's imd_imd_fft_meas registers (0x05:0x17/0x18/0x19) used to be read here.
|
||||||
// They are gone: its 20-bit |DFT|^2 output quantises both shoulder bins to 0 once
|
// They are gone: its 20-bit |DFT|^2 output quantises both shoulder bins to 0 once
|
||||||
// the shoulder is better than ~45 dB, and the old `if(imd_l==0) imd_l=1` clamp then
|
// the shoulder is better than ~45 dB, and the old `if(imd_l==0) imd_l=1` clamp then
|
||||||
@@ -942,9 +966,9 @@ void get_tsnr_limd_uimd(uint16 *ptsnr, uint8 *plmid, uint8 *pumid)
|
|||||||
// alternative either: with DPD bypassed, snr_txpwr/snr_errpwr reads 1.84 dB.
|
// alternative either: with DPD bypassed, snr_txpwr/snr_errpwr reads 1.84 dB.
|
||||||
//
|
//
|
||||||
// UIMD/LIMD and SNR now all come from the imx6's own 10 MSPS ORx window:
|
// UIMD/LIMD and SNR now all come from the imx6's own 10 MSPS ORx window:
|
||||||
// - LIMD/UIMD: iq_rx.c, same taps as the FPGA but band-averaged over 11 bins
|
// - LIMD/UIMD: iq_rx.c, fixed 500 kHz centre/left/right windows,
|
||||||
// and Welch-averaged. LIMD is the shoulder on the pilot's side. Half-dB units,
|
// Welch-averaged. LIMD is P0-PL and UIMD is P0-PR. Half-dB units,
|
||||||
// no +11 dB display offset (the web adds that).
|
// displayed without the historical +11 dB offset.
|
||||||
// - SNR: iq_vsb.c, the real 8VSB MER (I-path) of a demodulated window.
|
// - SNR: iq_vsb.c, the real 8VSB MER (I-path) of a demodulated window.
|
||||||
// If a source has not produced a fresh value we keep the previous one rather than
|
// If a source has not produced a fresh value we keep the previous one rather than
|
||||||
// report a fabricated number.
|
// report a fabricated number.
|
||||||
@@ -4215,18 +4239,17 @@ static void refresh_ad4(void *parg,void *parv)
|
|||||||
|
|
||||||
//需添加A:FSSI和B:FSSI ; FSSI 单源: update_fssi_state() 是唯一计算点,
|
//需添加A:FSSI和B:FSSI ; FSSI 单源: update_fssi_state() 是唯一计算点,
|
||||||
//本函数只读 st_extend_state.a_fssi/b_fssi 显示, 不再自己取数/换算。
|
//本函数只读 st_extend_state.a_fssi/b_fssi 显示, 不再自己取数/换算。
|
||||||
//ab_flag=TRUE -> 当前选中 B 路(set_ab_switch(1)); FALSE -> A 路(set_ab_switch(0))。
|
//ab_flag=TRUE -> 逻辑/丝印 B;FALSE -> 逻辑/丝印 A,硬件位由统一映射转换。
|
||||||
if(atsc_para.system_para.ab_mode==1)
|
if(atsc_para.system_para.ab_mode==1)
|
||||||
{
|
{
|
||||||
if(check_delay_over(&tickbak,50)==TRUE)
|
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)
|
if(++ppara->ab_sec>16)
|
||||||
{
|
{
|
||||||
ppara->ab_flag = ppara->ab_flag?FALSE:TRUE;
|
ppara->ab_flag = ppara->ab_flag?FALSE:TRUE;
|
||||||
ppara->ab_sec = 0;
|
ppara->ab_sec = 0;
|
||||||
}
|
}
|
||||||
set_ab_switch(ppara->ab_flag);
|
}
|
||||||
}
|
|
||||||
if(ppara->ab_flag)
|
if(ppara->ab_flag)
|
||||||
{
|
{
|
||||||
ppara->fssi = st_extend_state.b_fssi;
|
ppara->fssi = st_extend_state.b_fssi;
|
||||||
@@ -6513,7 +6536,7 @@ static void refresh_we1(void *parg,void *parv)
|
|||||||
sprintf(out_str,"%3d",txfreq_to_hz(ppara->ch));
|
sprintf(out_str,"%3d",txfreq_to_hz(ppara->ch));
|
||||||
lcd_write_string(LINE_2,prompt_pos.pos[0],out_str);
|
lcd_write_string(LINE_2,prompt_pos.pos[0],out_str);
|
||||||
|
|
||||||
if(ppara->power>=WE_POWER_ZERO) //-15dB~0dB~+10dB 0~300~500
|
if(ppara->power>=WE_POWER_ZERO) //V1.0: -15~+10dB; V1.1: -15~+21dB
|
||||||
{
|
{
|
||||||
positive = TRUE;
|
positive = TRUE;
|
||||||
temp = ppara->power-WE_POWER_ZERO;
|
temp = ppara->power-WE_POWER_ZERO;
|
||||||
@@ -10578,10 +10601,9 @@ static BOOL proc_key_wc1(void *parg,uint8 key)
|
|||||||
add_4_hex(&ppara->gl, p->item_index);
|
add_4_hex(&ppara->gl, p->item_index);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
ppara->dpd_ctl+=0x0010;
|
|
||||||
break;
|
|
||||||
case 5:
|
case 5:
|
||||||
ppara->dpd_ctl+=0x0001;
|
// PR16: production supports only disabled (0x00) or enabled (0x07).
|
||||||
|
ppara->dpd_ctl = (ppara->dpd_ctl == 0x07) ? 0x00 : 0x07;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -10601,10 +10623,8 @@ static BOOL proc_key_wc1(void *parg,uint8 key)
|
|||||||
dec_4_hex(&ppara->gl, p->item_index);
|
dec_4_hex(&ppara->gl, p->item_index);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
ppara->dpd_ctl-=0x0010;
|
|
||||||
break;
|
|
||||||
case 5:
|
case 5:
|
||||||
ppara->dpd_ctl-=0x0001;
|
ppara->dpd_ctl = (ppara->dpd_ctl == 0x07) ? 0x00 : 0x07;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -11062,7 +11082,7 @@ static BOOL proc_key_we1(void *parg,uint8 key)
|
|||||||
case 1:
|
case 1:
|
||||||
if(atsc_para.rf_para.agc==0)
|
if(atsc_para.rf_para.agc==0)
|
||||||
{
|
{
|
||||||
if(ppara->power<WE_POWER_MAX)
|
if(ppara->power<get_we_power_max())
|
||||||
{
|
{
|
||||||
ppara->power++;
|
ppara->power++;
|
||||||
}
|
}
|
||||||
@@ -14533,13 +14553,10 @@ static void sec_proc_func(void)
|
|||||||
|
|
||||||
static void update_fssi_state(void)
|
static void update_fssi_state(void)
|
||||||
{
|
{
|
||||||
// 0x23 IQB_UPD: 1=B, 0=A (E_SELECT_A=0/E_SELECT_B=1)
|
// 0x23 IQB_UPD: 逻辑A/B由 DPD_RF_SEL_VALUE() 映射到反向硬件位
|
||||||
uint8 a_fssi;
|
|
||||||
uint8 b_fssi;
|
|
||||||
uint8 data;
|
|
||||||
|
|
||||||
static uint16 sec_cnt=0;
|
static uint16 sec_cnt=0;
|
||||||
static uint8 ab_flg =0;
|
static uint8 ab_flg =0;
|
||||||
|
uint8 just_switched = 0; /* PR16: 本秒刚拨过开关 -> 只拨不读, 留 1s 给 AGC 建立 */
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -14574,39 +14591,42 @@ static void update_fssi_state(void)
|
|||||||
ab_flg = 0;
|
ab_flg = 0;
|
||||||
else
|
else
|
||||||
ab_flg = 1;
|
ab_flg = 1;
|
||||||
|
just_switched = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(atsc_para.system_para.ab_mode==1){
|
if(atsc_para.system_para.ab_mode==1){
|
||||||
if(ab_flg){
|
if(ab_flg){
|
||||||
//B
|
//B
|
||||||
set_ab_switch(E_SELECT_B);
|
set_ab_switch(E_SELECT_B);
|
||||||
{ /* 显示滞回: FAGC 一步 0.5dB = FSSI 2.5%, 无滞回会来回闪 */
|
/* PR16: 拨完开关这一秒不读。AGC 一个 gain step = 256 个积分周期
|
||||||
|
* x 51.2us ~= 13ms, 1s 足够建立; 不跳过的话这一次读到的还是 A 的增益字
|
||||||
|
* (原代码 A 分支那句被注释掉的 delay_ms(9) 就是想解决这个)。
|
||||||
|
* 每路驻留 10s, 只丢首采, 剩 9 次照常刷新。 */
|
||||||
|
if(!just_switched){
|
||||||
|
/* 显示滞回: 相邻档位间来回闪时保持前值 */
|
||||||
static uint8 b_prev = 0;
|
static uint8 b_prev = 0;
|
||||||
b_prev = orx_fssi_hysteresis(b_prev,
|
b_prev = orx_fssi_hysteresis(b_prev, orx_fssi_now());
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
//delay_ms(9);
|
|
||||||
//A
|
//A
|
||||||
set_ab_switch(E_SELECT_A);
|
set_ab_switch(E_SELECT_A);
|
||||||
{
|
if(!just_switched){
|
||||||
static uint8 a_prev = 0;
|
static uint8 a_prev = 0;
|
||||||
a_prev = orx_fssi_hysteresis(a_prev,
|
a_prev = orx_fssi_hysteresis(a_prev, orx_fssi_now());
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//printf("[%s,%d] A_FSSI=%d B_FSSI=%d\n",__FUNCTION__,__LINE__,st_extend_state.a_fssi,st_extend_state.b_fssi);
|
//printf("[%s,%d] A_FSSI=%d B_FSSI=%d\n",__FUNCTION__,__LINE__,st_extend_state.a_fssi,st_extend_state.b_fssi);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
/* ab_mode!=1: 开关常驻 A, 不轮换, 也就没有建立时间问题 */
|
||||||
set_ab_switch(0);
|
set_ab_switch(0);
|
||||||
//printf("AB switch A\n");
|
//printf("AB switch A\n");
|
||||||
{
|
{
|
||||||
static uint8 a_prev2 = 0;
|
static uint8 a_prev2 = 0;
|
||||||
a_prev2 = orx_fssi_hysteresis(a_prev2,
|
a_prev2 = orx_fssi_hysteresis(a_prev2, orx_fssi_now());
|
||||||
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);
|
||||||
@@ -14800,7 +14820,7 @@ int sys_rf_agc_switch_proc(uint8 trigger)
|
|||||||
//1 射频开关切换到A
|
//1 射频开关切换到A
|
||||||
u_dpdctl_r23.u8val = 0;
|
u_dpdctl_r23.u8val = 0;
|
||||||
u_dpdctl_r23.dpd_bt.iqb_upd = 0;
|
u_dpdctl_r23.dpd_bt.iqb_upd = 0;
|
||||||
u_dpdctl_r23.dpd_bt.rf_sel = 0; //A
|
u_dpdctl_r23.dpd_bt.rf_sel = DPD_RF_SEL_VALUE(E_SELECT_A);
|
||||||
u_dpdctl_r23.dpd_bt.sync_mode = 0;
|
u_dpdctl_r23.dpd_bt.sync_mode = 0;
|
||||||
fpga_byte_write(0x05, FPGA_ADDR_DPD_IQB_UPD, u_dpdctl_r23.u8val);
|
fpga_byte_write(0x05, FPGA_ADDR_DPD_IQB_UPD, u_dpdctl_r23.u8val);
|
||||||
|
|
||||||
|
|||||||
@@ -863,7 +863,7 @@ static uint32 fpga_upgrade_process(void)
|
|||||||
#define FPGA_UP_STATUS_PATH "/tmp/fpga_up_status"
|
#define FPGA_UP_STATUS_PATH "/tmp/fpga_up_status"
|
||||||
|
|
||||||
/* Publish flash progress for the web page: one line "<PHASE> <PCT> <RET>".
|
/* Publish flash progress for the web page: one line "<PHASE> <PCT> <RET>".
|
||||||
* PHASE: PENDING|ERASE|WRITE|DONE|ERROR ; fpga_up_status.cgi serves this file. */
|
* PHASE: PENDING|ERASE|WRITE|RELINK|READY|ERROR ; fpga_up_status.cgi serves this file. */
|
||||||
void fpga_up_write_status(const char *phase, int pct, int ret)
|
void fpga_up_write_status(const char *phase, int pct, int ret)
|
||||||
{
|
{
|
||||||
FILE *sf = fopen(FPGA_UP_STATUS_PATH, "w");
|
FILE *sf = fopen(FPGA_UP_STATUS_PATH, "w");
|
||||||
@@ -990,7 +990,7 @@ int fpga_upgrade_from_file(const char *path)
|
|||||||
delay_ms(200);
|
delay_ms(200);
|
||||||
}
|
}
|
||||||
g_now_only_upgrade_fpga = 0;
|
g_now_only_upgrade_fpga = 0;
|
||||||
if(ret == 0) fpga_up_write_status("DONE", 100, 0);
|
if(ret == 0) fpga_up_write_status("RELINK", 100, 0);
|
||||||
else fpga_up_write_status("ERROR", last_pct < 0 ? 0 : last_pct, ret);
|
else fpga_up_write_status("ERROR", last_pct < 0 ? 0 : last_pct, ret);
|
||||||
log_printf(LOG_DEBUG, I_NOUSED, "fpga upgrade: finished ret=%d lines=%d\n", ret, upState.lineCnt);
|
log_printf(LOG_DEBUG, I_NOUSED, "fpga upgrade: finished ret=%d lines=%d\n", ret, upState.lineCnt);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -2174,8 +2174,8 @@ void check_fpga_up_finished_reboot_sys(void)
|
|||||||
fpga_up_sys_reboot=0;
|
fpga_up_sys_reboot=0;
|
||||||
sec_cnt=0;
|
sec_cnt=0;
|
||||||
/* PR10: the FPGA update image was already IPROG-loaded by reset_fpga_system() in
|
/* PR10: the FPGA update image was already IPROG-loaded by reset_fpga_system() in
|
||||||
* check_fpga_upgrade_done(). The old reset_imx_system() reboot is a no-op on this
|
* check_fpga_upgrade_done(). An IMX reboot is unnecessary for an FPGA-only update.
|
||||||
* board (Task10: no HW soft-reboot path). Instead re-establish RF in-place: reconfig
|
* Re-establish RF in-place: reconfig
|
||||||
* the FPGA + re-handshake the JESD link (skip 9009, which stayed powered) + reload the
|
* the FPGA + re-handshake the JESD link (skip 9009, which stayed powered) + reload the
|
||||||
* DPD HOLD curve. This makes a web FPGA .mcs upgrade self-recover WITHOUT a power cycle. */
|
* DPD HOLD curve. This makes a web FPGA .mcs upgrade self-recover WITHOUT a power cycle. */
|
||||||
extern void hot_reinit_rf(int reload_fpga);
|
extern void hot_reinit_rf(int reload_fpga);
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ extern int g_trigger_up_start_flg;
|
|||||||
static volatile int g_hot_relink_busy = 0; /* re-entrancy guard: only one relink at a time
|
static volatile int g_hot_relink_busy = 0; /* re-entrancy guard: only one relink at a time
|
||||||
* (the upgrade-completion path and the runtime
|
* (the upgrade-completion path and the runtime
|
||||||
* cfg_valid poll can both request one). */
|
* cfg_valid poll can both request one). */
|
||||||
|
static volatile int g_fpga_upgrade_relink_pending = 0; /* web upgrade waits for RF relink */
|
||||||
void hot_reinit_rf(int reload_fpga)
|
void hot_reinit_rf(int reload_fpga)
|
||||||
{
|
{
|
||||||
extern uint8 hot_get_adpc_mode(void);
|
extern uint8 hot_get_adpc_mode(void);
|
||||||
@@ -70,6 +71,8 @@ void hot_reinit_rf(int reload_fpga)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
g_hot_relink_busy = 1;
|
g_hot_relink_busy = 1;
|
||||||
|
if(g_fpga_upgrade_relink_pending)
|
||||||
|
fpga_up_write_status("RELINK", 100, 0);
|
||||||
prev_adpc = hot_get_adpc_mode(); /* capture DPD mode BEFORE force-stop wipes it */
|
prev_adpc = hot_get_adpc_mode(); /* capture DPD mode BEFORE force-stop wipes it */
|
||||||
|
|
||||||
log_printf(LOG_INFO, I_USER_CFG, "[HOT-RESTART] begin (reload_fpga=%d, jesd-relink, skip TALISE, prev_adpc=%d)\n", reload_fpga, prev_adpc);
|
log_printf(LOG_INFO, I_USER_CFG, "[HOT-RESTART] begin (reload_fpga=%d, jesd-relink, skip TALISE, prev_adpc=%d)\n", reload_fpga, prev_adpc);
|
||||||
@@ -105,6 +108,10 @@ void hot_reinit_rf(int reload_fpga)
|
|||||||
hot_dpd_rearm(prev_adpc);
|
hot_dpd_rearm(prev_adpc);
|
||||||
}
|
}
|
||||||
log_printf(LOG_INFO, I_USER_CFG, "[HOT-RESTART] done\n");
|
log_printf(LOG_INFO, I_USER_CFG, "[HOT-RESTART] done\n");
|
||||||
|
if(g_fpga_upgrade_relink_pending){
|
||||||
|
fpga_up_write_status("READY", 100, 0);
|
||||||
|
g_fpga_upgrade_relink_pending = 0;
|
||||||
|
}
|
||||||
g_hot_relink_busy = 0;
|
g_hot_relink_busy = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,10 +132,9 @@ void sys_upgrade(void)
|
|||||||
result = atoi(msg);
|
result = atoi(msg);
|
||||||
//usleep(1000000);
|
//usleep(1000000);
|
||||||
if(result == 0){
|
if(result == 0){
|
||||||
log_printf(LOG_DEBUG,I_NOUSED,"run upgrade sh, and will reboot\n");
|
log_printf(LOG_DEBUG,I_NOUSED,"run upgrade sh (kernel/DTB -> reboot, userspace-only -> app restart)\n");
|
||||||
system(DO_UPGRADE_RUN);
|
system(DO_UPGRADE_RUN);
|
||||||
log_printf(LOG_DEBUG,I_NOUSED,"system rebooting....\n");
|
log_printf(LOG_DEBUG,I_NOUSED,"upgrade script started restart/reboot flow\n");
|
||||||
// reboot in runme.sh
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,8 +171,11 @@ void* imx_up_thr(void * args)
|
|||||||
atsc_adpc_force_stop_flag = TRUE;
|
atsc_adpc_force_stop_flag = TRUE;
|
||||||
usleep(2000000);
|
usleep(2000000);
|
||||||
int fret = fpga_upgrade_from_file(FPGA_UP_MCS_PATH);
|
int fret = fpga_upgrade_from_file(FPGA_UP_MCS_PATH);
|
||||||
log_printf(LOG_DEBUG,I_NOUSED,"FPGA upgrade result=%d (0=ok, app will reset+reboot)\n", fret);
|
if(fret == 0)
|
||||||
/* success path: check_fpga_upgrade_done() set fpga_up_sys_reboot=1 -> imx6 reboots */
|
g_fpga_upgrade_relink_pending = 1;
|
||||||
|
log_printf(LOG_DEBUG,I_NOUSED,"FPGA upgrade result=%d (0=ok, app will hot-relink RF)\n", fret);
|
||||||
|
/* Success path uses fpga_up_sys_reboot as a legacy delayed-work flag;
|
||||||
|
* check_fpga_up_finished_reboot_sys() now performs an in-place RF relink. */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* test hook: `touch /tmp/hot_restart_now` (or `echo 1 > ...` for reload_fpga) triggers
|
/* test hook: `touch /tmp/hot_restart_now` (or `echo 1 > ...` for reload_fpga) triggers
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# 宿主机(x86)单元测试。不依赖交叉工具链、不依赖板子。
|
||||||
|
CC = gcc
|
||||||
|
CFLAGS = -Wall -Wextra -O0 -g -I../inc
|
||||||
|
LDLIBS = -lm
|
||||||
|
|
||||||
|
test_orx_power: test_orx_power.c ../bsp/orx_power.c
|
||||||
|
$(CC) $(CFLAGS) -o $@ test_orx_power.c ../bsp/orx_power.c $(LDLIBS)
|
||||||
|
|
||||||
|
.PHONY: check clean
|
||||||
|
check: test_orx_power
|
||||||
|
./test_orx_power
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f test_orx_power
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
/* 宿主机单元测试: orx_power 纯算法模块。
|
||||||
|
* 注意: inc/type.h 自带 uint8_t/uint16_t typedef, 本文件禁止 include <stdint.h>。 */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "type.h"
|
||||||
|
#include "orx_power.h"
|
||||||
|
|
||||||
|
static int g_fail = 0;
|
||||||
|
|
||||||
|
static void chk_f(const char *name, float got, float want, float tol)
|
||||||
|
{
|
||||||
|
if (fabsf(got - want) > tol) {
|
||||||
|
printf("FAIL %s: got %.4f want %.4f (tol %.4f)\n", name, got, want, tol);
|
||||||
|
g_fail++;
|
||||||
|
} else {
|
||||||
|
printf("ok %s: %.4f\n", name, got);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void chk_u(const char *name, unsigned got, unsigned want)
|
||||||
|
{
|
||||||
|
if (got != want) {
|
||||||
|
printf("FAIL %s: got %u want %u\n", name, got, want);
|
||||||
|
g_fail++;
|
||||||
|
} else {
|
||||||
|
printf("ok %s: %u\n", name, got);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
/* --- 合成: 真机实测工作点 cw=0x0f00, rms=992 --- */
|
||||||
|
/* 0.5*15 + 10*log10(992) = 7.5 + 29.9651 = 37.4651 */
|
||||||
|
chk_f("combine_operating_point",
|
||||||
|
orx_power_combine_db(0x0f00, 992), 37.4651f, 0.001f);
|
||||||
|
|
||||||
|
/* --- 拼接连续性: AGC 跨一步时 p_db 必须连续 ---
|
||||||
|
cw_hi 15->16 使粗档 +0.5dB, 同时增益降 0.5dB 使 rms *10^-0.05 = 992*0.89125 = 884 */
|
||||||
|
chk_f("splice_continuity",
|
||||||
|
orx_power_combine_db(0x1000, 884),
|
||||||
|
orx_power_combine_db(0x0f00, 992), 0.02f);
|
||||||
|
|
||||||
|
/* --- 单调性: 输入升高 -> p_db 升高 --- */
|
||||||
|
if (!(orx_power_combine_db(0x0f00, 1100) > orx_power_combine_db(0x0f00, 992))) {
|
||||||
|
printf("FAIL monotonic_rms\n"); g_fail++;
|
||||||
|
} else printf("ok monotonic_rms\n");
|
||||||
|
if (!(orx_power_combine_db(0x1000, 992) > orx_power_combine_db(0x0f00, 992))) {
|
||||||
|
printf("FAIL monotonic_cw\n"); g_fail++;
|
||||||
|
} else printf("ok monotonic_cw\n");
|
||||||
|
|
||||||
|
/* --- rms=0 不得产生 -inf --- */
|
||||||
|
if (!isfinite(orx_power_combine_db(0x0f00, 0))) {
|
||||||
|
printf("FAIL rms_zero_guard\n"); g_fail++;
|
||||||
|
} else printf("ok rms_zero_guard\n");
|
||||||
|
|
||||||
|
/* --- 中位数: 真机观测到的污染模式(2/5 为野值) --- */
|
||||||
|
{
|
||||||
|
uint16 s[5] = {992, 8417, 990, 991, 8414};
|
||||||
|
chk_u("median5_rejects_outliers", orx_median5(s), 992);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
uint16 s2[5] = {990, 991, 992, 993, 994};
|
||||||
|
chk_u("median5_clean", orx_median5(s2), 992);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- 滞回: 1 个百分点的抖动被吃掉, 2 个以上放行 --- */
|
||||||
|
chk_u("hyst_holds_1", orx_fssi_hysteresis(75, 76), 75);
|
||||||
|
chk_u("hyst_passes_2", orx_fssi_hysteresis(75, 77), 77);
|
||||||
|
chk_u("hyst_down_2", orx_fssi_hysteresis(75, 73), 73);
|
||||||
|
chk_u("hyst_same", orx_fssi_hysteresis(75, 75), 75);
|
||||||
|
|
||||||
|
/* --- PR13: FSSI 由 ORx gain index 线性映射 --- */
|
||||||
|
chk_u("fssi_gi_full_weak", orx_fssi_from_gain_index(255, 195, 255), 0);
|
||||||
|
chk_u("fssi_gi_full_strong", orx_fssi_from_gain_index(195, 195, 255), 100);
|
||||||
|
chk_u("fssi_gi_mid", orx_fssi_from_gain_index(225, 195, 255), 50);
|
||||||
|
chk_u("fssi_gi_quarter", orx_fssi_from_gain_index(240, 195, 255), 25);
|
||||||
|
chk_u("fssi_gi_round", orx_fssi_from_gain_index(224, 195, 255), 52);
|
||||||
|
chk_u("fssi_gi_clamp_below", orx_fssi_from_gain_index(190, 195, 255), 100);
|
||||||
|
chk_u("fssi_gi_narrow_range",orx_fssi_from_gain_index(245, 240, 250), 50);
|
||||||
|
chk_u("fssi_gi_degenerate", orx_fssi_from_gain_index(200, 220, 220), 0);
|
||||||
|
|
||||||
|
/* --- PR13: ORx 数字功率 rms -> 0.5dB 步数 --- */
|
||||||
|
chk_u("halfdb_992", (unsigned)orx_pwr_halfdb(992), 60);
|
||||||
|
chk_u("halfdb_1000",(unsigned)orx_pwr_halfdb(1000), 60);
|
||||||
|
chk_u("halfdb_884", (unsigned)orx_pwr_halfdb(884), 59);
|
||||||
|
chk_u("halfdb_1", (unsigned)orx_pwr_halfdb(1), 0);
|
||||||
|
chk_u("halfdb_0_guard", (unsigned)orx_pwr_halfdb(0), 0);
|
||||||
|
|
||||||
|
/* --- PR13: Δ(0.5dB 步数) -> FWD% --- */
|
||||||
|
chk_u("fwd_hdb_zero", orx_fwd_percent_hdb(0), 100);
|
||||||
|
chk_u("fwd_hdb_clamp_p6", orx_fwd_percent_hdb(6), 200);
|
||||||
|
chk_u("fwd_hdb_clamp_p10", orx_fwd_percent_hdb(10), 200);
|
||||||
|
chk_u("fwd_hdb_minus6", orx_fwd_percent_hdb(-6), 50);
|
||||||
|
chk_u("fwd_hdb_minus20", orx_fwd_percent_hdb(-20), 10);
|
||||||
|
chk_u("fwd_hdb_minus40", orx_fwd_percent_hdb(-40), 0);
|
||||||
|
chk_u("fwd_hdb_minus39", orx_fwd_percent_hdb(-39), 1);
|
||||||
|
|
||||||
|
/* --- PR13: 拼接连续性 ---
|
||||||
|
FAGC 涨一步(0.5dB)与 rms 相应降 10^(-0.05) 时, cw_hi+pwr_hdb 之和不变 */
|
||||||
|
chk_u("splice_continuity_hdb",
|
||||||
|
(unsigned)(1 + orx_pwr_halfdb(884)),
|
||||||
|
(unsigned)(0 + orx_pwr_halfdb(992)));
|
||||||
|
|
||||||
|
/* ================= PR16: 死区内可分辨的 FSSI ================= */
|
||||||
|
#define REF 1024u
|
||||||
|
#define GIMIN 195u
|
||||||
|
#define GIMAX 255u
|
||||||
|
|
||||||
|
/* 1) 后向兼容: rms 恰在设定点时, 必须与纯 gain index 版逐档相等。
|
||||||
|
* cw_hi -> gi = 255-cw_hi。量程 60 档, 每档 100/60 = 1.667%。 */
|
||||||
|
{
|
||||||
|
unsigned cw_hi;
|
||||||
|
int mismatch = 0;
|
||||||
|
for (cw_hi = 0; cw_hi <= 60; cw_hi++) {
|
||||||
|
unsigned a = orx_fssi_from_power((uint16)(cw_hi << 8), REF, REF, GIMIN, GIMAX);
|
||||||
|
unsigned b = orx_fssi_from_gain_index((uint8)(255 - cw_hi), GIMIN, GIMAX);
|
||||||
|
if (a != b) {
|
||||||
|
printf("FAIL compat cw_hi=%u: power=%u gain_index=%u\n", cw_hi, a, b);
|
||||||
|
mismatch++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chk_u("pr16_compat_at_setpoint_mismatches", (unsigned)mismatch, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2) 核心诉求: gain index 冻结(cw 不变)时, rms 变化必须改变读数。
|
||||||
|
* 这正是旧算法做不到的 —— 2dB 死区内 A/B 差异会被整个吃掉。 */
|
||||||
|
{
|
||||||
|
unsigned lo = orx_fssi_from_power(0x1E00, 812, REF, GIMIN, GIMAX); /* -1.0dB */
|
||||||
|
unsigned mid= orx_fssi_from_power(0x1E00, REF, REF, GIMIN, GIMAX); /* 0.0dB */
|
||||||
|
unsigned hi = orx_fssi_from_power(0x1E00, 1290, REF, GIMIN, GIMAX); /* +1.0dB */
|
||||||
|
unsigned same = orx_fssi_from_gain_index((uint8)(255-0x1E), GIMIN, GIMAX);
|
||||||
|
printf(" pr16 deadzone sweep: lo=%u mid=%u hi=%u (old algo would be %u for all)\n",
|
||||||
|
lo, mid, hi, same);
|
||||||
|
if (!(lo < mid && mid < hi)) {
|
||||||
|
printf("FAIL pr16_deadzone_resolves: not strictly increasing\n");
|
||||||
|
g_fail++;
|
||||||
|
} else {
|
||||||
|
printf("ok pr16_deadzone_resolves\n");
|
||||||
|
}
|
||||||
|
/* 死区两端(±1dB)之间至少要拉开 1 个百分点, 否则滞回(阈值2)会把它抹平 */
|
||||||
|
if ((int)hi - (int)lo < 3) {
|
||||||
|
printf("FAIL pr16_deadzone_span: hi-lo=%d, too small vs hysteresis\n",
|
||||||
|
(int)hi - (int)lo);
|
||||||
|
g_fail++;
|
||||||
|
} else {
|
||||||
|
printf("ok pr16_deadzone_span: %d pts across the 2dB dead zone\n",
|
||||||
|
(int)hi - (int)lo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3) 单调性与钳位 */
|
||||||
|
{
|
||||||
|
unsigned c0 = orx_fssi_from_power(0x0000, REF, REF, GIMIN, GIMAX);
|
||||||
|
unsigned c1 = orx_fssi_from_power(0x3C00, REF, REF, GIMIN, GIMAX); /* cw_hi=60 */
|
||||||
|
chk_u("pr16_clamp_low", c0, 0);
|
||||||
|
chk_u("pr16_clamp_high", c1, 100);
|
||||||
|
chk_u("pr16_clamp_over", orx_fssi_from_power(0xFF00, REF, REF, GIMIN, GIMAX), 100);
|
||||||
|
chk_u("pr16_clamp_under_rms", orx_fssi_from_power(0x0000, 1, REF, GIMIN, GIMAX), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4) 退化入参不得崩 / 不得除零 */
|
||||||
|
{
|
||||||
|
chk_u("pr16_guard_gi_equal", orx_fssi_from_power(0x1E00, REF, REF, 200, 200), 0);
|
||||||
|
chk_u("pr16_guard_rms_zero",
|
||||||
|
orx_fssi_from_power(0x1E00, 0, REF, GIMIN, GIMAX) <= 100 ? 1u : 0u, 1u);
|
||||||
|
chk_u("pr16_guard_ref_zero",
|
||||||
|
orx_fssi_from_power(0x1E00, REF, 0, GIMIN, GIMAX) <= 100 ? 1u : 0u, 1u);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(g_fail ? "\n%d FAILED\n" : "\nALL PASSED\n", g_fail);
|
||||||
|
return g_fail ? 1 : 0;
|
||||||
|
}
|
||||||
+142
@@ -0,0 +1,142 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import csv
|
||||||
|
import datetime as dt
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import vxi11
|
||||||
|
|
||||||
|
|
||||||
|
DOC_DIR = Path(r"\\192.168.10.100\goodman\fox_work\emv7x_pltform\atsc_smod1.0\src\atsc_app\说明_ssh访问方式")
|
||||||
|
DOC_PATH = DOC_DIR / "全频段功率归一化差值操作说明.txt"
|
||||||
|
BOARD_HOST = "192.168.1.143"
|
||||||
|
BOARD_PASS = "anywavecom"
|
||||||
|
BOARD_HOSTKEY = "ssh-rsa 2048 SHA256:qjLNx9e5kBgids1ULw3jYDq8U3ka3MfQPMAUJDCMHYY"
|
||||||
|
SA_IP = "192.168.1.147"
|
||||||
|
|
||||||
|
WAIT_STABLE_S = 5
|
||||||
|
LOW_POINTS = [58, 63, 69, 79, 85, 177, 183, 189, 195, 201, 207, 213]
|
||||||
|
U_POINTS = list(range(473, 888, 6))
|
||||||
|
POINTS = LOW_POINTS + U_POINTS
|
||||||
|
|
||||||
|
|
||||||
|
def set_tx_freq(mhz):
|
||||||
|
remote = "echo 'freq {}' > /tmp/atsc_ctrl".format(mhz)
|
||||||
|
cmd = [
|
||||||
|
"plink.exe",
|
||||||
|
"-ssh",
|
||||||
|
"root@{}".format(BOARD_HOST),
|
||||||
|
"-pw",
|
||||||
|
BOARD_PASS,
|
||||||
|
"-batch",
|
||||||
|
"-hostkey",
|
||||||
|
BOARD_HOSTKEY,
|
||||||
|
remote,
|
||||||
|
]
|
||||||
|
proc = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
|
||||||
|
return proc.returncode, (proc.stdout or "").strip(), (proc.stderr or "").strip()
|
||||||
|
|
||||||
|
|
||||||
|
def set_sa_center(inst, mhz):
|
||||||
|
commands = [
|
||||||
|
":SENS:FREQ:CENT {}E6".format(mhz),
|
||||||
|
":SENS:FREQ:CENT {} MHz".format(mhz),
|
||||||
|
":FREQ:CENT {}MHz".format(mhz),
|
||||||
|
]
|
||||||
|
last = None
|
||||||
|
for _attempt in range(3):
|
||||||
|
for cmd in commands:
|
||||||
|
inst.write(cmd)
|
||||||
|
time.sleep(0.6)
|
||||||
|
center = float(inst.ask(":SENS:FREQ:CENT?").strip()) / 1e6
|
||||||
|
err = inst.ask(":SYST:ERR?").strip()
|
||||||
|
last = center, err
|
||||||
|
if abs(center - mhz) <= 0.001:
|
||||||
|
return center, err, True
|
||||||
|
time.sleep(0.8)
|
||||||
|
center, err = last
|
||||||
|
return center, err, False
|
||||||
|
|
||||||
|
|
||||||
|
def read_chpower(inst):
|
||||||
|
raw = inst.ask(":READ:CHPower?").strip()
|
||||||
|
return float(raw.split(",")[0].strip())
|
||||||
|
|
||||||
|
|
||||||
|
def write_outputs(stamp, rows):
|
||||||
|
csv_path = DOC_DIR / "fullband_chpower_once_{}.csv".format(stamp)
|
||||||
|
txt_path = DOC_DIR / "fullband_chpower_once_{}.txt".format(stamp)
|
||||||
|
|
||||||
|
with csv_path.open("w", encoding="utf-8-sig", newline="") as handle:
|
||||||
|
writer = csv.DictWriter(handle, fieldnames=["freq_mhz", "chpower_dbm"])
|
||||||
|
writer.writeheader()
|
||||||
|
writer.writerows(
|
||||||
|
{"freq_mhz": row["freq_mhz"], "chpower_dbm": row["chpower_dbm"]}
|
||||||
|
for row in rows
|
||||||
|
)
|
||||||
|
|
||||||
|
lines = [
|
||||||
|
"# 全频段 Channel Power 单次读取结果 ({})".format(stamp),
|
||||||
|
"",
|
||||||
|
"- 测量条件:发端设频后设置频谱仪中心频率,等待 {}s,读取一次 CHPower。".format(WAIT_STABLE_S),
|
||||||
|
"- 数据列:freq_mhz, chpower_dbm。",
|
||||||
|
"- CSV:{}".format(csv_path.name),
|
||||||
|
"",
|
||||||
|
"| freq_mhz | chpower_dbm |",
|
||||||
|
"|---:|---:|",
|
||||||
|
]
|
||||||
|
for row in rows:
|
||||||
|
lines.append("| {} | {} |".format(row["freq_mhz"], row["chpower_dbm"]))
|
||||||
|
text = "\n".join(lines) + "\n"
|
||||||
|
txt_path.write_text(text, encoding="utf-8")
|
||||||
|
|
||||||
|
with DOC_PATH.open("a", encoding="utf-8") as handle:
|
||||||
|
handle.write("\n\n")
|
||||||
|
handle.write(text)
|
||||||
|
|
||||||
|
return csv_path, txt_path
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
stamp = dt.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
rows = []
|
||||||
|
csv_path = DOC_DIR / "fullband_chpower_once_{}.csv".format(stamp)
|
||||||
|
print("CSV: {}".format(csv_path), flush=True)
|
||||||
|
print("Total points: {}".format(len(POINTS)), flush=True)
|
||||||
|
|
||||||
|
inst = vxi11.Instrument(SA_IP)
|
||||||
|
inst.timeout = 15
|
||||||
|
print("SA IDN: {}".format(inst.ask("*IDN?").strip()), flush=True)
|
||||||
|
inst.write("*CLS")
|
||||||
|
|
||||||
|
for idx, mhz in enumerate(POINTS, 1):
|
||||||
|
rc, out, err = set_tx_freq(mhz)
|
||||||
|
center, sa_err, ok = set_sa_center(inst, mhz)
|
||||||
|
if rc != 0 or not ok:
|
||||||
|
print(
|
||||||
|
"[{:02d}/{}] {} MHz setup warning tx_rc={} center={:.6f} err={} out={} stderr={}".format(
|
||||||
|
idx, len(POINTS), mhz, rc, center, sa_err, out, err
|
||||||
|
),
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
time.sleep(WAIT_STABLE_S)
|
||||||
|
chpower = read_chpower(inst)
|
||||||
|
rows.append({"freq_mhz": mhz, "chpower_dbm": "{:.6f}".format(chpower)})
|
||||||
|
|
||||||
|
with csv_path.open("w", encoding="utf-8-sig", newline="") as handle:
|
||||||
|
writer = csv.DictWriter(handle, fieldnames=["freq_mhz", "chpower_dbm"])
|
||||||
|
writer.writeheader()
|
||||||
|
writer.writerows(rows)
|
||||||
|
print("[{:02d}/{}] {} MHz chpower={:.6f} dBm".format(idx, len(POINTS), mhz, chpower), flush=True)
|
||||||
|
|
||||||
|
csv_path, txt_path = write_outputs(stamp, rows)
|
||||||
|
print("DONE", flush=True)
|
||||||
|
print("CSV={}".format(csv_path), flush=True)
|
||||||
|
print("TXT={}".format(txt_path), flush=True)
|
||||||
|
print("DOC={}".format(DOC_PATH), flush=True)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -266,7 +266,7 @@ s8 ad9528_reg_read(u16 addr, u32 *data, u8 width)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s8 ad9528_reg_write(u16 addr, u32 data, u8 width)
|
s8 ad9528_reg_write(u16 addr, u32 data, u8 width)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -291,10 +291,40 @@ s8 ad9528_reg_write(u16 addr, u32 data, u8 width)
|
|||||||
{
|
{
|
||||||
return RET_FAIL;
|
return RET_FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ad9528_is_ready(void)
|
||||||
s8 ad9528_config(void)
|
{
|
||||||
|
u32 chip_type = 0;
|
||||||
|
u32 part_version = 0;
|
||||||
|
u32 pll_cfg = 0;
|
||||||
|
u32 sysref_ctrl = 0;
|
||||||
|
u32 pll_status = 0;
|
||||||
|
|
||||||
|
/* Read-only probe used during Linux boot. Do not issue IO_UPDATE, reset the
|
||||||
|
* device, or touch SYSREF: a warm reboot must preserve the live RF clocks. */
|
||||||
|
if (ad9528_reg_read(0x0003, &chip_type, 1) != RET_SUCC ||
|
||||||
|
ad9528_reg_read(0x0006, &part_version, 1) != RET_SUCC ||
|
||||||
|
ad9528_reg_read(0x0200, &pll_cfg, 1) != RET_SUCC ||
|
||||||
|
ad9528_reg_read(0x0403, &sysref_ctrl, 1) != RET_SUCC ||
|
||||||
|
ad9528_reg_read(0x0508, &pll_status, 1) != RET_SUCC) {
|
||||||
|
PRINTF_ERROR("AD9528 ready check: SPI read failed\r\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRINTF_INFO("AD9528 ready check: type=0x%02x version=0x%02x "
|
||||||
|
"pll_cfg=0x%02x sysref=0x%02x status=0x%02x\r\n",
|
||||||
|
chip_type, part_version, pll_cfg, sysref_ctrl, pll_status);
|
||||||
|
|
||||||
|
return chip_type == 0x05 &&
|
||||||
|
part_version == 0x03 &&
|
||||||
|
pll_cfg == 0xE6 &&
|
||||||
|
sysref_ctrl == 0x91 &&
|
||||||
|
(pll_status & 0x02) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
s8 ad9528_config(void)
|
||||||
{
|
{
|
||||||
//u8 typeflag;
|
//u8 typeflag;
|
||||||
//u32 ad9528_data = 0x0;
|
//u32 ad9528_data = 0x0;
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ typedef struct {
|
|||||||
}AD9528_REG_STR;
|
}AD9528_REG_STR;
|
||||||
|
|
||||||
|
|
||||||
s8 ad9528_config(void);
|
s8 ad9528_config(void);
|
||||||
|
int ad9528_is_ready(void);
|
||||||
|
|
||||||
#endif
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|||||||
+18
-14
@@ -21,29 +21,33 @@
|
|||||||
#include "ad9528_config.h"
|
#include "ad9528_config.h"
|
||||||
|
|
||||||
|
|
||||||
int main(void)
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
|
||||||
openSpiDev(SPIDEV_AD9528_NODE_NAME);
|
ret = openSpiDev(SPIDEV_AD9528_NODE_NAME);
|
||||||
if(ret < 0 ) {
|
if(ret < 0 ) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = initSpiDev();
|
ret = initSpiDev();
|
||||||
if(ret < 0 ) {
|
if(ret < 0 ) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ad9528_config();
|
if(argc == 2 && strcmp(argv[1], "--check-ready") == 0) {
|
||||||
|
return ad9528_is_ready() ? 0 : 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ad9528_config();
|
||||||
if(ret == 0) {
|
if(ret == 0) {
|
||||||
PRINTF_INFO("ad9528 cfg ok.\r\n");
|
PRINTF_INFO("ad9528 cfg ok.\r\n");
|
||||||
} else {
|
} else {
|
||||||
PRINTF_INFO("ERROR:ad9528 cfg fail.\r\n");
|
PRINTF_INFO("ERROR:ad9528 cfg fail.\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ret == 0 ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,68 +5,107 @@
|
|||||||
|
|
||||||
***************************************/
|
***************************************/
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//程序名字
|
|
||||||
#define PROGRAM_NAME "imx_atsc_app"
|
|
||||||
#define NODE_NAME "http"
|
#define NODE_NAME "http"
|
||||||
|
|
||||||
//查找进程中程序名字
|
|
||||||
#define PROGRAM_NAME_FIND "imx_atsc_app"
|
|
||||||
#define PROGRAM_NODE_NAME_FIND "http"
|
#define PROGRAM_NODE_NAME_FIND "http"
|
||||||
|
|
||||||
//输出目录
|
//输出目录
|
||||||
#define PROGRAM_DIR_OUT_FILE "/tmp/atsc_app_status"
|
|
||||||
#define NODE_PROGRAM_DIR_OUT_FILE "/tmp/web_p_status"
|
#define NODE_PROGRAM_DIR_OUT_FILE "/tmp/web_p_status"
|
||||||
|
|
||||||
//要运行的程序
|
//要运行的程序
|
||||||
//#define RUN_NAME_PROGRAM "/aw/anywave/app/imx_atsc_app 1 &"
|
#define PROGRAM_EXE_PATH "/aw/anywave/app/imx_atsc_app"
|
||||||
#define RUN_NAME_PROGRAM "/aw/anywave/app/imx_atsc_app 1 2>&1 | loglog /var/module_log/ imx_atsc_app.log 1 &"
|
#define RUN_NAME_PROGRAM PROGRAM_EXE_PATH " 2>&1 | /usr/sbin/loglog /var/module_log/ imx_atsc_app.log 1 &"
|
||||||
#define RUN_NODE_NAME_PROGRAM "cd /aw/anywave/web/sh && ./start_web.sh &"
|
#define RUN_NODE_NAME_PROGRAM "cd /aw/anywave/web/sh && ./start_web.sh &"
|
||||||
|
|
||||||
|
/* The application renames its main task with pthread_setname_np(), therefore
|
||||||
|
* BusyBox `ps | grep imx_atsc_app` can report it as missing and the daemon may
|
||||||
|
* start a second instance. /proc/<pid>/exe remains stable across task renames
|
||||||
|
* and also lets us match the exact executable instead of a substring. */
|
||||||
|
static int is_app_running(void)
|
||||||
|
{
|
||||||
|
DIR *proc_dir;
|
||||||
|
struct dirent *entry;
|
||||||
|
char exe_link[64];
|
||||||
|
char exe_path[PATH_MAX];
|
||||||
|
ssize_t exe_len;
|
||||||
|
size_t expected_len = strlen(PROGRAM_EXE_PATH);
|
||||||
|
|
||||||
int main(int argc,char *argv[])
|
proc_dir = opendir("/proc");
|
||||||
|
if(proc_dir == NULL)
|
||||||
|
{
|
||||||
|
/* Fail safe: an unreadable /proc must not cause duplicate app starts. */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while((entry = readdir(proc_dir)) != NULL)
|
||||||
|
{
|
||||||
|
const char *p = entry->d_name;
|
||||||
|
|
||||||
|
if(*p == '\0')
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
while(*p >= '0' && *p <= '9')
|
||||||
|
{
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
if(*p != '\0')
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(exe_link, sizeof(exe_link), "/proc/%s/exe", entry->d_name);
|
||||||
|
exe_len = readlink(exe_link, exe_path, sizeof(exe_path) - 1);
|
||||||
|
if(exe_len < 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
exe_path[exe_len] = '\0';
|
||||||
|
|
||||||
|
if(strcmp(exe_path, PROGRAM_EXE_PATH) == 0 ||
|
||||||
|
(strncmp(exe_path, PROGRAM_EXE_PATH, expected_len) == 0 &&
|
||||||
|
strcmp(exe_path + expected_len, " (deleted)") == 0))
|
||||||
|
{
|
||||||
|
closedir(proc_dir);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(proc_dir);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(void)
|
||||||
{
|
{
|
||||||
int fd = 0;
|
int fd = 0;
|
||||||
int ret = 0;
|
|
||||||
int remux_ok = 0;
|
int remux_ok = 0;
|
||||||
int node_ok = 0;
|
int node_ok = 0;
|
||||||
|
|
||||||
char buf[100];
|
char buf[100];
|
||||||
char cmd[100] = {0};
|
char cmd[256] = {0};
|
||||||
|
|
||||||
//开启守护进程
|
//开启守护进程
|
||||||
daemon(0,0);
|
daemon(0,0);
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
//新建输出文件
|
remux_ok = is_app_running();
|
||||||
memset(cmd, 0 ,sizeof(cmd));
|
|
||||||
sprintf(cmd, "echo 0 > %s", PROGRAM_DIR_OUT_FILE);
|
|
||||||
ret = os_system(cmd);
|
|
||||||
|
|
||||||
//获得程序ID
|
|
||||||
memset(cmd, 0 ,sizeof(cmd));
|
|
||||||
sprintf(cmd, "ps | grep %s | grep -v grep >> %s", PROGRAM_NAME_FIND, PROGRAM_DIR_OUT_FILE);
|
|
||||||
ret = os_system(cmd);
|
|
||||||
|
|
||||||
//打开输出文件
|
|
||||||
fd = open(PROGRAM_DIR_OUT_FILE, O_CREAT|O_RDONLY, 0777);
|
|
||||||
memset(buf, 0, 100);
|
|
||||||
read(fd, buf, 100);
|
|
||||||
close(fd);
|
|
||||||
remux_ok = match(buf, PROGRAM_NAME, 90);
|
|
||||||
|
|
||||||
// web
|
// web
|
||||||
memset(cmd, 0 ,sizeof(cmd));
|
memset(cmd, 0 ,sizeof(cmd));
|
||||||
sprintf(cmd, "echo 0 > %s", NODE_PROGRAM_DIR_OUT_FILE);
|
sprintf(cmd, "echo 0 > %s", NODE_PROGRAM_DIR_OUT_FILE);
|
||||||
ret = os_system(cmd);
|
(void)os_system(cmd);
|
||||||
|
|
||||||
//获得程序ID
|
//获得程序ID
|
||||||
memset(cmd, 0 ,sizeof(cmd));
|
memset(cmd, 0 ,sizeof(cmd));
|
||||||
sprintf(cmd, "ps | grep %s | grep -v grep >> %s", PROGRAM_NODE_NAME_FIND, NODE_PROGRAM_DIR_OUT_FILE);
|
sprintf(cmd, "ps | grep %s | grep -v grep >> %s", PROGRAM_NODE_NAME_FIND, NODE_PROGRAM_DIR_OUT_FILE);
|
||||||
ret = os_system(cmd);
|
(void)os_system(cmd);
|
||||||
|
|
||||||
//打开输出文件
|
//打开输出文件
|
||||||
fd = open(NODE_PROGRAM_DIR_OUT_FILE, O_CREAT|O_RDONLY, 0777);
|
fd = open(NODE_PROGRAM_DIR_OUT_FILE, O_CREAT|O_RDONLY, 0777);
|
||||||
@@ -80,14 +119,17 @@ int main(int argc,char *argv[])
|
|||||||
//判断是否有程序文件运行
|
//判断是否有程序文件运行
|
||||||
if(1 != remux_ok)
|
if(1 != remux_ok)
|
||||||
{
|
{
|
||||||
//运行程序
|
/* Debounce once before starting. This prevents a duplicate launch
|
||||||
|
* when the app is between exec/startup states during an upgrade. */
|
||||||
usleep(1000000);
|
usleep(1000000);
|
||||||
|
if(!is_app_running())
|
||||||
memset(cmd, 0 ,sizeof(cmd));
|
{
|
||||||
sprintf(cmd, "%s", RUN_NAME_PROGRAM);
|
memset(cmd, 0 ,sizeof(cmd));
|
||||||
printf("imx_atsc_app stop , daemon work.\n%s\n\n", cmd);
|
snprintf(cmd, sizeof(cmd), "%s", RUN_NAME_PROGRAM);
|
||||||
system(cmd);
|
printf("imx_atsc_app stop, daemon restart without legacy argument.\n%s\n\n", cmd);
|
||||||
usleep(100000);
|
system(cmd);
|
||||||
|
usleep(100000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(1 != node_ok)
|
if(1 != node_ok)
|
||||||
{
|
{
|
||||||
@@ -102,13 +144,9 @@ int main(int argc,char *argv[])
|
|||||||
|
|
||||||
|
|
||||||
//删除输出文件
|
//删除输出文件
|
||||||
memset(cmd, 0 ,sizeof(cmd));
|
|
||||||
sprintf(cmd, "rm %s", PROGRAM_DIR_OUT_FILE);
|
|
||||||
ret = os_system(cmd);
|
|
||||||
|
|
||||||
memset(cmd, 0 ,sizeof(cmd));
|
memset(cmd, 0 ,sizeof(cmd));
|
||||||
sprintf(cmd, "rm %s", NODE_PROGRAM_DIR_OUT_FILE);
|
sprintf(cmd, "rm %s", NODE_PROGRAM_DIR_OUT_FILE);
|
||||||
ret = os_system(cmd);
|
(void)os_system(cmd);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# 2026-07-29 Web Dashboard / Spectrum 修正记录
|
||||||
|
|
||||||
|
涉及目录:`www/`(主 web 站点)。`www_korea/`、`www_nlogo/` 未包含 Spectrum 页面,本次未修改。
|
||||||
|
|
||||||
|
## 修改内容
|
||||||
|
|
||||||
|
1. Dashboard 的 IMD 卡片已直接在各页面静态 HTML 中调整为 **LIMD 在前、UIMD 在后**;不再依赖运行时 DOM 重排,兼容旧浏览器。
|
||||||
|
2. Dashboard 的 SNR 去除了错误的固定 `+10 dB` 显示偏移;状态阈值同步改为真实 SNR/MER 的 `27 / 20 dB`。
|
||||||
|
3. Spectrum 页面复用 Dashboard 已获取的状态接口字段 `a_fssi` / `b_fssi`,在频谱标签中显示 FSSI;不新增轮询请求。
|
||||||
|
4. 恢复 Spectrum 的 Shoulder、LIMD、UIMD、Pilot、Peak、RBW、Packets、Contiguous、`> 1 field` 信息,以及 Eye Diagram 的 SNR/MER、眼高、眼宽、τ0、段同步、载波偏移、SNR raw、ORx AGC jitter;LIMD/UIMD 以不可拆分的组合卡片同排显示。缺失或非数值字段显示 `--`,不影响其他指标刷新。
|
||||||
|
5. Spectrum 与 Eye Diagram 改为标签页:默认选择 Spectrum;切换标签时仅显示并轮询当前标签对应的图形。
|
||||||
|
6. 修正标题读取:Spectrum 从 `sys_cfg` 返回的标题字段(索引 6)读取,避免显示 `0`;Login 页面改为设置 `document.title`,并保留 `Digital Exciter` 作为空标题回退。
|
||||||
|
7. Spectrum / Eye Diagram 标签页改用完整名称 `ORx Receive Spectrum`、`8VSB Eye Diagram`,并删除内容区重复标题。
|
||||||
|
8. 恢复 Spectrum 工具栏:刷新周期(0.5 / 1 / 2 / 5 秒)、Pause、Peak hold;暂停和刷新周期对当前选中的 Spectrum 或 Eye Diagram 标签页生效。
|
||||||
|
9. 压缩 Spectrum 指标卡片尺寸并禁用换行,使 FSSI、频谱指标及 LIMD/UIMD 组合保持同一行;卡片最小内容宽度调整为 120 px,以在常用页面宽度下接近充满整行;窄窗口通过横向滚动完整查看。
|
||||||
|
10. 移除 Dashboard 与 LCD 中 LIMD/UIMD 的历史固定 `+11 dB` 显示偏移,使其与 Spectrum 原始测量值一致;保留 `sb/sc` 校准量。Dashboard 的 LIMD/UIMD 正常状态阈值由 47 dB 同步调整为 36 dB。
|
||||||
|
11. FPGA `.mcs` 上传和写 Flash 进度轮询期间暂停 Settings 页的前端 20 分钟自动登出;在上传异常、升级完成、升级失败或确认重载后恢复原超时与常规状态刷新。
|
||||||
|
|
||||||
|
## 后端核对
|
||||||
|
|
||||||
|
`atsc_app/task/iweb_task.c` 已在 `State_SysState` 返回中输出 FSSI A/B 字段,`atsc_app/task/http_get.c` 已从 `st_extend_state` 赋值。本次无需修改 `atsc_app` 接口或字段顺序。
|
||||||
|
|
||||||
|
## 验证建议
|
||||||
|
|
||||||
|
1. 登录后确认所有 Dashboard 页面均为 `SNR, LIMD, UIMD` 顺序,且 SNR 与 Status 页一致。
|
||||||
|
2. 打开 Spectrum,确认默认显示频谱和 FSSI;点击 Eye Diagram 后只显示眼图和 SNR/MER。
|
||||||
|
3. 将网页标题配置为空与非空各验证一次,确认 Spectrum/Login 不再显示 `0` 或空标题。
|
||||||
|
12. FPGA consecutive-upgrade status race fixed: publish PENDING 0 0 when the web command is accepted, and start polling only after the upload CGI returns. Completion text now states FPGA reload/RF reconnect instead of IMX reboot.
|
||||||
|
13. FPGA upgrade publishes RELINK after Flash/IPROG and READY only after hot_reinit_rf() completes. The page redirects to login.html with a five-second countdown only on READY; transient CGI errors keep polling.
|
||||||
|
14. Login title fix: atsc_status_cfg.cgi is session-protected and returned unauthorized before login. Added login.cgi?action=get_title as a public read-only title lookup and updated login.html to use it.
|
||||||
|
|
||||||
|
15. Restored the historical fixed +11 dB display offset for LIMD/UIMD in the shared Dashboard header and LCD, and restored the matching 47 dB Dashboard normal threshold. Status already used this same display conversion. Spectrum now labels its unshifted values LIMD RAW / UIMD RAW.
|
||||||
|
16. When FSSI Refresh (A/B switching) is ON, Spectrum and Eye Diagram polling and drawing are suspended and both panels show an explicit unavailable notice. Polling resumes automatically when FSSI Refresh is OFF.
|
||||||
|
17. FSSI Refresh now controls backend IQ collection as well: update_fssi_state() detects only OFF/ON edges, calls iq_spec_set_enable(0) while A/B Refresh is ON, and resumes clean collection with iq_spec_set_enable(1) when it is OFF. The edge handling precedes FSSI's DPD-busy early returns, so web and LCD changes are covered.
|
||||||
|
18. Corrected login.cgi?action=get_title HTTP headers: C source now emits real CRLF separators instead of literal backslash text. Rebuild and deploy login.cgi for the configured login title to reach the browser.
|
||||||
|
|
||||||
|
19. 新增 overview.html 作为登录后的默认落地页;登录 CGI 成功重定向、认证代理默认页及已有会话访问登录页时,统一进入 Overview,并将 Overview 加入受保护页面列表。
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
/* 受保护的 HTML 页面列表 */
|
/* 受保护的 HTML 页面列表 */
|
||||||
static const char *protected_pages[] = {
|
static const char *protected_pages[] = {
|
||||||
|
"overview.html",
|
||||||
"status_info.html",
|
"status_info.html",
|
||||||
"sys_set.html",
|
"sys_set.html",
|
||||||
"sys_rf.html",
|
"sys_rf.html",
|
||||||
@@ -43,7 +44,7 @@ static const char* get_requested_page(const char *query_string) {
|
|||||||
static char page_name[256] = {0};
|
static char page_name[256] = {0};
|
||||||
|
|
||||||
if (!query_string || strlen(query_string) == 0) {
|
if (!query_string || strlen(query_string) == 0) {
|
||||||
return "status_info.html"; /* 默认页面 */
|
return "overview.html"; /* 默认页面 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 解析 query string 中的 page=xxx 参数 */
|
/* 解析 query string 中的 page=xxx 参数 */
|
||||||
@@ -57,7 +58,7 @@ static const char* get_requested_page(const char *query_string) {
|
|||||||
return page_name;
|
return page_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "status_info.html";
|
return "overview.html";
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 发送重定向响应 */
|
/* 发送重定向响应 */
|
||||||
@@ -130,7 +131,7 @@ int main(int argc, char **argv) {
|
|||||||
if (cookie && extract_cookie(cookie, SESSION_COOKIE_NAME,
|
if (cookie && extract_cookie(cookie, SESSION_COOKIE_NAME,
|
||||||
session_id, sizeof(session_id))) {
|
session_id, sizeof(session_id))) {
|
||||||
if (validate_session(session_id, NULL, 0)) {
|
if (validate_session(session_id, NULL, 0)) {
|
||||||
send_redirect("status_info.html");
|
send_redirect("overview.html");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* 否则返回登录页 */
|
/* 否则返回登录页 */
|
||||||
|
|||||||
@@ -51,6 +51,29 @@ int simplified_create_client_socket(int port)
|
|||||||
return sockfd;
|
return sockfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Read an application value for a public login-page endpoint. */
|
||||||
|
static int simplified_get_data_from_process(int modulePort, int moduleKey, char *buf, size_t bufLen)
|
||||||
|
{
|
||||||
|
cgi_process_elem_t elem;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if(!buf || bufLen == 0 || modulePort <= START_PROCESS_MODULE || modulePort >= END_PROCESS_MODULE)
|
||||||
|
return -1;
|
||||||
|
memset(&elem, 0, sizeof(elem));
|
||||||
|
elem.method = GET_METHOD;
|
||||||
|
elem.keyType = moduleKey;
|
||||||
|
fd = simplified_create_client_socket(modulePort);
|
||||||
|
if(fd < 0)
|
||||||
|
return -1;
|
||||||
|
if(write(fd, &elem, sizeof(elem)) != sizeof(elem) || read(fd, &elem, sizeof(elem)) != sizeof(elem)){
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
snprintf(buf, bufLen, "%s", elem.data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Define simplified version of set_data_from_process to avoid linking issues */
|
/* Define simplified version of set_data_from_process to avoid linking issues */
|
||||||
void simplified_set_data_from_process(int modulePort, int moduleKey, char *buf) {
|
void simplified_set_data_from_process(int modulePort, int moduleKey, char *buf) {
|
||||||
if(modulePort <= START_PROCESS_MODULE || modulePort >= END_PROCESS_MODULE)
|
if(modulePort <= START_PROCESS_MODULE || modulePort >= END_PROCESS_MODULE)
|
||||||
@@ -398,7 +421,7 @@ int main(int argc, char **argv) {
|
|||||||
sizeof(cookie_header), 0);
|
sizeof(cookie_header), 0);
|
||||||
printf("Content-type: text/html\r\n");
|
printf("Content-type: text/html\r\n");
|
||||||
printf("%s\r\n", cookie_header);
|
printf("%s\r\n", cookie_header);
|
||||||
printf("Location: status_info.html\r\n\r\n");
|
printf("Location: overview.html\r\n\r\n");
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -412,6 +435,16 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
/* === Handle GET request - get current username === */
|
/* === Handle GET request - get current username === */
|
||||||
if (method && strcasecmp(method, "GET") == 0 && query) {
|
if (method && strcasecmp(method, "GET") == 0 && query) {
|
||||||
|
/* Public read-only title used before a session exists on login.html. */
|
||||||
|
if (strstr(query, "action=get_title")) {
|
||||||
|
char title[MSG_DATA_LEN] = {0};
|
||||||
|
printf("Content-type: text/plain\r\nCache-Control: no-store\r\n\r\n");
|
||||||
|
if(simplified_get_data_from_process(WEB_SOCKET_PORT, CGIKEY_WEB_TITLE,
|
||||||
|
title, sizeof(title)) == 0)
|
||||||
|
printf("%s", title);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
/* Check for get_user action */
|
/* Check for get_user action */
|
||||||
if (strstr(query, "action=get_user")) {
|
if (strstr(query, "action=get_user")) {
|
||||||
char *cookie = getenv("HTTP_COOKIE");
|
char *cookie = getenv("HTTP_COOKIE");
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
#define USER_WEB_INDEX_HTML "status_info.html"
|
#define USER_WEB_INDEX_HTML "overview.html"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+631
-77
@@ -7,7 +7,7 @@
|
|||||||
--accent-green-bright: #4ade80;
|
--accent-green-bright: #4ade80;
|
||||||
--accent-green-dim: #16a34a;
|
--accent-green-dim: #16a34a;
|
||||||
/* Error / alert */
|
/* Error / alert */
|
||||||
--status-err: #ef4444;
|
--status-err: #ff5252;
|
||||||
--status-err-dim: #dc2626;
|
--status-err-dim: #dc2626;
|
||||||
/* Warning / amber */
|
/* Warning / amber */
|
||||||
--status-warn: #f59e0b;
|
--status-warn: #f59e0b;
|
||||||
@@ -23,15 +23,54 @@
|
|||||||
/* Text */
|
/* Text */
|
||||||
--text-primary: #f9fafb;
|
--text-primary: #f9fafb;
|
||||||
--text-secondary: #d1d5db;
|
--text-secondary: #d1d5db;
|
||||||
--text-muted: #9ca3af;
|
--text-muted: #b0b8c4;
|
||||||
|
--plp-pill-bg: #3a4452;
|
||||||
|
--chart-inset: #2a313c;
|
||||||
|
/* Informational text/labels (status notes, progress labels). Light value set in .light-theme. */
|
||||||
|
--info-text: var(--status-neutral);
|
||||||
|
/* Mode identity colours (overview Signal Chain page only). Light values set in .light-theme.
|
||||||
|
Gateway intentionally reuses --accent-green / --accent-green-bright (brand). */
|
||||||
|
--mode-exciter: #60a5fa; --mode-exciter-hover: #93bbfd;
|
||||||
|
--mode-translator: #fb923c; --mode-translator-hover: #fdba74;
|
||||||
|
--mode-standalone: #a78bfa; --mode-standalone-hover: #c4b5fd;
|
||||||
|
--mode-exciter-tint: rgba(96,165,250,0.10);
|
||||||
|
--mode-translator-tint: rgba(251,146,60,0.10);
|
||||||
|
--mode-standalone-tint: rgba(167,139,250,0.10);
|
||||||
|
--mode-gateway-tint: rgba(34,197,94,0.10);
|
||||||
|
/* Stronger (hover) variants of the mode tints. */
|
||||||
|
--mode-exciter-tint-strong: rgba(96,165,250,0.22);
|
||||||
|
--mode-translator-tint-strong: rgba(251,146,60,0.22);
|
||||||
|
--mode-standalone-tint-strong: rgba(167,139,250,0.22);
|
||||||
|
--mode-gateway-tint-strong: rgba(34,197,94,0.22);
|
||||||
/* Borders */
|
/* Borders */
|
||||||
--border-subtle: #4b5563;
|
--border-subtle: #4b5563;
|
||||||
--border-accent: var(--accent-green);
|
--border-accent: var(--accent-green);
|
||||||
/* Sidebar */
|
/* Sidebar (DARK ISLAND — sidebar stays dark navy in BOTH themes, so these
|
||||||
|
tokens are NOT overridden in .light-theme; see the .light-theme block). */
|
||||||
--sidebar-bg: #0f172a;
|
--sidebar-bg: #0f172a;
|
||||||
--sidebar-text: #94a3b8;
|
--sidebar-text: #94a3b8;
|
||||||
--sidebar-text-active: #ffffff;
|
--sidebar-text-active: #ffffff;
|
||||||
--sidebar-hover: #1e293b;
|
--sidebar-hover: #1e293b;
|
||||||
|
/* Active-item accent. Lives as its own token (not --accent-green) because the
|
||||||
|
sidebar is a dark island: this MUST stay bright green in light theme, whereas
|
||||||
|
--accent-green darkens. Same value as --accent-green in dark theme. */
|
||||||
|
--sidebar-accent: #22c55e;
|
||||||
|
/* Primary-action button family (theme-aware). Dark values = today's look,
|
||||||
|
so dark-only products are visually unchanged. Light values softened
|
||||||
|
(entry 011) so buttons sit in harmony on the light page, not as heavy
|
||||||
|
dark blocks. Status dots keep --accent-green and stay vivid. */
|
||||||
|
--btn-green-bg: var(--accent-green-dim);
|
||||||
|
--btn-green-bg-hover: var(--accent-green);
|
||||||
|
--btn-green-text: #06230f;
|
||||||
|
--btn-green-text-hover: #06230f;
|
||||||
|
--btn-neutral-bg: #4b5563;
|
||||||
|
--btn-neutral-text: #ffffff;
|
||||||
|
/* Sidebar footer/labels: PINNED readable colours (sidebar is a dark island,
|
||||||
|
so these must NOT follow the theme tokens, which flip dark in light mode). */
|
||||||
|
--sidebar-footer-h: #cbd5e1;
|
||||||
|
--sidebar-footer-p: #94a3b8;
|
||||||
|
--sidebar-footer-link: #7fb3d5;
|
||||||
|
--sidebar-label: #7c8595;
|
||||||
/* Legacy / UI */
|
/* Legacy / UI */
|
||||||
--primary: var(--accent-green);
|
--primary: var(--accent-green);
|
||||||
--primary-hover: var(--accent-green-dim);
|
--primary-hover: var(--accent-green-dim);
|
||||||
@@ -41,6 +80,31 @@
|
|||||||
--sidebar-width: 220px;
|
--sidebar-width: 220px;
|
||||||
--header-height: 64px;
|
--header-height: 64px;
|
||||||
--dashboard-height: auto;
|
--dashboard-height: auto;
|
||||||
|
|
||||||
|
/* ===== PLOT PALETTE (family, added 2026-08-11) ==============================
|
||||||
|
Colours for the spectrum + eye canvases on the Measurements page. Present as
|
||||||
|
DORMANT family tokens: only smod renders a Measurements page today, but the
|
||||||
|
page is planned for other products, so the tokens live in the shared sheet
|
||||||
|
rather than being hand-copied later (the exact mistake that motivated this
|
||||||
|
whole stylesheet consolidation). Values are the instrument-plot palette; four
|
||||||
|
of them already equal existing family tokens (trace=--status-neutral,
|
||||||
|
hold=--status-warn, band=neutral@10%, eye-level near neutral) and are kept as
|
||||||
|
separate names so a family colour change cannot silently restyle a scope plot.
|
||||||
|
The plot field stays dark in BOTH themes on purpose: the eye is a density map
|
||||||
|
whose floor must read as "no signal", which a dark field gives and a light one
|
||||||
|
does not. Not overridden in .light-theme, deliberately. =================== */
|
||||||
|
--plot-bg: #0b1220;
|
||||||
|
--plot-grid: #1e293b;
|
||||||
|
--plot-grid-zero: #334155;
|
||||||
|
--plot-axis-label: #94a3b8;
|
||||||
|
--plot-axis-title: #64748b;
|
||||||
|
--plot-trace: #38bdf8;
|
||||||
|
--plot-trace-hold: #f59e0b;
|
||||||
|
--plot-trace-error: #eab308;
|
||||||
|
--plot-marker: #f87171;
|
||||||
|
--plot-band: rgba(56, 189, 248, 0.10);
|
||||||
|
--plot-eye-level: rgba(57, 208, 255, 0.45);
|
||||||
|
--plot-eye-decision: #a3e635;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
@@ -165,20 +229,41 @@ body {
|
|||||||
.app-sidebar li.active a {
|
.app-sidebar li.active a {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--sidebar-text-active);
|
color: var(--sidebar-text-active);
|
||||||
border-left: 3px solid var(--accent-green);
|
border-left: 3px solid var(--sidebar-accent);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- Sidebar group labels (Operations / Configuration / Admin) ---
|
||||||
|
Added for ATSC 3.0 pages. Scoped by the .app-sidebar ancestor plus
|
||||||
|
the .ov-group-label class, so these rules only affect <li> elements
|
||||||
|
that opt in with class="ov-group-label". No existing pages in other
|
||||||
|
products (7x, 2D Marble, ATSC 1.0) use this class, so adding these
|
||||||
|
rules has zero visual or behavioral impact on them.
|
||||||
|
The existing .app-sidebar li a and .app-sidebar li.active a rules
|
||||||
|
require an <a> child; these label items have no <a>, so no collision. */
|
||||||
|
.app-sidebar li.ov-group-label {
|
||||||
|
padding: 16px 16px 4px 16px;
|
||||||
|
font-size: 9px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 1.6px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--sidebar-label);
|
||||||
|
border-left: 3px solid transparent;
|
||||||
|
pointer-events: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.app-sidebar li.ov-group-label:first-child { padding-top: 6px; }
|
||||||
|
|
||||||
.sidebar-footer {
|
.sidebar-footer {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
color: var(--text-muted);
|
color: var(--sidebar-footer-p);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
border-top: 1px solid var(--border-subtle);
|
border-top: 1px solid var(--border-subtle);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-footer h4 {
|
.sidebar-footer h4 {
|
||||||
color: var(--text-secondary);
|
color: var(--sidebar-footer-h);
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -192,11 +277,11 @@ body {
|
|||||||
|
|
||||||
.sidebar-footer .redbox-label {
|
.sidebar-footer .redbox-label {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--text-secondary);
|
color: var(--sidebar-footer-h);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-footer a {
|
.sidebar-footer a {
|
||||||
color: var(--status-neutral);
|
color: var(--sidebar-footer-link);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -537,8 +622,8 @@ body {
|
|||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.5px;
|
||||||
background-color: var(--accent-green);
|
background-color: var(--btn-green-bg);
|
||||||
color: #fff;
|
color: var(--btn-green-text-hover);
|
||||||
padding: 4px 10px;
|
padding: 4px 10px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
@@ -547,7 +632,9 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.box-header.collapsed .toggle-text {
|
.box-header.collapsed .toggle-text {
|
||||||
background-color: var(--text-muted);
|
background-color: var(--btn-neutral-bg);
|
||||||
|
color: var(--btn-neutral-text);
|
||||||
|
border: 1px solid var(--border-subtle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Accordion Specific Settings */
|
/* Accordion Specific Settings */
|
||||||
@@ -773,8 +860,8 @@ select.display-only-select:disabled.state-ok {
|
|||||||
/* Buttons */
|
/* Buttons */
|
||||||
input[type="button"].btn-modern,
|
input[type="button"].btn-modern,
|
||||||
input[type="submit"].btn-modern {
|
input[type="submit"].btn-modern {
|
||||||
background-color: var(--accent-green-dim);
|
background-color: var(--btn-green-bg);
|
||||||
color: var(--text-primary);
|
color: var(--btn-green-text);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
padding: 8px 18px;
|
padding: 8px 18px;
|
||||||
@@ -788,14 +875,14 @@ input[type="submit"].btn-modern {
|
|||||||
|
|
||||||
input[type="button"].btn-modern:hover,
|
input[type="button"].btn-modern:hover,
|
||||||
input[type="submit"].btn-modern:hover {
|
input[type="submit"].btn-modern:hover {
|
||||||
background-color: var(--accent-green);
|
background-color: var(--btn-green-bg-hover);
|
||||||
border-color: var(--accent-green);
|
border-color: var(--btn-green-bg-hover);
|
||||||
color: #fff;
|
color: var(--btn-green-text-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="button"].btn-modern:active,
|
input[type="button"].btn-modern:active,
|
||||||
input[type="submit"].btn-modern:active {
|
input[type="submit"].btn-modern:active {
|
||||||
background-color: var(--accent-green-dim);
|
background-color: var(--btn-green-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="button"].btn-modern:disabled,
|
input[type="button"].btn-modern:disabled,
|
||||||
@@ -809,12 +896,26 @@ input[type="submit"].btn-modern:disabled {
|
|||||||
/* Unsaved reminder */
|
/* Unsaved reminder */
|
||||||
input[type="button"].btn-modern.is-unsaved:not(:disabled),
|
input[type="button"].btn-modern.is-unsaved:not(:disabled),
|
||||||
input[type="submit"].btn-modern.is-unsaved:not(:disabled) {
|
input[type="submit"].btn-modern.is-unsaved:not(:disabled) {
|
||||||
background-color: var(--accent-green) !important;
|
background-color: var(--btn-green-bg) !important;
|
||||||
border-color: var(--accent-green) !important;
|
border-color: var(--btn-green-bg-hover) !important;
|
||||||
color: #fff !important;
|
color: var(--btn-green-text-hover) !important;
|
||||||
box-shadow: 0 0 10px rgba(34, 197, 94, 0.35);
|
box-shadow: 0 0 10px rgba(34, 197, 94, 0.35);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Cancel / secondary modal button — neutral, theme-aware (replaces hardcoded #64748b). */
|
||||||
|
input[type="button"].btn-modern.btn-cancel,
|
||||||
|
input[type="submit"].btn-modern.btn-cancel {
|
||||||
|
background-color: var(--btn-neutral-bg);
|
||||||
|
color: var(--btn-neutral-text);
|
||||||
|
border-color: var(--border-subtle);
|
||||||
|
}
|
||||||
|
input[type="button"].btn-modern.btn-cancel:hover,
|
||||||
|
input[type="submit"].btn-modern.btn-cancel:hover {
|
||||||
|
background-color: var(--btn-neutral-bg);
|
||||||
|
border-color: var(--text-muted);
|
||||||
|
color: var(--btn-neutral-text);
|
||||||
|
}
|
||||||
|
|
||||||
fieldset {
|
fieldset {
|
||||||
border: none;
|
border: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1073,9 +1174,9 @@ BUTTON VIEW DETAILS
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-view-details:hover {
|
.btn-view-details:hover {
|
||||||
background-color: var(--accent-green);
|
background-color: var(--btn-green-bg-hover);
|
||||||
border-color: var(--accent-green);
|
border-color: var(--btn-green-bg-hover);
|
||||||
color: #fff;
|
color: var(--btn-green-text-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ==========================================================================
|
/* ==========================================================================
|
||||||
@@ -1372,12 +1473,12 @@ BUTTON VIEW DETAILS
|
|||||||
|
|
||||||
/* PLP row layout — same style as subframe-header: label | bar | bitrate | pct */
|
/* PLP row layout — same style as subframe-header: label | bar | bitrate | pct */
|
||||||
.plp-row {
|
.plp-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 42px 1fr auto auto;
|
grid-template-columns: 56px 1fr auto auto auto;
|
||||||
gap: 2px 10px;
|
gap: 2px 10px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 6px 0;
|
padding: 6px 0;
|
||||||
border-bottom: 1px solid rgba(75, 85, 99, 0.4);
|
border-bottom: 1px solid rgba(75, 85, 99, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.plp-row:last-of-type {
|
.plp-row:last-of-type {
|
||||||
@@ -1395,7 +1496,7 @@ BUTTON VIEW DETAILS
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: rgba(75, 85, 99, 0.3);
|
background: var(--plp-pill-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Bar track (background) */
|
/* Bar track (background) */
|
||||||
@@ -1465,20 +1566,13 @@ BUTTON VIEW DETAILS
|
|||||||
.plp-pct-text.pct-red { color: var(--status-err); }
|
.plp-pct-text.pct-red { color: var(--status-err); }
|
||||||
.plp-pct-text.pct-muted { color: var(--text-muted); }
|
.plp-pct-text.pct-muted { color: var(--text-muted); }
|
||||||
|
|
||||||
/* Detail line (services + FULL indicator, below bar) */
|
/* FULL indicator — beside pct text in the same row */
|
||||||
.plp-detail {
|
|
||||||
grid-column: 2 / -1;
|
|
||||||
grid-row: 2;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.plp-full-indicator {
|
.plp-full-indicator {
|
||||||
font-size: 11px;
|
grid-column: 5;
|
||||||
font-weight: 700;
|
grid-row: 1;
|
||||||
color: var(--status-err);
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--status-err);
|
||||||
background: rgba(239, 68, 68, 0.15);
|
background: rgba(239, 68, 68, 0.15);
|
||||||
border: 1px solid rgba(239, 68, 68, 0.35);
|
border: 1px solid rgba(239, 68, 68, 0.35);
|
||||||
padding: 1px 7px;
|
padding: 1px 7px;
|
||||||
@@ -1488,32 +1582,53 @@ BUTTON VIEW DETAILS
|
|||||||
}
|
}
|
||||||
|
|
||||||
.plp-full-indicator.visible {
|
.plp-full-indicator.visible {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
animation: alarmPulse 1.5s ease-in-out infinite;
|
animation: alarmPulse 1.5s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plp-services {
|
/* PLP trend sparkline container — inline chart below each utilization bar.
|
||||||
font-size: 13px;
|
JS renderer writes SVG into this container on each 2-sec poll.
|
||||||
font-weight: 600;
|
Placed in grid row 2 (row 1 = bar + pct + FULL indicator) spanning the bar columns.
|
||||||
color: var(--status-neutral);
|
Services displayed as left sidebar panel inside the SVG when present. */
|
||||||
max-width: 400px;
|
.plp-trend-spark {
|
||||||
overflow: hidden;
|
grid-column: 2 / -1;
|
||||||
text-overflow: ellipsis;
|
grid-row: 2;
|
||||||
white-space: nowrap;
|
margin-top: 4px;
|
||||||
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plp-services:empty {
|
/* Hide trend container when PLP row is hidden */
|
||||||
display: none;
|
.plp-row[style*="display: none"] .plp-trend-spark,
|
||||||
|
.plp-row[style*="display:none"] .plp-trend-spark {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plp-services::before {
|
/* Sparkline SVG element — fills container width.
|
||||||
content: '[';
|
ViewBox 700×120 with preserveAspectRatio="none" — stretches to match bar width.
|
||||||
color: var(--border-subtle);
|
When services exist, left margin widens (ML += svcBandW) to accommodate sidebar panel. */
|
||||||
|
.plp-trend-svg {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
/* height set dynamically by JS via inline style — matches viewBox ratio */
|
||||||
}
|
}
|
||||||
|
|
||||||
.plp-services::after {
|
/* Max/min envelope band — semi-transparent green fill (set inline by JS). */
|
||||||
content: ']';
|
.plp-trend-svg polygon {
|
||||||
color: var(--border-subtle);
|
stroke: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subframe header trend — matches bar height */
|
||||||
|
.subframe-header .plp-trend-spark {
|
||||||
|
grid-column: 2 / -1;
|
||||||
|
grid-row: 2;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Global header trend — extra tall for emphasis */
|
||||||
|
.global-header .plp-trend-spark {
|
||||||
|
grid-column: 2 / -1;
|
||||||
|
grid-row: 2;
|
||||||
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Subframe header bar (total payload at top) */
|
/* Subframe header bar (total payload at top) */
|
||||||
@@ -1589,22 +1704,16 @@ BUTTON VIEW DETAILS
|
|||||||
|
|
||||||
/* Responsive adjustments for utilization bars */
|
/* Responsive adjustments for utilization bars */
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
.plp-row {
|
.plp-row {
|
||||||
grid-template-columns: 36px 1fr auto;
|
grid-template-columns: 48px 1fr auto auto;
|
||||||
}
|
}
|
||||||
.plp-row .plp-bitrate-text {
|
.plp-row .plp-bitrate-text {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
.plp-pct-text {
|
.plp-pct-text {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.plp-detail {
|
.subframe-header {
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
.plp-services {
|
|
||||||
max-width: none;
|
|
||||||
}
|
|
||||||
.subframe-header {
|
|
||||||
grid-template-columns: 48px 1fr;
|
grid-template-columns: 48px 1fr;
|
||||||
}
|
}
|
||||||
.subframe-header .plp-bitrate-text {
|
.subframe-header .plp-bitrate-text {
|
||||||
@@ -1615,12 +1724,12 @@ BUTTON VIEW DETAILS
|
|||||||
.global-header {
|
.global-header {
|
||||||
grid-template-columns: 48px 1fr;
|
grid-template-columns: 48px 1fr;
|
||||||
}
|
}
|
||||||
.global-header .plp-bitrate-text {
|
.global-header .plp-bitrate-text {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ==========================================================================
|
/* ==========================================================================
|
||||||
SESSION TIMEOUT WARNING
|
SESSION TIMEOUT WARNING
|
||||||
@@ -1694,3 +1803,448 @@ SESSION TIMEOUT WARNING
|
|||||||
.session-timeout-actions .btn-modern {
|
.session-timeout-actions .btn-modern {
|
||||||
min-width: 120px;
|
min-width: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* =============================================================================
|
||||||
|
Sprint 1 — dashboard bar mode-adaptive visibility + group separators
|
||||||
|
Additive only. No existing rule, selector, or variable is modified.
|
||||||
|
Shared with 2D and exciter UIs — these classes are new and will only apply
|
||||||
|
where the new data-tile attribute is present on summary-card elements.
|
||||||
|
============================================================================= */
|
||||||
|
|
||||||
|
/* Marker class for tile-hidden state (reserved; current implementation uses
|
||||||
|
inline style="display:none" set by applyBarTileVisibility for idempotency.
|
||||||
|
Class kept for future use: allow CSS-only hiding via toggled class if we
|
||||||
|
move to a transition/animation approach later.) */
|
||||||
|
.summary-card[data-tile].tile-hidden { display: none; }
|
||||||
|
|
||||||
|
/* Group-separator spacing for the bar.
|
||||||
|
The bar is a flex row; group separation is a slightly larger left margin
|
||||||
|
on the first tile of each group (applied via a data-group-first attribute
|
||||||
|
that pages set on select tiles).
|
||||||
|
Purely additive. Tiles without data-group-first are unaffected. */
|
||||||
|
.summary-card[data-group-first="input"] { margin-left: 8px; }
|
||||||
|
.summary-card[data-group-first="output"] { margin-left: 8px; }
|
||||||
|
.summary-card[data-group-first="housekeeping"] { margin-left: 8px; }
|
||||||
|
|
||||||
|
/* =============================================================================
|
||||||
|
Sprint 1 Increment 6 — Live Trends panel (Overview page only)
|
||||||
|
Additive only. No existing rule, selector, or variable is modified.
|
||||||
|
These classes are new (ov-trend-*); they only apply to the new panel
|
||||||
|
container on Overview and do not affect the shared 2D / exciter UI.
|
||||||
|
============================================================================= */
|
||||||
|
|
||||||
|
/* Panel container — visual weight matches the existing ov-chain-card. */
|
||||||
|
.ov-trend-panel {
|
||||||
|
background: var(--bg-panel);
|
||||||
|
border: 1px solid var(--border-subtle);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ov-trend-panel:empty { display: none; }
|
||||||
|
|
||||||
|
/* Header row — small caps, dot, title, buffer-age label. */
|
||||||
|
.ov-trend-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 11px;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin-bottom: 12px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ov-trend-header .ov-trend-dot {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--status-ok, #22c55e);
|
||||||
|
box-shadow: 0 0 4px rgba(34, 197, 94, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ov-trend-header .ov-trend-title {
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ov-trend-header .ov-trend-sep {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ov-trend-header .ov-trend-age {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grid of per-metric cards. Uses flex so cards auto-size to available
|
||||||
|
width, wrapping to a new row only when needed. */
|
||||||
|
.ov-trend-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ov-trend-card {
|
||||||
|
flex: 1 1 200px;
|
||||||
|
min-width: 180px;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border-subtle);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ov-trend-label {
|
||||||
|
font-size: 10px;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ov-trend-value {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 4px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ov-trend-num {
|
||||||
|
font-family: 'JetBrains Mono', 'Courier New', monospace;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ov-trend-unit {
|
||||||
|
font-size: 10px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
/* No text-transform: units carry meaningful case (Mbps / dB / dBm /
|
||||||
|
dBc / MHz / %). Matches the signal chain's .ov-blk-primary .ov-u
|
||||||
|
convention; avoids collapsing dB / dBm / dBc distinctions into the
|
||||||
|
same upper-cased suffix. */
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The sparkline itself. Height fixed; width fills the card. */
|
||||||
|
.ov-trend-spark {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
stroke: var(--status-ok, #22c55e);
|
||||||
|
stroke-width: 1.5;
|
||||||
|
fill: none;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
stroke-linecap: round;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* N/A overlay wrapper -- anchors the overlay text above the sparkline.
|
||||||
|
Positioned so overlay sits centered on the sparkline area. */
|
||||||
|
.ov-trend-spark-wrap {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Shown on FSSI-affected charts (SNR, FWD%, UIMD, LIMD) when FSSI is
|
||||||
|
active. Per v0.4 B4: overlay reads "N/A -- FSSI active" and line
|
||||||
|
drawing is suppressed (handled by extractors returning null).
|
||||||
|
pointer-events: none so the overlay never intercepts clicks. */
|
||||||
|
.ov-trend-na-overlay {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 11px;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
color: var(--text-muted, #8a8a8a);
|
||||||
|
background: rgba(0, 0, 0, 0.04);
|
||||||
|
pointer-events: none;
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When FSSI-active, tone down the card's current-value number. */
|
||||||
|
.ov-trend-card-na .ov-trend-num-na {
|
||||||
|
color: var(--text-muted, #8a8a8a);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Min/max labels overlaid on the sparkline's right edge. Show the raw
|
||||||
|
data range (not the minSpan-floored display range) so operators can
|
||||||
|
always read the actual values — important when minSpan stretches a
|
||||||
|
tiny real variation into a taller chart. pointer-events: none so the
|
||||||
|
labels never intercept interaction with the svg. */
|
||||||
|
.ov-trend-spark-labels {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 2px;
|
||||||
|
bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
pointer-events: none;
|
||||||
|
font-family: ui-monospace, Menlo, Consolas, monospace;
|
||||||
|
font-size: 9px;
|
||||||
|
line-height: 1;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ov-trend-label-max,
|
||||||
|
.ov-trend-label-min {
|
||||||
|
/* Units are not repeated on these labels — the card's main value
|
||||||
|
already shows the unit, and repeating it here adds clutter. */
|
||||||
|
padding: 1px 2px;
|
||||||
|
background: var(--bg-main);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
CONTEXTUAL WARNING NOTES (added 2026-05-31)
|
||||||
|
Shared classes for the amber "this feature is gated / mode-restricted"
|
||||||
|
messages that were previously hand-written inline on each page.
|
||||||
|
Two variants:
|
||||||
|
.warning-banner — block callout (tinted box) above a section
|
||||||
|
.field-note — small inline note appended after a field/label
|
||||||
|
IMPORTANT: these classes intentionally do NOT set `display`. Visibility is
|
||||||
|
toggled by page JS via `element.style.display = ''` (show) / 'none' (hide),
|
||||||
|
which relies on the element's own inline `display:none` default. Adding
|
||||||
|
`display` here would break that toggling (a cleared inline value would fall
|
||||||
|
through to this rule). Keep `style="display:none"` on the element.
|
||||||
|
========================================================================== */
|
||||||
|
.warning-banner {
|
||||||
|
padding: 8px 14px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--status-warn);
|
||||||
|
background: rgba(245, 158, 11, 0.08);
|
||||||
|
border: 1px solid rgba(245, 158, 11, 0.2);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.field-note {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--status-warn);
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
/* Error state for .warning-banner — red instead of amber (e.g. expired cert).
|
||||||
|
Toggle `is-error` in JS instead of setting inline colors. */
|
||||||
|
.warning-banner.is-error {
|
||||||
|
color: var(--status-err);
|
||||||
|
background: rgba(239, 68, 68, 0.08);
|
||||||
|
border-color: rgba(239, 68, 68, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
SECONDARY (CANCEL) BUTTON (added 2026-05-31)
|
||||||
|
Neutral-grey companion to .btn-modern, for Cancel / dismiss actions (e.g. the
|
||||||
|
account-settings modal). Use: class="btn-modern btn-secondary"
|
||||||
|
It only overrides the colour of .btn-modern; size/shape/hover come from
|
||||||
|
.btn-modern. Theme-aware via --bg-card-hover.
|
||||||
|
========================================================================== */
|
||||||
|
input[type="button"].btn-secondary,
|
||||||
|
input[type="submit"].btn-secondary {
|
||||||
|
background-color: var(--bg-card-hover);
|
||||||
|
}
|
||||||
|
input[type="button"].btn-secondary:hover,
|
||||||
|
input[type="submit"].btn-secondary:hover {
|
||||||
|
background-color: var(--border-subtle);
|
||||||
|
border-color: var(--border-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
PROGRESS BARS (added 2026-05-31)
|
||||||
|
Restart / reset / firmware-update progress. The bar fill is styled via the
|
||||||
|
browser pseudo-elements (the plain `color` property does NOT colour a
|
||||||
|
<progress> fill in Chromium/WebKit). Fill colour matches the info-text notes
|
||||||
|
shown beside each bar. Theme-aware via --info-text.
|
||||||
|
========================================================================== */
|
||||||
|
progress.aw-progress {
|
||||||
|
width: 100%;
|
||||||
|
height: 24px;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: var(--bg-card);
|
||||||
|
}
|
||||||
|
progress.aw-progress::-webkit-progress-bar { background: var(--bg-card); border-radius: 4px; }
|
||||||
|
progress.aw-progress::-webkit-progress-value { background: var(--info-text); border-radius: 4px; }
|
||||||
|
progress.aw-progress::-moz-progress-bar { background: var(--info-text); }
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
LIGHT THEME (entry 010 — additive, owner-approved palette "Concept C balanced")
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
Activated by adding class "light-theme" to the root <html> element. Default
|
||||||
|
(no class) = dark theme above. This block ONLY overrides token VALUES; it adds
|
||||||
|
no new selectors that affect dark mode and changes nothing in :root.
|
||||||
|
|
||||||
|
Design intent (owner-approved, comfort-contrast — AA floor, NOT maximum):
|
||||||
|
- Page is soft grey (#e8ebf0), NOT white — reduces glare/eye-fatigue.
|
||||||
|
- Text is dark slate (#2a3340), NOT pure black — softer on the eyes.
|
||||||
|
- Status colours are muted, not neon. All measured AA on their real surface.
|
||||||
|
|
||||||
|
DARK ISLAND: the sidebar stays dark navy in BOTH themes, so --sidebar-bg/-text/
|
||||||
|
-text-active/-hover and --sidebar-accent are deliberately NOT overridden here —
|
||||||
|
they inherit their :root (dark) values. --sidebar-accent stays bright green.
|
||||||
|
|
||||||
|
CSS owns colour; JS owns identity (entry 008) — so the mode-* overrides below
|
||||||
|
recolour the overview wizard/chips automatically with ZERO JS change.
|
||||||
|
============================================================================ */
|
||||||
|
:root.light-theme {
|
||||||
|
/* Green - primary accent / status good / active */
|
||||||
|
--accent-green: #0f7034;
|
||||||
|
--accent-green-bright: #126e30;
|
||||||
|
--accent-green-dim: #0c5a2a;
|
||||||
|
/* Error / alert */
|
||||||
|
--status-err: #b5302a;
|
||||||
|
--status-err-dim: #8f231e; /* darker companion to --status-err */
|
||||||
|
/* Warning / amber */
|
||||||
|
--status-warn: #8f4a08;
|
||||||
|
--status-warn-dim: #7c4106; /* darker companion to --status-warn */
|
||||||
|
/* Neutral / info */
|
||||||
|
--status-neutral: #0a5f8f;
|
||||||
|
--status-neutral-dim: #084b70; /* darker companion to --status-neutral */
|
||||||
|
/* Light palette surfaces */
|
||||||
|
--bg-main: #dde2e9; /* soft grey page, NOT white */
|
||||||
|
--bg-panel: #eaeef3; /* card surface, gentle off-white */
|
||||||
|
--bg-card: #d8dee6; /* inset/chip surface */
|
||||||
|
--bg-card-hover: #cbd2dc;
|
||||||
|
/* Text */
|
||||||
|
--text-primary: #2a3340; /* dark slate, NOT pure black */
|
||||||
|
--text-secondary: #3a4452;
|
||||||
|
--text-muted: #525b6a;
|
||||||
|
--plp-pill-bg: #cdd4de;
|
||||||
|
--chart-inset: #ccd3dd;
|
||||||
|
/* --info-text inherits the :root link to --status-neutral → resolves to #0a5f8f.
|
||||||
|
Not re-declared here on purpose (the link is theme-agnostic). */
|
||||||
|
/* Mode identity colours (overview Signal Chain page). Gateway reuses --accent-green. */
|
||||||
|
--mode-exciter: #15598f; --mode-exciter-hover: #1f6fb0;
|
||||||
|
--mode-translator: #9a420a; --mode-translator-hover: #c8590f;
|
||||||
|
--mode-standalone: #5b3da8; --mode-standalone-hover: #6f4dc6;
|
||||||
|
--mode-exciter-tint: rgba(21,89,143,0.10);
|
||||||
|
--mode-translator-tint: rgba(154,66,10,0.10);
|
||||||
|
--mode-standalone-tint: rgba(91,61,168,0.10);
|
||||||
|
--mode-gateway-tint: rgba(15,112,52,0.10);
|
||||||
|
--mode-exciter-tint-strong: rgba(21,89,143,0.22);
|
||||||
|
--mode-translator-tint-strong: rgba(154,66,10,0.22);
|
||||||
|
--mode-standalone-tint-strong: rgba(91,61,168,0.22);
|
||||||
|
--mode-gateway-tint-strong: rgba(15,112,52,0.22);
|
||||||
|
/* Light button family: soft green + dark-green text (Strategy 2); soft grey
|
||||||
|
neutral. These keep white-on-dark NOT used here — light uses dark text. */
|
||||||
|
--btn-green-bg: #8fcfa8;
|
||||||
|
--btn-green-bg-hover: #7bc098;
|
||||||
|
--btn-green-text: #06230f;
|
||||||
|
--btn-green-text-hover: #06230f;
|
||||||
|
--btn-neutral-bg: #d4d9e1;
|
||||||
|
--btn-neutral-text: #3a4452;
|
||||||
|
/* NOTE: --sidebar-footer-*, --sidebar-label are intentionally NOT redefined
|
||||||
|
here — the sidebar is a dark island, so they keep their pinned :root values. */
|
||||||
|
/* Borders */
|
||||||
|
--border-subtle: #cdd3dc;
|
||||||
|
/* --border-accent inherits :root link to --accent-green → resolves to #0f7034. */
|
||||||
|
/* Sidebar tokens: NOT overridden (dark island). --sidebar-accent stays #22c55e. */
|
||||||
|
/* --primary / --primary-hover inherit :root links to --accent-green(-dim). */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
THEME TOGGLE + UTILITY CLUSTER (entry 010 — additive, light-theme feature)
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
A header control to switch dark/light. Grouped with the Admin dropdown in a
|
||||||
|
"utility cluster" so the two read as one controls zone on the right edge.
|
||||||
|
Styling: faint green fill + green text/icon + green border (owner-approved
|
||||||
|
"Option 1+2"). Wording names the DESTINATION ("Light Mode"/"Dark Mode") with
|
||||||
|
a matching sun/moon icon. Collapses to icon-only at narrow widths.
|
||||||
|
|
||||||
|
NOTE: the cluster must NOT set overflow:hidden — .dropdown-content is
|
||||||
|
position:absolute and would be clipped. Cluster is transparent/no-clip.
|
||||||
|
============================================================================ */
|
||||||
|
|
||||||
|
/* Cluster wraps the toggle + the existing Admin dropdown <h3> contents. */
|
||||||
|
/* The cluster is a visible "controls" panel: subtle card background, border,
|
||||||
|
rounded, slight lift — so the toggle + Admin read as ONE unit (owner-approved
|
||||||
|
"3a"). It does NOT clip (dropdown-content is absolute and must escape). */
|
||||||
|
.utility-cluster {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border-subtle);
|
||||||
|
border-radius: 9px;
|
||||||
|
padding: 4px 6px;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
|
||||||
|
}
|
||||||
|
/* Thin divider between the toggle and the Admin dropdown inside the cluster. */
|
||||||
|
.utility-divider {
|
||||||
|
width: 1px;
|
||||||
|
height: 22px;
|
||||||
|
background: var(--border-subtle);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
/* Inside the cluster, flatten the toggle + Admin so they read as cluster items,
|
||||||
|
not standalone buttons. They keep their colour but drop their own box. */
|
||||||
|
.utility-cluster .theme-toggle {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
height: 30px;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
.utility-cluster .theme-toggle:hover {
|
||||||
|
background: var(--mode-gateway-tint);
|
||||||
|
border-color: var(--accent-green);
|
||||||
|
}
|
||||||
|
.utility-cluster .dropbtn-modern {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
.utility-cluster .dropdown:hover .dropbtn-modern {
|
||||||
|
border-color: var(--accent-green);
|
||||||
|
}
|
||||||
|
/* The header <h3> already has margin-left:auto (pushes it to the right edge).
|
||||||
|
We keep that and just lay out the cluster inside it, right-aligned. */
|
||||||
|
.app-header h3.has-toggle { display: flex; align-items: center; justify-content: flex-end; }
|
||||||
|
|
||||||
|
.theme-toggle {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 7px;
|
||||||
|
height: 34px;
|
||||||
|
padding: 0 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: var(--mode-gateway-tint);
|
||||||
|
border: 1px solid var(--accent-green);
|
||||||
|
color: var(--accent-green);
|
||||||
|
transition: background 0.15s ease, border-color 0.15s ease;
|
||||||
|
}
|
||||||
|
.theme-toggle:hover { background: var(--bg-card-hover); }
|
||||||
|
.theme-toggle svg { width: 16px; height: 16px; color: var(--accent-green); }
|
||||||
|
|
||||||
|
/* Icon + label swap by theme. Default (dark) shows "Light Mode" + sun
|
||||||
|
(the destination); .light-theme shows "Dark Mode" + moon. */
|
||||||
|
.theme-toggle .tt-sun { display: inline-flex; }
|
||||||
|
.theme-toggle .tt-moon { display: none; }
|
||||||
|
.theme-toggle .tt-to-light { display: inline; }
|
||||||
|
.theme-toggle .tt-to-dark { display: none; }
|
||||||
|
:root.light-theme .theme-toggle .tt-sun { display: none; }
|
||||||
|
:root.light-theme .theme-toggle .tt-moon { display: inline-flex; }
|
||||||
|
:root.light-theme .theme-toggle .tt-to-light { display: none; }
|
||||||
|
:root.light-theme .theme-toggle .tt-to-dark { display: inline; }
|
||||||
|
|
||||||
|
/* Smooth theme transition on the big surfaces. */
|
||||||
|
body, .app-header, .app-sidebar, .scard, .panel, .card {
|
||||||
|
transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Narrow widths: collapse the toggle to icon-only (drop the text label). */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.theme-toggle .tt-label { display: none; }
|
||||||
|
.theme-toggle { padding: 0 9px; }
|
||||||
|
}
|
||||||
|
|||||||
Executable → Regular
+78
-22
@@ -14,8 +14,32 @@
|
|||||||
// Power decimal lookup table
|
// Power decimal lookup table
|
||||||
var POWER_DECIMALS = ["00","05","10","15","20","25","30","35","40","45","50","55","60","65","70","75","80","85","90","95"];
|
var POWER_DECIMALS = ["00","05","10","15","20","25","30","35","40","45","50","55","60","65","70","75","80","85","90","95"];
|
||||||
|
|
||||||
// Input source labels
|
// Input source labels — C1 (2026-08-05): smod's OWN status map. Index 5 = SMPTE per
|
||||||
var INPUT_SOURCES = ["ASI1","ASI2","EAS","Tuner","TSoIP","None"];
|
// smod firmware (matches the status page's proven parse). Copied by LABEL from the
|
||||||
|
// family, values re-read from this product (register W9 — never copy values).
|
||||||
|
// SMPTE slot PROVISIONAL pending the board-path hardware answer (team question).
|
||||||
|
// "None" is NOT a source: it renders only for absent/out-of-range data via
|
||||||
|
// getInputSource(). Frozen so no page can patch it into a parallel list again.
|
||||||
|
var INPUT_SOURCES = Object.freeze(["ASI1","ASI2","EAS","Tuner","TSoIP","SMPTE"]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map a firmware input-source index to its display name.
|
||||||
|
* The ONLY translator from form[1] to a label — status_info.html calls this too
|
||||||
|
* (C1 killed its private ternary). Out-of-range/absent -> "None" (genuine no-input).
|
||||||
|
*/
|
||||||
|
function getInputSource(index) {
|
||||||
|
var i = Number(index);
|
||||||
|
return (isFinite(i) && i >= 0 && i < INPUT_SOURCES.length) ? INPUT_SOURCES[i] : "None";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* R-2 (2026-08-05): NaN guard for numeric card population (in-tree pattern from
|
||||||
|
* sys_spectrum.html). Finite -> formatted via fn; else "--" (readback convention).
|
||||||
|
*/
|
||||||
|
function fmtNum(v, fn) {
|
||||||
|
var n = Number(v);
|
||||||
|
return isFinite(n) ? fn(n) : "--";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate frequency from channel number
|
* Calculate frequency from channel number
|
||||||
@@ -42,6 +66,7 @@
|
|||||||
* @returns {number|string} Temperature in Fahrenheit
|
* @returns {number|string} Temperature in Fahrenheit
|
||||||
*/
|
*/
|
||||||
function convertTemperature(rawValue, unit) {
|
function convertTemperature(rawValue, unit) {
|
||||||
|
if (!isFinite(Number(rawValue))) return "--"; // R-2: absent data -> "--", never NaN
|
||||||
if (unit === 0) {
|
if (unit === 0) {
|
||||||
return rawValue; // Already Fahrenheit
|
return rawValue; // Already Fahrenheit
|
||||||
} else {
|
} else {
|
||||||
@@ -74,21 +99,32 @@
|
|||||||
* @returns {string} Status class
|
* @returns {string} Status class
|
||||||
*/
|
*/
|
||||||
function getSnrStatus(snr) {
|
function getSnrStatus(snr) {
|
||||||
if (snr === "N/A") return 'off';
|
// "N/A" and "N/A-FSSI" both mean no meaningful reading -> grey, never red.
|
||||||
var val = parseFloat(snr); // already carries the +10 dB display offset
|
// (FSSI parity round; the isNaN belt matches the 1.0 core's fall-through.)
|
||||||
if (val >= 37) return 'ok';
|
if (typeof snr === "string" && snr.indexOf("N/A") === 0) return 'off';
|
||||||
if (val >= 30) return 'warn';
|
var val = parseFloat(snr); // true measured SNR/MER in dB
|
||||||
|
if (isNaN(val)) return 'off';
|
||||||
|
if (val >= 27) return 'ok';
|
||||||
|
if (val >= 20) return 'warn';
|
||||||
return 'err';
|
return 'err';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateSpectrumFssi(aFssi, bFssi, abMode) {
|
||||||
|
var fssi = document.getElementById('rd_fssi');
|
||||||
|
if (!fssi) return;
|
||||||
|
fssi.innerText = (abMode === 1) ? (aFssi + ' / ' + bFssi) : aFssi;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get IMD status (UIMD/LIMD)
|
* Get IMD status (UIMD/LIMD)
|
||||||
* @param {string} imd - IMD value or "N/A"
|
* @param {string} imd - IMD value or "N/A"
|
||||||
* @returns {string} Status class
|
* @returns {string} Status class
|
||||||
*/
|
*/
|
||||||
function getImdStatus(imd) {
|
function getImdStatus(imd) {
|
||||||
if (imd === "N/A") return 'off';
|
if (typeof imd === "string" && imd.indexOf("N/A") === 0) return 'off';
|
||||||
return parseFloat(imd) >= 47 ? 'ok' : 'err';
|
var v = parseFloat(imd);
|
||||||
|
if (isNaN(v)) return 'off';
|
||||||
|
return v >= 47 ? 'ok' : 'err';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -126,7 +162,10 @@
|
|||||||
(data.ctrl.toUpperCase() === "REMOTE") ? 'neutral' : 'warn');
|
(data.ctrl.toUpperCase() === "REMOTE") ? 'neutral' : 'warn');
|
||||||
setCard('sum_rf', 'dot_rf', (data.rfStat === 1) ? "On" : "Off",
|
setCard('sum_rf', 'dot_rf', (data.rfStat === 1) ? "On" : "Off",
|
||||||
(data.rfStat === 1) ? 'ok' : 'off');
|
(data.rfStat === 1) ? 'ok' : 'off');
|
||||||
setCard('sum_outpow', 'dot_outpow', data.finalPower, 'ok');
|
// REG-5 (2026-08-05, 1.0 C.4 parity): OUTPUT POWER must not stay green with a
|
||||||
|
// stale figure while RF is off/muted — grey it whenever RF is not On.
|
||||||
|
setCard('sum_outpow', 'dot_outpow', data.finalPower,
|
||||||
|
(data.rfStat === 1) ? 'ok' : 'off');
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -186,19 +225,34 @@
|
|||||||
var chan = Number(form[7]);
|
var chan = Number(form[7]);
|
||||||
var freqVal = calcFrequency(chan);
|
var freqVal = calcFrequency(chan);
|
||||||
|
|
||||||
// SNR: half-dB from the app plus the fixed +10 dB display offset.
|
// SNR: half-dB value from the app, displayed as the true measured SNR/MER.
|
||||||
var snr = (ab_modenum === 1) ? "N/A" : (Number(form[11]) / 2 + 10).toFixed(1);
|
var rawSnr = Number(form[11]);
|
||||||
|
var snr = (ab_modenum === 1) ? "N/A-FSSI" :
|
||||||
|
(rawSnr <= 0 ? "N/A" : fmtNum(form[11], function(n){ return (n / 2).toFixed(1); }));
|
||||||
|
|
||||||
// LIMD / UIMD: half-dB from the app plus the fixed +11 dB display offset.
|
// LIMD / UIMD are half-dB values from the app. Apply the fixed +11 dB
|
||||||
// Rounding the sum and then appending the half misreports odd half-dB
|
// display offset used by Status and the LCD.
|
||||||
// values by 1 dB, so format the sum directly.
|
var rawLimd = Number(form[12]);
|
||||||
var limd = (ab_modenum === 1) ? "N/A" : (Number(form[12]) / 2 + 11).toFixed(1);
|
var rawUimd = Number(form[13]);
|
||||||
var uimd = (ab_modenum === 1) ? "N/A" : (Number(form[13]) / 2 + 11).toFixed(1);
|
var limd = (ab_modenum === 1) ? "N/A-FSSI" :
|
||||||
|
(rawLimd <= 0 ? "N/A" : fmtNum(form[12], function(n){ return (n / 2 + 11).toFixed(1); }));
|
||||||
|
var uimd = (ab_modenum === 1) ? "N/A-FSSI" :
|
||||||
|
(rawUimd <= 0 ? "N/A" : fmtNum(form[13], function(n){ return (n / 2 + 11).toFixed(1); }));
|
||||||
|
|
||||||
// Input source
|
// The status interface already carries the shared A/B FSSI values.
|
||||||
var inputIdx = Number(form[1]);
|
// Spectrum consumes the same values instead of issuing another request.
|
||||||
if (inputIdx < 0 || inputIdx > 5) inputIdx = 5;
|
updateSpectrumFssi(Number(form[16]), Number(form[17]), ab_modenum);
|
||||||
var inputSrc = INPUT_SOURCES[inputIdx];
|
var refreshActive = (ab_modenum === 1);
|
||||||
|
if (typeof window.setSpectrumRefreshActive === 'function') {
|
||||||
|
// Let the spectrum page compare with and update its previous state.
|
||||||
|
window.setSpectrumRefreshActive(refreshActive);
|
||||||
|
} else {
|
||||||
|
window.fssiRefreshActive = refreshActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input source — C1: via the single accessor; the old >5 clamp is gone
|
||||||
|
// (it is what rendered firmware idx 5 = SMPTE as "None").
|
||||||
|
var inputSrc = getInputSource(form[1]);
|
||||||
|
|
||||||
// Input status
|
// Input status
|
||||||
var inputStatus = (Number(form[2]) === 0) ? "OK" : "Err";
|
var inputStatus = (Number(form[2]) === 0) ? "OK" : "Err";
|
||||||
@@ -222,7 +276,8 @@
|
|||||||
var p_sign = (Number(rf_form[1]) === 0) ? "+" : "-";
|
var p_sign = (Number(rf_form[1]) === 0) ? "+" : "-";
|
||||||
var p_int = Number(rf_form[2]);
|
var p_int = Number(rf_form[2]);
|
||||||
var p_dec = POWER_DECIMALS[Number(rf_form[3])] || "00";
|
var p_dec = POWER_DECIMALS[Number(rf_form[3])] || "00";
|
||||||
var finalPower = p_sign + p_int + "." + p_dec;
|
// R-2: absent power data -> "--", never "+NaN.00"
|
||||||
|
var finalPower = isFinite(p_int) ? (p_sign + p_int + "." + p_dec) : "--";
|
||||||
|
|
||||||
// Update all cards using the shared function
|
// Update all cards using the shared function
|
||||||
window.updateDashboardCards({
|
window.updateDashboardCards({
|
||||||
@@ -254,7 +309,8 @@
|
|||||||
getSnrStatus: getSnrStatus,
|
getSnrStatus: getSnrStatus,
|
||||||
getImdStatus: getImdStatus,
|
getImdStatus: getImdStatus,
|
||||||
POWER_DECIMALS: POWER_DECIMALS,
|
POWER_DECIMALS: POWER_DECIMALS,
|
||||||
INPUT_SOURCES: INPUT_SOURCES
|
INPUT_SOURCES: INPUT_SOURCES,
|
||||||
|
getInputSource: getInputSource
|
||||||
};
|
};
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/* smod js/theme.js — ported verbatim from the 1.0 dashboard_core.js:605-651 (entry 010)
|
||||||
|
* as its own file because smod's login.html loads no dashboard.js and the module must run
|
||||||
|
* in <head> on EVERY page (prevents a wrong-theme first paint). Theme round 2026-08-06. */
|
||||||
|
/* ============================================================================
|
||||||
|
* THEME TOGGLE (light/dark) — entry 010, light-theme feature
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
* Adds/removes the "light-theme" class on the root <html> element. CSS owns all
|
||||||
|
* colour (see styles.css :root.light-theme block); this only flips the class
|
||||||
|
* and remembers the choice. Default = dark (no class). Exposed as a global so
|
||||||
|
* the inline header onclick can reach it, matching this codebase's convention
|
||||||
|
* of page-local inline handlers (e.g. toggleSidebar).
|
||||||
|
*
|
||||||
|
* localStorage is used for persistence. This is real on-device firmware, where
|
||||||
|
* localStorage is available; the embedded browser retains it across reloads.
|
||||||
|
* ==========================================================================*/
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
var KEY = 'aw-theme'; // stored value: 'light' or 'dark'
|
||||||
|
var CLASS = 'light-theme';
|
||||||
|
var root = document.documentElement;
|
||||||
|
|
||||||
|
function apply(theme) {
|
||||||
|
if (theme === 'light') {
|
||||||
|
root.classList.add(CLASS);
|
||||||
|
} else {
|
||||||
|
root.classList.remove(CLASS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the saved choice as early as possible to avoid a flash of the
|
||||||
|
// wrong theme. dashboard_core.js is loaded in <head>, so this runs before
|
||||||
|
// the body paints on most pages.
|
||||||
|
try {
|
||||||
|
var saved = window.localStorage ? localStorage.getItem(KEY) : null;
|
||||||
|
apply(saved === 'light' ? 'light' : 'dark'); // default dark
|
||||||
|
} catch (e) {
|
||||||
|
apply('dark');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global handler for the header toggle button's onclick.
|
||||||
|
window.toggleTheme = function () {
|
||||||
|
var nowLight = root.classList.toggle(CLASS);
|
||||||
|
try {
|
||||||
|
if (window.localStorage) {
|
||||||
|
localStorage.setItem(KEY, nowLight ? 'light' : 'dark');
|
||||||
|
}
|
||||||
|
} catch (e) { /* storage unavailable; theme still applies for this session */ }
|
||||||
|
};
|
||||||
|
})();
|
||||||
+25
-15
@@ -4,7 +4,8 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title id="webtitle"></title>
|
<title id="webtitle"></title>
|
||||||
<link href="css/styles.css" rel="stylesheet" media="screen">
|
<script src="js/theme.js"></script>
|
||||||
|
<link href="css/styles.css" rel="stylesheet" media="screen">
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
<script src="js/jquery.js"></script>
|
<script src="js/jquery.js"></script>
|
||||||
<script src="js/dashboard.js"></script>
|
<script src="js/dashboard.js"></script>
|
||||||
@@ -299,12 +300,13 @@
|
|||||||
<div class="app-layout">
|
<div class="app-layout">
|
||||||
<aside class="app-sidebar">
|
<aside class="app-sidebar">
|
||||||
<ul class="items">
|
<ul class="items">
|
||||||
|
<li><a href="overview.html" title="Overview">Overview</a></li>
|
||||||
<li><a href="status_info.html" title="Status">Status</a></li>
|
<li><a href="status_info.html" title="Status">Status</a></li>
|
||||||
<li><a href="sys_set.html" title="Settings">Settings</a></li>
|
<li><a href="sys_set.html" title="Settings">Settings</a></li>
|
||||||
<li><a href="sys_rf.html" title="RF Control">RF Control</a></li>
|
<li><a href="sys_rf.html" title="RF Control">RF Control</a></li>
|
||||||
<li><a href="sys_psip.html" title="TSID&PSIP">TSID&PSIP</a></li>
|
<li><a href="sys_psip.html" title="TSID&PSIP">TSID&PSIP</a></li>
|
||||||
<li><a href="sys_ts.html" title="TSoIP">TSoIP</a></li>
|
<li><a href="sys_ts.html" title="TSoIP">TSoIP</a></li>
|
||||||
<li><a href="sys_spectrum.html" title="Spectrum">Spectrum</a></li>
|
<li><a href="sys_spectrum.html" title="RF Performance">RF Performance</a></li>
|
||||||
<li class="active"><a href="log_output.html" title="Log">Log</a></li>
|
<li class="active"><a href="log_output.html" title="Log">Log</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="sidebar-footer">
|
<div class="sidebar-footer">
|
||||||
@@ -324,8 +326,15 @@
|
|||||||
</button>
|
</button>
|
||||||
<h1><a href="https://anywavecom.net/" class="logo-bg" title="Anywave Broadcast Inc."></a></h1>
|
<h1><a href="https://anywavecom.net/" class="logo-bg" title="Anywave Broadcast Inc."></a></h1>
|
||||||
<h2 id="webtitle_header"><span></span></h2>
|
<h2 id="webtitle_header"><span></span></h2>
|
||||||
<h3>
|
<h3 class="has-toggle">
|
||||||
<div class="dropdown">
|
<div class="utility-cluster">
|
||||||
|
<button class="theme-toggle" onclick="toggleTheme()" aria-label="Switch color theme" title="Switch color theme">
|
||||||
|
<span class="tt-sun" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg></span>
|
||||||
|
<span class="tt-moon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg></span>
|
||||||
|
<span class="tt-label"><span class="tt-to-light">Light Mode</span><span class="tt-to-dark">Dark Mode</span></span>
|
||||||
|
</button>
|
||||||
|
<span class="utility-divider" aria-hidden="true"></span>
|
||||||
|
<div class="dropdown">
|
||||||
<button class="dropbtn-modern">Admin ▾</button>
|
<button class="dropbtn-modern">Admin ▾</button>
|
||||||
<div class="dropdown-content">
|
<div class="dropdown-content">
|
||||||
<a href="#" onclick="openAccountModal(); return false;">
|
<a href="#" onclick="openAccountModal(); return false;">
|
||||||
@@ -345,7 +354,8 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</h3>
|
</div>
|
||||||
|
</h3>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Fixed Dashboard Summary - Top of page, under header -->
|
<!-- Fixed Dashboard Summary - Top of page, under header -->
|
||||||
@@ -371,16 +381,6 @@
|
|||||||
<span class="summary-unit">dB</span>
|
<span class="summary-unit">dB</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="summary-card">
|
|
||||||
<div class="summary-label-row">
|
|
||||||
<span class="status-dot status-ok" id="dot_uimd" aria-hidden="true"></span>
|
|
||||||
<span class="summary-label">UIMD</span>
|
|
||||||
</div>
|
|
||||||
<div class="summary-value-wrapper">
|
|
||||||
<span class="summary-value value-ok" id="sum_uimd">--</span>
|
|
||||||
<span class="summary-unit">dBc</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="summary-card">
|
<div class="summary-card">
|
||||||
<div class="summary-label-row">
|
<div class="summary-label-row">
|
||||||
<span class="status-dot status-ok" id="dot_limd" aria-hidden="true"></span>
|
<span class="status-dot status-ok" id="dot_limd" aria-hidden="true"></span>
|
||||||
@@ -391,6 +391,16 @@
|
|||||||
<span class="summary-unit">dBc</span>
|
<span class="summary-unit">dBc</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="summary-card">
|
||||||
|
<div class="summary-label-row">
|
||||||
|
<span class="status-dot status-ok" id="dot_uimd" aria-hidden="true"></span>
|
||||||
|
<span class="summary-label">UIMD</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-value-wrapper">
|
||||||
|
<span class="summary-value value-ok" id="sum_uimd">--</span>
|
||||||
|
<span class="summary-unit">dBc</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="summary-card">
|
<div class="summary-card">
|
||||||
<div class="summary-label-row">
|
<div class="summary-label-row">
|
||||||
<span class="status-dot status-off" id="dot_rf" aria-hidden="true"></span>
|
<span class="status-dot status-off" id="dot_rf" aria-hidden="true"></span>
|
||||||
|
|||||||
+16
-9
@@ -3,7 +3,8 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title id="webtitle"></title>
|
<title id="webtitle">Digital Exciter</title>
|
||||||
|
<script src="js/theme.js"></script>
|
||||||
<link href="css/styles.css" rel="stylesheet" media="screen">
|
<link href="css/styles.css" rel="stylesheet" media="screen">
|
||||||
<style>
|
<style>
|
||||||
#content .box table td:first-child {
|
#content .box table td:first-child {
|
||||||
@@ -40,13 +41,13 @@ function loginsubmit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function refresh_show(){
|
function refresh_show(){
|
||||||
var queryData = "web_title=&"
|
/* atsc_status_cfg.cgi requires a session, but the login page has none yet. */
|
||||||
$.get("atsc_status_cfg.cgi?"+queryData, function(data, status) {
|
$.get("login.cgi?action=get_title", function(data) {
|
||||||
var table = data.split("|");
|
var titleText = String(data || "").replace(/^#/, "").trim() || "Digital Exciter";
|
||||||
var title = table[0].split("#");
|
document.title = titleText;
|
||||||
document.getElementById('webtitle').textContent = title[1];
|
document.getElementById('webtitle').textContent = titleText;
|
||||||
document.getElementById('webtitle_header').textContent = title[1];
|
document.getElementById('webtitle_header').textContent = titleText;
|
||||||
});
|
}, "text");
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@@ -73,7 +74,13 @@ function refresh_show(){
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<h1><a href="https://anywavecom.net/" class="logo-bg" title="Anywave Broadcast Inc."></a></h1>
|
<h1><a href="https://anywavecom.net/" class="logo-bg" title="Anywave Broadcast Inc."></a></h1>
|
||||||
<h2 id="webtitle_header"><span></span></h2>
|
<h2 id="webtitle_header"><span>Digital Exciter</span></h2>
|
||||||
|
|
||||||
|
<h3 class="has-toggle" style="margin-left:auto"><div class="utility-cluster"><button class="theme-toggle" onclick="toggleTheme()" aria-label="Switch color theme" title="Switch color theme">
|
||||||
|
<span class="tt-sun" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg></span>
|
||||||
|
<span class="tt-moon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg></span>
|
||||||
|
<span class="tt-label"><span class="tt-to-light">Light Mode</span><span class="tt-to-dark">Dark Mode</span></span>
|
||||||
|
</button></div></h3>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main class="app-main">
|
<main class="app-main">
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Executable → Regular
+146
-62
@@ -4,6 +4,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title id="webtitle"></title>
|
<title id="webtitle"></title>
|
||||||
|
<script src="js/theme.js"></script>
|
||||||
<link href="css/styles.css" rel="stylesheet" media="screen">
|
<link href="css/styles.css" rel="stylesheet" media="screen">
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
<script src="js/jquery.js"></script>
|
<script src="js/jquery.js"></script>
|
||||||
@@ -41,7 +42,19 @@ function get_remote_local_ctrl() {
|
|||||||
var table = data.split("|");
|
var table = data.split("|");
|
||||||
// Logic for remote/local control if needed
|
// Logic for remote/local control if needed
|
||||||
// g_remot_local_ctrl = Number(table[0].split("#")[5]);
|
// g_remot_local_ctrl = Number(table[0].split("#")[5]);
|
||||||
document.title = table[0].split("#")[11];
|
/* P-4 ROOT CAUSE + the better fix (2026-08-06).
|
||||||
|
What was here: document.title = table[0].split("#")[11]; — copied from the
|
||||||
|
1.0 BY VALUE, index and all (the same copy-by-value class as C1's
|
||||||
|
INPUT_SOURCES). The 1.0's sys_cfg carries five NTP fields after ctl so its
|
||||||
|
title sits at index 11; smod's sys_cfg has no NTP fields there, so index 11
|
||||||
|
is ab_mode — the FSSI Refresh switch. The tab was displaying the FSSI switch
|
||||||
|
state: "0" or "1", flipping as FSSI was toggled (owner, 2026-08-06).
|
||||||
|
Why the line is DELETED rather than re-indexed: this page already has a
|
||||||
|
correct, authoritative title source — the dedicated web_title section polled
|
||||||
|
in SetFormDefaults below (table[7]). Two writers for one value is the defect,
|
||||||
|
not the index; re-indexing would have left a second writer racing the first
|
||||||
|
(and is exactly what C1 removed on the input-source axis). One source of
|
||||||
|
truth: the web_title section. This function now only does what its name says. */
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +64,11 @@ function get_remote_local_ctrl() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function SetFormDefaults() {
|
function SetFormDefaults() {
|
||||||
|
// R-2 (2026-08-05): NaN guards — in-tree pattern from sys_spectrum.html. Readbacks
|
||||||
|
// show "--" for absent data; finite values format as before.
|
||||||
|
function fmtScaled(v, scale, dp){ var n = Number(v); return isFinite(n) ? (n*scale).toFixed(dp) : "--"; }
|
||||||
|
function fmtRaw(v){ var n = Number(v); return isFinite(n) ? n : "--"; }
|
||||||
|
|
||||||
var queryData = "State_SysState=&tuner_status=&pac_status=&gps_status=&State_SysVer=&mac_sn_status=&sys_rf=&web_title=&"
|
var queryData = "State_SysState=&tuner_status=&pac_status=&gps_status=&State_SysVer=&mac_sn_status=&sys_rf=&web_title=&"
|
||||||
$.get("atsc_status_cfg.cgi?"+queryData, function(data, status) {
|
$.get("atsc_status_cfg.cgi?"+queryData, function(data, status) {
|
||||||
var temp_unit=1;
|
var temp_unit=1;
|
||||||
@@ -63,10 +81,23 @@ function SetFormDefaults() {
|
|||||||
var ab_modenum = Number(form[25]);
|
var ab_modenum = Number(form[25]);
|
||||||
temp_unit = Number(form[26]);
|
temp_unit = Number(form[26]);
|
||||||
|
|
||||||
// Input Source Map
|
// Input Source — C1 (2026-08-05): the page-private ternary is DELETED; this and the
|
||||||
var i = Number(form[1]);
|
// dashboard cards read the ONE map via DashboardHelpers.getInputSource (the two lists
|
||||||
var s_str = (i==0)?"ASI1":(i==1)?"ASI2":(i==2)?"EAS":(i==3)?"Tuner":(i==4)?"TSoIP":(i==5)?"SMPTE":"None";
|
// disagreed at index 5 — SMPTE vs None — which is your "None for a selected input").
|
||||||
cf.asi_input.value = s_str;
|
cf.asi_input.value = window.DashboardHelpers.getInputSource(form[1]);
|
||||||
|
|
||||||
|
/* REG-2 / ST.F11 port (2026-08-05, from the 1.0): auto-expand Tuner Status on
|
||||||
|
ENTERING tuner/translator mode — smod IS a translator, so this is its primary
|
||||||
|
mode. Same "changed" semantics as the 1.0's adaptation of 3.0's publishMode:
|
||||||
|
expand only on the transition INTO Tuner input (form[1]===3, same index here,
|
||||||
|
proven by this page's own map), tracked by _stTunerMode — NEVER on every poll,
|
||||||
|
so an operator's manual collapse is not fought. Box-headers exist by now
|
||||||
|
(initCollapsibleTables runs at DOMContentLoaded, before this callback). */
|
||||||
|
(function(){
|
||||||
|
var inTuner = (Number(form[1]) === 3);
|
||||||
|
if (inTuner && window._stTunerMode !== true) { autoExpandTunerStatus(); }
|
||||||
|
window._stTunerMode = inTuner;
|
||||||
|
})();
|
||||||
|
|
||||||
// Input Status
|
// Input Status
|
||||||
cf.asi_status.value = (Number(form[2])==0) ? "OK" : "Err";
|
cf.asi_status.value = (Number(form[2])==0) ? "OK" : "Err";
|
||||||
@@ -76,12 +107,15 @@ function SetFormDefaults() {
|
|||||||
|
|
||||||
cf.ts_rate.value = form[4]; // TS Rate
|
cf.ts_rate.value = form[4]; // TS Rate
|
||||||
|
|
||||||
// Temperature
|
// Temperature — R-2: absent data -> "--", never NaN cascades
|
||||||
if(temp_unit==0){
|
var t_raw = Number(form[5]);
|
||||||
|
if(!isFinite(t_raw)){
|
||||||
|
cf.temp1.value = "--"; cf.temp1_c.value = "--";
|
||||||
|
} else if(temp_unit==0){
|
||||||
cf.temp1.value = form[5];
|
cf.temp1.value = form[5];
|
||||||
cf.temp1_c.value = ((cf.temp1.value - 32)/1.8).toFixed(2);
|
cf.temp1_c.value = ((t_raw - 32)/1.8).toFixed(2);
|
||||||
} else {
|
} else {
|
||||||
cf.temp1.value = (form[5]*1.8+32).toFixed(2);
|
cf.temp1.value = (t_raw*1.8+32).toFixed(2);
|
||||||
cf.temp1_c.value = form[5];
|
cf.temp1_c.value = form[5];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,27 +136,27 @@ function SetFormDefaults() {
|
|||||||
|
|
||||||
// Offset
|
// Offset
|
||||||
i = Number(form[8]);
|
i = Number(form[8]);
|
||||||
s_str = (i==0) ? "+" : "-";
|
var s_str = (i==0) ? "+" : "-"; // scratch var — its declaration moved here when C1 deleted the input-source ternary that used to declare it
|
||||||
cf.hz1.value = s_str + form[9];
|
cf.hz1.value = s_str + form[9];
|
||||||
|
|
||||||
// FWD Power & SNR
|
// FWD Power & SNR
|
||||||
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-FSSI" : (rawFwd + " %");
|
||||||
|
|
||||||
// SNR. Half-dB from the app, shown as-is (PR12 removed the old fixed
|
// SNR. Half-dB from the app, shown as-is (PR12 removed the old fixed
|
||||||
// +10 dB display offset; this is the true measured MER).
|
// +10 dB display offset; this is the true measured MER).
|
||||||
var tmpdata = Number(form[11]);
|
var tmpdata = Number(form[11]);
|
||||||
var snrStr = (tmpdata/2).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-FSSI" : (tmpdata<=0 ? "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.
|
||||||
// (Do not Math.round() the sum and then paste on the half: for an odd
|
// (Do not Math.round() the sum and then paste on the half: for an odd
|
||||||
// half-dB value that rounds x.5 up and reports 1 dB too much.)
|
// half-dB value that rounds x.5 up and reports 1 dB too much.)
|
||||||
tmpdata = Number(form[12]);
|
tmpdata = Number(form[12]);
|
||||||
cf.l_imd.value = (ab_modenum==1) ? "N/A" : (tmpdata/2 + 11).toFixed(1);
|
cf.l_imd.value = (ab_modenum==1) ? "N/A-FSSI" : (tmpdata<=0 ? "N/A" : (tmpdata/2 + 11).toFixed(1));
|
||||||
|
|
||||||
tmpdata = Number(form[13]);
|
tmpdata = Number(form[13]);
|
||||||
cf.u_imd.value = (ab_modenum==1) ? "N/A" : (tmpdata/2 + 11).toFixed(1);
|
cf.u_imd.value = (ab_modenum==1) ? "N/A-FSSI" : (tmpdata<=0 ? "N/A" : (tmpdata/2 + 11).toFixed(1));
|
||||||
|
|
||||||
// Control & Amp
|
// Control & Amp
|
||||||
cf.f1ctrl.value = (Number(form[14])==0) ? "Remote" : "Local";
|
cf.f1ctrl.value = (Number(form[14])==0) ? "Remote" : "Local";
|
||||||
@@ -143,6 +177,13 @@ function SetFormDefaults() {
|
|||||||
cf.b_fssi.value = (ab_modenum==1) ? (Number(form[17]) + " %") : "N/A";
|
cf.b_fssi.value = (ab_modenum==1) ? (Number(form[17]) + " %") : "N/A";
|
||||||
|
|
||||||
// AGC State
|
// AGC State
|
||||||
|
/* FSSI active alternates the RFinA feedback path AGC realizes on,
|
||||||
|
so AGC state (and its error codes) are not meaningful — show the
|
||||||
|
reason instead of a misleading adjustment value. Takes priority.
|
||||||
|
(Ported from the 1.0, status_info.html:239-243 — FSSI parity round.) */
|
||||||
|
if(ab_modenum==1){
|
||||||
|
s_str = "N/A-FSSI";
|
||||||
|
} else {
|
||||||
agc_err_code = Number(form[27]);
|
agc_err_code = Number(form[27]);
|
||||||
var agc_adj = Number(form[28]);
|
var agc_adj = Number(form[28]);
|
||||||
if(agc_err_code==1) s_str="Down Limit!";
|
if(agc_err_code==1) s_str="Down Limit!";
|
||||||
@@ -154,6 +195,7 @@ function SetFormDefaults() {
|
|||||||
s_str="OK";
|
s_str="OK";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
cf.agc_state.value = s_str;
|
cf.agc_state.value = s_str;
|
||||||
|
|
||||||
// --- 2. PARSE SYS_RF FOR DASHBOARD ---
|
// --- 2. PARSE SYS_RF FOR DASHBOARD ---
|
||||||
@@ -191,37 +233,40 @@ function SetFormDefaults() {
|
|||||||
// Form 1: Tuner
|
// Form 1: Tuner
|
||||||
var form1 = table[1].split("#");
|
var form1 = table[1].split("#");
|
||||||
var cf1 = document.getElementById("tuner");
|
var cf1 = document.getElementById("tuner");
|
||||||
cf1.rx_freq.value = Number(form1[1]);
|
cf1.rx_freq.value = fmtRaw(form1[1]);
|
||||||
cf1.rssi.value = Number(form1[2]);
|
cf1.rssi.value = fmtRaw(form1[2]);
|
||||||
cf1.rsnr.value = Number(form1[3]);
|
cf1.rsnr.value = fmtRaw(form1[3]);
|
||||||
cf1.lock.selectedIndex = Number(form1[4]);
|
cf1.lock.selectedIndex = Number(form1[4]);
|
||||||
|
|
||||||
// Form 2: PAC Status
|
// Form 2: PAC Status
|
||||||
var form2 = table[2].split("#");
|
var form2 = table[2].split("#");
|
||||||
var cf2 = document.getElementById("pac_status");
|
var cf2 = document.getElementById("pac_status");
|
||||||
cf2.v9v.value = (Number(form2[1])*0.1).toFixed(2);
|
cf2.v9v.value = fmtScaled(form2[1], 0.1, 2);
|
||||||
cf2.v12v.value = (Number(form2[2])*0.1).toFixed(2);
|
cf2.v12v.value = fmtScaled(form2[2], 0.1, 2);
|
||||||
cf2.v50v.value = (Number(form2[3])*0.1).toFixed(2);
|
cf2.v50v.value = fmtScaled(form2[3], 0.1, 2);
|
||||||
cf2.pac_fwd.value = (Number(form2[4])*0.1).toFixed(2);
|
cf2.pac_fwd.value = fmtScaled(form2[4], 0.1, 2);
|
||||||
cf2.ref.value = (Number(form2[5])*0.1).toFixed(2);
|
cf2.ref.value = fmtScaled(form2[5], 0.1, 2);
|
||||||
cf2.vswr.value = (Number(form2[6])*0.1).toFixed(2);
|
cf2.vswr.value = fmtScaled(form2[6], 0.1, 2);
|
||||||
|
|
||||||
// PAC Temp
|
// PAC Temp — R-2 guarded
|
||||||
if(temp_unit==0){
|
var pac_t = Number(form2[7]);
|
||||||
cf2.temp.value = (Number(form2[7])*0.1).toFixed(2);
|
if(!isFinite(pac_t)){
|
||||||
|
cf2.temp.value = "--"; cf2.temp_c.value = "--";
|
||||||
|
} else if(temp_unit==0){
|
||||||
|
cf2.temp.value = (pac_t*0.1).toFixed(2);
|
||||||
cf2.temp_c.value = (cf2.temp.value < 32) ? 0 : ((cf2.temp.value - 32)/1.8).toFixed(2);
|
cf2.temp_c.value = (cf2.temp.value < 32) ? 0 : ((cf2.temp.value - 32)/1.8).toFixed(2);
|
||||||
} else {
|
} else {
|
||||||
cf2.temp.value = (Number(form2[7])*0.1*1.8+32).toFixed(2);
|
cf2.temp.value = (pac_t*0.1*1.8+32).toFixed(2);
|
||||||
cf2.temp_c.value = (Number(form2[7])*0.1).toFixed(2);
|
cf2.temp_c.value = (pac_t*0.1).toFixed(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
cf2.pain.value = Number(form2[8]); // This is PA_IN
|
cf2.pain.value = Number(form2[8]); // This is PA_IN
|
||||||
|
|
||||||
cf2.c50v1.value = (Number(form2[9])*0.1).toFixed(2);
|
cf2.c50v1.value = fmtScaled(form2[9], 0.1, 2);
|
||||||
cf2.c50v2.value = (Number(form2[10])*0.1).toFixed(2);
|
cf2.c50v2.value = fmtScaled(form2[10], 0.1, 2);
|
||||||
cf2.c50v3.value = (Number(form2[11])*0.1).toFixed(2);
|
cf2.c50v3.value = fmtScaled(form2[11], 0.1, 2);
|
||||||
cf2.c50v4.value = (Number(form2[12])*0.1).toFixed(2);
|
cf2.c50v4.value = fmtScaled(form2[12], 0.1, 2);
|
||||||
cf2.gv.value = (Number(form2[13])*0.1).toFixed(2);
|
cf2.gv.value = fmtScaled(form2[13], 0.1, 2);
|
||||||
cf2.lvl.selectedIndex = Number(form2[14]);
|
cf2.lvl.selectedIndex = Number(form2[14]);
|
||||||
|
|
||||||
// Form 3: GPS
|
// Form 3: GPS
|
||||||
@@ -252,8 +297,11 @@ function SetFormDefaults() {
|
|||||||
if (table[7]) {
|
if (table[7]) {
|
||||||
var title = table[7].split("#");
|
var title = table[7].split("#");
|
||||||
var titleStr = (title[1] != null) ? title[1] : "";
|
var titleStr = (title[1] != null) ? title[1] : "";
|
||||||
document.getElementById('webtitle').textContent = titleStr;
|
/* P-4 guard: never paint a short/absent read over a good title (R-2 class). */
|
||||||
document.getElementById('webtitle_header').textContent = titleStr;
|
if (titleStr !== "") {
|
||||||
|
document.getElementById('webtitle').textContent = titleStr;
|
||||||
|
document.getElementById('webtitle_header').textContent = titleStr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update Status Colors and UI helpers
|
// Update Status Colors and UI helpers
|
||||||
@@ -330,14 +378,15 @@ 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') {
|
/* v.indexOf('N/A') !== 0 excludes both "N/A" and "N/A-FSSI" (FSSI-active) — 1.0 :413 pattern */
|
||||||
|
if (id === 'tsnr' && v !== '' && v.indexOf('N/A') !== 0) {
|
||||||
var snrNum = parseFloat(v); // true measured dB (PR12: no display offset)
|
var snrNum = parseFloat(v); // true measured dB (PR12: no display offset)
|
||||||
if (snrNum < 15) { 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.indexOf('N/A') !== 0) {
|
||||||
if (parseFloat(v) < 47) { tr.classList.add('row-has-error'); break; }
|
if (parseFloat(v) < 47) { tr.classList.add('row-has-error'); break; }
|
||||||
}
|
}
|
||||||
if (id === 'u_imd' && v !== '' && v !== 'N/A') {
|
if (id === 'u_imd' && v !== '' && v.indexOf('N/A') !== 0) {
|
||||||
if (parseFloat(v) < 47) { tr.classList.add('row-has-error'); break; }
|
if (parseFloat(v) < 47) { tr.classList.add('row-has-error'); break; }
|
||||||
}
|
}
|
||||||
// License fields: INACTIVE is an error
|
// License fields: INACTIVE is an error
|
||||||
@@ -381,7 +430,7 @@ 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.indexOf("N/A") !== 0) {
|
||||||
var snrVal = parseFloat(tsnrEl.value); // true measured dB (PR12: no display offset)
|
var snrVal = parseFloat(tsnrEl.value); // true measured dB (PR12: no display offset)
|
||||||
if(snrVal >= 27) tsnrEl.style.color = color_ok;
|
if(snrVal >= 27) tsnrEl.style.color = color_ok;
|
||||||
else if(snrVal >= 20) tsnrEl.style.color = color_warn;
|
else if(snrVal >= 20) tsnrEl.style.color = color_warn;
|
||||||
@@ -389,12 +438,12 @@ function updateStatusColors() {
|
|||||||
} else if(tsnrEl) tsnrEl.style.color = "";
|
} else if(tsnrEl) tsnrEl.style.color = "";
|
||||||
|
|
||||||
var limdEl = document.getElementById("l_imd");
|
var limdEl = document.getElementById("l_imd");
|
||||||
if(limdEl && limdEl.value !== "" && limdEl.value !== "N/A") {
|
if(limdEl && limdEl.value !== "" && limdEl.value.indexOf("N/A") !== 0) {
|
||||||
limdEl.style.color = parseFloat(limdEl.value) >= 47 ? color_ok : color_err;
|
limdEl.style.color = parseFloat(limdEl.value) >= 47 ? color_ok : color_err;
|
||||||
} else if(limdEl) limdEl.style.color = "";
|
} else if(limdEl) limdEl.style.color = "";
|
||||||
|
|
||||||
var uimdEl = document.getElementById("u_imd");
|
var uimdEl = document.getElementById("u_imd");
|
||||||
if(uimdEl && uimdEl.value !== "" && uimdEl.value !== "N/A") {
|
if(uimdEl && uimdEl.value !== "" && uimdEl.value.indexOf("N/A") !== 0) {
|
||||||
uimdEl.style.color = parseFloat(uimdEl.value) >= 47 ? color_ok : color_err;
|
uimdEl.style.color = parseFloat(uimdEl.value) >= 47 ? color_ok : color_err;
|
||||||
} else if(uimdEl) uimdEl.style.color = "";
|
} else if(uimdEl) uimdEl.style.color = "";
|
||||||
|
|
||||||
@@ -599,7 +648,28 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Collapsible Tables - MAIN STATUS, PAC STATUS, GPS STATUS
|
/* ST.F11 support (2026-08-05): expand the Tuner Status box if collapsed. Called only on
|
||||||
|
the transition into tuner/translator mode by the data callback — see the port note there. */
|
||||||
|
function autoExpandTunerStatus() {
|
||||||
|
var headers = document.querySelectorAll('.box-header');
|
||||||
|
for (var i = 0; i < headers.length; i++) {
|
||||||
|
var titleSpan = headers[i].querySelector('span');
|
||||||
|
if (titleSpan && titleSpan.innerText.indexOf('Tuner Status') !== -1) {
|
||||||
|
if (headers[i].classList.contains('collapsed')) {
|
||||||
|
headers[i].classList.remove('collapsed');
|
||||||
|
var content = headers[i].nextElementSibling;
|
||||||
|
if (content && content.className.indexOf('box-content') !== -1) {
|
||||||
|
content.style.display = 'block';
|
||||||
|
}
|
||||||
|
var badge = headers[i].querySelector('.toggle-text');
|
||||||
|
if (badge) badge.innerText = 'Hide Details';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collapsible Tables - MAIN STATUS, TUNER STATUS, PAC STATUS, GPS STATUS
|
||||||
function initCollapsibleTables() {
|
function initCollapsibleTables() {
|
||||||
var headers = document.querySelectorAll('.app-main h2');
|
var headers = document.querySelectorAll('.app-main h2');
|
||||||
for (var i = 0; i < headers.length; i++) {
|
for (var i = 0; i < headers.length; i++) {
|
||||||
@@ -621,20 +691,26 @@ function initCollapsibleTables() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (box) {
|
if (box) {
|
||||||
|
/* REG-1 (2026-08-05, 1.0 C.2 port — "3.0 parity"): the primary status block
|
||||||
|
is OPEN on load so the operator sees it without a click; every other
|
||||||
|
section starts collapsed. Tuner Status is additionally auto-expanded on
|
||||||
|
entering translator mode by the data callback (ST.F11) — it cannot be
|
||||||
|
decided here because the mode is not yet read at init. */
|
||||||
|
var startOpen = (headerText === "Main Status");
|
||||||
var boxHeader = document.createElement('div');
|
var boxHeader = document.createElement('div');
|
||||||
boxHeader.className = "box-header collapsed";
|
boxHeader.className = startOpen ? "box-header" : "box-header collapsed";
|
||||||
var titleSpan = document.createElement('span');
|
var titleSpan = document.createElement('span');
|
||||||
titleSpan.innerText = headerText + " Details";
|
titleSpan.innerText = headerText + " Details";
|
||||||
var toggleSpan = document.createElement('span');
|
var toggleSpan = document.createElement('span');
|
||||||
toggleSpan.className = "toggle-text";
|
toggleSpan.className = "toggle-text";
|
||||||
toggleSpan.innerText = "View Details";
|
toggleSpan.innerText = startOpen ? "Hide Details" : "View Details";
|
||||||
|
|
||||||
boxHeader.appendChild(titleSpan);
|
boxHeader.appendChild(titleSpan);
|
||||||
boxHeader.appendChild(toggleSpan);
|
boxHeader.appendChild(toggleSpan);
|
||||||
|
|
||||||
var contentDiv = document.createElement('div');
|
var contentDiv = document.createElement('div');
|
||||||
contentDiv.className = "box-content";
|
contentDiv.className = "box-content";
|
||||||
contentDiv.style.display = "none";
|
contentDiv.style.display = startOpen ? "block" : "none";
|
||||||
|
|
||||||
while (box.firstChild) {
|
while (box.firstChild) {
|
||||||
contentDiv.appendChild(box.firstChild);
|
contentDiv.appendChild(box.firstChild);
|
||||||
@@ -683,17 +759,17 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body onload="refresh_show()">
|
|
||||||
<body onload="refresh_show()">
|
<body onload="refresh_show()">
|
||||||
<div class="app-layout">
|
<div class="app-layout">
|
||||||
<aside class="app-sidebar">
|
<aside class="app-sidebar">
|
||||||
<ul class="items">
|
<ul class="items">
|
||||||
|
<li><a href="overview.html" title="Overview">Overview</a></li>
|
||||||
<li class="active"><a href="status_info.html" title="Status">Status</a></li>
|
<li class="active"><a href="status_info.html" title="Status">Status</a></li>
|
||||||
<li><a href="sys_set.html" title="Settings">Settings</a></li>
|
<li><a href="sys_set.html" title="Settings">Settings</a></li>
|
||||||
<li><a href="sys_rf.html" title="RF Control">RF Control</a></li>
|
<li><a href="sys_rf.html" title="RF Control">RF Control</a></li>
|
||||||
<li><a href="sys_psip.html" title="TSID&PSIP">TSID&PSIP</a></li>
|
<li><a href="sys_psip.html" title="TSID&PSIP">TSID&PSIP</a></li>
|
||||||
<li><a href="sys_ts.html" title="TSoIP">TSoIP</a></li>
|
<li><a href="sys_ts.html" title="TSoIP">TSoIP</a></li>
|
||||||
<li><a href="sys_spectrum.html" title="Spectrum">Spectrum</a></li>
|
<li><a href="sys_spectrum.html" title="RF Performance">RF Performance</a></li>
|
||||||
<li><a href="log_output.html" title="Log">Log</a></li>
|
<li><a href="log_output.html" title="Log">Log</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="sidebar-footer">
|
<div class="sidebar-footer">
|
||||||
@@ -713,8 +789,15 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
</button>
|
</button>
|
||||||
<h1><a href="https://anywavecom.net/" class="logo-bg" title="Anywave Broadcast Inc."></a></h1>
|
<h1><a href="https://anywavecom.net/" class="logo-bg" title="Anywave Broadcast Inc."></a></h1>
|
||||||
<h2 id="webtitle_header"><span></span></h2>
|
<h2 id="webtitle_header"><span></span></h2>
|
||||||
<h3>
|
<h3 class="has-toggle">
|
||||||
<div class="dropdown">
|
<div class="utility-cluster">
|
||||||
|
<button class="theme-toggle" onclick="toggleTheme()" aria-label="Switch color theme" title="Switch color theme">
|
||||||
|
<span class="tt-sun" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg></span>
|
||||||
|
<span class="tt-moon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg></span>
|
||||||
|
<span class="tt-label"><span class="tt-to-light">Light Mode</span><span class="tt-to-dark">Dark Mode</span></span>
|
||||||
|
</button>
|
||||||
|
<span class="utility-divider" aria-hidden="true"></span>
|
||||||
|
<div class="dropdown">
|
||||||
<button class="dropbtn-modern">Admin ▾</button>
|
<button class="dropbtn-modern">Admin ▾</button>
|
||||||
<div class="dropdown-content">
|
<div class="dropdown-content">
|
||||||
<a href="#" onclick="openAccountModal(); return false;">
|
<a href="#" onclick="openAccountModal(); return false;">
|
||||||
@@ -734,7 +817,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</h3>
|
</div>
|
||||||
|
</h3>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Fixed Dashboard Summary - Top of page, under header -->
|
<!-- Fixed Dashboard Summary - Top of page, under header -->
|
||||||
@@ -760,16 +844,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
<span class="summary-unit">dB</span>
|
<span class="summary-unit">dB</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="summary-card">
|
|
||||||
<div class="summary-label-row">
|
|
||||||
<span class="status-dot status-ok" id="dot_uimd" aria-hidden="true"></span>
|
|
||||||
<span class="summary-label">UIMD</span>
|
|
||||||
</div>
|
|
||||||
<div class="summary-value-wrapper">
|
|
||||||
<span class="summary-value value-ok" id="sum_uimd">--</span>
|
|
||||||
<span class="summary-unit">dBc</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="summary-card">
|
<div class="summary-card">
|
||||||
<div class="summary-label-row">
|
<div class="summary-label-row">
|
||||||
<span class="status-dot status-ok" id="dot_limd" aria-hidden="true"></span>
|
<span class="status-dot status-ok" id="dot_limd" aria-hidden="true"></span>
|
||||||
@@ -780,6 +854,16 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
<span class="summary-unit">dBc</span>
|
<span class="summary-unit">dBc</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="summary-card">
|
||||||
|
<div class="summary-label-row">
|
||||||
|
<span class="status-dot status-ok" id="dot_uimd" aria-hidden="true"></span>
|
||||||
|
<span class="summary-label">UIMD</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-value-wrapper">
|
||||||
|
<span class="summary-value value-ok" id="sum_uimd">--</span>
|
||||||
|
<span class="summary-unit">dBc</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="summary-card">
|
<div class="summary-card">
|
||||||
<div class="summary-label-row">
|
<div class="summary-label-row">
|
||||||
<span class="status-dot status-off" id="dot_rf" aria-hidden="true"></span>
|
<span class="status-dot status-off" id="dot_rf" aria-hidden="true"></span>
|
||||||
|
|||||||
+120
-18
@@ -4,7 +4,8 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title id="webtitle"></title>
|
<title id="webtitle"></title>
|
||||||
<link href="css/styles.css" rel="stylesheet" media="screen">
|
<script src="js/theme.js"></script>
|
||||||
|
<link href="css/styles.css" rel="stylesheet" media="screen">
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
<script src="js/jquery.js"></script>
|
<script src="js/jquery.js"></script>
|
||||||
<script src="js/dashboard.js"></script>
|
<script src="js/dashboard.js"></script>
|
||||||
@@ -57,10 +58,99 @@
|
|||||||
|
|
||||||
var table = data.split("|");
|
var table = data.split("|");
|
||||||
g_remot_local_ctrl = Number(table[0].split("#")[5]);
|
g_remot_local_ctrl = Number(table[0].split("#")[5]);
|
||||||
document.title = table[0].split("#")[11];
|
/* P-4b (2026-08-06): the title write here — document.title =
|
||||||
|
table[0].split("#")[11] — is DELETED. On smod index 11 is ab_mode
|
||||||
|
(the FSSI Refresh switch), not the web title. This page already sets
|
||||||
|
the title from its web_title section below. Round 3 fixed only
|
||||||
|
status_info; this is the rest of that class. */
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* REG-4 (2026-08-05) — CTRL gate, ported from the 1.0 (its C.5, 3.0 SLT.7 parity; the
|
||||||
|
owner-found DESTRUCTIVE class: saving TSID/program rows while CTRL != Enable writes
|
||||||
|
non-effective values the operator believes took). When CTRL (ts_edit) != Enable(1),
|
||||||
|
the TSID save and every program-row save are disabled. The CTRL row's own Set
|
||||||
|
(#ts_ctrl_set) stays live so CTRL can be changed (CTRL+TSID share one post here, as
|
||||||
|
on the 1.0 — changing CTRL via that button is the intended path). No-op in Local
|
||||||
|
mode, where SetFormDefaults has already disabled everything. Safe on load and on
|
||||||
|
every ts_edit change. */
|
||||||
|
/* P-6 (2026-08-06, CORRECTED after owner review). What the licence gate must do —
|
||||||
|
matched to what the 1.0 (sys_psip.html:157-168) and the 3.0 actually do, and to the
|
||||||
|
owner's ruling "no licence should STILL ALLOW Clear":
|
||||||
|
|
||||||
|
no PSIP licence -> remove ONLY the "Enable" choice from ts_edit.
|
||||||
|
Disable(0) and Clear(2) stay fully usable; every Set stays live; NO grey-out,
|
||||||
|
NO banner. The operator can still disable or clear PSIP without a licence; they
|
||||||
|
simply cannot ENABLE it.
|
||||||
|
|
||||||
|
The prior version of this function was the reverse of that — it forced the value off
|
||||||
|
Enable, disabled both Sets and the whole program table, and showed a banner. That
|
||||||
|
locked out Clear, which is exactly backwards. Corrected here.
|
||||||
|
|
||||||
|
R4 (2026-08-11) — THE PREVIOUS COMMENT HERE WAS WRONG AND IS CORRECTED.
|
||||||
|
It claimed the 1.0's $('option').hide() "browsers do not honour reliably", promoted
|
||||||
|
from an ambiguous field report. Retrieval settled it: the 1.0's TSoIP page has NO
|
||||||
|
licence handling at all (zero references in sys_tsoip.html), so the option the owner
|
||||||
|
saw selectable cannot have been a 1.0 TSoIP gate; and smod had no gate whatsoever
|
||||||
|
before round 3, which explains "selectable on both" on its own. The owner then
|
||||||
|
confirmed the 1.0's Enable option DISAPPEARS when unlicensed — i.e. hiding works.
|
||||||
|
Owner ruling 2026-08-11: the option must be HIDDEN, not shown-but-unselectable.
|
||||||
|
|
||||||
|
So the gate now hides AND disables: hidden removes it from the list (1.0 parity, and
|
||||||
|
what the owner sees), disabled is the belt for any engine that renders a hidden
|
||||||
|
option anyway. Disable(0) and Clear(2) and every Set remain untouched. */
|
||||||
|
var g_psip_licensed = null; // null = not yet known; never gate on an unknown
|
||||||
|
function applyLicenceGate(){
|
||||||
|
var edit = document.getElementById("ts_edit");
|
||||||
|
if (!edit) return;
|
||||||
|
var enableOpt = edit.querySelector('option[value="1"]');
|
||||||
|
if (!enableOpt) return;
|
||||||
|
if (g_psip_licensed === false) {
|
||||||
|
enableOpt.hidden = true; // out of the list entirely (owner ruling)
|
||||||
|
enableOpt.style.display = "none"; // same effect for engines ignoring [hidden]
|
||||||
|
enableOpt.disabled = true; // belt: unselectable even if rendered
|
||||||
|
if (Number(edit.value) === 1) { edit.value = "0"; } // if it was on Enable, fall back to Disable
|
||||||
|
} else {
|
||||||
|
enableOpt.hidden = false; // licensed or unknown -> Enable available
|
||||||
|
enableOpt.style.display = "";
|
||||||
|
enableOpt.disabled = false;
|
||||||
|
}
|
||||||
|
// Disable/Clear and every Set are deliberately left ALONE — Clear must work unlicensed.
|
||||||
|
}
|
||||||
|
function get_licence(){
|
||||||
|
$.get("atsc_status_cfg.cgi?sn_license=&", function(data, status){
|
||||||
|
if (status !== "success" || !data) return; // unknown -> no gate
|
||||||
|
var licence_para = data.split("|")[0].split("#");
|
||||||
|
var raw = licence_para[2];
|
||||||
|
var key_psip = Number(raw);
|
||||||
|
/* Fail-open on a garbled read (round-1 bench caught the fail-closed version): only a
|
||||||
|
clean 0/1 is a licence answer; anything else stays unknown and ungated. */
|
||||||
|
if (raw === undefined || raw === "" || isNaN(key_psip)) return;
|
||||||
|
g_psip_licensed = (key_psip === 1);
|
||||||
|
applyLicenceGate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyCtrlGate() {
|
||||||
|
if (g_remot_local_ctrl == 1) return; // Local: all already disabled
|
||||||
|
var edit = document.getElementById("ts_edit");
|
||||||
|
var enabled = edit && Number(edit.value) === 1; // 1 = Enable
|
||||||
|
if (enabled) return; // Enable: normal Set state stands
|
||||||
|
// Not Enable -> block dependent saves; leave #ts_ctrl_set live.
|
||||||
|
var tsidBtn = document.getElementById("ts_tsid_set");
|
||||||
|
if (tsidBtn) { tsidBtn.disabled = true; tsidBtn.value = "Set"; }
|
||||||
|
$('#psip_table_form input[type="button"]').prop("disabled", true).val("Set");
|
||||||
|
}
|
||||||
|
// REG-4: re-gate the moment the operator changes CTRL in the form (registered once)
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
var tsEditEl = document.getElementById('ts_edit');
|
||||||
|
if (tsEditEl) tsEditEl.addEventListener('change', function () {
|
||||||
|
applyCtrlGate();
|
||||||
|
applyLicenceGate(); // P-6: licence outranks CTRL — re-assert on every change
|
||||||
|
});
|
||||||
|
get_licence(); // P-6: read the licence once, gate when it answers
|
||||||
|
});
|
||||||
|
|
||||||
// Updated SetFormDefaults to populate the Single Form
|
// Updated SetFormDefaults to populate the Single Form
|
||||||
function SetFormDefaults() {
|
function SetFormDefaults() {
|
||||||
// 1. DISABLE BUTTONS START (only in Remote mode)
|
// 1. DISABLE BUTTONS START (only in Remote mode)
|
||||||
@@ -133,6 +223,9 @@ function SetFormDefaults() {
|
|||||||
}
|
}
|
||||||
if (typeof setTsidFormSnapshotUpdate === 'function') setTsidFormSnapshotUpdate();
|
if (typeof setTsidFormSnapshotUpdate === 'function') setTsidFormSnapshotUpdate();
|
||||||
if (typeof psipFormSnapshotUpdate === 'function') psipFormSnapshotUpdate();
|
if (typeof psipFormSnapshotUpdate === 'function') psipFormSnapshotUpdate();
|
||||||
|
// REG-4: gate dependent saves whenever fresh CTRL state lands (after the re-enable above)
|
||||||
|
applyCtrlGate();
|
||||||
|
applyLicenceGate(); // P-6: licence gate outranks CTRL gate; re-assert after every repaint
|
||||||
|
|
||||||
document.getElementById('webtitle').textContent = title[1];
|
document.getElementById('webtitle').textContent = title[1];
|
||||||
document.getElementById('webtitle_header').textContent = title[1];
|
document.getElementById('webtitle_header').textContent = title[1];
|
||||||
@@ -474,12 +567,13 @@ function cmd_confirm() {
|
|||||||
<div class="app-layout">
|
<div class="app-layout">
|
||||||
<aside class="app-sidebar">
|
<aside class="app-sidebar">
|
||||||
<ul class="items">
|
<ul class="items">
|
||||||
|
<li><a href="overview.html" title="Overview">Overview</a></li>
|
||||||
<li><a href="status_info.html" title="Status">Status</a></li>
|
<li><a href="status_info.html" title="Status">Status</a></li>
|
||||||
<li><a href="sys_set.html" title="Settings">Settings</a></li>
|
<li><a href="sys_set.html" title="Settings">Settings</a></li>
|
||||||
<li><a href="sys_rf.html" title="RF Control">RF Control</a></li>
|
<li><a href="sys_rf.html" title="RF Control">RF Control</a></li>
|
||||||
<li class="active"><a href="sys_psip.html" title="TSID&PSIP">TSID&PSIP</a></li>
|
<li class="active"><a href="sys_psip.html" title="TSID&PSIP">TSID&PSIP</a></li>
|
||||||
<li><a href="sys_ts.html" title="TSoIP">TSoIP</a></li>
|
<li><a href="sys_ts.html" title="TSoIP">TSoIP</a></li>
|
||||||
<li><a href="sys_spectrum.html" title="Spectrum">Spectrum</a></li>
|
<li><a href="sys_spectrum.html" title="RF Performance">RF Performance</a></li>
|
||||||
<li><a href="log_output.html" title="Log">Log</a></li>
|
<li><a href="log_output.html" title="Log">Log</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="sidebar-footer">
|
<div class="sidebar-footer">
|
||||||
@@ -499,8 +593,15 @@ function cmd_confirm() {
|
|||||||
</button>
|
</button>
|
||||||
<h1><a href="https://anywavecom.net/" class="logo-bg" title="Anywave Broadcast Inc."></a></h1>
|
<h1><a href="https://anywavecom.net/" class="logo-bg" title="Anywave Broadcast Inc."></a></h1>
|
||||||
<h2 id="webtitle_header"><span></span></h2>
|
<h2 id="webtitle_header"><span></span></h2>
|
||||||
<h3>
|
<h3 class="has-toggle">
|
||||||
<div class="dropdown">
|
<div class="utility-cluster">
|
||||||
|
<button class="theme-toggle" onclick="toggleTheme()" aria-label="Switch color theme" title="Switch color theme">
|
||||||
|
<span class="tt-sun" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg></span>
|
||||||
|
<span class="tt-moon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg></span>
|
||||||
|
<span class="tt-label"><span class="tt-to-light">Light Mode</span><span class="tt-to-dark">Dark Mode</span></span>
|
||||||
|
</button>
|
||||||
|
<span class="utility-divider" aria-hidden="true"></span>
|
||||||
|
<div class="dropdown">
|
||||||
<button class="dropbtn-modern">Admin ▾</button>
|
<button class="dropbtn-modern">Admin ▾</button>
|
||||||
<div class="dropdown-content">
|
<div class="dropdown-content">
|
||||||
<a href="#" onclick="openAccountModal(); return false;">
|
<a href="#" onclick="openAccountModal(); return false;">
|
||||||
@@ -520,7 +621,8 @@ function cmd_confirm() {
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</h3>
|
</div>
|
||||||
|
</h3>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Fixed Dashboard Summary - Top of page, under header -->
|
<!-- Fixed Dashboard Summary - Top of page, under header -->
|
||||||
@@ -546,16 +648,6 @@ function cmd_confirm() {
|
|||||||
<span class="summary-unit">dB</span>
|
<span class="summary-unit">dB</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="summary-card">
|
|
||||||
<div class="summary-label-row">
|
|
||||||
<span class="status-dot status-ok" id="dot_uimd" aria-hidden="true"></span>
|
|
||||||
<span class="summary-label">UIMD</span>
|
|
||||||
</div>
|
|
||||||
<div class="summary-value-wrapper">
|
|
||||||
<span class="summary-value value-ok" id="sum_uimd">--</span>
|
|
||||||
<span class="summary-unit">dBc</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="summary-card">
|
<div class="summary-card">
|
||||||
<div class="summary-label-row">
|
<div class="summary-label-row">
|
||||||
<span class="status-dot status-ok" id="dot_limd" aria-hidden="true"></span>
|
<span class="status-dot status-ok" id="dot_limd" aria-hidden="true"></span>
|
||||||
@@ -566,6 +658,16 @@ function cmd_confirm() {
|
|||||||
<span class="summary-unit">dBc</span>
|
<span class="summary-unit">dBc</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="summary-card">
|
||||||
|
<div class="summary-label-row">
|
||||||
|
<span class="status-dot status-ok" id="dot_uimd" aria-hidden="true"></span>
|
||||||
|
<span class="summary-label">UIMD</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-value-wrapper">
|
||||||
|
<span class="summary-value value-ok" id="sum_uimd">--</span>
|
||||||
|
<span class="summary-unit">dBc</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="summary-card">
|
<div class="summary-card">
|
||||||
<div class="summary-label-row">
|
<div class="summary-label-row">
|
||||||
<span class="status-dot status-off" id="dot_rf" aria-hidden="true"></span>
|
<span class="status-dot status-off" id="dot_rf" aria-hidden="true"></span>
|
||||||
@@ -659,7 +761,7 @@ function cmd_confirm() {
|
|||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="button" class="btn-modern" alt="Set" value="Set" disabled onclick="return tsid_psip_top_set();">
|
<input type="button" id="ts_ctrl_set" class="btn-modern" alt="Set" value="Set" disabled onclick="return tsid_psip_top_set();">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -668,7 +770,7 @@ function cmd_confirm() {
|
|||||||
<input type="text" maxlength="5" size="8" name="tsid" id="tsid" value="">
|
<input type="text" maxlength="5" size="8" name="tsid" id="tsid" value="">
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="button" class="btn-modern" alt="Set" value="Set" disabled onclick="return tsid_psip_top_set();">
|
<input type="button" id="ts_tsid_set" class="btn-modern" alt="Set" value="Set" disabled onclick="return tsid_psip_top_set();">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
+88
-22
@@ -4,6 +4,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title id="webtitle"></title>
|
<title id="webtitle"></title>
|
||||||
|
<script src="js/theme.js"></script>
|
||||||
<link href="css/styles.css" rel="stylesheet" media="screen">
|
<link href="css/styles.css" rel="stylesheet" media="screen">
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
<script src="js/jquery.js"></script>
|
<script src="js/jquery.js"></script>
|
||||||
@@ -66,7 +67,11 @@ function get_remote_local_ctrl() {
|
|||||||
|
|
||||||
var table = data.split("|");
|
var table = data.split("|");
|
||||||
g_remot_local_ctrl = Number(table[0].split("#")[5]);
|
g_remot_local_ctrl = Number(table[0].split("#")[5]);
|
||||||
document.title = table[0].split("#")[11];
|
/* P-4b (2026-08-06): the title write here — document.title =
|
||||||
|
table[0].split("#")[11] — is DELETED. On smod index 11 is ab_mode
|
||||||
|
(the FSSI Refresh switch), not the web title. This page already sets
|
||||||
|
the title from its web_title section below. Round 3 fixed only
|
||||||
|
status_info; this is the rest of that class. */
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,12 +471,13 @@ if (document.readyState === 'loading') {
|
|||||||
<div class="app-layout">
|
<div class="app-layout">
|
||||||
<aside class="app-sidebar">
|
<aside class="app-sidebar">
|
||||||
<ul class="items">
|
<ul class="items">
|
||||||
|
<li><a href="overview.html" title="Overview">Overview</a></li>
|
||||||
<li><a href="status_info.html" title="Status">Status</a></li>
|
<li><a href="status_info.html" title="Status">Status</a></li>
|
||||||
<li><a href="sys_set.html" title="Settings">Settings</a></li>
|
<li><a href="sys_set.html" title="Settings">Settings</a></li>
|
||||||
<li class="active"><a href="sys_rf.html" title="RF Control">RF Control</a></li>
|
<li class="active"><a href="sys_rf.html" title="RF Control">RF Control</a></li>
|
||||||
<li><a href="sys_psip.html" title="TSID&PSIP">TSID&PSIP</a></li>
|
<li><a href="sys_psip.html" title="TSID&PSIP">TSID&PSIP</a></li>
|
||||||
<li><a href="sys_ts.html" title="TSoIP">TSoIP</a></li>
|
<li><a href="sys_ts.html" title="TSoIP">TSoIP</a></li>
|
||||||
<li><a href="sys_spectrum.html" title="Spectrum">Spectrum</a></li>
|
<li><a href="sys_spectrum.html" title="RF Performance">RF Performance</a></li>
|
||||||
<li><a href="log_output.html" title="Log">Log</a></li>
|
<li><a href="log_output.html" title="Log">Log</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="sidebar-footer">
|
<div class="sidebar-footer">
|
||||||
@@ -491,8 +497,15 @@ if (document.readyState === 'loading') {
|
|||||||
</button>
|
</button>
|
||||||
<h1><a href="https://anywavecom.net/" class="logo-bg" title="Anywave Broadcast Inc."></a></h1>
|
<h1><a href="https://anywavecom.net/" class="logo-bg" title="Anywave Broadcast Inc."></a></h1>
|
||||||
<h2 id="webtitle_header"><span></span></h2>
|
<h2 id="webtitle_header"><span></span></h2>
|
||||||
<h3>
|
<h3 class="has-toggle">
|
||||||
<div class="dropdown">
|
<div class="utility-cluster">
|
||||||
|
<button class="theme-toggle" onclick="toggleTheme()" aria-label="Switch color theme" title="Switch color theme">
|
||||||
|
<span class="tt-sun" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg></span>
|
||||||
|
<span class="tt-moon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg></span>
|
||||||
|
<span class="tt-label"><span class="tt-to-light">Light Mode</span><span class="tt-to-dark">Dark Mode</span></span>
|
||||||
|
</button>
|
||||||
|
<span class="utility-divider" aria-hidden="true"></span>
|
||||||
|
<div class="dropdown">
|
||||||
<button class="dropbtn-modern">Admin ▾</button>
|
<button class="dropbtn-modern">Admin ▾</button>
|
||||||
<div class="dropdown-content">
|
<div class="dropdown-content">
|
||||||
<a href="#" onclick="openAccountModal(); return false;">
|
<a href="#" onclick="openAccountModal(); return false;">
|
||||||
@@ -512,7 +525,8 @@ if (document.readyState === 'loading') {
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</h3>
|
</div>
|
||||||
|
</h3>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Fixed Dashboard Summary - Top of page, under header -->
|
<!-- Fixed Dashboard Summary - Top of page, under header -->
|
||||||
@@ -538,16 +552,6 @@ if (document.readyState === 'loading') {
|
|||||||
<span class="summary-unit">dB</span>
|
<span class="summary-unit">dB</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="summary-card">
|
|
||||||
<div class="summary-label-row">
|
|
||||||
<span class="status-dot status-ok" id="dot_uimd" aria-hidden="true"></span>
|
|
||||||
<span class="summary-label">UIMD</span>
|
|
||||||
</div>
|
|
||||||
<div class="summary-value-wrapper">
|
|
||||||
<span class="summary-value value-ok" id="sum_uimd">--</span>
|
|
||||||
<span class="summary-unit">dBc</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="summary-card">
|
<div class="summary-card">
|
||||||
<div class="summary-label-row">
|
<div class="summary-label-row">
|
||||||
<span class="status-dot status-ok" id="dot_limd" aria-hidden="true"></span>
|
<span class="status-dot status-ok" id="dot_limd" aria-hidden="true"></span>
|
||||||
@@ -558,6 +562,16 @@ if (document.readyState === 'loading') {
|
|||||||
<span class="summary-unit">dBc</span>
|
<span class="summary-unit">dBc</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="summary-card">
|
||||||
|
<div class="summary-label-row">
|
||||||
|
<span class="status-dot status-ok" id="dot_uimd" aria-hidden="true"></span>
|
||||||
|
<span class="summary-label">UIMD</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-value-wrapper">
|
||||||
|
<span class="summary-value value-ok" id="sum_uimd">--</span>
|
||||||
|
<span class="summary-unit">dBc</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="summary-card">
|
<div class="summary-card">
|
||||||
<div class="summary-label-row">
|
<div class="summary-label-row">
|
||||||
<span class="status-dot status-off" id="dot_rf" aria-hidden="true"></span>
|
<span class="status-dot status-off" id="dot_rf" aria-hidden="true"></span>
|
||||||
@@ -696,6 +710,21 @@ if (document.readyState === 'loading') {
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
<!-- AGC / FSSI interaction (Session 4, 2026-08-11 — owner decision: LEAVE UNGATED).
|
||||||
|
DECISION: AGC stays freely settable on this page, including while FSSI Refresh is On.
|
||||||
|
ANALYSIS behind the decision (recorded so a future session does not "fix" this blindly):
|
||||||
|
- Under FSSI, AGC state is not physically meaningful: "FSSI active alternates the
|
||||||
|
RFinA feedback path AGC realizes on" (status_info.html physics comment, identical in
|
||||||
|
the 1.0 and 3.0). The status page already shows AGC as "N/A-FSSI" when FSSI is On.
|
||||||
|
- The family's hard interlock is FSSI<->ADPC, not FSSI<->AGC. smod HAS that ADPC gate
|
||||||
|
(sys_set.html: blocks ADPC transitions while FSSI Refresh On; 3.0 sys_set.html:858
|
||||||
|
"do not relax"). ADPC and AGC share the same RFinA feedback loop, so gating ADPC was
|
||||||
|
the intended guard; AGC is covered indirectly through it.
|
||||||
|
- NONE of the three products (smod, 1.0, 3.0) gates AGC on FSSI at the control layer —
|
||||||
|
so this is consistent family behaviour, not a smod-unique omission.
|
||||||
|
Owner ruled 2026-08-11 to keep AGC ungated (the ADPC interlock is the guard). If this is
|
||||||
|
ever revisited, the RF page would need to poll FSSI state (it does not today) — see the
|
||||||
|
S4 ledger append and S5 handoff for the full reasoning. -->
|
||||||
<tr>
|
<tr>
|
||||||
<td>AGC</td>
|
<td>AGC</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -882,7 +911,30 @@ if (document.readyState === 'loading') {
|
|||||||
</table>
|
</table>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<!-- This warning is ABOUT the TX Frequency control above it.
|
||||||
|
The PR07 Reference Output section had been inserted between them, pushing it
|
||||||
|
to the bottom of the page, away from the control it describes (on the 1.0 it
|
||||||
|
sits adjacent because nothing intervenes). Moved back to sit under its control. -->
|
||||||
|
<!-- R8 (2026-08-11): STANDING informational warning -> plain amber TEXT, matching the 3.0
|
||||||
|
exactly (3.0 sys_rf.html:974). The family rule, read off the 3.0's own pages:
|
||||||
|
- plain amber text (color:var(--status-warn), no box) = a STANDING FACT about how a
|
||||||
|
control behaves (this note, and the 10 MHz note below). Always visible.
|
||||||
|
- .warning-banner (tinted box) = a CONDITIONAL mode/section advisory that appears and
|
||||||
|
disappears (e.g. the 3.0's hidden "Gateway RF-In mode" note).
|
||||||
|
- .warning-banner.is-error (red box) = a genuine fault.
|
||||||
|
R3/R4 wrongly put this standing note in the tinted box; the 3.0 never did. Corrected. -->
|
||||||
|
<div style="padding: 10px; color: var(--status-warn); font-size: 12px;">
|
||||||
|
Warning: Exciter RF Output Power will be reset to -25 dBm when the channel (TX Frequency) is changed.
|
||||||
|
</div>
|
||||||
|
</div><!-- /.box — P-7 (2026-08-06): THE STRUCTURAL DEFECT. The RF Control card
|
||||||
|
was never closed here, so the PR07 "Reference Output" section (h2, table and
|
||||||
|
note) rendered INSIDE the RF Control card — an h2 with a full-width rule
|
||||||
|
sitting in another card's padding, which is the "doesn't fit / out of the
|
||||||
|
picture" the owner reported. Closing it makes Reference Output a sibling
|
||||||
|
section: h2 then its own .box, exactly like every other section on sys_set. -->
|
||||||
|
|
||||||
<h2>Reference Output</h2>
|
<h2>Reference Output</h2>
|
||||||
|
<div class="box">
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td>10 MHz Output</td>
|
<td>10 MHz Output</td>
|
||||||
@@ -897,21 +949,35 @@ if (document.readyState === 'loading') {
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<div style="padding: 4px 10px; color: #64748b; font-size: 12px;">
|
<!-- R8: standing informational note -> plain amber text, same rule as the -25 dBm note above. -->
|
||||||
Enables the AB5 10 MHz reference clock output. Default Off; resets to Off on every power-up.
|
<!-- R11 (2026-08-17, owner-ruled). SUPERSEDES the R9/D-27.3 and D-28.1 rulings above, which
|
||||||
|
decided WHERE the word "Warning:" sat before either of us had read the sentence for sense.
|
||||||
|
History worth keeping: this note was chased for several rounds as a "missing Warning:"
|
||||||
|
bug. It was never missing — the word was present in source AND in the unit's own served
|
||||||
|
bytes (owner F12 Network->Response capture, 2026-08-17). Both of us mis-read it, because
|
||||||
|
both of us expected "Warning:" at the START of the sentence, as it is on the -25 dBm note
|
||||||
|
above; here it sat mid-sentence and the eye slid past it.
|
||||||
|
Reading it for SENSE surfaced the real defect: the sentence opened like a command
|
||||||
|
("Enables...") and then hung "Warning:" in front of a NON-hazard.
|
||||||
|
THE GOVERNING RULE, which applies to any future note: a standing note earns "Warning:"
|
||||||
|
only when it flags a REAL CONSEQUENCE; reserve the word so the label keeps discriminating.
|
||||||
|
This matters here specifically because the tree's only two prose notes (this one and the
|
||||||
|
-25 dBm note above) share ONE identical amber style — so COLOUR cannot distinguish a
|
||||||
|
hazard from a heads-up, and the LABEL is the only thing that can.
|
||||||
|
Hence: the -25 dBm note keeps "Warning:" (real consequence — silent power loss on a
|
||||||
|
channel change); this one becomes "Note:" (behavioural heads-up only). Styling unchanged. -->
|
||||||
|
<div style="padding: 6px 10px 2px; color: var(--status-warn); font-size: 12px;">
|
||||||
|
Note: The 10 MHz reference clock output is Off by default, and resets to Off on every power-up.
|
||||||
</div>
|
</div>
|
||||||
|
</div><!-- /.box Reference Output -->
|
||||||
|
|
||||||
<div style="padding: 10px; color: #b91c1c; font-weight: 500;">
|
|
||||||
Warning: Exciter RF Output Power will be reset to -25dBm when the channel(TX Frequency) is changed.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="accountModal" class="modal">
|
<div id="accountModal" class="modal">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h3>ACCOUNT SETTINGS</h3>
|
<h3>Account Settings</h3>
|
||||||
<span class="close" onclick="closeAccountModal()">×</span>
|
<span class="close" onclick="closeAccountModal()">×</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
|||||||
Executable → Regular
+151
-71
@@ -5,6 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<title id="webtitle"></title>
|
<title id="webtitle"></title>
|
||||||
|
<script src="js/theme.js"></script>
|
||||||
<link href="css/styles.css" rel="stylesheet" media="screen">
|
<link href="css/styles.css" rel="stylesheet" media="screen">
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
<script src="js/jquery.js"></script>
|
<script src="js/jquery.js"></script>
|
||||||
@@ -16,9 +17,17 @@ var lastTime = new Date().getTime();
|
|||||||
var currentTime = new Date().getTime();
|
var currentTime = new Date().getTime();
|
||||||
var timeOut = 20 * 60 * 1000; // 20 min
|
var timeOut = 20 * 60 * 1000; // 20 min
|
||||||
var upgrade_imx_flg = 0;
|
var upgrade_imx_flg = 0;
|
||||||
|
/* Keep the settings page authenticated while a long FPGA upload/flash is active. */
|
||||||
|
var fpga_upgrade_in_progress = 0;
|
||||||
|
function finishFpgaUpgrade(){
|
||||||
|
fpga_upgrade_in_progress = 0;
|
||||||
|
upgrade_imx_flg = 0; // resume normal status refresh
|
||||||
|
lastTime = new Date().getTime();
|
||||||
|
}
|
||||||
var remot_local_ctrl = 0; // 0 remote 1 local
|
var remot_local_ctrl = 0; // 0 remote 1 local
|
||||||
var g_old_adpc_status = 0;
|
var g_old_adpc_status = 0;
|
||||||
var g_old_adpc = 0;
|
var g_old_adpc = 0;
|
||||||
|
var ldpd_ctl = 0;
|
||||||
var g_key_tsoip_valid = 0;
|
var g_key_tsoip_valid = 0;
|
||||||
var g_key_eas_valid = 0;
|
var g_key_eas_valid = 0;
|
||||||
var g_old_ntp_ip1 = 0;
|
var g_old_ntp_ip1 = 0;
|
||||||
@@ -37,7 +46,7 @@ $(function(){
|
|||||||
|
|
||||||
function testTime(){
|
function testTime(){
|
||||||
currentTime = new Date().getTime();
|
currentTime = new Date().getTime();
|
||||||
if(currentTime - lastTime > timeOut){
|
if(!fpga_upgrade_in_progress && currentTime - lastTime > timeOut){
|
||||||
window.location.href="login.cgi?logout=1"
|
window.location.href="login.cgi?logout=1"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,6 +154,17 @@ $(document).ready(function(){
|
|||||||
update_dpd_state();
|
update_dpd_state();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* REG-3 (2026-08-05) — ONE canonical control-lock list. The grey/green mismatch the owner
|
||||||
|
reported came from hand-typed parallel lists (the load-lock had 9 IDs; the IMX-upgrade
|
||||||
|
lock was missing #upgrade_fpga_id/#key_set_id/#ntp_set_id; the FPGA flash locked only
|
||||||
|
its own button). Every full-page lock/unlock now goes through lockAllControls();
|
||||||
|
NEVER re-type the ID list anywhere. Text (Loading.../Local Mode/Set) stays owned by
|
||||||
|
each call site — this helper owns only the disabled state. */
|
||||||
|
var ALL_CONTROL_IDS = '#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';
|
||||||
|
function lockAllControls(lock) {
|
||||||
|
$(ALL_CONTROL_IDS).attr("disabled", !!lock);
|
||||||
|
}
|
||||||
|
|
||||||
function SetFormDefaults(){
|
function SetFormDefaults(){
|
||||||
if(upgrade_imx_flg == 1) return;
|
if(upgrade_imx_flg == 1) return;
|
||||||
|
|
||||||
@@ -154,7 +174,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, #upgrade_fpga_id, #key_set_id, #ntp_set_id, #timezone_set_id')
|
$(ALL_CONTROL_IDS)
|
||||||
.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=&";
|
||||||
@@ -211,6 +231,10 @@ function SetFormDefaults(){
|
|||||||
cf0.agc_att.value = Number(syspara[i++])*(-0.5);
|
cf0.agc_att.value = Number(syspara[i++])*(-0.5);
|
||||||
cf0.adrv9009_att.value = syspara[i++];
|
cf0.adrv9009_att.value = syspara[i++];
|
||||||
cf0.orx_manu_gain.value = syspara[i++];
|
cf0.orx_manu_gain.value = syspara[i++];
|
||||||
|
ldpd_ctl = Number(syspara[i++]);
|
||||||
|
var adpc_label = document.getElementById('adpc_label');
|
||||||
|
if (adpc_label) adpc_label.style.color = (ldpd_ctl === 0x07) ? '' : '#d6a700';
|
||||||
|
cf0.hardware_version.value = (syspara[i] === undefined || Number(syspara[i]) !== 0) ? 1 : 0;
|
||||||
|
|
||||||
// sysnet form1
|
// sysnet form1
|
||||||
i = 1;
|
i = 1;
|
||||||
@@ -279,12 +303,12 @@ 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, #upgrade_fpga_id, #key_set_id, #ntp_set_id, #timezone_set_id')
|
$(ALL_CONTROL_IDS)
|
||||||
.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, #upgrade_fpga_id, #key_set_id, #ntp_set_id, #timezone_set_id')
|
$(ALL_CONTROL_IDS)
|
||||||
.attr("disabled", false)
|
.attr("disabled", false)
|
||||||
.val("Set");
|
.val("Set");
|
||||||
$('#upgrade_imx_id').val("Upgrade");
|
$('#upgrade_imx_id').val("Upgrade");
|
||||||
@@ -459,9 +483,10 @@ function set_syspara() {
|
|||||||
postData += "#"+cf.agc_att.value;
|
postData += "#"+cf.agc_att.value;
|
||||||
postData += "#"+cf.adrv9009_att.value;
|
postData += "#"+cf.adrv9009_att.value;
|
||||||
postData += "#"+cf.orx_manu_gain.value;
|
postData += "#"+cf.orx_manu_gain.value;
|
||||||
|
postData += "#"+cf.hardware_version.value;
|
||||||
|
|
||||||
if(cf.ab_mode.value == "1" && Number(cf.adp.value) > 0) {
|
if(cf.ab_mode.value == "1" && Number(cf.adp.value) > 0) {
|
||||||
alert("The ADPC can't be set to Hold or Update or UpdateLIN/UpdateNLIN mode when 'FSSI Refresh' switch 'On' !!! Please change 'FSSI Refresh' to 'Off'.");
|
alert("The ADPC cannot be set to Hold or Update or UpdateLIN/UpdateNLIN mode when 'FSSI Refresh' switch is 'On'. Please change 'FSSI Refresh' to 'Off'.");
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
var bak_adpc = Number(g_old_adpc);
|
var bak_adpc = Number(g_old_adpc);
|
||||||
@@ -510,6 +535,7 @@ function setsysnet() {
|
|||||||
|
|
||||||
if(cmd_confirm()){
|
if(cmd_confirm()){
|
||||||
$.post("atsc_status_cfg.cgi?",postData,function(data, status){
|
$.post("atsc_status_cfg.cgi?",postData,function(data, status){
|
||||||
|
if(status!="success"){ upgrade_imx_flg = 0; lockAllControls(false); return; } // P-5: release on failure
|
||||||
if(status=="success"){
|
if(status=="success"){
|
||||||
SetFormDefaults();
|
SetFormDefaults();
|
||||||
} else{
|
} else{
|
||||||
@@ -522,6 +548,20 @@ function setsysnet() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function sysresetff() {
|
function sysresetff() {
|
||||||
|
/* P-5 (2026-08-06). Two gaps closed here, both proven against the 1.0:
|
||||||
|
(a) LOCAL-LOCKOUT: the 1.0 refuses a restart in Local mode and re-syncs the
|
||||||
|
form (its sys_set.html:926); smod posted regardless. A restart is
|
||||||
|
destructive, so it must honour the same lockout as every other write.
|
||||||
|
(b) CONTROL LOCK: the restart path locked nothing, so every Set/Upgrade
|
||||||
|
button stayed live (and green) while the unit rebooted — the owner's
|
||||||
|
hardware finding. REG-3 already built ONE lockAllControls(); the restart
|
||||||
|
simply never called it. Same single-lock-list class as the upgrades. */
|
||||||
|
if(remot_local_ctrl == 1){
|
||||||
|
cmd_confirm();
|
||||||
|
SetFormDefaults();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var postData = "sysreset=";
|
var postData = "sysreset=";
|
||||||
var cf = document.getElementById("form_reset");
|
var cf = document.getElementById("form_reset");
|
||||||
var prog_val = 1;
|
var prog_val = 1;
|
||||||
@@ -534,13 +574,14 @@ function sysresetff() {
|
|||||||
|
|
||||||
if(cmd_confirm()){
|
if(cmd_confirm()){
|
||||||
upgrade_imx_flg = 1;
|
upgrade_imx_flg = 1;
|
||||||
|
lockAllControls(true);
|
||||||
|
|
||||||
$.post("atsc_status_cfg.cgi?",postData,function(data, status){
|
$.post("atsc_status_cfg.cgi?",postData,function(data, status){
|
||||||
if(status=="success"){
|
if(status=="success"){
|
||||||
var t = setInterval(function() {
|
var t = setInterval(function() {
|
||||||
$('#rst_tb').hide();
|
$('#rst_tb').hide();
|
||||||
$('#rst_progid').show();
|
$('#rst_progid').show();
|
||||||
$('#rst_note').text("System is now reseting. Please don't power off during the reseting. Wait for a minute and the page will automatically refreshed.");
|
$('#rst_note').text("The unit is now restarting. Please do not power off during the restart. Wait for a minute and the page will automatically refresh.");
|
||||||
|
|
||||||
var rst_prog_num = document.getElementById("rst_progid");
|
var rst_prog_num = document.getElementById("rst_progid");
|
||||||
rst_prog_num.value = prog_val;
|
rst_prog_num.value = prog_val;
|
||||||
@@ -599,18 +640,15 @@ function uploadfile(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
var result=document.getElementById('result');
|
var result=document.getElementById('result');
|
||||||
$('#upgrade_imx_id').attr("disabled",true);
|
// REG-3: ALL controls lock during an upgrade — including the FPGA/key/NTP buttons the
|
||||||
|
// old hand-typed list missed (the grey/green mismatch).
|
||||||
|
lockAllControls(true);
|
||||||
$('#formupgrade').attr("disabled",true);
|
$('#formupgrade').attr("disabled",true);
|
||||||
$('#net_set_id').attr("disabled",true);
|
|
||||||
$('#para_set_id').attr("disabled",true);
|
|
||||||
$('#sys_rtc_id').attr("disabled",true);
|
|
||||||
$('#sysreset_bt_id').attr("disabled",true);
|
|
||||||
$('#timezone_set_id').attr("disabled",true);
|
|
||||||
|
|
||||||
var file = document.getElementById('Upgrade').files[0];
|
var file = document.getElementById('Upgrade').files[0];
|
||||||
if (!file) {
|
if (!file) {
|
||||||
alert('Please select a file');
|
alert('Please select a file');
|
||||||
$('#upgrade_imx_id').attr("disabled",false); // undo the disables above
|
lockAllControls(false); // undo the disables above (all of them)
|
||||||
$('#formupgrade').attr("disabled",false);
|
$('#formupgrade').attr("disabled",false);
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -643,7 +681,10 @@ function uploadfile(){
|
|||||||
// 1. Update the Text (You already have this)
|
// 1. Update the Text (You already have this)
|
||||||
result.innerHTML = "Uploading package ... " + percent + '%';
|
result.innerHTML = "Uploading package ... " + percent + '%';
|
||||||
|
|
||||||
// 2. ADD THIS LINE to update the Green Bar visual
|
// 2. Show + update the Green Bar. Starts display:none so it is not an empty
|
||||||
|
// grey bar at rest (matches the sibling #prog_id / #rst_progid pattern);
|
||||||
|
// revealed here when upload begins, hidden again on completion below.
|
||||||
|
$("#progress").show();
|
||||||
document.getElementById('progress').value = percent;
|
document.getElementById('progress').value = percent;
|
||||||
|
|
||||||
if(e.loaded == e.total){
|
if(e.loaded == e.total){
|
||||||
@@ -651,7 +692,7 @@ function uploadfile(){
|
|||||||
$('#formupgrade').hide();
|
$('#formupgrade').hide();
|
||||||
$('#prog_id').show();
|
$('#prog_id').show();
|
||||||
$("#progress").hide();
|
$("#progress").hide();
|
||||||
$('#retnote').text("Code package has been uploaded successfully. System is now upgrading and reboot. Please don't power off the system during the upgrade. Wait for a minute and the page will automatically refreshed.");
|
$('#retnote').text("Code package uploaded successfully. System is now upgrading and rebooting. Please do not power off during the upgrade. Wait for a minute and the page will automatically refresh.");
|
||||||
SetFormDefaults();
|
SetFormDefaults();
|
||||||
var prog_num = document.getElementById("prog_id");
|
var prog_num = document.getElementById("prog_id");
|
||||||
prog_num.value = widthddd;
|
prog_num.value = widthddd;
|
||||||
@@ -689,7 +730,8 @@ function uploadFpgaFile(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
var result = document.getElementById('fpga_result');
|
var result = document.getElementById('fpga_result');
|
||||||
document.getElementById('upgrade_fpga_id').disabled = true;
|
// REG-3: a flash locks the whole page's controls, not only its own button
|
||||||
|
lockAllControls(true);
|
||||||
|
|
||||||
// The .mcs is ~23 MB and takes ~10 min to land on the board's flash. The board runs a
|
// The .mcs is ~23 MB and takes ~10 min to land on the board's flash. The board runs a
|
||||||
// small forking httpd; if testTime() keeps firing update_dpd_state/get_ntp_server_state/
|
// small forking httpd; if testTime() keeps firing update_dpd_state/get_ntp_server_state/
|
||||||
@@ -699,6 +741,8 @@ function uploadFpgaFile(){
|
|||||||
// the imx6 package; the FPGA path was missing it, which is why large .mcs uploads never
|
// the imx6 package; the FPGA path was missing it, which is why large .mcs uploads never
|
||||||
// finished from the browser while a scripted POST (no background polling) did.
|
// finished from the browser while a scripted POST (no background polling) did.
|
||||||
upgrade_imx_flg = 1;
|
upgrade_imx_flg = 1;
|
||||||
|
fpga_upgrade_in_progress = 1;
|
||||||
|
lastTime = new Date().getTime();
|
||||||
|
|
||||||
var params = new FormData();
|
var params = new FormData();
|
||||||
params.append('file', file);
|
params.append('file', file);
|
||||||
@@ -706,22 +750,33 @@ function uploadFpgaFile(){
|
|||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
xhr.onerror = function(){
|
xhr.onerror = function(){
|
||||||
alert('FPGA upload request failed');
|
alert('FPGA upload request failed');
|
||||||
document.getElementById('upgrade_fpga_id').disabled = false;
|
lockAllControls(false);
|
||||||
upgrade_imx_flg = 0; // let the dashboard resume
|
finishFpgaUpgrade();
|
||||||
};
|
};
|
||||||
xhr.onabort = xhr.ontimeout = function(){
|
xhr.onabort = xhr.ontimeout = function(){
|
||||||
document.getElementById('upgrade_fpga_id').disabled = false;
|
lockAllControls(false);
|
||||||
upgrade_imx_flg = 0;
|
finishFpgaUpgrade();
|
||||||
};
|
};
|
||||||
xhr.upload.onprogress = function(e){
|
xhr.upload.onprogress = function(e){
|
||||||
var percent = Math.round(e.loaded / e.total * 100);
|
var percent = Math.round(e.loaded / e.total * 100);
|
||||||
result.innerHTML = "Uploading .mcs ... " + percent + '%';
|
result.innerHTML = "Uploading .mcs ... " + percent + '%';
|
||||||
|
$("#fpga_progress").show(); // reveal on upload start (starts display:none, sibling pattern)
|
||||||
document.getElementById('fpga_progress').value = percent;
|
document.getElementById('fpga_progress').value = percent;
|
||||||
if(e.loaded == e.total){
|
if(e.loaded == e.total){
|
||||||
// upload done -> the board now erases+writes the flash; track its progress
|
// upload done -> the board now erases+writes the flash; track its progress
|
||||||
result.innerHTML = "Upload complete. Flashing FPGA... DO NOT power off.";
|
result.innerHTML = "Upload complete. Flashing FPGA... DO NOT power off.";
|
||||||
document.getElementById('fpga_progress').value = 0;
|
document.getElementById('fpga_progress').value = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.onload = function(){
|
||||||
|
if(xhr.status >= 200 && xhr.status < 300){
|
||||||
|
/* The CGI has accepted this request and reset the server-side status.
|
||||||
|
* Start polling now so a previous DONE result cannot be consumed. */
|
||||||
startFpgaFlashPoll();
|
startFpgaFlashPoll();
|
||||||
|
}else{
|
||||||
|
alert('FPGA upload request failed');
|
||||||
|
lockAllControls(false);
|
||||||
|
finishFpgaUpgrade();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
xhr.open('POST', 'fpga_upload.cgi', true);
|
xhr.open('POST', 'fpga_upload.cgi', true);
|
||||||
@@ -747,26 +802,35 @@ function startFpgaFlashPoll(){
|
|||||||
if(phase==='PENDING'){ result.innerHTML = "Preparing (stopping DPD)..."; bar.value = 0; }
|
if(phase==='PENDING'){ result.innerHTML = "Preparing (stopping DPD)..."; bar.value = 0; }
|
||||||
else if(phase==='ERASE'){ result.innerHTML = "Erasing flash (~30s)..."; bar.value = 0; }
|
else if(phase==='ERASE'){ result.innerHTML = "Erasing flash (~30s)..."; bar.value = 0; }
|
||||||
else if(phase==='WRITE'){ sawProgress = true; result.innerHTML = "Writing FPGA image ... " + pct + "%"; bar.value = pct; }
|
else if(phase==='WRITE'){ sawProgress = true; result.innerHTML = "Writing FPGA image ... " + pct + "%"; bar.value = pct; }
|
||||||
else if(phase==='DONE'){
|
else if(phase==='RELINK' || phase==='DONE'){
|
||||||
bar.value = 100;
|
bar.value = 100;
|
||||||
result.innerHTML = "FPGA flash complete (100%). System is rebooting.";
|
result.innerHTML = "FPGA flash complete (100%). Re-establishing RF...";
|
||||||
note.innerText = "Done. FPGA reloaded + RF re-established automatically (no power cycle needed).";
|
note.innerText = "FPGA reloaded. Waiting for RF/JESD relink...";
|
||||||
clearInterval(poll); if(btn) btn.disabled = false;
|
}
|
||||||
|
else if(phase==='READY'){
|
||||||
|
var redirectLeft = 5;
|
||||||
|
bar.value = 100;
|
||||||
|
note.innerText = "RF re-established. Redirecting to login page...";
|
||||||
|
clearInterval(poll); lockAllControls(false); finishFpgaUpgrade();
|
||||||
|
result.innerHTML = "FPGA and RF are ready. Opening login page in " + redirectLeft + " s...";
|
||||||
|
var redirectTimer = setInterval(function(){
|
||||||
|
redirectLeft--;
|
||||||
|
if(redirectLeft <= 0){ clearInterval(redirectTimer); window.location.href = 'login.html'; }
|
||||||
|
else result.innerHTML = "FPGA and RF are ready. Opening login page in " + redirectLeft + " s...";
|
||||||
|
}, 1000);
|
||||||
}
|
}
|
||||||
else if(phase==='ERROR'){
|
else if(phase==='ERROR'){
|
||||||
result.innerHTML = "<span style='color:#d00;font-weight:bold'>FPGA flash FAILED (code " + ret + "). Aborted — check the device log. The board falls back to golden on cold boot.</span>";
|
result.innerHTML = "<span style='color:#d00;font-weight:bold'>FPGA flash FAILED (code " + ret + "). Aborted — check the device log. The board falls back to golden on cold boot.</span>";
|
||||||
clearInterval(poll); if(btn) btn.disabled = false;
|
clearInterval(poll); lockAllControls(false); finishFpgaUpgrade();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error:function(){
|
error:function(){
|
||||||
// the board reboots on success and drops the connection; if we had
|
// FPGA reload may temporarily interrupt CGI access. Do not redirect
|
||||||
// already seen write progress, treat a sustained drop as done-then-reboot
|
// until the backend explicitly publishes READY.
|
||||||
errAfterProgress++;
|
errAfterProgress++;
|
||||||
if(sawProgress && errAfterProgress >= 4){
|
if(sawProgress && errAfterProgress >= 4){
|
||||||
bar.value = 100;
|
result.innerHTML = "FPGA flashed. Waiting for RF/JESD relink...";
|
||||||
result.innerHTML = "FPGA flashed; board is rebooting (connection lost). Cold-restart it when it returns.";
|
note.innerText = "Connection temporarily unavailable; retrying status check.";
|
||||||
note.innerText = "Done. FPGA reloaded + RF re-established automatically (no power cycle needed).";
|
|
||||||
clearInterval(poll); if(btn) btn.disabled = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1249,12 +1313,13 @@ function set_timezone() {
|
|||||||
<div class="app-layout">
|
<div class="app-layout">
|
||||||
<aside class="app-sidebar">
|
<aside class="app-sidebar">
|
||||||
<ul class="items">
|
<ul class="items">
|
||||||
|
<li><a href="overview.html" title="Overview">Overview</a></li>
|
||||||
<li><a href="status_info.html" title="Status">Status</a></li>
|
<li><a href="status_info.html" title="Status">Status</a></li>
|
||||||
<li class="active"><a href="sys_set.html" title="Settings">Settings</a></li>
|
<li class="active"><a href="sys_set.html" title="Settings">Settings</a></li>
|
||||||
<li><a href="sys_rf.html" title="RF Control">RF Control</a></li>
|
<li><a href="sys_rf.html" title="RF Control">RF Control</a></li>
|
||||||
<li><a href="sys_psip.html" title="TSID&PSIP">TSID&PSIP</a></li>
|
<li><a href="sys_psip.html" title="TSID&PSIP">TSID&PSIP</a></li>
|
||||||
<li><a href="sys_ts.html" title="TSoIP">TSoIP</a></li>
|
<li><a href="sys_ts.html" title="TSoIP">TSoIP</a></li>
|
||||||
<li><a href="sys_spectrum.html" title="Spectrum">Spectrum</a></li>
|
<li><a href="sys_spectrum.html" title="RF Performance">RF Performance</a></li>
|
||||||
<li><a href="log_output.html" title="Log">Log</a></li>
|
<li><a href="log_output.html" title="Log">Log</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="sidebar-footer">
|
<div class="sidebar-footer">
|
||||||
@@ -1274,8 +1339,15 @@ function set_timezone() {
|
|||||||
</button>
|
</button>
|
||||||
<h1><a href="https://anywavecom.net/" class="logo-bg" title="Anywave Broadcast Inc."></a></h1>
|
<h1><a href="https://anywavecom.net/" class="logo-bg" title="Anywave Broadcast Inc."></a></h1>
|
||||||
<h2 id="webtitle_header"><span></span></h2>
|
<h2 id="webtitle_header"><span></span></h2>
|
||||||
<h3>
|
<h3 class="has-toggle">
|
||||||
<div class="dropdown">
|
<div class="utility-cluster">
|
||||||
|
<button class="theme-toggle" onclick="toggleTheme()" aria-label="Switch color theme" title="Switch color theme">
|
||||||
|
<span class="tt-sun" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg></span>
|
||||||
|
<span class="tt-moon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg></span>
|
||||||
|
<span class="tt-label"><span class="tt-to-light">Light Mode</span><span class="tt-to-dark">Dark Mode</span></span>
|
||||||
|
</button>
|
||||||
|
<span class="utility-divider" aria-hidden="true"></span>
|
||||||
|
<div class="dropdown">
|
||||||
<button class="dropbtn-modern">Admin ▾</button>
|
<button class="dropbtn-modern">Admin ▾</button>
|
||||||
<div class="dropdown-content">
|
<div class="dropdown-content">
|
||||||
<a href="#" onclick="openAccountModal(); return false;">
|
<a href="#" onclick="openAccountModal(); return false;">
|
||||||
@@ -1295,7 +1367,8 @@ function set_timezone() {
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</h3>
|
</div>
|
||||||
|
</h3>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Fixed Dashboard Summary - Top of page, under header -->
|
<!-- Fixed Dashboard Summary - Top of page, under header -->
|
||||||
@@ -1321,16 +1394,6 @@ function set_timezone() {
|
|||||||
<span class="summary-unit">dB</span>
|
<span class="summary-unit">dB</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="summary-card">
|
|
||||||
<div class="summary-label-row">
|
|
||||||
<span class="status-dot status-ok" id="dot_uimd" aria-hidden="true"></span>
|
|
||||||
<span class="summary-label">UIMD</span>
|
|
||||||
</div>
|
|
||||||
<div class="summary-value-wrapper">
|
|
||||||
<span class="summary-value value-ok" id="sum_uimd">--</span>
|
|
||||||
<span class="summary-unit">dBc</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="summary-card">
|
<div class="summary-card">
|
||||||
<div class="summary-label-row">
|
<div class="summary-label-row">
|
||||||
<span class="status-dot status-ok" id="dot_limd" aria-hidden="true"></span>
|
<span class="status-dot status-ok" id="dot_limd" aria-hidden="true"></span>
|
||||||
@@ -1341,6 +1404,16 @@ function set_timezone() {
|
|||||||
<span class="summary-unit">dBc</span>
|
<span class="summary-unit">dBc</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="summary-card">
|
||||||
|
<div class="summary-label-row">
|
||||||
|
<span class="status-dot status-ok" id="dot_uimd" aria-hidden="true"></span>
|
||||||
|
<span class="summary-label">UIMD</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-value-wrapper">
|
||||||
|
<span class="summary-value value-ok" id="sum_uimd">--</span>
|
||||||
|
<span class="summary-unit">dBc</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="summary-card">
|
<div class="summary-card">
|
||||||
<div class="summary-label-row">
|
<div class="summary-label-row">
|
||||||
<span class="status-dot status-off" id="dot_rf" aria-hidden="true"></span>
|
<span class="status-dot status-off" id="dot_rf" aria-hidden="true"></span>
|
||||||
@@ -1445,7 +1518,7 @@ function set_timezone() {
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>ADPC</td>
|
<td id="adpc_label">ADPC</td>
|
||||||
<td>
|
<td>
|
||||||
<select name="adp" id="adp">
|
<select name="adp" id="adp">
|
||||||
<option value="0">Off
|
<option value="0">Off
|
||||||
@@ -1548,6 +1621,13 @@ function set_timezone() {
|
|||||||
<td>
|
<td>
|
||||||
<input maxlength="3" size="5" name="orx_manu_gain" value="">(195~255)
|
<input maxlength="3" size="5" name="orx_manu_gain" value="">(195~255)
|
||||||
</td>
|
</td>
|
||||||
|
<td>HardwareVersion</td>
|
||||||
|
<td>
|
||||||
|
<select name="hardware_version" id="hardware_version" style="width:100px">
|
||||||
|
<option value="0">V1.0</option>
|
||||||
|
<option value="1">V1.1</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><input type="button" class="btn-modern" disabled id="para_set_id" alt="Set" value="Set" onclick="return set_syspara();"></td>
|
<td><input type="button" class="btn-modern" disabled id="para_set_id" alt="Set" value="Set" onclick="return set_syspara();"></td>
|
||||||
@@ -1596,7 +1676,7 @@ function set_timezone() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<h2>RTC Configuration</h2>
|
<h2>Time & Clock Configuration</h2>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<form id="form_rtc">
|
<form id="form_rtc">
|
||||||
<table>
|
<table>
|
||||||
@@ -1660,11 +1740,11 @@ function set_timezone() {
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
<!-- R4 (2026-08-11): RTC and NTP merged into ONE "Time & Clock Configuration"
|
||||||
|
section, matching the 1.0 (sys_set.html:1556-1642), which holds form_rtc and
|
||||||
<br>
|
form_ntp in a single .box separated by this hairline rule. Both forms, both
|
||||||
<h2>NTP Server Configuration</h2>
|
Set buttons and every field id are unchanged - only the section wrapper is. -->
|
||||||
<div class="box">
|
<hr style="border:none; border-top:1px solid var(--border-subtle); margin:10px 0;">
|
||||||
<form id="form_ntp">
|
<form id="form_ntp">
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -1696,16 +1776,16 @@ function set_timezone() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<h2>SYSTEM KEY</h2>
|
<h2>System Key</h2>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<form id="form_key">
|
<form id="form_key">
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td>PSIP KEY</td>
|
<td>PSIP Key</td>
|
||||||
<td style="width:500px"><fieldset>
|
<td style="width:500px"><fieldset>
|
||||||
<input style="width:100px" maxlength="8" size="9" name="psip_key" id="psip_key" value="" oninput="validateHex(this)">
|
<input style="width:100px" maxlength="8" size="9" name="psip_key" id="psip_key" value="" oninput="validateHex(this)">
|
||||||
</fieldset></td>
|
</fieldset></td>
|
||||||
<td>EAS KEY</td>
|
<td>EAS Key</td>
|
||||||
<td style="width:500px"><fieldset>
|
<td style="width:500px"><fieldset>
|
||||||
<input style="width:100px" maxlength="8" size="9" name="eas_key" id="eas_key" value="" oninput="validateHex(this)">
|
<input style="width:100px" maxlength="8" size="9" name="eas_key" id="eas_key" value="" oninput="validateHex(this)">
|
||||||
</fieldset></td>
|
</fieldset></td>
|
||||||
@@ -1718,51 +1798,51 @@ function set_timezone() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<h2>System Reset Configuration</h2>
|
<h2>Remote Restart</h2>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<form id="form_reset">
|
<form id="form_reset">
|
||||||
<table id="rst_tb">
|
<table id="rst_tb">
|
||||||
<tr>
|
<tr>
|
||||||
<td>System Reset</td>
|
<td>Remote Restart</td>
|
||||||
<td>
|
<td>
|
||||||
<select name="sysreset" id="sysreset">
|
<select name="sysreset" id="sysreset">
|
||||||
<option value="0">No
|
<option value="0">No
|
||||||
<option value="1">Yes
|
<option value="1">Yes
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
<td><input type="button" class="btn-modern" disabled id="sysreset_bt_id" alt="Set" value="Set" onclick="return sysresetff();"> </td>
|
<td><input type="button" class="btn-modern" disabled id="sysreset_bt_id" alt="Restart" value="Restart" onclick="return sysresetff();"> </td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div><span id="rst_note" style="color:#0B77B7; font-size:14px" ></span></div>
|
<div><span id="rst_note" style="color:var(--info-text); font-size:14px" ></span></div>
|
||||||
<br><br>
|
<br><br>
|
||||||
<div>
|
<div>
|
||||||
<progress id="rst_progid" style="width:100%; margin-left:1px; display:none; color:#0bb710;" value="0" max="100"></progress>
|
<progress id="rst_progid" class="aw-progress" style="width:100%; margin-left:1px; display:none" value="0" max="100"></progress>
|
||||||
<span id='rst_progsval' style="color:#0B77B7; font-size:18px;"></span>
|
<span id='rst_progsval' style="color:var(--info-text); font-size:18px;"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<h2>Remote Update</h2>
|
<h2>MCU Update (.tar.gz)</h2>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<form id="formupgrade" target="myIframe" name="formupgrade" method="post" action="upload.cgi" enctype="multipart/form-data">
|
<form id="formupgrade" target="myIframe" name="formupgrade" method="post" action="upload.cgi" enctype="multipart/form-data">
|
||||||
<div id="divupgrade" style="padding: 20px;">
|
<div id="divupgrade" style="padding: 20px;">
|
||||||
<span id="LUpgrade" ></span>
|
<span id="LUpgrade" ></span>
|
||||||
<input name="Upgrade" type="file" id="Upgrade"/>
|
<input name="Upgrade" type="file" id="Upgrade" accept=".tar.gz,.tgz"/>
|
||||||
<input type="button" class="btn-modern" alt="UPGRADE" value="Upgrade" disabled id="upgrade_imx_id" onclick='uploadfile("imx")'>
|
<input type="button" class="btn-modern" alt="MCU UPGRADE" value="Upgrade MCU" disabled id="upgrade_imx_id" onclick='uploadfile("imx")'>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<iframe name="myIframe" id="myIframe" style="display: none;"></iframe>
|
<iframe name="myIframe" id="myIframe" style="display: none;"></iframe>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div><span id="retnote" style="color:#0B77B7; font-size:14px" ></span></div>
|
<div><span id="retnote" style="color:var(--info-text); font-size:14px" ></span></div>
|
||||||
<br><br>
|
<br><br>
|
||||||
<div>
|
<div>
|
||||||
<progress id="prog_id" style="width:100%; margin-left:1px; display:none; color:#0bb710;" value="0" max="100"></progress>
|
<progress id="prog_id" class="aw-progress" style="width:100%; margin-left:1px; display:none" value="0" max="100"></progress>
|
||||||
<br><br>
|
<br><br>
|
||||||
<span id='result' style="color: #0B77B7 ;font-size:18px"></span>
|
<span id='result' style="color:var(--info-text);font-size:18px"></span>
|
||||||
<progress id='progress' style="width:100%; margin-left:1px; color:#0B77B7; font-size:18px" value="0" max="100"></progress>
|
<progress id='progress' class="aw-progress" style="width:100%; margin-left:1px; display:none" value="0" max="100"></progress>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
@@ -1777,11 +1857,11 @@ function set_timezone() {
|
|||||||
</form>
|
</form>
|
||||||
<iframe name="myIframeFpga" id="myIframeFpga" style="display: none;"></iframe>
|
<iframe name="myIframeFpga" id="myIframeFpga" style="display: none;"></iframe>
|
||||||
</div>
|
</div>
|
||||||
<div><span id="fpga_retnote" style="color:#0B77B7; font-size:14px"></span></div>
|
<div><span id="fpga_retnote" style="color:var(--info-text); font-size:14px"></span></div>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
<span id='fpga_result' style="color: #0B77B7 ;font-size:18px"></span>
|
<span id='fpga_result' style="color:var(--info-text);font-size:18px"></span>
|
||||||
<progress id='fpga_progress' style="width:100%; margin-left:1px; color:#0B77B7; font-size:18px" value="0" max="100"></progress>
|
<progress id='fpga_progress' class="aw-progress" style="width:100%; margin-left:1px; display:none" value="0" max="100"></progress>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
Executable → Regular
+352
-77
@@ -3,8 +3,9 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title id="webtitle"></title>
|
<title id="webtitle">Digital Exciter</title>
|
||||||
<link href="css/styles.css" rel="stylesheet" media="screen">
|
<script src="js/theme.js"></script>
|
||||||
|
<link href="css/styles.css" rel="stylesheet" media="screen">
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
<script src="js/jquery.js"></script>
|
<script src="js/jquery.js"></script>
|
||||||
<script src="js/dashboard.js"></script>
|
<script src="js/dashboard.js"></script>
|
||||||
@@ -13,13 +14,26 @@
|
|||||||
.spec-toolbar { display:flex; flex-wrap:wrap; gap:18px; align-items:center; margin-bottom:10px; }
|
.spec-toolbar { display:flex; flex-wrap:wrap; gap:18px; align-items:center; margin-bottom:10px; }
|
||||||
.spec-toolbar label { font-size:13px; color:inherit; }
|
.spec-toolbar label { font-size:13px; color:inherit; }
|
||||||
.spec-readouts { display:flex; flex-wrap:wrap; gap:10px; margin-top:12px; }
|
.spec-readouts { display:flex; flex-wrap:wrap; gap:10px; margin-top:12px; }
|
||||||
.spec-readout { min-width:130px; padding:8px 12px; border:1px solid rgba(148,163,184,0.35); border-radius:6px; }
|
.spec-readout { min-width:110px; padding:6px 8px; border:1px solid rgba(148,163,184,0.35); border-radius:6px; }
|
||||||
.spec-readout .k { display:block; font-size:11px; letter-spacing:.04em; text-transform:uppercase; opacity:.7; }
|
.spec-readout-pair { display:flex; gap:10px; flex:0 0 auto; }
|
||||||
.spec-readout .v { display:block; font-size:18px; font-weight:600; }
|
.spec-readout .k { display:block; font-size:10px; letter-spacing:.04em; text-transform:uppercase; opacity:.7; }
|
||||||
#spec_canvas { width:100%; height:420px; display:block; background:#0b1220; border-radius:6px; }
|
.spec-readout .v { display:block; font-size:16px; font-weight:600; }
|
||||||
#eye_canvas { width:100%; height:380px; display:block; background:#0b1220; border-radius:6px; }
|
#spectrum_panel .spec-readouts { gap:8px; overflow-x:auto; padding-bottom:2px; }
|
||||||
|
#spectrum_panel .spec-readout-pair { gap:8px; }
|
||||||
|
#spec_canvas { width:100%; height:420px; display:block; background:var(--plot-bg); border-radius:6px; }
|
||||||
|
#eye_canvas { width:100%; height:380px; display:block; background:var(--plot-bg); border-radius:6px; }
|
||||||
#eye_state { font-size:13px; opacity:.7; margin-left:auto; }
|
#eye_state { font-size:13px; opacity:.7; margin-left:auto; }
|
||||||
#spec_state { font-size:13px; opacity:.7; margin-left:auto; }
|
#spec_state { font-size:13px; opacity:.7; margin-left:auto; }
|
||||||
|
.spec-tabs { display:flex; gap:0; border-bottom:1px solid rgba(148,163,184,.35); margin-bottom:16px; }
|
||||||
|
.spec-tab { appearance:none; border:0; border-bottom:3px solid transparent; background:transparent; color:inherit; cursor:pointer; font:inherit; font-weight:600; padding:10px 18px; }
|
||||||
|
.spec-tab:hover { background:rgba(148,163,184,.10); }
|
||||||
|
.spec-tab.active { border-bottom-color:var(--status-neutral); color:var(--status-neutral); }
|
||||||
|
.spectrum-panel[hidden] { display:none; }
|
||||||
|
.fssi-refresh-notice { display:none; padding:44px 18px; text-align:center; color:#fbbf24; font-weight:600; }
|
||||||
|
.spectrum-panel.fssi-refresh-active canvas,
|
||||||
|
.spectrum-panel.fssi-refresh-active .spec-readouts,
|
||||||
|
.spectrum-panel.fssi-refresh-active .spec-toolbar { display:none; }
|
||||||
|
.spectrum-panel.fssi-refresh-active .fssi-refresh-notice { display:block; }
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
var lastTime = new Date().getTime();
|
var lastTime = new Date().getTime();
|
||||||
@@ -46,7 +60,11 @@
|
|||||||
$.get("atsc_status_cfg.cgi?sys_cfg=&", function(data, status){
|
$.get("atsc_status_cfg.cgi?sys_cfg=&", function(data, status){
|
||||||
if (typeof data === 'object' && data !== null) return;
|
if (typeof data === 'object' && data !== null) return;
|
||||||
var table = String(data).split("|");
|
var table = String(data).split("|");
|
||||||
document.title = table[0].split("#")[11];
|
var sysCfg = table[0].split("#");
|
||||||
|
var title = sysCfg[6] || "Digital Exciter";
|
||||||
|
document.title = title;
|
||||||
|
document.getElementById('webtitle').textContent = title;
|
||||||
|
document.getElementById('webtitle_header').textContent = title;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,23 +87,74 @@
|
|||||||
var specFails = 0;
|
var specFails = 0;
|
||||||
|
|
||||||
var Y_TOP = 30, Y_BOT = -80; // dB rel in-band flat density
|
var Y_TOP = 30, Y_BOT = -80; // dB rel in-band flat density
|
||||||
var SH_LO = 3.5, SH_HI = 4.8; // shoulder band the app takes the median over
|
/* PR19 measurement windows: P0=-0.25..+0.25 MHz,
|
||||||
|
* PL=-3.5..-3.0 MHz and PR=+3.0..+3.5 MHz. */
|
||||||
|
var SH_LO = 3.0, SH_HI = 3.5;
|
||||||
|
var activeView = 'spectrum';
|
||||||
|
var fssiRefreshActive = false;
|
||||||
|
|
||||||
function specInit(){
|
function specInit(){
|
||||||
window.addEventListener('resize', function(){ if (specLast) drawSpectrum(specLast); else drawGrid(); });
|
window.addEventListener('resize', function(){
|
||||||
drawGrid();
|
if (activeView === 'spectrum') {
|
||||||
specRestart();
|
if (specLast) drawSpectrum(specLast); else drawGrid();
|
||||||
eyeRestart();
|
} else if (eyeLastData) {
|
||||||
|
drawEye(eyeLastData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
selectSpectrumView('spectrum');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* PR12: the Refresh/Pause/Peak-hold toolbar and the status lines were
|
function selectSpectrumView(view) {
|
||||||
* removed on request, so these helpers tolerate the elements being absent
|
activeView = (view === 'eye') ? 'eye' : 'spectrum';
|
||||||
* (fixed 1 s refresh, never paused, no peak-hold). */
|
var spectrumActive = activeView === 'spectrum';
|
||||||
|
document.getElementById('spectrum_panel').hidden = !spectrumActive;
|
||||||
|
document.getElementById('eye_panel').hidden = spectrumActive;
|
||||||
|
document.getElementById('spectrum_tab').classList.toggle('active', spectrumActive);
|
||||||
|
document.getElementById('eye_tab').classList.toggle('active', !spectrumActive);
|
||||||
|
document.getElementById('spectrum_tab').setAttribute('aria-selected', spectrumActive);
|
||||||
|
document.getElementById('eye_tab').setAttribute('aria-selected', !spectrumActive);
|
||||||
|
|
||||||
|
if (fssiRefreshActive) {
|
||||||
|
setState('FSSI Refresh ON');
|
||||||
|
setEyeState('FSSI Refresh ON');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spectrumActive) {
|
||||||
|
if (EYE_TIMER) clearInterval(EYE_TIMER);
|
||||||
|
EYE_TIMER = null;
|
||||||
|
if (specLast) drawSpectrum(specLast); else drawGrid();
|
||||||
|
specRestart();
|
||||||
|
} else {
|
||||||
|
if (SPEC_TIMER) clearInterval(SPEC_TIMER);
|
||||||
|
SPEC_TIMER = null;
|
||||||
|
if (eyeLastData) drawEye(eyeLastData);
|
||||||
|
eyeRestart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Spectrum controls are shared by both tabs. */
|
||||||
function elChecked(id){ var e = document.getElementById(id); return !!(e && e.checked); }
|
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 specRateMs(){ var e = document.getElementById('spec_rate'); return e ? Number(e.value) : 1000; }
|
||||||
|
|
||||||
|
function spectrumControlsChanged(){
|
||||||
|
if (activeView === 'spectrum') {
|
||||||
|
specRestart();
|
||||||
|
} else {
|
||||||
|
eyeRestart();
|
||||||
|
if (elChecked('spec_pause')) setEyeState('paused');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function peakHoldChanged(){
|
||||||
|
specPeak = null;
|
||||||
|
if (activeView === 'spectrum' && specLast) drawSpectrum(specLast);
|
||||||
|
}
|
||||||
|
|
||||||
function specRestart(){
|
function specRestart(){
|
||||||
if (SPEC_TIMER) clearInterval(SPEC_TIMER);
|
if (SPEC_TIMER) clearInterval(SPEC_TIMER);
|
||||||
|
if (activeView !== 'spectrum') return;
|
||||||
|
if (fssiRefreshActive) { setState('FSSI Refresh ON'); return; }
|
||||||
if (elChecked('spec_pause')) { setState('paused'); return; }
|
if (elChecked('spec_pause')) { setState('paused'); return; }
|
||||||
specFetch();
|
specFetch();
|
||||||
SPEC_TIMER = setInterval(specFetch, specRateMs());
|
SPEC_TIMER = setInterval(specFetch, specRateMs());
|
||||||
@@ -94,6 +163,26 @@
|
|||||||
function setState(s){ var e = document.getElementById('spec_state'); if (e) e.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 setEyeState(s){ var e = document.getElementById('eye_state'); if (e) e.innerText = s; }
|
||||||
|
|
||||||
|
window.setSpectrumRefreshActive = function(active){
|
||||||
|
active = !!active;
|
||||||
|
if (fssiRefreshActive === active) return;
|
||||||
|
fssiRefreshActive = active;
|
||||||
|
document.getElementById('spectrum_panel').classList.toggle('fssi-refresh-active', active);
|
||||||
|
document.getElementById('eye_panel').classList.toggle('fssi-refresh-active', active);
|
||||||
|
if (active) {
|
||||||
|
if (SPEC_TIMER) clearInterval(SPEC_TIMER);
|
||||||
|
if (EYE_TIMER) clearInterval(EYE_TIMER);
|
||||||
|
SPEC_TIMER = null;
|
||||||
|
EYE_TIMER = null;
|
||||||
|
setState('FSSI Refresh ON');
|
||||||
|
setEyeState('FSSI Refresh ON');
|
||||||
|
} else if (activeView === 'spectrum') {
|
||||||
|
specRestart();
|
||||||
|
} else {
|
||||||
|
eyeRestart();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
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})
|
||||||
.done(function(d){
|
.done(function(d){
|
||||||
@@ -102,18 +191,73 @@
|
|||||||
setState(d && d.error === 'nodata' ? 'no window captured yet' : 'error');
|
setState(d && d.error === 'nodata' ? 'no window captured yet' : 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (fssiRefreshActive) return;
|
||||||
specFails = 0;
|
specFails = 0;
|
||||||
specLast = d;
|
specLast = d;
|
||||||
updateReadouts(d);
|
if (activeView === 'spectrum') {
|
||||||
drawSpectrum(d);
|
updateReadouts(d);
|
||||||
setState('window ' + d.wid + ' | updated ' + new Date().toLocaleTimeString());
|
drawSpectrum(d);
|
||||||
|
setState('window ' + d.wid + ' | updated ' + new Date().toLocaleTimeString());
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.fail(function(){ specFails++; setState('fetch failed (' + specFails + ')'); });
|
.fail(function(){ specFails++; setState('fetch failed (' + specFails + ')'); });
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateReadouts(d){
|
function updateReadouts(d){
|
||||||
/* PR12: the readout cards under the spectrum were removed; d.shoulder
|
/* The PR19 values are positive P0-P{L,R} attenuation magnitudes. Apply the
|
||||||
* and d.pilot are still drawn on the plot itself in drawSpectrum(). */
|
established +11 dB display correction so these cards match Status/LCD. */
|
||||||
|
var IMD_PILOT_OFFSET = 11; // dB; documented pilot offset (1.0 status_info_new.html)
|
||||||
|
/* Threshold colouring for the three shoulder-suppression readouts, to match
|
||||||
|
the dashboard's scheme (owner, 2026-08-17). The dashboard colours SNR/LIMD/
|
||||||
|
UIMD via getSnrStatus/getImdStatus; SNR here is already coloured below
|
||||||
|
(line ~303). LIMD and UIMD use the dashboard's IMD rule directly: >= 47 dBc
|
||||||
|
green, else red (they already carry the +11 offset so they match the
|
||||||
|
dashboard values). Shoulder has NO pre-existing threshold anywhere; owner
|
||||||
|
ruled it uses the SAME 47 dBc line as the IMDs on its ABSOLUTE value (it is
|
||||||
|
the same kind of dBc shoulder-suppression measure, only signed negative). All
|
||||||
|
OTHER spectrum readouts (FSSI, Pilot, Peak, RBW, ...) stay plain — no invented
|
||||||
|
thresholds. A '--'/NaN reading clears the colour (stays default), never red. */
|
||||||
|
function imdColour(v){ // dBc magnitude vs the 47 dBc pass line
|
||||||
|
return (v === null) ? '' : (Math.abs(v) >= 47 ? 'var(--accent-green)' : 'var(--status-err)');
|
||||||
|
}
|
||||||
|
var shoulderVal = setReadout('rd_shoulder', d.shoulder + IMD_PILOT_OFFSET, 1);
|
||||||
|
var limdVal = setReadout('rd_limd', d.limd + IMD_PILOT_OFFSET, 1);
|
||||||
|
var uimdVal = setReadout('rd_uimd', d.uimd + IMD_PILOT_OFFSET, 1);
|
||||||
|
var shoulderEl = document.getElementById('rd_shoulder');
|
||||||
|
var limdEl = document.getElementById('rd_limd');
|
||||||
|
var uimdEl = document.getElementById('rd_uimd');
|
||||||
|
if (shoulderEl) shoulderEl.style.color = imdColour(shoulderVal);
|
||||||
|
if (limdEl) limdEl.style.color = imdColour(limdVal);
|
||||||
|
if (uimdEl) uimdEl.style.color = imdColour(uimdVal);
|
||||||
|
setReadout('rd_pilot', d.pilot, 4);
|
||||||
|
setReadout('rd_peak', d.peak, 1);
|
||||||
|
setReadout('rd_rbw', d.rbw_khz, 2);
|
||||||
|
setReadout('rd_ms', d.ms, 2);
|
||||||
|
|
||||||
|
var packets = document.getElementById('rd_pkts');
|
||||||
|
if (packets) {
|
||||||
|
packets.innerText = (d.pkts === undefined || d.npkt === undefined) ? '--' : d.pkts + '/' + d.npkt;
|
||||||
|
packets.style.color = (d.pkts === d.npkt) ? '' : 'var(--status-err)';
|
||||||
|
}
|
||||||
|
|
||||||
|
var field = document.getElementById('rd_field');
|
||||||
|
var contiguous = readNumber(d.ms);
|
||||||
|
if (field) {
|
||||||
|
field.innerText = (contiguous === null) ? '--' : (contiguous > 24.2 ? 'yes' : 'no');
|
||||||
|
field.style.color = (contiguous === null) ? '' : (contiguous > 24.2 ? 'var(--accent-green)' : 'var(--status-err)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readNumber(value){
|
||||||
|
var number = Number(value);
|
||||||
|
return isFinite(number) ? number : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setReadout(id, value, decimals){
|
||||||
|
var element = document.getElementById(id);
|
||||||
|
var number = readNumber(value);
|
||||||
|
if (element) element.innerText = (number === null) ? '--' : number.toFixed(decimals);
|
||||||
|
return number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -128,6 +272,8 @@
|
|||||||
|
|
||||||
function eyeRestart(){
|
function eyeRestart(){
|
||||||
if (EYE_TIMER) clearInterval(EYE_TIMER);
|
if (EYE_TIMER) clearInterval(EYE_TIMER);
|
||||||
|
if (activeView !== 'eye') return;
|
||||||
|
if (fssiRefreshActive) { setEyeState('FSSI Refresh ON'); return; }
|
||||||
if (elChecked('spec_pause')) return;
|
if (elChecked('spec_pause')) return;
|
||||||
eyeFetch();
|
eyeFetch();
|
||||||
EYE_TIMER = setInterval(eyeFetch, specRateMs());
|
EYE_TIMER = setInterval(eyeFetch, specRateMs());
|
||||||
@@ -142,24 +288,47 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!d.sync){ setEyeState('no symbol lock'); return; }
|
if (!d.sync){ setEyeState('no symbol lock'); return; }
|
||||||
|
if (fssiRefreshActive) return;
|
||||||
eyeFails = 0;
|
eyeFails = 0;
|
||||||
eyeLastData = d;
|
eyeLastData = d;
|
||||||
eyeReadouts(d);
|
if (activeView === 'eye') {
|
||||||
if (d.wid !== eyeWid){ eyeWid = d.wid; drawEye(d); }
|
eyeReadouts(d);
|
||||||
setEyeState('window ' + d.wid + ' | ' + d.nsym + ' symbols');
|
if (d.wid !== eyeWid){ eyeWid = d.wid; drawEye(d); }
|
||||||
|
setEyeState('window ' + d.wid + ' | ' + d.nsym + ' symbols');
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.fail(function(){ eyeFails++; setEyeState('fetch failed (' + eyeFails + ')'); });
|
.fail(function(){ eyeFails++; setEyeState('fetch failed (' + eyeFails + ')'); });
|
||||||
}
|
}
|
||||||
|
|
||||||
function eyeReadouts(d){
|
function eyeReadouts(d){
|
||||||
/* PR12: only SNR/MER and SNR raw remain on request. */
|
var eyeHeights = Array.isArray(d.eye_h) ? d.eye_h.map(readNumber).filter(function(value){ return value !== null; }) : [];
|
||||||
document.getElementById('rd_snr').innerText = d.snr_db.toFixed(2);
|
var sigma = Array.isArray(d.sigma) ? d.sigma.map(readNumber).filter(function(value){ return value !== null; }) : [];
|
||||||
document.getElementById('rd_snrraw').innerText = (d.snr_raw_db === undefined) ? '--' : d.snr_raw_db.toFixed(2);
|
var minh = eyeHeights.length ? Math.min.apply(null, eyeHeights) : null;
|
||||||
|
var sAvg = sigma.length ? sigma.reduce(function(a, b){ return a + b; }, 0) / sigma.length : null;
|
||||||
|
|
||||||
|
var snr = setReadout('rd_snr', d.snr_db, 2);
|
||||||
|
setReadout('rd_eyeh', minh, 2);
|
||||||
|
setReadout('rd_eyew', d.eye_w_ui, 3);
|
||||||
|
setReadout('rd_tau', d.tau, 4);
|
||||||
|
setReadout('rd_segsync', d.segsync_ratio, 1);
|
||||||
|
setReadout('rd_coff', d.carrier_off_hz, 3);
|
||||||
|
setReadout('rd_snrraw', d.snr_raw_db, 2);
|
||||||
|
var jitter = setReadout('rd_gjit', d.gain_jitter_pct, 2);
|
||||||
|
|
||||||
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');
|
if (sn) sn.style.color = (snr === null) ? '' : (snr >= 27 ? 'var(--accent-green)' : (snr >= 20 ? 'var(--status-warn)' : 'var(--status-err)'));
|
||||||
|
|
||||||
|
var height = document.getElementById('rd_eyeh');
|
||||||
|
if (height) height.style.color = (minh === null || sAvg === null) ? '' :
|
||||||
|
((minh > 0.6 && minh > 2 - 6 * sAvg - 0.35) ? 'var(--accent-green)' : (minh > 0.3 ? 'var(--status-warn)' : 'var(--status-err)'));
|
||||||
|
|
||||||
|
var jitterElement = document.getElementById('rd_gjit');
|
||||||
|
if (jitterElement) jitterElement.style.color = (jitter === null) ? '' :
|
||||||
|
(jitter < 1.0 ? 'var(--accent-green)' : (jitter < 4 ? 'var(--status-warn)' : 'var(--status-err)'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawEye(d){
|
function drawEye(d){
|
||||||
|
var P = PLOT();
|
||||||
var c = document.getElementById('eye_canvas');
|
var c = document.getElementById('eye_canvas');
|
||||||
var dpr = window.devicePixelRatio || 1;
|
var dpr = window.devicePixelRatio || 1;
|
||||||
var W = c.clientWidth, H = c.clientHeight;
|
var W = c.clientWidth, H = c.clientHeight;
|
||||||
@@ -169,7 +338,7 @@
|
|||||||
var g = c.getContext('2d');
|
var g = c.getContext('2d');
|
||||||
g.setTransform(dpr,0,0,dpr,0,0);
|
g.setTransform(dpr,0,0,dpr,0,0);
|
||||||
g.clearRect(0,0,W,H);
|
g.clearRect(0,0,W,H);
|
||||||
g.fillStyle = '#0b1220'; g.fillRect(0,0,W,H);
|
g.fillStyle = P.bg; g.fillRect(0,0,W,H);
|
||||||
|
|
||||||
var cols = d.eye.cols, rows = d.eye.rows, amp = d.eye.amp;
|
var cols = d.eye.cols, rows = d.eye.rows, amp = d.eye.amp;
|
||||||
var bin = atob(d.eye.data), x, y;
|
var bin = atob(d.eye.data), x, y;
|
||||||
@@ -196,30 +365,74 @@
|
|||||||
g.drawImage(off, L, T, W - L - R, H - T - B);
|
g.drawImage(off, L, T, W - L - R, H - T - B);
|
||||||
|
|
||||||
function yOfLvl(v){ return T + (amp - v) / (2 * amp) * (H - T - B); }
|
function yOfLvl(v){ return T + (amp - v) / (2 * amp) * (H - T - B); }
|
||||||
g.strokeStyle = 'rgba(57,208,255,0.45)'; g.lineWidth = 0.7;
|
g.strokeStyle = P.eyeLevel; g.lineWidth = 0.7;
|
||||||
[-7,-5,-3,-1,1,3,5,7].forEach(function(v){
|
[-7,-5,-3,-1,1,3,5,7].forEach(function(v){
|
||||||
var yy = yOfLvl(v);
|
var yy = yOfLvl(v);
|
||||||
g.beginPath(); g.moveTo(L,yy); g.lineTo(W-R,yy); g.stroke();
|
g.beginPath(); g.moveTo(L,yy); g.lineTo(W-R,yy); g.stroke();
|
||||||
});
|
});
|
||||||
var xc = L + (W - L - R) / 2; /* decision instant = centre column */
|
var xc = L + (W - L - R) / 2; /* decision instant = centre column */
|
||||||
g.strokeStyle = '#a3e635'; g.setLineDash([4,3]);
|
g.strokeStyle = P.decision; g.setLineDash([4,3]);
|
||||||
g.beginPath(); g.moveTo(xc,T); g.lineTo(xc,H-B); g.stroke();
|
g.beginPath(); g.moveTo(xc,T); g.lineTo(xc,H-B); g.stroke();
|
||||||
g.setLineDash([]);
|
g.setLineDash([]);
|
||||||
|
|
||||||
g.fillStyle = '#94a3b8'; g.font = '11px sans-serif';
|
g.fillStyle = P.axisLbl; g.font = '11px sans-serif';
|
||||||
g.textAlign = 'right'; g.textBaseline = 'middle';
|
g.textAlign = 'right'; g.textBaseline = 'middle';
|
||||||
[-7,-5,-3,-1,1,3,5,7].forEach(function(v){ g.fillText(v.toFixed(0), L-6, yOfLvl(v)); });
|
[-7,-5,-3,-1,1,3,5,7].forEach(function(v){ g.fillText(v.toFixed(0), L-6, yOfLvl(v)); });
|
||||||
g.textAlign = 'center'; g.textBaseline = 'top';
|
g.textAlign = 'center'; g.textBaseline = 'top';
|
||||||
[-1,-0.5,0,0.5,1].forEach(function(u){
|
[-1,-0.5,0,0.5,1].forEach(function(u){
|
||||||
g.fillText(u.toFixed(1), L + (u+1)/2*(W-L-R), H-B+5);
|
g.fillText(u.toFixed(1), L + (u+1)/2*(W-L-R), H-B+5);
|
||||||
});
|
});
|
||||||
g.fillStyle = '#64748b'; g.textBaseline = 'bottom';
|
g.fillStyle = P.axisTtl; g.textBaseline = 'bottom';
|
||||||
g.fillText('symbol periods (UI) - decision instant at 0', (L+W-R)/2, H-2);
|
g.fillText('symbol periods (UI) - decision instant at 0', (L+W-R)/2, H-2);
|
||||||
g.save(); g.translate(12,(T+H-B)/2); g.rotate(-Math.PI/2);
|
g.save(); g.translate(12,(T+H-B)/2); g.rotate(-Math.PI/2);
|
||||||
g.textBaseline = 'top'; g.fillText('normalised I level', 0, 0); g.restore();
|
g.textBaseline = 'top'; g.fillText('normalised I level', 0, 0); g.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- canvas ---- */
|
/* ---- canvas ---- */
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------
|
||||||
|
R5 (2026-08-11): colour lives in the stylesheet, never in this file.
|
||||||
|
Read once per draw and cached until the theme class changes -- at a 1 Hz
|
||||||
|
redraw the cost is nil, and it means a family colour ruling reaches these
|
||||||
|
plots without anyone editing JavaScript again.
|
||||||
|
Each read carries the pre-R5 literal as its fallback ON PURPOSE: this
|
||||||
|
product has already shipped a unit running a STALE styles.css against a
|
||||||
|
correct package (S3, the white-banner round). If that happens again the
|
||||||
|
plots degrade to exactly today's appearance instead of to black.
|
||||||
|
----------------------------------------------------------------------- */
|
||||||
|
var PLOT = (function(){
|
||||||
|
var cache = null, cacheKey = null;
|
||||||
|
return function(){
|
||||||
|
var key = document.documentElement.className;
|
||||||
|
if (cache && cacheKey === key) return cache;
|
||||||
|
var cs = getComputedStyle(document.documentElement);
|
||||||
|
function v(name, fallback){
|
||||||
|
var got = cs.getPropertyValue(name);
|
||||||
|
got = got ? got.trim() : '';
|
||||||
|
return got || fallback;
|
||||||
|
}
|
||||||
|
cache = {
|
||||||
|
bg: v('--plot-bg', '#0b1220'),
|
||||||
|
grid: v('--plot-grid', '#1e293b'),
|
||||||
|
zero: v('--plot-grid-zero', '#334155'),
|
||||||
|
axisLbl: v('--plot-axis-label', '#94a3b8'),
|
||||||
|
axisTtl: v('--plot-axis-title', '#64748b'),
|
||||||
|
trace: v('--plot-trace', '#38bdf8'),
|
||||||
|
hold: v('--plot-trace-hold', '#f59e0b'),
|
||||||
|
marker: v('--plot-marker', '#f87171'),
|
||||||
|
band: v('--plot-band', 'rgba(56,189,248,0.10)'),
|
||||||
|
eyeLevel: v('--plot-eye-level', 'rgba(57,208,255,0.45)'),
|
||||||
|
decision: v('--plot-eye-decision', '#a3e635'),
|
||||||
|
traceError: v('--plot-trace-error', '#eab308')
|
||||||
|
};
|
||||||
|
cacheKey = key;
|
||||||
|
return cache;
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
/* NOTE: the eye's density colormap (40+255v^0.75 / 20+200v^1.6 / 60+120v^2.6)
|
||||||
|
is an ALGORITHM, not a palette entry, and is left exactly as shipped. It is
|
||||||
|
the reason the plot field stays dark in both themes -- see styles.css. */
|
||||||
|
|
||||||
function specGeom(){
|
function specGeom(){
|
||||||
var c = document.getElementById('spec_canvas');
|
var c = document.getElementById('spec_canvas');
|
||||||
var dpr = window.devicePixelRatio || 1;
|
var dpr = window.devicePixelRatio || 1;
|
||||||
@@ -235,34 +448,39 @@
|
|||||||
function yOf(m, db){ return m.t + (Y_TOP - db)/(Y_TOP - Y_BOT) * (m.h - m.t - m.b); }
|
function yOf(m, db){ return m.t + (Y_TOP - db)/(Y_TOP - Y_BOT) * (m.h - m.t - m.b); }
|
||||||
|
|
||||||
function drawGrid(fmin, fmax){
|
function drawGrid(fmin, fmax){
|
||||||
|
var P = PLOT();
|
||||||
if (fmin === undefined){ fmin = -5; fmax = 5; }
|
if (fmin === undefined){ fmin = -5; fmax = 5; }
|
||||||
var m = specGeom(), g = m.g, f, db;
|
var m = specGeom(), g = m.g, f, db;
|
||||||
g.clearRect(0,0,m.w,m.h);
|
g.clearRect(0,0,m.w,m.h);
|
||||||
g.fillStyle = '#0b1220'; g.fillRect(0,0,m.w,m.h);
|
g.fillStyle = P.bg; g.fillRect(0,0,m.w,m.h);
|
||||||
|
|
||||||
g.fillStyle = 'rgba(56,189,248,0.10)'; // the band the shoulder is measured in
|
g.fillStyle = P.band; // the band the shoulder is measured in
|
||||||
[[SH_LO,SH_HI],[-SH_HI,-SH_LO]].forEach(function(b){
|
[[SH_LO,SH_HI],[-SH_HI,-SH_LO]].forEach(function(b){
|
||||||
var x0 = xOf(m,Math.max(b[0],fmin),fmin,fmax), x1 = xOf(m,Math.min(b[1],fmax),fmin,fmax);
|
var x0 = xOf(m,Math.max(b[0],fmin),fmin,fmax), x1 = xOf(m,Math.min(b[1],fmax),fmin,fmax);
|
||||||
g.fillRect(x0, m.t, x1-x0, m.h-m.t-m.b);
|
g.fillRect(x0, m.t, x1-x0, m.h-m.t-m.b);
|
||||||
});
|
});
|
||||||
|
g.fillStyle = 'rgba(163,230,53,0.08)'; // P0 window
|
||||||
|
g.fillRect(xOf(m,Math.max(-0.25,fmin),fmin,fmax), m.t,
|
||||||
|
xOf(m,Math.min(0.25,fmax),fmin,fmax)-xOf(m,Math.max(-0.25,fmin),fmin,fmax),
|
||||||
|
m.h-m.t-m.b);
|
||||||
|
|
||||||
g.lineWidth = 1;
|
g.lineWidth = 1;
|
||||||
g.font = '11px sans-serif';
|
g.font = '11px sans-serif';
|
||||||
g.textAlign = 'center'; g.textBaseline = 'top';
|
g.textAlign = 'center'; g.textBaseline = 'top';
|
||||||
for (f = Math.ceil(fmin); f <= fmax; f++){
|
for (f = Math.ceil(fmin); f <= fmax; f++){
|
||||||
var x = Math.round(xOf(m,f,fmin,fmax)) + 0.5;
|
var x = Math.round(xOf(m,f,fmin,fmax)) + 0.5;
|
||||||
g.strokeStyle = '#1e293b';
|
g.strokeStyle = P.grid;
|
||||||
g.beginPath(); g.moveTo(x,m.t); g.lineTo(x,m.h-m.b); g.stroke();
|
g.beginPath(); g.moveTo(x,m.t); g.lineTo(x,m.h-m.b); g.stroke();
|
||||||
g.fillStyle = '#94a3b8'; g.fillText(f.toFixed(0), x, m.h-m.b+5);
|
g.fillStyle = P.axisLbl; g.fillText(f.toFixed(0), x, m.h-m.b+5);
|
||||||
}
|
}
|
||||||
g.textAlign = 'right'; g.textBaseline = 'middle';
|
g.textAlign = 'right'; g.textBaseline = 'middle';
|
||||||
for (db = Y_TOP; db >= Y_BOT; db -= 10){
|
for (db = Y_TOP; db >= Y_BOT; db -= 10){
|
||||||
var y = Math.round(yOf(m,db)) + 0.5;
|
var y = Math.round(yOf(m,db)) + 0.5;
|
||||||
g.strokeStyle = (db === 0) ? '#334155' : '#1e293b';
|
g.strokeStyle = (db === 0) ? P.zero : P.grid;
|
||||||
g.beginPath(); g.moveTo(m.l,y); g.lineTo(m.w-m.r,y); g.stroke();
|
g.beginPath(); g.moveTo(m.l,y); g.lineTo(m.w-m.r,y); g.stroke();
|
||||||
g.fillStyle = '#94a3b8'; g.fillText(db.toFixed(0), m.l-6, y);
|
g.fillStyle = P.axisLbl; g.fillText(db.toFixed(0), m.l-6, y);
|
||||||
}
|
}
|
||||||
g.fillStyle = '#64748b'; g.textAlign = 'center'; g.textBaseline = 'bottom';
|
g.fillStyle = P.axisTtl; g.textAlign = 'center'; g.textBaseline = 'bottom';
|
||||||
g.fillText('MHz from carrier', (m.l+m.w-m.r)/2, m.h-2);
|
g.fillText('MHz from carrier', (m.l+m.w-m.r)/2, m.h-2);
|
||||||
g.save(); g.translate(12,(m.t+m.h-m.b)/2); g.rotate(-Math.PI/2);
|
g.save(); g.translate(12,(m.t+m.h-m.b)/2); g.rotate(-Math.PI/2);
|
||||||
g.textBaseline = 'top'; g.fillText('dB rel. in-band flat density', 0, 0); g.restore();
|
g.textBaseline = 'top'; g.fillText('dB rel. in-band flat density', 0, 0); g.restore();
|
||||||
@@ -282,23 +500,36 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function drawSpectrum(d){
|
function drawSpectrum(d){
|
||||||
|
var P = PLOT();
|
||||||
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 (elChecked('spec_hold')){
|
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, P.hold, 1);
|
||||||
} else specPeak = null;
|
} else specPeak = null;
|
||||||
|
|
||||||
trace(m, d.psd, d.f0, d.df, fmin, fmax, '#38bdf8', 1.2);
|
trace(m, d.psd, d.f0, d.df, fmin, fmax, P.trace, 1.2);
|
||||||
|
|
||||||
var y = yOf(m, d.shoulder); // measured shoulder level
|
/* PR19: show the two uncorrected P0-PL/P0-PR shoulder results. The
|
||||||
m.g.strokeStyle = '#f87171'; m.g.setLineDash([5,4]); m.g.lineWidth = 1;
|
* dashboard's LIMD/UIMD adds the established +11 dB correction, so its
|
||||||
m.g.beginPath(); m.g.moveTo(m.l,y); m.g.lineTo(m.w-m.r,y); m.g.stroke();
|
* value is exactly 11 dB above the matching label here. Quantise to the
|
||||||
m.g.setLineDash([]);
|
* same 0.5 dB resolution used by the status CGI. */
|
||||||
m.g.fillStyle = '#f87171'; m.g.textAlign = 'left'; m.g.textBaseline = 'bottom';
|
function drawShoulder(fa, fb, level, value, label, align){
|
||||||
m.g.fillText('shoulder ' + d.shoulder.toFixed(1) + ' dB', m.l+6, y-3);
|
var y = yOf(m, Math.max(Math.min(level,Y_TOP),Y_BOT));
|
||||||
|
var x0 = xOf(m,fa,fmin,fmax), x1 = xOf(m,fb,fmin,fmax);
|
||||||
|
m.g.strokeStyle = P.marker; m.g.setLineDash([5,4]); m.g.lineWidth = 1;
|
||||||
|
m.g.beginPath(); m.g.moveTo(x0,y); m.g.lineTo(x1,y); m.g.stroke();
|
||||||
|
m.g.setLineDash([]);
|
||||||
|
m.g.fillStyle = P.marker; m.g.textAlign = align; m.g.textBaseline = 'bottom';
|
||||||
|
m.g.fillText(label + ' ' + (Math.round(value*2)/2).toFixed(1) + ' dB',
|
||||||
|
align === 'left' ? x0+3 : x1-3, y-3);
|
||||||
|
}
|
||||||
|
if (d.pl_db !== undefined && d.limd !== undefined)
|
||||||
|
drawShoulder(-3.5,-3.0,d.pl_db,d.limd,'L shoulder','left');
|
||||||
|
if (d.pr_db !== undefined && d.uimd !== undefined)
|
||||||
|
drawShoulder(3.0,3.5,d.pr_db,d.uimd,'R shoulder','right');
|
||||||
|
|
||||||
/* PR12: pilot vertical marker removed on request (it covered the pilot
|
/* PR12: pilot vertical marker removed on request (it covered the pilot
|
||||||
* peak). The pilot frequency is still available in d.pilot. */
|
* peak). The pilot frequency is still available in d.pilot. */
|
||||||
@@ -311,7 +542,7 @@
|
|||||||
/* DISABLED on request (2026-07-25): drop the leading `false &&` to restore */
|
/* DISABLED on request (2026-07-25): drop the leading `false &&` to restore */
|
||||||
if (false && eyeLastData && eyeLastData.err && eyeLastData.err.db){
|
if (false && eyeLastData && eyeLastData.err && eyeLastData.err.db){
|
||||||
var er = eyeLastData.err, g2 = m.g, started = false, ei;
|
var er = eyeLastData.err, g2 = m.g, started = false, ei;
|
||||||
g2.strokeStyle = '#eab308'; g2.lineWidth = 1.4; g2.beginPath();
|
g2.strokeStyle = P.traceError; g2.lineWidth = 1.4; g2.beginPath();
|
||||||
for (ei = 0; ei < er.db.length; ei++){
|
for (ei = 0; ei < er.db.length; ei++){
|
||||||
var ef = er.f0 + ei*er.df;
|
var ef = er.f0 + ei*er.df;
|
||||||
if (ef < fmin || ef > fmax){ started = false; continue; }
|
if (ef < fmin || ef > fmax){ started = false; continue; }
|
||||||
@@ -498,12 +729,13 @@
|
|||||||
<div class="app-layout">
|
<div class="app-layout">
|
||||||
<aside class="app-sidebar">
|
<aside class="app-sidebar">
|
||||||
<ul class="items">
|
<ul class="items">
|
||||||
|
<li><a href="overview.html" title="Overview">Overview</a></li>
|
||||||
<li><a href="status_info.html" title="Status">Status</a></li>
|
<li><a href="status_info.html" title="Status">Status</a></li>
|
||||||
<li><a href="sys_set.html" title="Settings">Settings</a></li>
|
<li><a href="sys_set.html" title="Settings">Settings</a></li>
|
||||||
<li><a href="sys_rf.html" title="RF Control">RF Control</a></li>
|
<li><a href="sys_rf.html" title="RF Control">RF Control</a></li>
|
||||||
<li><a href="sys_psip.html" title="TSID&PSIP">TSID&PSIP</a></li>
|
<li><a href="sys_psip.html" title="TSID&PSIP">TSID&PSIP</a></li>
|
||||||
<li><a href="sys_ts.html" title="TSoIP">TSoIP</a></li>
|
<li><a href="sys_ts.html" title="TSoIP">TSoIP</a></li>
|
||||||
<li class="active"><a href="sys_spectrum.html" title="Spectrum">Spectrum</a></li>
|
<li class="active"><a href="sys_spectrum.html" title="RF Performance">RF Performance</a></li>
|
||||||
<li><a href="log_output.html" title="Log">Log</a></li>
|
<li><a href="log_output.html" title="Log">Log</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="sidebar-footer">
|
<div class="sidebar-footer">
|
||||||
@@ -523,8 +755,15 @@
|
|||||||
</button>
|
</button>
|
||||||
<h1><a href="https://anywavecom.net/" class="logo-bg" title="Anywave Broadcast Inc."></a></h1>
|
<h1><a href="https://anywavecom.net/" class="logo-bg" title="Anywave Broadcast Inc."></a></h1>
|
||||||
<h2 id="webtitle_header"><span></span></h2>
|
<h2 id="webtitle_header"><span></span></h2>
|
||||||
<h3>
|
<h3 class="has-toggle">
|
||||||
<div class="dropdown">
|
<div class="utility-cluster">
|
||||||
|
<button class="theme-toggle" onclick="toggleTheme()" aria-label="Switch color theme" title="Switch color theme">
|
||||||
|
<span class="tt-sun" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg></span>
|
||||||
|
<span class="tt-moon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg></span>
|
||||||
|
<span class="tt-label"><span class="tt-to-light">Light Mode</span><span class="tt-to-dark">Dark Mode</span></span>
|
||||||
|
</button>
|
||||||
|
<span class="utility-divider" aria-hidden="true"></span>
|
||||||
|
<div class="dropdown">
|
||||||
<button class="dropbtn-modern">Admin ▾</button>
|
<button class="dropbtn-modern">Admin ▾</button>
|
||||||
<div class="dropdown-content">
|
<div class="dropdown-content">
|
||||||
<a href="#" onclick="openAccountModal(); return false;">
|
<a href="#" onclick="openAccountModal(); return false;">
|
||||||
@@ -544,7 +783,8 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</h3>
|
</div>
|
||||||
|
</h3>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Fixed Dashboard Summary - Top of page, under header -->
|
<!-- Fixed Dashboard Summary - Top of page, under header -->
|
||||||
@@ -570,16 +810,6 @@
|
|||||||
<span class="summary-unit">dB</span>
|
<span class="summary-unit">dB</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="summary-card">
|
|
||||||
<div class="summary-label-row">
|
|
||||||
<span class="status-dot status-ok" id="dot_uimd" aria-hidden="true"></span>
|
|
||||||
<span class="summary-label">UIMD</span>
|
|
||||||
</div>
|
|
||||||
<div class="summary-value-wrapper">
|
|
||||||
<span class="summary-value value-ok" id="sum_uimd">--</span>
|
|
||||||
<span class="summary-unit">dBc</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="summary-card">
|
<div class="summary-card">
|
||||||
<div class="summary-label-row">
|
<div class="summary-label-row">
|
||||||
<span class="status-dot status-ok" id="dot_limd" aria-hidden="true"></span>
|
<span class="status-dot status-ok" id="dot_limd" aria-hidden="true"></span>
|
||||||
@@ -590,6 +820,16 @@
|
|||||||
<span class="summary-unit">dBc</span>
|
<span class="summary-unit">dBc</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="summary-card">
|
||||||
|
<div class="summary-label-row">
|
||||||
|
<span class="status-dot status-ok" id="dot_uimd" aria-hidden="true"></span>
|
||||||
|
<span class="summary-label">UIMD</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-value-wrapper">
|
||||||
|
<span class="summary-value value-ok" id="sum_uimd">--</span>
|
||||||
|
<span class="summary-unit">dBc</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="summary-card">
|
<div class="summary-card">
|
||||||
<div class="summary-label-row">
|
<div class="summary-label-row">
|
||||||
<span class="status-dot status-off" id="dot_rf" aria-hidden="true"></span>
|
<span class="status-dot status-off" id="dot_rf" aria-hidden="true"></span>
|
||||||
@@ -670,27 +910,62 @@
|
|||||||
|
|
||||||
|
|
||||||
<main class="app-main">
|
<main class="app-main">
|
||||||
<h2>ORx Receive Spectrum</h2>
|
<div class="spec-tabs" role="tablist" aria-label="Spectrum display">
|
||||||
<div class="box">
|
<button type="button" class="spec-tab active" id="spectrum_tab" role="tab" aria-selected="true" onclick="selectSpectrumView('spectrum')">Output Spectrum</button>
|
||||||
<!-- PR12: Refresh/Pause/Peak-hold toolbar + status line removed on
|
<button type="button" class="spec-tab" id="eye_tab" role="tab" aria-selected="false" onclick="selectSpectrumView('eye')">8VSB Eye Diagram</button>
|
||||||
request (fixed 1 s refresh). Readout cards were removed earlier;
|
|
||||||
the on-plot shoulder line and yellow error trace remain. -->
|
|
||||||
<canvas id="spec_canvas"></canvas>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>8VSB Eye Diagram</h2>
|
<section class="spectrum-panel" id="spectrum_panel" role="tabpanel" aria-labelledby="spectrum_tab">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
|
<div class="fssi-refresh-notice">FSSI Refresh is ON. Spectrum and Eye Diagram are suspended.</div>
|
||||||
|
<div class="spec-toolbar">
|
||||||
|
<label>Refresh
|
||||||
|
<select id="spec_rate" onchange="spectrumControlsChanged()">
|
||||||
|
<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="spectrumControlsChanged()"> Pause</label>
|
||||||
|
<label><input type="checkbox" id="spec_hold" onchange="peakHoldChanged()"> Peak hold</label>
|
||||||
|
<span id="spec_state">starting…</span>
|
||||||
|
</div>
|
||||||
|
<canvas id="spec_canvas"></canvas>
|
||||||
|
<div class="spec-readouts">
|
||||||
|
<div class="spec-readout" title="ORx front-end signal strength from the FAGC gain index. 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 class="spec-readout" title="Shoulder level, dBc (signed; includes the −11 dB pilot offset)."><span class="k">Shoulder</span><span class="v"><span id="rd_shoulder">--</span> dBc</span></div>
|
||||||
|
<div class="spec-readout-pair">
|
||||||
|
<div class="spec-readout" title="Lower shoulder IMD, dBc (includes the +11 dB pilot offset; matches the dashboard LIMD)."><span class="k">LIMD</span><span class="v"><span id="rd_limd">--</span> dBc</span></div>
|
||||||
|
<div class="spec-readout" title="Upper shoulder IMD, dBc (includes the +11 dB pilot offset; matches the dashboard UIMD)."><span class="k">UIMD</span><span class="v"><span id="rd_uimd">--</span> dBc</span></div>
|
||||||
|
</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">> 1 field</span><span class="v" id="rd_field">--</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="spectrum-panel" id="eye_panel" role="tabpanel" aria-labelledby="eye_tab" hidden>
|
||||||
|
<div class="box">
|
||||||
|
<div class="fssi-refresh-notice">FSSI Refresh is ON. Spectrum and Eye Diagram are suspended.</div>
|
||||||
<!-- PR12: eye caption + status line removed on request. -->
|
<!-- PR12: eye caption + status line removed on request. -->
|
||||||
<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="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="Smallest of the 7 eye openings at the decision instant."><span class="k">Min eye height</span><span class="v" id="rd_eyeh">--</span></div>
|
||||||
<!-- PR13: FSSI from the ORx FAGC gain index (255->0%, 195->100%).
|
<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>
|
||||||
Value comes from the shared 3 s dashboard poll (dashboard.js),
|
<div class="spec-readout" title="Sampling phase offset."><span class="k">τ0</span><span class="v"><span id="rd_tau">--</span> sa</span></div>
|
||||||
not from iq_eye.cgi. Shows "A / B" when FSSI refresh is on. -->
|
<div class="spec-readout" title="Segment synchronization correlation ratio."><span class="k">Seg sync</span><span class="v" id="rd_segsync">--</span></div>
|
||||||
<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 class="spec-readout" title="Pilot phase drift across the window."><span class="k">Carrier offset</span><span class="v"><span id="rd_coff">--</span> Hz</span></div>
|
||||||
|
<div class="spec-readout" title="MER before ORx AGC gain steps are tracked out."><span class="k">SNR raw</span><span class="v"><span id="rd_snrraw">--</span> dB</span></div>
|
||||||
|
<div class="spec-readout" title="RMS of ORx RF AGC gain steps over the window."><span class="k">ORx AGC jitter</span><span class="v"><span id="rd_gjit">--</span> %</span></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
+97
-31
@@ -4,7 +4,8 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title id="webtitle"></title>
|
<title id="webtitle"></title>
|
||||||
<link href="css/styles.css" rel="stylesheet" media="screen">
|
<script src="js/theme.js"></script>
|
||||||
|
<link href="css/styles.css" rel="stylesheet" media="screen">
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
<script src="js/jquery.js"></script>
|
<script src="js/jquery.js"></script>
|
||||||
<script src="js/dashboard.js"></script>
|
<script src="js/dashboard.js"></script>
|
||||||
@@ -58,10 +59,66 @@
|
|||||||
|
|
||||||
var table = data.split("|");
|
var table = data.split("|");
|
||||||
g_remot_local_ctrl = Number(table[0].split("#")[5]);
|
g_remot_local_ctrl = Number(table[0].split("#")[5]);
|
||||||
document.title = table[0].split("#")[11];
|
/* P-4b (2026-08-06): the title write here — document.title =
|
||||||
|
table[0].split("#")[11] — is DELETED. On smod index 11 is ab_mode
|
||||||
|
(the FSSI Refresh switch), not the web title. This page already sets
|
||||||
|
the title from its web_title section below. Round 3 fixed only
|
||||||
|
status_info; this is the rest of that class. */
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
R4 (2026-08-11, owner ruling) — NO UNNAMED VALUE MAY BE POSTED
|
||||||
|
==========================================================================
|
||||||
|
Protocol / Length / Type each carried a third option, "Invalid", and the page
|
||||||
|
clamped any out-of-range firmware value into it (if(i >= 2) i = 2). The owner
|
||||||
|
challenged the premise and was right: these are SETTINGS. The write path posts
|
||||||
|
the raw select value (tsoip_setcfg, "#"+forms.pktp.value), so an operator who
|
||||||
|
pressed Set while a control sat on "Invalid" would send 2 back to the unit — a
|
||||||
|
value this page cannot name. Disabling the option would NOT have closed that:
|
||||||
|
a disabled option can still be the selected one and .value still reads 2.
|
||||||
|
|
||||||
|
So the option is deleted, and the read path is made honest instead: a value we
|
||||||
|
recognise selects its option; anything else leaves the control UNSET, states the
|
||||||
|
reason, and holds this section's Set until the operator picks a real value. The
|
||||||
|
guard only ever blocks; it releases as soon as all three resolve (and never in
|
||||||
|
Local mode, where the button is locked for a different reason). */
|
||||||
|
var TS_ENUM_IDS = ['pktp', 'pklen', 'pkrec'];
|
||||||
|
function setEnumFromUnit(sel, i) {
|
||||||
|
if (!sel) return false;
|
||||||
|
if (i >= 0 && i < sel.options.length) { sel.selectedIndex = i; return true; }
|
||||||
|
sel.selectedIndex = -1; // no phantom slot to land on
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function tsEnumsResolved() {
|
||||||
|
for (var k = 0; k < TS_ENUM_IDS.length; k++) {
|
||||||
|
var sel = document.getElementById(TS_ENUM_IDS[k]);
|
||||||
|
if (!sel || sel.selectedIndex < 0) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
function applyTsEnumGuard() {
|
||||||
|
var ok = tsEnumsResolved();
|
||||||
|
var note = document.getElementById('ts_enum_note');
|
||||||
|
var btn = document.getElementById('setcfg_id1');
|
||||||
|
if (note) note.style.display = ok ? 'none' : 'block';
|
||||||
|
if (!btn) return;
|
||||||
|
if (!ok) { btn.disabled = true; }
|
||||||
|
else if (g_remot_local_ctrl != 1) { btn.disabled = false; }
|
||||||
|
}
|
||||||
|
function attachTsEnumGuard() {
|
||||||
|
for (var k = 0; k < TS_ENUM_IDS.length; k++) {
|
||||||
|
var sel = document.getElementById(TS_ENUM_IDS[k]);
|
||||||
|
if (sel) sel.addEventListener('change', applyTsEnumGuard);
|
||||||
|
}
|
||||||
|
applyTsEnumGuard();
|
||||||
|
}
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', attachTsEnumGuard);
|
||||||
|
} else {
|
||||||
|
attachTsEnumGuard();
|
||||||
|
}
|
||||||
|
|
||||||
function SetFormDefaults() {
|
function SetFormDefaults() {
|
||||||
// 1. DISABLE BUTTONS START (only in Remote mode)
|
// 1. DISABLE BUTTONS START (only in Remote mode)
|
||||||
if(g_remot_local_ctrl == 1) {
|
if(g_remot_local_ctrl == 1) {
|
||||||
@@ -150,17 +207,11 @@
|
|||||||
var lpk_para = Number(tb_systs[ops]);
|
var lpk_para = Number(tb_systs[ops]);
|
||||||
ops++;
|
ops++;
|
||||||
|
|
||||||
i = (lpk_para&0xff0000)>>16;
|
/* R4 (2026-08-11): the three clamps that parked an unrecognised firmware value on
|
||||||
if(i >= 2) i = 2;
|
a phantom "Invalid" option are gone, and so is the option. See setEnumFromUnit. */
|
||||||
fcfg1.pktp.selectedIndex = i;
|
setEnumFromUnit(fcfg1.pktp, (lpk_para&0xff0000)>>16);
|
||||||
|
setEnumFromUnit(fcfg1.pklen, (lpk_para&0x00ff00)>>8);
|
||||||
i = (lpk_para&0x00ff00)>>8;
|
setEnumFromUnit(fcfg1.pkrec, (lpk_para&0x0000ff));
|
||||||
if(i >= 2) i = 2;
|
|
||||||
fcfg1.pklen.selectedIndex = i;
|
|
||||||
|
|
||||||
i = (lpk_para&0x0000ff);
|
|
||||||
if(i >= 2) i = 2;
|
|
||||||
fcfg1.pkrec.selectedIndex = i;
|
|
||||||
|
|
||||||
fcfg1.fec_swh.selectedIndex=Number(tb_systs[ops]);
|
fcfg1.fec_swh.selectedIndex=Number(tb_systs[ops]);
|
||||||
ops++;
|
ops++;
|
||||||
@@ -186,6 +237,7 @@
|
|||||||
// Remote mode: enable button
|
// Remote mode: enable button
|
||||||
$('#setnet_id_1, #setcfg_id1').attr("disabled", false).val("Set");
|
$('#setnet_id_1, #setcfg_id1').attr("disabled", false).val("Set");
|
||||||
}
|
}
|
||||||
|
applyTsEnumGuard(); // R4: re-assert after every readback — only ever blocks
|
||||||
|
|
||||||
if (typeof setFormSnapshotUpdate === 'function') setFormSnapshotUpdate();
|
if (typeof setFormSnapshotUpdate === 'function') setFormSnapshotUpdate();
|
||||||
|
|
||||||
@@ -514,12 +566,13 @@ function cmd_confirm() {
|
|||||||
<div class="app-layout">
|
<div class="app-layout">
|
||||||
<aside class="app-sidebar">
|
<aside class="app-sidebar">
|
||||||
<ul class="items">
|
<ul class="items">
|
||||||
|
<li><a href="overview.html" title="Overview">Overview</a></li>
|
||||||
<li><a href="status_info.html" title="Status">Status</a></li>
|
<li><a href="status_info.html" title="Status">Status</a></li>
|
||||||
<li><a href="sys_set.html" title="Settings">Settings</a></li>
|
<li><a href="sys_set.html" title="Settings">Settings</a></li>
|
||||||
<li><a href="sys_rf.html" title="RF Control">RF Control</a></li>
|
<li><a href="sys_rf.html" title="RF Control">RF Control</a></li>
|
||||||
<li><a href="sys_psip.html" title="TSID&PSIP">TSID&PSIP</a></li>
|
<li><a href="sys_psip.html" title="TSID&PSIP">TSID&PSIP</a></li>
|
||||||
<li class="active"><a href="sys_ts.html" title="TSoIP">TSoIP</a></li>
|
<li class="active"><a href="sys_ts.html" title="TSoIP">TSoIP</a></li>
|
||||||
<li><a href="sys_spectrum.html" title="Spectrum">Spectrum</a></li>
|
<li><a href="sys_spectrum.html" title="RF Performance">RF Performance</a></li>
|
||||||
<li><a href="log_output.html" title="Log">Log</a></li>
|
<li><a href="log_output.html" title="Log">Log</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="sidebar-footer">
|
<div class="sidebar-footer">
|
||||||
@@ -539,8 +592,15 @@ function cmd_confirm() {
|
|||||||
</button>
|
</button>
|
||||||
<h1><a href="https://anywavecom.net/" class="logo-bg" title="Anywave Broadcast Inc."></a></h1>
|
<h1><a href="https://anywavecom.net/" class="logo-bg" title="Anywave Broadcast Inc."></a></h1>
|
||||||
<h2 id="webtitle_header"><span></span></h2>
|
<h2 id="webtitle_header"><span></span></h2>
|
||||||
<h3>
|
<h3 class="has-toggle">
|
||||||
<div class="dropdown">
|
<div class="utility-cluster">
|
||||||
|
<button class="theme-toggle" onclick="toggleTheme()" aria-label="Switch color theme" title="Switch color theme">
|
||||||
|
<span class="tt-sun" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg></span>
|
||||||
|
<span class="tt-moon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg></span>
|
||||||
|
<span class="tt-label"><span class="tt-to-light">Light Mode</span><span class="tt-to-dark">Dark Mode</span></span>
|
||||||
|
</button>
|
||||||
|
<span class="utility-divider" aria-hidden="true"></span>
|
||||||
|
<div class="dropdown">
|
||||||
<button class="dropbtn-modern">Admin ▾</button>
|
<button class="dropbtn-modern">Admin ▾</button>
|
||||||
<div class="dropdown-content">
|
<div class="dropdown-content">
|
||||||
<a href="#" onclick="openAccountModal(); return false;">
|
<a href="#" onclick="openAccountModal(); return false;">
|
||||||
@@ -560,7 +620,8 @@ function cmd_confirm() {
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</h3>
|
</div>
|
||||||
|
</h3>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Fixed Dashboard Summary - Top of page, under header -->
|
<!-- Fixed Dashboard Summary - Top of page, under header -->
|
||||||
@@ -586,16 +647,6 @@ function cmd_confirm() {
|
|||||||
<span class="summary-unit">dB</span>
|
<span class="summary-unit">dB</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="summary-card">
|
|
||||||
<div class="summary-label-row">
|
|
||||||
<span class="status-dot status-ok" id="dot_uimd" aria-hidden="true"></span>
|
|
||||||
<span class="summary-label">UIMD</span>
|
|
||||||
</div>
|
|
||||||
<div class="summary-value-wrapper">
|
|
||||||
<span class="summary-value value-ok" id="sum_uimd">--</span>
|
|
||||||
<span class="summary-unit">dBc</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="summary-card">
|
<div class="summary-card">
|
||||||
<div class="summary-label-row">
|
<div class="summary-label-row">
|
||||||
<span class="status-dot status-ok" id="dot_limd" aria-hidden="true"></span>
|
<span class="status-dot status-ok" id="dot_limd" aria-hidden="true"></span>
|
||||||
@@ -606,6 +657,16 @@ function cmd_confirm() {
|
|||||||
<span class="summary-unit">dBc</span>
|
<span class="summary-unit">dBc</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="summary-card">
|
||||||
|
<div class="summary-label-row">
|
||||||
|
<span class="status-dot status-ok" id="dot_uimd" aria-hidden="true"></span>
|
||||||
|
<span class="summary-label">UIMD</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-value-wrapper">
|
||||||
|
<span class="summary-value value-ok" id="sum_uimd">--</span>
|
||||||
|
<span class="summary-unit">dBc</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="summary-card">
|
<div class="summary-card">
|
||||||
<div class="summary-label-row">
|
<div class="summary-label-row">
|
||||||
<span class="status-dot status-off" id="dot_rf" aria-hidden="true"></span>
|
<span class="status-dot status-off" id="dot_rf" aria-hidden="true"></span>
|
||||||
@@ -720,7 +781,7 @@ function cmd_confirm() {
|
|||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr style="display: none;">
|
<tr style="display: none;">
|
||||||
<td>NET SPEED</td>
|
<td>Net Speed</td>
|
||||||
<td>
|
<td>
|
||||||
<select name="eth_spd" id="eth_spd">
|
<select name="eth_spd" id="eth_spd">
|
||||||
<option value="0">100Mbps
|
<option value="0">100Mbps
|
||||||
@@ -762,7 +823,6 @@ function cmd_confirm() {
|
|||||||
<select name="pktp" id="pktp">
|
<select name="pktp" id="pktp">
|
||||||
<option value="0">UDP
|
<option value="0">UDP
|
||||||
<option value="1">RTP
|
<option value="1">RTP
|
||||||
<option value="2">Invalid
|
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -772,7 +832,6 @@ function cmd_confirm() {
|
|||||||
<select name="pklen" id="pklen">
|
<select name="pklen" id="pklen">
|
||||||
<option value="0">188
|
<option value="0">188
|
||||||
<option value="1">204
|
<option value="1">204
|
||||||
<option value="2">Invalid
|
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
<td>Type</td>
|
<td>Type</td>
|
||||||
@@ -780,7 +839,6 @@ function cmd_confirm() {
|
|||||||
<select name="pkrec" id="pkrec">
|
<select name="pkrec" id="pkrec">
|
||||||
<option value="0">Multicast
|
<option value="0">Multicast
|
||||||
<option value="1">Unicast
|
<option value="1">Unicast
|
||||||
<option value="2">Invalid
|
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -813,6 +871,14 @@ function cmd_confirm() {
|
|||||||
<td> <input type="button" class="btn-modern" alt="Set" value="Set" disabled id="setcfg_id1" onclick="return tsoip_setcfg(0);"> </td>
|
<td> <input type="button" class="btn-modern" alt="Set" value="Set" disabled id="setcfg_id1" onclick="return tsoip_setcfg(0);"> </td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
<!-- R4 (2026-08-11): shown only when the unit reports a Protocol/Length/Type
|
||||||
|
value this page has no name for. Replaces the old "Invalid" option. -->
|
||||||
|
<!-- R6: invented .field-note.is-error dropped. Unrecognised value is a genuine
|
||||||
|
section-level fault -> .warning-banner.is-error (red), per the 3.0 scope rule. -->
|
||||||
|
<div class="warning-banner is-error" id="ts_enum_note" style="display:none">
|
||||||
|
The unit reported a Protocol, Length or Type value this page does not recognise.
|
||||||
|
That control has been left unset and Set is held until a valid value is chosen.
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
ATSC SMOD 工程修改记录
|
||||||
|
|
||||||
|
2026-08-28
|
||||||
|
- 完成 V1.1 硬件功率配置相关调整:默认硬件版本、功率上限及 V1.0/V1.1 切换支持。
|
||||||
|
- 开启切换频点时先降至 -25 dB 的保护流程。
|
||||||
|
- 更新网页系统设置布局,将 HardwareVersion 与 ORX_Manu_Gain 同行,并移除旧提示。
|
||||||
|
- 新增 ADRV9009 射频主链路说明文档 `src/atsc_app/tar_sum_atsc_rf_chain.md`。
|
||||||
|
- 本次修改按用户要求未执行编译验证。
|
||||||
|
|
||||||
|
2026-08-21
|
||||||
|
- 当前工作版本更新为 V136R,尚未作为稳定版本提交。
|
||||||
|
- 已知问题:设备开机后以及 DPD 处理完成后,SNR 和左右带肩数据不能及时更新。
|
||||||
|
- 当前修改已增加 DPD 完成后恢复 ORx FAGC 跟踪的处理,但开机刷新场景及整体效果仍需验证。
|
||||||
|
- 在冷启动和 DPD 两种场景验证通过前,不作为正式 V136R 发布。
|
||||||
|
|
||||||
|
2026-08-20
|
||||||
|
- 初次完整提交,提交基线版本为 V135R。
|
||||||
|
- Git 提交:daf8c673(chore: 初始化 ATSC SMOD 工程仓库)。
|
||||||
Reference in New Issue
Block a user