Added more sliders and buttons, waterfall colors are now adjustable from the GUI.

This commit is contained in:
ha7ilm 2016-03-20 11:32:37 +01:00
parent 3c1d3b5b42
commit 06bd8b92aa
3 changed files with 266 additions and 144 deletions

View File

@ -1,7 +1,7 @@
<!DOCTYPE HTML>
<!--
This file is part of OpenWebRX,
This file is part of OpenWebRX,
an open-source SDR receiver software with a web UI.
Copyright (c) 2013-2015 by Andras Retzler <randras@sdr.hu>
@ -31,8 +31,8 @@
var starting_mod = "%[START_MOD]";
var starting_offset_frequency = %[START_OFFSET_FREQ];
var waterfall_colors=%[WATERFALL_COLORS];
var waterfall_min_level=%[WATERFALL_MIN_LEVEL];
var waterfall_max_level=%[WATERFALL_MAX_LEVEL];
var waterfall_min_level_default=%[WATERFALL_MIN_LEVEL];
var waterfall_max_level_default=%[WATERFALL_MAX_LEVEL];
</script>
<script src="sdr.js"></script>
<script src="openwebrx.js"></script>
@ -42,8 +42,8 @@
<body onload="openwebrx_init();">
<div id="webrx-page-container">
<div id="webrx-top-container">
<div id="webrx-top-photo-clip">
<img src="gfx/openwebrx-top-photo.jpg" id="webrx-top-photo"/>
<div id="webrx-top-photo-clip">
<img src="gfx/openwebrx-top-photo.jpg" id="webrx-top-photo"/>
<div id="webrx-rx-photo-title">%[RX_PHOTO_TITLE]</div>
<div id="webrx-rx-photo-desc">%[RX_PHOTO_DESC]</div>
</div>
@ -72,27 +72,43 @@
<div id="openwebrx-scale-container">
<canvas id="openwebrx-scale-canvas" width="0" height="0"></canvas>
</div>
<div id="webrx-canvas-container">
<div id="openwebrx-phantom-canvas"></div>
<!-- add canvas here by javascript -->
</div>
<div id="openwebrx-panels-container">
<div class="openwebrx-panel" id="openwebrx-panel-receiver" data-panel-name="client-params" data-panel-pos="right" data-panel-order="0" data-panel-size="215,115">
<div class="openwebrx-panel" id="openwebrx-panel-receiver" data-panel-name="client-params" data-panel-pos="right" data-panel-order="0" data-panel-size="259,115">
<div id="webrx-actual-freq">---.--- MHz</div>
<div id="webrx-mouse-freq">---.--- MHz</div>
<!--<div class="openwebrx-button" onclick="ws.send('SET mod=wfm');" >WFM</div>-->
<div class="openwebrx-panel-line">
<div class="openwebrx-button" onclick="demodulator_analog_replace('nfm');">FM</div>
<div class="openwebrx-button" onclick="demodulator_analog_replace('am');">AM</div>
<div class="openwebrx-button" onclick="demodulator_analog_replace('lsb');">LSB</div>
<div class="openwebrx-button" onclick="demodulator_analog_replace('usb');">USB</div>
<div class="openwebrx-button" onclick="demodulator_analog_replace('cw');">CW</div>
<div class="openwebrx-button openwebrx-demodulator-button" onclick="demodulator_analog_replace('nfm');">FM</div>
<div class="openwebrx-button openwebrx-demodulator-button" onclick="demodulator_analog_replace('am');">AM</div>
<div class="openwebrx-button openwebrx-demodulator-button" onclick="demodulator_analog_replace('lsb');">LSB</div>
<div class="openwebrx-button openwebrx-demodulator-button" onclick="demodulator_analog_replace('usb');">USB</div>
<div class="openwebrx-button openwebrx-demodulator-button" onclick="demodulator_analog_replace('cw');">CW</div>
</div>
<div class="openwebrx-panel-line">
<div id="openwebrx-mute-off" class="openwebrx-button" onclick="toggleMute()"><img src="gfx/openwebrx-speaker.png" id="openwebrx-mute-img"></div>
<input id="openwebrx-panel-volume" type="range" min="0" max="150" value="50" step="1" onchange="updateVolume()" oninput="updateVolume()">
<div title="Mute on/off" id="openwebrx-mute-off" class="openwebrx-button" onclick="toggleMute();"><img src="gfx/openwebrx-speaker.png" class="openwebrx-sliderbtn-img"></div>
<input title="Volume" id="openwebrx-panel-volume" class="openwebrx-panel-slider" type="range" min="0" max="150" value="50" step="1" onchange="updateVolume()" oninput="updateVolume()">
<div title="Auto-adjust waterfall colors" id="openwebrx-waterfall-colors-auto" class="openwebrx-button" onclick="waterfall_measure_minmax_now=true;"><img src="gfx/openwebrx-waterfall-auto.png" class="openwebrx-sliderbtn-img"></div>
<input title="Waterfall minimum level" id="openwebrx-waterfall-color-min" class="openwebrx-panel-slider" type="range" min="-200" max="100" value="50" step="1" onchange="updateWaterfallColors(0);" oninput="updateVolume()">
</div>
<div class="openwebrx-panel-line">
<div title="Disable squelch" id="openwebrx-squelch-default" class="openwebrx-button" onclick="setSquelchDefault()"><img src="gfx/openwebrx-squelch-button.png" class="openwebrx-sliderbtn-img"></div>
<input title="Squelch" id="openwebrx-panel-squelch" class="openwebrx-panel-slider" type="range" min="0" max="100" value="0" step="1" onchange="updateVolume()" oninput="updateVolume()">
<div title="Set waterfall colors to default" id="openwebrx-waterfall-colors-default" class="openwebrx-button" onclick="waterfallColorsDefault()"><img src="gfx/openwebrx-waterfall-default.png" class="openwebrx-sliderbtn-img"></div>
<input title="Waterfall maximum level" id="openwebrx-waterfall-color-max" class="openwebrx-panel-slider" type="range" min="-200" max="100" value="50" step="1" onchange="updateWaterfallColors(1);" oninput="updateVolume()">
</div>
<div class="openwebrx-panel-line">
<div class="openwebrx-button openwebrx-square-button" onclick="zoomInOneStep();" title="Zoom in one step"> <img src="gfx/openwebrx-zoom-in.png" /></div>
<div class="openwebrx-button openwebrx-square-button" onclick="zoomOutOneStep();" title="Zoom out one step"> <img src="gfx/openwebrx-zoom-out.png" /></div>
<div class="openwebrx-button openwebrx-square-button" onclick="zoomInTotal();" title="Zoom in totally"><img src="gfx/openwebrx-zoom-in-total.png" /></div>
<div class="openwebrx-button openwebrx-square-button" onclick="zoomOutTotal();" title="Zoom out totally"><img src="gfx/openwebrx-zoom-out-total.png" /></div>
</div>
</div>

