Files
lolra/ch32v/lib/calculator.html
T
2024-07-09 03:18:12 -07:00

242 lines
7.6 KiB
HTML

<!DOCTYPE html>
<HTML>
<HEAD>
<LINK rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
<META charset="UTF-8">
<STYLE>
body {
background-color: Canvas;
color: CanvasText;
}
:root {
color-scheme: dark;
}
</STYLE>
<SCRIPT src=webhidcontrol.js></SCRIPT>
<SCRIPT>
function DrawSpan( colspan, freq, target, docolor, extrastr = "" )
{
var fdist = Math.abs( freq - target );
fdist = Math.pow( fdist, 0.5 ) * 500;
// if( fdist > 255 ) fdist = 255;
let ret = "<TD COLSPAN=" + colspan + ' ';
if( docolor ) ret += 'STYLE="color:black;background-color:rgb(' + fdist + ',' + (511-fdist) + ',0)";';
ret += '>' + extrastr + freq.toFixed(6) + "</TD>";
return ret;
}
function Goertz( n, mhz, fr, brf )
{
let omega = fr * 3.1415926535*2.0;
var textarea = document.getElementById("goertzeloutput");
var g_goertzel_omega_per_sample = Math.round( ( omega*2*(1<<29)) );
var g_goertzel_coefficient = Math.round( (2 * Math.cos( omega ) * (1<<30)) );
var g_goertzel_coefficient_s = Math.round( (2 * Math.sin( omega ) * (1<<30)) );
textarea.value =
"int g_pwm_period = ("+n+"-1);\n" +
"int g_goertzel_buffer = ("+brf+");\n" +
"int32_t g_goertzel_omega_per_sample = " + g_goertzel_coefficient.toFixed(0) + "; // " + ( omega / (3.1415926535*2.0)).toFixed(6) + " of whole per step / " + mhz.toFixed(6) + "MHz\n" +
"int32_t g_goertzel_coefficient = " + g_goertzel_coefficient.toFixed(0) + ";\n" +
"int32_t g_goertzel_coefficient_s = " + g_goertzel_coefficient_s.toFixed(0) + ";\n";
// Highlight its content
textarea.select();
// Copy the highlighted text
document.execCommand("copy");
updateWebHidDeviceWithParameters( [ (n-1)|0, brf|0, g_goertzel_omega_per_sample|0, g_goertzel_coefficient|0, g_goertzel_coefficient_s|0 ] );
}
function computeTable()
{
let xtal = Number(document.getElementById("crystalmhz").value );
let target = Number(document.getElementById("targetmhz").value );
let quadrature = document.getElementById("QUADRATURE").checked;
let quanta = Math.round(Number(document.getElementById("quanta").value));
let quantasearch = Math.round(Number(document.getElementById("quantasearch").value));
const max_harmonics = 28|0;
const min_harmonics = (quadrature?1:0)|0;
let contents = "";
if( quadrature )
{
contents += "<TABLE>" +
"<TR><TD>Quadrature:</TD></TR>" +
"<TR><TD>I = + + - -</TD></TR>" +
"<TR><TD>Q = + - - +</TD></TR>" +
"<TR><TD>Differntial:</TD></TR>" +
"<TR><TD>V = + - + -</TD></TR>" +
"<TR><TD>You choose the mode you operate in, either Quadrature or differential</TD></TR>" +
"</TABLE>" +
"<p> Table shows: </P>" +
"<TABLE BORDER=1>" +
"<TR><TD>Sample Frequency Harmonic</TD></TR>" +
"<TR><TD>Lower Quadrature Frequency</TD></TR>" +
"<TR><TD>Upper Quadrature Frequency</TD></TR>" +
"<TR><TD>Differential Frequency</TD></TR>" +
"</TABLE>";
}
else
{
contents +=
"<TABLE BORDER=1>" +
"<TR><TD>Goertzel</TD></TR>" +
"<TR><TD>Goertzel (Inverse)</TD></TR>" +
"</TABLE><TEXTAREA ROWS=6 COLS=120 ID=goertzeloutput></TEXTAREA>" +
"<P>Click on a ordinal offset to create the C code needed for that tuning parameter. Clicking will copy-to-clipboard.</P>" +
"<P>N Divisor #30 (row 3) is usually pretty good. And, try to select things near 0.25 / 0.75, and avoid 0.0, 0.5, and 1.0.</P>" +
"<P>Goertzel's mode is for the ch32v203</P>";
}
contents += "<TABLE BORDER=1>";
contents += '<TR><TH>d\\h</div></TH>';
for( let h = 0|min_harmonics; h <= max_harmonics; h++ )
{
contents += "<TH COLSPAN=2>" + h + "</TH>";
}
for( let n = 0|28; n <= 66; n++ )
{
let freq = ( xtal / n );
let goertzelpoint = 0;
let goertzelpointinv = 0;
let tgoertzelp = 0;
let tgoertzelpi = 0;
let quantaA = 0;
let quantaINV = 0;
for( let h = 0|min_harmonics; h <= max_harmonics; h++ )
{
let base = freq * h;
let next = freq * (h+1);
// Round quanta down to next order-of-4-even
for( let tquanta = (quanta&0xffffc) - (quantasearch&0xffffc); tquanta < (quanta&0xffffc) + (quantasearch&0xffffc); tquanta+=4 )
{
if( target <= next && target >= base )
{
var t;
let tgoertzelpoint = ( target - base ) / ( next - base );
tgoertzelpoint = Math.round(tquanta * tgoertzelpoint)/tquanta;
if( Math.abs( freq*(h+tgoertzelpoint) - target ) < Math.abs( freq*(h+goertzelpoint) - target ) )
{
goertzelpoint = tgoertzelpoint;
tgoertzelp = h;
quantaA = tquanta;
}
let tgoertzelpointinv = (1.0 - ( target - base ) / ( next - base ));
tgoertzelpointinv = Math.round(tquanta * tgoertzelpointinv)/tquanta;
if( Math.abs( freq*(h-tgoertzelpointinv+1) - target ) < Math.abs( freq*(h-goertzelpointinv+1) - target ) )
{
goertzelpointinv = tgoertzelpointinv;
tgoertzelpi = h;
quantaINV = tquanta;
}
}
}
}
contents += "</TR>";
for( let mode = 0; mode < 4; mode++ )
{
contents += "<TR>";
if( mode == 0 )
contents += "<TD ROWSPAN=" + 4 + ">" + n + "</TD>";
for( let h = 0|min_harmonics; h <= max_harmonics; h++ )
{
if( quadrature )
{
if( mode == 0 )
contents += DrawSpan( 2, freq * h, target, false );
else if( mode == 1 )
contents += DrawSpan( 2, freq * (h-.25), target, true );
else if( mode == 2 )
contents += DrawSpan( 2, freq * (h+.25), target, true );
else if( mode == 3 )
contents += DrawSpan( 2, freq * (h+0.5), target, true );
}
else
{
if( mode == 0 )
{
contents += "<TD COLSPAN=2>"
if( tgoertzelp == h ) contents += "<SPAN ONCLICK='Goertz(" + n + ", " + freq * (h+goertzelpoint) + ", " + (goertzelpoint) + ", " + quantaA + ")'>↑" + (goertzelpoint).toFixed(6) + "</SPAN>";
contents += "</TD>";
}
else if( mode == 1 )
{
contents += DrawSpan( 2, freq * (h+goertzelpoint), target, true );
}
else if( mode == 2 )
{
contents += "<TD COLSPAN=2>"
if( tgoertzelpi == h-1 ) contents += "<SPAN ONCLICK='Goertz(" + n + ", " + freq * (h-goertzelpointinv) + ", " + goertzelpointinv + ", " + quantaINV + ")'>↓" + goertzelpointinv.toFixed(6) + "</SPAN>";
contents += "</TD>";
}
else if( mode == 3 )
{
contents += DrawSpan( 2, freq * (h-goertzelpointinv), target, true );
}
}
}
contents += "</TD>";
}
}
contents += "</TABLE>";
document.getElementById( "TABLE" ).innerHTML = contents;
}
function onLoad()
{
onLoadWebHidControl();
}
</SCRIPT>
</HEAD>
<BODY onLoad="onLoad()">
<TABLE WIDTH=100%>
<TR>
<TD COLSPAN=3>
<p>Tool for computing tuning to specific frequencies by use of direct ADC reading at specific timer-controlled rate to "tune" to specific frequencies either by quadrature or differential.</p>
</TD>
<TD ROWSPAN=2 VALIGN=TOP WIDTH=100% ID=LiveGraphContainer>
<CANVAS WIDTH=100% HEIGHT=200 ID=LiveGraph> </CANVAS>
</TD>
</TR>
<TR>
<TD VALIGN=TOP>
<TABLE WIDTH=480>
<TR><TD>Crystal MHz</TD><TD><INPUT ID=crystalmhz VALUE=144></TD></TR>
<TR><TD>Target MHz</TD><TD><INPUT ID=targetmhz VALUE=27.019360></TD></TR>
<TR><TD>Quanta</TD><TD><INPUT ID=quanta VALUE=1024> (Goertzel's Only)</TD></TR>
<TR><TD>Quanta Search Range</TD><TD><INPUT ID=quantasearch VALUE=64> (Goertzel's Only)</TD></TR>
<TR><TD>Table Type</TD><TD><INPUT TYPE=RADIO ID=QUADRATURE NAME=computetype checked>Quadrature</INPUT><INPUT TYPE=RADIO ID=GOERTZELS NAME=computetype>Goertzels</INPUT></TD></TR>
<TR><TD COLSPAN=2><INPUT TYPE=SUBMIT VALUE="Compute" ONCLICK="computeTable()"></TD></TR>
</TABLE>
</TD>
<TD VALIGN=TOP>
Live Control:
<TABLE><TR><TD><INPUT TYPE=SUBMIT onClick="reqConnect()" VALUE="Open Device" ID=connectButton></TD><TD><DIV ID=STATUS></DIV></TD></TR></TABLE>
<DIV ID="StatusPerf"></DIV>
</TD>
</TD>
</TR>
</TABLE>
<DIV ID=TABLE></DIV>
</BODY>
</HTML>