mirror of
https://github.com/wavelog/wavelog.git
synced 2026-03-22 10:24:14 +00:00
343 lines
12 KiB
PHP
343 lines
12 KiB
PHP
<?php
|
|
class CBR_Parser
|
|
{
|
|
public function parse_from_file($filename, $serial_number_present = false, $trx_number_present = false) : array
|
|
{
|
|
//load file, call parser
|
|
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, $trx_number_present = false) : array
|
|
{
|
|
//split the input into lines
|
|
$lines = explode("\n", trim($input));
|
|
|
|
//initialize the result array
|
|
$qso_lines_raw = [];
|
|
$header = [];
|
|
|
|
//helper variable to determine common 59 element indices in QSO lines
|
|
$common_59_indices = null;
|
|
|
|
//flag to indicate processing mode
|
|
$qso_mode = false;
|
|
|
|
//helper variable to determine the maximum number of qso data fields
|
|
$max_qso_fields = 0;
|
|
|
|
//loop through each line
|
|
foreach ($lines as $line) {
|
|
|
|
//if we encounter "QSO" or "X-QSO" switch processing mode to QSO mode
|
|
if (strpos($line, 'QSO:') === 0 or strpos($line, 'X-QSO:') === 0) {
|
|
$qso_mode = true;
|
|
}else {
|
|
$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;
|
|
}
|
|
|
|
//process and collect header lines if qso mode is not set
|
|
if (!$qso_mode) {
|
|
|
|
//split the line into an array using ': ' as the delimiter
|
|
$parts = explode(': ', $line, 2);
|
|
|
|
//collect header information
|
|
$header[$parts[0]] = trim($parts[1]);
|
|
|
|
//skip to next line
|
|
continue;
|
|
}
|
|
|
|
//process and collect QSO lines if qso mode is set
|
|
if ($qso_mode) {
|
|
|
|
//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 ?? 0, count($qso_elements));
|
|
|
|
//find all occurrences of "59" or "599"
|
|
$indices_of_59 = [];
|
|
foreach ($qso_elements as $index => $value) {
|
|
if ($value === "59" or $value === "599") {
|
|
$indices_of_59[] = $index;
|
|
}
|
|
}
|
|
|
|
//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
|
|
$common_59_indices = $indices_of_59;
|
|
} else {
|
|
//intersect with current indices, preserving only common indices
|
|
$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;
|
|
}
|
|
}
|
|
|
|
//abort further processing if no qso lines were found, return header only
|
|
if(count($qso_lines_raw) < 1) {
|
|
|
|
//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['error'] = __("Broken CBR file - incomplete header found.");
|
|
return $result;
|
|
}
|
|
|
|
//get positions of 59s inside QSO lines
|
|
$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();
|
|
|
|
//load Frequency library
|
|
if(!$CI->load->is_loaded('Frequency')) {
|
|
$CI->load->library('Frequency');
|
|
}
|
|
|
|
//using 59 positions, remake qso_lines
|
|
$qso_lines = [];
|
|
|
|
//change all QSOs into associative arrays with meaningful keys
|
|
foreach ($qso_lines_raw as $line) {
|
|
|
|
$qso_line = [];
|
|
|
|
//get well defined fields
|
|
$qso_line["QSO_MARKER"] = $line[0];
|
|
$qso_line["FREQ"] = $line[1];
|
|
$qso_line["CBR_MODE"] = $line[2];
|
|
$qso_line["DATE"] = $line[3];
|
|
$qso_line["TIME"] = $line[4];
|
|
$qso_line["OWN_CALLSIGN"] = $line[5];
|
|
$qso_line["SENT_59"] = $line[$sent_59_pos];
|
|
|
|
//set serial if requested
|
|
if($serial_number_present) {
|
|
$qso_line["SENT_SERIAL"] = $line[$sent_59_pos + 1];
|
|
}
|
|
|
|
//get all remaining sent exchanges
|
|
$exchange_nr = 1;
|
|
$startindex = ($sent_59_pos + ($serial_number_present ? 2 : 1));
|
|
$endindex = ($rcvd_59_pos - 1);
|
|
for ($i = $startindex; $i < $endindex; $i++) {
|
|
$qso_line["SENT_EXCH_" . $exchange_nr] = $line[$i];
|
|
$exchange_nr++;
|
|
}
|
|
|
|
//get rest of the well defined fields
|
|
$qso_line["RCVD_CALLSIGN"] = $line[$rcvd_59_pos - 1];
|
|
$qso_line["RCVD_59"] = $line[$rcvd_59_pos];
|
|
|
|
//set serial if requested
|
|
if($serial_number_present) {
|
|
$qso_line["RCVD_SERIAL"] = $line[$rcvd_59_pos + 1];
|
|
}
|
|
|
|
//get all remaining received exchanges
|
|
$exchange_nr = 1;
|
|
$startindex = ($rcvd_59_pos + ($serial_number_present ? 2 : 1));
|
|
$endindex = $trx_number_present ? (count($line) - 1) : (count($line));
|
|
for ($i = $startindex; $i < $endindex; $i++) {
|
|
$qso_line["RCVD_EXCH_" . $exchange_nr] = $line[$i];
|
|
$exchange_nr++;
|
|
}
|
|
|
|
//end of data in CQR format
|
|
//enhance QSO data with additional fields
|
|
$band = "";
|
|
|
|
//convert frequency to integer if possible
|
|
if(is_numeric($qso_line["FREQ"])) {
|
|
$frequency = (int)$qso_line["FREQ"];
|
|
}else{
|
|
$frequency = null;
|
|
}
|
|
|
|
//convert CBR values to band where no real frequency is given.
|
|
//if frequency is given, consult the frequency library
|
|
switch ($qso_line["FREQ"]) {
|
|
case '50':
|
|
$band = '6m';
|
|
break;
|
|
case '70':
|
|
$band = '4m';
|
|
break;
|
|
case '144':
|
|
$band = '2m';
|
|
break;
|
|
case '222':
|
|
$band = '1.25m';
|
|
break;
|
|
case '432':
|
|
$band = '70cm';
|
|
break;
|
|
case '902':
|
|
$band = '33cm';
|
|
break;
|
|
case '1.2G':
|
|
$band = '23cm';
|
|
break;
|
|
case '2.3G':
|
|
$band = '13cm';
|
|
break;
|
|
case '3.4G':
|
|
$band = '9cm';
|
|
break;
|
|
case '5.7G':
|
|
$band = '6cm';
|
|
break;
|
|
case '10G':
|
|
$band = '3cm';
|
|
break;
|
|
case '24G':
|
|
$band = '1.25cm';
|
|
break;
|
|
case '47G':
|
|
$band = 'SAT';
|
|
break;
|
|
case '75G':
|
|
$band = 'SAT';
|
|
break;
|
|
case '122G':
|
|
$band = 'SAT';
|
|
break;
|
|
case '134G':
|
|
$band = 'SAT';
|
|
break;
|
|
case '241G':
|
|
$band = 'SAT';
|
|
break;
|
|
case 'LIGHT':
|
|
$band = 'SAT';
|
|
break;
|
|
default:
|
|
$band = $CI->frequency->GetBand($frequency * 1000);
|
|
break;
|
|
}
|
|
|
|
//set band data for QSO
|
|
$qso_line["BAND"] = $band;
|
|
|
|
//get Wavelog mode
|
|
$mode = "";
|
|
switch ($qso_line["CBR_MODE"]) {
|
|
case 'CW':
|
|
$mode = 'CW';
|
|
break;
|
|
case 'PH':
|
|
$mode = 'SSB';
|
|
break;
|
|
case 'FM':
|
|
$mode = 'FM';
|
|
break;
|
|
case 'RY':
|
|
$mode = 'RTTY';
|
|
break;
|
|
case 'DG':
|
|
//indeterminate Digimode
|
|
$mode = '';
|
|
break;
|
|
default:
|
|
//something is wrong with the CBR file
|
|
$mode = '';
|
|
break;
|
|
}
|
|
|
|
//set mode data for QSO
|
|
$qso_line["MODE"] = $mode;
|
|
|
|
//collect new associative array
|
|
array_push($qso_lines, $qso_line);
|
|
}
|
|
|
|
//construct result, including positions of 59s for further processing down the line
|
|
$result = [];
|
|
$result["HEADER"] = $header;
|
|
$result["QSOS"] = $qso_lines;
|
|
$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) - ($trx_number_present ? 1 : 0);
|
|
|
|
//return result
|
|
return $result;
|
|
}
|
|
}
|
|
?>
|