Как мне разобрать и проверить строку? - PullRequest
0 голосов
/ 23 января 2020

Хорошо, у меня есть проект на C ++, который я хочу завершить, который включает получение предложений NMEA и их анализ, чтобы проверить, соответствуют ли они «грамматическим правилам» NMEA.

Текущий метод, который я использовал, использует операторы if, но это явно не очень хорошая практика кодирования.

Какие еще методы я мог бы использовать, чтобы попытаться проверить строковые предложения для указанных c символов и групп символов?

1 Ответ

1 голос
/ 23 января 2020

Предположим, что у вас есть данные NMEA, подобные этим

$ GPGGA, 124613.90,5543.3221231, N, 03739.1368442, E, 1,15,0.69,147.0851, M, 14,4298, M ,, * 54 $ GPGSV, 3,1,10,27,12,078,41,05,31,308,49,16,25,043,44,02,11,268,44 * 7E $ GPGSV, 3,2,10,26,03,031,39 , 07,74,216,52,09,58,121,52,30,39,234,48 * 71 $ GPGSV, 3,3,10,23,30,116,46,04,37,114,47 * 79 $ GLGSV, 2,1,07 , 84,17,338,43,78,15,212,48,85,12,032,46,67,84,223,53 * 67 $ GLGSV, 2,2,07,77,67,195,47,76,50,047,54,66,32,144 , 52 * 5C $ GPGGA, 124614,00,5543,3221239, N 03739,1368445, E, 1,15,0.69,147.0864, ​​M, 14,4298, M ,, * 53 $ GPGSV, 3,1,10,27,12,078,41,05 , 31,308,49,16,25,043,43,02,11,268,44 * 79 $ GPGSV, 3,2,10,26,03,031,39,07,74,216,52,09,58,121,52,30,39,234,48 * 71 $ GPGSV, 3,3,10,23,30,116,46,04,37,114,47 * 79 $ GLGSV, 2,1,07,84,17,338,43,78,15,212,48,85,12,032,46 , 67,84,223,53 * 67 $ ГЛГСВ, 2,2,07,77,67,195,47,76,50,047,54,66,32,144,52 * 5 C

А если мы хотим извлечь данные GGA и GSV, вы можете использовать следующий код:

#include <string>
#include <ctime>
#include <cstring>
#include <iostream>
#include <fstream>
#include <iomanip>

constexpr size_t NumberOfFixQualityStrings = 9;
constexpr size_t NumberOfSatellitesPerGSVSentencePart = 4;
constexpr size_t MaxNumberOfPartsInSentence = 10;
constexpr size_t MaxTokensInSentence = 64;
constexpr size_t NumberOfFieldsInGGA = 12;

std::string fixQualityString[NumberOfFixQualityStrings]{ 
    "invalid", "GPS fix (SPS)", "DGPS fix", "PPS fix", "Real Time Kinematic", "Float RTK", 
    "estimated (dead reckoning", "Manual input mode", "Simulation mode" };

//  essential fix data which provide 3D location and accuracy data
struct GGA { 
    // Time of last satellite fix
    unsigned int fixTimeInUtcHours{};
    unsigned int fixTimeInUtcMinutes{};
    unsigned int fixTimeInUtcSeconds{};
    unsigned int fixTimeInUtcMilliSeconds{};
    // Position: Lattitude
    unsigned int lattitudeInDegree{};
    double lattitudeInMinutes{};
    std::string lattitideDirection{};
    // Position: Longitude
    unsigned int longitudeInDegree{};
    double longitudeInMinutes{};
    std::string longitudeDirection{};
    // FixQuality // see dteails as string above
    unsigned int fixQuality{};
    std::string fixQualityString{};
    // Number of satellites being tracked (can be more than shown in GSV, not all are beeing used for calculation)
    unsigned int numberOfTrackedSatellites{};
    //  Horizontal dilution of position
    double horizontalDilution{};
    // Altitude, Meters, above mean sea level
    double altitude{};
    std::string altitudeDimension{};
    // Height of geoid (mean sea level) above WGS84 ellipsoid
    double goidHight{};
    std::string goidHightDimension{};
};

// Detail information for satellites in satellit view (GSV)
struct SatelliteData {
    std::string satellitePRNnumber{};
    double elevationInDegress{};
    double azimuthInDegrees{};
    double snr{};  // signal noise ratio
};

