首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >来自 VISHAY 的超大动态范围光传感器:VEML7700(YUNSWJ 建模闪烁抑制版)

来自 VISHAY 的超大动态范围光传感器:VEML7700(YUNSWJ 建模闪烁抑制版)

作者头像
云深无际
发布2026-03-05 17:10:16
发布2026-03-05 17:10:16
1060
举报
文章被收录于专栏:云深之无迹云深之无迹

我最近在设计一个亮度计,在做传感器选型的时候发现了一个性能不错的东西,在使用过程中做了一些仿真工作,现在分享出来:

没想到到吧?是它家的东西
没想到到吧?是它家的东西

没想到到吧?是它家的东西

东西很小:

性能也很好
性能也很好

性能也很好

VEML7700 = 光电二极管 + 低噪声放大器 + 积分型 ADC + I2C 数字接口

不是模拟输出,而是:

光强 → 模拟前端积分 → 16bit ADC → I2C 直接读 Lux

它的动态范围非常出色!

测量范围:0 ~ 140,000 lx

分辨率最低:0.0042 lx/bit

ADC 分辨率:16 bit

说明:16 bit = 65536 级

结合增益和积分时间可以覆盖:微光环境(暗屏调光),强光环境(阳光直射)

随便查了一下,最强也就 10w,咱们 14w 非常稳妥
随便查了一下,最强也就 10w,咱们 14w 非常稳妥

随便查了一下,最强也就 10w,咱们 14w 非常稳妥

啊哈?

注意到:

居然有工频干扰
居然有工频干扰

居然有工频干扰

那我问你,里面的 ADC 类型是什么?

结构为:

代码语言:javascript
复制
光电二极管
    ↓
低噪声放大器
    ↓
积分型 ADC
    ↓
数字滤波
    ↓
I2C接口

它是典型的 积分式 ADC 架构(类似 ΔΣ 前端思路)。

当然了,这个东西的寄存器也很简单,是讲 IIC 协议的绝佳好东西,日后课程就讲这个。

详细推导其 100Hz 抑制原理

100 Hz(以及 120 Hz)闪烁抑制,本质来自 VEML7700 的 积分式测量(integrating ADC / integrating measurement),它天然等效于一个“矩形窗平均滤波器”,在频域上就是 sinc(梳状零点),只要积分时间选得合适,就能把 100/120 Hz 压到很低甚至理论为 0;芯片也明确写了支持 100 Hz 与 120 Hz flicker noise rejection。(因为这个内容可以迁移,就建模了)

关于对“讨人厌工频干扰”的测量

LHE7668-工频干扰抑制性能

先把“光闪烁”写成数学模型

LED/室内灯的亮度常见会有随电源产生的调制(很多驱动是整流/纹波),最典型频率就是:

50 Hz 市电 → 亮度纹波常在 100 Hz

60 Hz 市电 → 亮度纹波常在 120 Hz

把入射光照(或等效光电流)写成:

其中 可能是 100 Hz 或 120 Hz。

积分式 ADC 等效“时间平均”

VEML7700 的 ALS 测量设置里有积分时间 ALS_IT(25/50/100/200/400/800 ms)。

在此
在此

在此

积分测量可以抽象成:在一个采样周期里把信号做平均(或积分再归一化):

把 代进去:

常数项积分就是 。关键是余弦项:

整理成幅度形式,会得到一个与起始相位有关的项乘上一个“频率响应包络”。对“抑制能力”最关键的是幅度上界(相位最坏情况)满足:

残留

也就是经典的 sinc

结论:积分时间 越长,且如果 ,该频率分量会被“理论完全抑制(零点)”。

为什么能“卡死”100 Hz 和 120 Hz

sinc 的零点条件是:

也就是:

对 100 Hz:

对 120 Hz:

现在看 VEML7700 的积分时间选项:25/50/100/200/400/800 ms

50 ms = 5×10 ms,同时也是 6×8.333 ms→ 对 100 Hz、120 Hz 都是“整数周期积分”,理论零点。

100/200/400/800 ms 同理:

100 ms = 10×10 ms = 12×8.333 ms

200 ms = 20×10 ms = 24×8.333 ms

