Better GPS handling.

Improved nmea parsing. Beter fix validation.
master
Michal Fratczak 2020-04-15 15:39:40 +02:00
rodzic d0407b0056
commit 1b7f91c207
7 zmienionych plików z 129 dodań i 42 usunięć

Wyświetl plik

@ -36,7 +36,7 @@ bool GLOB::dynamics_add(const std::string& name, const dynamics_t::tp timestamp,
}
dynamics_t GLOB::dynamics_get(const std::string& name)
dynamics_t GLOB::dynamics_get(const std::string& name) const
{
auto d = dynamics_.find(name);
if( d == dynamics_.end() )
@ -44,3 +44,11 @@ dynamics_t GLOB::dynamics_get(const std::string& name)
else
return d->second;
}
std::vector<std::string> GLOB::dynamics_keys() const
{
std::vector<std::string> ret;
for(const auto& k : dynamics_)
ret.push_back(k.first);
return ret;
}

Wyświetl plik

@ -1,8 +1,10 @@
#pragma once
#include <string>
#include <vector>
#include <map>
#include <atomic>
#include <chrono>
#include "mtx2/mtx2.h"
#include "nmea/nmea.h"
@ -19,7 +21,8 @@ private:
GLOB( const GLOB& ) = delete; // non construction-copyable
GLOB& operator=( const GLOB& ) = delete; // non copyable
std::atomic<nmea_t> nmea; // GPS data
std::atomic<nmea_t> nmea_; // GPS data
std::chrono::steady_clock::time_point gps_fix_timestamp_;
// sensors dynamics
std::map<std::string, dynamics_t> dynamics_; // index: value name (alt, temp1, etc.)
@ -51,11 +54,15 @@ public:
std::atomic<float> temperature{0};
bool dynamics_add(const std::string& name, const dynamics_t::tp timestamp, const float value);
dynamics_t dynamics_get(const std::string& name);
dynamics_t dynamics_get(const std::string& name) const;
std::vector<std::string> dynamics_keys() const; // names in dynamics_
void nmea_set(const nmea_t& in_nmea) { get().nmea_ = in_nmea; }
nmea_t nmea_get() { nmea_t ret = get().nmea_; return ret; }
void gps_fix_now() { gps_fix_timestamp_ = std::chrono::steady_clock::now(); }
int gps_fix_age() const { return (std::chrono::steady_clock::now() - gps_fix_timestamp_).count() / 1e9; }
nmea_t nmea_get() { nmea_t ret = get().nmea; return ret; }
void nmea_set(const nmea_t& in_nmea) { get().nmea = in_nmea; }
std::string str() const;
};

Wyświetl plik

@ -40,7 +40,21 @@ bool dynamics_t::add(const tp timestamp, const float val)
return true;
}
std::chrono::system_clock::time_point dynamics_t::utc2tp(const std::string utc)
std::string dynamics_t::json() const
{
std::stringstream ss;
std::string sep(",");
ss<<"{";
ss<<"'initialised':"<<initialised_<<sep;
ss<<"'val':"<<val_<<sep;
ss<<"'min':"<<val_min_<<sep;
ss<<"'max':"<<val_max_<<sep;
ss<<"'dVdT':"<<dVdT_;
ss<<"}";
return ss.str();
}
std::chrono::system_clock::time_point dynamics_t::utc2tp(const std::string& utc)
{
using namespace std;

Wyświetl plik

@ -30,7 +30,9 @@ public:
float max() const { return val_max_; }
float dVdT() const { return dVdT_; }
static std::chrono::system_clock::time_point utc2tp(const std::string utc);
std::string json() const;
static std::chrono::system_clock::time_point utc2tp(const std::string& utc);
};

Wyświetl plik

