/////////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2020 Jon Beniston, M7RCE // // // // This program is free software; you can redistribute it and/or modify // // it under the terms of the GNU General Public License as published by // // the Free Software Foundation as version 3 of the License, or // // (at your option) any later version. // // // // This program is distributed in the hope that it will be useful, // // but WITHOUT ANY WARRANTY; without even the implied warranty of // // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // // GNU General Public License V3 for more details. // // // // You should have received a copy of the GNU General Public License // // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// #ifndef INCLUDE_OSNDB_H #define INCLUDE_OSNDB_H #include #include #include #include #include #include #include #include #include "csv.h" #define OSNDB_URL "https://opensky-network.org/datasets/metadata/aircraftDatabase.csv" struct AircraftInformation { int m_icao; QString m_registration; QString m_manufacturerName; QString m_model; QString m_owner; QString m_operator; QString m_operatorICAO; QString m_registered; // Read OpenSky Network CSV file // This is large and contains lots of data we don't want, so we convert to // a smaller version to speed up loading time // Note that we use C file functions rather than QT, as these are ~30% faster // and the QT version seemed to occasionally crash static QHash *readOSNDB(const QString &filename) { int cnt = 0; QHash *aircraftInfo = nullptr; // Column numbers used for the data as of 2020/10/28 int icaoCol = 0; int registrationCol = 1; int manufacturerNameCol = 3; int modelCol = 4; int ownerCol = 13; int operatorCol = 9; int operatorICAOCol = 11; int registeredCol = 15; qDebug() << "AircraftInformation::readOSNDB: " << filename; FILE *file; QByteArray utfFilename = filename.toUtf8(); if ((file = fopen(utfFilename.constData(), "r")) != NULL) { char row[2048]; if (fgets(row, sizeof(row), file)) { aircraftInfo = new QHash(); aircraftInfo->reserve(500000); // Read header int idx = 0; char *p = strtok(row, ","); while (p != NULL) { if (!strcmp(p, "icao24")) icaoCol = idx; else if (!strcmp(p, "registration")) registrationCol = idx; else if (!strcmp(p, "manufacturername")) manufacturerNameCol = idx; else if (!strcmp(p, "model")) modelCol = idx; else if (!strcmp(p, "owner")) ownerCol = idx; else if (!strcmp(p, "operator")) operatorCol = idx; else if (!strcmp(p, "operatoricao")) operatorICAOCol = idx; else if (!strcmp(p, "registered")) registeredCol = idx; p = strtok(NULL, ","); idx++; } // Read data while (fgets(row, sizeof(row), file)) { int icao = 0; char *icaoString = NULL; char *registration = NULL; size_t registrationLen = 0; char *manufacturerName = NULL; size_t manufacturerNameLen = 0; char *model = NULL; size_t modelLen = 0; char *owner = NULL; size_t ownerLen = 0; char *operatorName = NULL; size_t operatorNameLen = 0; char *operatorICAO = NULL; size_t operatorICAOLen = 0; char *registered = NULL; size_t registeredLen = 0; p = strtok(row, ","); idx = 0; while (p != NULL) { // Read strings, stripping quotes if (idx == icaoCol) { icaoString = p+1; icaoString[strlen(icaoString)-1] = '\0'; icao = strtol(icaoString, NULL, 16); } else if (idx == registrationCol) { registration = p+1; registrationLen = strlen(registration)-1; registration[registrationLen] = '\0'; } else if (idx == manufacturerNameCol) { manufacturerName = p+1; manufacturerNameLen = strlen(manufacturerName)-1; manufacturerName[manufacturerNameLen] = '\0'; } else if (idx == modelCol) { model = p+1; modelLen = strlen(model)-1; model[modelLen] = '\0'; } else if (idx == ownerCol) { owner = p+1; ownerLen = strlen(owner)-1; owner[ownerLen] = '\0'; } else if (idx == operatorCol) { operatorName = p+1; operatorNameLen = strlen(operatorName)-1; operatorName[operatorNameLen] = '\0'; } else if (idx == operatorICAOCol) { operatorICAO = p+1; operatorICAOLen = strlen(operatorICAO)-1; operatorICAO[operatorICAOLen] = '\0'; } else if (idx == registeredCol) { registered = p+1; registeredLen = strlen(registered)-1; registered[registeredLen] = '\0'; } p = strtok(NULL, ","); idx++; } // Only create the entry if we have some interesting data if ((icao != 0) && (registrationLen > 0 || modelLen > 0 || ownerLen > 0 || operatorNameLen > 0 || operatorICAOLen > 0)) { QString modelQ = QString(model); // Tidy up the model names if (modelQ.endsWith(" (Boeing)")) modelQ = modelQ.left(modelQ.size() - 9); else if (modelQ.startsWith("BOEING ")) modelQ = modelQ.right(modelQ.size() - 7); else if (modelQ.startsWith("Boeing ")) modelQ = modelQ.right(modelQ.size() - 7); else if (modelQ.startsWith("AIRBUS ")) modelQ = modelQ.right(modelQ.size() - 7); else if (modelQ.startsWith("Airbus ")) modelQ = modelQ.right(modelQ.size() - 7); else if (modelQ.endsWith(" (Cessna)")) modelQ = modelQ.left(modelQ.size() - 9); AircraftInformation *aircraft = new AircraftInformation(); aircraft->m_icao = icao; aircraft->m_registration = QString(registration); aircraft->m_manufacturerName = QString(manufacturerName); aircraft->m_model = modelQ; aircraft->m_owner = QString(owner); aircraft->m_operator = QString(operatorName); aircraft->m_operatorICAO = QString(operatorICAO); aircraft->m_registered = QString(registered); aircraftInfo->insert(icao, aircraft); cnt++; } } } fclose(file); } else qDebug() << "AircraftInformation::readOSNDB: Failed to open " << filename; qDebug() << "AircraftInformation::readOSNDB: Read " << cnt << " aircraft"; return aircraftInfo; } // Write a reduced size and validated version of the DB, so it loads quicker static bool writeFastDB(const QString &filename, QHash *aircraftInfo) { QFile file(filename); if (file.open(QIODevice::WriteOnly)) { file.write("icao24,registration,manufacturername,model,owner,operator,operatoricao,registered\n"); QHash::iterator i = aircraftInfo->begin(); while (i != aircraftInfo->end()) { AircraftInformation *info = i.value(); file.write(QString("%1").arg(info->m_icao, 1, 16).toUtf8()); file.write(","); file.write(info->m_registration.toUtf8()); file.write(","); file.write(info->m_manufacturerName.toUtf8()); file.write(","); file.write(info->m_model.toUtf8()); file.write(","); file.write(info->m_owner.toUtf8()); file.write(","); file.write(info->m_operator.toUtf8()); file.write(","); file.write(info->m_operatorICAO.toUtf8()); file.write(","); file.write(info->m_registered.toUtf8()); file.write("\n"); ++i; } file.close(); return true; } else { qCritical() << "AircraftInformation::writeFastDB failed to open " << filename << " for writing: " << file.errorString(); return false; } } // Read smaller CSV file with no validation. Takes about 0.5s instead of 2s. static QHash *readFastDB(const QString &filename) { int cnt = 0; QHash *aircraftInfo = nullptr; qDebug() << "AircraftInformation::readFastDB: " << filename; FILE *file; QByteArray utfFilename = filename.toUtf8(); if ((file = fopen(utfFilename.constData(), "r")) != NULL) { char row[2048]; if (fgets(row, sizeof(row), file)) { // Check header if (!strcmp(row, "icao24,registration,manufacturername,model,owner,operator,operatoricao,registered\n")) { aircraftInfo = new QHash(); aircraftInfo->reserve(500000); // Read data while (fgets(row, sizeof(row), file)) { char *p = row; AircraftInformation *aircraft = new AircraftInformation(); char *icaoString = csvNext(&p); int icao = strtol(icaoString, NULL, 16); aircraft->m_icao = icao; aircraft->m_registration = QString(csvNext(&p)); aircraft->m_manufacturerName = QString(csvNext(&p)); aircraft->m_model = QString(csvNext(&p)); aircraft->m_owner = QString(csvNext(&p)); aircraft->m_operator = QString(csvNext(&p)); aircraft->m_operatorICAO = QString(csvNext(&p)); aircraft->m_registered = QString(csvNext(&p)); aircraftInfo->insert(icao, aircraft); cnt++; } } else qDebug() << "AircraftInformation::readFastDB: Unexpected header"; } else qDebug() << "AircraftInformation::readFastDB: Empty file"; fclose(file); } else qDebug() << "AircraftInformation::readFastDB: Failed to open " << filename; qDebug() << "AircraftInformation::readFastDB - read " << cnt << " aircraft"; return aircraftInfo; } }; #endif