400 ms = 40×10 ms = 48×8.333 ms

800 ms = 80×10 ms = 96×8.333 ms→ 对 100/120 Hz 都会落在 sinc 零点上

所以 只要 ALS_IT 选 50 ms 或更长这些“整倍数”选项,100/120 Hz 闪烁分量在理想矩形积分模型下会被强力压制(理论为 0)。

那 25 ms 为什么也能“抑制”(但不一定是零点)

25 ms = 2.5×10 ms(对 100 Hz 不是整数周期),所以 100 Hz 不会是严格零点,但 sinc 仍会给出衰减:

对应约 -17.9 dB 的幅度衰减(功率约 -35.8 dB);而对 120 Hz:120×25 ms = 3(整数),则是零点;因此会看到:短积分时间也有抑制,但最佳 notch 来自“整周期积分”。

怎么选 ALS_IT?

要最强的 100/120 Hz 抑制,优先 ALS_IT = 50 / 100 / 200 / 400 / 800 ms(这些对 100/120 都是整周期积分);如果要更快响应又想有一定抑制:25 ms 也能压,但对 100 Hz 不是“完全消除”;这也和 datasheet 给出的 ALS_IT 可选值以及“100/120 Hz flicker noise rejection”特性相一致。

更近一步

我继续把这个 sinc 推导扩展成“完整离散采样链”版本:包含 积分窗口 + 刷新周期(refresh time) 对频谱的影响(会出现更复杂的梳状谱),并给出在不同 ALS_IT 下对 100/120 Hz 的理论衰减量对比表;100/120 Hz 抑制主要由“积分窗口”决定(sinc 零点),而“刷新周期 Ts(refresh time)”主要决定残余闪烁在输出序列里会变成什么“表观频率”(alias 到低频/直流),但不会把已经被积分削弱的幅度再放大

思路是:推导 → 离散化 → 刷新采样/混叠 → 仿真验证

连续时间:积分窗口 = 矩形窗平均滤波

把光照(或光电流)写成“直流 + 闪烁”:

传感器对每次测量做“积分平均”(积分型 ADC/积分测量等效):

其中 。

把余弦项积分出来(核心一步):

用三角恒等式整理(把相位相关的那部分提出来)后,可得到余弦分量的幅度被一个频率响应系数乘上:

这就是矩形窗平均的 sinc 频响(零点在 )。

100/120 Hz 为什么能被“消到零”(关键是 T 选得对)

零点条件:

100 Hz:周期 10 ms,所以只要 是 10 ms 的整数倍就能“理论全消”

120 Hz:周期 8.333… ms,只要 是 8.333… ms 的整数倍就能“理论全消”

VEML7700 给的 ALS_IT 选项里:50/100/200/400/800 ms 都同时满足:

100 Hz: 个周期

120 Hz: 个周期 因此 100/120 Hz 都落在 sinc 零点上(理想模型下)。

25 ms

对 120 Hz:(零点,理论全消)

对 100 Hz:(不是整数周期,只能衰减) 其幅度衰减:

离散采样:加入 refresh time Ts(输出每 Ts 更新一次)

真实输出不是连续的 ,而是每隔 refresh time 输出一次:

这相当于先用积分窗口滤波(sinc 衰减 100/120),再以 采样输出

对单一正弦闪烁分量:

幅度:仍然是 ,与 Ts 无关(Ts 只改变相位/采样点)

表观频率:采样会把 混叠到

所以 100 Hz 很可能在输出序列里变成“超低频漂移”甚至“接近直流”;这解释了在低刷新率(power saving)下可能看到:即使灯在 100 Hz 闪,输出里也只表现为慢慢抖/慢漂(alias),而不是 100 Hz 的快速抖动。

仿真验证

先用闭式积分公式直接算 (不靠高采样离散近似),对 datasheet power saving 表中的多个(ALS_IT, PSM mode, refresh time)组合,分别对 100/120 Hz:

计算理论衰减

仿真得到输出 AC 的 RMS(去均值后的 rms)

计算 alias 后的表观频率

VEML7700 100/120Hz flicker rejection (integration + refresh sampling)
VEML7700 100/120Hz flicker rejection (integration + refresh sampling)

