mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
feat(fm): add universal FM RAW decoder output
This commit is contained in:
+15
-12
@@ -193,7 +193,7 @@ begin
|
||||
'<select id="modGrpSel">' +
|
||||
'<option value="ssb" selected>SSB</option><option value="cw">CW</option>' +
|
||||
'<option value="fm">FM</option><option value="am">AM</option>' +
|
||||
'<option value="digi">DIGI</option><option value="dmr">DMR</option>' +
|
||||
'<option value="digi">DIGI</option><option value="dmr">DMR</option><option value="raw">FM RAW</option>' +
|
||||
'</select>' +
|
||||
'<select id="stepSel">' +
|
||||
'<option value="10">10 Hz</option><option value="100" selected>100 Hz</option>' +
|
||||
@@ -299,14 +299,15 @@ begin
|
||||
'function updRfAgc(){const b=q("rfagcBtn");if(!b)return;b.style.display=cur.rf_agc_visible?"":"none";b.textContent="RF AGC "+rfagcModeName(cur.rx_gain_mode);if(rfagcEls)rfagcRefresh();}' +
|
||||
|
||||
|
||||
'const MODE_N=["LSB","USB","DSB","CWL","CWU","FM","AM","SAM","DIGU","DIGL","WFM","DMR"];' +
|
||||
'const GRP_MAP={ssb:[0,1],cw:[3,4],fm:[5,10],am:[6,7],digi:[8,9],dmr:[11,11]};' +
|
||||
'const MODE_TO_GRP=["ssb","ssb","ssb","cw","cw","fm","am","am","digi","digi","fm","dmr"];' +
|
||||
'const MODE_N=["LSB","USB","DSB","CWL","CWU","FM","AM","SAM","DIGU","DIGL","WFM","DMR","FM RAW"];' +
|
||||
'const M_LSB=MODE_N.indexOf("LSB"),M_USB=MODE_N.indexOf("USB"),M_DSB=MODE_N.indexOf("DSB"),M_CWL=MODE_N.indexOf("CWL"),M_CWU=MODE_N.indexOf("CWU"),M_FM=MODE_N.indexOf("FM"),M_AM=MODE_N.indexOf("AM"),M_SAM=MODE_N.indexOf("SAM"),M_DIGU=MODE_N.indexOf("DIGU"),M_DIGL=MODE_N.indexOf("DIGL"),M_WFM=MODE_N.indexOf("WFM"),M_DMR=MODE_N.indexOf("DMR"),M_FMRAW=MODE_N.indexOf("FM RAW");' +
|
||||
'const GRP_MAP={ssb:[M_LSB,M_USB],cw:[M_CWL,M_CWU],fm:[M_FM,M_WFM],am:[M_AM,M_SAM],digi:[M_DIGU,M_DIGL],dmr:[M_DMR,M_DMR],raw:[M_FMRAW,M_FMRAW]};' +
|
||||
'const MODE_TO_GRP=[];Object.entries(GRP_MAP).forEach(([g,ms])=>ms.forEach(m=>MODE_TO_GRP[m]=g));MODE_TO_GRP[M_DSB]="ssb";' +
|
||||
'function modGrpFromMode(m){return MODE_TO_GRP[m]||"ssb";}' +
|
||||
'function modGrp(){return q("modGrpSel").value||"ssb";}' +
|
||||
'function modTglTarget(){const grp=GRP_MAP[modGrp()];const m=cur.mode||0;return(m===grp[0])?grp[1]:grp[0];}' +
|
||||
'function updModTglLabel(){const b=q("modTglBtn");if(!b)return;const grp=GRP_MAP[modGrp()];const m=cur.mode||0;const show=(m===grp[0]||m===grp[1])?m:grp[0];b.textContent=MODE_N[show];}' +
|
||||
'function syncModGrp(m){const g=MODE_TO_GRP[m]||"ssb";const s=q("modGrpSel");if(s&&s.value!==g)s.value=g;const f=q("fnpBtn");if(f)f.style.display=(m===11)?"none":"";updModTglLabel();}' +
|
||||
'function syncModGrp(m){const g=MODE_TO_GRP[m]||"ssb";const s=q("modGrpSel");if(s&&s.value!==g)s.value=g;const f=q("fnpBtn");if(f)f.style.display=(m===M_DMR)?"none":"";updModTglLabel();}' +
|
||||
'q("modGrpSel").onchange=function(){const grp=GRP_MAP[modGrp()];const defaultMode=grp[0];cur.mode=defaultMode;syncModGrp(defaultMode);pend("mode");ws2({cmd:"mode",mode:defaultMode});};' +
|
||||
'q("modTglBtn").onclick=function(){audioKick();const next=modTglTarget();cur.mode=next;syncModGrp(next);pend("mode");ws2({cmd:"mode",mode:next});};' +
|
||||
'q("agcSel").onchange=function(e){pend("agc");ws2({cmd:"agc",mode:+e.target.value});};' +
|
||||
@@ -320,6 +321,7 @@ begin
|
||||
'fm:["NFM","FM"],' +
|
||||
'wfm:["200k","160k","120k"],' +
|
||||
'dmr:["12.5k"],' +
|
||||
'raw:["6.25k","10k","12.5k","20k","25k"],' +
|
||||
'digi:["3.0k","2.7k","2.4k","2.1k","1.8k","1.5k","1.2k","900","600","300"]};' +
|
||||
'var FNP_BW={ssb:[5000,4400,3800,3300,2900,2700,2400,2100,1800,1000],' +
|
||||
'dsb:[16000,12000,10000,8000,6600,5200,4000,3100,2900,2400],' +
|
||||
@@ -328,13 +330,14 @@ begin
|
||||
'fm:[11000,16000],' +
|
||||
'wfm:[200000,160000,120000],' +
|
||||
'dmr:[12500],' +
|
||||
'raw:[6250,10000,12500,20000,25000],' +
|
||||
'digi:[3000,2700,2400,2100,1800,1500,1200,900,600,300]};' +
|
||||
'var fnpOv=null;' +
|
||||
'function fnpGrp(){var m=cur.mode|0;return(m===3||m===4)?"cw":(m===5)?"fm":(m===10)?"wfm":(m===11)?"dmr":(m===8||m===9)?"digi":(m===2)?"dsb":(m===6||m===7)?"am":"ssb";}' +
|
||||
'function fnpGrp(){var m=cur.mode|0;return(m===M_CWL||m===M_CWU)?"cw":(m===M_FM)?"fm":(m===M_WFM)?"wfm":(m===M_DMR)?"dmr":(m===M_FMRAW)?"raw":(m===M_DIGU||m===M_DIGL)?"digi":(m===M_DSB)?"dsb":(m===M_AM||m===M_SAM)?"am":"ssb";}' +
|
||||
'function closeFnp(){if(fnpOv){fnpOv.remove();fnpOv=null;}document.removeEventListener("click",fnpOutside);}' +
|
||||
'function fnpOutside(e){if(fnpOv&&!fnpOv.contains(e.target)&&e.target!==q("fnpBtn"))closeFnp();}' +
|
||||
'function showFnp(){' +
|
||||
'if((cur.mode|0)===11)return;' +
|
||||
'if((cur.mode|0)===M_DMR)return;' +
|
||||
'if(fnpOv){closeFnp();return;}' +
|
||||
'var g=fnpGrp(),ns=FNP_N[g],bws=FNP_BW[g],cb=cur.filter_bw||2700;' +
|
||||
'fnpOv=document.createElement("div");' +
|
||||
@@ -791,7 +794,7 @@ begin
|
||||
'const nowTx=!!cur.transmitting;if(nowTx!==prevTx){prevTx=nowTx;if(nowTx)startTXMic();else stopTXMic();}}' +
|
||||
|
||||
'function pal(db){const v=Math.round(cl((db-wfLo)/Math.max(1,wfHi-wfLo),0,1)*255);const c=PAL[v]|0;return[(c>>16)&255,(c>>8)&255,c&255];}' +
|
||||
'function fltLH(){const bw=cur.filter_bw||2700;const m=cur.mode|0;if(m===0)return[-bw,-100];if(m===1)return[100,bw];return[-bw/2,bw/2];}' +
|
||||
'function fltLH(){const bw=cur.filter_bw||2700;const m=cur.mode|0;if(m===M_LSB)return[-bw,-100];if(m===M_USB)return[100,bw];return[-bw/2,bw/2];}' +
|
||||
'function xToHz(x,w){return localCenter+((x/Math.max(1,w))-.5)*localSpan;}' +
|
||||
'function hzToX(f,w){return((f-localCenter)/localSpan+.5)*w;}' +
|
||||
|
||||
@@ -817,7 +820,7 @@ begin
|
||||
|
||||
'function drawRuler(){const w=rulerCv.clientWidth,h=rulerCv.clientHeight;rctx.fillStyle="#0c1210";rctx.fillRect(0,0,w,h);' +
|
||||
'const st=localCenter-localSpan/2;' +
|
||||
'const isFM=(cur.mode===5||cur.mode===11),fmStep=FM_STEP_HZ[fmStepIdx];' +
|
||||
'const isFM=(cur.mode===M_FM||cur.mode===M_DMR||cur.mode===M_FMRAW),fmStep=FM_STEP_HZ[fmStepIdx];' +
|
||||
'if(isFM&&fmStep>0){' +
|
||||
'const pxPerStep=w*fmStep/localSpan;' +
|
||||
'const labelMult=Math.max(1,Math.ceil((rctx.measureText("000.000").width+6)/pxPerStep));' +
|
||||
@@ -832,7 +835,7 @@ begin
|
||||
|
||||
'function drawSpec(bins){const w=specCv.clientWidth,h=specCv.clientHeight;sctx.fillStyle="#050a05";sctx.fillRect(0,0,w,h);' +
|
||||
'for(let i=1;i<5;i++){const y=i*h/5;sctx.strokeStyle="#121e12";sctx.lineWidth=1;sctx.beginPath();sctx.moveTo(0,y);sctx.lineTo(w,y);sctx.stroke();}' +
|
||||
'if((cur.mode===5||cur.mode===11)&&FM_STEP_HZ[fmStepIdx]>0){' +
|
||||
'if((cur.mode===M_FM||cur.mode===M_DMR||cur.mode===M_FMRAW)&&FM_STEP_HZ[fmStepIdx]>0){' +
|
||||
'const fmSt=FM_STEP_HZ[fmStepIdx],st2=localCenter-localSpan/2;' +
|
||||
'const pxSt=w*fmSt/localSpan,gm=Math.max(1,Math.ceil(4/pxSt));' +
|
||||
'const fn=Math.ceil(st2/fmSt);' +
|
||||
@@ -875,7 +878,7 @@ begin
|
||||
'if(smCv.width!==(sw*dpr|0)||smCv.height!==(sh*dpr|0)){smCv.width=sw*dpr|0;smCv.height=sh*dpr|0;mctx.setTransform(dpr,0,0,dpr,0,0);}' +
|
||||
'if(wfBuf.width!==wfCv.clientWidth||wfBuf.height!==wfCv.clientHeight){wfBuf.width=wfCv.clientWidth;wfBuf.height=wfCv.clientHeight;wfBCtx.fillStyle="#000";wfBCtx.fillRect(0,0,wfBuf.width,wfBuf.height);}}' +
|
||||
|
||||
'function syncStepSel(){const isFM=(cur.mode===5||cur.mode===11);const sel=q("stepSel");if(!sel)return;' +
|
||||
'function syncStepSel(){const isFM=(cur.mode===M_FM||cur.mode===M_DMR||cur.mode===M_FMRAW);const sel=q("stepSel");if(!sel)return;' +
|
||||
'if(isFM&&!inFMStep){lastHFStep=+sel.value||100;sel.innerHTML="";' +
|
||||
'FM_STEP_LBL.forEach((l,i)=>{const o=document.createElement("option");o.value=FM_STEP_HZ[i];o.textContent=l;sel.appendChild(o);});' +
|
||||
'sel.value=FM_STEP_HZ[fmStepIdx];' +
|
||||
@@ -937,7 +940,7 @@ begin
|
||||
'if(dragging&&Math.abs(e.offsetX-dragX0)<4){' +
|
||||
// клик — не трогаем localCenter: фильтр встаёт точно туда куда кликнули.
|
||||
// Спектр остаётся на месте, сервер пришлёт center_hz через ~200ms и всё перецентрируется.
|
||||
'const snap=((cur.mode===5||cur.mode===11)&&inFMStep)?FM_STEP_HZ[fmStepIdx]:100;' +
|
||||
'const snap=((cur.mode===M_FM||cur.mode===M_DMR||cur.mode===M_FMRAW)&&inFMStep)?FM_STEP_HZ[fmStepIdx]:100;' +
|
||||
'const nf=cl(Math.round(xToHz(e.offsetX,cv.clientWidth)/snap)*snap,vfoMin(),vfoMax());' +
|
||||
'setActiveVfoHz(nf);' +
|
||||
'if(activeVfo==="A")cur.vfo_a_hz=nf;else cur.vfo_b_hz=nf;' +
|
||||
|
||||
Reference in New Issue
Block a user