// Part of a GSV sentence
struct GSVSentencePart {
    size_t numberOfSentencesForFullData{};
    size_t sentencePartNumber{};
    size_t numberOfSatellitesInView{};
    size_t numberOfSatellitesInThisPart{};
    SatelliteData satelliteData[NumberOfSatellitesPerGSVSentencePart];
};
struct GSV
{
    GSVSentencePart gsvSentencePart[MaxNumberOfPartsInSentence];
    size_t numberOfParts{};
};

bool checksumTest(std::string& line) {
    bool result{ false };
    // Check, if there is a 2 digt checksum at the end and convert it to decimal
    if (size_t pos{}, checkSumGiven{ std::stoul(line.substr(line.size() - 2), &pos, 16) }; pos == 2)
    {
        // Strip off checksum part
        line = line.substr(1,line.size() - 4);
        // Calculate checksum
        unsigned char calculatedChecksum{ 0U }; for (const unsigned char c : line)  calculatedChecksum ^= c;
        // Get result
        result = (calculatedChecksum == checkSumGiven);
    }
    return result;
}

// Split all strings into a tokens
size_t splitIntoTokens(std::string& s, std::string (&tokens)[MaxTokensInSentence]) {
    // Number of converted tokens
    size_t numberOfTokens{ 0 };
    // First check checksum
    if (checksumTest(s)) {
        // Now split along each comma
        for (size_t i{ 0U }, startpos{ 0U }; i < s.size(); ++i) {
            // So, if there is a comma or the end of the string
            if ((s[i] == ',') || (i == (s.size() - 1))) {
                // Copy substring
                tokens[numberOfTokens++] = s.substr(startpos, i - startpos);
                startpos = i + 1;
            }
        }
    }
    return numberOfTokens;
}


GGA convertStringToGGA(std::string& s) {
    GGA gga; 
    // Split string into tokens and check, if it worked
    if (std::string tokens[MaxTokensInSentence]; splitIntoTokens(s, tokens) > NumberOfFieldsInGGA && tokens[0] == "GPGGA") {
        gga.fixTimeInUtcHours = std::stoul(tokens[1].substr(0, 2));
        gga.fixTimeInUtcMinutes = std::stoul(tokens[1].substr(2, 2));
        gga.fixTimeInUtcSeconds = std::stoul(tokens[1].substr(4, 2));
        gga.fixTimeInUtcMilliSeconds = std::stod(tokens[1].substr(6, 2))*1000.0;
        gga.lattitudeInDegree = std::stoul(tokens[2].substr(0, 2));
        gga.lattitudeInMinutes = std::stod(tokens[2].substr(2));
        gga.lattitideDirection = tokens[3];
        gga.longitudeInDegree = std::stoul(tokens[4].substr(0, 2));
        gga.longitudeInMinutes = std::stod(tokens[4].substr(2));
        gga.longitudeDirection = tokens[5];
        gga.fixQuality = std::stoul(tokens[6]);
        gga.fixQualityString = (gga.fixQuality < NumberOfFixQualityStrings) ? fixQualityString[gga.fixQuality] : fixQualityString[0];
        gga.numberOfTrackedSatellites = std::stoul(tokens[7]);
        gga.horizontalDilution = std::stod(tokens[8]);
        gga.altitude = std::stod(tokens[9]);
        gga.altitudeDimension = tokens[10];
        gga.goidHight = std::stod(tokens[11]);
        gga.goidHightDimension = tokens[12];
    }
    return gga;
}