VEML7700 100/120Hz flicker rejection (integration + refresh sampling)

太长了,放最后。

图 1
图 1

图 1

随 ALS_IT 的变化(100/120 Hz)

图二
图二

图二

(ALS_IT=100ms, Ts=4100ms)输出 AC 几乎为 0(因为 100/120 都在零点上,只剩数值误差量级);这与“积分整周期会把 100/120 卡到零点”的推导完全一致。

代码语言:javascript
复制
[{'ALS_IT (ms)': 100,
'Aliased f_out (Hz)': 4.440892098500626e-15,
'Flicker f (Hz)': 100.0,
'PSM mode': 1,
'Refresh Ts (ms)': 600,
'Residual RMS (A*|H|/sqrt2)': 2.7564237370048413e-17,
'Residual RMS (sim)': 1.0292132110016965e-12,
'Sample rate Fs (Hz)': 1.6666666666666667,
'|H_int(f)| (theory)': 3.898171832519376e-17},
 {'ALS_IT (ms)': 100,
'Aliased f_out (Hz)': 3.3306690738754696e-15,
'Flicker f (Hz)': 100.0,
'PSM mode': 2,
'Refresh Ts (ms)': 1100,
'Residual RMS (A*|H|/sqrt2)': 2.7564237370048413e-17,
'Residual RMS (sim)': 1.5765560160124284e-12,
'Sample rate Fs (Hz)': 0.9090909090909091,
'|H_int(f)| (theory)': 3.898171832519376e-17},
 {'ALS_IT (ms)': 100,
'Aliased f_out (Hz)': 5.551115123125783e-15,
'Flicker f (Hz)': 100.0,
'PSM mode': 3,
'Refresh Ts (ms)': 2100,
'Residual RMS (A*|H|/sqrt2)': 2.7564237370048413e-17,
'Residual RMS (sim)': 3.38815681875831e-12,
'Sample rate Fs (Hz)': 0.47619047619047616,
'|H_int(f)| (theory)': 3.898171832519376e-17},
 {'ALS_IT (ms)': 100,
'Aliased f_out (Hz)': 9.159339953157541e-15,
'Flicker f (Hz)': 100.0,
'PSM mode': 4,
'Refresh Ts (ms)': 4100,
'Residual RMS (A*|H|/sqrt2)': 2.7564237370048413e-17,
'Residual RMS (sim)': 7.003769644029207e-12,
'Sample rate Fs (Hz)': 0.24390243902439027,
'|H_int(f)| (theory)': 3.898171832519376e-17},
 {'ALS_IT (ms)': 200,
'Aliased f_out (Hz)': 2.220446049250313e-15,
'Flicker f (Hz)': 100.0,
'PSM mode': 1,
'Refresh Ts (ms)': 700,
'Residual RMS (A*|H|/sqrt2)': 2.7564237370048413e-17,
'Residual RMS (sim)': 6.631849509652089e-13,
'Sample rate Fs (Hz)': 1.4285714285714286,
'|H_int(f)| (theory)': 3.898171832519376e-17},
 {'ALS_IT (ms)': 200,
'Aliased f_out (Hz)': 4.440892098500626e-15,
'Flicker f (Hz)': 100.0,
'PSM mode': 2,
'Refresh Ts (ms)': 1200,
'Residual RMS (A*|H|/sqrt2)': 2.7564237370048413e-17,
'Residual RMS (sim)': 2.7962493131201986e-13,
'Sample rate Fs (Hz)': 0.8333333333333334,
'|H_int(f)| (theory)': 3.898171832519376e-17},
 {'ALS_IT (ms)': 200,
'Aliased f_out (Hz)': 3.3306690738754696e-15,
'Flicker f (Hz)': 100.0,
'PSM mode': 3,
'Refresh Ts (ms)': 2200,
'Residual RMS (A*|H|/sqrt2)': 2.7564237370048413e-17,
'Residual RMS (sim)': 1.796050142148529e-12,
'Sample rate Fs (Hz)': 0.45454545454545453,
'|H_int(f)| (theory)': 3.898171832519376e-17},
 {'ALS_IT (ms)': 200,
'Aliased f_out (Hz)': 5.551115123125783e-15,
'Flicker f (Hz)': 100.0,
'PSM mode': 4,
'Refresh Ts (ms)': 4200,
'Residual RMS (A*|H|/sqrt2)': 2.7564237370048413e-17,
'Residual RMS (sim)': 3.4456600945190602e-12,
'Sample rate Fs (Hz)': 0.23809523809523808,
'|H_int(f)| (theory)': 3.898171832519376e-17},
 {'ALS_IT (ms)': 400,
'Aliased f_out (Hz)': 4.440892098500626e-15,
'Flicker f (Hz)': 100.0,
'PSM mode': 1,
'Refresh Ts (ms)': 900,
'Residual RMS (A*|H|/sqrt2)': 2.7564237370048413e-17,
'Residual RMS (sim)': 1.4840777570411563e-13,
'Sample rate Fs (Hz)': 1.1111111111111112,
'|H_int(f)| (theory)': 3.898171832519376e-17},
 {'ALS_IT (ms)': 400,
'Aliased f_out (Hz)': 2.220446049250313e-15,
'Flicker f (Hz)': 100.0,
'PSM mode': 2,
'Refresh Ts (ms)': 1400,
'Residual RMS (A*|H|/sqrt2)': 2.7564237370048413e-17,
'Residual RMS (sim)': 1.2013635062764906e-13,
'Sample rate Fs (Hz)': 0.7142857142857143,
'|H_int(f)| (theory)': 3.898171832519376e-17},
 {'ALS_IT (ms)': 400,
'Aliased f_out (Hz)': 4.440892098500626e-15,
'Flicker f (Hz)': 100.0,
'PSM mode': 3,
'Refresh Ts (ms)': 2400,
'Residual RMS (A*|H|/sqrt2)': 2.7564237370048413e-17,
'Residual RMS (sim)': 1.0690675190600384e-12,
'Sample rate Fs (Hz)': 0.4166666666666667,
'|H_int(f)| (theory)': 3.898171832519376e-17},
 {'ALS_IT (ms)': 400,
'Aliased f_out (Hz)': 3.3306690738754696e-15,
'Flicker f (Hz)': 100.0,
'PSM mode': 4,
'Refresh Ts (ms)': 4400,
'Residual RMS (A*|H|/sqrt2)': 2.7564237370048413e-17,
'Residual RMS (sim)': 1.916262794144013e-12,
'Sample rate Fs (Hz)': 0.22727272727272727,
'|H_int(f)| (theory)': 3.898171832519376e-17},
 {'ALS_IT (ms)': 800,
'Aliased f_out (Hz)': 8.881784197001252e-15,
'Flicker f (Hz)': 100.0,
'PSM mode': 1,
'Refresh Ts (ms)': 1300,
'Residual RMS (A*|H|/sqrt2)': 2.7564237370048413e-17,
'Residual RMS (sim)': 1.211411191229325e-13,
'Sample rate Fs (Hz)': 0.7692307692307692,
'|H_int(f)| (theory)': 3.898171832519376e-17},
 {'ALS_IT (ms)': 800,
'Aliased f_out (Hz)': 4.440892098500626e-15,
'Flicker f (Hz)': 100.0,
'PSM mode': 2,
'Refresh Ts (ms)': 1800,
'Residual RMS (A*|H|/sqrt2)': 2.7564237370048413e-17,
'Residual RMS (sim)': 9.305829788260955e-14,
'Sample rate Fs (Hz)': 0.5555555555555556,
'|H_int(f)| (theory)': 3.898171832519376e-17},
 {'ALS_IT (ms)': 800,
'Aliased f_out (Hz)': 2.220446049250313e-15,
'Flicker f (Hz)': 100.0,
'PSM mode': 3,
'Refresh Ts (ms)': 2800,
'Residual RMS (A*|H|/sqrt2)': 2.7564237370048413e-17,
'Residual RMS (sim)': 6.744487182985767e-13,
'Sample rate Fs (Hz)': 0.35714285714285715,
'|H_int(f)| (theory)': 3.898171832519376e-17},
 {'ALS_IT (ms)': 800,
'Aliased f_out (Hz)': 4.440892098500626e-15,
'Flicker f (Hz)': 100.0,
'PSM mode': 4,
'Refresh Ts (ms)': 4800,
'Residual RMS (A*|H|/sqrt2)': 2.7564237370048413e-17,
'Residual RMS (sim)': 9.854802457162338e-14,
'Sample rate Fs (Hz)': 0.20833333333333334,
'|H_int(f)| (theory)': 3.898171832519376e-17},
 {'ALS_IT (ms)': 100,
'Aliased f_out (Hz)': 5.329070518200751e-15,
'Flicker f (Hz)': 120.0,
'PSM mode': 1,
'Refresh Ts (ms)': 600,
'Residual RMS (A*|H|/sqrt2)': 2.756423737004841e-17,
'Residual RMS (sim)': 2.345822114388084e-13,
'Sample rate Fs (Hz)': 1.6666666666666667,
'|H_int(f)| (theory)': 3.8981718325193755e-17},
 {'ALS_IT (ms)': 100,
'Aliased f_out (Hz)': 3.9968028886505635e-15,
'Flicker f (Hz)': 120.0,
'PSM mode': 2,
'Refresh Ts (ms)': 1100,
'Residual RMS (A*|H|/sqrt2)': 2.756423737004841e-17,
'Residual RMS (sim)': 1.7921556589833297e-12,
'Sample rate Fs (Hz)': 0.9090909090909091,
'|H_int(f)| (theory)': 3.8981718325193755e-17},
 {'ALS_IT (ms)': 100,
'Aliased f_out (Hz)': 6.661338147750939e-15,
'Flicker f (Hz)': 120.0,
'PSM mode': 3,
'Refresh Ts (ms)': 2100,
'Residual RMS (A*|H|/sqrt2)': 2.756423737004841e-17,
'Residual RMS (sim)': 2.6547579978994212e-12,
'Sample rate Fs (Hz)': 0.47619047619047616,
'|H_int(f)| (theory)': 3.8981718325193755e-17},
 {'ALS_IT (ms)': 100,
'Aliased f_out (Hz)': 1.099120794378905e-14,
'Flicker f (Hz)': 120.0,
'PSM mode': 4,
'Refresh Ts (ms)': 4100,
'Residual RMS (A*|H|/sqrt2)': 2.756423737004841e-17,
'Residual RMS (sim)': 5.895750347530821e-12,
'Sample rate Fs (Hz)': 0.24390243902439027,
'|H_int(f)| (theory)': 3.8981718325193755e-17},
 {'ALS_IT (ms)': 200,
'Aliased f_out (Hz)': 2.6645352591003757e-15,
'Flicker f (Hz)': 120.0,
'PSM mode': 1,
'Refresh Ts (ms)': 700,
'Residual RMS (A*|H|/sqrt2)': 2.756423737004841e-17,
'Residual RMS (sim)': 5.450190209624567e-13,
'Sample rate Fs (Hz)': 1.4285714285714286,
'|H_int(f)| (theory)': 3.8981718325193755e-17},
 {'ALS_IT (ms)': 200,
'Aliased f_out (Hz)': 5.329070518200751e-15,
'Flicker f (Hz)': 120.0,
'PSM mode': 2,
'Refresh Ts (ms)': 1200,
'Residual RMS (A*|H|/sqrt2)': 2.756423737004841e-17,
'Residual RMS (sim)': 1.0645791361968601e-12,
'Sample rate Fs (Hz)': 0.8333333333333334,
'|H_int(f)| (theory)': 3.8981718325193755e-17},
 {'ALS_IT (ms)': 200,
'Aliased f_out (Hz)': 3.9968028886505635e-15,
'Flicker f (Hz)': 120.0,
'PSM mode': 3,
'Refresh Ts (ms)': 2200,
'Residual RMS (A*|H|/sqrt2)': 2.756423737004841e-17,
'Residual RMS (sim)': 1.6661758038683603e-12,
'Sample rate Fs (Hz)': 0.45454545454545453,
'|H_int(f)| (theory)': 3.8981718325193755e-17},
 {'ALS_IT (ms)': 200,
'Aliased f_out (Hz)': 6.661338147750939e-15,
'Flicker f (Hz)': 120.0,
'PSM mode': 4,
'Refresh Ts (ms)': 4200,
'Residual RMS (A*|H|/sqrt2)': 2.756423737004841e-17,
'Residual RMS (sim)': 3.7249748066843815e-12,
'Sample rate Fs (Hz)': 0.23809523809523808,
'|H_int(f)| (theory)': 3.8981718325193755e-17},
 {'ALS_IT (ms)': 400,
'Aliased f_out (Hz)': 5.329070518200751e-15,
'Flicker f (Hz)': 120.0,
'PSM mode': 1,
'Refresh Ts (ms)': 900,
'Residual RMS (A*|H|/sqrt2)': 2.756423737004841e-17,
'Residual RMS (sim)': 2.0013185800332077e-13,
'Sample rate Fs (Hz)': 1.1111111111111112,
'|H_int(f)| (theory)': 3.8981718325193755e-17},
 {'ALS_IT (ms)': 400,
'Aliased f_out (Hz)': 2.6645352591003757e-15,
'Flicker f (Hz)': 120.0,
'PSM mode': 2,
'Refresh Ts (ms)': 1400,
'Residual RMS (A*|H|/sqrt2)': 2.756423737004841e-17,
'Residual RMS (sim)': 5.01898682545424e-13,
'Sample rate Fs (Hz)': 0.7142857142857143,
'|H_int(f)| (theory)': 3.8981718325193755e-17},
 {'ALS_IT (ms)': 400,
'Aliased f_out (Hz)': 5.329070518200751e-15,
'Flicker f (Hz)': 120.0,
'PSM mode': 3,
'Refresh Ts (ms)': 2400,
'Residual RMS (A*|H|/sqrt2)': 2.756423737004841e-17,
'Residual RMS (sim)': 3.286798044931687e-13,
'Sample rate Fs (Hz)': 0.4166666666666667,
'|H_int(f)| (theory)': 3.8981718325193755e-17},
 {'ALS_IT (ms)': 400,
'Aliased f_out (Hz)': 3.9968028886505635e-15,
'Flicker f (Hz)': 120.0,
'PSM mode': 4,
'Refresh Ts (ms)': 4400,
'Residual RMS (A*|H|/sqrt2)': 2.756423737004841e-17,
'Residual RMS (sim)': 3.5579182447460383e-13,
'Sample rate Fs (Hz)': 0.22727272727272727,
'|H_int(f)| (theory)': 3.8981718325193755e-17},
 {'ALS_IT (ms)': 800,
'Aliased f_out (Hz)': 1.0658141036401503e-14,
'Flicker f (Hz)': 120.0,
'PSM mode': 1,
'Refresh Ts (ms)': 1300,
'Residual RMS (A*|H|/sqrt2)': 2.756423737004841e-17,
'Residual RMS (sim)': 1.916018930536057e-13,
'Sample rate Fs (Hz)': 0.7692307692307692,
'|H_int(f)| (theory)': 3.8981718325193755e-17},
 {'ALS_IT (ms)': 800,
'Aliased f_out (Hz)': 5.329070518200751e-15,
'Flicker f (Hz)': 120.0,
'PSM mode': 2,
'Refresh Ts (ms)': 1800,
'Residual RMS (A*|H|/sqrt2)': 2.756423737004841e-17,
'Residual RMS (sim)': 3.7895292157526576e-13,
'Sample rate Fs (Hz)': 0.5555555555555556,
'|H_int(f)| (theory)': 3.8981718325193755e-17},
 {'ALS_IT (ms)': 800,
'Aliased f_out (Hz)': 2.6645352591003757e-15,
'Flicker f (Hz)': 120.0,
'PSM mode': 3,
'Refresh Ts (ms)': 2800,
'Residual RMS (A*|H|/sqrt2)': 2.756423737004841e-17,
'Residual RMS (sim)': 6.30280040835984e-13,
'Sample rate Fs (Hz)': 0.35714285714285715,
'|H_int(f)| (theory)': 3.8981718325193755e-17},
 {'ALS_IT (ms)': 800,
'Aliased f_out (Hz)': 5.329070518200751e-15,
'Flicker f (Hz)': 120.0,
'PSM mode': 4,
'Refresh Ts (ms)': 4800,
'Residual RMS (A*|H|/sqrt2)': 2.756423737004841e-17,
'Residual RMS (sim)': 1.6358238563673158e-13,
'Sample rate Fs (Hz)': 0.20833333333333334,
'|H_int(f)| (theory)': 3.8981718325193755e-17}]
代码语言:javascript
复制
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# -----------------------------
# VEML7700 flicker rejection simulation
# Model: y[n] = (1/Tint) * ∫_{n*Ts}^{n*Ts+Tint} x(t) dt
# where x(t) = E0 + A*cos(2π f t + φ0)
# This is an "integrate-and-dump" (rectangular window average) sampled every Ts (refresh time).
#
# Theoretical magnitude attenuation at flicker frequency f:
# |H_int(f)| = |sin(π f Tint) / (π f Tint)|  = |sinc(f*Tint)|  (normalized sinc with π inside)
#
# Sampling maps the residual sinusoid to a discrete-time (possibly aliased) frequency,
# but does NOT change its magnitude (only its apparent frequency in the output sequence).
# -----------------------------

