2020-05-01 22:05:20 +00:00
|
|
|
function DemodulatorPanel(el) {
|
|
|
|
var self = this;
|
|
|
|
self.el = el;
|
|
|
|
self.demodulator = null;
|
2020-05-03 10:48:25 +00:00
|
|
|
self.mode = null;
|
2020-05-01 22:05:20 +00:00
|
|
|
|
|
|
|
var displayEl = el.find('.webrx-actual-freq')
|
|
|
|
this.tuneableFrequencyDisplay = displayEl.tuneableFrequencyDisplay();
|
|
|
|
displayEl.on('frequencychange', function(event, freq) {
|
2020-05-02 00:32:49 +00:00
|
|
|
self.getDemodulator().set_offset_frequency(freq - self.center_freq);
|
2020-05-01 22:05:20 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
Modes.registerModePanel(this);
|
2020-05-01 23:07:44 +00:00
|
|
|
el.on('click', '.openwebrx-demodulator-button', function() {
|
|
|
|
var modulation = $(this).data('modulation');
|
|
|
|
if (modulation) {
|
|
|
|
self.setMode(modulation);
|
|
|
|
} else {
|
|
|
|
self.disableDigiMode();
|
|
|
|
}
|
2020-05-01 22:05:20 +00:00
|
|
|
});
|
|
|
|
el.on('change', '.openwebrx-secondary-demod-listbox', function() {
|
2020-05-01 23:07:44 +00:00
|
|
|
var value = $(this).val();
|
|
|
|
if (value === 'none') {
|
|
|
|
self.disableDigiMode();
|
|
|
|
} else {
|
|
|
|
self.setMode(value);
|
|
|
|
}
|
2020-05-01 22:05:20 +00:00
|
|
|
});
|
2020-05-03 17:55:48 +00:00
|
|
|
el.on('click', '.openwebrx-squelch-default', function() {
|
|
|
|
if (!self.squelchAvailable()) return;
|
|
|
|
el.find('.openwebrx-squelch-slider').val(getLogSmeterValue(smeter_level) + 10);
|
|
|
|
self.updateSquelch();
|
|
|
|
});
|
|
|
|
el.on('change', '.openwebrx-squelch-slider', function() {
|
|
|
|
self.updateSquelch();
|
|
|
|
});
|
2020-05-02 13:17:09 +00:00
|
|
|
window.addEventListener('hashchange', function() {
|
|
|
|
self.onHashChange();
|
|
|
|
});
|
2020-05-01 22:05:20 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
DemodulatorPanel.prototype.render = function() {
|
|
|
|
var available = Modes.getModes().filter(function(m){ return m.isAvailable(); });
|
|
|
|
var normalModes = available.filter(function(m){ return m.type === 'analog'; });
|
|
|
|
var digiModes = available.filter(function(m){ return m.type === 'digimode'; });
|
|
|
|
|
|
|
|
var html = []
|
|
|
|
|
|
|
|
var buttons = normalModes.map(function(m){
|
|
|
|
return $(
|
|
|
|
'<div ' +
|
|
|
|
'class="openwebrx-button openwebrx-demodulator-button" ' +
|
|
|
|
'data-modulation="' + m.modulation + '" ' +
|
|
|
|
'id="openwebrx-button-' + m.modulation + '" r' +
|
|
|
|
'>' + m.name + '</div>'
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
var index = 0;
|
|
|
|
var arrayLength = buttons.length;
|
|
|
|
var chunks = [];
|
|
|
|
|
|
|
|
for (index = 0; index < arrayLength; index += 5) {
|
|
|
|
chunks.push(buttons.slice(index, index + 5));
|
|
|
|
}
|
|
|
|
|
|
|
|
html.push.apply(html, chunks.map(function(chunk){
|
|
|
|
$line = $('<div class="openwebrx-panel-line openwebrx-panel-flex-line"></div>');
|
|
|
|
$line.append.apply($line, chunk);
|
|
|
|
return $line
|
|
|
|
}));
|
|
|
|
|
|
|
|
html.push($(
|
|
|
|
'<div class="openwebrx-panel-line openwebrx-panel-flex-line">' +
|
2020-05-01 23:07:44 +00:00
|
|
|
'<div class="openwebrx-button openwebrx-demodulator-button openwebrx-button-dig">DIG</div>' +
|
2020-05-01 22:05:20 +00:00
|
|
|
'<select class="openwebrx-secondary-demod-listbox">' +
|
|
|
|
'<option value="none"></option>' +
|
|
|
|
digiModes.map(function(m){
|
|
|
|
return '<option value="' + m.modulation + '">' + m.name + '</option>';
|
|
|
|
}).join('') +
|
|
|
|
'</select>' +
|
|
|
|
'</div>'
|
|
|
|
));
|
|
|
|
|
|
|
|
this.el.find(".openwebrx-modes").html(html);
|
|
|
|
};
|
|
|
|
|
2020-05-01 23:07:44 +00:00
|
|
|
DemodulatorPanel.prototype.setMode = function(modulation) {
|
2020-05-01 22:05:20 +00:00
|
|
|
var mode = Modes.findByModulation(modulation);
|
|
|
|
if (!mode) {
|
|
|
|
return;
|
|
|
|
}
|
2020-05-03 10:48:25 +00:00
|
|
|
if (this.mode === mode) {
|
|
|
|
return;
|
|
|
|
}
|
2020-05-01 22:05:20 +00:00
|
|
|
if (!mode.isAvailable()) {
|
2020-05-01 23:07:44 +00:00
|
|
|
divlog('Modulation "' + mode.name + '" not supported. Please check requirements', true);
|
2020-05-01 22:05:20 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-05-01 23:07:44 +00:00
|
|
|
|
|
|
|
if (mode.type === 'digimode') {
|
|
|
|
modulation = mode.underlying[0];
|
|
|
|
}
|
|
|
|
|
2020-05-03 17:55:48 +00:00
|
|
|
var current = this.collectParams();
|
2020-05-01 23:07:44 +00:00
|
|
|
if (this.demodulator) {
|
2020-05-03 17:55:48 +00:00
|
|
|
current.offset_frequency = this.demodulator.get_offset_frequency();
|
|
|
|
current.squelch_level = this.demodulator.getSquelch();
|
2020-05-01 22:05:20 +00:00
|
|
|
}
|
2020-05-01 23:07:44 +00:00
|
|
|
|
2020-05-03 10:48:25 +00:00
|
|
|
this.stopDemodulator();
|
2020-05-03 17:55:48 +00:00
|
|
|
this.demodulator = new Demodulator(current.offset_frequency, modulation);
|
|
|
|
this.demodulator.setSquelch(current.squelch_level);
|
2020-05-03 10:48:25 +00:00
|
|
|
var self = this;
|
2020-05-03 19:26:11 +00:00
|
|
|
var updateFrequency = function(freq) {
|
2020-05-03 10:48:25 +00:00
|
|
|
self.tuneableFrequencyDisplay.setFrequency(self.center_freq + freq);
|
|
|
|
self.updateHash();
|
2020-05-03 19:26:11 +00:00
|
|
|
};
|
|
|
|
this.demodulator.on("frequencychange", updateFrequency);
|
|
|
|
updateFrequency(this.demodulator.get_offset_frequency());
|
|
|
|
var updateSquelch = function(squelch) {
|
2020-05-03 17:55:48 +00:00
|
|
|
self.el.find('.openwebrx-squelch-slider').val(squelch);
|
|
|
|
self.updateHash();
|
|
|
|
};
|
|
|
|
this.demodulator.on('squelchchange', updateSquelch);
|
|
|
|
updateSquelch(this.demodulator.getSquelch());
|
|
|
|
|
2020-05-01 23:07:44 +00:00
|
|
|
if (mode.type === 'digimode') {
|
|
|
|
this.demodulator.set_secondary_demod(mode.modulation);
|
2020-05-03 10:23:23 +00:00
|
|
|
if (mode.bandpass) {
|
|
|
|
this.demodulator.setBandpass(mode.bandpass);
|
|
|
|
}
|
2020-05-01 23:07:44 +00:00
|
|
|
} else {
|
|
|
|
this.demodulator.set_secondary_demod(false);
|
|
|
|
}
|
2020-05-01 23:35:38 +00:00
|
|
|
|
2020-05-03 10:48:25 +00:00
|
|
|
this.demodulator.start();
|
2020-05-03 11:10:54 +00:00
|
|
|
this.mode = mode;
|
2020-05-01 23:07:44 +00:00
|
|
|
|
|
|
|
this.updateButtons();
|
|
|
|
this.updatePanels();
|
2020-05-02 13:17:09 +00:00
|
|
|
this.updateHash();
|
2020-05-01 23:07:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
DemodulatorPanel.prototype.disableDigiMode = function() {
|
2020-05-03 10:48:25 +00:00
|
|
|
this.setMode(this.getDemodulator().get_modulation());
|
2020-05-01 23:07:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
DemodulatorPanel.prototype.updatePanels = function() {
|
|
|
|
var modulation = this.getDemodulator().get_secondary_demod();
|
2020-05-01 22:05:20 +00:00
|
|
|
$('#openwebrx-panel-digimodes').attr('data-mode', modulation);
|
2020-05-01 23:07:44 +00:00
|
|
|
toggle_panel("openwebrx-panel-digimodes", !!modulation);
|
2020-05-01 22:05:20 +00:00
|
|
|
toggle_panel("openwebrx-panel-wsjt-message", ['ft8', 'wspr', 'jt65', 'jt9', 'ft4'].indexOf(modulation) >= 0);
|
|
|
|
toggle_panel("openwebrx-panel-js8-message", modulation == "js8");
|
|
|
|
toggle_panel("openwebrx-panel-packet-message", modulation === "packet");
|
|
|
|
toggle_panel("openwebrx-panel-pocsag-message", modulation === "pocsag");
|
2020-05-03 10:09:18 +00:00
|
|
|
|
|
|
|
modulation = this.getDemodulator().get_modulation();
|
|
|
|
var showing = 'openwebrx-panel-metadata-' + modulation;
|
|
|
|
$(".openwebrx-meta-panel").each(function (_, p) {
|
|
|
|
toggle_panel(p.id, p.id === showing);
|
|
|
|
});
|
|
|
|
clear_metadata();
|
2020-05-01 22:05:20 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
DemodulatorPanel.prototype.getDemodulator = function() {
|
|
|
|
return this.demodulator;
|
|
|
|
};
|
|
|
|
|
2020-05-03 17:55:48 +00:00
|
|
|
DemodulatorPanel.prototype.collectParams = function() {
|
|
|
|
var defaults = {
|
|
|
|
offset_frequency: 0,
|
|
|
|
squelch_level: -150,
|
|
|
|
mod: 'nfm'
|
|
|
|
}
|
|
|
|
return $.extend(new Object(), defaults, this.initialParams || {}, this.transformHashParams(this.parseHash()));
|
|
|
|
};
|
|
|
|
|
2020-05-01 22:05:20 +00:00
|
|
|
DemodulatorPanel.prototype.startDemodulator = function() {
|
2020-05-02 13:07:47 +00:00
|
|
|
if (!Modes.initComplete()) return;
|
2020-05-03 17:55:48 +00:00
|
|
|
var params = this.collectParams();
|
2020-05-01 22:05:20 +00:00
|
|
|
this._apply(params);
|
|
|
|
};
|
|
|
|
|
2020-05-02 12:51:00 +00:00
|
|
|
DemodulatorPanel.prototype.stopDemodulator = function() {
|
|
|
|
if (!this.demodulator) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.demodulator.stop();
|
2020-05-03 11:10:54 +00:00
|
|
|
this.demodulator = null;
|
|
|
|
this.mode = null;
|
2020-05-02 12:51:00 +00:00
|
|
|
}
|
|
|
|
|
2020-05-01 22:05:20 +00:00
|
|
|
DemodulatorPanel.prototype._apply = function(params) {
|
|
|
|
this.setMode(params.mod);
|
|
|
|
this.getDemodulator().set_offset_frequency(params.offset_frequency);
|
2020-05-03 17:55:48 +00:00
|
|
|
this.getDemodulator().setSquelch(params.squelch_level);
|
2020-05-01 22:05:20 +00:00
|
|
|
this.updateButtons();
|
|
|
|
};
|
|
|
|
|
|
|
|
DemodulatorPanel.prototype.setInitialParams = function(params) {
|
|
|
|
this.initialParams = params;
|
|
|
|
};
|
|
|
|
|
2020-05-02 13:17:09 +00:00
|
|
|
DemodulatorPanel.prototype.onHashChange = function() {
|
|
|
|
this._apply(this.transformHashParams(this.parseHash()));
|
2020-05-02 00:13:16 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
DemodulatorPanel.prototype.transformHashParams = function(params) {
|
2020-05-03 17:55:48 +00:00
|
|
|
var ret = {
|
|
|
|
mod: params.secondary_mod || params.mod
|
2020-05-02 00:13:16 +00:00
|
|
|
};
|
2020-05-03 17:55:48 +00:00
|
|
|
if (params.offset_frequency) ret.offset_frequency = params.offset_frequency;
|
|
|
|
if (params.sql) ret.squelch_level = parseInt(params.sql);
|
|
|
|
return ret;
|
2020-05-01 22:05:20 +00:00
|
|
|
};
|
|
|
|
|
2020-05-03 17:55:48 +00:00
|
|
|
DemodulatorPanel.prototype.squelchAvailable = function () {
|
|
|
|
return this.mode && this.mode.squelch;
|
|
|
|
}
|
|
|
|
|
2020-05-01 22:05:20 +00:00
|
|
|
DemodulatorPanel.prototype.updateButtons = function() {
|
|
|
|
var $buttons = this.el.find(".openwebrx-demodulator-button");
|
|
|
|
$buttons.removeClass("highlighted").removeClass('disabled');
|
|
|
|
var demod = this.getDemodulator()
|
|
|
|
if (!demod) return;
|
2020-05-01 23:07:44 +00:00
|
|
|
this.el.find('[data-modulation=' + demod.get_modulation() + ']').addClass("highlighted");
|
|
|
|
var secondary_demod = demod.get_secondary_demod()
|
2020-05-01 22:05:20 +00:00
|
|
|
if (secondary_demod) {
|
2020-05-01 23:07:44 +00:00
|
|
|
this.el.find(".openwebrx-button-dig").addClass("highlighted");
|
|
|
|
this.el.find('.openwebrx-secondary-demod-listbox').val(secondary_demod);
|
2020-05-01 22:05:20 +00:00
|
|
|
var mode = Modes.findByModulation(secondary_demod);
|
|
|
|
if (mode) {
|
|
|
|
$buttons.filter(function(){
|
2020-05-01 23:07:44 +00:00
|
|
|
var mod = $(this).data('modulation');
|
|
|
|
return mod && mode.underlying.indexOf(mod) < 0;
|
2020-05-01 22:05:20 +00:00
|
|
|
}).addClass('disabled');
|
|
|
|
}
|
2020-05-01 23:07:44 +00:00
|
|
|
} else {
|
|
|
|
this.el.find('.openwebrx-secondary-demod-listbox').val('none');
|
2020-05-01 22:05:20 +00:00
|
|
|
}
|
2020-05-03 17:55:48 +00:00
|
|
|
var squelch_disabled = !this.squelchAvailable();
|
|
|
|
this.el.find('.openwebrx-squelch-slider').prop('disabled', squelch_disabled);
|
|
|
|
this.el.find('.openwebrx-squelch-default')[squelch_disabled ? 'addClass' : 'removeClass']('disabled');
|
2020-05-01 22:05:20 +00:00
|
|
|
}
|
|
|
|
|
2020-05-02 00:32:49 +00:00
|
|
|
DemodulatorPanel.prototype.setCenterFrequency = function(center_freq) {
|
|
|
|
if (this.center_freq === center_freq) {
|
2020-05-02 13:07:47 +00:00
|
|
|
return;
|
2020-05-02 00:32:49 +00:00
|
|
|
}
|
2020-05-02 13:07:47 +00:00
|
|
|
this.stopDemodulator();
|
2020-05-02 00:32:49 +00:00
|
|
|
this.center_freq = center_freq;
|
2020-05-02 13:07:47 +00:00
|
|
|
this.startDemodulator();
|
2020-05-02 00:32:49 +00:00
|
|
|
};
|
|
|
|
|
2020-05-02 13:17:09 +00:00
|
|
|
DemodulatorPanel.prototype.parseHash = function() {
|
|
|
|
if (!window.location.hash) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
var params = window.location.hash.substring(1).split(",").map(function(x) {
|
|
|
|
var harr = x.split('=');
|
|
|
|
return [harr[0], harr.slice(1).join('=')];
|
|
|
|
}).reduce(function(params, p){
|
|
|
|
params[p[0]] = p[1];
|
|
|
|
return params;
|
|
|
|
}, {});
|
|
|
|
|
|
|
|
return this.validateHash(params);
|
|
|
|
};
|
|
|
|
|
|
|
|
DemodulatorPanel.prototype.validateHash = function(params) {
|
|
|
|
var self = this;
|
|
|
|
params = Object.keys(params).filter(function(key) {
|
2020-05-03 17:55:48 +00:00
|
|
|
if (key == 'freq' || key == 'mod' || key == 'secondary_mod' || key == 'sql') {
|
2020-05-02 13:17:09 +00:00
|
|
|
return params.freq && Math.abs(params.freq - self.center_freq) < bandwidth;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}).reduce(function(p, key) {
|
|
|
|
p[key] = params[key];
|
|
|
|
return p;
|
|
|
|
}, {});
|
|
|
|
|
|
|
|
if (params['freq']) {
|
|
|
|
params['offset_frequency'] = params['freq'] - self.center_freq;
|
|
|
|
delete params['freq'];
|
|
|
|
}
|
|
|
|
|
|
|
|
return params;
|
|
|
|
};
|
|
|
|
|
|
|
|
DemodulatorPanel.prototype.updateHash = function() {
|
|
|
|
var demod = this.getDemodulator();
|
|
|
|
if (!demod) return;
|
|
|
|
var self = this;
|
|
|
|
window.location.hash = $.map({
|
|
|
|
freq: demod.get_offset_frequency() + self.center_freq,
|
|
|
|
mod: demod.get_modulation(),
|
2020-05-03 17:55:48 +00:00
|
|
|
secondary_mod: demod.get_secondary_demod(),
|
|
|
|
sql: demod.getSquelch(),
|
2020-05-02 13:17:09 +00:00
|
|
|
}, function(value, key){
|
|
|
|
if (!value) return undefined;
|
|
|
|
return key + '=' + value;
|
|
|
|
}).filter(function(v) {
|
|
|
|
return !!v;
|
|
|
|
}).join(',');
|
|
|
|
};
|
|
|
|
|
2020-05-03 17:55:48 +00:00
|
|
|
DemodulatorPanel.prototype.updateSquelch = function() {
|
|
|
|
var sliderValue = parseInt(this.el.find(".openwebrx-squelch-slider").val());
|
|
|
|
var demod = this.getDemodulator();
|
|
|
|
if (demod) demod.setSquelch(sliderValue);
|
|
|
|
};
|
|
|
|
|
2020-05-01 22:05:20 +00:00
|
|
|
$.fn.demodulatorPanel = function(){
|
|
|
|
if (!this.data('panel')) {
|
|
|
|
this.data('panel', new DemodulatorPanel(this));
|
|
|
|
};
|
|
|
|
return this.data('panel');
|
|
|
|
};
|