GSVSentencePart convertToGSVSentencePart(std::string& s) {
    GSVSentencePart gsvsp;
    // Split string into tokens and check, if it worked
    std::string tokens[MaxTokensInSentence];
    if (size_t numberOfCOnvertedTokens = splitIntoTokens(s, tokens); numberOfCOnvertedTokens > 0 && tokens[0] == "GPGSV") {
        gsvsp.numberOfSentencesForFullData = std::stoul(tokens[1]);
        gsvsp.sentencePartNumber = std::stoul(tokens[2]);
        gsvsp.numberOfSatellitesInView = std::stoul(tokens[3]);
        gsvsp.numberOfSatellitesInThisPart = 0;
        for (size_t currentToken = 4; currentToken < numberOfCOnvertedTokens; currentToken += 4) {
            gsvsp.satelliteData[gsvsp.numberOfSatellitesInThisPart].satellitePRNnumber = tokens[currentToken];
            gsvsp.satelliteData[gsvsp.numberOfSatellitesInThisPart].elevationInDegress = stod(tokens[currentToken + 1]);
            gsvsp.satelliteData[gsvsp.numberOfSatellitesInThisPart].azimuthInDegrees= stod(tokens[currentToken + 2]);
            gsvsp.satelliteData[gsvsp.numberOfSatellitesInThisPart].snr = stod(tokens[currentToken + 3]);
            ++gsvsp.numberOfSatellitesInThisPart;
        }
    }
    return gsvsp;
}
std::string calculateElapsedTime(const GGA& previousGGA, const GGA& nextGGA) {
    std::tm tmPrevious{}, tmNext{};
    tmPrevious.tm_year = 100; tmPrevious.tm_mon = 1; tmPrevious.tm_mday = 1;
    tmNext.tm_year = 100; tmNext.tm_mon = 1; tmNext.tm_mday = 1;
    tmPrevious.tm_hour = previousGGA.fixTimeInUtcHours;
    tmPrevious.tm_min = previousGGA.fixTimeInUtcMinutes;
    tmPrevious.tm_sec = previousGGA.fixTimeInUtcSeconds;
    std::time_t previousTime = std::mktime(&tmPrevious);
    tmNext.tm_hour = nextGGA.fixTimeInUtcHours;
    tmNext.tm_min = nextGGA.fixTimeInUtcMinutes;
    tmNext.tm_sec = nextGGA.fixTimeInUtcSeconds;
    std::time_t nextTime = std::mktime(&tmNext);
    double diff = std::difftime(nextTime, previousTime);
    diff  = diff + 1.0*nextGGA.fixTimeInUtcMilliSeconds/1000.0- 1.0*previousGGA.fixTimeInUtcMilliSeconds/1000.0;
    return std::to_string(diff);
}

int main() {
    // Open file and check, if it is open
    if (std::ifstream nmeaFile("r:\\log.txt"); nmeaFile) {

        GGA previousGGA;
        GGA nextGGA;
        GSV gsv;
        size_t state{ 0 };
        for (std::string line{}; std::getline(nmeaFile, line); ) {
            switch  ( state) {

            case 0:     // wait for first GGA data
                if (line.substr(0, 6) == "$GPGGA") {

                    previousGGA = nextGGA;
                    nextGGA = convertStringToGGA(line);

                    state = 1;
                    gsv = {};
                }
                break;
            case 1: // wait for GSV
                if (line.substr(0, 6) == "$GPGSV") {
                    gsv.gsvSentencePart[gsv.numberOfParts] = convertToGSVSentencePart(line);
                    if (gsv.gsvSentencePart[gsv.numberOfParts].numberOfSentencesForFullData ==
                        gsv.gsvSentencePart[gsv.numberOfParts].sentencePartNumber) {
                        state = 0;
                        ++gsv.numberOfParts;
                        // Now all data are available in reable and structed format.
                        // You can do, what you want with them
                        // For example, we can print all Satellite Data:
                        size_t counter{ 0 };
                        for (size_t i = 0; i < gsv.numberOfParts; ++i) {
                            for (size_t j = 0; j < gsv.gsvSentencePart[i].numberOfSatellitesInThisPart; j++) {
                                std::cout << "Satellite: " << std::setw(2) << ++counter << "  Satellite name: " <<
                                    std::setw(3) << gsv.gsvSentencePart[i].satelliteData[j].satellitePRNnumber <<
                                    "   SNR: " << std::setw(8) << gsv.gsvSentencePart[i].satelliteData[j].snr << 
                                    "  Elapsed time: "<< calculateElapsedTime(previousGGA, nextGGA)<< " s\n";
                            }
                        }
                        --gsv.numberOfParts;
                    }
                    ++gsv.numberOfParts;
                }
                break;
            } 
        }
    }
    return 0;
}

Стиль кодирования - "умолять" внутренний "-уровень для более легкого понимания.

Современный подход C ++ был бы совершенно другим, но не таким простым для понимания.

...