def sinc_pi(x):
    """sinc with π inside: sin(πx)/(πx), with sinc(0)=1"""
    x = np.asarray(x, dtype=float)
    y = np.ones_like(x)
    nz = np.abs(x) > 1e-15
    y[nz] = np.sin(np.pi * x[nz]) / (np.pi * x[nz])
    return y

def integrate_average_over_window(f, Tint, t0, phi0, E0=1.0, A=1.0):
    """
    Closed-form window average of E0 + A*cos(2π f t + phi0)
    over [t0, t0+Tint], divided by Tint.
    """
    # Average of constant:
    y = E0
    # Average of cosine:
    # (A/Tint) * ∫ cos(2π f t + phi0) dt = (A/(2π f Tint)) * [sin(2π f t + phi0)]_{t0}^{t0+Tint}
    if f == 0:
        y += A * np.cos(phi0)  # degenerate
    else:
        y += (A / (2 * np.pi * f * Tint)) * (
            np.sin(2 * np.pi * f * (t0 + Tint) + phi0) - np.sin(2 * np.pi * f * t0 + phi0)
        )
    return y

def simulate_sequence(f, Tint, Ts, N=5000, phi0=None, E0=1.0, A=1.0):
    """
    Simulate y[n] for N samples with window average, using closed-form integration.
    """
    if phi0 is None:
        phi0 = np.random.uniform(0, 2*np.pi)
    t0 = np.arange(N) * Ts
    y = np.array([integrate_average_over_window(f, Tint, t, phi0, E0=E0, A=A) for t in t0])
    return y, phi0

