feat: web beacon marker as two-half filter (Manchester lobes)

Replace the single tracked-beacon line with a filter band spanning the
beacon width, split into two halves (left/right Manchester lobes at
±400 Hz) with a central divider (carrier null) and band edges at
±450 Hz, matching the desktop dec-marker width. Reference frequency is
now a green dashed line. Frontend only (WebPageHtml).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-23 17:17:09 +03:00
co-authored by Claude Opus 4.8
parent 0fbe5d5edd
commit 2ecc0c93e2
+15 -4
View File
@@ -748,11 +748,22 @@ begin
'function drawMk(ctx,w,h){if(!markerOn)return;ctx.save();ctx.strokeStyle="rgba(255,80,80,.85)";ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(markerX,0);ctx.lineTo(markerX,h);ctx.stroke();' + 'function drawMk(ctx,w,h){if(!markerOn)return;ctx.save();ctx.strokeStyle="rgba(255,80,80,.85)";ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(markerX,0);ctx.lineTo(markerX,h);ctx.stroke();' +
'const lbl=((xToHz(markerX,w))/1e6).toFixed(4)+" M";ctx.fillStyle="rgba(35,5,5,.82)";ctx.fillRect(cl(markerX+5,0,w-88),h-16,84,13);ctx.fillStyle="#f88";ctx.font="10px monospace";ctx.fillText(lbl,cl(markerX+8,2,w-86),h-6);ctx.restore();}' + 'const lbl=((xToHz(markerX,w))/1e6).toFixed(4)+" M";ctx.fillStyle="rgba(35,5,5,.82)";ctx.fillRect(cl(markerX+5,0,w-88),h-16,84,13);ctx.fillStyle="#f88";ctx.font="10px monospace";ctx.fillText(lbl,cl(markerX+8,2,w-86),h-6);ctx.restore();}' +
// Маркеры QO-100 маяка: опорная (зелёная) + измеренная/наведение (оранжевая). // Маркеры QO-100 маяка: опорная (зелёная пунктир) + ФИЛЬТР по ширине маяка из
'function vbar(ctx,x,w,h,col){if(x<0||x>w)return;ctx.strokeStyle=col;ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(x+.5,0);ctx.lineTo(x+.5,h);ctx.stroke();}' + // ДВУХ ПОЛОВИНОК (две несущие/лепестка Manchester ±400Гц, нуль по центру).
'var BCN_HALF=450;' + // полуширина фильтра маяка, Гц (как десктоп dec-маркер)
'function vbar(ctx,x,h,col,w){if(x<0||x>w)return;ctx.strokeStyle=col;ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(x+.5,0);ctx.lineTo(x+.5,h);ctx.stroke();}' +
'function drawBcn(ctx,w,h){if(!cur.beacon_on)return;ctx.save();' + 'function drawBcn(ctx,w,h){if(!cur.beacon_on)return;ctx.save();' +
'if(cur.beacon_ref_hz>0)vbar(ctx,hzToX(cur.beacon_ref_hz,w),w,h,"rgba(80,255,120,.8)");' + // Опорная — где маяк ДОЛЖЕН быть.
'if(cur.beacon_track_hz>0)vbar(ctx,hzToX(cur.beacon_track_hz,w),w,h,"rgba(255,170,60,.85)");' + 'if(cur.beacon_ref_hz>0){ctx.setLineDash([4,3]);vbar(ctx,hzToX(cur.beacon_ref_hz,w),h,"rgba(80,255,120,.85)",w);ctx.setLineDash([]);}' +
// Фильтр маяка вокруг измеренной частоты: левая+правая половины (лепестки) с
// разделом по центру (нуль несущей) и гранями полосы.
'var t=cur.beacon_track_hz;' +
'if(t>0){var xL=hzToX(t-BCN_HALF,w),xC=hzToX(t,w),xR=hzToX(t+BCN_HALF,w);' +
'ctx.fillStyle="rgba(255,170,60,.14)";' +
'ctx.fillRect(xL,0,Math.max(0,xC-xL-1),h);' + // левая половина
'ctx.fillRect(xC+1,0,Math.max(0,xR-xC-1),h);' + // правая половина
'vbar(ctx,xL,h,"rgba(255,170,60,.75)",w);vbar(ctx,xR,h,"rgba(255,170,60,.75)",w);' + // грани
'vbar(ctx,xC,h,"rgba(255,170,60,.4)",w);}' + // центр (нуль/раздел)
'ctx.restore();}' + 'ctx.restore();}' +
'function drawRuler(){const w=rulerCv.clientWidth,h=rulerCv.clientHeight;rctx.fillStyle="#0c1210";rctx.fillRect(0,0,w,h);' + 'function drawRuler(){const w=rulerCv.clientWidth,h=rulerCv.clientHeight;rctx.fillStyle="#0c1210";rctx.fillRect(0,0,w,h);' +