diff --git a/.gitignore b/.gitignore index 962d0b24d..0399f2c04 100644 --- a/.gitignore +++ b/.gitignore @@ -68,3 +68,4 @@ _deps # Clangd .cache +/snap/* diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 612c68618..377282ec2 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,13 @@ Changelog for package plotjuggler ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +3.8.6 (2024-01-09) +------------------ +* fix issue `#906 `_: support nanoseconds timestamp in csv +* fix issue `#904 `_: wring ROS odometry parsing +* add moving variance +* Contributors: Davide Faconti + 3.8.5 (2024-01-03) ------------------ * fix issue `#901 `_ diff --git a/CMakeLists.txt b/CMakeLists.txt index 12c7ca48e..013a17514 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.10.2) -PROJECT(plotjuggler LANGUAGES C CXX VERSION 3.8.5) +PROJECT(plotjuggler LANGUAGES C CXX VERSION 3.8.6) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/README.md b/README.md index ee080a159..61de8b567 100644 --- a/README.md +++ b/README.md @@ -114,22 +114,20 @@ Refer to the instructions in that repository if you want to compile PJ and its R This massive file will install a version of PlotJuggler that can work with both ROS1 and ROS2. -[![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg)](https://snapcraft.io/plotjuggler) +![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg) + +To install it in Ubuntu 22.04, with ROS2 support, run: ``` sudo snap install plotjuggler ``` -When launching you have two options available: +If you are still use ROS1 (Ubuntu 20.04), install instead: -- `plotjuggler.ros` to load the ROS1 plugins. -- `plotjuggler.ros2` to load the ROS2 plugins. +``` +sudo snap install plotjuggler-ros +``` -In addition, the command `plotjuggler` is an alias to `plotjuggler.ros`. -If you'd prefer to alias `plotjuggler.ros2` instead, -you can do so with the command `sudo snap set plotjuggler ros-plugin-version=2`. -Revert it simply replacing `2` with `1`. -Note that this also affect the desktop launcher. ## Compile from source diff --git a/package.xml b/package.xml index 39ac0590a..4fba45616 100644 --- a/package.xml +++ b/package.xml @@ -1,7 +1,7 @@ plotjuggler - 3.8.5 + 3.8.6 PlotJuggler: juggle with data Davide Faconti diff --git a/plotjuggler_app/plotwidget.cpp b/plotjuggler_app/plotwidget.cpp index 7a9311d4b..9eea88646 100644 --- a/plotjuggler_app/plotwidget.cpp +++ b/plotjuggler_app/plotwidget.cpp @@ -674,6 +674,10 @@ QDomElement PlotWidget::xmlSaveState(QDomDocument& doc) const { plot_el.setAttribute("style", "Steps"); } + else if (curveStyle() == PlotWidgetBase::STEPSINV) + { + plot_el.setAttribute("style", "StepsInv"); + } for (auto& it : curveList()) { @@ -877,6 +881,10 @@ bool PlotWidget::xmlLoadState(QDomElement& plot_widget, bool autozoom) { changeCurvesStyle(PlotWidgetBase::STEPS); } + else if (style == "StepsInv") + { + changeCurvesStyle(PlotWidgetBase::STEPSINV); + } } QString bg_data = plot_widget.attribute("background_data"); diff --git a/plotjuggler_app/plotwidget_editor.cpp b/plotjuggler_app/plotwidget_editor.cpp index f0971c35e..9358befd9 100644 --- a/plotjuggler_app/plotwidget_editor.cpp +++ b/plotjuggler_app/plotwidget_editor.cpp @@ -68,6 +68,10 @@ PlotwidgetEditor::PlotwidgetEditor(PlotWidget* plotwidget, QWidget* parent) { ui->radioSteps->setChecked(true); } + else if (_plotwidget->curveStyle() == PlotWidgetBase::STEPSINV) + { + ui->radioSteps->setChecked(true); + } else { ui->radioBoth->setChecked(true); @@ -320,6 +324,14 @@ void PlotwidgetEditor::on_radioSteps_toggled(bool checked) } } +void PlotwidgetEditor::on_radioStepsInv_toggled(bool checked) +{ + if (checked) + { + _plotwidget->changeCurvesStyle(PlotWidgetBase::STEPSINV); + } +} + void PlotwidgetEditor::on_checkBoxMax_toggled(bool checked) { ui->lineLimitMax->setEnabled(checked); diff --git a/plotjuggler_app/plotwidget_editor.h b/plotjuggler_app/plotwidget_editor.h index a28104824..6c1ac7cf2 100644 --- a/plotjuggler_app/plotwidget_editor.h +++ b/plotjuggler_app/plotwidget_editor.h @@ -86,6 +86,8 @@ private slots: void on_radioSteps_toggled(bool checked); + void on_radioStepsInv_toggled(bool checked); + private: Ui::PlotWidgetEditor* ui; diff --git a/plotjuggler_app/plotwidget_editor.ui b/plotjuggler_app/plotwidget_editor.ui index ad7860f30..cc49f2c34 100644 --- a/plotjuggler_app/plotwidget_editor.ui +++ b/plotjuggler_app/plotwidget_editor.ui @@ -344,7 +344,14 @@ - Steps + Steps (pre) + + + + + + + Step (post) diff --git a/plotjuggler_base/include/PlotJuggler/plotwidget_base.h b/plotjuggler_base/include/PlotJuggler/plotwidget_base.h index 4865ff17f..8b5b3fd88 100644 --- a/plotjuggler_base/include/PlotJuggler/plotwidget_base.h +++ b/plotjuggler_base/include/PlotJuggler/plotwidget_base.h @@ -33,7 +33,8 @@ class PlotWidgetBase : public QWidget DOTS, LINES_AND_DOTS, STICKS, - STEPS + STEPS, + STEPSINV }; struct CurveInfo diff --git a/plotjuggler_base/include/PlotJuggler/reactive_function.h b/plotjuggler_base/include/PlotJuggler/reactive_function.h index 2333b7893..601cfe43e 100644 --- a/plotjuggler_base/include/PlotJuggler/reactive_function.h +++ b/plotjuggler_base/include/PlotJuggler/reactive_function.h @@ -29,6 +29,8 @@ struct TimeseriesRef unsigned size() const; + void clear() const; + PJ::PlotData* _plot_data = nullptr; }; diff --git a/plotjuggler_base/src/plotwidget_base.cpp b/plotjuggler_base/src/plotwidget_base.cpp index b5ed59dfb..b5969d8b9 100644 --- a/plotjuggler_base/src/plotwidget_base.cpp +++ b/plotjuggler_base/src/plotwidget_base.cpp @@ -723,6 +723,11 @@ void PlotWidgetBase::setStyle(QwtPlotCurve* curve, CurveStyle style) break; case STEPS: curve->setStyle(QwtPlotCurve::Steps); + curve->setCurveAttribute(QwtPlotCurve::Inverted, false); + break; + case STEPSINV: + curve->setStyle(QwtPlotCurve::Steps); + curve->setCurveAttribute(QwtPlotCurve::Inverted, true); break; } } diff --git a/plotjuggler_base/src/reactive_function.cpp b/plotjuggler_base/src/reactive_function.cpp index d8915d046..d9c713a36 100644 --- a/plotjuggler_base/src/reactive_function.cpp +++ b/plotjuggler_base/src/reactive_function.cpp @@ -114,6 +114,7 @@ void ReactiveLuaFunction::prepareLua() _timeseries_ref["at"] = &TimeseriesRef::at; _timeseries_ref["set"] = &TimeseriesRef::set; _timeseries_ref["atTime"] = &TimeseriesRef::atTime; + _timeseries_ref["clear"] = &TimeseriesRef::clear; //--------------------------------------- _created_timeseries = _lua_engine.new_usertype("Timeseries"); @@ -194,6 +195,11 @@ unsigned TimeseriesRef::size() const return _plot_data->size(); } +void TimeseriesRef::clear() const +{ + _plot_data->clear(); +} + CreatedSeriesBase::CreatedSeriesBase(PlotDataMapRef* data_map, const std::string& name, bool timeseries) { diff --git a/plotjuggler_plugins/DataLoadCSV/dataload_csv.cpp b/plotjuggler_plugins/DataLoadCSV/dataload_csv.cpp index ec95349d4..9a67e103f 100644 --- a/plotjuggler_plugins/DataLoadCSV/dataload_csv.cpp +++ b/plotjuggler_plugins/DataLoadCSV/dataload_csv.cpp @@ -508,9 +508,51 @@ bool DataLoadCSV::readDataFromFile(FileLoadInfo* info, PlotDataMapRef& plot_data bool parse_date_format = _ui->checkBoxDateFormat->isChecked(); QString format_string = _ui->lineEditDateFormat->text(); + auto ParseTimestamp = [&](QString str, bool& is_number) { + QString str_trimmed = str.trimmed(); + double val = 0.0; + is_number = false; + // Support the case where the timestamp is in nanoseconds / microseconds + uint64_t ts = str_trimmed.toULong(&is_number); + uint64_t first_ts = 1400000000; //May 13, 2014 + uint64_t last_ts = 2000000000; // May 18, 2033 + if(is_number) + { + if(ts > first_ts*1e9 && ts < last_ts*1e9) { + // convert from nanoseconds to seconds + val = double(ts) * 1e-9; + } + else if(ts > first_ts*1e6 && ts < last_ts*1e6) { + // convert from nanoseconds to seconds + val = double(ts) * 1e-6; + } + } + // Try a double value (seconds) + if (!is_number) { + val = str_trimmed.toDouble(&is_number); + } + + // handle numbers with comma instead of point as decimal separator + if (!is_number) + { + static QLocale locale_with_comma(QLocale::German); + val = locale_with_comma.toDouble(str_trimmed, &is_number); + } + if (!is_number && parse_date_format && !format_string.isEmpty()) + { + QDateTime ts = QDateTime::fromString(str_trimmed, format_string); + is_number = ts.isValid(); + if (is_number) + { + val = ts.toMSecsSinceEpoch() / 1000.0; + } + } + return val; + }; + auto ParseNumber = [&](QString str, bool& is_number) { QString str_trimmed = str.trimmed(); - double val = str_trimmed.toDouble(&is_number); + double val = val = str_trimmed.toDouble(&is_number); // handle numbers with comma instead of point as decimal separator if (!is_number) { @@ -589,13 +631,13 @@ bool DataLoadCSV::readDataFromFile(FileLoadInfo* info, PlotDataMapRef& plot_data return false; } - double t = linecount; + double timestamp = linecount; if (time_index >= 0) { bool is_number = false; t_str = string_items[time_index]; - t = ParseNumber(t_str, is_number); + timestamp = ParseTimestamp(t_str, is_number); time_header_str = header_string_items[time_index]; if (!is_number) @@ -627,7 +669,7 @@ bool DataLoadCSV::readDataFromFile(FileLoadInfo* info, PlotDataMapRef& plot_data return false; } - if (prev_time > t && !sortRequired) + if (prev_time > timestamp && !sortRequired) { QMessageBox msgBox; QString timeName; @@ -671,7 +713,7 @@ bool DataLoadCSV::readDataFromFile(FileLoadInfo* info, PlotDataMapRef& plot_data } } - prev_time = t; + prev_time = timestamp; prev_t_str = t_str; } @@ -682,11 +724,11 @@ bool DataLoadCSV::readDataFromFile(FileLoadInfo* info, PlotDataMapRef& plot_data double y = ParseNumber(str, is_number); if (is_number) { - plots_vector[i]->pushBack({ t, y }); + plots_vector[i]->pushBack({ timestamp, y }); } else { - string_vector[i]->pushBack({ t, str.toStdString() }); + string_vector[i]->pushBack({ timestamp, str.toStdString() }); } } diff --git a/plotjuggler_plugins/ParserROS/ros_parser.cpp b/plotjuggler_plugins/ParserROS/ros_parser.cpp index 5dcc1a4e6..124fda319 100644 --- a/plotjuggler_plugins/ParserROS/ros_parser.cpp +++ b/plotjuggler_plugins/ParserROS/ros_parser.cpp @@ -203,7 +203,7 @@ void ParserROS::parseQuaternion(const std::string& prefix, double& timestamp) getSeries(prefix + "/x").pushBack({ timestamp, quat.x }); getSeries(prefix + "/y").pushBack({ timestamp, quat.y }); getSeries(prefix + "/z").pushBack({ timestamp, quat.z }); - getSeries(prefix + "/z").pushBack({ timestamp, quat.w }); + getSeries(prefix + "/w").pushBack({ timestamp, quat.w }); auto rpy = Msg::QuaternionToRPY(quat); getSeries(prefix + "/roll").pushBack({ timestamp, rpy.roll }); diff --git a/snap_core20/snapcraft.yaml b/snap_core20/snapcraft.yaml index f569b97fd..974fb9d88 100644 --- a/snap_core20/snapcraft.yaml +++ b/snap_core20/snapcraft.yaml @@ -1,4 +1,4 @@ -name: plotjuggler +name: plotjuggler-ros adopt-info: plotjuggler # parse metadata from the plotjuggler part summary: The timeseries visualization tool that you deserve description: | @@ -8,7 +8,7 @@ description: | The snap comes with only ROS 2 plugins. You can launch it with - $ plotjuggler + $ plotjuggler-ros issues: https://github.com/facontidavide/plotjuggler/issues source-code: https://github.com/facontidavide/plotjuggler @@ -28,7 +28,7 @@ package-repositories: url: http://packages.ros.org/ros/ubuntu apps: - plotjuggler: + plotjuggler-ros: command: usr/bin/launcher-plotjuggler-ros plugs: [network, network-bind, home, removable-media] extensions: [kde-neon] @@ -87,6 +87,8 @@ parts: - python3-vcstool - ros-noetic-ros-environment - ros-noetic-catkin + stage-packages: + - libpsm-infinipath1 override-pull: | if [ ! -d plotjuggler-ros-plugins ]; then diff --git a/snap_core22/local/launcher-plotjuggler-ros2 b/snap_core22/local/launcher-plotjuggler-ros2 index b39a73f54..695572fe4 100755 --- a/snap_core22/local/launcher-plotjuggler-ros2 +++ b/snap_core22/local/launcher-plotjuggler-ros2 @@ -1,5 +1,7 @@ #!/bin/bash +export LD_LIBRARY_PATH=$SNAP/opt/ros/humble/lib:$SNAP/opt/ros/snap/lib:$SNAP/usr/local/lib:$LD_LIBRARY_PATH + # Paths to ROS 2 plugins export PLUGIN_FOLDERS=$SNAP/opt/ros/snap/lib/plotjuggler_ros export AMENT_PREFIX_PATH=$SNAP/opt/ros/humble diff --git a/snap_core22/snapcraft.yaml b/snap_core22/snapcraft.yaml index 6b13d7426..7e2335cf6 100644 --- a/snap_core22/snapcraft.yaml +++ b/snap_core22/snapcraft.yaml @@ -6,7 +6,7 @@ description: | using an intuitive "drag and drop" interface. The snap comes with only ROS 2 plugins. - You can launch it with + You can launch it with: $ plotjuggler