def alias_frequency(f, Fs):
    """
    Map analog frequency f (Hz) to aliased frequency in [0, Fs/2] for real-valued sampling.
    """
    if Fs <= 0:
        return np.nan
    # Wrap to [0, Fs)
    f_mod = np.mod(f, Fs)
    # Fold to [0, Fs/2]
    return min(f_mod, Fs - f_mod)

# -----------------------------
# Use refresh-time table from the VEML7700 datasheet (for ALS_GAIN = x2, power-saving enabled):
# ALS_IT: 100/200/400/800 ms, PSM Mode 1..4 -> refresh time as listed.
# -----------------------------
refresh_table_ms = {
    100: {1: 600, 2: 1100, 3: 2100, 4: 4100},
    200: {1: 700, 2: 1200, 3: 2200, 4: 4200},
    400: {1: 900, 2: 1400, 3: 2400, 4: 4400},
    800: {1: 1300, 2: 1800, 3: 2800, 4: 4800},
}

# Flicker frequencies of interest
f_list = [100.0, 120.0]

# Build a comparison table
rows = []
np.random.seed(0)

for Tint_ms, modes in refresh_table_ms.items():
    Tint = Tint_ms / 1000.0
    for mode, Ts_ms in modes.items():
        Ts = Ts_ms / 1000.0
        Fs = 1.0 / Ts
        for f in f_list:
            # Theoretical attenuation magnitude for the integrate window
            att = abs(sinc_pi(f * Tint))  # |sin(pi f Tint)/(pi f Tint)|
            
            # Simulate and measure residual RMS (after removing mean)
            y, phi0 = simulate_sequence(f, Tint, Ts, N=4000, phi0=None, E0=10.0, A=1.0)
            y_ac = y - np.mean(y)
            rms = np.sqrt(np.mean(y_ac**2))
            
            # Expected RMS for a sinusoid of amplitude (A*att) is (A*att)/sqrt(2)
            expected_rms = (1.0 * att) / np.sqrt(2)
            
            rows.append({
                "ALS_IT (ms)": Tint_ms,
                "PSM mode": mode,
                "Refresh Ts (ms)": Ts_ms,
                "Sample rate Fs (Hz)": Fs,
                "Flicker f (Hz)": f,
                "Aliased f_out (Hz)": alias_frequency(f, Fs),
                "|H_int(f)| (theory)": att,
                "Residual RMS (sim)": rms,
                "Residual RMS (A*|H|/sqrt2)": expected_rms,
            })

