| 78 | // --------------------------------------------------------------------------- |
| 79 | |
| 80 | QVector<QsoRecord> AdifParser::parse(const QByteArray& data) |
| 81 | { |
| 82 | QVector<QsoRecord> records; |
| 83 | const QString text = QString::fromUtf8(data); |
| 84 | |
| 85 | // Skip ADIF header section (everything before <EOH>) |
| 86 | int bodyStart = 0; |
| 87 | int eoh = text.indexOf("<EOH>", 0, Qt::CaseInsensitive); |
| 88 | if (eoh != -1) |
| 89 | bodyStart = eoh + 5; |
| 90 | |
| 91 | // Split on <EOR> record separators |
| 92 | static const QRegularExpression eorRe("<EOR>", QRegularExpression::CaseInsensitiveOption); |
| 93 | int pos = bodyStart; |
| 94 | QRegularExpressionMatchIterator it = eorRe.globalMatch(text, bodyStart); |
| 95 | |
| 96 | while (it.hasNext()) { |
| 97 | auto eorMatch = it.next(); |
| 98 | const QString block = text.mid(pos, eorMatch.capturedStart() - pos); |
| 99 | pos = eorMatch.capturedEnd(); |
| 100 | |
| 101 | if (block.trimmed().isEmpty()) continue; |
| 102 | |
| 103 | QsoRecord rec; |
| 104 | rec.callsign = extractField(block, "CALL").trimmed().toUpper(); |
| 105 | if (rec.callsign.isEmpty()) continue; |
| 106 | |
| 107 | // Band: prefer explicit <BAND> field, fall back to <FREQ> → freqToBand. |
| 108 | // Some loggers export bare numbers ("10", "20") instead of ADIF-standard |
| 109 | // labels ("10m", "20m") — normalise those here. |
| 110 | rec.band = extractField(block, "BAND").trimmed().toLower(); |
| 111 | if (!rec.band.isEmpty() && !rec.band.endsWith('m') && !rec.band.endsWith("cm")) { |
| 112 | static const QHash<QString,QString> bandMap = { |
| 113 | {"160","160m"},{"80","80m"},{"60","60m"},{"40","40m"}, |
| 114 | {"30","30m"},{"20","20m"},{"17","17m"},{"15","15m"}, |
| 115 | {"12","12m"},{"10","10m"},{"6","6m"},{"4","4m"}, |
| 116 | {"2","2m"},{"70","70cm"}, |
| 117 | }; |
| 118 | const QString mapped = bandMap.value(rec.band); |
| 119 | if (!mapped.isEmpty()) rec.band = mapped; |
| 120 | } |
| 121 | if (rec.band.isEmpty()) { |
| 122 | const QString freqStr = extractField(block, "FREQ").trimmed(); |
| 123 | if (!freqStr.isEmpty()) { |
| 124 | bool ok = false; |
| 125 | double mhz = freqStr.toDouble(&ok); |
| 126 | if (ok) rec.band = freqToBand(mhz); |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | // Mode |
| 131 | const QString mode = extractField(block, "MODE").trimmed(); |
| 132 | const QString submode = extractField(block, "SUBMODE").trimmed(); |
| 133 | rec.modeGroup = normaliseMode(mode, submode); |
| 134 | |
| 135 | records.append(rec); |
| 136 | } |
| 137 |
nothing calls this directly
no test coverage detected