mirror of
https://github.com/wavelog/wavelog.git
synced 2026-03-22 10:24:14 +00:00
Initial commit of DX Waterfall for Wavelog
This commit is contained in:
@@ -46,6 +46,18 @@ class Band extends CI_Controller {
|
||||
$this->load->view('interface_assets/footer', $footerData);
|
||||
}
|
||||
|
||||
// API endpoint to get band edges for the logged-in user
|
||||
public function get_user_bandedges()
|
||||
{
|
||||
$this->load->model('bands');
|
||||
|
||||
$data = $this->bands->get_all_bandedges_for_user();
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($data);
|
||||
return;
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
$this->load->model('bands');
|
||||
|
||||
@@ -88,6 +88,14 @@ class QSO extends CI_Controller {
|
||||
$data['user_dok_to_qso_tab'] = 0;
|
||||
}
|
||||
|
||||
// Get status of DX Waterfall enable option
|
||||
$qkey_opt=$this->user_options_model->get_options('dxwaterfall',array('option_name'=>'enable','option_key'=>'boolean'))->result();
|
||||
if (count($qkey_opt)>0) {
|
||||
$data['user_dxwaterfall_enable'] = $qkey_opt[0]->option_value;
|
||||
} else {
|
||||
$data['user_dxwaterfall_enable'] = 0;
|
||||
}
|
||||
|
||||
$data['qso_count'] = $this->session->userdata('qso_page_last_qso_count');
|
||||
|
||||
$this->load->library('form_validation');
|
||||
|
||||
@@ -208,6 +208,7 @@ class User extends CI_Controller {
|
||||
$data['user_dashboard_map'] = $this->input->post('user_dashboard_map') ?? 'Y';
|
||||
$data['user_dashboard_banner'] = $this->input->post('user_dashboard_banner') ?? 'Y';
|
||||
$data['user_dashboard_solar'] = $this->input->post('user_dashboard_solar') ?? 'Y';
|
||||
$data['user_dxwaterfall_enable'] = $this->input->post('user_dxwaterfall_enable') ?? 'N';
|
||||
$data['user_stylesheet'] = $this->input->post('user_stylesheet');
|
||||
$data['user_qth_lookup'] = $this->input->post('user_qth_lookup');
|
||||
$data['user_sota_lookup'] = $this->input->post('user_sota_lookup');
|
||||
@@ -303,6 +304,7 @@ class User extends CI_Controller {
|
||||
$this->input->post('user_dashboard_banner') ?? 'Y',
|
||||
$this->input->post('user_dashboard_solar') ?? 'Y',
|
||||
$this->input->post('clubstation') == '1' ? true : false,
|
||||
$this->input->post('user_dxwaterfall_enable') ?? 'N',
|
||||
$this->input->post('global_oqrs_text') ?? '',
|
||||
$this->input->post('oqrs_grouped_search') ?? 'off',
|
||||
$this->input->post('oqrs_grouped_search_show_station_name') ?? 'off',
|
||||
@@ -366,6 +368,7 @@ class User extends CI_Controller {
|
||||
$data['oqrs_grouped_search_show_station_name'] = $this->input->post('oqrs_grouped_search_show_station_name') ?? 'off';
|
||||
$data['oqrs_auto_matching'] = $this->input->post('oqrs_auto_matching') ?? 'on';
|
||||
$data['oqrs_direct_auto_matching'] = $this->input->post('oqrs_direct_auto_matching') ?? 'on';
|
||||
$data['user_dxwaterfall_enable'] = $this->input->post('user_dxwaterfall_enable') ?? 'N';
|
||||
$this->load->view('user/edit', $data);
|
||||
$this->load->view('interface_assets/footer', $footerData);
|
||||
}
|
||||
@@ -733,6 +736,16 @@ class User extends CI_Controller {
|
||||
}
|
||||
}
|
||||
|
||||
// DX Waterfall enable option
|
||||
if($this->input->post('user_dxwaterfall_enable')) {
|
||||
$data['user_dxwaterfall_enable'] = $this->input->post('user_dxwaterfall_enable', false);
|
||||
} else {
|
||||
$dkey_opt=$this->user_options_model->get_options('dxwaterfall',array('option_name'=>'enable','option_key'=>'boolean'), $this->uri->segment(3))->result();
|
||||
if (count($dkey_opt)>0) {
|
||||
$data['user_dxwaterfall_enable'] = $dkey_opt[0]->option_value;
|
||||
}
|
||||
}
|
||||
|
||||
if($this->input->post('user_hamsat_workable_only')) {
|
||||
$data['user_hamsat_workable_only'] = $this->input->post('user_hamsat_workable_only', false);
|
||||
} else {
|
||||
|
||||
@@ -224,7 +224,7 @@ class User_Model extends CI_Model {
|
||||
$user_wwff_to_qso_tab, $user_pota_to_qso_tab, $user_sig_to_qso_tab, $user_dok_to_qso_tab,
|
||||
$user_lotw_name, $user_lotw_password, $user_eqsl_name, $user_eqsl_password, $user_clublog_name, $user_clublog_password,
|
||||
$user_winkey, $on_air_widget_enabled, $on_air_widget_display_last_seen, $on_air_widget_show_only_most_recent_radio,
|
||||
$qso_widget_display_qso_time, $dashboard_banner,$dashboard_solar, $clubstation = 0) {
|
||||
$qso_widget_display_qso_time, $dashboard_banner,$dashboard_solar, $clubstation = 0,$user_dxwaterfall_enable) {
|
||||
// Check that the user isn't already used
|
||||
if(!$this->exists($username)) {
|
||||
$data = array(
|
||||
@@ -313,7 +313,7 @@ class User_Model extends CI_Model {
|
||||
$this->db->query("insert into user_options (user_id, option_type, option_name, option_key, option_value) values (" . $insert_id . ", 'widget','on_air','display_last_seen','".(xss_clean($on_air_widget_display_last_seen ?? 'false'))."');");
|
||||
$this->db->query("insert into user_options (user_id, option_type, option_name, option_key, option_value) values (" . $insert_id . ", 'widget','on_air','display_only_most_recent_radio','".(xss_clean($on_air_widget_show_only_most_recent_radio ?? 'true'))."');");
|
||||
$this->db->query("insert into user_options (user_id, option_type, option_name, option_key, option_value) values (" . $insert_id . ", 'widget','qso','display_qso_time','".(xss_clean($qso_widget_display_qso_time ?? 'false'))."');");
|
||||
|
||||
$this->db->query("insert into user_options (user_id, option_type, option_name, option_key, option_value) values (" . $insert_id . ", 'dxwaterfall','enable','boolean','".xss_clean($user_dxwaterfall_enable ?? 'N')."');");
|
||||
return OK;
|
||||
} else {
|
||||
return EUSERNAMEEXISTS;
|
||||
@@ -387,11 +387,13 @@ class User_Model extends CI_Model {
|
||||
$this->db->query("replace into user_options (user_id, option_type, option_name, option_key, option_value) values (" . $fields['id'] . ", 'dashboard','show_map','boolean','".xss_clean($fields['user_dashboard_map'] ?? 'Y')."');");
|
||||
$this->db->query("replace into user_options (user_id, option_type, option_name, option_key, option_value) values (" . $fields['id'] . ", 'dashboard','show_dashboard_banner','boolean','".xss_clean($fields['user_dashboard_banner'] ?? 'Y')."');");
|
||||
$this->db->query("replace into user_options (user_id, option_type, option_name, option_key, option_value) values (" . $fields['id'] . ", 'dashboard','show_dashboard_solar','boolean','".xss_clean($fields['user_dashboard_solar'] ?? 'N')."');");
|
||||
$this->db->query("replace into user_options (user_id, option_type, option_name, option_key, option_value) values (" . $fields['id'] . ", 'dxwaterfall','enable','boolean','".xss_clean($fields['user_dxwaterfall_enable'] ?? 'N')."');");
|
||||
$this->session->set_userdata('dashboard_last_qso_count', $dashboard_last_qso_count);
|
||||
$this->session->set_userdata('qso_page_last_qso_count', $qso_page_last_qso_count);
|
||||
$this->session->set_userdata('user_dashboard_map',xss_clean($fields['user_dashboard_map'] ?? 'Y'));
|
||||
$this->session->set_userdata('user_dashboard_banner',xss_clean($fields['user_dashboard_banner'] ?? 'Y'));
|
||||
$this->session->set_userdata('user_dashboard_solar',xss_clean($fields['user_dashboard_solar'] ?? 'N'));
|
||||
$this->session->set_userdata('user_dxwaterfall_enable',xss_clean($fields['user_dxwaterfall_enable'] ?? 'N'));
|
||||
|
||||
// Check to see if the user is allowed to change user levels
|
||||
if($this->session->userdata('user_type') == 99) {
|
||||
@@ -551,6 +553,7 @@ class User_Model extends CI_Model {
|
||||
'user_dashboard_map' => ((($this->session->userdata('user_dashboard_map') ?? 'Y') == 'Y') ? $this->user_options_model->get_options('dashboard', array('option_name' => 'show_map', 'option_key' => 'boolean'))->row()->option_value ?? 'Y' : $this->session->userdata('user_dashboard_map')),
|
||||
'user_dashboard_banner' => ((($this->session->userdata('user_dashboard_banner') ?? 'Y') == 'Y') ? $this->user_options_model->get_options('dashboard', array('option_name' => 'show_dashboard_banner', 'option_key' => 'boolean'))->row()->option_value ?? 'Y' : $this->session->userdata('user_dashboard_banner')),
|
||||
'user_dashboard_solar' => ((($this->session->userdata('user_dashboard_solar') ?? 'N') == 'Y') ? $this->session->userdata('user_dashboard_solar') : $this->user_options_model->get_options('dashboard', array('option_name' => 'show_dashboard_solar', 'option_key' => 'boolean'))->row()->option_value ?? 'N'),
|
||||
'user_dxwaterfall_enable' => ((($this->session->userdata('user_dxwaterfall_enable') ?? 'N') == 'Y') ? $this->session->userdata('user_dxwaterfall_enable') : $this->user_options_model->get_options('dxwaterfall', array('option_name' => 'enable', 'option_key' => 'boolean'))->row()->option_value ?? 'N'),
|
||||
'user_date_format' => $u->row()->user_date_format,
|
||||
'user_stylesheet' => $u->row()->user_stylesheet,
|
||||
'user_qth_lookup' => isset($u->row()->user_qth_lookup) ? $u->row()->user_qth_lookup : 0,
|
||||
|
||||
@@ -69,6 +69,47 @@
|
||||
var lang_notes_duplication_disabled_short = "<?= __("Duplication Disabled"); ?>";
|
||||
var lang_notes_not_found = "<?= __("No notes were found"); ?>";
|
||||
|
||||
/*
|
||||
DX Waterfall Language
|
||||
*/
|
||||
var lang_dxwaterfall_tune_to_spot = "<?= __("Tune to spot frequency [Ctrl+Shift+Space]"); ?>";
|
||||
var lang_dxwaterfall_cycle_through = "<?= __("Cycle through"); ?>";
|
||||
var lang_dxwaterfall_spots_currently_showing = "<?= __("spots (currently showing"); ?>";
|
||||
var lang_dxwaterfall_log_qso_with = "<?= __("Log QSO with"); ?>";
|
||||
var lang_dxwaterfall_new_continent = "<?= __("New Continent"); ?>";
|
||||
var lang_dxwaterfall_new_dxcc = "<?= __("New DXCC"); ?>";
|
||||
var lang_dxwaterfall_new_callsign = "<?= __("New Callsign"); ?>";
|
||||
var lang_dxwaterfall_previous_spot = "<?= __("Previous spot [Ctrl+Left] | First spot [Ctrl+Down]"); ?>";
|
||||
var lang_dxwaterfall_no_spots_lower = "<?= __("No spots at lower frequency"); ?>";
|
||||
var lang_dxwaterfall_next_spot = "<?= __("Next spot [Ctrl+Right] | Last spot [Ctrl+Up]"); ?>";
|
||||
var lang_dxwaterfall_no_spots_higher = "<?= __("No spots at higher frequency"); ?>";
|
||||
var lang_dxwaterfall_no_spots_available = "<?= __("No spots available"); ?>";
|
||||
var lang_dxwaterfall_cycle_unworked = "<?= __("Cycle through unworked continents/DXCC"); ?>";
|
||||
var lang_dxwaterfall_dx_hunter = "<?= __("DX Hunter"); ?>";
|
||||
var lang_dxwaterfall_no_unworked = "<?= __("No unworked continents/DXCC on this band"); ?>";
|
||||
var lang_dxwaterfall_fetching_spots = "<?= __("Fetching spots..."); ?>";
|
||||
var lang_dxwaterfall_click_to_cycle = "<?= __("Click to cycle or wait 1.5s to apply"); ?>";
|
||||
var lang_dxwaterfall_change_continent = "<?= __("Change spotter continent"); ?>";
|
||||
var lang_dxwaterfall_filter_by_mode = "<?= __("Filter by mode"); ?>";
|
||||
var lang_dxwaterfall_toggle_phone = "<?= __("Toggle Phone mode filter"); ?>";
|
||||
var lang_dxwaterfall_phone = "<?= __("Phone"); ?>";
|
||||
var lang_dxwaterfall_toggle_cw = "<?= __("Toggle CW mode filter"); ?>";
|
||||
var lang_dxwaterfall_cw = "<?= __("CW"); ?>";
|
||||
var lang_dxwaterfall_toggle_digi = "<?= __("Toggle Digital mode filter"); ?>";
|
||||
var lang_dxwaterfall_digi = "<?= __("Digi"); ?>";
|
||||
var lang_dxwaterfall_zoom_out = "<?= __("Zoom out [Ctrl+-]"); ?>";
|
||||
var lang_dxwaterfall_reset_zoom = "<?= __("Reset zoom to default (3)"); ?>";
|
||||
var lang_dxwaterfall_zoom_in = "<?= __("Zoom in [Ctrl++]"); ?>";
|
||||
var lang_dxwaterfall_waiting_data = "<?= __("Waiting for DX Cluster data..."); ?>";
|
||||
var lang_dxwaterfall_comment = "<?= __("Comment: "); ?>";
|
||||
var lang_dxwaterfall_modes_label = "<?= __("modes:"); ?>";
|
||||
var lang_dxwaterfall_out_of_bandplan = "<?= __("OUT OF BANDPLAN"); ?>";
|
||||
var lang_dxwaterfall_changing_frequency = "<?= __("Changing radio frequency..."); ?>";
|
||||
var lang_dxwaterfall_spots_fetched = "<?= __("spot(s) de"); ?>";
|
||||
var lang_dxwaterfall_fetched_for_band = "<?= __("fetched from DXCluster for band"); ?>";
|
||||
var lang_dxwaterfall_displaying = "<?= __(", displaying"); ?>";
|
||||
var lang_dxwaterfall_invalid = "<?= __("INVALID"); ?>";
|
||||
|
||||
</script>
|
||||
|
||||
<!-- General JS Files used across Wavelog -->
|
||||
@@ -1096,6 +1137,9 @@ $($('#callsign')).on('keypress',function(e) {
|
||||
<?php if ($this->uri->segment(1) == "qso") { ?>
|
||||
|
||||
<script src="<?php echo base_url() ;?>assets/js/sections/qso.js"></script>
|
||||
<?php if ($this->session->userdata('user_dxwaterfall_enable') == 'Y') { ?>
|
||||
<script src="<?php echo base_url() ;?>assets/js/dxwaterfall.js"></script>
|
||||
<?php } ?>
|
||||
<script src="<?php echo base_url() ;?>assets/js/sections/satellite_functions.js"></script>
|
||||
<script src="<?php echo base_url() ;?>assets/js/bootstrap-multiselect.js"></script>
|
||||
<?php if ($this->session->userdata('isWinkeyEnabled')) { ?>
|
||||
@@ -1366,6 +1410,9 @@ mymap.on('mousemove', onQsoMapMove);
|
||||
<script>
|
||||
$( document ).ready(function() {
|
||||
// Javascript for controlling rig frequency.
|
||||
var updateFromCAT_lock =0; // This mechanism prevents multiple simultaneous calls to query the CAT interface information
|
||||
var dxwaterfall_cat_debounce_lock = 0; // Lock to prevent CAT frequency updates when sending commands from waterfall
|
||||
window.dxwaterfall_cat_debounce_lock = dxwaterfall_cat_debounce_lock; // Make it global for waterfall access
|
||||
var updateFromCAT = function() {
|
||||
var cat2UI = function(ui, cat, allow_empty, allow_zero, callback_on_update) {
|
||||
// Check, if cat-data is available
|
||||
@@ -1385,8 +1432,11 @@ mymap.on('mousemove', onQsoMapMove);
|
||||
}
|
||||
|
||||
if($('select.radios option:selected').val() != '0') {
|
||||
|
||||
radioID = $('select.radios option:selected').val();
|
||||
if ((typeof radioID !== 'undefined') && (radioID !== null) && (radioID !== "")) {
|
||||
if ((typeof radioID !== 'undefined') && (radioID !== null) && (radioID !== "") && (updateFromCAT_lock == 0)) {
|
||||
updateFromCAT_lock = 1;
|
||||
// Debug: Log that we're polling CAT
|
||||
$.getJSON( "radio/json/" + radioID, function( data ) {
|
||||
/* {
|
||||
"frequency": "2400210000",
|
||||
@@ -1410,31 +1460,79 @@ mymap.on('mousemove', onQsoMapMove);
|
||||
if($('.radio_login_error').length != 0) {
|
||||
$(".radio_login_error" ).remove();
|
||||
}
|
||||
cat2UI($('#frequency'),data.frequency,false,true,function(d){
|
||||
$('#frequency').trigger('change');
|
||||
if ($("#band").val() != frequencyToBand(d)) {
|
||||
$("#band").val(frequencyToBand(d)).trigger('change'); // Let's only change if we really have a different band!
|
||||
}
|
||||
});
|
||||
|
||||
cat2UI($('#frequency_rx'),data.frequency_rx,false,true,function(d){$("#band_rx").val(frequencyToBand(d))});
|
||||
cat2UI($('.mode'),data.mode,false,false,function(d){setRst($(".mode").val())});
|
||||
cat2UI($('#sat_name'),data.satname,false,false);
|
||||
cat2UI($('#sat_mode'),data.satmode,false,false);
|
||||
cat2UI($('#transmit_power'),data.power,false,false);
|
||||
cat2UI($('#selectPropagation'),data.prop_mode,false,false);
|
||||
|
||||
// Display CAT Timeout warning based on the figure given in the config file
|
||||
var minutes = Math.floor(<?php echo $this->optionslib->get_option('cat_timeout_interval'); ?> / 60);
|
||||
|
||||
if(data.updated_minutes_ago > minutes) {
|
||||
|
||||
dxwaterfall_allowcat = false;
|
||||
$(".radio_cat_state" ).remove();
|
||||
if($('.radio_timeout_error').length == 0) {
|
||||
$('#radio_status').prepend('<div class="alert alert-danger radio_timeout_error" role="alert"><i class="fas fa-broadcast-tower"></i> Radio connection timed-out: ' + $('select.radios option:selected').text() + ' data is ' + data.updated_minutes_ago + ' minutes old.</div>');
|
||||
} else {
|
||||
$('.radio_timeout_error').html('Radio connection timed-out: ' + $('select.radios option:selected').text() + ' data is ' + data.updated_minutes_ago + ' minutes old.');
|
||||
}
|
||||
|
||||
dxwaterfall_caturl = "";
|
||||
} else {
|
||||
|
||||
// Handle frequency updates with waterfall debounce lock
|
||||
// Sync local variable with global one
|
||||
dxwaterfall_cat_debounce_lock = window.dxwaterfall_cat_debounce_lock || 0;
|
||||
|
||||
if (dxwaterfall_cat_debounce_lock == 0) {
|
||||
// Normal case - no lock, update frequency as usual
|
||||
cat2UI($('#frequency'),data.frequency,false,true,function(d){
|
||||
$('#frequency').trigger('change');
|
||||
if ($("#band").val() != frequencyToBand(d)) {
|
||||
$("#band").val(frequencyToBand(d)).trigger('change'); // Let's only change if we really have a different band!
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Locked - check if radio confirms our sent frequency
|
||||
if (typeof window.dxwaterfall_expected_frequency !== 'undefined' && window.dxwaterfall_expected_frequency) {
|
||||
var expectedFreq = parseFloat(window.dxwaterfall_expected_frequency);
|
||||
var actualFreq = parseFloat(data.frequency);
|
||||
var tolerance = 1000; // 1000 Hz tolerance (1 kHz)
|
||||
var diff = Math.abs(expectedFreq - actualFreq);
|
||||
|
||||
if (diff <= tolerance) {
|
||||
// Radio confirmed the frequency change - unlock and update
|
||||
dxwaterfall_cat_debounce_lock = 0;
|
||||
window.dxwaterfall_cat_debounce_lock = 0;
|
||||
window.dxwaterfall_expected_frequency = null;
|
||||
|
||||
cat2UI($('#frequency'),data.frequency,false,true,function(d){
|
||||
$('#frequency').trigger('change');
|
||||
if ($("#band").val() != frequencyToBand(d)) {
|
||||
$("#band").val(frequencyToBand(d)).trigger('change');
|
||||
}
|
||||
// Invalidate waterfall frequency cache to force update from new CAT frequency
|
||||
if (typeof dxWaterfall !== 'undefined' && dxWaterfall.invalidateFrequencyCache) {
|
||||
dxWaterfall.invalidateFrequencyCache();
|
||||
}
|
||||
// Update waterfall cached frequency after CAT frequency change
|
||||
if (typeof dxWaterfall !== 'undefined' && dxWaterfall.commitFrequency) {
|
||||
dxWaterfall.commitFrequency();
|
||||
}
|
||||
});
|
||||
}
|
||||
// If frequency doesn't match, stay locked and don't update
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
cat2UI($('#frequency_rx'),data.frequency_rx,false,true,function(d){$("#band_rx").val(frequencyToBand(d))});
|
||||
cat2UI($('.mode'),data.mode,false,false,function(d){setRst($(".mode").val())});
|
||||
cat2UI($('#sat_name'),data.satname,false,false);
|
||||
cat2UI($('#sat_mode'),data.satmode,false,false);
|
||||
cat2UI($('#transmit_power'),data.power,false,false);
|
||||
cat2UI($('#selectPropagation'),data.prop_mode,false,false);
|
||||
|
||||
dxwaterfall_allowcat = true;
|
||||
dxwaterfall_caturl=data.cat_url;
|
||||
|
||||
$(".radio_timeout_error" ).remove();
|
||||
separator = '<span style="margin-left:10px"></span>';
|
||||
text = '<i class="fas fa-broadcast-tower"></i>' + separator + '<b>TX:</b> ' + data.frequency_formatted;
|
||||
@@ -1465,13 +1563,19 @@ mymap.on('mousemove', onQsoMapMove);
|
||||
}
|
||||
}
|
||||
}
|
||||
updateFromCAT_lock = 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Update frequency every three second
|
||||
setInterval(updateFromCAT, 3000);
|
||||
// Update frequency from CAT
|
||||
// Add a small delay (500ms) before first poll to allow radio interface to initialize
|
||||
setTimeout(function() {
|
||||
updateFromCAT();
|
||||
// Then poll every 500ms
|
||||
setInterval(updateFromCAT, 500 * 1); // Note: this is minimum update intervals, there is a lock mechanism to prevent overlapping calls.
|
||||
}, 500);
|
||||
|
||||
// If a radios selected from drop down select radio update.
|
||||
$('.radios').change(updateFromCAT);
|
||||
|
||||
@@ -40,6 +40,152 @@ switch ($date_format) {
|
||||
var user_date_format = "<?php echo $date_format; ?>"; // Pass the user's date format to JavaScript
|
||||
</script>
|
||||
|
||||
<!--- DX Waterfall --->
|
||||
<?php if ($this->session->userdata('user_dxwaterfall_enable') == 'Y' && $manual_mode == 0) { ?>
|
||||
<script language="javascript">
|
||||
let dxwaterfall_decont = '<?php echo $this->optionslib->get_option('dxcluster_decont'); ?>';
|
||||
let dxwaterfall_maxage = '<?php echo $this->optionslib->get_option('dxcluster_maxage'); ?>';
|
||||
let dxwaterfall_allowcat = 0;
|
||||
let dxwaterfall_caturl = "";
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#dxWaterfall {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
display: block;
|
||||
border-left: 1px solid #000000;
|
||||
border-right: 1px solid #000000;
|
||||
}
|
||||
#dxWaterfallSpot {
|
||||
width: 100%;
|
||||
background-color: #000000;
|
||||
color: #FFFFFF;
|
||||
padding: 5px;
|
||||
font-family: "Consolas", "Courier New", monospace;
|
||||
font-size: 11px;
|
||||
border-left: 1px solid #000000;
|
||||
border-right: 1px solid #000000;
|
||||
border-top: 1px solid #000000;
|
||||
min-height: 20px;
|
||||
line-height: 1.4;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
#dxWaterfallMenu {
|
||||
width: 100%;
|
||||
background-color: #000000;
|
||||
color: #FFFFFF;
|
||||
padding: 5px;
|
||||
font-family: "Consolas", "Courier New", monospace;
|
||||
font-size: 11px;
|
||||
border-left: 1px solid #000000;
|
||||
border-right: 1px solid #000000;
|
||||
border-bottom: 1px solid #000000;
|
||||
min-height: 20px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
#dxWaterfallMenu .zoom-in-icon,
|
||||
#dxWaterfallMenu .zoom-out-icon,
|
||||
#dxWaterfallMenu .zoom-reset-icon,
|
||||
#dxWaterfallMenu .prev-spot-icon,
|
||||
#dxWaterfallMenu .next-spot-icon,
|
||||
#dxWaterfallMenu .smart-hunter-icon,
|
||||
#dxWaterfallMenu .continent-cycle-icon,
|
||||
#dxWaterfallMenu .mode-filter-icon {
|
||||
cursor: pointer;
|
||||
color: #FFFFFF;
|
||||
margin: 0 2px;
|
||||
font-size: 12px;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
#dxWaterfallMenu .zoom-in-icon:hover:not(.disabled),
|
||||
#dxWaterfallMenu .zoom-out-icon:hover:not(.disabled),
|
||||
#dxWaterfallMenu .zoom-reset-icon:hover:not(.disabled),
|
||||
#dxWaterfallMenu .prev-spot-icon:hover:not(.disabled),
|
||||
#dxWaterfallMenu .next-spot-icon:hover:not(.disabled),
|
||||
#dxWaterfallMenu .smart-hunter-icon:hover:not(.disabled),
|
||||
#dxWaterfallMenu .smart-hunter-text:hover:not(.disabled),
|
||||
#dxWaterfallMenu .continent-cycle-icon:hover:not(.disabled),
|
||||
#dxWaterfallMenu .continent-cycle-text:hover:not(.disabled),
|
||||
#dxWaterfallMenu .mode-filter-icon:hover:not(.disabled) {
|
||||
color: #AAAAAA;
|
||||
}
|
||||
#dxWaterfallMenu .smart-hunter-text,
|
||||
#dxWaterfallMenu .continent-cycle-text {
|
||||
cursor: pointer;
|
||||
color: #FFFFFF;
|
||||
margin-left: 5px;
|
||||
font-size: 11px;
|
||||
transition: color 0.2s;
|
||||
user-select: none;
|
||||
}
|
||||
#dxWaterfallMenu .mode-filter-phone,
|
||||
#dxWaterfallMenu .mode-filter-cw,
|
||||
#dxWaterfallMenu .mode-filter-digi {
|
||||
user-select: none;
|
||||
}
|
||||
#dxWaterfallMenu .mode-filter-phone:hover:not(.disabled),
|
||||
#dxWaterfallMenu .mode-filter-cw:hover:not(.disabled),
|
||||
#dxWaterfallMenu .mode-filter-digi:hover:not(.disabled) {
|
||||
opacity: 0.7;
|
||||
}
|
||||
@keyframes blink {
|
||||
0%, 49% { opacity: 1; }
|
||||
50%, 100% { opacity: 0.3; }
|
||||
}
|
||||
#dxWaterfallSpot .flag-emoji {
|
||||
font-family: "Twemoji Country Flags", "Helvetica", "Comic Sans", sans-serif !important;
|
||||
font-style: normal !important;
|
||||
font-variant: normal !important;
|
||||
}
|
||||
#dxWaterfallSpot .copy-icon,
|
||||
#dxWaterfallSpot .tune-icon,
|
||||
#dxWaterfallSpot .cycle-spot-icon {
|
||||
cursor: pointer;
|
||||
color: #FFFFFF;
|
||||
margin-right: 5px;
|
||||
font-size: 12px;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
#dxWaterfallSpot .copy-icon:hover,
|
||||
#dxWaterfallSpot .tune-icon:hover,
|
||||
#dxWaterfallSpot .cycle-spot-icon:hover {
|
||||
color: #AAAAAA;
|
||||
}
|
||||
#dxWaterfallSpot .new-continent-icon {
|
||||
color: #FFD700; /* Gold */
|
||||
margin-left: 5px;
|
||||
font-size: 12px;
|
||||
}
|
||||
#dxWaterfallSpot .new-dxcc-icon {
|
||||
color: #C0C0C0; /* Silver */
|
||||
margin-left: 5px;
|
||||
font-size: 12px;
|
||||
}
|
||||
#dxWaterfallSpot .new-callsign-icon {
|
||||
color: #CD7F32; /* Bronze */
|
||||
margin-left: 5px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dxwaterfallpane{
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<div class="row dxwaterfallpane">
|
||||
<div class="col-sm-12"><div id="dxWaterfallSpot"> </div></div>
|
||||
<div class="col-sm-12">
|
||||
<canvas id="dxWaterfall"></canvas>
|
||||
</div>
|
||||
<div class="col-sm-12"><div id="dxWaterfallMenu"> </div></div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<div class="row qsopane">
|
||||
|
||||
<div class="col-sm-5">
|
||||
@@ -818,7 +964,7 @@ switch ($date_format) {
|
||||
<?php
|
||||
$result = $this->optionslib->get_option('disable_refresh_past_contacts');
|
||||
if($result === null) { ?>
|
||||
<div id="qso-last-table" hx-get="<?php echo site_url('/qso/component_past_contacts'); ?>" hx-trigger="load, qso_event, every 5s">
|
||||
<div id="qso-last-table" hx-get="<?php echo site_url('/qso/component_past_contacts'); ?>" hx-trigger="load, qso_event, every 15s">
|
||||
<?php } else { ?>
|
||||
<div id="qso-last-table" hx-get="<?php echo site_url('/qso/component_past_contacts'); ?>" hx-trigger="load, qso_event">
|
||||
<?php } ?>
|
||||
|
||||
@@ -430,6 +430,16 @@
|
||||
} ?>
|
||||
</select>
|
||||
</div>
|
||||
<!--- DX Waterfall -->
|
||||
<div class="mb-3">
|
||||
<label for="user_dxwaterfall_enable"><?= __("DX Waterfall"); ?></label>
|
||||
<?php if(!isset($user_dxwaterfall_enable)) { $user_dxwaterfall_enable='N'; }?>
|
||||
<select class="form-select" id="user_dxwaterfall_enable" name="user_dxwaterfall_enable" aria-describedby="user_dxwaterfall_enable_Help" required>
|
||||
<option value='Y' <?php if($user_dxwaterfall_enable == "Y") { echo "selected=\"selected\""; } ?>><?= __("Enabled"); ?></option>
|
||||
<option value='N' <?php if($user_dxwaterfall_enable == "N") { echo "selected=\"selected\""; } ?>><?= __("Disabled"); ?></option>
|
||||
</select>
|
||||
<small id="user_dxwaterfall_enable_Help" class="form-text text-muted"><?= __("Show an interactive DX Cluster 'Waterfall' on the QSO logging page."); ?></small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
4558
assets/js/dxwaterfall.js
Normal file
4558
assets/js/dxwaterfall.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -61,8 +61,31 @@ async function set_qrg() {
|
||||
}
|
||||
|
||||
async function set_new_qrg() {
|
||||
let new_qrg = $('#freq_calculated').val().trim();
|
||||
let new_qrg = $('#freq_calculated').val();
|
||||
|
||||
// Trim and validate input
|
||||
if (new_qrg !== null && new_qrg !== undefined) {
|
||||
new_qrg = new_qrg.trim();
|
||||
}
|
||||
|
||||
let parsed_qrg = parseFloat(new_qrg);
|
||||
|
||||
// If field is empty or parsing failed, fetch default frequency for current band/mode
|
||||
if (!new_qrg || new_qrg === '' || isNaN(parsed_qrg) || !isFinite(parsed_qrg) || parsed_qrg <= 0) {
|
||||
if (typeof base_url !== 'undefined') {
|
||||
try {
|
||||
const result = await $.get(base_url + 'index.php/qso/band_to_freq/' + $('#band').val() + '/' + $('.mode').val());
|
||||
$('#frequency').val(result);
|
||||
await set_qrg();
|
||||
return;
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch default frequency:', error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let unit = $('#qrg_unit').html();
|
||||
|
||||
// check if the input contains a unit and parse the qrg
|
||||
@@ -131,6 +154,14 @@ $('#freq_calculated').on('change', function () {
|
||||
|
||||
$('#freq_calculated').on('keydown', function (e) {
|
||||
if (e.which === 13) {
|
||||
// Check if DX Waterfall is active - if so, let it handle Enter key
|
||||
if (typeof dxWaterfall !== 'undefined' && $('#dxWaterfall').length > 0) {
|
||||
// DX Waterfall is active, don't handle Enter here
|
||||
// Let dxwaterfall.js handle it instead
|
||||
return;
|
||||
}
|
||||
|
||||
// DX Waterfall not active, use normal behavior
|
||||
e.preventDefault();
|
||||
if ($('#callsign').val() != '') {
|
||||
set_new_qrg().then(() => {
|
||||
@@ -140,4 +171,4 @@ $('#freq_calculated').on('keydown', function (e) {
|
||||
set_new_qrg();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -399,7 +399,7 @@ function parseUserDate(user_provided_date) { // creates JS-Date out of user-prov
|
||||
month = parseInt(parts[1], 10) - 1;
|
||||
year = parseInt(parts[2], 10);
|
||||
}
|
||||
if (isNaN(day) || day < 1 || day > 31 || isNaN(month) || month < 0 || month > 11 || isNaN(year)) return null;
|
||||
if (isNaN(day) || day < 1 || day > 31 || isNaN(month) || month < 0 || month > 11 || isNaN(year)) return null;
|
||||
return new Date(year, month, day);
|
||||
}
|
||||
|
||||
@@ -1795,9 +1795,12 @@ $('#start_date').on('change', function () {
|
||||
/* on mode change */
|
||||
$('.mode').on('change', function () {
|
||||
if ($('#radio').val() == 0 && $('#sat_name').val() == '') {
|
||||
$.get(base_url + 'index.php/qso/band_to_freq/' + $('#band').val() + '/' + $('.mode').val(), function (result) {
|
||||
$('#frequency').val(result).trigger("change");
|
||||
});
|
||||
// Only fetch default frequency if frequency field is empty
|
||||
if ($('#frequency').val() == '' || $('#frequency').val() == null) {
|
||||
$.get(base_url + 'index.php/qso/band_to_freq/' + $('#band').val() + '/' + $('.mode').val(), function (result) {
|
||||
$('#frequency').val(result).trigger("change");
|
||||
});
|
||||
}
|
||||
$('#frequency_rx').val("");
|
||||
}
|
||||
$("#callsign").blur();
|
||||
@@ -1806,17 +1809,54 @@ $('.mode').on('change', function () {
|
||||
/* Calculate Frequency */
|
||||
/* on band change */
|
||||
$('#band').on('change', function () {
|
||||
if ($('#radio').val() == 0) {
|
||||
$.get(base_url + 'index.php/qso/band_to_freq/' + $(this).val() + '/' + $('.mode').val(), function (result) {
|
||||
$('#frequency').val(result).trigger("change");
|
||||
});
|
||||
const selectedBand = $(this).val();
|
||||
|
||||
// Clear the QSO form when band is manually changed
|
||||
$('#btn_reset').click();
|
||||
|
||||
// Set flag to prevent waterfall from auto-reverting the band change
|
||||
if (typeof dxWaterfall !== 'undefined') {
|
||||
dxWaterfall.userChangedBand = true;
|
||||
if (dxWaterfall.userChangedBandTimer) {
|
||||
clearTimeout(dxWaterfall.userChangedBandTimer);
|
||||
}
|
||||
dxWaterfall.userChangedBandTimer = setTimeout(function() {
|
||||
dxWaterfall.userChangedBand = false;
|
||||
}, 10000);
|
||||
|
||||
// Reset operation timer when band is manually changed
|
||||
dxWaterfall.operationStartTime = Date.now();
|
||||
}
|
||||
|
||||
// Always fetch default frequency for the new band when band is changed manually
|
||||
// This ensures frequency field matches the selected band
|
||||
$.get(base_url + 'index.php/qso/band_to_freq/' + selectedBand + '/' + $('.mode').val(), function (result) {
|
||||
// Set the frequency
|
||||
$('#frequency').val(result);
|
||||
|
||||
// Update the displayed frequency field with proper units
|
||||
set_qrg();
|
||||
|
||||
// If DX Waterfall is active and CAT is available, tune the radio
|
||||
if (typeof dxWaterfall !== 'undefined' && $('#dxWaterfall').length > 0) {
|
||||
// Check if CAT is available (same check as in dxwaterfall.js)
|
||||
const catAvailable = (typeof dxwaterfall_allowcat !== 'undefined' && dxwaterfall_allowcat !== null &&
|
||||
typeof dxwaterfall_caturl !== 'undefined' && dxwaterfall_caturl !== null &&
|
||||
dxwaterfall_allowcat && dxwaterfall_caturl !== "");
|
||||
|
||||
if (catAvailable && typeof setFrequency === 'function') {
|
||||
// Convert Hz to kHz and tune the radio
|
||||
const freqKHz = result / 1000;
|
||||
setFrequency(freqKHz);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$('#frequency_rx').val("");
|
||||
$('#band_rx').val("");
|
||||
$("#selectPropagation").val("");
|
||||
$("#sat_name").val("");
|
||||
$("#sat_mode").val("");
|
||||
set_qrg();
|
||||
$("#callsign").blur();
|
||||
stop_az_ele_ticker();
|
||||
});
|
||||
|
||||
261
assets/json/iaru_bandplans.json
Normal file
261
assets/json/iaru_bandplans.json
Normal file
@@ -0,0 +1,261 @@
|
||||
{
|
||||
"version": "2025-10-19",
|
||||
"R1": {
|
||||
"2200m": {
|
||||
"start_hz": 135700,
|
||||
"end_hz": 137800
|
||||
},
|
||||
"630m": {
|
||||
"start_hz": 472000,
|
||||
"end_hz": 479000
|
||||
},
|
||||
"160m": {
|
||||
"start_hz": 1810000,
|
||||
"end_hz": 2000000
|
||||
},
|
||||
"80m": {
|
||||
"start_hz": 3500000,
|
||||
"end_hz": 3800000
|
||||
},
|
||||
"60m": {
|
||||
"start_hz": 5351500,
|
||||
"end_hz": 5366500
|
||||
},
|
||||
"40m": {
|
||||
"start_hz": 7000000,
|
||||
"end_hz": 7200000
|
||||
},
|
||||
"30m": {
|
||||
"start_hz": 10100000,
|
||||
"end_hz": 10150000
|
||||
},
|
||||
"20m": {
|
||||
"start_hz": 14000000,
|
||||
"end_hz": 14350000
|
||||
},
|
||||
"17m": {
|
||||
"start_hz": 18068000,
|
||||
"end_hz": 18168000
|
||||
},
|
||||
"15m": {
|
||||
"start_hz": 21000000,
|
||||
"end_hz": 21450000
|
||||
},
|
||||
"12m": {
|
||||
"start_hz": 24890000,
|
||||
"end_hz": 24990000
|
||||
},
|
||||
"10m": {
|
||||
"start_hz": 28000000,
|
||||
"end_hz": 29700000
|
||||
},
|
||||
"70cm": {
|
||||
"start_hz": 430000000,
|
||||
"end_hz": 440000000
|
||||
},
|
||||
"23cm": {
|
||||
"start_hz": 1240000000,
|
||||
"end_hz": 1300000000
|
||||
},
|
||||
"13cm": {
|
||||
"start_hz": 2300000000,
|
||||
"end_hz": 2450000000
|
||||
},
|
||||
"9cm": {
|
||||
"start_hz": 3400000000,
|
||||
"end_hz": 3475000000
|
||||
},
|
||||
"6cm": {
|
||||
"start_hz": 5650000000,
|
||||
"end_hz": 5850000000
|
||||
},
|
||||
"3cm": {
|
||||
"start_hz": 10000000000,
|
||||
"end_hz": 10500000000
|
||||
},
|
||||
"1.2cm": {
|
||||
"start_hz": 24000000000,
|
||||
"end_hz": 24250000000
|
||||
}
|
||||
},
|
||||
"R2": {
|
||||
"2200m": {
|
||||
"start_hz": 135700,
|
||||
"end_hz": 137800
|
||||
},
|
||||
"630m": {
|
||||
"start_hz": 472000,
|
||||
"end_hz": 479000
|
||||
},
|
||||
"160m": {
|
||||
"start_hz": 1800000,
|
||||
"end_hz": 2000000
|
||||
},
|
||||
"80m": {
|
||||
"start_hz": 3500000,
|
||||
"end_hz": 4000000
|
||||
},
|
||||
"60m": {
|
||||
"start_hz": 5351500,
|
||||
"end_hz": 5366500
|
||||
},
|
||||
"40m": {
|
||||
"start_hz": 7000000,
|
||||
"end_hz": 7300000
|
||||
},
|
||||
"30m": {
|
||||
"start_hz": 10100000,
|
||||
"end_hz": 10150000
|
||||
},
|
||||
"20m": {
|
||||
"start_hz": 14000000,
|
||||
"end_hz": 14350000
|
||||
},
|
||||
"17m": {
|
||||
"start_hz": 18068000,
|
||||
"end_hz": 18168000
|
||||
},
|
||||
"15m": {
|
||||
"start_hz": 21000000,
|
||||
"end_hz": 21450000
|
||||
},
|
||||
"12m": {
|
||||
"start_hz": 24890000,
|
||||
"end_hz": 24990000
|
||||
},
|
||||
"10m": {
|
||||
"start_hz": 28000000,
|
||||
"end_hz": 29700000
|
||||
},
|
||||
"6m": {
|
||||
"start_hz": 50000000,
|
||||
"end_hz": 54000000
|
||||
},
|
||||
"2m": {
|
||||
"start_hz": 144000000,
|
||||
"end_hz": 148000000
|
||||
},
|
||||
"1.25m": {
|
||||
"start_hz": 220000000,
|
||||
"end_hz": 225000000
|
||||
},
|
||||
"70cm": {
|
||||
"start_hz": 420000000,
|
||||
"end_hz": 450000000
|
||||
},
|
||||
"33cm": {
|
||||
"start_hz": 902000000,
|
||||
"end_hz": 928000000
|
||||
},
|
||||
"23cm": {
|
||||
"start_hz": 1240000000,
|
||||
"end_hz": 1294000000
|
||||
},
|
||||
"13cm": {
|
||||
"start_hz": 2303000000,
|
||||
"end_hz": 2450000000
|
||||
},
|
||||
"9cm": {
|
||||
"start_hz": 3300000000,
|
||||
"end_hz": 3500000000
|
||||
},
|
||||
"6cm": {
|
||||
"start_hz": 5650000000,
|
||||
"end_hz": 5850000000
|
||||
},
|
||||
"3cm": {
|
||||
"start_hz": 10000000000,
|
||||
"end_hz": 10500000000
|
||||
},
|
||||
"1.2cm": {
|
||||
"start_hz": 24000000000,
|
||||
"end_hz": 24250000000
|
||||
}
|
||||
},
|
||||
"R3": {
|
||||
"2200m": {
|
||||
"start_hz": 135700,
|
||||
"end_hz": 137800
|
||||
},
|
||||
"630m": {
|
||||
"start_hz": 472000,
|
||||
"end_hz": 479000
|
||||
},
|
||||
"160m": {
|
||||
"start_hz": 1800000,
|
||||
"end_hz": 2000000
|
||||
},
|
||||
"80m": {
|
||||
"start_hz": 3500000,
|
||||
"end_hz": 3900000
|
||||
},
|
||||
"60m": {
|
||||
"start_hz": 5351500,
|
||||
"end_hz": 5366500
|
||||
},
|
||||
"40m": {
|
||||
"start_hz": 7000000,
|
||||
"end_hz": 7200000
|
||||
},
|
||||
"30m": {
|
||||
"start_hz": 10110000,
|
||||
"end_hz": 10150000
|
||||
},
|
||||
"20m": {
|
||||
"start_hz": 14000000,
|
||||
"end_hz": 14350000
|
||||
},
|
||||
"17m": {
|
||||
"start_hz": 18068000,
|
||||
"end_hz": 18168000
|
||||
},
|
||||
"15m": {
|
||||
"start_hz": 21000000,
|
||||
"end_hz": 21125000
|
||||
},
|
||||
"12m": {
|
||||
"start_hz": 24890000,
|
||||
"end_hz": 24990000
|
||||
},
|
||||
"10m": {
|
||||
"start_hz": 28000000,
|
||||
"end_hz": 29700000
|
||||
},
|
||||
"6m": {
|
||||
"start_hz": 50000000,
|
||||
"end_hz": 54000000
|
||||
},
|
||||
"2m": {
|
||||
"start_hz": 144000000,
|
||||
"end_hz": 148000000
|
||||
},
|
||||
"70cm": {
|
||||
"start_hz": 430000000,
|
||||
"end_hz": 440000000
|
||||
},
|
||||
"23cm": {
|
||||
"start_hz": 1240000000,
|
||||
"end_hz": 1300000000
|
||||
},
|
||||
"13cm": {
|
||||
"start_hz": 2300000000,
|
||||
"end_hz": 2450000000
|
||||
},
|
||||
"9cm": {
|
||||
"start_hz": 3300000000,
|
||||
"end_hz": 3500000000
|
||||
},
|
||||
"6cm": {
|
||||
"start_hz": 5650000000,
|
||||
"end_hz": 5850000000
|
||||
},
|
||||
"3cm": {
|
||||
"start_hz": 10000000000,
|
||||
"end_hz": 10500000000
|
||||
},
|
||||
"1.2cm": {
|
||||
"start_hz": 24000000000,
|
||||
"end_hz": 24250000000
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user