@ -70,13 +70,19 @@ zmq::message_t make_zmq_reply(const std::string& i_msg_str)
{
auto& G = GLOB::get();
if(i_msg_str == "nmea") {
const std::string reply_str( G.nmea_get().str() );
std::string reply_str( "{'nmea':" + G.nmea_get().json() );
reply_str += ",'fixAge':" + std::to_string(G.gps_fix_age());
reply_str += "}";
zmq::message_t reply( reply_str.size() );
memcpy( (void*) reply.data(), reply_str.c_str(), reply_str.size() );
return reply;
}
else if(i_msg_str == "temp") {
std::string reply_str = std::to_string( G.temperature );
else if(i_msg_str == "dynamics") {
std::string reply_str("{'dynamics':{");
for(auto& dyn_name : G.dynamics_keys())
reply_str += "'" + dyn_name + "':" + G.dynamics_get(dyn_name).json() + ",";
reply_str.pop_back(); // last comma
reply_str += "}}";
zmq::message_t reply( reply_str.size() );
memcpy( (void*) reply.data(), reply_str.c_str(), reply_str.size() );
return reply;
@ -189,17 +195,33 @@ int main1(int argc, char** argv)
//
std::thread ublox_thread( [uBlox_i2c_fd]() {
while(G_RUN) {
// std::this_thread::sleep_for( std::chrono::seconds(5) );
const vector<char> ublox_data = uBLOX_read_msg(uBlox_i2c_fd);
const string nmea_str( NMEA_get_last_msg(ublox_data.data(), ublox_data.size()) );
cout<<nmea_str<<C_OFF<<endl;
if( !NMEA_msg_checksum_ok(nmea_str) )
{
if( !NMEA_msg_checksum_ok(nmea_str) ) {
cerr<<C_RED<<"NMEA Checksum Fail: "<<nmea_str<<C_OFF<<endl;
continue;
}
nmea_t nmea = GLOB::get().nmea_get();
NMEA_parse( nmea_str.c_str(), nmea ); // nmea fields that were not altered by GGA/RMC will remain unmodified
GLOB::get().nmea_set(nmea); //update global nmea
nmea_t current_nmea;
if( NMEA_parse(nmea_str.c_str(), current_nmea) ) {
// only one at a time can be valid.
// fix_status is from RMC, fix_quality is from GGA
const bool gps_fix_valid =
current_nmea.fix_status == nmea_t::fix_status_t::kValid
|| current_nmea.fix_quality != nmea_t::fix_quality_t::kNoFix;
if(gps_fix_valid) {
GLOB::get().nmea_set(current_nmea);
GLOB::get().gps_fix_now();
}
else { // REUSE LAT,LON,ALT FROM LAST VALID SENTENCE
nmea_t valid_nmea = GLOB::get().nmea_get();
current_nmea.lat = valid_nmea.lat;
current_nmea.lon = valid_nmea.lon;
current_nmea.alt = valid_nmea.alt;
GLOB::get().nmea_set(current_nmea);
}
}
}
});
@ -236,7 +258,6 @@ int main1(int argc, char** argv)
if(!res.has_value())
continue;
string msg_str( (char*)msg.data(), msg.size() );
cout<<"ZMQ msg: "<<msg_str<<endl;
zmq_socket.send( make_zmq_reply(msg_str), zmq::send_flags::none );
}
});
@ -244,27 +265,20 @@ int main1(int argc, char** argv)
// READ SENSORS, CONSTRUCT TELEMETRY MESSAGE, RF SEND TEMEMETRY AND IMAGE
//
nmea_t valid_nmea;
ssdv_t ssdv_tiles;
int msg_id = 0;
while(G_RUN)
{
int msg_num = 0;
while( G_RUN && msg_num++ < G.cli.msg_num )
while( G_RUN
&& (msg_num++ < G.cli.msg_num || G.gps_fix_age() > 20) )
{
++msg_id;
// GPS data
//
const nmea_t current_nmea = G.nmea_get();
const bool gps_fix_valid =
current_nmea.fix_status == nmea_t::fix_status_t::kValid
&& current_nmea.fix_quality != nmea_t::fix_quality_t::kNoFix;
if(gps_fix_valid)
valid_nmea = current_nmea;
else // at least use time
memcpy( valid_nmea.utc, current_nmea.utc, sizeof(current_nmea.utc) );
const nmea_t valid_nmea = G.nmea_get();
// dynamics
@ -278,25 +292,22 @@ int main1(int argc, char** argv)
// telemetry message
//
stringstream msg_stream;
// Callsign, ID, UTC:
msg_stream<<G.cli.callsign;
msg_stream<<","<<msg_id;
msg_stream<<","<<valid_nmea.utc;
// ONLY VALID LAT,LON,ALT ARE BEING SENT. LOOK INTO uBLOX THREAD
msg_stream<<","<<valid_nmea.lat<<","<<valid_nmea.lon<<","<<valid_nmea.alt;
msg_stream<<","<<valid_nmea.sats<<","<<gps_fix_valid;
msg_stream<<","<<valid_nmea.sats<<","<<GLOB::get().gps_fix_age();
// Sensors:
msg_stream<<","<<setprecision(1)<<fixed<<G.temperature;
// CRC:
const string msg_and_crc = string("\0",1) + "$$$" + msg_stream.str() + '*' + CRC(msg_stream.str());
cout<<C_GREEN<<msg_and_crc<<C_OFF<<endl;
// emit telemetry msg RF
// emit telemetry msg @RF
//
mtx2_write(radio_fd, msg_and_crc + '\n');
// if no GPS fix, keep sending telemetry instead of SSDV
//
// for some reason this loop-restart does not work with for() loop
if( !gps_fix_valid )
msg_num = 0;
}
@ -326,15 +337,15 @@ int main1(int argc, char** argv)
//
cout<<"Closing sensors thread"<<endl;
sensors_thread.join();
cout<<"Closing uBlox I2C"<<endl;
cout<<"Closing uBlox I2C thread and device"<<endl;
ublox_thread.join();
close(uBlox_i2c_fd);
cout<<"Closing UART Radio"<<endl;
cout<<"Closing UART Radio device"<<endl;
close(radio_fd);
gpioWrite (G.cli.hw_pin_radio_on, 0);
cout<<"Closing gpio"<<endl;
gpioTerminate();
cout<<"Closing zmq"<<endl;
cout<<"Closing zmq thread"<<endl;
zmq_thread.join(); // will return after next received message, or stuck forever if no messages come in

Wyświetl plik

@ -137,7 +137,8 @@ bool NMEA_parse(const char* Buffer, nmea_t& o_nmea)
int scanned_positions =
sscanf(Buffer + 7, "%f,%f,%c,%f,%c,%d,%d,%f,%f,%c", &
utc, &lat, &ns, &lon, &ew, &quality, &sats, &hdop, &alt, &alt_units);
if(scanned_positions == 10)
if(scanned_positions >= 10)
{
lat = degree_2_decimal(lat);
if(ns == 'S')
@ -195,7 +196,8 @@ bool NMEA_parse(const char* Buffer, nmea_t& o_nmea)
int scanned_positions =
sscanf(Buffer+7, "%f,%c,%f,%c,%f,%c,%[^','],%[^','],%d",
&utc, &status, &lat, &ns, &lon, &ew, speedstring, coursestring, &date);
if(scanned_positions == 7)
if(scanned_positions >= 7)
{
speedOG = atof(speedstring);
courseOG = atof(coursestring);
@ -283,6 +285,48 @@ std::string nmea_t::str() const
}
std::string nmea_t::json() const
{
std::stringstream s;
std::string sep(",");
s<<"{";
s<<"'utc':"<<utc<<sep;
s<<"'lat':"<<lat<<sep;
s<<"'lon':"<<lon<<sep;
s<<"'alt':"<<alt<<sep;
s<<"'speed_over_ground_mps':"<<speed_over_ground_mps<<sep;
s<<"'course_over_ground_deg':"<<course_over_ground_deg<<sep;
s<<"'sats':"<<sats<<sep;
if(fix_status == nmea_t::fix_status_t::kValid)
s<<"'fixStatus':'VALID'"<<sep;
else if(fix_status == nmea_t::fix_status_t::kInvalid)
s<<"'fixStatus':'INVALID'"<<sep;
if( fix_quality == nmea_t::fix_quality_t::kNoFix )
s<<"'Q':'kNoFix'";
else if( fix_quality == nmea_t::fix_quality_t::kAutonomous )
s<<"'Q:kAutonomous'";
else if( fix_quality == nmea_t::fix_quality_t::kDifferential )
s<<"'Q:kDifferential'";
else if( fix_quality == nmea_t::fix_quality_t::kRtkFixed )
s<<"'Q:kRtkFixed'";
else if( fix_quality == nmea_t::fix_quality_t::kRtkFloat )
s<<"'Q:kRtkFloat'";
else if( fix_quality == nmea_t::fix_quality_t::kEstimated )
s<<"'Q:kEstimated'";
s<<"}";
return s.str();
}
// convert utc HHMMSS to seconds
int nmea_t::utc_as_seconds() const
{

Wyświetl plik

@ -19,13 +19,13 @@ public:
int sats = 0;
enum class fix_status_t {
enum class fix_status_t : int {
kInvalid=0, // V
kValid=1 // A
};
fix_status_t fix_status = fix_status_t::kInvalid;
enum class fix_quality_t {
enum class fix_quality_t : int {
kNoFix = 0,
kAutonomous = 1,
kDifferential = 2,
@ -36,6 +36,7 @@ public:
fix_quality_t fix_quality = fix_quality_t::kNoFix;
std::string str() const;
std::string json() const;
int utc_as_seconds() const; // convert utc HHMMSS to seconds
};