1 #include "MainWindow.hpp"
4 #include <QCoreApplication>
6 #include <QFontDatabase>
10 #include <QListWidget>
12 #include <QMessageBox>
13 #include <QPushButton>
14 #include <QSerialPort>
15 #include <QTextStream>
17 #include <marnav/nmea/checksum.hpp>
18 #include <marnav/nmea/nmea.hpp>
19 #include <marnav/nmea/bod.hpp>
20 #include <marnav/nmea/gga.hpp>
21 #include <marnav/nmea/gll.hpp>
22 #include <marnav/nmea/gsa.hpp>
23 #include <marnav/nmea/gsv.hpp>
24 #include <marnav/nmea/hdg.hpp>
25 #include <marnav/nmea/mwv.hpp>
26 #include <marnav/nmea/rmb.hpp>
27 #include <marnav/nmea/rmc.hpp>
28 #include <marnav/nmea/rte.hpp>
29 #include <marnav/nmea/vtg.hpp>
30 #include <marnav/nmea/pgrme.hpp>
31 #include <marnav/nmea/pgrmm.hpp>
32 #include <marnav/nmea/pgrmz.hpp>
33 #include <marnav/nmea/string.hpp>
35 namespace marnav_example
40 static QString
render(const marnav::nmea::status
& t
)
42 return marnav::nmea::to_string(t
).c_str();
45 static QString
render(const marnav::nmea::direction
& t
)
47 return marnav::nmea::to_string(t
).c_str();
50 static QString
render(const marnav::nmea::reference
& t
)
52 return marnav::nmea::to_string(t
).c_str();
55 static QString
render(const marnav::nmea::date
& t
)
57 return marnav::nmea::to_string(t
).c_str();
60 static QString
render(const marnav::nmea::quality
& t
)
62 return marnav::nmea::to_string(t
).c_str();
65 static QString
render(const marnav::nmea::pgrmz::fix_type
& t
)
67 return marnav::nmea::to_string(t
).c_str();
70 static QString
render(const marnav::nmea::unit::distance
& t
)
72 return marnav::nmea::to_string(t
).c_str();
75 static QString
render(const marnav::nmea::unit::velocity
& t
)
77 return marnav::nmea::to_string(t
).c_str();
80 static QString
render(uint32_t t
)
82 return QString
{"%1"}.arg(t
);
85 static QString
render(const std::string
& t
)
87 return QString
{t
.c_str()};
90 static QString
render(const marnav::nmea::time
& t
)
92 return QString
{"%1:%2:%3"}
93 .arg(t
.hour(), 2, 10, QLatin1Char('0'))
94 .arg(t
.minutes(), 2, 10, QLatin1Char('0'))
95 .arg(t
.seconds(), 2, 10, QLatin1Char('0'));
98 static QString
render(const marnav::geo::latitude
& t
)
100 return QString
{" %1°%2'%3%4"}
101 .arg(t
.degrees(), 2, 10, QLatin1Char('0'))
102 .arg(t
.minutes(), 2, 10, QLatin1Char('0'))
103 .arg(t
.seconds(), 2, 'f', 1, QLatin1Char('0'))
104 .arg(to_string(t
.hem()).c_str());
107 static QString
render(const marnav::geo::longitude
& t
)
109 return QString
{"%1°%2'%3%4"}
110 .arg(t
.degrees(), 3, 10, QLatin1Char('0'))
111 .arg(t
.minutes(), 2, 10, QLatin1Char('0'))
112 .arg(t
.seconds(), 2, 'f', 1, QLatin1Char('0'))
113 .arg(to_string(t
.hem()).c_str());
116 static QString
render(const marnav::nmea::selection_mode
& t
)
119 case marnav::nmea::selection_mode::manual
:
120 return QString
{"manual"};
121 case marnav::nmea::selection_mode::automatic
:
122 return QString
{"automatic"};
129 static QString
render(const marnav::nmea::route
& t
)
132 case marnav::nmea::route::complete
:
133 return QString
{"complete"};
134 case marnav::nmea::route::working
:
135 return QString
{"working"};
142 static QString
render(const marnav::nmea::waypoint
& t
)
147 static QString
render(const marnav::nmea::mode_indicator
& t
)
150 case marnav::nmea::mode_indicator::invalid
:
151 return QString
{"invalid"};
152 case marnav::nmea::mode_indicator::autonomous
:
153 return QString
{"autonomous"};
154 case marnav::nmea::mode_indicator::differential
:
155 return QString
{"differential"};
156 case marnav::nmea::mode_indicator::estimated
:
157 return QString
{"estimated"};
158 case marnav::nmea::mode_indicator::manual_input
:
159 return QString
{"manual input"};
160 case marnav::nmea::mode_indicator::simulated
:
161 return QString
{"simulated"};
162 case marnav::nmea::mode_indicator::data_not_valid
:
163 return QString
{"not valid"};
164 case marnav::nmea::mode_indicator::precise
:
165 return QString
{"precise"};
172 static QString
render(const marnav::units::length
& t
)
174 return QString
{"%1 NM"}.arg(t
.get
<marnav::units::nautical_miles
>().value());
177 static QString
render(const marnav::units::velocity
& t
)
179 return QString
{"%1 knots"}.arg(t
.get
<marnav::units::knots
>().value());
182 template <typename T
> static QString
render(const marnav::utils::optional
<T
> & t
)
189 static QString
details_rmb(const marnav::nmea::sentence
* s
)
191 const auto t
= marnav::nmea::sentence_cast
<marnav::nmea::rmb
>(s
);
193 result
+= "\nActive : " + render(t
->get_active());
194 result
+= "\nCross Track Error: " + render(t
->get_cross_track_error());
195 result
+= "\nWaypoint From : " + render(t
->get_waypoint_from());
196 result
+= "\nWaypoint To : " + render(t
->get_waypoint_to());
197 result
+= "\nLatitude : " + render(t
->get_lat());
198 result
+= "\nLongitude : " + render(t
->get_lon());
199 result
+= "\nRange : " + render(t
->get_range());
200 result
+= "\nBearing : " + render(t
->get_bearing());
201 result
+= "\nDest. Velocity : " + render(t
->get_dst_velocity());
202 result
+= "\nArrival Status : " + render(t
->get_arrival_status());
203 result
+= "\nMode Indicator : " + render(t
->get_mode_ind());
207 static QString
details_rmc(const marnav::nmea::sentence
* s
)
209 const auto t
= marnav::nmea::sentence_cast
<marnav::nmea::rmc
>(s
);
211 result
+= "\nTime UTC : " + render(t
->get_time_utc());
212 result
+= "\nStatus : " + render(t
->get_status());
213 result
+= "\nLatitude : " + render(t
->get_lat());
214 result
+= "\nLongitude: " + render(t
->get_lon());
215 result
+= "\nSOG : " + render(t
->get_sog());
216 result
+= "\nHeading : " + render(t
->get_heading());
217 result
+= "\nDate : " + render(t
->get_date());
218 result
+= "\nMagn Dev : " + render(t
->get_mag());
219 result
+= "\nMagn Hem : " + render(t
->get_mag_hem());
220 result
+= "\nMode Ind : " + render(t
->get_mode_ind());
224 static QString
details_mwv(const marnav::nmea::sentence
* s
)
226 const auto t
= marnav::nmea::sentence_cast
<marnav::nmea::mwv
>(s
);
228 result
+= "\nAngle : " + render(t
->get_angle());
229 result
+= "\nAngle Ref : " + render(t
->get_angle_ref());
230 result
+= "\nSpeed : " + render(t
->get_speed());
231 result
+= "\nSpeed Unit: " + render(t
->get_speed_unit());
232 result
+= "\nData Valid: " + render(t
->get_data_valid());
236 static QString
details_gga(const marnav::nmea::sentence
* s
)
238 const auto t
= marnav::nmea::sentence_cast
<marnav::nmea::gga
>(s
);
240 result
+= "\nTime : " + render(t
->get_time());
241 result
+= "\nLatitude : " + render(t
->get_lat());
242 result
+= "\nLongitude : " + render(t
->get_lon());
243 result
+= "\nQuality Ind : " + render(t
->get_quality_indicator());
244 result
+= "\nNum Satellites : " + render(t
->get_n_satellites());
245 result
+= "\nHoriz Dilution : " + render(t
->get_hor_dilution());
246 result
+= "\nAltitude : " + render(t
->get_altitude());
247 result
+= "\nGeodial Sep : " + render(t
->get_geodial_separation());
248 result
+= "\nDGPS Age : " + render(t
->get_dgps_age());
249 result
+= "\nDGPS Ref : " + render(t
->get_dgps_ref());
253 static QString
details_gsv(const marnav::nmea::sentence
* s
)
255 const auto t
= marnav::nmea::sentence_cast
<marnav::nmea::gsv
>(s
);
257 result
+= "\nNum Messages : " + render(t
->get_n_messages());
258 result
+= "\nMessages Number: " + render(t
->get_message_number());
259 result
+= "\nNum Sat in View: " + render(t
->get_n_satellites_in_view());
260 for (int i
= 0; i
< 4; ++i
) {
261 const auto sat
= t
->get_sat(i
);
263 result
+= QString
{"\nSat: PRN:%1 ELEV:%2 AZIMUTH:%3 SNR:%4"}
264 .arg(render(sat
->prn
), 2)
265 .arg(render(sat
->elevation
), 2)
266 .arg(render(sat
->azimuth
), 3)
267 .arg(render(sat
->snr
), 2);
273 static QString
details_gsa(const marnav::nmea::sentence
* s
)
275 const auto t
= marnav::nmea::sentence_cast
<marnav::nmea::gsa
>(s
);
277 result
+= "\nSelection Mode: " + render(t
->get_sel_mode());
278 result
+= "\nMode : " + render(t
->get_mode());
279 for (auto i
= 0; i
< marnav::nmea::gsa::max_satellite_ids
; ++i
) {
280 result
+= QString
{"\nSatellite %1 : %2"}.arg(i
, 2).arg(render(t
->get_satellite_id(i
)));
282 result
+= "\nPDOP : " + render(t
->get_pdop());
283 result
+= "\nHDOP : " + render(t
->get_hdop());
284 result
+= "\nVDOP : " + render(t
->get_vdop());
288 static QString
details_gll(const marnav::nmea::sentence
* s
)
290 const auto t
= marnav::nmea::sentence_cast
<marnav::nmea::gll
>(s
);
292 result
+= "\nLatitude : " + render(t
->get_lat());
293 result
+= "\nLongitude : " + render(t
->get_lon());
294 result
+= "\nTime UTC : " + render(t
->get_time_utc());
295 result
+= "\nStatus : " + render(t
->get_data_valid());
296 result
+= "\nMode Indicator: " + render(t
->get_mode_ind());
300 static QString
details_bod(const marnav::nmea::sentence
* s
)
302 const auto t
= marnav::nmea::sentence_cast
<marnav::nmea::bod
>(s
);
304 result
+= "\nBearing True : " + render(t
->get_bearing_true());
305 result
+= "\nBearing Magn : " + render(t
->get_bearing_magn());
306 result
+= "\nWaypoint To : " + render(t
->get_waypoint_to());
307 result
+= "\nWaypoint From: " + render(t
->get_waypoint_from());
311 static QString
details_vtg(const marnav::nmea::sentence
* s
)
313 const auto t
= marnav::nmea::sentence_cast
<marnav::nmea::vtg
>(s
);
315 result
+= "\nTrack True : " + render(t
->get_track_true());
316 result
+= "\nTrack Magn : " + render(t
->get_track_magn());
317 result
+= "\nSpeed Knots : " + render(t
->get_speed_kn());
318 result
+= "\nSpeed kmh : " + render(t
->get_speed_kmh());
319 result
+= "\nMode Indicator: " + render(t
->get_mode_ind());
323 static QString
details_pgrme(const marnav::nmea::sentence
* s
)
325 const auto t
= marnav::nmea::sentence_cast
<marnav::nmea::pgrme
>(s
);
327 result
+= "\nHPE : " + render(t
->get_horizontal_position_error());
328 result
+= "\nVPE : " + render(t
->get_vertical_position_error());
329 result
+= "\nOverall sph equiv pos err: "
330 + render(t
->get_overall_spherical_equiv_position_error());
334 static QString
details_pgrmm(const marnav::nmea::sentence
* s
)
336 const auto t
= marnav::nmea::sentence_cast
<marnav::nmea::pgrmm
>(s
);
338 result
+= "\nMap Datum: " + render(t
->get_map_datum());
342 static QString
details_pgrmz(const marnav::nmea::sentence
* s
)
344 const auto t
= marnav::nmea::sentence_cast
<marnav::nmea::pgrmz
>(s
);
346 result
+= "\nAltitude: " + render(t
->get_altitude()) + " feet";
347 result
+= "\nFix Type: " + render(t
->get_fix());
351 static QString
details_hdg(const marnav::nmea::sentence
* s
)
353 const auto t
= marnav::nmea::sentence_cast
<marnav::nmea::hdg
>(s
);
355 result
+= "\nHeading : " + render(t
->get_heading());
356 result
+= "\nMagn Deviation: " + render(t
->get_magn_dev()) + " "
357 + render(t
->get_magn_dev_hem());
358 result
+= "\nMagn Variation: " + render(t
->get_magn_var()) + " "
359 + render(t
->get_magn_var_hem());
363 static QString
details_rte(const marnav::nmea::sentence
* s
)
365 const auto t
= marnav::nmea::sentence_cast
<marnav::nmea::rte
>(s
);
367 result
+= "\nNum Messages : " + render(t
->get_n_messages());
368 result
+= "\nMessage Number: " + render(t
->get_message_number());
369 result
+= "\nMessage Mode : " + render(t
->get_message_mode());
370 for (int i
= 0; i
< marnav::nmea::rte::max_waypoints
; ++i
) {
371 const auto wp
= t
->get_waypoint_id(i
);
374 result
+= QString
{"\nWaypoint ID %1: %2"}.arg(i
, 2).arg(render(wp
));
381 static QString
get_details(const marnav::nmea::sentence
* s
)
383 #define ADD_SENTENCE(s) \
385 marnav::nmea::s::ID, detail::details_##s \
389 marnav::nmea::sentence_id id
;
390 std::function
<QString(const marnav::nmea::sentence
*)> func
;
392 using container
= std::vector
<entry
>;
393 static const container details
= {
395 ADD_SENTENCE(bod
), ADD_SENTENCE(gga
), ADD_SENTENCE(gsa
), ADD_SENTENCE(gll
),
396 ADD_SENTENCE(gsv
), ADD_SENTENCE(hdg
), ADD_SENTENCE(mwv
), ADD_SENTENCE(rmb
),
397 ADD_SENTENCE(rmc
), ADD_SENTENCE(rte
), ADD_SENTENCE(vtg
),
400 ADD_SENTENCE(pgrme
), ADD_SENTENCE(pgrmm
), ADD_SENTENCE(pgrmz
),
404 auto i
= std::find_if(begin(details
), end(details
),
405 [s
](const container::value_type
& entry
) { return entry
.id
== s
->id(); });
407 return (i
== end(details
)) ? QString
{"unknown"} : i
->func(s
);
410 MainWindow::MainWindow()
412 setWindowTitle(QCoreApplication::instance()->applicationName());
418 port
= new QSerialPort(this);
421 MainWindow::~MainWindow()
426 void MainWindow::create_menus()
428 auto menu_file
= menuBar()->addMenu(tr("&File"));
429 menu_file
->addAction(action_open_file
);
430 menu_file
->addSeparator();
431 menu_file
->addAction(action_open_port
);
432 menu_file
->addAction(action_close_port
);
433 menu_file
->addSeparator();
434 menu_file
->addAction(action_exit
);
436 auto menu_help
= menuBar()->addMenu(tr("&Help"));
437 menu_help
->addAction(action_about
);
438 menu_help
->addAction(action_about_qt
);
441 void MainWindow::create_actions()
443 action_exit
= new QAction(tr("E&xit"), this);
444 action_exit
->setStatusTip(tr("Quits the application"));
445 action_exit
->setShortcut(tr("Ctrl+Q"));
446 connect(action_exit
, &QAction::triggered
, this, &MainWindow::close
);
448 action_about
= new QAction(tr("About ..."), this);
449 action_about
->setStatusTip(tr("Shows the About dialog"));
450 connect(action_about
, &QAction::triggered
, this, &MainWindow::on_about
);
452 action_about_qt
= new QAction(tr("About Qt ..."), this);
453 action_about_qt
->setStatusTip(tr("Shows information about Qt"));
454 connect(action_about_qt
, &QAction::triggered
, this, &MainWindow::on_about_qt
);
456 action_open_port
= new QAction(tr("Open Port"), this);
457 action_open_port
->setStatusTip(tr("Opens Communication Port to read data"));
458 connect(action_open_port
, &QAction::triggered
, this, &MainWindow::on_open
);
460 action_close_port
= new QAction(tr("Close Port"), this);
461 action_close_port
->setStatusTip(tr("Closes Communication Port"));
462 connect(action_close_port
, &QAction::triggered
, this, &MainWindow::on_close
);
463 action_close_port
->setEnabled(false);
465 action_open_file
= new QAction(tr("Open File"), this);
466 action_open_file
->setStatusTip(tr("Opens file containing NMEA sentences"));
467 connect(action_open_file
, &QAction::triggered
, this, &MainWindow::on_open_file
);
470 void MainWindow::setup_ui()
472 QWidget
* center
= new QWidget(this);
473 center
->setMinimumWidth(1024);
474 center
->setMinimumHeight(600);
476 sentence_list
= new QListWidget(this);
477 sentence_list
->setSelectionMode(QAbstractItemView::SingleSelection
);
478 connect(sentence_list
, &QListWidget::itemSelectionChanged
, this,
479 &MainWindow::on_sentence_selection
);
481 sentence_desc
= new QLabel("", this);
482 sentence_desc
->setTextFormat(Qt::PlainText
);
483 sentence_desc
->setAlignment(Qt::AlignLeft
| Qt::AlignTop
);
485 QFont font
{"Monospace"};
486 font
.setStyleHint(QFont::TypeWriter
);
488 sentence_list
->setFont(font
);
489 sentence_desc
->setFont(font
);
491 port_name
= new QLineEdit(center
);
492 port_name
->setText("/dev/ttyUSB0");
493 cb_baudrate
= new QComboBox(center
);
494 cb_baudrate
->setEditable(false);
495 cb_baudrate
->addItems({"4800", "38400"});
497 auto l1
= new QHBoxLayout
;
498 l1
->addWidget(sentence_list
, 2);
499 l1
->addWidget(sentence_desc
, 1);
501 auto l0
= new QGridLayout
;
502 l0
->addWidget(new QLabel(tr("Port:"), this), 0, 0);
503 l0
->addWidget(port_name
, 0, 1);
504 l0
->addWidget(cb_baudrate
, 0, 2);
505 l0
->addLayout(l1
, 1, 0, 1, 3);
507 center
->setLayout(l0
);
508 setCentralWidget(center
);
511 void MainWindow::read_sentences_from_file(QString filename
)
513 QFile file
{filename
};
514 if (!file
.open(QFile::ReadOnly
)) {
515 QMessageBox::critical(this, "Error", "Unable to open file: " + filename
);
519 QTextStream
ifs(&file
);
521 QString s
= ifs
.readLine();
526 if (s
.startsWith("#"))
532 void MainWindow::open_file(QString filename
)
534 QFileInfo
file(filename
);
537 read_sentences_from_file(filename
);
540 void MainWindow::on_open_file()
543 = QFileDialog::getOpenFileName(this, tr("Open File"), ".", tr("Text Files (*.txt)"));
545 if (filename
.size() == 0)
548 sentence_list
->clear();
549 sentence_desc
->setText("");
551 read_sentences_from_file(filename
);
554 void MainWindow::add_item(QString raw_sentence
)
556 // if the list grows too large, remove a certain amount from
557 // the oldest entries before adding the new one.
559 if (sentence_list
->count() > 1000) {
560 for (int i
= 0; i
< 10; ++i
)
561 delete sentence_list
->takeItem(0);
563 sentence_list
->addItem(raw_sentence
);
566 void MainWindow::on_about()
568 QCoreApplication
* app
= QCoreApplication::instance();
569 QMessageBox::about(this, app
->applicationName(), app
->applicationName()
570 + ": example of Qt using marnav\n\nVersion: " + app
->applicationVersion()
571 + "\n\nSee file: LICENSE");
574 void MainWindow::on_about_qt()
576 QCoreApplication
* app
= QCoreApplication::instance();
577 QMessageBox::aboutQt(this, app
->applicationName());
580 void MainWindow::on_sentence_selection()
582 auto items
= sentence_list
->selectedItems();
586 auto item
= items
.front();
588 auto s
= marnav::nmea::make_sentence(item
->text().toStdString());
589 sentence_desc
->setText(QString
{"Tag: %1\nTalker: %2\n\n%3\n"}
590 .arg(s
->tag().c_str())
591 .arg(to_string(s
->get_talker()).c_str())
592 .arg(get_details(s
.get())));
593 } catch (marnav::nmea::unknown_sentence
&) {
594 sentence_desc
->setText("Unknown Sentence");
595 item
->setForeground(Qt::red
);
596 } catch (marnav::nmea::checksum_error
&) {
597 sentence_desc
->setText("Checksum Error");
598 item
->setForeground(Qt::red
);
599 } catch (std::invalid_argument
& error
) {
600 sentence_desc
->setText("Error: " + QString::fromStdString(error
.what()));
601 item
->setForeground(Qt::red
);
605 void MainWindow::on_open()
607 action_open_port
->setEnabled(false);
608 action_close_port
->setEnabled(true);
609 action_open_file
->setEnabled(false);
610 cb_baudrate
->setEnabled(false);
611 port_name
->setEnabled(false);
613 port
->setPortName(port_name
->text());
614 port
->setBaudRate(cb_baudrate
->currentText().toInt());
615 port
->setParity(QSerialPort::NoParity
);
616 port
->setDataBits(QSerialPort::Data8
);
617 port
->setStopBits(QSerialPort::OneStop
);
619 connect(port
, &QSerialPort::readyRead
, this, &MainWindow::on_data_ready
);
621 if (!port
->open(QIODevice::ReadOnly
)) {
623 QMessageBox::critical(this, "Error", "Unable to open port: " + port
->portName());
626 sentence_list
->clear();
627 sentence_desc
->setText("");
630 void MainWindow::on_close()
632 action_open_port
->setEnabled(true);
633 action_close_port
->setEnabled(true);
634 action_open_file
->setEnabled(true);
635 cb_baudrate
->setEnabled(true);
636 port_name
->setEnabled(true);
638 disconnect(port
, &QSerialPort::readyRead
, this, &MainWindow::on_data_ready
);
643 void MainWindow::on_data_ready()
647 auto rc
= port
->read(&raw
, sizeof(raw
));
649 // no more data for now
653 // an error has ocurred
662 add_item(received_data
.c_str());
663 received_data
.clear();
666 if (received_data
.size() > marnav::nmea::sentence::max_length
) {
667 // error ocurred, discard data
668 received_data
.clear();
670 received_data
+= raw
;