#ifndef __NMEA_H__ #define __NMEA_H__ #include #include #include #include #include #include template struct NMEA0183 { static int calc_checksum( const std::string &sentence){ int res(0); for(std::string::const_iterator it(sentence.begin()); it != sentence.end(); ++it){ res ^= (int)*it; } return res; } typedef std::vector parsed_t; static parsed_t parseSentence( const std::string &sentence){ using std::string; parsed_t res; // boostのtokenizer使うことも検討してもいいかも、とりあえず標準で頑張る for(string::size_type i(0); i < sentence.size(); i++){ string::size_type next_i = sentence.find_first_of(",*", i); if(next_i == string::npos){ res.push_back(sentence.substr(i)); break; }else{ res.push_back(sentence.substr(i, next_i - i)); } i = next_i; } // TODO: チェックサムがある場合は確認、あっていない場合は長さ0の配列を返す return res; } char talker[2]; mutable int checksum; NMEA0183() {} ~NMEA0183() {} NMEA0183(const parsed_t &parsed) { if((parsed.size() > 1) && (parsed[0][0] == '$')){ talker[0] = parsed[0][1]; talker[1] = parsed[0][2]; std::stringstream(parsed[parsed.size() - 1]) >> std::hex >> checksum; // 一番後ろはチェックサム } } struct GGA : public NMEA0183 { FloatT time_utc; // sec FloatT latitude; // North is possitive, unit is deg FloatT longitude; // East is possitive, unit is deg int quality; // 0 - nofix, 1 - fix, 2 - DGPS int satellites; FloatT hdop; FloatT altitude_geoid; // meter FloatT geoid_separation; // meter FloatT age_of_DGPS; // sec int station_id_DGPS; GGA() : NMEA0183() {} ~GGA() {} GGA(const parsed_t &parsed) : NMEA0183(parsed) { typedef std::stringstream ss_t; if(parsed.size() < 15){return;} if(parsed[0].substr(3, 3) != "GGA"){return;} /* * 1) Time (UTC) * 2) Latitude * 3) N or S (North or South) * 4) Longitude * 5) E or W (East or West) * 6) GPS Quality Indicator, * 0 - fix not available, * 1 - GPS fix, * 2 - Differential GPS fix * 7) Number of satellites in view, 00 - 12 * 8) Horizontal Dilution of precision * 9) Antenna Altitude above/below mean-sea-level (geoid) * 10) Units of antenna altitude, meters * 11) Geoidal separation, the difference between the WGS-84 earth * ellipsoid and mean-sea-level (geoid), "-" means mean-sea-level below ellipsoid * 12) Units of geoidal separation, meters * 13) Age of differential GPS data, time in seconds since last SC104 * type 1 or 9 update, null field when DGPS is not used * 14) Differential reference station ID, 0000-1023 * 15) Checksum */ { struct { int index; FloatT &value; } convert_list[] = { {1, time_utc}, {2, latitude}, {4, longitude}, {8, hdop}, {9, altitude_geoid}, {11, geoid_separation}, {13, age_of_DGPS}}; for(int i(0); i < sizeof(convert_list) / sizeof(convert_list[0]); i++){ ss_t(parsed[convert_list[i].index]) >> convert_list[i].value; } } { struct { int index; int &value; } convert_list[] = { {6, quality}, {7, satellites}, {14, station_id_DGPS}}; for(int i(0); i < sizeof(convert_list) / sizeof(convert_list[0]); i++){ ss_t(parsed[convert_list[i].index]) >> convert_list[i].value; } } int time_utc_min((int)time_utc / 100); time_utc = std::fmod(time_utc, 100); int time_utc_hr(time_utc_min / 100); time_utc_min -= time_utc_hr * 100; time_utc += ((time_utc_hr * 60 + time_utc_min) * 60); FloatT latitude_min(std::fmod(latitude, 100.0)), longitude_min(std::fmod(longitude, 100.0)); latitude -= latitude_min; latitude /= 100; latitude += (latitude_min / 60); longitude -= longitude_min; longitude /= 100; longitude += (longitude_min / 60); if(parsed[3][0] != 'N'){latitude *= -1;} if(parsed[5][0] != 'E'){longitude *= -1;} if((parsed[10][0] != 'M') && (parsed[10][0] != 'm')){ /* unit is not meter */ } if((parsed[12][0] != 'M') && (parsed[12][0] != 'm')){ /* unit is not meter */ } } friend std::ostream &operator<<(std::ostream &out, const GGA &gga){ std::stringstream ss; ss << std::setprecision(12) << std::setfill('0'); ss << gga.talker[0] << gga.talker[1] << "GGA,"; int time_utc_hr((int)(gga.time_utc / 3600)), time_utc_min((int)(gga.time_utc / 60)); time_utc_min -= time_utc_hr * 60; ss << std::setw(2) << time_utc_hr << std::setw(2) << time_utc_min << (gga.time_utc - ((time_utc_hr * 60 + time_utc_min) * 60)) << ","; int latitude_deg((int)gga.latitude), longitude_deg((int)gga.longitude); FloatT latitude_min(std::abs(gga.latitude - latitude_deg) * 60), longitude_min(std::abs(gga.longitude - longitude_deg) * 60); latitude_deg = std::abs(latitude_deg); longitude_deg = std::abs(longitude_deg); ss << std::setw(2) << latitude_deg; if(latitude_min < 10){ss << '0';} ss << latitude_min << "," << (gga.latitude >= 0 ? 'N' : 'S') << ","; ss << std::setw(3) << longitude_deg; if(longitude_min < 10){ss << '0';} ss << longitude_min << "," << (gga.longitude >= 0 ? 'E' : 'W') << ","; ss << gga.quality << "," << std::setw(2) << gga.satellites << "," << gga.hdop << "," << gga.altitude_geoid << ",M," << gga.geoid_separation << ",M," << gga.age_of_DGPS << "," << std::setw(4) << gga.station_id_DGPS; gga.checksum = NMEA0183::calc_checksum(ss.str()); ss << "*" << std::hex << std::setw(2) << gga.checksum; return out << '$' << ss.str(); } }; struct GSA : public NMEA0183 { char selection_mode; char mode; int satellite_ids[12]; FloatT pdop; FloatT hdop; FloatT vdop; GSA() : NMEA0183() {} ~GSA() {} GSA(const parsed_t &parsed) : NMEA0183(parsed) { typedef std::stringstream ss_t; if(parsed.size() < 18){return;} if(parsed[0].substr(3, 3) != "GSA"){return;} /* * 1) Selection mode * 2) Mode * 3) ID of 1st satellite used for fix * 4) ID of 2nd satellite used for fix * ... * 14) ID of 12th satellite used for fix * 15) PDOP in meters * 16) HDOP in meters * 17) VDOP in meters * 18) Checksum */ { struct { int index; FloatT &value; } convert_list[] = { {15, pdop}, {16, hdop}, {17, vdop}}; for(int i(0); i < sizeof(convert_list) / sizeof(convert_list[0]); i++){ ss_t(parsed[convert_list[i].index]) >> convert_list[i].value; } } { for(int i(0); i < sizeof(satellite_ids) / sizeof(satellite_ids[0]); i++){ if(parsed[3 + i].size()){ ss_t(parsed[3 + i]) >> satellite_ids[i]; }else{ satellite_ids[i] = 0; } } } selection_mode = parsed[1][0]; mode = parsed[2][0]; } friend std::ostream &operator<<(std::ostream &out, const GSA &gsa){ std::stringstream ss; ss << std::setprecision(12) << std::setfill('0'); ss << gsa.talker[0] << gsa.talker[1] << "GSA," << gsa.selection_mode << "," << gsa.mode << ","; for(int i(0); i < sizeof(gsa.satellite_ids) / sizeof(gsa.satellite_ids[0]); i++){ if(gsa.satellite_ids[i] != 0){ ss << std::setw(2) << gsa.satellite_ids[i]; } ss << ","; } ss << gsa.pdop << "," << gsa.hdop << "," << gsa.vdop; gsa.checksum = NMEA0183::calc_checksum(ss.str()); ss << "*" << std::hex << std::setw(2) << gsa.checksum; return out << '$' << ss.str(); } }; struct VTG : public NMEA0183 { FloatT deg_true; FloatT deg_mag; FloatT speed_ms; // m/sec VTG() : NMEA0183() {} ~VTG() {} VTG(const parsed_t &parsed) : NMEA0183(parsed) { typedef std::stringstream ss_t; if(parsed.size() < 9){return;} if(parsed[0].substr(3, 3) != "VTG"){return;} if((parsed[2][0] != 'T') || (parsed[4][0] != 'M') || (parsed[6][0] != 'N') || (parsed[8][0] != 'K')){return;} /* * 1) Track Degrees * 2) T = True * 3) Track Degrees * 4) M = Magnetic * 5) Speed Knots * 6) N = Knots * 7) Speed Kilometers Per Hour * 8) K = Kilometres Per Hour * 9) Checksum */ { struct { int index; FloatT &value; } convert_list[] = { {1, deg_true}, {3, deg_mag}, {7, speed_ms}}; for(int i(0); i < sizeof(convert_list) / sizeof(convert_list[0]); i++){ ss_t(parsed[convert_list[i].index]) >> convert_list[i].value; } } speed_ms *= (1000.0 / 3600); // km/hr => m/sec } friend std::ostream &operator<<(std::ostream &out, const VTG &vtg){ std::stringstream ss; ss << std::setprecision(6) << std::setfill('0'); ss << vtg.talker[0] << vtg.talker[1] << "VTG," << vtg.deg_true << ",T," << vtg.deg_mag << ",M," << (vtg.speed_ms * (3600.0/1852)) << ",N," << (vtg.speed_ms * (3600.0/1000)) << ",K"; vtg.checksum = NMEA0183::calc_checksum(ss.str()); ss << "*" << std::hex << std::setw(2) << vtg.checksum; return out << '$' << ss.str(); } }; }; #endif