kopia lustrzana https://github.com/jamescoxon/dl-fldigi
361 wiersze
11 KiB
C++
361 wiersze
11 KiB
C++
#include <FL/Fl.H>
|
|
#include <FL/filename.H>
|
|
#include <FL/fl_ask.H>
|
|
|
|
#include <string>
|
|
#include <cstring>
|
|
#include <cstdlib>
|
|
#include <string>
|
|
|
|
#include "adif_io.h"
|
|
#include "config.h"
|
|
#include "configuration.h"
|
|
#include "lgbook.h"
|
|
#include "icons.h"
|
|
#include "gettext.h"
|
|
#include "debug.h"
|
|
#include "util.h"
|
|
#include "date.h"
|
|
|
|
#ifdef __WOE32__
|
|
static const char *szEOL = "\r\n";
|
|
#else
|
|
static const char *szEOL = "\n";
|
|
#endif
|
|
static const char *szEOR = "<EOR>";
|
|
|
|
// These ADIF fields define the QSO database
|
|
|
|
const char *fieldnames[] = {
|
|
"ADDRESS", "AGE", "ARRL_SECT", "BAND", "CALL", "CNTY", "COMMENT",
|
|
"CONT", "CONTEST_ID", "COUNTRY", "CQZ", "DXCC", "EXPORT", "FREQ",
|
|
"GRIDSQUARE", "IOTA", "ITUZ", "MODE", "STX_STRING",
|
|
"NAME", "NOTES", "OPERATOR", "PFX", "PROP_MODE",
|
|
"QSLRDATE", "QSLSDATE", "QSL_MSG", "QSL_RCVD", "QSL_SENT", "QSL_VIA", "QSO_DATE", "QSO_DATE_OFF",
|
|
"QTH",
|
|
"RST_RCVD", "RST_SENT", "RX_PWR",
|
|
"SAT_MODE", "SAT_NAME", "SRX",
|
|
"STATE", "STX", "TEN_TEN",
|
|
"TIME_OFF", "TIME_ON", "TX_PWR", "VE_PROV", "SRX_STRING"
|
|
};
|
|
|
|
FIELD fields[] = {
|
|
// TYPE, NAME, WIDGET
|
|
{ADDRESS, 0, NULL}, // contacted stations mailing address
|
|
{AGE, 0, NULL}, // contacted operators age in years
|
|
{ARRL_SECT, 0, NULL}, // contacted stations ARRL section
|
|
{BAND, 0, &btnSelectBand}, // QSO band
|
|
{CALL, 0, &btnSelectCall}, // contacted stations CALLSIGN
|
|
{CNTY, 0, &btnSelectCNTY}, // secondary political subdivision, ie: county
|
|
{COMMENT, 0, NULL}, // comment field for QSO
|
|
{CONT, 0, &btnSelectCONT}, // contacted stations continent
|
|
{CONTEST_ID, 0, NULL}, // QSO contest identifier
|
|
{COUNTRY, 0, &btnSelectCountry}, // contacted stations DXCC entity name
|
|
{CQZ, 0, &btnSelectCQZ}, // contacted stations CQ Zone
|
|
{DXCC, 0, &btnSelectDXCC}, // contacted stations Country Code
|
|
{EXPORT, 0, NULL}, // used to indicate record is to be exported
|
|
{FREQ, 0, &btnSelectFreq}, // QSO frequency in Mhz
|
|
{GRIDSQUARE, 0, &btnSelectLOC}, // contacted stations Maidenhead Grid Square
|
|
{IOTA, 0, &btnSelectIOTA}, // Islands on the air
|
|
{ITUZ, 0, &btnSelectITUZ}, // ITU zone
|
|
{MODE, 0, &btnSelectMode}, // QSO mode
|
|
{MYXCHG, 0, &btnSelectMyXchg}, // contest exchange sent
|
|
{NAME, 0, &btnSelectName}, // contacted operators NAME
|
|
{NOTES, 0, &btnSelectNotes}, // QSO notes
|
|
{OPERATOR, 0, NULL}, // Callsign of person logging the QSO
|
|
{PFX, 0, NULL}, // WPA prefix
|
|
{PROP_MODE, 0, NULL}, // propogation mode
|
|
{QSLRDATE, 0, &btnSelectQSLrcvd}, // QSL received date
|
|
{QSLSDATE, 0, &btnSelectQSLsent}, // QSL sent date
|
|
{QSL_MSG, 0, NULL}, // personal message to appear on qsl card
|
|
{QSL_RCVD, 0, NULL}, // QSL received status
|
|
{QSL_SENT, 0, NULL}, // QSL sent status
|
|
{QSL_VIA, 0, NULL},
|
|
{QSO_DATE, 0, &btnSelectQSOdateOn}, // QSO data
|
|
{QSO_DATE_OFF, 0, &btnSelectQSOdateOff},// QSO data OFF, according to ADIF 2.2.6
|
|
{QTH, 0, &btnSelectQth}, // contacted stations city
|
|
{RST_RCVD, 0, &btnSelectRSTrcvd}, // received signal report
|
|
{RST_SENT, 0, &btnSelectRSTsent}, // sent signal report
|
|
{RX_PWR, 0, NULL}, // power of other station in watts
|
|
{SAT_MODE, 0, NULL}, // satellite mode
|
|
{SAT_NAME, 0, NULL}, // satellite name
|
|
{SRX, 0, &btnSelectSerialIN}, // received serial number for a contest QSO
|
|
{STATE, 0, &btnSelectState}, // contacted stations STATE
|
|
{STX, 0, &btnSelectSerialOUT}, // QSO transmitted serial number
|
|
{TEN_TEN, 0, NULL}, // ten ten # of other station
|
|
{TIME_OFF, 0, &btnSelectTimeOFF}, // HHMM or HHMMSS in UTC
|
|
{TIME_ON, 0, &btnSelectTimeON}, // HHMM or HHMMSS in UTC
|
|
{TX_PWR, 0, &btnSelectTX_pwr}, // power transmitted by this station
|
|
{VE_PROV, 0, &btnSelectProvince}, // 2 letter abbreviation for Canadian Province
|
|
{XCHG1, 0, &btnSelectXchgIn} // contest exchange #1 / free1 in xlog
|
|
};
|
|
|
|
static void initfields()
|
|
{
|
|
for (int i = 0; i < NUMFIELDS; i++)
|
|
fields[i].name = new string(fieldnames[i]);
|
|
}
|
|
|
|
static int findfield( const char *p )
|
|
{
|
|
int m;
|
|
int test;
|
|
|
|
if (strncasecmp (p, "EOR>", 4) == 0)
|
|
return -1;
|
|
|
|
// following two tests are for backward compatibility with older
|
|
// fldigi fields
|
|
// changed to comply with ADIF 2.2.3
|
|
|
|
if (strncasecmp (p, "XCHG1>", 6) == 0)
|
|
return XCHG1;
|
|
if (strncasecmp (p, "MYXCHG>", 7) == 0)
|
|
return MYXCHG;
|
|
|
|
string tststr;
|
|
for (m = 0; m < NUMFIELDS; m++) {
|
|
tststr = *(fields[m].name);
|
|
tststr += ':';
|
|
if ( (test = strncasecmp( p, tststr.c_str(), tststr.length() )) == 0)
|
|
return fields[m].type;
|
|
}
|
|
return -2; //search key not found
|
|
}
|
|
|
|
cAdifIO::cAdifIO ()
|
|
{
|
|
initfields();
|
|
}
|
|
|
|
void cAdifIO::fillfield (int fieldnum, char *buff){
|
|
const char *p = buff;
|
|
int fldsize;
|
|
while (*p != ':' && *p != '>') p++;
|
|
if (*p == '>') return; // bad ADIF specifier ---> no ':' after field name
|
|
// found first ':'
|
|
p++;
|
|
fldsize = 0;
|
|
const char *p2 = strchr(buff,'>');
|
|
if (!p2) return;
|
|
while (p != p2) {
|
|
if (*p >= '0' && *p <= '9') {
|
|
fldsize = fldsize * 10 + *p - '0';
|
|
}
|
|
p++;
|
|
}
|
|
adifqso.putField (fieldnum, p2+1, fldsize);
|
|
}
|
|
|
|
void cAdifIO::readFile (const char *fname, cQsoDb *db) {
|
|
long filesize = 0;
|
|
char *buff;
|
|
int found;
|
|
int retval;
|
|
|
|
// open the adif file
|
|
adiFile = fopen (fname, "r");
|
|
if (!adiFile)
|
|
return;
|
|
// determine its size for buffer creation
|
|
fseek (adiFile, 0, SEEK_END);
|
|
filesize = ftell (adiFile);
|
|
|
|
if (filesize == 0) {
|
|
fl_alert2(_("Empty ADIF logbook file"));
|
|
return;
|
|
}
|
|
|
|
buff = new char[filesize + 1];
|
|
// read the entire file into the buffer
|
|
fseek (adiFile, 0, SEEK_SET);
|
|
retval = fread (buff, filesize, 1, adiFile);
|
|
fclose (adiFile);
|
|
|
|
// relaxed file integrity test to all importing from non conforming log programs
|
|
if ((strcasestr(buff, "<ADIF_VER:") != 0) &&
|
|
(strcasestr(buff, "<CALL:") == 0)) {
|
|
delete [] buff;
|
|
return;
|
|
}
|
|
if (strcasestr(buff, "<CALL:") == 0) {
|
|
fl_alert2(_("Not an ADIF file"));
|
|
delete [] buff;
|
|
return;
|
|
}
|
|
|
|
char *p1 = buff, *p2;
|
|
if (*p1 != '<') { // yes, skip over header to start of records
|
|
p1 = strchr(buff, '<');
|
|
while (strncasecmp (p1+1,"EOH>", 4) != 0) {
|
|
p1 = strchr(p1+1, '<'); // find next <> field
|
|
}
|
|
if (!p1) {
|
|
delete [] buff;
|
|
return; // must not be an ADIF compliant file
|
|
}
|
|
p1 += 1;
|
|
}
|
|
|
|
p2 = strchr(p1,'<'); // find first ADIF specifier
|
|
adifqso.clearRec();
|
|
|
|
while (p2) {
|
|
found = findfield(p2+1);
|
|
if (found > -1)
|
|
fillfield (found, p2+1);
|
|
else if (found == -1) { // <eor> reached; add this record to db
|
|
//update fields for older db
|
|
if (adifqso.getField(TIME_OFF)[0] == 0)
|
|
adifqso.putField(TIME_OFF, adifqso.getField(TIME_ON));
|
|
|
|
if (adifqso.getField(TIME_ON)[0] == 0)
|
|
adifqso.putField(TIME_ON, adifqso.getField(TIME_OFF));
|
|
|
|
if ((strlen(adifqso.getField(QSO_DATE)) > 7) &&
|
|
(adifqso.getField(QSO_DATE_OFF)[0] == 0)) {
|
|
char d_str[20];
|
|
int d, m, y, t_on, t_off;
|
|
strcpy(d_str, adifqso.getField(QSO_DATE));
|
|
d = atoi(&d_str[6]); d_str[6] = 0;
|
|
m = atoi(&d_str[4]); d_str[4] = 0;
|
|
y = atoi(d_str);
|
|
t_on = atoi(adifqso.getField(TIME_ON));
|
|
t_off = atoi(adifqso.getField(TIME_OFF));
|
|
Date dt(m, d, y);
|
|
if (t_off < t_on) dt++;
|
|
adifqso.putField(QSO_DATE_OFF, dt.szDate(2));
|
|
}
|
|
db->qsoNewRec (&adifqso);
|
|
adifqso.clearRec();
|
|
}
|
|
p1 = p2 + 1;
|
|
p2 = strchr(p1,'<');
|
|
}
|
|
|
|
db->SortByDate(progdefaults.sort_date_time_off);
|
|
delete [] buff;
|
|
}
|
|
|
|
static const char *adifmt = "<%s:%d>";
|
|
|
|
// write ALL or SELECTED records to the designated file
|
|
|
|
int cAdifIO::writeFile (const char *fname, cQsoDb *db)
|
|
{
|
|
string ADIFHEADER;
|
|
ADIFHEADER = "File: %s";
|
|
ADIFHEADER.append(szEOL);
|
|
ADIFHEADER.append("<ADIF_VER:%d>%s");
|
|
ADIFHEADER.append(szEOL);
|
|
ADIFHEADER.append("<PROGRAMID:%d>%s");
|
|
ADIFHEADER.append(szEOL);
|
|
ADIFHEADER.append("<PROGRAMVERSION:%d>%s");
|
|
ADIFHEADER.append(szEOL);
|
|
ADIFHEADER.append("<EOH>");
|
|
ADIFHEADER.append(szEOL);
|
|
// open the adif file
|
|
cQsoRec *rec;
|
|
string sFld;
|
|
adiFile = fopen (fname, "w");
|
|
if (!adiFile)
|
|
return 1;
|
|
fprintf (adiFile, ADIFHEADER.c_str(),
|
|
fl_filename_name(fname),
|
|
strlen(ADIF_VERS), ADIF_VERS,
|
|
strlen(PACKAGE_NAME), PACKAGE_NAME,
|
|
strlen(PACKAGE_VERSION), PACKAGE_VERSION);
|
|
|
|
for (int i = 0; i < db->nbrRecs(); i++) {
|
|
rec = db->getRec(i);
|
|
if (rec->getField(EXPORT)[0] == 'E') {
|
|
for (int j = 0; j < NUMFIELDS; j++) {
|
|
if (fields[j].btn != NULL)
|
|
if ((*fields[j].btn)->value()) {
|
|
sFld = rec->getField(fields[j].type);
|
|
if (!sFld.empty())
|
|
fprintf(adiFile, adifmt,
|
|
fields[j].name->c_str(),
|
|
sFld.length());
|
|
fprintf(adiFile, "%s", sFld.c_str());
|
|
}
|
|
}
|
|
rec->putField(EXPORT,"");
|
|
db->qsoUpdRec(i, rec);
|
|
fprintf(adiFile, "%s", szEOR);
|
|
fprintf(adiFile, "%s", szEOL);
|
|
}
|
|
}
|
|
fclose (adiFile);
|
|
return 0;
|
|
}
|
|
|
|
// write ALL records to the common log
|
|
|
|
int cAdifIO::writeLog (const char *fname, cQsoDb *db) {
|
|
|
|
string ADIFHEADER;
|
|
ADIFHEADER = "File: %s";
|
|
ADIFHEADER.append(szEOL);
|
|
ADIFHEADER.append("<ADIF_VER:%d>%s");
|
|
ADIFHEADER.append(szEOL);
|
|
ADIFHEADER.append("<PROGRAMID:%d>%s");
|
|
ADIFHEADER.append(szEOL);
|
|
ADIFHEADER.append("<PROGRAMVERSION:%d>%s");
|
|
ADIFHEADER.append(szEOL);
|
|
ADIFHEADER.append("<DATA CHECKSUM:%d>%s");
|
|
ADIFHEADER.append(szEOL);
|
|
ADIFHEADER.append("<EOH>");
|
|
ADIFHEADER.append(szEOL);
|
|
|
|
// open the adif file
|
|
string sFld;
|
|
cQsoRec *rec;
|
|
Ccrc16 checksum;
|
|
string s_checksum;
|
|
|
|
adiFile = fopen (fname, "w");
|
|
if (!adiFile) {
|
|
LOG_ERROR("Cannot write to %s", fname);
|
|
return 1;
|
|
}
|
|
|
|
string records;
|
|
string record;
|
|
char recfield[200];
|
|
|
|
records.clear();
|
|
for (int i = 0; i < db->nbrRecs(); i++) {
|
|
rec = db->getRec(i);
|
|
record.clear();
|
|
for (int j = 0; j < NUMFIELDS; j++) {
|
|
sFld = rec->getField(j);
|
|
if (!sFld.empty()) {
|
|
snprintf(recfield, sizeof(recfield), adifmt,
|
|
fields[j].name->c_str(), sFld.length());
|
|
record.append(recfield).append(sFld);
|
|
}
|
|
}
|
|
record.append(szEOR);
|
|
record.append(szEOL);
|
|
records.append(record);
|
|
db->qsoUpdRec(i, rec);
|
|
}
|
|
|
|
s_checksum = checksum.scrc16(records);
|
|
|
|
fprintf (adiFile, ADIFHEADER.c_str(),
|
|
fl_filename_name(fname),
|
|
strlen(ADIF_VERS), ADIF_VERS,
|
|
strlen(PACKAGE_NAME), PACKAGE_NAME,
|
|
strlen(PACKAGE_VERSION), PACKAGE_VERSION,
|
|
s_checksum.length(), s_checksum.c_str()
|
|
);
|
|
fprintf (adiFile, "%s", records.c_str());
|
|
|
|
fclose (adiFile);
|
|
|
|
return 0;
|
|
}
|
|
|