df = pd.DataFrame(rows)

# Sort for readability
df = df.sort_values(["Flicker f (Hz)", "ALS_IT (ms)", "PSM mode"]).reset_index(drop=True)

# Show the table
# from caas_jupyter_tools import display_dataframe_to_user
# display_dataframe_to_user("VEML7700 100/120Hz flicker rejection (integration + refresh sampling)", df)
import pprint 
pprint.pprint(df.to_dict(orient='records'))
# -----------------------------
# Plot 1: Theoretical attenuation |H_int(f)| vs integration time for 100 and 120 Hz
# -----------------------------
Tint_ms_grid = np.array([25, 50, 100, 200, 400, 800])
Tint_grid = Tint_ms_grid / 1000.0

plt.figure()
for f in f_list:
    att_grid = abs(sinc_pi(f * Tint_grid))
    plt.plot(Tint_ms_grid, att_grid, marker='o', label=f"f = {int(f)} Hz")
plt.xlabel("Integration time ALS_IT (ms)")
plt.ylabel("|H_int(f)| = |sin(π f T)/(π f T)|")
plt.title("Theoretical flicker attenuation from window integration (rectangular average)")
plt.grid(True)
plt.legend()
plt.show()

# -----------------------------
# Plot 2: Example time-domain output showing aliasing frequency (same magnitude, different apparent frequency)
# Choose a configuration where Fs is low enough that 100Hz aliases to a small frequency.
# Example: ALS_IT=100ms, Mode 4 -> Ts=4100ms -> Fs≈0.244Hz, so 100Hz aliases near ~0.0..0.122Hz.
# -----------------------------
Tint_ms = 100
mode = 4
Ts_ms = refresh_table_ms[Tint_ms][mode]
Tint = Tint_ms/1000.0
Ts = Ts_ms/1000.0
Fs = 1.0/Ts

