From 09e892fc334f7a9d27010aa879ed6194b9727b74 Mon Sep 17 00:00:00 2001 From: Andreas Kristiansen <6977712+AndreasK79@users.noreply.github.com> Date: Thu, 22 Jan 2026 11:55:30 +0100 Subject: [PATCH 01/11] [DXCC ID] All checking now goes via the class --- application/controllers/Api.php | 16 +- application/controllers/Calltester.php | 352 +++++++++++++++++++++- application/controllers/Dxcalendar.php | 7 +- application/controllers/Dxcluster.php | 10 +- application/controllers/Logbook.php | 10 +- application/libraries/DxclusterCache.php | 8 +- application/models/Logbook_model.php | 355 +---------------------- 7 files changed, 388 insertions(+), 370 deletions(-) diff --git a/application/controllers/Api.php b/application/controllers/Api.php index 3311591ee..7dcfdf2d4 100644 --- a/application/controllers/Api.php +++ b/application/controllers/Api.php @@ -956,8 +956,8 @@ class API extends CI_Controller { ]; $return['callsign'] = $lookup_callsign; - - $callsign_dxcc_lookup = $this->logbook_model->dxcc_lookup($lookup_callsign, $date); + $dxccobj = new Dxcc(null); + $callsign_dxcc_lookup = $dxccobj->dxcc_lookup($lookup_callsign, $date); $last_slash_pos = strrpos($lookup_callsign, '/'); @@ -974,7 +974,7 @@ class API extends CI_Controller { break; default: // If its not one of the above suffix slashes its likely dxcc - $ans2 = $this->logbook_model->dxcc_lookup($suffix_slash, $date); + $ans2 = $dxccobj->dxcc_lookup($suffix_slash, $date); $suffix_slash_item = null; } @@ -1283,19 +1283,19 @@ class API extends CI_Controller { private function sanitize_cat_url($url) { // Basic sanitization $url = trim($url); - + // Check if URL is valid and uses http or https - if (!filter_var($url, FILTER_VALIDATE_URL) || + if (!filter_var($url, FILTER_VALIDATE_URL) || (!preg_match('/^https?:\/\//', $url))) { return false; } - + // Remove trailing slashes $url = rtrim($url, '/'); - + // Additional XSS cleaning $url = $this->security->xss_clean($url); - + return $url; } diff --git a/application/controllers/Calltester.php b/application/controllers/Calltester.php index f922c2ce0..09fc100d2 100644 --- a/application/controllers/Calltester.php +++ b/application/controllers/Calltester.php @@ -66,7 +66,6 @@ class Calltester extends CI_Controller { foreach ($callarray->result() as $call) { $i++; - //$dxcc = $this->logbook_model->dxcc_lookup($call->col_call, $call->date); $dxcc = $dxccobj->dxcc_lookup($call->col_call, $call->date); $dxcc['adif'] = (isset($dxcc['adif'])) ? $dxcc['adif'] : 0; @@ -99,9 +98,8 @@ class Calltester extends CI_Controller { return $data; } - /* Uses Logbook_model and the normal dxcc lookup, which is slow */ + /* Uses the normal dxcc lookup, which is slow */ function doDxccCheckModel($de) { - $this->load->model('logbook_model'); $i = 0; $result = array(); @@ -112,7 +110,7 @@ class Calltester extends CI_Controller { foreach ($callarray->result() as $call) { $i++; - $dxcc = $this->logbook_model->dxcc_lookup($call->col_call, $call->date); + $dxcc = $this->dxcc_lookup($call->col_call, $call->date); $dxcc['adif'] = (isset($dxcc['adif'])) ? $dxcc['adif'] : 0; $dxcc['entity'] = (isset($dxcc['entity'])) ? $dxcc['entity'] : 0; @@ -255,8 +253,6 @@ class Calltester extends CI_Controller { // Starting clock time in seconds $start_time = microtime(true); - $this->load->model('logbook_model'); - $file = 'uploads/calls.csv'; $handle = fopen($file,"r"); @@ -272,7 +268,7 @@ class Calltester extends CI_Controller { // COL_CALL,COL_DXCC,COL_TIME_ON $i++; - $dxcc = $this->logbook_model->dxcc_lookup($data[0], $data[2]); + $dxcc = $this->dxcc_lookup($data[0], $data[2]); $dxcc['adif'] = (isset($dxcc['adif'])) ? $dxcc['adif'] : 0; $dxcc['entity'] = (isset($dxcc['entity'])) ? $dxcc['entity'] : 0; @@ -315,8 +311,6 @@ class Calltester extends CI_Controller { // Starting clock time in seconds $start_time = microtime(true); - $this->load->model('logbook_model'); - $file = 'uploads/calls.csv'; $handle = fopen($file,"r"); @@ -332,7 +326,7 @@ class Calltester extends CI_Controller { // COL_CALL,COL_DXCC,COL_TIME_ON $i++; - $dxcc = $this->logbook_model->check_dxcc_table($data[0], $data[2]); + $dxcc = $this->check_dxcc_table($data[0], $data[2]); $data[1] = $data[1] == "NULL" ? 0 : $data[1]; @@ -805,8 +799,6 @@ class Calltester extends CI_Controller { // Starting clock time in seconds $start_time = microtime(true); - $this->load->model('logbook_model'); - $result = array(); $i = 0; @@ -846,4 +838,340 @@ class Calltester extends CI_Controller { $this->load->view('calltester/call'); $this->load->view('interface_assets/footer'); } + + public function dxcc_lookup($call, $date) { + + $date = date("Y-m-d", strtotime($date)); + $csadditions = '/^X$|^D$|^T$|^P$|^R$|^B$|^A$|^M$|^LH$|^L$|^J$|^SK$/'; + + $dxcc_exceptions = $this->db->select('`entity`, `adif`, `cqz`,`cont`,`long`,`lat`') + ->where('`call`', $call) + ->where('(start <= ', $date) + ->or_where('start is null)', NULL, false) + ->where('(end >= ', $date) + ->or_where('end is null)', NULL, false) + ->get('dxcc_exceptions'); + if ($dxcc_exceptions->num_rows() > 0) { + $row = $dxcc_exceptions->row_array(); + return $row; + } else { + + if (preg_match('/(^KG4)[A-Z09]{3}/', $call)) { // KG4/ and KG4 5 char calls are Guantanamo Bay. If 4 or 6 char, it is USA + $call = "K"; + } elseif (preg_match('/(^OH\/)|(\/OH[1-9]?$)/', $call)) { # non-Aland prefix! + $call = "OH"; # make callsign OH = finland + } elseif (preg_match('/(^CX\/)|(\/CX[1-9]?$)/', $call)) { # non-Antarctica prefix! + $call = "CX"; # make callsign CX = Uruguay + } elseif (preg_match('/(^3D2R)|(^3D2.+\/R)/', $call)) { # seems to be from Rotuma + $call = "3D2/R"; # will match with Rotuma + } elseif (preg_match('/^3D2C/', $call)) { # seems to be from Conway Reef + $call = "3D2/C"; # will match with Conway + } elseif (preg_match('/(^LZ\/)|(\/LZ[1-9]?$)/', $call)) { # LZ/ is LZ0 by DXCC but this is VP8h + $call = "LZ"; + } elseif (preg_match('/(^KG4)[A-Z09]{2}/', $call)) { + $call = "KG4"; + } elseif (preg_match('/(^KG4)[A-Z09]{1}/', $call)) { + $call = "K"; + } elseif (preg_match('/\w\/\w/', $call)) { + if (preg_match_all('/^((\d|[A-Z])+\/)?((\d|[A-Z]){3,})(\/(\d|[A-Z])+)?(\/(\d|[A-Z])+)?$/', $call, $matches)) { + $prefix = $matches[1][0]; + $callsign = $matches[3][0]; + $suffix = $matches[5][0]; + if ($prefix) { + $prefix = substr($prefix, 0, -1); # Remove the / at the end + } + if ($suffix) { + $suffix = substr($suffix, 1); # Remove the / at the beginning + }; + if (preg_match($csadditions, $suffix)) { + if ($prefix) { + $call = $prefix; + } else { + $call = $callsign; + } + } else { + $result = $this->wpx($call, 1); # use the wpx prefix instead + if ($result == '') { + $row['adif'] = 0; + $row['cont'] = ''; + $row['entity'] = '- NONE -'; + $row['ituz'] = 0; + $row['cqz'] = 0; + $row['long'] = '0'; + $row['lat'] = '0'; + return $row; + } else { + $call = $result . "AA"; + } + } + } + } + + $len = strlen($call); + $dxcc_array = []; + + // Fetch all candidates in one shot instead of looping + $dxcc_result = $this->db->query("SELECT `dxcc_prefixes`.`record`, `dxcc_prefixes`.`call`, `dxcc_prefixes`.`entity`, `dxcc_prefixes`.`adif`, `dxcc_prefixes`.`cqz`, `dxcc_entities`.`ituz`, `dxcc_prefixes`.`cont`, `dxcc_prefixes`.`long`, `dxcc_prefixes`.`lat`, `dxcc_prefixes`.`start`, `dxcc_prefixes`.`end` + FROM `dxcc_prefixes` + LEFT JOIN `dxcc_entities` ON `dxcc_entities`.`adif` = `dxcc_prefixes`.`adif` + WHERE ? like concat(`call`,'%') + and `dxcc_prefixes`.`call` like ? + AND (`dxcc_prefixes`.`start` <= ? OR `dxcc_prefixes`.`start` is null) + AND (`dxcc_prefixes`.`end` >= ? OR `dxcc_prefixes`.`end` is null) order by length(`call`) desc limit 1", array($call, substr($call, 0, 1) . '%', $date, $date)); + + foreach ($dxcc_result->result_array() as $row) { + $dxcc_array[$row['call']] = $row; + } + + // query the table, removing a character from the right until a match + for ($i = $len; $i > 0; $i--) { + //printf("searching for %s\n", substr($call, 0, $i)); + if (array_key_exists(substr($call, 0, $i), $dxcc_array)) { + $row = $dxcc_array[substr($call, 0, $i)]; + // $row = $dxcc_result->row_array(); + return $row; + } + } + } + + return array( + 'adif' => 0, + 'cqz' => 0, + 'ituz' => 0, + 'long' => '', + 'lat' => '', + 'entity' => 'None', + ); + } + + function wpx($testcall, $i) { + $prefix = ''; + $a = ''; + $b = ''; + $c = ''; + + $lidadditions = '/^QRP$|^LGT$/'; + $csadditions = '/^X$|^D$|^T$|^P$|^R$|^B$|^A$|^M$|^LH$|^L$|^J$|^SK$/'; + $noneadditions = '/^MM$|^AM$/'; + + # First check if the call is in the proper format, A/B/C where A and C + # are optional (prefix of guest country and P, MM, AM etc) and B is the + # callsign. Only letters, figures and "/" is accepted, no further check if the + # callsign "makes sense". + # 23.Apr.06: Added another "/X" to the regex, for calls like RV0AL/0/P + # as used by RDA-DXpeditions.... + + if (preg_match_all('/^((\d|[A-Z])+\/)?((\d|[A-Z]){3,})(\/(\d|[A-Z])+)?(\/(\d|[A-Z])+)?$/', $testcall, $matches)) { + + # Now $1 holds A (incl /), $3 holds the callsign B and $5 has C + # We save them to $a, $b and $c respectively to ensure they won't get + # lost in further Regex evaluations. + $a = $matches[1][0]; + $b = $matches[3][0]; + $c = $matches[5][0]; + + if ($a) { + $a = substr($a, 0, -1); # Remove the / at the end + } + if ($c) { + $c = substr($c, 1); # Remove the / at the beginning + }; + + # In some cases when there is no part A but B and C, and C is longer than 2 + # letters, it happens that $a and $b get the values that $b and $c should + # have. This often happens with liddish callsign-additions like /QRP and + # /LGT, but also with calls like DJ1YFK/KP5. ~/.yfklog has a line called + # "lidadditions", which has QRP and LGT as defaults. This sorts out half of + # the problem, but not calls like DJ1YFK/KH5. This is tested in a second + # try: $a looks like a call (.\d[A-Z]) and $b doesn't (.\d), they are + # swapped. This still does not properly handle calls like DJ1YFK/KH7K where + # only the OP's experience says that it's DJ1YFK on KH7K. + if (!$c && $a && $b) { # $a and $b exist, no $c + if (preg_match($lidadditions, $b)) { # check if $b is a lid-addition + $b = $a; + $a = null; # $a goes to $b, delete lid-add + } elseif ((preg_match('/\d[A-Z]+$/', $a)) && (preg_match('/\d$/', $b) || preg_match('/^[A-Z]\d[A-Z]$/', $b))) { # check for call in $a + $temp = $b; + $b = $a; + $a = $temp; + } + } + + # *** Added later *** The check didn't make sure that the callsign + # contains a letter. there are letter-only callsigns like RAEM, but not + # figure-only calls. + + if (preg_match('/^[0-9]+$/', $b)) { # Callsign only consists of numbers. Bad! + return null; # exit, undef + } + + # Depending on these values we have to determine the prefix. + # Following cases are possible: + # + # 1. $a and $c undef --> only callsign, subcases + # 1.1 $b contains a number -> everything from start to number + # 1.2 $b contains no number -> first two letters plus 0 + # 2. $a undef, subcases: + # 2.1 $c is only a number -> $a with changed number + # 2.2 $c is /P,/M,/MM,/AM -> 1. + # 2.3 $c is something else and will be interpreted as a Prefix + # 3. $a is defined, will be taken as PFX, regardless of $c + + if (($a == null) && ($c == null)) { # Case 1 + if (preg_match('/\d/', $b)) { # Case 1.1, contains number + preg_match('/(.+\d)[A-Z]*/', $b, $matches); # Prefix is all but the last + $prefix = $matches[1]; # Letters + } else { # Case 1.2, no number + $prefix = substr($b, 0, 2) . "0"; # first two + 0 + } + } elseif (($a == null) && (isset($c))) { # Case 2, CALL/X + if (preg_match('/^(\d)/', $c)) { # Case 2.1, number + preg_match('/(.+\d)[A-Z]*/', $b, $matches); # regular Prefix in $1 + # Here we need to find out how many digits there are in the + # prefix, because for example A45XR/0 is A40. If there are 2 + # numbers, the first is not deleted. If course in exotic cases + # like N66A/7 -> N7 this brings the wrong result of N67, but I + # think that's rather irrelevant cos such calls rarely appear + # and if they do, it's very unlikely for them to have a number + # attached. You can still edit it by hand anyway.. + if (preg_match('/^([A-Z]\d)\d$/', $matches[1])) { # e.g. A45 $c = 0 + $prefix = $matches[1] . $c; # -> A40 + } else { # Otherwise cut all numbers + preg_match('/(.*[A-Z])\d+/', $matches[1], $match); # Prefix w/o number in $1 + $prefix = $match[1] . $c; # Add attached number + } + } elseif (preg_match($csadditions, $c)) { + preg_match('/(.+\d)[A-Z]*/', $b, $matches); # Known attachment -> like Case 1.1 + $prefix = $matches[1]; + } elseif (preg_match($noneadditions, $c)) { + return ''; + } elseif (preg_match('/^\d\d+$/', $c)) { # more than 2 numbers -> ignore + preg_match('/(.+\d)[A-Z]* /', $b, $matches); # see above + $prefix = $matches[1][0]; + } else { # Must be a Prefix! + if (preg_match('/\d$/', $c)) { # ends in number -> good prefix + $prefix = $c; + } else { # Add Zero at the end + $prefix = $c . "0"; + } + } + } elseif (($a) && (preg_match($noneadditions, $c))) { # Case 2.1, X/CALL/X ie TF/DL2NWK/MM - DXCC none + return ''; + } elseif ($a) { + # $a contains the prefix we want + if (preg_match('/\d$/', $a)) { # ends in number -> good prefix + $prefix = $a; + } else { # add zero if no number + $prefix = $a . "0"; + } + } + # In very rare cases (right now I can only think of KH5K and KH7K and FRxG/T + # etc), the prefix is wrong, for example KH5K/DJ1YFK would be KH5K0. In this + # case, the superfluous part will be cropped. Since this, however, changes the + # DXCC of the prefix, this will NOT happen when invoked from with an + # extra parameter $_[1]; this will happen when invoking it from &dxcc. + + if (preg_match('/(\w+\d)[A-Z]+\d/', $prefix, $matches) && $i == null) { + $prefix = $matches[1][0]; + } + return $prefix; + } else { + return ''; + } + } + + /* + * Check the dxcc_prefixes table and return (dxcc, country) + */ + public function check_dxcc_table($call, $date) { + + $date = date("Y-m-d", strtotime($date)); + $csadditions = '/^X$|^D$|^T$|^P$|^R$|^B$|^A$|^M$|^LH$|^L$|^J$|^SK$/'; + + $dxcc_exceptions = $this->db->select('`entity`, `adif`, `cqz`, `cont`') + ->where('`call`', $call) + ->where('(start <= ', $date) + ->or_where('start is null)', NULL, false) + ->where('(end >= ', $date) + ->or_where('end is null)', NULL, false) + ->get('dxcc_exceptions'); + + if ($dxcc_exceptions->num_rows() > 0) { + $row = $dxcc_exceptions->row_array(); + return array($row['adif'], $row['entity'], $row['cqz'], $row['cont']); + } + if (preg_match('/(^KG4)[A-Z09]{3}/', $call)) { // KG4/ and KG4 5 char calls are Guantanamo Bay. If 4 or 6 char, it is USA + $call = "K"; + } elseif (preg_match('/(^OH\/)|(\/OH[1-9]?$)/', $call)) { # non-Aland prefix! + $call = "OH"; # make callsign OH = finland + } elseif (preg_match('/(^CX\/)|(\/CX[1-9]?$)/', $call)) { # non-Antarctica prefix! + $call = "CX"; # make callsign CX = Uruguay + } elseif (preg_match('/(^3D2R)|(^3D2.+\/R)/', $call)) { # seems to be from Rotuma + $call = "3D2/R"; # will match with Rotuma + } elseif (preg_match('/^3D2C/', $call)) { # seems to be from Conway Reef + $call = "3D2/C"; # will match with Conway + } elseif (preg_match('/(^LZ\/)|(\/LZ[1-9]?$)/', $call)) { # LZ/ is LZ0 by DXCC but this is VP8h + $call = "LZ"; + } elseif (preg_match('/(^KG4)[A-Z09]{2}/', $call)) { + $call = "KG4"; + } elseif (preg_match('/(^KG4)[A-Z09]{1}/', $call)) { + $call = "K"; + } elseif (preg_match('/\w\/\w/', $call)) { + if (preg_match_all('/^((\d|[A-Z])+\/)?((\d|[A-Z]){3,})(\/(\d|[A-Z])+)?(\/(\d|[A-Z])+)?$/', $call, $matches)) { + $prefix = $matches[1][0]; + $callsign = $matches[3][0]; + $suffix = $matches[5][0]; + if ($prefix) { + $prefix = substr($prefix, 0, -1); # Remove the / at the end + } + if ($suffix) { + $suffix = substr($suffix, 1); # Remove the / at the beginning + }; + if (preg_match($csadditions, $suffix)) { + if ($prefix) { + $call = $prefix; + } else { + $call = $callsign; + } + } else { + $result = $this->wpx($call, 1); # use the wpx prefix instead + if ($result == '') { + $row['adif'] = 0; + $row['entity'] = '- NONE -'; + $row['cqz'] = 0; + $row['cont'] = ''; + return array($row['adif'], $row['entity'], $row['cqz'], $row['cont']); + } else { + $call = $result . "AA"; + } + } + } + } + + $len = strlen($call); + $dxcc_array = []; + // Fetch all candidates in one shot instead of looping + $dxcc_result = $this->db->query("SELECT `call`, `entity`, `adif`, `cqz`, `cont` + FROM `dxcc_prefixes` + WHERE ? like concat(`call`,'%') + and `call` like ? + AND (`start` <= ? OR start is null) + AND (`end` >= ? OR end is null) order by length(`call`) desc limit 1", array($call, substr($call, 0, 1) . '%', $date, $date)); + + foreach ($dxcc_result->result_array() as $row) { + $dxcc_array[$row['call']] = $row; + } + + // query the table, removing a character from the right until a match + for ($i = $len; $i > 0; $i--) { + //printf("searching for %s\n", substr($call, 0, $i)); + if (array_key_exists(substr($call, 0, $i), $dxcc_array)) { + $row = $dxcc_array[substr($call, 0, $i)]; + // $row = $dxcc_result->row_array(); + return array($row['adif'], $row['entity'], $row['cqz'], $row['cont']); + } + } + + return array("Not Found", "Not Found"); + } } diff --git a/application/controllers/Dxcalendar.php b/application/controllers/Dxcalendar.php index d6e275d3c..991448576 100644 --- a/application/controllers/Dxcalendar.php +++ b/application/controllers/Dxcalendar.php @@ -1,5 +1,9 @@ config->item('qso_date_format'); } + $dxccobj = new Dxcc(null); foreach ($rssdata->channel->item as $item) { $dxped=(object)[]; $title = explode('--', $item->title); @@ -47,7 +52,7 @@ class Dxcalendar extends CI_Controller { $call = (string) $descsplit[3]; $dxped->call = trim(str_replace('--', '', $call)); - $chk_dxcc=$this->logbook_model->dxcc_lookup($dxped->call."X",$dxped->dates[2]->format('Y-m-d')); // X because sometimes only the pref is in XML + $chk_dxcc = $dxccobj->dxcc_lookup($dxped->call."X",$dxped->dates[2]->format('Y-m-d')); // X because sometimes only the pref is in XML if ($chk_dxcc['adif'] ?? '' != '') { $chk_dxcc_val=$chk_dxcc['adif']; $dxped->no_dxcc=false; diff --git a/application/controllers/Dxcluster.php b/application/controllers/Dxcluster.php index b4a92e75c..928d3a8d9 100644 --- a/application/controllers/Dxcluster.php +++ b/application/controllers/Dxcluster.php @@ -1,5 +1,9 @@ load->model('logbook_model'); + $date = date('Y-m-d', time()); + $dxccobj = new Dxcc($date); - $date = date('Ymd', time()); - $dxcc = $this->logbook_model->dxcc_lookup($call, $date); + $dxcc = $dxccobj->dxcc_lookup($call, $date); if ($dxcc) { header('Content-Type: application/json'); diff --git a/application/controllers/Logbook.php b/application/controllers/Logbook.php index 414cf6d1c..96370589a 100644 --- a/application/controllers/Logbook.php +++ b/application/controllers/Logbook.php @@ -1,5 +1,8 @@ subdivisions->get_primary_subdivision_name($data['query']->result()[0]->COL_DXCC); $data['secondary_subdivision'] = $this->subdivisions->get_secondary_subdivision_name($data['query']->result()[0]->COL_DXCC); $data['max_upload'] = ini_get('upload_max_filesize'); - } + } $this->load->view('interface_assets/mini_header', $data); $this->load->view('view_log/qso'); $this->load->view('interface_assets/footer'); @@ -1180,11 +1183,12 @@ class Logbook extends CI_Controller { $this->load->model('user_model'); if(!$this->user_model->authorize($this->config->item('auth_mode'))) { return; } - $this->load->model("logbook_model"); if ($date == ''){ $date = date("Y-m-d"); } - $ans = $this->logbook_model->dxcc_lookup($call, $date); + $dxccobj = new Dxcc($date); + + $ans = $dxccobj->dxcc_lookup($call, $date); return $ans; } diff --git a/application/libraries/DxclusterCache.php b/application/libraries/DxclusterCache.php index 083e7723a..ea1e07654 100644 --- a/application/libraries/DxclusterCache.php +++ b/application/libraries/DxclusterCache.php @@ -1,6 +1,10 @@ deleteFile($this->getWorkedCallKey($logbook_key, $callsign)); // Look up DXCC and continent from callsign - $this->CI->load->model('logbook_model'); - $dxcc_info = $this->CI->logbook_model->dxcc_lookup($callsign, date('Y-m-d')); + $dxccobj = new Dxcc($date); + $dxcc_info = $dxccobj->dxcc_lookup($callsign, date('Y-m-d')); if (!empty($dxcc_info['adif'])) { $this->deleteFile($this->getWorkedDxccKey($logbook_key, $dxcc_info['adif'])); diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index f86403146..b0f82066c 100644 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -1,5 +1,9 @@ input->post('continent') == ""); if ($needs_dxcc_lookup) { - $dxcc = $this->check_dxcc_table(strtoupper(trim($callsign)), $datetime); + $dxccobj = new Dxcc($date); + $dxcc = $dxccobj->dxcc_lookup(strtoupper(trim($callsign)), $datetime); } if ($this->input->post('country') == "") { @@ -4908,19 +4913,23 @@ class Logbook_model extends CI_Model { $entity = $this->get_entity($record['dxcc']); $dxcc = array($record['dxcc'] ?? '', $entity['name'] ?? ''); } else { - $dxcc = $this->check_dxcc_table($record['call'], $time_off); + $dxccobj = new Dxcc(null); + $dxcclookupresult = $dxccobj->dxcc_lookup($record['call'], $time_off); + $dxcc = array($dxcclookupresult['adif'], $dxcclookupresult['entity'], $dxcclookupresult['cqz'], $dxcclookupresult['cont']); } } else { - $dxcc = $this->check_dxcc_table($record['call'], $time_off); + $dxccobj = new Dxcc(null); + $dxcclookupresult = $dxccobj->dxcc_lookup($record['call'], $time_off); + $dxcc = array($dxcclookupresult['adif'], $dxcclookupresult['entity'], $dxcclookupresult['cqz'], $dxcclookupresult['cont']); } } else { $dxcc = NULL; } if (isset($record['cont'])) { - $cont=$record['cont']; + $cont = $record['cont']; } elseif (($dxcc[3] ?? '') != '') { - $cont=$dxcc[3]; + $cont = $dxcc[3]; } else { $cont=''; } @@ -5764,342 +5773,6 @@ class Logbook_model extends CI_Model { } } - /* - * Check the dxxc_prefixes table and return (dxcc, country) - */ - public function check_dxcc_table($call, $date) { - - $date = date("Y-m-d", strtotime($date)); - $csadditions = '/^X$|^D$|^T$|^P$|^R$|^B$|^A$|^M$|^LH$|^L$|^J$|^SK$/'; - - $dxcc_exceptions = $this->db->select('`entity`, `adif`, `cqz`, `cont`') - ->where('`call`', $call) - ->where('(start <= ', $date) - ->or_where('start is null)', NULL, false) - ->where('(end >= ', $date) - ->or_where('end is null)', NULL, false) - ->get('dxcc_exceptions'); - - if ($dxcc_exceptions->num_rows() > 0) { - $row = $dxcc_exceptions->row_array(); - return array($row['adif'], $row['entity'], $row['cqz'], $row['cont']); - } - if (preg_match('/(^KG4)[A-Z09]{3}/', $call)) { // KG4/ and KG4 5 char calls are Guantanamo Bay. If 4 or 6 char, it is USA - $call = "K"; - } elseif (preg_match('/(^OH\/)|(\/OH[1-9]?$)/', $call)) { # non-Aland prefix! - $call = "OH"; # make callsign OH = finland - } elseif (preg_match('/(^CX\/)|(\/CX[1-9]?$)/', $call)) { # non-Antarctica prefix! - $call = "CX"; # make callsign CX = Uruguay - } elseif (preg_match('/(^3D2R)|(^3D2.+\/R)/', $call)) { # seems to be from Rotuma - $call = "3D2/R"; # will match with Rotuma - } elseif (preg_match('/^3D2C/', $call)) { # seems to be from Conway Reef - $call = "3D2/C"; # will match with Conway - } elseif (preg_match('/(^LZ\/)|(\/LZ[1-9]?$)/', $call)) { # LZ/ is LZ0 by DXCC but this is VP8h - $call = "LZ"; - } elseif (preg_match('/(^KG4)[A-Z09]{2}/', $call)) { - $call = "KG4"; - } elseif (preg_match('/(^KG4)[A-Z09]{1}/', $call)) { - $call = "K"; - } elseif (preg_match('/\w\/\w/', $call)) { - if (preg_match_all('/^((\d|[A-Z])+\/)?((\d|[A-Z]){3,})(\/(\d|[A-Z])+)?(\/(\d|[A-Z])+)?$/', $call, $matches)) { - $prefix = $matches[1][0]; - $callsign = $matches[3][0]; - $suffix = $matches[5][0]; - if ($prefix) { - $prefix = substr($prefix, 0, -1); # Remove the / at the end - } - if ($suffix) { - $suffix = substr($suffix, 1); # Remove the / at the beginning - }; - if (preg_match($csadditions, $suffix)) { - if ($prefix) { - $call = $prefix; - } else { - $call = $callsign; - } - } else { - $result = $this->wpx($call, 1); # use the wpx prefix instead - if ($result == '') { - $row['adif'] = 0; - $row['entity'] = '- NONE -'; - $row['cqz'] = 0; - $row['cont'] = ''; - return array($row['adif'], $row['entity'], $row['cqz'], $row['cont']); - } else { - $call = $result . "AA"; - } - } - } - } - - $len = strlen($call); - $dxcc_array = []; - // Fetch all candidates in one shot instead of looping - $dxcc_result = $this->db->query("SELECT `call`, `entity`, `adif`, `cqz`, `cont` - FROM `dxcc_prefixes` - WHERE ? like concat(`call`,'%') - and `call` like ? - AND (`start` <= ? OR start is null) - AND (`end` >= ? OR end is null) order by length(`call`) desc limit 1", array($call, substr($call, 0, 1) . '%', $date, $date)); - - foreach ($dxcc_result->result_array() as $row) { - $dxcc_array[$row['call']] = $row; - } - - // query the table, removing a character from the right until a match - for ($i = $len; $i > 0; $i--) { - //printf("searching for %s\n", substr($call, 0, $i)); - if (array_key_exists(substr($call, 0, $i), $dxcc_array)) { - $row = $dxcc_array[substr($call, 0, $i)]; - // $row = $dxcc_result->row_array(); - return array($row['adif'], $row['entity'], $row['cqz'], $row['cont']); - } - } - - return array("Not Found", "Not Found"); - } - - public function dxcc_lookup($call, $date) { - - $date = date("Y-m-d", strtotime($date)); - $csadditions = '/^X$|^D$|^T$|^P$|^R$|^B$|^A$|^M$|^LH$|^L$|^J$|^SK$/'; - - $dxcc_exceptions = $this->db->select('`entity`, `adif`, `cqz`,`cont`,`long`,`lat`') - ->where('`call`', $call) - ->where('(start <= ', $date) - ->or_where('start is null)', NULL, false) - ->where('(end >= ', $date) - ->or_where('end is null)', NULL, false) - ->get('dxcc_exceptions'); - if ($dxcc_exceptions->num_rows() > 0) { - $row = $dxcc_exceptions->row_array(); - return $row; - } else { - - if (preg_match('/(^KG4)[A-Z09]{3}/', $call)) { // KG4/ and KG4 5 char calls are Guantanamo Bay. If 4 or 6 char, it is USA - $call = "K"; - } elseif (preg_match('/(^OH\/)|(\/OH[1-9]?$)/', $call)) { # non-Aland prefix! - $call = "OH"; # make callsign OH = finland - } elseif (preg_match('/(^CX\/)|(\/CX[1-9]?$)/', $call)) { # non-Antarctica prefix! - $call = "CX"; # make callsign CX = Uruguay - } elseif (preg_match('/(^3D2R)|(^3D2.+\/R)/', $call)) { # seems to be from Rotuma - $call = "3D2/R"; # will match with Rotuma - } elseif (preg_match('/^3D2C/', $call)) { # seems to be from Conway Reef - $call = "3D2/C"; # will match with Conway - } elseif (preg_match('/(^LZ\/)|(\/LZ[1-9]?$)/', $call)) { # LZ/ is LZ0 by DXCC but this is VP8h - $call = "LZ"; - } elseif (preg_match('/(^KG4)[A-Z09]{2}/', $call)) { - $call = "KG4"; - } elseif (preg_match('/(^KG4)[A-Z09]{1}/', $call)) { - $call = "K"; - } elseif (preg_match('/\w\/\w/', $call)) { - if (preg_match_all('/^((\d|[A-Z])+\/)?((\d|[A-Z]){3,})(\/(\d|[A-Z])+)?(\/(\d|[A-Z])+)?$/', $call, $matches)) { - $prefix = $matches[1][0]; - $callsign = $matches[3][0]; - $suffix = $matches[5][0]; - if ($prefix) { - $prefix = substr($prefix, 0, -1); # Remove the / at the end - } - if ($suffix) { - $suffix = substr($suffix, 1); # Remove the / at the beginning - }; - if (preg_match($csadditions, $suffix)) { - if ($prefix) { - $call = $prefix; - } else { - $call = $callsign; - } - } else { - $result = $this->wpx($call, 1); # use the wpx prefix instead - if ($result == '') { - $row['adif'] = 0; - $row['cont'] = ''; - $row['entity'] = '- NONE -'; - $row['ituz'] = 0; - $row['cqz'] = 0; - $row['long'] = '0'; - $row['lat'] = '0'; - return $row; - } else { - $call = $result . "AA"; - } - } - } - } - - $len = strlen($call); - $dxcc_array = []; - - // Fetch all candidates in one shot instead of looping - $dxcc_result = $this->db->query("SELECT `dxcc_prefixes`.`record`, `dxcc_prefixes`.`call`, `dxcc_prefixes`.`entity`, `dxcc_prefixes`.`adif`, `dxcc_prefixes`.`cqz`, `dxcc_entities`.`ituz`, `dxcc_prefixes`.`cont`, `dxcc_prefixes`.`long`, `dxcc_prefixes`.`lat`, `dxcc_prefixes`.`start`, `dxcc_prefixes`.`end` - FROM `dxcc_prefixes` - LEFT JOIN `dxcc_entities` ON `dxcc_entities`.`adif` = `dxcc_prefixes`.`adif` - WHERE ? like concat(`call`,'%') - and `dxcc_prefixes`.`call` like ? - AND (`dxcc_prefixes`.`start` <= ? OR `dxcc_prefixes`.`start` is null) - AND (`dxcc_prefixes`.`end` >= ? OR `dxcc_prefixes`.`end` is null) order by length(`call`) desc limit 1", array($call, substr($call, 0, 1) . '%', $date, $date)); - - foreach ($dxcc_result->result_array() as $row) { - $dxcc_array[$row['call']] = $row; - } - - // query the table, removing a character from the right until a match - for ($i = $len; $i > 0; $i--) { - //printf("searching for %s\n", substr($call, 0, $i)); - if (array_key_exists(substr($call, 0, $i), $dxcc_array)) { - $row = $dxcc_array[substr($call, 0, $i)]; - // $row = $dxcc_result->row_array(); - return $row; - } - } - } - - return array( - 'adif' => 0, - 'cqz' => 0, - 'ituz' => 0, - 'long' => '', - 'lat' => '', - 'entity' => 'None', - ); - } - - function wpx($testcall, $i) { - $prefix = ''; - $a = ''; - $b = ''; - $c = ''; - - $lidadditions = '/^QRP$|^LGT$/'; - $csadditions = '/^X$|^D$|^T$|^P$|^R$|^B$|^A$|^M$|^LH$|^L$|^J$|^SK$/'; - $noneadditions = '/^MM$|^AM$/'; - - # First check if the call is in the proper format, A/B/C where A and C - # are optional (prefix of guest country and P, MM, AM etc) and B is the - # callsign. Only letters, figures and "/" is accepted, no further check if the - # callsign "makes sense". - # 23.Apr.06: Added another "/X" to the regex, for calls like RV0AL/0/P - # as used by RDA-DXpeditions.... - - if (preg_match_all('/^((\d|[A-Z])+\/)?((\d|[A-Z]){3,})(\/(\d|[A-Z])+)?(\/(\d|[A-Z])+)?$/', $testcall, $matches)) { - - # Now $1 holds A (incl /), $3 holds the callsign B and $5 has C - # We save them to $a, $b and $c respectively to ensure they won't get - # lost in further Regex evaluations. - $a = $matches[1][0]; - $b = $matches[3][0]; - $c = $matches[5][0]; - - if ($a) { - $a = substr($a, 0, -1); # Remove the / at the end - } - if ($c) { - $c = substr($c, 1); # Remove the / at the beginning - }; - - # In some cases when there is no part A but B and C, and C is longer than 2 - # letters, it happens that $a and $b get the values that $b and $c should - # have. This often happens with liddish callsign-additions like /QRP and - # /LGT, but also with calls like DJ1YFK/KP5. ~/.yfklog has a line called - # "lidadditions", which has QRP and LGT as defaults. This sorts out half of - # the problem, but not calls like DJ1YFK/KH5. This is tested in a second - # try: $a looks like a call (.\d[A-Z]) and $b doesn't (.\d), they are - # swapped. This still does not properly handle calls like DJ1YFK/KH7K where - # only the OP's experience says that it's DJ1YFK on KH7K. - if (!$c && $a && $b) { # $a and $b exist, no $c - if (preg_match($lidadditions, $b)) { # check if $b is a lid-addition - $b = $a; - $a = null; # $a goes to $b, delete lid-add - } elseif ((preg_match('/\d[A-Z]+$/', $a)) && (preg_match('/\d$/', $b) || preg_match('/^[A-Z]\d[A-Z]$/', $b))) { # check for call in $a - $temp = $b; - $b = $a; - $a = $temp; - } - } - - # *** Added later *** The check didn't make sure that the callsign - # contains a letter. there are letter-only callsigns like RAEM, but not - # figure-only calls. - - if (preg_match('/^[0-9]+$/', $b)) { # Callsign only consists of numbers. Bad! - return null; # exit, undef - } - - # Depending on these values we have to determine the prefix. - # Following cases are possible: - # - # 1. $a and $c undef --> only callsign, subcases - # 1.1 $b contains a number -> everything from start to number - # 1.2 $b contains no number -> first two letters plus 0 - # 2. $a undef, subcases: - # 2.1 $c is only a number -> $a with changed number - # 2.2 $c is /P,/M,/MM,/AM -> 1. - # 2.3 $c is something else and will be interpreted as a Prefix - # 3. $a is defined, will be taken as PFX, regardless of $c - - if (($a == null) && ($c == null)) { # Case 1 - if (preg_match('/\d/', $b)) { # Case 1.1, contains number - preg_match('/(.+\d)[A-Z]*/', $b, $matches); # Prefix is all but the last - $prefix = $matches[1]; # Letters - } else { # Case 1.2, no number - $prefix = substr($b, 0, 2) . "0"; # first two + 0 - } - } elseif (($a == null) && (isset($c))) { # Case 2, CALL/X - if (preg_match('/^(\d)/', $c)) { # Case 2.1, number - preg_match('/(.+\d)[A-Z]*/', $b, $matches); # regular Prefix in $1 - # Here we need to find out how many digits there are in the - # prefix, because for example A45XR/0 is A40. If there are 2 - # numbers, the first is not deleted. If course in exotic cases - # like N66A/7 -> N7 this brings the wrong result of N67, but I - # think that's rather irrelevant cos such calls rarely appear - # and if they do, it's very unlikely for them to have a number - # attached. You can still edit it by hand anyway.. - if (preg_match('/^([A-Z]\d)\d$/', $matches[1])) { # e.g. A45 $c = 0 - $prefix = $matches[1] . $c; # -> A40 - } else { # Otherwise cut all numbers - preg_match('/(.*[A-Z])\d+/', $matches[1], $match); # Prefix w/o number in $1 - $prefix = $match[1] . $c; # Add attached number - } - } elseif (preg_match($csadditions, $c)) { - preg_match('/(.+\d)[A-Z]*/', $b, $matches); # Known attachment -> like Case 1.1 - $prefix = $matches[1]; - } elseif (preg_match($noneadditions, $c)) { - return ''; - } elseif (preg_match('/^\d\d+$/', $c)) { # more than 2 numbers -> ignore - preg_match('/(.+\d)[A-Z]* /', $b, $matches); # see above - $prefix = $matches[1][0]; - } else { # Must be a Prefix! - if (preg_match('/\d$/', $c)) { # ends in number -> good prefix - $prefix = $c; - } else { # Add Zero at the end - $prefix = $c . "0"; - } - } - } elseif (($a) && (preg_match($noneadditions, $c))) { # Case 2.1, X/CALL/X ie TF/DL2NWK/MM - DXCC none - return ''; - } elseif ($a) { - # $a contains the prefix we want - if (preg_match('/\d$/', $a)) { # ends in number -> good prefix - $prefix = $a; - } else { # add zero if no number - $prefix = $a . "0"; - } - } - # In very rare cases (right now I can only think of KH5K and KH7K and FRxG/T - # etc), the prefix is wrong, for example KH5K/DJ1YFK would be KH5K0. In this - # case, the superfluous part will be cropped. Since this, however, changes the - # DXCC of the prefix, this will NOT happen when invoked from with an - # extra parameter $_[1]; this will happen when invoking it from &dxcc. - - if (preg_match('/(\w+\d)[A-Z]+\d/', $prefix, $matches) && $i == null) { - $prefix = $matches[1][0]; - } - return $prefix; - } else { - return ''; - } - } - public function get_entity($dxcc) { $sql = "SELECT name, cqz, lat, `long` FROM dxcc_entities WHERE adif = ?"; $query = $this->db->query($sql, $dxcc); From fb16184181c823af457a8315e9d3dcf47130fc83 Mon Sep 17 00:00:00 2001 From: Andreas Kristiansen <6977712+AndreasK79@users.noreply.github.com> Date: Thu, 22 Jan 2026 12:56:33 +0100 Subject: [PATCH 02/11] Fix date --- application/libraries/DxclusterCache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/DxclusterCache.php b/application/libraries/DxclusterCache.php index ea1e07654..b8d2b6a08 100644 --- a/application/libraries/DxclusterCache.php +++ b/application/libraries/DxclusterCache.php @@ -80,7 +80,7 @@ class DxclusterCache { $this->deleteFile($this->getWorkedCallKey($logbook_key, $callsign)); // Look up DXCC and continent from callsign - $dxccobj = new Dxcc($date); + $dxccobj = new Dxcc(null); $dxcc_info = $dxccobj->dxcc_lookup($callsign, date('Y-m-d')); if (!empty($dxcc_info['adif'])) { From 1e9fba98783a2db5a653b1ee741b703ed5d2f8ea Mon Sep 17 00:00:00 2001 From: Andreas Kristiansen <6977712+AndreasK79@users.noreply.github.com> Date: Thu, 22 Jan 2026 16:03:48 +0100 Subject: [PATCH 03/11] Updated comment --- src/Dxcc/Dxcc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dxcc/Dxcc.php b/src/Dxcc/Dxcc.php index 7dff41096..2312e326e 100644 --- a/src/Dxcc/Dxcc.php +++ b/src/Dxcc/Dxcc.php @@ -337,7 +337,7 @@ class Dxcc { } /* - * Read cty.dat from AD1C + * Read DXCC data from the database */ function read_data($date = null) { $CI = &get_instance(); From d5fc310ad155bf08e0ba6ff8e8cd7d3eced2be4f Mon Sep 17 00:00:00 2001 From: Andreas Kristiansen <6977712+AndreasK79@users.noreply.github.com> Date: Sat, 24 Jan 2026 08:31:27 +0100 Subject: [PATCH 04/11] Added another layer for 1 char suffixes --- application/controllers/Calltester.php | 30 +++++++++++++++++++++++++- src/Dxcc/Dxcc.php | 14 +++++++++--- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/application/controllers/Calltester.php b/application/controllers/Calltester.php index 09fc100d2..c28fc77e1 100644 --- a/application/controllers/Calltester.php +++ b/application/controllers/Calltester.php @@ -794,6 +794,34 @@ class Calltester extends CI_Controller { 'Date' => $date = date('Y-m-d', time()) ); + $testarray[] = array( + 'Callsign' => 'VP8ADR/40', + 'Country' => 'Falkland Islands', + 'Adif' => 141, + 'Date' => $date = date('Y-m-d', time()) + ); + + $testarray[] = array( + 'Callsign' => 'VP8ADR/400', + 'Country' => 'Falkland Islands', + 'Adif' => 141, + 'Date' => $date = date('Y-m-d', time()) + ); + + $testarray[] = array( + 'Callsign' => 'LU7CC/E', + 'Country' => 'Argentina', + 'Adif' => 100, + 'Date' => $date = date('Y-m-d', time()) + ); + + $testarray[] = array( + 'Callsign' => 'FR/F6KDF/T', + 'Country' => 'Tromelin Island', + 'Adif' => 276, + 'Date' => '1999-08-04' + ); + set_time_limit(3600); // Starting clock time in seconds @@ -806,7 +834,7 @@ class Calltester extends CI_Controller { $dxccobj = new Dxcc(null); foreach ($testarray as $call) { - $i++; + $i++; $dxcc = $dxccobj->dxcc_lookup($call['Callsign'], $call['Date']); $dxcc['adif'] = (isset($dxcc['adif'])) ? $dxcc['adif'] : 0; diff --git a/src/Dxcc/Dxcc.php b/src/Dxcc/Dxcc.php index 2312e326e..a0a034621 100644 --- a/src/Dxcc/Dxcc.php +++ b/src/Dxcc/Dxcc.php @@ -195,6 +195,14 @@ class Dxcc { return null; # exit, undef } + if (preg_match('/^[0-9]{2,}$/', $c)) { # If suffix consists of two or more digits -> ignore suffix, To catch callsigns like VP8ADR/40 + $c = null; + } + + if (preg_match('/^[A-Z]{1}$/', ($c ?? ''))) { # If suffix consists of exactly one letter -> ignore suffix, To catch callsigns like LU7CC/E + $c = null; + } + # Depending on these values we have to determine the prefix. # Following cases are possible: # @@ -280,8 +288,8 @@ class Dxcc { $prefix = $matches[1]; } elseif (preg_match($this->noneadditions, $c)) { return ''; - } elseif (preg_match('/^\d\d+$/', $c)) { # more than 2 numbers -> ignore - if (!preg_match('/(.+\d)[A-Z]* /', $b, $matches)) { + } elseif (preg_match('/^\d\d+$/', $c)) { # more than 2 numbers -> ignore + if (!preg_match('/(.+\d)[A-Z]*/', $b, $matches)) { $this->logError('preg_match failed for multi-digit case', [ 'testcall' => $testcall, 'b' => $b, @@ -303,7 +311,7 @@ class Dxcc { $prefix = $c . "0"; } } - } elseif (($a) && (preg_match($this->noneadditions, $c))) { # Case 2.1, X/CALL/X ie TF/DL2NWK/MM - DXCC none + } elseif (($a) && (preg_match($this->noneadditions, ($c ?? '')))) { # Case 2.1, X/CALL/X ie TF/DL2NWK/MM - DXCC none return ''; } elseif ($a) { # $a contains the prefix we want From 175cb5dcee09d3ac671adfdfb424771e82874fd5 Mon Sep 17 00:00:00 2001 From: Andreas Kristiansen <6977712+AndreasK79@users.noreply.github.com> Date: Sun, 25 Jan 2026 11:15:05 +0100 Subject: [PATCH 05/11] Init dxcc class only once during import --- application/models/Logbook_model.php | 15 ++++++++++----- src/Dxcc/Dxcc.php | 10 +++++++++- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index b0f82066c..935d6e1f5 100644 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -8,6 +8,7 @@ class Logbook_model extends CI_Model { private $station_result = []; private $spot_status_cache = []; // In-memory cache for DX cluster spot statuses + private $dxcc_object; public function __construct() { $this->oop_populate_modes(); @@ -194,7 +195,7 @@ class Logbook_model extends CI_Model { $this->input->post('continent') == ""); if ($needs_dxcc_lookup) { - $dxccobj = new Dxcc($date); + $dxccobj = new Dxcc(null); $dxcc = $dxccobj->dxcc_lookup(strtoupper(trim($callsign)), $datetime); } @@ -4913,13 +4914,17 @@ class Logbook_model extends CI_Model { $entity = $this->get_entity($record['dxcc']); $dxcc = array($record['dxcc'] ?? '', $entity['name'] ?? ''); } else { - $dxccobj = new Dxcc(null); - $dxcclookupresult = $dxccobj->dxcc_lookup($record['call'], $time_off); + if ($this->dxcc_object == null) { + $this->dxcc_object = new Dxcc(null); + } + $dxcclookupresult = $this->dxcc_object->dxcc_lookup($record['call'], $time_off); $dxcc = array($dxcclookupresult['adif'], $dxcclookupresult['entity'], $dxcclookupresult['cqz'], $dxcclookupresult['cont']); } } else { - $dxccobj = new Dxcc(null); - $dxcclookupresult = $dxccobj->dxcc_lookup($record['call'], $time_off); + if ($this->dxcc_object == null) { + $this->dxcc_object = new Dxcc(null); + } + $dxcclookupresult = $this->dxcc_object->dxcc_lookup($record['call'], $time_off); $dxcc = array($dxcclookupresult['adif'], $dxcclookupresult['entity'], $dxcclookupresult['cqz'], $dxcclookupresult['cont']); } } else { diff --git a/src/Dxcc/Dxcc.php b/src/Dxcc/Dxcc.php index a0a034621..139c866db 100644 --- a/src/Dxcc/Dxcc.php +++ b/src/Dxcc/Dxcc.php @@ -92,6 +92,7 @@ class Dxcc { $row['cqz'] = 0; $row['long'] = '0'; $row['lat'] = '0'; + $row['cont'] = null; return $row; } else { $call = $result . "AA"; @@ -127,7 +128,14 @@ class Dxcc { } } - return array("Not Found", "Not Found"); + return array( + 'adif' => 0, + 'entity' => '- NONE ', + 'cqz' => '0', + 'long' => '0', + 'lat' => '0', + 'cont' => null + ); } function wpx($testcall, $i) { From 8fa76d2ff1adbb32eaa7f7491c73e70a483afb5b Mon Sep 17 00:00:00 2001 From: Andreas Kristiansen <6977712+AndreasK79@users.noreply.github.com> Date: Sun, 25 Jan 2026 11:35:15 +0100 Subject: [PATCH 06/11] Fix date for dxcc id --- application/models/Logbook_model.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index 935d6e1f5..993ab8e82 100644 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -4917,14 +4917,14 @@ class Logbook_model extends CI_Model { if ($this->dxcc_object == null) { $this->dxcc_object = new Dxcc(null); } - $dxcclookupresult = $this->dxcc_object->dxcc_lookup($record['call'], $time_off); + $dxcclookupresult = $this->dxcc_object->dxcc_lookup($record['call'], date('Y-m-d', strtotime($record['qso_date']))); $dxcc = array($dxcclookupresult['adif'], $dxcclookupresult['entity'], $dxcclookupresult['cqz'], $dxcclookupresult['cont']); } } else { if ($this->dxcc_object == null) { $this->dxcc_object = new Dxcc(null); } - $dxcclookupresult = $this->dxcc_object->dxcc_lookup($record['call'], $time_off); + $dxcclookupresult = $this->dxcc_object->dxcc_lookup($record['call'], date('Y-m-d', strtotime($record['qso_date']))); $dxcc = array($dxcclookupresult['adif'], $dxcclookupresult['entity'], $dxcclookupresult['cqz'], $dxcclookupresult['cont']); } } else { From 19675bae84161bf99a9facdfe42c3868b8d2c0f7 Mon Sep 17 00:00:00 2001 From: Andreas Kristiansen <6977712+AndreasK79@users.noreply.github.com> Date: Sun, 25 Jan 2026 17:15:36 +0100 Subject: [PATCH 07/11] Fix for A45/0 callsigns --- application/controllers/Calltester.php | 10 +++++++++- src/Dxcc/Dxcc.php | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/application/controllers/Calltester.php b/application/controllers/Calltester.php index c28fc77e1..8f42d2431 100644 --- a/application/controllers/Calltester.php +++ b/application/controllers/Calltester.php @@ -822,9 +822,16 @@ class Calltester extends CI_Controller { 'Date' => '1999-08-04' ); + $testarray[] = array( + 'Callsign' => 'A6050Y/5', + 'Country' => 'United Arab Emirates', + 'Adif' => 391, + 'Date' => $date = date('Y-m-d', time()) + ); set_time_limit(3600); // Starting clock time in seconds + $start_time = microtime(true); $result = array(); @@ -835,6 +842,7 @@ class Calltester extends CI_Controller { foreach ($testarray as $call) { $i++; + $dxcc = $dxccobj->dxcc_lookup($call['Callsign'], $call['Date']); $dxcc['adif'] = (isset($dxcc['adif'])) ? $dxcc['adif'] : 0; @@ -1062,7 +1070,7 @@ class Calltester extends CI_Controller { # think that's rather irrelevant cos such calls rarely appear # and if they do, it's very unlikely for them to have a number # attached. You can still edit it by hand anyway.. - if (preg_match('/^([A-Z]\d)\d$/', $matches[1])) { # e.g. A45 $c = 0 + if (preg_match('/^([A-Z]\d{2,})$/', $matches[1])) { # e.g. A45 $c = 0 $prefix = $matches[1] . $c; # -> A40 } else { # Otherwise cut all numbers preg_match('/(.*[A-Z])\d+/', $matches[1], $match); # Prefix w/o number in $1 diff --git a/src/Dxcc/Dxcc.php b/src/Dxcc/Dxcc.php index 139c866db..602d16e10 100644 --- a/src/Dxcc/Dxcc.php +++ b/src/Dxcc/Dxcc.php @@ -269,7 +269,7 @@ class Dxcc { ]); return ''; } - if (preg_match('/^([A-Z]\d)\d$/', $matches[1])) { # e.g. A45 $c = 0 + if (preg_match('/^([A-Z]\d{2,})$/', $matches[1])) { # e.g. A45 $c = 0 $prefix = $matches[1] . $c; # -> A40 } else { # Otherwise cut all numbers if (!preg_match('/(.*[A-Z])\d+/', $matches[1], $match)) { From c5fbb9da35ff04eeead291b51b1d16c58577ef5c Mon Sep 17 00:00:00 2001 From: Andreas Kristiansen <6977712+AndreasK79@users.noreply.github.com> Date: Sun, 25 Jan 2026 17:46:16 +0100 Subject: [PATCH 08/11] Add more callsigns to tester --- application/controllers/Calltester.php | 118 +++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/application/controllers/Calltester.php b/application/controllers/Calltester.php index 8f42d2431..b250f8411 100644 --- a/application/controllers/Calltester.php +++ b/application/controllers/Calltester.php @@ -828,6 +828,124 @@ class Calltester extends CI_Controller { 'Adif' => 391, 'Date' => $date = date('Y-m-d', time()) ); + + $testarray[] = array( + 'Callsign' => '9H5G/C6A', + 'Country' => 'Bahamas', + 'Adif' => 60, + 'Date' => $date = date('Y-m-d', time()) + ); + + $testarray[] = array( + 'Callsign' => 'A45XR/0', + 'Country' => 'Oman', + 'Adif' => 370, + 'Date' => $date = date('Y-m-d', time()) + ); + + $testarray[] = array( + 'Callsign' => 'RAEM', + 'Country' => 'Asiatic Russia', + 'Adif' => 54, + 'Date' => $date = date('Y-m-d', time()) + ); + + $testarray[] = array( + 'Callsign' => 'DJ1YFK/VE1', + 'Country' => 'Canada', + 'Adif' => 1, + 'Date' => $date = date('Y-m-d', time()) + ); + + $testarray[] = array( + 'Callsign' => 'HD1QRC90', + 'Country' => 'Ecuador', + 'Adif' => 120, + 'Date' => $date = date('Y-m-d', time()) + ); + + $testarray[] = array( + 'Callsign' => '3D2RH', + 'Country' => 'Fiji Islands', + 'Adif' => 176, + 'Date' => $date = date('Y-m-d', time()) + ); + + $testarray[] = array( + 'Callsign' => '3D2CB', + 'Country' => 'Fiji Islands', + 'Adif' => 176, + 'Date' => $date = date('Y-m-d', time()) + ); + + $testarray[] = array( + 'Callsign' => '3D2RW', + 'Country' => 'Fiji Islands', + 'Adif' => 176, + 'Date' => $date = date('Y-m-d', time()) + ); + + $testarray[] = array( + 'Callsign' => 'LU1ZC', + 'Country' => 'South Shetland Islands', + 'Adif' => 241, + 'Date' => $date = date('Y-m-d', time()) + ); + + $testarray[] = array( + 'Callsign' => 'RI0POL', + 'Country' => 'Asiatic Russia', + 'Adif' => 54, + 'Date' => $date = date('Y-m-d', time()) + ); + + $testarray[] = array( + 'Callsign' => 'PJ6A', + 'Country' => 'Saba & St. Eustatius', + 'Adif' => 519, + 'Date' => $date = date('Y-m-d', time()) + ); + + $testarray[] = array( + 'Callsign' => 'PJ4D', + 'Country' => 'Bonaire', + 'Adif' => 520, + 'Date' => $date = date('Y-m-d', time()) + ); + + $testarray[] = array( + 'Callsign' => '4X50CZ/SK', + 'Country' => 'Israel', + 'Adif' => 336, + 'Date' => $date = date('Y-m-d', time()) + ); + $testarray[] = array( + 'Callsign' => 'RK3BY/0', + 'Country' => 'Asiatic Russia', + 'Adif' => 15, + 'Date' => $date = date('Y-m-d', time()) + ); + + $testarray[] = array( + 'Callsign' => 'IU0KNS/ERA', + 'Country' => 'Italy', + 'Adif' => 248, + 'Date' => $date = date('Y-m-d', time()) + ); + + $testarray[] = array( + 'Callsign' => 'IU8BPS/AWD', + 'Country' => 'Italy', + 'Adif' => 248, + 'Date' => $date = date('Y-m-d', time()) + ); + + $testarray[] = array( + 'Callsign' => 'IK7XNF/GIRO', + 'Country' => 'Italy', + 'Adif' => 248, + 'Date' => $date = date('Y-m-d', time()) + ); set_time_limit(3600); // Starting clock time in seconds From 469778255c983aa175cad825d5ba3d1c0960e788 Mon Sep 17 00:00:00 2001 From: Andreas Kristiansen <6977712+AndreasK79@users.noreply.github.com> Date: Sun, 25 Jan 2026 21:17:46 +0100 Subject: [PATCH 09/11] Adjust callsign test list --- application/controllers/Calltester.php | 29 +++++++++++++------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/application/controllers/Calltester.php b/application/controllers/Calltester.php index b250f8411..e97b98141 100644 --- a/application/controllers/Calltester.php +++ b/application/controllers/Calltester.php @@ -864,7 +864,7 @@ class Calltester extends CI_Controller { 'Date' => $date = date('Y-m-d', time()) ); - $testarray[] = array( + $testarray[] = array( 'Callsign' => '3D2RH', 'Country' => 'Fiji Islands', 'Adif' => 176, @@ -885,19 +885,6 @@ class Calltester extends CI_Controller { 'Date' => $date = date('Y-m-d', time()) ); - $testarray[] = array( - 'Callsign' => 'LU1ZC', - 'Country' => 'South Shetland Islands', - 'Adif' => 241, - 'Date' => $date = date('Y-m-d', time()) - ); - - $testarray[] = array( - 'Callsign' => 'RI0POL', - 'Country' => 'Asiatic Russia', - 'Adif' => 54, - 'Date' => $date = date('Y-m-d', time()) - ); $testarray[] = array( 'Callsign' => 'PJ6A', @@ -946,6 +933,20 @@ class Calltester extends CI_Controller { 'Adif' => 248, 'Date' => $date = date('Y-m-d', time()) ); + + $testarray[] = array( + 'Callsign' => 'VJ5A', + 'Country' => 'Australia', + 'Adif' => 150, + 'Date' => $date = date('Y-m-d', time()) + ); + + $testarray[] = array( + 'Callsign' => 'VL2IG', + 'Country' => 'Australia', + 'Adif' => 150, + 'Date' => $date = date('Y-m-d', time()) + ); set_time_limit(3600); // Starting clock time in seconds From 75d998486a99e977689b61161671a0ca36dee62c Mon Sep 17 00:00:00 2001 From: Andreas Kristiansen <6977712+AndreasK79@users.noreply.github.com> Date: Mon, 26 Jan 2026 09:12:38 +0100 Subject: [PATCH 10/11] Ignore unknown 2 char prefixes --- application/controllers/Calltester.php | 33 ++++++++------------------ src/Dxcc/Dxcc.php | 8 ++++++- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/application/controllers/Calltester.php b/application/controllers/Calltester.php index e97b98141..c9ecaf663 100644 --- a/application/controllers/Calltester.php +++ b/application/controllers/Calltester.php @@ -864,28 +864,6 @@ class Calltester extends CI_Controller { 'Date' => $date = date('Y-m-d', time()) ); - $testarray[] = array( - 'Callsign' => '3D2RH', - 'Country' => 'Fiji Islands', - 'Adif' => 176, - 'Date' => $date = date('Y-m-d', time()) - ); - - $testarray[] = array( - 'Callsign' => '3D2CB', - 'Country' => 'Fiji Islands', - 'Adif' => 176, - 'Date' => $date = date('Y-m-d', time()) - ); - - $testarray[] = array( - 'Callsign' => '3D2RW', - 'Country' => 'Fiji Islands', - 'Adif' => 176, - 'Date' => $date = date('Y-m-d', time()) - ); - - $testarray[] = array( 'Callsign' => 'PJ6A', 'Country' => 'Saba & St. Eustatius', @@ -906,7 +884,8 @@ class Calltester extends CI_Controller { 'Adif' => 336, 'Date' => $date = date('Y-m-d', time()) ); - $testarray[] = array( + + $testarray[] = array( 'Callsign' => 'RK3BY/0', 'Country' => 'Asiatic Russia', 'Adif' => 15, @@ -947,6 +926,14 @@ class Calltester extends CI_Controller { 'Adif' => 150, 'Date' => $date = date('Y-m-d', time()) ); + + $testarray[] = array( + 'Callsign' => 'RU9CK/Z1', + 'Country' => 'Asiatic Russia', + 'Adif' => 15, + 'Date' => $date = date('Y-m-d', time()) + ); + set_time_limit(3600); // Starting clock time in seconds diff --git a/src/Dxcc/Dxcc.php b/src/Dxcc/Dxcc.php index 602d16e10..0aeb539fa 100644 --- a/src/Dxcc/Dxcc.php +++ b/src/Dxcc/Dxcc.php @@ -167,6 +167,12 @@ class Dxcc { $c = substr($c, 1); # Remove the / at the beginning }; + if (preg_match('/^([A-Z]\d|\d[A-Z])$/', ($c ?? ''))) { # If suffix consists of exactly 2 chars (letter+digit or digit+letter) - ignore if prefix isn't known at all + if (!array_key_exists($c, $this->dxcc)) { + $c = null; + } + } + # In some cases when there is no part A but B and C, and C is longer than 2 # letters, it happens that $a and $b get the values that $b and $c should # have. This often happens with liddish callsign-additions like /QRP and @@ -203,7 +209,7 @@ class Dxcc { return null; # exit, undef } - if (preg_match('/^[0-9]{2,}$/', $c)) { # If suffix consists of two or more digits -> ignore suffix, To catch callsigns like VP8ADR/40 + if (preg_match('/^[0-9]{2,}$/', $c ?? '')) { # If suffix consists of two or more digits -> ignore suffix, To catch callsigns like VP8ADR/40 $c = null; } From 6f01b34ee57862c996eec42e786daddef09e8713 Mon Sep 17 00:00:00 2001 From: Andreas Kristiansen <6977712+AndreasK79@users.noreply.github.com> Date: Mon, 26 Jan 2026 10:35:42 +0100 Subject: [PATCH 11/11] Revert my latest unknown prefix change --- application/controllers/Calltester.php | 7 ------- src/Dxcc/Dxcc.php | 6 ------ 2 files changed, 13 deletions(-) diff --git a/application/controllers/Calltester.php b/application/controllers/Calltester.php index c9ecaf663..f1088151e 100644 --- a/application/controllers/Calltester.php +++ b/application/controllers/Calltester.php @@ -927,13 +927,6 @@ class Calltester extends CI_Controller { 'Date' => $date = date('Y-m-d', time()) ); - $testarray[] = array( - 'Callsign' => 'RU9CK/Z1', - 'Country' => 'Asiatic Russia', - 'Adif' => 15, - 'Date' => $date = date('Y-m-d', time()) - ); - set_time_limit(3600); // Starting clock time in seconds diff --git a/src/Dxcc/Dxcc.php b/src/Dxcc/Dxcc.php index 0aeb539fa..fd26ca638 100644 --- a/src/Dxcc/Dxcc.php +++ b/src/Dxcc/Dxcc.php @@ -167,12 +167,6 @@ class Dxcc { $c = substr($c, 1); # Remove the / at the beginning }; - if (preg_match('/^([A-Z]\d|\d[A-Z])$/', ($c ?? ''))) { # If suffix consists of exactly 2 chars (letter+digit or digit+letter) - ignore if prefix isn't known at all - if (!array_key_exists($c, $this->dxcc)) { - $c = null; - } - } - # In some cases when there is no part A but B and C, and C is longer than 2 # letters, it happens that $a and $b get the values that $b and $c should # have. This often happens with liddish callsign-additions like /QRP and