View File

@ -1,6 +1,6 @@
/*
This file is part of OpenWebRX,
This file is part of OpenWebRX,
an open-source SDR receiver software with a web UI.
Copyright (c) 2013-2015 by Andras Retzler <randras@sdr.hu>
@ -37,7 +37,6 @@ input[type=range]
{
-webkit-appearance: none;
margin: 10px 0;
width: 100%;
}
input[type=range]:focus
{
@ -242,7 +241,7 @@ input[type=range]:focus::-ms-fill-upper
position: absolute;
bottom: 0px;
left: 0px;
background-image:url(gfx/webrx-photo-gradient-corner.png);
background-image:url(gfx/webrx-photo-gradient-corner.png);
width: 59px;
height: 92px;
@ -255,7 +254,7 @@ input[type=range]:focus::-ms-fill-upper
left: 59px;
right: 59px;
height: 92px;
background-image:url(gfx/webrx-photo-gradient-middle.png);
background-image:url(gfx/webrx-photo-gradient-middle.png);
}
#webrx-photo-gradient-right
@ -263,7 +262,7 @@ input[type=range]:focus::-ms-fill-upper
position: absolute;
bottom: 0px;
right: 0px;
background-image:url(gfx/webrx-photo-gradient-corner.png);
background-image:url(gfx/webrx-photo-gradient-corner.png);
width: 59px;
height: 92px;
-webkit-transform:scaleX(-1);
@ -456,7 +455,7 @@ input[type=range]:focus::-ms-fill-upper
background-color: #999999;
color: White;
z-index:9999; /*should be higher?
}*/
/* removed non-free fonts like that: */
@ -484,7 +483,7 @@ input[type=range]:focus::-ms-fill-upper
padding: 0;
margin: 0;
line-height:22px;
}
#webrx-mouse-freq
@ -497,6 +496,7 @@ input[type=range]:focus::-ms-fill-upper
margin-bottom: 5px;
}
.openwebrx-panel
{
visibility: hidden;
@ -551,13 +551,72 @@ input[type=range]:focus::-ms-fill-upper
color: #FFFF50;
}
.openwebrx-button:active
.openwebrx-button:active
{
background: #777777;
color: #FFFF50;
color: #FFFF50;
}
#openwebrx-client-log-title
.openwebrx-demodulator-button
{
width: 35px;
height: 19px;
font-size: 12pt;
text-align: center;
}
.openwebrx-square-button img
{
height: 30px;
}
.openwebrx-round-button
{
margin-right: -2px;
width: 35px;
height: 35px;
border-radius: 25px;
}
.openwebrx-round-button img
{
height: 30px;
}
.openwebrx-round-button-small
{
margin-right: -3px;
width: 20px;
height: 20px;
border-radius: 25px;
}
.openwebrx-round-button-small img
{
height: 20px;
}
img.openwebrx-mirror-img
{
transform: scale(-1, 1);
}
.openwebrx-round-rightarrow img
{
position: relative;
left: 12px;
top: 3px;
}
.openwebrx-round-leftarrow img
{
position: relative;
left: 7px;
top: 3px;
}
#openwebrx-client-log-title
{
margin-bottom: 5px;
font-weight: bold;
@ -626,7 +685,7 @@ input[type=range]:focus::-ms-fill-upper
display: table-cell;
padding-left: 5px;
padding-right: 5px;
cursor:pointer;
cursor:pointer;
}
#openwebrx-main-buttons li:hover
@ -668,14 +727,14 @@ input[type=range]:focus::-ms-fill-upper
color: white;
}
#openwebrx-panel-volume
.openwebrx-panel-slider
{
position: relative;
top: -2px;
width:170px;
width:91px;
}
#openwebrx-mute-img
.openwebrx-sliderbtn-img
{
width: 14px;
position:relative;

View File

@ -1,6 +1,6 @@
/*
This file is part of OpenWebRX,
This file is part of OpenWebRX,
an open-source SDR receiver software with a web UI.
Copyright (c) 2013-2015 by Andras Retzler <randras@sdr.hu>
@ -58,12 +58,12 @@ var waterfall_timer;
something.fade_i=0;
n_of_iters=time_ms/(1000/fps);
change=(to-from)/(n_of_iters-1);
something.fade_timer=window.setInterval(
function(){
if(something.fade_i++<n_of_iters)
something.style.opacity=parseFloat(something.style.opacity)+change;
else
else
{something.style.opacity=to; window.clearInterval(something.fade_timer); }
},1000/fps);
}*/
@ -115,12 +115,12 @@ function open_rx_photo()
function style_value(of_what,which)
{
if(of_what.currentStyle) return of_what.currentStyle[which];
else if (window.getComputedStyle) return document.defaultView.getComputedStyle(of_what,null).getPropertyValue(which);
else if (window.getComputedStyle) return document.defaultView.getComputedStyle(of_what,null).getPropertyValue(which);
}
function updateVolume()
{
volume = parseFloat(e("openwebrx-panel-volume").value) / 100;
volume = parseFloat(e("openwebrx-panel-volume").value) / 100;
}
function toggleMute()
@ -137,15 +137,46 @@ function toggleMute()
e("openwebrx-mute-off").id="openwebrx-mute-on";
e("openwebrx-mute-img").src="gfx/openwebrx-speaker-muted.png";
e("openwebrx-panel-volume").disabled=true;
e("openwebrx-panel-volume").style.opacity=0.5;
volumeBeforeMute = e("openwebrx-panel-volume").value;
e("openwebrx-panel-volume").style.opacity=0.5;
volumeBeforeMute = e("openwebrx-panel-volume").value;
e("openwebrx-panel-volume").value=0;
}
updateVolume();
}
function zoomInOneStep () { zoom_set(zoom_level+1); }
function zoomOutOneStep () { zoom_set(zoom_level-1); }
function zoomInTotal () { zoom_set(zoom_levels.length-1); }
function zoomOutTotal () { zoom_set(0); }
function setSquelchDefault() { e("openwebrx-panel-squelch").value=0; }
function updateWaterfallColors(which)
{
wfmax=e("openwebrx-waterfall-color-max");
wfmin=e("openwebrx-waterfall-color-min");
if(parseInt(wfmin.value)>=parseInt(wfmax.value))
{
if(!which) wfmin.value=(parseInt(wfmax.value)-1).toString();
else wfmax.value=(parseInt(wfmin.value)+1).toString();
}
waterfall_min_level=parseInt(wfmin.value);
waterfall_max_level=parseInt(wfmax.value);
}
function waterfallColorsDefault()
{
waterfall_min_level=waterfall_min_level_default;
waterfall_max_level=waterfall_max_level_default;
e("openwebrx-waterfall-color-min").value=waterfall_min_level.toString();
e("openwebrx-waterfall-color-max").value=waterfall_max_level.toString();
}
function waterfallColorsAuto()
{
e("openwebrx-waterfall-color-min").value=(waterfall_measure_minmax_min-20).toString();
e("openwebrx-waterfall-color-max").value=(waterfall_measure_minmax_max+30).toString();
updateWaterfallColors(0);
}
// ========================================================
// ================= ANIMATION ROUTINES =================
@ -165,8 +196,8 @@ function animate(object,style_name,unit,from,to,accel,time_ms,fps,to_exec)
if(object.anim_i++<n_of_iters)
{
if(accel==1) object.style[style_name]=(parseFloat(object.style[style_name])+change).toString()+unit;
else
{
else
{
remain=parseFloat(object.style[style_name])-to;
if(Math.abs(remain)>9||unit!="px") new_val=(to+accel*remain);
else {if(Math.abs(remain)<2) new_val=to;
@ -174,7 +205,7 @@ function animate(object,style_name,unit,from,to,accel,time_ms,fps,to_exec)
object.style[style_name]=new_val.toString()+unit;
}
}
else
else
{object.style[style_name]=to.toString()+unit; window.clearInterval(object.anim_timer); delete object.anim_timer; }
if(to_exec!=0) to_exec();
},1000/fps);
@ -209,7 +240,7 @@ function demod_envelope_draw(range, from, to, color, line)
// A "drag range" object is returned, containing information about the draggable areas of the envelope
// (beginning, ending and the line showing the offset frequency).
if(typeof color == "undefined") color="#ffff00"; //yellow
env_bounding_line_w=5; //
env_bounding_line_w=5; //
env_att_w=5; // _______ ___env_h2 in px ___|_____
env_h1=17; // _/| \_ ___env_h1 in px _/ |_ \_
env_h2=5; // |||env_att_line_w |_env_lineplus
@ -219,11 +250,11 @@ function demod_envelope_draw(range, from, to, color, line)
from_px=scale_px_from_freq(from,range);
to_px=scale_px_from_freq(to,range);
if(to_px<from_px) /* swap'em */ { temp_px=to_px; to_px=from_px; from_px=temp_px; }
/*from_px-=env_bounding_line_w/2;
to_px+=env_bounding_line_w/2;*/
from_px-=(env_att_w+env_bounding_line_w);
to_px+=(env_att_w+env_bounding_line_w);
to_px+=(env_att_w+env_bounding_line_w);
// do drawing:
scale_ctx.lineWidth=3;
scale_ctx.strokeStyle=color;
@ -247,7 +278,7 @@ function demod_envelope_draw(range, from, to, color, line)
scale_ctx.globalAlpha = 1;
scale_ctx.stroke();
}
if(typeof line != "undefined") // out of screen?
if(typeof line != "undefined") // out of screen?
{
line_px=scale_px_from_freq(line,range);
if(!(line_px<0||line_px>window.innerWidth))
@ -275,12 +306,12 @@ function demod_envelope_where_clicked(x, drag_ranges, key_modifiers)
if(drag_ranges.envelope_on_screen&&in_range(x,drag_ranges.whole_envelope)) return dr.pbs;
}
if(drag_ranges.envelope_on_screen)
{
{
// For low and high cut:
if(in_range(x,drag_ranges.beginning)) return dr.beginning;
if(in_range(x,drag_ranges.ending)) return dr.ending;
// Last priority: having clicked anything else on the envelope, without holding the shift key
if(in_range(x,drag_ranges.whole_envelope)) return dr.anything_else;
if(in_range(x,drag_ranges.whole_envelope)) return dr.anything_else;
}
return dr.none; //User doesn't drag the envelope for this demodulator
}
@ -304,7 +335,7 @@ demodulator.draggable_ranges={none: 0, beginning:1 /*from*/, ending: 2 /*to*/, a
// This can be used as a base for basic audio demodulators.
// It already supports most basic modulations used for ham radio and commercial services: AM/FM/LSB/USB
demodulator_response_time=50;
demodulator_response_time=50;
//in ms; if we don't limit the number of SETs sent to the server, audio will underrun (possibly output buffer is cleared on SETs in GNU Radio
function demodulator_default_analog(offset_frequency,subtype)
@ -318,7 +349,7 @@ function demodulator_default_analog(offset_frequency,subtype)
high_cut_limit: audio_context.sampleRate/2,
low_cut_limit: -audio_context.sampleRate/2
};
//Subtypes only define some filter parameters and the mod string sent to server,
//Subtypes only define some filter parameters and the mod string sent to server,
//so you may set these parameters in your custom child class.
//Why? As of demodulation is done on the server, difference is mainly on the server side.
this.server_mod=subtype;
@ -339,23 +370,23 @@ function demodulator_default_analog(offset_frequency,subtype)
this.low_cut=700;
this.high_cut=900;
this.server_mod="ssb";
}
}
else if(subtype=="nfm")
{
this.low_cut=-4000;
this.high_cut=4000;
}
}
else if(subtype=="am")
{
this.low_cut=-4000;
this.high_cut=4000;
}
}
this.wait_for_timer=false;
this.set_after=false;
this.set=function()
{ //set() is a wrapper to call doset(), but it ensures that doset won't execute more frequently than demodulator_response_time.
if(!this.wait_for_timer)
if(!this.wait_for_timer)
{
this.doset(false);
this.set_after=false;
@ -384,7 +415,7 @@ function demodulator_default_analog(offset_frequency,subtype)
// for drawing the filter envelope above scale
this.envelope.parent=this;
this.envelope.draw=function(visible_range)
this.envelope.draw=function(visible_range)
{
this.visible_range=visible_range;
this.drag_ranges=demod_envelope_draw(range,
@ -428,22 +459,22 @@ function demodulator_default_analog(offset_frequency,subtype)
//dragging any other parts of the filter envelope while holding Shift does emulate the PBS knob
//(PassBand Shift) on radio equipment: PBS does move the whole passband without moving the offset
//frequency.
if(this.dragged_range==dr.beginning||this.dragged_range==dr.bfo||this.dragged_range==dr.pbs)
if(this.dragged_range==dr.beginning||this.dragged_range==dr.bfo||this.dragged_range==dr.pbs)
{
//we don't let low_cut go beyond its limits
if((new_value=this.drag_origin.low_cut+minus*freq_change)<this.parent.filter.low_cut_limit) return true;
//nor the filter passband be too small
if(this.parent.high_cut-new_value<this.parent.filter.min_passband) return true;
if(this.parent.high_cut-new_value<this.parent.filter.min_passband) return true;
//sanity check to prevent GNU Radio "firdes check failed: fa <= fb"
if(new_value>=this.parent.high_cut) return true;
this.parent.low_cut=new_value;
}
if(this.dragged_range==dr.ending||this.dragged_range==dr.bfo||this.dragged_range==dr.pbs)
if(this.dragged_range==dr.ending||this.dragged_range==dr.bfo||this.dragged_range==dr.pbs)
{
//we don't let high_cut go beyond its limits
if((new_value=this.drag_origin.high_cut+minus*freq_change)>this.parent.filter.high_cut_limit) return true;
//nor the filter passband be too small
if(new_value-this.parent.low_cut<this.parent.filter.min_passband) return true;
if(new_value-this.parent.low_cut<this.parent.filter.min_passband) return true;
//sanity check to prevent GNU Radio "firdes check failed: fa <= fb"
if(new_value<=this.parent.low_cut) return true;
this.parent.high_cut=new_value;
@ -459,17 +490,17 @@ function demodulator_default_analog(offset_frequency,subtype)
mkenvelopes(this.visible_range);
this.parent.set();
//will have to change this when changing to multi-demodulator mode:
e("webrx-actual-freq").innerHTML=format_frequency("{x} MHz",center_freq+this.parent.offset_frequency,1e6,4);
e("webrx-actual-freq").innerHTML=format_frequency("{x} MHz",center_freq+this.parent.offset_frequency,1e6,4);
return true;
};
this.envelope.drag_end=function(x)
{ //in this demodulator we've already changed values in the drag_move() function so we shouldn't do too much here.
to_return=this.dragged_range!=demodulator.draggable_ranges.none; //this part is required for cliking anywhere on the scale to set offset
this.dragged_range=demodulator.draggable_ranges.none;
return to_return;
};
}
demodulator_default_analog.prototype=new demodulator();
@ -496,9 +527,9 @@ function demodulator_add(what)
}
function demodulator_analog_replace(subtype)
{ //this function should only exist until the multi-demodulator capability is added
{ //this function should only exist until the multi-demodulator capability is added
var temp_offset=0;
if(demodulators.length)
if(demodulators.length)
{
temp_offset=demodulators[0].offset_frequency;
demodulator_remove(0);
@ -525,7 +556,7 @@ var scale_canvas;
function scale_setup()
{
e("webrx-actual-freq").innerHTML=format_frequency("{x} MHz",canvas_get_frequency(window.innerWidth/2),1e6,4);
scale_canvas=e("openwebrx-scale-canvas");
scale_canvas=e("openwebrx-scale-canvas");
scale_ctx=scale_canvas.getContext("2d");
scale_canvas.addEventListener("mousedown", scale_canvas_mousedown, false);
scale_canvas.addEventListener("mousemove", scale_canvas_mousemove, false);
@ -563,7 +594,7 @@ function scale_offset_freq_from_px(x, visible_range)
function scale_canvas_mousemove(evt)
{
var event_handled;
if(scale_canvas_drag_params.mouse_down&&!scale_canvas_drag_params.drag&&Math.abs(evt.pageX-scale_canvas_drag_params.start_x)>canvas_drag_min_delta)
if(scale_canvas_drag_params.mouse_down&&!scale_canvas_drag_params.drag&&Math.abs(evt.pageX-scale_canvas_drag_params.start_x)>canvas_drag_min_delta)
//we can use the main drag_min_delta thing of the main canvas
{
scale_canvas_drag_params.drag=true;
@ -577,7 +608,7 @@ function scale_canvas_mousemove(evt)
for (var i=0;i<demodulators.length;i++) event_handled|=demodulators[i].envelope.drag_move(evt.pageX);
if (!event_handled) demodulator_set_offset_frequency(0,scale_offset_freq_from_px(evt.pageX));
}
}
function scale_canvas_end_drag(x)
@ -681,13 +712,13 @@ var scale_min_space_bw_small_markers=7;
function get_scale_mark_spacing(range)
{
out={};
fcalc=function(freq)
{
fcalc=function(freq)
{
out.numlarge=(range.bw/freq);
out.large=canvas_container.clientWidth/out.numlarge; //distance between large markers (these have text)
out.ratio=5; //(ratio-1) small markers exist per large marker
out.small=out.large/out.ratio; //distance between small markers
if(out.small<scale_min_space_bw_small_markers) return false;
if(out.small<scale_min_space_bw_small_markers) return false;
if(out.small/2>=scale_min_space_bw_small_markers&&freq.toString()[0]!="5") {out.small/=2; out.ratio*=2; }
out.smallbw=freq/out.ratio;
return true;
@ -725,11 +756,11 @@ function mkscale()
{
var x=scale_px_from_freq(marker_hz,range);
if(x>window.innerWidth) break;
scale_ctx.beginPath();
scale_ctx.beginPath();
scale_ctx.moveTo(x, 22);
if(marker_hz%spacing.params.large_marker_per_hz==0)
{ //large marker
if(typeof first_large == "undefined") var first_large=marker_hz;
if(typeof first_large == "undefined") var first_large=marker_hz;
last_large=marker_hz;
scale_ctx.lineWidth=3.5;
scale_ctx.lineTo(x,22+11);
@ -742,16 +773,16 @@ function mkscale()
if(scale_px_from_freq(marker_hz+spacing.smallbw*spacing.ratio,range)-text_measured.width>=scale_min_space_bw_texts)
{ //and if we have enough space to draw it correctly without clipping
scale_ctx.textAlign = "left";
scale_ctx.fillText(text_to_draw, 0, text_h_pos);
scale_ctx.fillText(text_to_draw, 0, text_h_pos);
}
}
else if( zoom_level==0 && (range.end-spacing.smallbw*spacing.ratio<marker_hz) && (x>window.innerWidth-text_measured.width/2) )
else if( zoom_level==0 && (range.end-spacing.smallbw*spacing.ratio<marker_hz) && (x>window.innerWidth-text_measured.width/2) )
{ // if this is the last overall marker when zoomed out... and if it would be clipped off the screen...
if(window.innerWidth-text_measured.width-scale_px_from_freq(marker_hz-spacing.smallbw*spacing.ratio,range)>=scale_min_space_bw_texts)
{ //and if we have enough space to draw it correctly without clipping
scale_ctx.textAlign = "right";
scale_ctx.fillText(text_to_draw, window.innerWidth, text_h_pos);
}
scale_ctx.fillText(text_to_draw, window.innerWidth, text_h_pos);
}
}
else scale_ctx.fillText(text_to_draw, x, text_h_pos); //draw text normally
}
@ -791,7 +822,7 @@ function resize_scale()
function canvas_mouseover(evt)
{
if(!waterfall_setup_done) return;
//e("webrx-freq-show").style.visibility="visible";
//e("webrx-freq-show").style.visibility="visible";
}
function canvas_mouseout(evt)
@ -854,17 +885,17 @@ function canvas_mousemove(evt)
element.style.left=realX.toString()+"px";*/
if(canvas_mouse_down)
{
if(!canvas_drag&&Math.abs(evt.pageX-canvas_drag_start_x)>canvas_drag_min_delta)
if(!canvas_drag&&Math.abs(evt.pageX-canvas_drag_start_x)>canvas_drag_min_delta)
{
canvas_drag=true;
canvas_container.style.cursor="move";
}
if(canvas_drag)
if(canvas_drag)
{
var deltaX=canvas_drag_last_x-evt.pageX;
var deltaY=canvas_drag_last_y-evt.pageY;
//zoom_center_where=zoom_center_where_calc(evt.pageX);
var dpx=range.hps*deltaX;
var dpx=range.hps*deltaX;
if(
!(zoom_center_rel+dpx>(bandwidth/2-canvas_container.clientWidth*(1-zoom_center_where)*range.hps)) &&
!(zoom_center_rel+dpx<-bandwidth/2+canvas_container.clientWidth*zoom_center_where*range.hps)
@ -892,10 +923,10 @@ function canvas_mouseup(evt)
if(!waterfall_setup_done) return;
relativeX=(evt.offsetX)?evt.offsetX:evt.layerX;
if(!canvas_drag)
if(!canvas_drag)
{
//ws.send("SET offset_freq="+canvas_get_freq_offset(relativeX).toString());
demodulator_set_offset_frequency(0, canvas_get_freq_offset(relativeX));
demodulator_set_offset_frequency(0, canvas_get_freq_offset(relativeX));
e("webrx-actual-freq").innerHTML=format_frequency("{x} MHz",canvas_get_frequency(relativeX),1e6,4);
}
else
@ -928,7 +959,7 @@ function canvas_mousewheel(evt)
//console.log(dir);
//i/=120;
/*while (i--)*/ zoom_step(dir, relativeX, zoom_center_where_calc(evt.pageX));
evt.preventDefault();
evt.preventDefault();
//evt.returnValue = false; //disable scrollbar move
}
@ -961,12 +992,26 @@ function mkzoomlevels()
function zoom_step(out, where, onscreen)
{
if((out&&zoom_level==0)||(!out&&zoom_level>=zoom_levels_count-1)) return;
if(out) --zoom_level;
else ++zoom_level;
zoom_center_rel=canvas_get_freq_offset(where);
//console.log("zoom_step || zlevel: "+zoom_level.toString()+" zlevel_val: "+zoom_levels[zoom_level].toString()+" zoom_center_rel: "+zoom_center_rel.toString());
zoom_center_where=onscreen;
console.log(zoom_center_where, zoom_center_rel, where);
resize_canvases(true);
mkscale();
}
function zoom_set(level)
{
if(!(level>=0&&level<=zoom_levels.length-1)) return;
level=parseInt(level);
zoom_level = level;
//zoom_center_rel=canvas_get_freq_offset(-canvases[0].offsetLeft+canvas_container.clientWidth/2); //zoom to screen center instead of demod envelope
zoom_center_rel=demodulators[0].offset_frequency;
zoom_center_where=0.5+(zoom_center_rel/bandwidth); //this is a kind of hack
console.log(zoom_center_where, zoom_center_rel, -canvases[0].offsetLeft+canvas_container.clientWidth/2);
resize_canvases(true);
mkscale();
}
@ -977,7 +1022,7 @@ function zoom_calc()
var canvases_new_width=winsize*zoom_levels[zoom_level];
zoom_offset_px=-((canvases_new_width*(0.5+zoom_center_rel/bandwidth))-(winsize*zoom_center_where));
if(zoom_offset_px>0) zoom_offset_px=0;
if(zoom_offset_px<winsize-canvases_new_width)
if(zoom_offset_px<winsize-canvases_new_width)
zoom_offset_px=winsize-canvases_new_width;
//console.log("zoom_calc || zopx:"+zoom_offset_px.toString()+ " maxoff:"+(winsize-canvases_new_width).toString()+" relval:"+(0.5+zoom_center_rel/bandwidth).toString() );
}
@ -1001,9 +1046,9 @@ function audio_calculate_resampling(targetRate)
while(true)
{
audio_server_output_rate = Math.floor(targetRate / i);
if(audio_server_output_rate < output_range_min)
{
audio_client_resampling_factor = audio_server_output_rate = 0;
if(audio_server_output_rate < output_range_min)
{
audio_client_resampling_factor = audio_server_output_rate = 0;
divlog("Your audio card sampling rate ("+targetRate.toString()+") is not supported.<br />Please change your operating system default settings in order to fix this.",1);
}
if(audio_server_output_rate >= output_range_min && audio_server_output_rate <= output_range_max) break; //okay, we're done
@ -1029,7 +1074,7 @@ function on_ws_recv(evt)
{
var stringData=arrayBufferToString(evt.data);
if(stringData.substring(0,16)=="CLIENT DE SERVER") divlog("Acknowledged WebSocket connection: "+stringData);
}
if(firstChars=="AUD")
{
@ -1048,7 +1093,7 @@ function on_ws_recv(evt)
else if(fft_compression="adpcm")
{
fft_codec.reset();
var waterfall_i16=fft_codec.decode(new Uint8Array(evt.data,4));
var waterfall_f32=new Float32Array(waterfall_i16.length-COMPRESS_FFT_PAD_N);
for(var i=0;i<waterfall_i16.length;i++) waterfall_f32[i]=waterfall_i16[i+COMPRESS_FFT_PAD_N]/100;
@ -1068,10 +1113,10 @@ function on_ws_recv(evt)
case "setup":
waterfall_init();
audio_preinit();
break;
break;
case "bandwidth":
bandwidth=parseInt(param[1]);
break;
break;
case "center_freq":
center_freq=parseInt(param[1]); //there was no ; and it was no problem... why?
break;
@ -1122,6 +1167,7 @@ function add_problem(what)
}
waterfall_measure_minmax=false;
waterfall_measure_minmax_now=false;
waterfall_measure_minmax_min=1e100;
waterfall_measure_minmax_max=-1e100;
@ -1139,13 +1185,14 @@ function waterfall_measure_minmax_print()
function waterfall_add_queue(what)
{
if(waterfall_measure_minmax) waterfall_measure_minmax_do(what);
if(waterfall_measure_minmax_now) { waterfall_measure_minmax_do(what); waterfall_measure_minmax_now=false; waterfallColorsAuto(); }
waterfall_queue.push(what);
}
function waterfall_dequeue()
{
if(waterfall_queue.length) waterfall_add(waterfall_queue.shift());
if(waterfall_queue.length>Math.max(fft_fps/2,20)) //in case of emergency
if(waterfall_queue.length>Math.max(fft_fps/2,20)) //in case of emergency
{
console.log("waterfall queue length:", waterfall_queue.length);
add_problem("fft overflow");
@ -1165,7 +1212,7 @@ function divlog(what, is_error)
{
is_error=!!is_error;
was_error |= is_error;
if(is_error)
if(is_error)
{
what="<span class=\"webrx-error\">"+what+"</span>";
if(e("openwebrx-panel-log").openwebrxHidden) toggle_panel("openwebrx-panel-log"); //show panel if any error is present
@ -1213,7 +1260,7 @@ function gain_ff(gain_value,data) //great! solved clicking! will have to move to
function audio_prepare(data)
{
//audio_rebuffer.push(sdrjs.ConvertI16_F(data));//no resampling
//audio_rebuffer.push(audio_resampler.process(sdrjs.ConvertI16_F(data)));//resampling without ADPCM
if(audio_compression=="none")
@ -1223,7 +1270,7 @@ function audio_prepare(data)
else return;
//console.log("prepare",data.length,audio_rebuffer.remaining());
while(audio_rebuffer.remaining())
while(audio_rebuffer.remaining())
{
audio_prepared_buffers.push(audio_rebuffer.take());
audio_buffer_current_count_debug++;
@ -1236,7 +1283,7 @@ function audio_prepare_without_resampler(data)
{
audio_rebuffer.push(sdrjs.ConvertI16_F(data));
console.log("prepare",data.length,audio_rebuffer.remaining());
while(audio_rebuffer.remaining())
while(audio_rebuffer.remaining())
{
audio_prepared_buffers.push(audio_rebuffer.take());
audio_buffer_current_count_debug++;
@ -1271,7 +1318,7 @@ function audio_prepare_old(data)
if(audio_last_output_offset==audio_buffer_size) dopush();
}
else
{ //array is larger than the remaining space in the output buffer
{ //array is larger than the remaining space in the output buffer
var copied=audio_buffer_size-audio_last_output_offset;
var remain=data.length-copied;
for(var i=0;i<audio_buffer_size-audio_last_output_offset;i++) //fill the remaining space in the output buffer
@ -1320,12 +1367,12 @@ function audio_buffer_progressbar_update()
var text="buffer";
if(overrun) { text="overrun"; console.log("audio overrun, "+(++audio_overrun_cnt).toString()); }
if(underrun) { text="underrun"; console.log("audio underrun, "+(++audio_underrun_cnt).toString()); }
if(overrun||underrun)
{
if(overrun||underrun)
{
audio_buffer_progressbar_update_disabled=true;
window.setTimeout(function(){audio_buffer_progressbar_update_disabled=false; audio_buffer_progressbar_update();},1000);
}
progressbar_set(e("openwebrx-bar-audio-buffer"),(underrun)?1:audio_buffer_value/1.5,"Audio "+text+" ["+(audio_buffer_value).toFixed(1)+" s]",overrun||underrun||audio_buffer_value<0.25);
progressbar_set(e("openwebrx-bar-audio-buffer"),(underrun)?1:audio_buffer_value/1.5,"Audio "+text+" ["+(audio_buffer_value).toFixed(1)+" s]",overrun||underrun||audio_buffer_value<0.25);
}
@ -1344,10 +1391,10 @@ function audio_flush()
}
function audio_onprocess_notused(e)
function audio_onprocess_notused(e)
{
//https://github.com/0xfe/experiments/blob/master/www/tone/js/sinewave.js
if(audio_received.length==0)
if(audio_received.length==0)
{ add_problem("audio underrun"); return; }
output = e.outputBuffer.getChannelData(0);
int_buffer = audio_received[0];
@ -1356,7 +1403,7 @@ function audio_onprocess_notused(e)
obi=0; //output buffer index
debug_str=""
while(1)
while(1)
{
if(int_buffer.length-audio_buffer_index>read_remain)
{
@ -1367,7 +1414,7 @@ function audio_onprocess_notused(e)
break;
}
else
{
{
for (i=audio_buffer_index; i<int_buffer.length; i++)
output[obi++] = int_buffer[i]/32768;
read_remain-=(int_buffer.length-audio_buffer_index);
@ -1380,7 +1427,7 @@ function audio_onprocess_notused(e)
else*/
audio_received.splice(0,1);
//debug_str+="added remain, remain="+read_remain.toString()+" abi="+audio_buffer_index.toString()+" alen="+int_buffer.length.toString()+" i="+i.toString()+" arecva="+audio_received.length.toString()+" obi="+obi.toString()+"\n";
audio_buffer_index = 0;
audio_buffer_index = 0;
if(audio_received.length == 0 || read_remain == 0) return;
int_buffer = audio_received[0];
}
@ -1392,7 +1439,7 @@ function audio_onprocess_notused(e)
function audio_flush_notused()
{
if (audio_buffer_current_size>audio_buffer_maximal_length_sec*audio_context.sampleRate)
{
{
add_problem("audio overrun");
console.log("audio_flush() :: size: "+audio_buffer_current_size.toString()+" allowed: "+(audio_buffer_maximal_length_sec*audio_context.sampleRate).toString());
while (audio_buffer_current_size>audio_buffer_maximal_length_sec*audio_context.sampleRate*0.5)
@ -1410,7 +1457,7 @@ function webrx_set_param(what, value)
function parsehash()
{
if(h=window.location.hash)
if(h=window.location.hash)
{
h.substring(1).split(",").forEach(function(x){
harr=x.split("=");
@ -1418,28 +1465,28 @@ function parsehash()
if(harr[0]=="mod") starting_mod = harr[1];
if(harr[0]=="freq") {
console.log(parseInt(harr[1]));
console.log(center_freq);
console.log(center_freq);
starting_offset_frequency = parseInt(harr[1])-center_freq;
}
});
}
}
function audio_preinit()
{
try
try
{
window.AudioContext = window.AudioContext||window.webkitAudioContext;
audio_context = new AudioContext();
}
catch(e)
catch(e)
{
divlog('Your browser does not support Web Audio API, which is required for WebRX to run. Please upgrade to a HTML5 compatible browser.', 1);
}
//we send our setup packet
parsehash();
//needs audio_context.sampleRate to exist
@ -1465,7 +1512,7 @@ function audio_init()
audio_node = createjsnode_function(audio_buffer_size, 0, 1);
audio_node.onaudioprocess = audio_onprocess;
audio_node.connect(audio_context.destination);
// --- Resampling ---
// --- Resampling ---
//https://github.com/grantgalitz/XAudioJS/blob/master/XAudioServer.js
//audio_resampler = new Resampler(audio_received_sample_rate, audio_context.sampleRate, 1, audio_buffer_size, true);
//audio_input_buffer_size = audio_buffer_size*(audio_received_sample_rate/audio_context.sampleRate);
@ -1478,8 +1525,8 @@ function audio_init()
audio_source.buffer = buffer;
audio_source.noteOn(0);*/
demodulator_analog_replace(starting_mod);
if(starting_offset_frequency)
{
if(starting_offset_frequency)
{
demodulators[0].offset_frequency = starting_offset_frequency;
e("webrx-actual-freq").innerHTML=format_frequency("{x} MHz",center_freq+starting_offset_frequency,1e6,4);
demodulators[0].set();
@ -1499,11 +1546,11 @@ function audio_init()
function on_ws_closed()
{
try
{
{
audio_node.disconnect();
}
catch (dont_care) {}
divlog("WebSocket has closed unexpectedly. Please reload the page.", 1);
divlog("WebSocket has closed unexpectedly. Please reload the page.", 1);
}
function on_ws_error(event)
@ -1516,11 +1563,11 @@ String.prototype.startswith=function(str){ return this.indexOf(str) == 0; }; //h
function open_websocket()
{
//if(ws_url.startswith("ws://localhost:")&&window.location.hostname!="127.0.0.1"&&window.location.hostname!="localhost")
//{
//divlog("Server administrator should set <em>server_hostname</em> correctly, because it is left as <em>\"localhost\"</em>. Now guessing hostname from page URL.",1);
//{
//divlog("Server administrator should set <em>server_hostname</em> correctly, because it is left as <em>\"localhost\"</em>. Now guessing hostname from page URL.",1);
ws_url="ws://"+(window.location.origin.split("://")[1])+"/ws/"; //guess automatically -> now default behaviour
//}
if (!("WebSocket" in window))
if (!("WebSocket" in window))
divlog("Your browser does not support WebSocket, which is required for WebRX to run. Please upgrade to a HTML5 compatible browser.");
ws = new WebSocket(ws_url+client_id);
ws.onopen = on_ws_opened;
@ -1566,15 +1613,15 @@ var canvas_container;
var canvas_phantom;
function add_canvas()
{
{
new_canvas = document.createElement("canvas");
new_canvas.width=fft_size;
new_canvas.height=canvas_default_height;
canvas_actual_line=canvas_default_height-1;
new_canvas.style.width=(canvas_container.clientWidth*zoom_levels[zoom_level]).toString()+"px";
new_canvas.style.width=(canvas_container.clientWidth*zoom_levels[zoom_level]).toString()+"px";
new_canvas.style.left=zoom_offset_px.toString()+"px";
new_canvas.style.height=canvas_default_height.toString()+"px";
new_canvas.openwebrx_top=(-canvas_default_height+1);
new_canvas.openwebrx_top=(-canvas_default_height+1);
new_canvas.style.top=new_canvas.openwebrx_top.toString()+"px";
canvas_context = new_canvas.getContext("2d");
canvas_container.appendChild(new_canvas);
@ -1608,7 +1655,7 @@ canvas_maxshift=0;
function shift_canvases()
{
canvases.forEach(function(p)
canvases.forEach(function(p)
{
p.style.top=(p.openwebrx_top++).toString()+"px";
});
@ -1621,8 +1668,8 @@ function shift_canvases()
}
else
canvas_phantom.style.display="none";
//canvas_container.style.height=(((canvases.length-1)*canvas_default_height)+(canvas_default_height-canvas_actual_line)).toString()+"px";
//canvas_container.style.height="100%";
}
@ -1634,7 +1681,7 @@ function resize_canvases(zoom)
zoom_calc();
new_width=(canvas_container.clientWidth*zoom_levels[zoom_level]).toString()+"px";
var zoom_value=zoom_offset_px.toString()+"px";
canvases.forEach(function(p)
canvases.forEach(function(p)
{
p.style.width=new_width;
p.style.left=zoom_value;
@ -1695,7 +1742,7 @@ function waterfall_add(data)
remain=pixel_per_point-(1-remain);
}
}
}
else
{ //make line smaller (linear decimation, moving average)
@ -1708,7 +1755,7 @@ function waterfall_add(data)
{
if(remain>1)
{
last_pixel+=data[i];
last_pixel+=data[i];
remain--;
}
else
@ -1722,8 +1769,8 @@ function waterfall_add(data)
}
}
//Add line to waterfall image
base=(h-1)*w*4;
//Add line to waterfall image
base=(h-1)*w*4;
for(x=0;x<w;x++)
{
color=waterfall_mkcolor(scaled[x]);
@ -1731,7 +1778,7 @@ function waterfall_add(data)
waterfall_image.data[base+x*4+i] = ((color>>>0)>>((3-i)*8))&0xff;
}*/
//Add line to waterfall image
//Add line to waterfall image
oneline_image = canvas_context.createImageData(w,1);
for(x=0;x<w;x++)
{
@ -1740,12 +1787,12 @@ function waterfall_add(data)
oneline_image.data[x*4+i] = ((color>>>0)>>((3-i)*8))&0xff;
}
//Draw image
canvas_context.putImageData(oneline_image, 0, canvas_actual_line--);
shift_canvases();
if(canvas_actual_line<0) add_canvas();
//divlog("Drawn FFT");
}
@ -1769,14 +1816,14 @@ function check_top_bar_congestion()
var rightmost=Math.max(rmf(wet),rmf(wed));
var tl=e("openwebrx-main-buttons");
[wet, wed].map(function(what) {
if(rmf(what)>tl.offsetLeft-20) what.style.opacity=what.style.opacity="0";
[wet, wed].map(function(what) {
if(rmf(what)>tl.offsetLeft-20) what.style.opacity=what.style.opacity="0";
else wet.style.opacity=wed.style.opacity="1";
});
}
function openwebrx_resize()
function openwebrx_resize()
{
resize_canvases();
resize_waterfall_container(true);
@ -1795,10 +1842,11 @@ function openwebrx_init()
//Synchronise volume with slider
updateVolume();
waterfallColorsDefault();
}
/*
window.setInterval(function(){
window.setInterval(function(){
sum=0;
for(i=0;i<audio_received.length;i++)
sum+=audio_received[i].length;
@ -1833,15 +1881,15 @@ function debug_audio()
// ((audio_buffer_current_count_debug*audio_buffer_size)/audio_debug_time_taken).toFixed(1)+" sps output";
var audio_speed_value=audio_buffer_current_size_debug*kbps_mult/audio_debug_time_since_last_call;
progressbar_set(e("openwebrx-bar-audio-speed"),audio_speed_value/500000,"Audio stream ["+(audio_speed_value/1000).toFixed(0)+" kbps]",false);
progressbar_set(e("openwebrx-bar-audio-speed"),audio_speed_value/500000,"Audio stream ["+(audio_speed_value/1000).toFixed(0)+" kbps]",false);
var audio_output_value=(audio_buffer_current_count_debug*audio_buffer_size)/audio_debug_time_taken;
progressbar_set(e("openwebrx-bar-audio-output"),audio_output_value/55000,"Audio output ["+(audio_output_value/1000).toFixed(1)+" ksps]",audio_output_value>55000||audio_output_value<10000);
progressbar_set(e("openwebrx-bar-audio-output"),audio_output_value/55000,"Audio output ["+(audio_output_value/1000).toFixed(1)+" ksps]",audio_output_value>55000||audio_output_value<10000);
audio_buffer_progressbar_update();
var network_speed_value=debug_ws_data_received/audio_debug_time_taken;
progressbar_set(e("openwebrx-bar-network-speed"),network_speed_value*8/2000,"Network usage ["+(network_speed_value*8).toFixed(1)+" kbps]",false);
progressbar_set(e("openwebrx-bar-network-speed"),network_speed_value*8/2000,"Network usage ["+(network_speed_value*8).toFixed(1)+" kbps]",false);
audio_buffer_current_size_debug=0;
@ -1858,10 +1906,10 @@ function pop_bottommost_panel(from)
{
min_order=parseInt(from[0].dataset.panelOrder);
min_index=0;
for(i=0;i<from.length;i++)
for(i=0;i<from.length;i++)
{
actual_order=parseInt(from[i].dataset.panelOrder);
if(actual_order<min_order)
if(actual_order<min_order)
{
min_index=i;
min_order=actual_order;
@ -1893,7 +1941,7 @@ function place_panels()
c=plist[i];
if(c.className=="openwebrx-panel")
{
if(c.openwebrxHidden)
if(c.openwebrxHidden)
{
c.style.display="none";
continue;
@ -1907,7 +1955,7 @@ function place_panels()
//c.style.height=newSize[1]+"px";
if(!c.openwebrxPanelTransparent) c.style.margin=panel_margin.toString()+"px";
else c.style.marginLeft=panel_margin.toString()+"px";
c.openwebrxPanelWidth=parseInt(newSize[0]);
c.openwebrxPanelWidth=parseInt(newSize[0]);
c.openwebrxPanelHeight=parseInt(newSize[1]);
}
}
@ -1938,7 +1986,7 @@ function progressbar_set(obj,val,text,over)
if (val>1) val=1;
var innerBar=null;
var innerText=null;
for(var i=0;i<obj.children.length;i++)
for(var i=0;i<obj.children.length;i++)
{
if(obj.children[i].className=="openwebrx-progressbar-text") innerText=obj.children[i];
else if(obj.children[i].className=="openwebrx-progressbar-bar") innerBar=obj.children[i];
@ -1951,4 +1999,3 @@ function progressbar_set(obj,val,text,over)
if(innerText==null) return;
innerText.innerHTML=text;
}