diff --git a/application/controllers/Cabrillo.php b/application/controllers/Cabrillo.php index 972fd0f5e..1f745372f 100644 --- a/application/controllers/Cabrillo.php +++ b/application/controllers/Cabrillo.php @@ -188,19 +188,25 @@ class Cabrillo extends CI_Controller { //get flag about the presence of the serial number $serial_number_present = ($this->input->post('serial_number_present', true) == 1); + //get flag about the presence of the trx number + $trx_number_present = ($this->input->post('trx_number_present', true) == 1); + //parse the uploaded file - $parsed_cbr = $this->cbr_parser->parse_from_file('./uploads/'.$data['upload_data']['file_name'], $serial_number_present); + $parsed_cbr = $this->cbr_parser->parse_from_file('./uploads/'.$data['upload_data']['file_name'], $serial_number_present, $trx_number_present); - //return with error, reset upload filesize - if(count($parsed_cbr["QSOS"]) < 1) + //if parsing fails, return with error, reset upload filesize + if(isset($parsed_cbr['error'])) { - $data['error'] = __("Broken CBR file - no QSO data or incomplete header found."); + //get error message from parser + $data['error'] = $parsed_cbr['error']; + //reset upload filesize $data['max_upload'] = ini_get('upload_max_filesize'); //delete uploaded file unlink('./uploads/' . $data['upload_data']['file_name']); + //return view $this->load->view('interface_assets/header', $data); $this->load->view('adif/import', $data); $this->load->view('interface_assets/footer'); @@ -263,23 +269,25 @@ class Cabrillo extends CI_Controller { $stxstring = null; $srxstring = null; - //process all sent exchanges + //process all sent exchanges, handle those that are shorter than maximum gracefully for ($i=1; $i <= $sent_exchange_count; $i++) { - if($stxstring == null) - { - $stxstring = $qso["SENT_EXCH_" . $i]; - }else{ - $stxstring = $stxstring . ' ' . $qso["SENT_EXCH_" . $i]; + if(isset($qso["SENT_EXCH_" . $i])){ + if($stxstring == null){ + $stxstring = $qso["SENT_EXCH_" . $i]; + }else{ + $stxstring = $stxstring . ' ' . $qso["SENT_EXCH_" . $i]; + } } } - //process all sent exchanges + //process all sent exchanges, handle those that are shorter than maximum gracefully for ($i=1; $i <= $rcvd_exchange_count; $i++) { - if($srxstring == null) - { - $srxstring = $qso["RCVD_EXCH_" . $i]; - }else{ - $srxstring = $srxstring . ' ' . $qso["RCVD_EXCH_" . $i]; + if(isset($qso["RCVD_EXCH_" . $i])){ + if($srxstring == null){ + $srxstring = $qso["RCVD_EXCH_" . $i]; + }else{ + $srxstring = $srxstring . ' ' . $qso["RCVD_EXCH_" . $i]; + } } } diff --git a/application/libraries/Cbr_parser.php b/application/libraries/Cbr_parser.php index 73c35a0db..5c39d4458 100644 --- a/application/libraries/Cbr_parser.php +++ b/application/libraries/Cbr_parser.php @@ -1,13 +1,13 @@ parse(mb_convert_encoding(file_get_contents($filename), "UTF-8"), $serial_number_present); + return $this->parse(mb_convert_encoding(file_get_contents($filename), "UTF-8"), $serial_number_present, $trx_number_present); } - public function parse(string $input, $serial_number_present = false) : array + public function parse(string $input, $serial_number_present = false, $trx_number_present = false) : array { //split the input into lines $lines = explode("\n", trim($input)); @@ -35,6 +35,11 @@ class CBR_Parser $qso_mode = false; } + //if we encounter a QTC, skip that line + if(strpos($line, 'QTC:') === 0){ + continue; + } + //if we encounter "END-OF-LOG", stop processing lines if (strpos($line, 'END-OF-LOG') === 0) { break; @@ -59,13 +64,38 @@ class CBR_Parser //split the line into the elements $qso_elements = preg_split('/\s+/', trim($line)); + //check occurances of signal rapport values + $counts = array_count_values($qso_elements); + $count59s = ($counts["59"] ?? 0) + ($counts["599"] ?? 0); + + //for those few contests who do not have signal exchange, synthesize one based on best efforts + if($count59s < 2 and isset($header['CALLSIGN'])){ + + //get own callsign from header + $own_callsign = $header['CALLSIGN']; + + //get position of own callsign + $index = array_search($own_callsign, $qso_elements); + + //add synthesized sent signal rapport after own callsign + if ($index !== false) { + array_splice($qso_elements, $index + 1, 0, "59"); + } + + //search for the next amateur radio callsign after my own and add another 59. Abort after first find + for ($i = $index + 1; $i < count($qso_elements); $i++) { + $value = $qso_elements[$i]; + if(preg_match('/(?=[A-Z0-9]*[A-Z])[A-Z0-9]{1,3}[0-9][A-Z0-9]{0,7}/', $value) === 1){ + array_splice($qso_elements, $i + 1, 0, "59"); + break; + } + } + } + //determine maximum qso field size - $max_qso_fields = max($max_qso_fields, count($qso_elements)); + $max_qso_fields = max($max_qso_fields ?? 0, count($qso_elements)); - //add qso elements to qso line array - array_push($qso_lines_raw, $qso_elements); - - //find all occurrences of "59" + //find all occurrences of "59" or "599" $indices_of_59 = []; foreach ($qso_elements as $index => $value) { if ($value === "59" or $value === "599") { @@ -73,6 +103,15 @@ class CBR_Parser } } + //abort further processing if we find a line without a valid signal report (59 or 599) + if(count($indices_of_59) < 1) { + + //return error result + $result = []; + $result['error'] = __("Broken CBR file - no valid exchange or callsigns found"); + return $result; + } + //find common indices position if ($common_59_indices === null) { //initialize common indices on the first iteration @@ -82,6 +121,9 @@ class CBR_Parser $common_59_indices = array_intersect($common_59_indices, $indices_of_59); } + //add qso elements to qso line array + array_push($qso_lines_raw, $qso_elements); + //skip to next line continue; } @@ -89,30 +131,20 @@ class CBR_Parser //abort further processing if no qso lines were found, return header only if(count($qso_lines_raw) < 1) { - $result = []; - $result["HEADER"] = $header; - $result["QSOS"] = []; - $result["SENT_59_POS"] = 0; - $result["RCVD_59_POS"] = 0; - $result["SENT_EXCHANGE_COUNT"] = 0; - $result["RCVD_EXCHANGE_COUNT"] = 0; - //return result + //return error result + $result = []; + $result['error'] = __("Broken CBR file - no QSO data found."); return $result; } //abort if basic things (Callsign and Contest ID) are not included in the header $header_fields = array_keys($header); if(!in_array('CALLSIGN', $header_fields) or !in_array('CONTEST', $header_fields)){ + + //return error result $result = []; - $result["HEADER"] = $header; - $result["QSOS"] = []; - $result["SENT_59_POS"] = 0; - $result["RCVD_59_POS"] = 0; - $result["SENT_EXCHANGE_COUNT"] = 0; - $result["RCVD_EXCHANGE_COUNT"] = 0; - - //return blank result + $result['error'] = __("Broken CBR file - incomplete header found."); return $result; } @@ -120,6 +152,15 @@ class CBR_Parser $sent_59_pos = min($common_59_indices); $rcvd_59_pos = max($common_59_indices); + //abort if position of sent and received signal exchange is identical + if($sent_59_pos == $rcvd_59_pos){ + + //return error result + $result = []; + $result['error'] = __("Broken CBR file - no valid exchange or callsigns found"); + return $result; + } + //get codeigniter instance $CI = &get_instance(); @@ -171,7 +212,7 @@ class CBR_Parser //get all remaining received exchanges $exchange_nr = 1; $startindex = ($rcvd_59_pos + ($serial_number_present ? 2 : 1)); - $endindex = (count($line)); + $endindex = count($line) - ($trx_number_present ? 1 : 0); for ($i = $startindex; $i < $endindex; $i++) { $qso_line["RCVD_EXCH_" . $exchange_nr] = $line[$i]; $exchange_nr++; @@ -200,12 +241,15 @@ class CBR_Parser case '144': $band = '2m'; break; + case '220': case '222': $band = '1.25m'; break; + case '430': case '432': $band = '70cm'; break; + case '900': case '902': $band = '33cm'; break; @@ -292,7 +336,7 @@ class CBR_Parser $result["SENT_59_POS"] = $sent_59_pos; $result["RCVD_59_POS"] = $rcvd_59_pos; $result["SENT_EXCHANGE_COUNT"] = $rcvd_59_pos - $sent_59_pos - ($serial_number_present ? 3 : 2); - $result["RCVD_EXCHANGE_COUNT"] = $max_qso_fields - 1 - $rcvd_59_pos - ($serial_number_present ? 1 : 0); + $result["RCVD_EXCHANGE_COUNT"] = $max_qso_fields - 1 - $rcvd_59_pos - ($serial_number_present ? 1 : 0) - ($trx_number_present ? 1 : 0); //return result return $result; diff --git a/application/locale/bg_BG/LC_MESSAGES/messages.po b/application/locale/bg_BG/LC_MESSAGES/messages.po index 7010fcafe..a75b9771a 100644 --- a/application/locale/bg_BG/LC_MESSAGES/messages.po +++ b/application/locale/bg_BG/LC_MESSAGES/messages.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Report-Msgid-Bugs-To: translations@wavelog.org\n" -"POT-Creation-Date: 2025-11-25 06:28+0000\n" +"POT-Creation-Date: 2025-11-25 07:14+0000\n" "PO-Revision-Date: 2024-11-01 08:53+0000\n" "Last-Translator: Plamen Panteleev \n" "Language-Team: Bulgarian \n" "Language-Team: Bosnian \n" "Language-Team: Montenegrin \n" "Language-Team: Czech \n" "Language-Team: Greek \n" "Language-Team: Spanish \n" "Language-Team: Estonian \n" "Language-Team: Finnish \n" "Language-Team: Croatian \n" "Language-Team: Hungarian \n" "Language-Team: Armenian \n" "Language-Team: Italian \n" "Language-Team: Japanese \n" "Language-Team: Lithuanian \n" "Language-Team: Latvian \n" "Language-Team: Dutch \n" "Language-Team: Polish \n" "Language-Team: Portuguese (Portugal) \n" "Language-Team: Russian \n" "Language-Team: Albanian \n" "Language-Team: Serbian \n" "Language-Team: Turkish \n" "Language-Team: Chinese (Simplified Han script) ' . $contest['name'] . ''; } ?> +
+ + +
diff --git a/assets/lang_src/messages.pot b/assets/lang_src/messages.pot index ccdeabf73..3566e4e40 100644 --- a/assets/lang_src/messages.pot +++ b/assets/lang_src/messages.pot @@ -6,7 +6,7 @@ msgid "" msgstr "" "Report-Msgid-Bugs-To: translations@wavelog.org\n" -"POT-Creation-Date: 2025-11-25 06:28+0000\n" +"POT-Creation-Date: 2025-11-25 07:14+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -637,18 +637,14 @@ msgstr "" msgid "Cabrillo Import" msgstr "" -#: application/controllers/Cabrillo.php:197 -msgid "Broken CBR file - no QSO data or incomplete header found." -msgstr "" - -#: application/controllers/Cabrillo.php:243 +#: application/controllers/Cabrillo.php:249 #, php-format msgid "" "QSO %d not found or more than 1 QSO found that match the criteria of the CBR " "file. Skipping as a safety measure." msgstr "" -#: application/controllers/Cabrillo.php:301 +#: application/controllers/Cabrillo.php:309 msgid "CBR Data Imported" msgstr "" @@ -3170,6 +3166,19 @@ msgstr "" msgid "QRZCQ Error" msgstr "" +#: application/libraries/Cbr_parser.php:111 +#: application/libraries/Cbr_parser.php:160 +msgid "Broken CBR file - no valid exchange or callsigns found" +msgstr "" + +#: application/libraries/Cbr_parser.php:137 +msgid "Broken CBR file - no QSO data found." +msgstr "" + +#: application/libraries/Cbr_parser.php:147 +msgid "Broken CBR file - incomplete header found." +msgstr "" + #: application/libraries/Subdivisions.php:31 msgctxt "Division Name (States in various countries)." msgid "Province" @@ -4538,7 +4547,7 @@ msgid "Toggle all checkboxes" msgstr "" #: application/views/adif/import.php:221 application/views/adif/import.php:364 -#: application/views/adif/import.php:384 application/views/adif/import.php:422 +#: application/views/adif/import.php:384 application/views/adif/import.php:426 #: application/views/clublog/export.php:50 #: application/views/hrdlog/export.php:50 application/views/qrz/export.php:55 #: application/views/webadif/export.php:55 @@ -4698,17 +4707,23 @@ msgstr "" #: application/views/adif/import.php:415 msgid "" +"The CBR file contains a TRX number at the end of each QSO line (for multi-op " +"stations)" +msgstr "" + +#: application/views/adif/import.php:419 +msgid "" "A serial number is ALWAYS part of the exchange for both parties in this " "contest." msgstr "" -#: application/views/adif/import.php:417 +#: application/views/adif/import.php:421 msgid "" "If you or your partner only sometimes exchange serial numbers, please leave " "this unchecked." msgstr "" -#: application/views/adif/import.php:418 +#: application/views/adif/import.php:422 msgid "" "If unchecked, this will erase the default serial number that (for example) " "N1MM+ produces. If checked, it will correct the serial number if necessary."