plt.figure()
for f in f_list:
    y, phi0 = simulate_sequence(f, Tint, Ts, N=250, phi0=0.3, E0=10.0, A=1.0)
    y_ac = y - np.mean(y)
    plt.plot(y_ac, label=f"Input flicker {int(f)} Hz, alias {alias_frequency(f, Fs):.4f} Hz")
plt.xlabel("Sample index n (one sample per refresh)")
plt.ylabel("Output AC component (counts, normalized)")
plt.title(f"Example output AC after integration (ALS_IT={Tint_ms}ms) and sampling (Ts={Ts_ms}ms)")
plt.grid(True)
plt.legend()
plt.show()
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-03-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云深之无迹 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 啊哈?
  • 详细推导其 100Hz 抑制原理
  • 先把“光闪烁”写成数学模型
  • 积分式 ADC 等效“时间平均”
  • 为什么能“卡死”100 Hz 和 120 Hz
    • 对 100 Hz:
    • 对 120 Hz:
  • 那 25 ms 为什么也能“抑制”(但不一定是零点)
  • 怎么选 ALS_IT?
  • 更近一步
  • 连续时间:积分窗口 = 矩形窗平均滤波
  • 100/120 Hz 为什么能被“消到零”(关键是 T 选得对)
  • 离散采样:加入 refresh time Ts(输出每 Ts 更新一次)
  • 仿真验证
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档