├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── .gitmodules
├── FlightPlot.iml
├── FlightPlot.ipr
├── README.md
├── build.xml
├── flightplot.icns
├── generate_csv.sh
├── lib
├── AppleJavaExtensions.jar
├── annotations.jar
├── asm4-all.jar
├── forms_rt.jar
├── jarbundler-2.4.0.jar
├── javac2.jar
├── jcommon-1.0.17.jar
├── jdom.jar
├── jfreechart-1.0.14.jar
├── universalJavaApplicationStub
└── vecmath.jar
├── packaging
├── archlinux
│ └── PKGBUILD
└── ubuntu
│ └── FlightPlot
│ ├── DEBIAN
│ └── control
│ └── usr
│ └── share
│ ├── applications
│ └── flightplot.desktop
│ └── icons
│ └── 64x64
│ └── apps
│ └── flightplot.png
└── src
├── me
└── drton
│ └── flightplot
│ ├── AddProcessorDialog.form
│ ├── AddProcessorDialog.java
│ ├── ColorParamTableCellEditor.java
│ ├── ColorSupplier.java
│ ├── FieldsListDialog.form
│ ├── FieldsListDialog.java
│ ├── FlightPlot.form
│ ├── FlightPlot.java
│ ├── LogInfo.form
│ ├── LogInfo.java
│ ├── Marker.java
│ ├── MarkersList.java
│ ├── OSValidator.java
│ ├── ParamValueTableCellEditor.java
│ ├── ParamValueTableCellRenderer.java
│ ├── PlotExportDialog.form
│ ├── PlotExportDialog.java
│ ├── PlotItem.java
│ ├── PreferencesUtil.java
│ ├── Preset.java
│ ├── ProcessorPreset.java
│ ├── Series.java
│ ├── TaggedValueMarker.java
│ ├── XYPoint.java
│ ├── export
│ ├── AbstractTrackExporter.java
│ ├── AbstractTrackReader.java
│ ├── GPXTrackExporter.java
│ ├── KMLTrackExporter.java
│ ├── PX4TrackReader.java
│ ├── TrackExportDialog.form
│ ├── TrackExportDialog.java
│ ├── TrackExporter.java
│ ├── TrackExporterConfiguration.java
│ ├── TrackPoint.java
│ ├── TrackReader.java
│ ├── TrackReaderConfiguration.java
│ ├── TrackReaderFactory.java
│ └── ULogTrackReader.java
│ └── processors
│ ├── ATan2.java
│ ├── Abs.java
│ ├── Battery.java
│ ├── Derivative.java
│ ├── EulerFromQuaternion.java
│ ├── Expression.java
│ ├── GlobalPositionProjection.java
│ ├── Integral.java
│ ├── LandDetector.java
│ ├── NEDFromBodyProjection.java
│ ├── PlotProcessor.java
│ ├── PosPIDControlSimulator.java
│ ├── PosRatePIDControlSimulator.java
│ ├── PositionEstimator.java
│ ├── PositionEstimatorKF.java
│ ├── ProcessorsList.java
│ ├── Simple.java
│ ├── Text.java
│ └── tools
│ ├── LowPassFilter.java
│ └── PID.java
├── net
└── objecthunter
│ └── exp4j
│ ├── Expression.java
│ ├── ExpressionBuilder.java
│ ├── ValidationResult.java
│ ├── function
│ ├── Function.java
│ └── Functions.java
│ ├── operator
│ ├── Operator.java
│ └── Operators.java
│ ├── shuntingyard
│ └── ShuntingYard.java
│ └── tokenizer
│ ├── ArgumentSeparatorToken.java
│ ├── CloseParenthesesToken.java
│ ├── FunctionToken.java
│ ├── NumberToken.java
│ ├── OpenParenthesesToken.java
│ ├── OperatorToken.java
│ ├── Token.java
│ ├── Tokenizer.java
│ └── VariableToken.java
└── org
└── json
├── JSONArray.java
├── JSONException.java
├── JSONObject.java
├── JSONString.java
└── JSONTokener.java
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | branches: [ '*' ]
6 | pull_request:
7 | branches: [ master ]
8 | release:
9 | types: [ created ]
10 |
11 | jobs:
12 | build:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v3
16 | with:
17 | submodules: 'recursive'
18 | - name: Set up JDK
19 | uses: actions/setup-java@v3
20 | with:
21 | java-version: '8'
22 | distribution: 'temurin'
23 | - name: Build with Ant
24 | run: ant zip_artifacts
25 | - name: Upload artifacts
26 | uses: softprops/action-gh-release@v1
27 | if: ${{ github.event_name == 'release' }}
28 | with:
29 | files: |
30 | out/production/flightplot.jar.zip
31 | out/production/flightplot.app.zip
32 | env:
33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 | out
3 | /bin
4 | /.settings
5 | /.classpath
6 | /.project
7 | *.iws
8 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "jMAVlib"]
2 | path = jMAVlib
3 | url = https://github.com/PX4/jMAVlib.git
4 |
--------------------------------------------------------------------------------
/FlightPlot.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | FlightPlot
2 | ==========
3 |
4 | [](https://travis-ci.org/PX4/FlightPlot)
5 |
6 | Universal flight log plotter
7 |
8 | Releases can be found on [GitHub releases](https://github.com/PX4/FlightPlot/releases).
9 |
10 | Overview
11 | --------
12 |
13 | ### Supported formats:
14 | - PX4 log (.px4log, .bin)
15 | - APM log (.bin)
16 | - ULog (.ulg)
17 |
18 | ### Features:
19 | - Data processing: low pass filtering, scaling, shifting, derivative, integral, etc.
20 | - Track export in KML and GPS format
21 | - Saving plot as image
22 |
23 |
24 | Building from source
25 | --------------------
26 |
27 | Requirements:
28 | - Java 6 or newer (JDK, http://www.oracle.com/technetwork/java/javase/downloads/index.html)
29 | - ant
30 |
31 | Clone the repository. The `--recursive` flag is required to pull in the [jMAVlib](https://github.com/PX4/jMAVlib) submodule).
32 | ```
33 | git clone --recursive https://github.com/PX4/FlightPlot.git
34 | ```
35 |
36 | Build:
37 | ```
38 | cd FlightPlot
39 | ant
40 | ```
41 |
42 | If you want to create deb file for ubuntu, use gen_deb.
43 | ```
44 | cd FlightPlot
45 | ant gen_deb
46 | sudo dpkg -i out/production/FlightPlot.deb
47 | ```
48 |
49 | Run:
50 | ```
51 | java -jar out/production/flightplot.jar
52 | ```
53 |
--------------------------------------------------------------------------------
/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/flightplot.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PX4/FlightPlot/3955296e32f82e76c4e5bd5b1c724b2162039880/flightplot.icns
--------------------------------------------------------------------------------
/generate_csv.sh:
--------------------------------------------------------------------------------
1 | java -classpath out/production/flightplot.jar me/drton/jmavlib/log/ulog/ULogReader
2 |
3 |
--------------------------------------------------------------------------------
/lib/AppleJavaExtensions.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PX4/FlightPlot/3955296e32f82e76c4e5bd5b1c724b2162039880/lib/AppleJavaExtensions.jar
--------------------------------------------------------------------------------
/lib/annotations.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PX4/FlightPlot/3955296e32f82e76c4e5bd5b1c724b2162039880/lib/annotations.jar
--------------------------------------------------------------------------------
/lib/asm4-all.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PX4/FlightPlot/3955296e32f82e76c4e5bd5b1c724b2162039880/lib/asm4-all.jar
--------------------------------------------------------------------------------
/lib/forms_rt.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PX4/FlightPlot/3955296e32f82e76c4e5bd5b1c724b2162039880/lib/forms_rt.jar
--------------------------------------------------------------------------------
/lib/jarbundler-2.4.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PX4/FlightPlot/3955296e32f82e76c4e5bd5b1c724b2162039880/lib/jarbundler-2.4.0.jar
--------------------------------------------------------------------------------
/lib/javac2.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PX4/FlightPlot/3955296e32f82e76c4e5bd5b1c724b2162039880/lib/javac2.jar
--------------------------------------------------------------------------------
/lib/jcommon-1.0.17.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PX4/FlightPlot/3955296e32f82e76c4e5bd5b1c724b2162039880/lib/jcommon-1.0.17.jar
--------------------------------------------------------------------------------
/lib/jdom.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PX4/FlightPlot/3955296e32f82e76c4e5bd5b1c724b2162039880/lib/jdom.jar
--------------------------------------------------------------------------------
/lib/jfreechart-1.0.14.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PX4/FlightPlot/3955296e32f82e76c4e5bd5b1c724b2162039880/lib/jfreechart-1.0.14.jar
--------------------------------------------------------------------------------
/lib/vecmath.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PX4/FlightPlot/3955296e32f82e76c4e5bd5b1c724b2162039880/lib/vecmath.jar
--------------------------------------------------------------------------------
/packaging/archlinux/PKGBUILD:
--------------------------------------------------------------------------------
1 | # Maintainer: Thomas Gubler
2 | _libname=flightplot
3 | pkgname=java-${_libname}-git
4 | pkgver=20150101
5 | pkgrel=1
6 | pkgdesc="PX4/APM flight log plotter"
7 | arch=(any)
8 | url="https://pixhawk.org/dev/flightplot"
9 | license=('unknown')
10 | depends=('java-runtime')
11 | makedepends=('apache-ant')
12 | optdepends=()
13 | provides=('java-flightplot')
14 | conflicts=('java-flightplot')
15 | options=(!emptydirs)
16 | md5sums=('SKIP')
17 |
18 | _gitroot="https://github.com/DrTon/FlightPlot.git"
19 | _gitname="FlightPlot"
20 | source=(git+$_gitroot)
21 |
22 | prepare() {
23 | cd "$srcdir/$_gitname"
24 | git submodule init
25 | git submodule update
26 | }
27 |
28 | build() {
29 | cd "$srcdir/$_gitname"
30 | ant flightplot
31 |
32 | printf "#!/bin/sh\nexec /usr/bin/java -jar '/usr/share/java/${_libname}/flightplot.jar' '$@'" > archlinux_start_script
33 | }
34 |
35 | package() {
36 | cd "$srcdir/$_gitname"
37 | install -Dm755 out/production/flightplot.jar ${pkgdir}/usr/share/java/${_libname}/${_libname}.jar
38 | install -Dm755 archlinux_start_script ${pkgdir}/usr/bin/${_libname}
39 | }
40 |
41 | pkgver() {
42 | cd "$pkgname"
43 | printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
44 | }
45 |
46 | # vim:set ts=2 sw=2 et:
47 |
--------------------------------------------------------------------------------
/packaging/ubuntu/FlightPlot/DEBIAN/control:
--------------------------------------------------------------------------------
1 | Package: FlightPlot
2 | Version: 0.2.20
3 | Section: devel
4 | Priority: optional
5 | Architecture: all
6 | Depends: build-essential,
7 | cmake
8 | Maintainer: Anton Babushkin , SungTae Moon
9 | Homepage: https://github.com/DrTon/FlightPlot
10 | Description: Universal flight log plotter
11 |
12 |
--------------------------------------------------------------------------------
/packaging/ubuntu/FlightPlot/usr/share/applications/flightplot.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Encoding=UTF-8
3 | Name=FlightPlot
4 | Type=Application
5 | Exec=java -jar /usr/local/bin/flightplot.jar
6 | Terminal=false
7 | Icon=/usr/share/icons/64x64/apps/flightplot.png
8 | Comment=FlightPlot
9 | Categories=Utility;Application;
10 | Name=FlightPlot
11 |
--------------------------------------------------------------------------------
/packaging/ubuntu/FlightPlot/usr/share/icons/64x64/apps/flightplot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PX4/FlightPlot/3955296e32f82e76c4e5bd5b1c724b2162039880/packaging/ubuntu/FlightPlot/usr/share/icons/64x64/apps/flightplot.png
--------------------------------------------------------------------------------
/src/me/drton/flightplot/AddProcessorDialog.form:
--------------------------------------------------------------------------------
1 |
2 |
102 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/AddProcessorDialog.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot;
2 |
3 | import me.drton.flightplot.processors.PlotProcessor;
4 |
5 | import javax.swing.*;
6 | import java.awt.event.*;
7 |
8 | public class AddProcessorDialog extends JDialog {
9 | private JPanel contentPane;
10 | private JButton buttonOK;
11 | private JButton buttonCancel;
12 | private JTextField titleField;
13 | private JList processorTypesList;
14 | private DefaultListModel processorTypesListModel;
15 | private String[] processorTypes;
16 |
17 | private ProcessorPreset origProcessorPreset = null;
18 | private Runnable callback;
19 |
20 | public AddProcessorDialog(String[] processorTypes) {
21 | this.processorTypes = processorTypes;
22 | //this.processorTypes = processorTypes;
23 | setContentPane(contentPane);
24 | setModal(true);
25 | setTitle("Add Processor");
26 | getRootPane().setDefaultButton(buttonOK);
27 | buttonOK.addActionListener(new ActionListener() {
28 | public void actionPerformed(ActionEvent e) {
29 | onOK();
30 | }
31 | });
32 | buttonCancel.addActionListener(new ActionListener() {
33 | public void actionPerformed(ActionEvent e) {
34 | onCancel();
35 | }
36 | });
37 | // call onCancel() when cross is clicked
38 | setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
39 | addWindowListener(new WindowAdapter() {
40 | public void windowClosing(WindowEvent e) {
41 | onCancel();
42 | }
43 | });
44 | // call onCancel() on ESCAPE
45 | contentPane.registerKeyboardAction(new ActionListener() {
46 | public void actionPerformed(ActionEvent e) {
47 | onCancel();
48 | }
49 | }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
50 | }
51 |
52 | public String getProcessorTitle() {
53 | return titleField.getText();
54 | }
55 |
56 | public ProcessorPreset getOrigProcessorPreset() {
57 | return origProcessorPreset;
58 | }
59 |
60 | public String getProcessorType() {
61 | return (String) processorTypesList.getSelectedValue();
62 | }
63 |
64 | public void display(Runnable callback, ProcessorPreset processorPreset) {
65 | if (processorTypesListModel.size() == 0) {
66 | for (String processorType : processorTypes) {
67 | processorTypesListModel.addElement(processorType);
68 | }
69 | processorTypesList.setSelectedValue("Simple", true);
70 | }
71 | this.callback = callback;
72 | if (processorPreset != null) {
73 | origProcessorPreset = processorPreset;
74 | titleField.setText(processorPreset.getTitle());
75 | processorTypesList.setSelectedValue(processorPreset.getProcessorType(), true);
76 | } else {
77 | origProcessorPreset = null;
78 | titleField.setText("");
79 | }
80 | titleField.requestFocus();
81 | this.setVisible(true);
82 | }
83 |
84 | private void onOK() {
85 | setVisible(false);
86 | callback.run();
87 | }
88 |
89 | private void onCancel() {
90 | setVisible(false);
91 | }
92 |
93 | private void createUIComponents() {
94 | processorTypesListModel = new DefaultListModel();
95 | processorTypesList = new JList(processorTypesListModel);
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/ColorParamTableCellEditor.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot;
2 |
3 | import javax.swing.*;
4 | import javax.swing.table.TableCellEditor;
5 | import java.awt.*;
6 |
7 | /**
8 | * Created by ton on 13.03.15.
9 | */
10 | class ColorParamTableCellEditor extends AbstractCellEditor implements TableCellEditor {
11 | private ColorSupplier colorSupplier;
12 | private Color color;
13 | private JComboBox select;
14 |
15 | public ColorParamTableCellEditor(ColorSupplier colorSupplier) {
16 | this.colorSupplier = colorSupplier;
17 | select = new JComboBox();
18 | select.setRenderer(new ColorCellRenderer());
19 | for (Paint paint : colorSupplier.getPaintSequence()) {
20 | select.addItem(paint);
21 | }
22 | }
23 |
24 | public JComboBox getComponent() {
25 | return select;
26 | }
27 |
28 | @Override
29 | public Object getCellEditorValue() {
30 | return colorSupplier.getPaint(select.getSelectedIndex());
31 | }
32 |
33 | @Override
34 | public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
35 | color = (Color) value;
36 | select.setSelectedItem(color);
37 | return select;
38 | }
39 |
40 | private class ColorCellRenderer extends JLabel implements ListCellRenderer {
41 | boolean setBg = false;
42 |
43 | public ColorCellRenderer() {
44 | setOpaque(true);
45 | setPreferredSize(new Dimension(0, 15));
46 | }
47 |
48 | @Override
49 | public void setBackground(Color bg) {
50 | if (!setBg) {
51 | return;
52 | }
53 | super.setBackground(bg);
54 | }
55 |
56 | public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
57 | setBg = true;
58 | setText("");
59 | setBackground((Color) value);
60 | setBorder(BorderFactory.createEmptyBorder());
61 | setBg = false;
62 |
63 | if (isSelected) {
64 | setBorder(BorderFactory.createLineBorder(Color.white, 2));
65 | }
66 |
67 | return this;
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/ColorSupplier.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot;
2 |
3 | import java.awt.*;
4 |
5 | /**
6 | * Created by ada on 22.12.14.
7 | */
8 | public class ColorSupplier {
9 | private Color[] paintSequence;
10 | private int[] paintUsage;
11 |
12 | {
13 | paintSequence = new Color[]{
14 | Color.RED,
15 | Color.GREEN,
16 | Color.BLUE,
17 | Color.CYAN,
18 | Color.MAGENTA,
19 | Color.BLACK,
20 | Color.LIGHT_GRAY,
21 | Color.ORANGE,
22 | Color.RED.darker(),
23 | Color.GREEN.darker(),
24 | Color.BLUE.darker(),
25 | Color.CYAN.darker(),
26 | Color.MAGENTA.darker(),
27 | Color.ORANGE.darker(),
28 | };
29 |
30 | paintUsage = new int[paintSequence.length];
31 | }
32 |
33 | public Color[] getPaintSequence() {
34 | return paintSequence;
35 | }
36 |
37 | public Color getPaint(int idx) {
38 | return paintSequence[idx];
39 | }
40 |
41 | public void resetColorsUsed() {
42 | for (int i = 0; i < paintSequence.length; i++) {
43 | paintUsage[i] = 0;
44 | }
45 | }
46 |
47 | public void markColorUsed(Color color) {
48 | for (int i = 0; i < paintSequence.length; i++) {
49 | if (color.equals(paintSequence[i])) {
50 | markColorUsed(i);
51 | }
52 | }
53 | }
54 |
55 | public void markColorUsed(int color_idx) {
56 | paintUsage[color_idx]++;
57 | }
58 |
59 | /**
60 | * Select color with minimal usage
61 | *
62 | * @param field
63 | * @return
64 | */
65 | public Color getNextColor(String field) {
66 | int minUsage = -1;
67 | int color_idx = 0;
68 | for (int i = 0; i < paintSequence.length; i++) {
69 | if (paintUsage[i] < minUsage || minUsage < 0) {
70 | minUsage = paintUsage[i];
71 | color_idx = i;
72 | }
73 | if (minUsage == 0) {
74 | markColorUsed(color_idx);
75 | break;
76 | }
77 | }
78 | return paintSequence[color_idx];
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/FieldsListDialog.form:
--------------------------------------------------------------------------------
1 |
2 |
98 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/FieldsListDialog.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot;
2 |
3 | import java.awt.event.ActionEvent;
4 | import java.awt.event.ActionListener;
5 | import java.awt.event.KeyEvent;
6 | import java.awt.event.MouseAdapter;
7 | import java.awt.event.MouseEvent;
8 | import java.awt.event.WindowAdapter;
9 | import java.awt.event.WindowEvent;
10 | import java.util.ArrayList;
11 | import java.util.Collections;
12 | import java.util.List;
13 | import java.util.Map;
14 | import java.util.regex.Pattern;
15 |
16 | import javax.swing.JButton;
17 | import javax.swing.JComponent;
18 | import javax.swing.JDialog;
19 | import javax.swing.JPanel;
20 | import javax.swing.JTable;
21 | import javax.swing.JTextField;
22 | import javax.swing.KeyStroke;
23 | import javax.swing.RowFilter;
24 | import javax.swing.table.DefaultTableModel;
25 | import javax.swing.table.TableRowSorter;
26 | import javax.swing.event.DocumentListener;
27 | import javax.swing.event.DocumentEvent;
28 |
29 | public class FieldsListDialog extends JDialog {
30 | private JPanel contentPane;
31 | private JButton buttonAdd;
32 | private JTable fieldsTable;
33 | private JButton buttonClose;
34 | private JTextField textSearch;
35 | private DefaultTableModel fieldsTableModel;
36 | private TableRowSorter sorter;
37 |
38 | public FieldsListDialog(final Runnable callbackAdd) {
39 | setContentPane(contentPane);
40 | setModal(false);
41 | setTitle("Fields List");
42 | getRootPane().setDefaultButton(buttonAdd);
43 | buttonAdd.addActionListener(new ActionListener() {
44 | @Override
45 | public void actionPerformed(ActionEvent e) {
46 | callbackAdd.run();
47 | }
48 | });
49 | buttonClose.addActionListener(new ActionListener() {
50 | public void actionPerformed(ActionEvent e) {
51 | onClose();
52 | }
53 | });
54 | // call onClose() when cross is clicked
55 | setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
56 | addWindowListener(new WindowAdapter() {
57 | public void windowClosing(WindowEvent e) {
58 | onClose();
59 | }
60 | });
61 | // call onClose() on ESCAPE
62 | contentPane.registerKeyboardAction(new ActionListener() {
63 | public void actionPerformed(ActionEvent e) {
64 | onClose();
65 | }
66 | }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
67 |
68 | fieldsTable.addMouseListener(new MouseAdapter() {
69 | public void mousePressed(MouseEvent me) {
70 | if (me.getClickCount() == 2) {
71 | callbackAdd.run();
72 | }
73 | }
74 | });
75 | textSearch.getDocument().addDocumentListener(new DocumentListener() {
76 | public void changedUpdate(DocumentEvent e) {
77 | filterFields(textSearch.getText());
78 | }
79 | public void removeUpdate(DocumentEvent e) {
80 | filterFields(textSearch.getText());
81 | }
82 | public void insertUpdate(DocumentEvent e) {
83 | filterFields(textSearch.getText());
84 | }
85 | });
86 | }
87 |
88 | private void onClose() {
89 | setVisible(false);
90 | }
91 |
92 | @Override
93 | public void setVisible(boolean value) {
94 | super.setVisible(value);
95 | // Focus search input on showing
96 | textSearch.requestFocus();
97 | }
98 |
99 | public void setFieldsList(Map fields) {
100 | while (fieldsTableModel.getRowCount() > 0) {
101 | fieldsTableModel.removeRow(0);
102 | }
103 | List fieldsList = new ArrayList(fields.keySet());
104 | Collections.sort(fieldsList);
105 | for (String field : fieldsList) {
106 | fieldsTableModel.addRow(new Object[]{field, fields.get(field)});
107 | }
108 | }
109 |
110 | private void filterFields(String str) {
111 | RowFilter rf = RowFilter.regexFilter("(?i)" + Pattern.quote(str), 0);
112 | sorter.setRowFilter(rf);
113 | }
114 |
115 | public List getSelectedFields() {
116 | List selectedFields = new ArrayList();
117 | for (int i : fieldsTable.getSelectedRows()) {
118 | selectedFields.add((String) fieldsTable.getValueAt(i, 0));
119 | }
120 | return selectedFields;
121 | }
122 |
123 | private void createUIComponents() {
124 | // Fields table
125 | fieldsTableModel = new DefaultTableModel() {
126 | @Override
127 | public boolean isCellEditable(int row, int col) {
128 | return false;
129 | }
130 | };
131 | fieldsTableModel.addColumn("Field");
132 | fieldsTableModel.addColumn("Type");
133 | fieldsTable = new JTable(fieldsTableModel);
134 | sorter = new TableRowSorter(fieldsTableModel);
135 | fieldsTable.setRowSorter(sorter);
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/LogInfo.form:
--------------------------------------------------------------------------------
1 |
2 |
73 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/LogInfo.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot;
2 |
3 | import me.drton.jmavlib.log.LogReader;
4 |
5 | import javax.swing.*;
6 | import javax.swing.table.DefaultTableModel;
7 | import java.text.DateFormat;
8 | import java.text.SimpleDateFormat;
9 | import java.util.*;
10 |
11 | /**
12 | * User: ton Date: 27.10.13 Time: 17:45
13 | */
14 | public class LogInfo {
15 | private JFrame mainFrame;
16 | private JPanel mainPanel;
17 | private JTable infoTable;
18 | private DefaultTableModel infoTableModel;
19 | private JTable parametersTable;
20 | private DefaultTableModel parametersTableModel;
21 | private DateFormat dateFormat;
22 |
23 | public LogInfo() {
24 | mainFrame = new JFrame("Log Info");
25 | mainFrame.setContentPane(mainPanel);
26 | mainFrame.pack();
27 | dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
28 | dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
29 | }
30 |
31 | public JFrame getFrame() {
32 | return mainFrame;
33 | }
34 |
35 | public void setVisible(boolean visible) {
36 | mainFrame.setVisible(visible);
37 | }
38 |
39 | public void updateInfo(LogReader logReader) {
40 | while (infoTableModel.getRowCount() > 0) {
41 | infoTableModel.removeRow(0);
42 | }
43 | while (parametersTableModel.getRowCount() > 0) {
44 | parametersTableModel.removeRow(0);
45 | }
46 | if (logReader != null) {
47 | infoTableModel.addRow(new Object[]{"Format", logReader.getFormat()});
48 | infoTableModel.addRow(new Object[]{"System", logReader.getSystemName()});
49 | infoTableModel.addRow(new Object[]{
50 | "Length, s", String.format(Locale.ROOT, "%.3f", logReader.getSizeMicroseconds() * 1e-6)});
51 | String startTimeStr = "";
52 | if (logReader.getUTCTimeReferenceMicroseconds() > 0) {
53 | startTimeStr = dateFormat.format(
54 | new Date((logReader.getStartMicroseconds() + logReader.getUTCTimeReferenceMicroseconds()) / 1000)) + " UTC";
55 | }
56 | infoTableModel.addRow(new Object[]{
57 | "Start Time", startTimeStr});
58 | infoTableModel.addRow(new Object[]{"Updates count", logReader.getSizeUpdates()});
59 | infoTableModel.addRow(new Object[]{"Errors", logReader.getErrors().size()});
60 | Map ver = logReader.getVersion();
61 | infoTableModel.addRow(new Object[]{"Hardware Version", ver.get("HW")});
62 | infoTableModel.addRow(new Object[]{"Firmware Version", ver.get("FW")});
63 | Map parameters = logReader.getParameters();
64 | List keys = new ArrayList(parameters.keySet());
65 | Collections.sort(keys);
66 | for (String key : keys) {
67 | parametersTableModel.addRow(new Object[]{key, parameters.get(key).toString()});
68 | }
69 | }
70 | }
71 |
72 | private void createUIComponents() {
73 | // Info table
74 | infoTableModel = new DefaultTableModel() {
75 | @Override
76 | public boolean isCellEditable(int row, int col) {
77 | return false;
78 | }
79 | };
80 | infoTableModel.addColumn("Property");
81 | infoTableModel.addColumn("Value");
82 | infoTable = new JTable(infoTableModel);
83 | // Parameters table
84 | parametersTableModel = new DefaultTableModel() {
85 | @Override
86 | public boolean isCellEditable(int row, int col) {
87 | return false;
88 | }
89 | };
90 | parametersTableModel.addColumn("Parameter");
91 | parametersTableModel.addColumn("Value");
92 | parametersTable = new JTable(parametersTableModel);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/Marker.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot;
2 |
3 | /**
4 | * Created by ton on 29.09.15.
5 | */
6 | public class Marker {
7 | public final double x;
8 | public final String label;
9 |
10 | public Marker(double x, String label) {
11 | this.x = x;
12 | this.label = label;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/MarkersList.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot;
2 |
3 | import me.drton.flightplot.Marker;
4 |
5 | import java.util.ArrayList;
6 |
7 | /**
8 | * Created by ton on 29.09.15.
9 | */
10 | public class MarkersList extends ArrayList implements PlotItem {
11 | private final String title;
12 |
13 | public MarkersList(String title) {
14 | this.title = title;
15 | }
16 |
17 | @Override
18 | public String getTitle() {
19 | return title;
20 | }
21 |
22 | public String getFullTitle(String processorTitle) {
23 | return processorTitle + (title.isEmpty() ? "" : (":" + title));
24 | }
25 |
26 | public void addMarker(double time, String label) {
27 | add(new Marker(time, label));
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/OSValidator.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot;
2 |
3 | /**
4 | * User: ton Date: 22.06.13 Time: 11:40
5 | */
6 | public class OSValidator {
7 |
8 | private static String OS = System.getProperty("os.name").toLowerCase();
9 |
10 | public static void main(String[] args) {
11 | System.out.println(OS);
12 | if (isWindows()) {
13 | System.out.println("This is Windows");
14 | } else if (isMac()) {
15 | System.out.println("This is Mac");
16 | } else if (isUnix()) {
17 | System.out.println("This is Unix or Linux");
18 | } else if (isSolaris()) {
19 | System.out.println("This is Solaris");
20 | } else {
21 | System.out.println("Your OS is not support!!");
22 | }
23 | }
24 |
25 | public static boolean isWindows() {
26 | return (OS.contains("win"));
27 | }
28 |
29 | public static boolean isMac() {
30 | return (OS.contains("mac"));
31 | }
32 |
33 | public static boolean isUnix() {
34 | return (OS.contains("nix") || OS.contains("nux") || OS.contains("aix"));
35 | }
36 |
37 | public static boolean isSolaris() {
38 | return (OS.contains("sunos"));
39 | }
40 | }
--------------------------------------------------------------------------------
/src/me/drton/flightplot/ParamValueTableCellEditor.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot;
2 |
3 | import javax.swing.*;
4 | import javax.swing.table.TableCellEditor;
5 | import java.awt.*;
6 | import java.awt.event.ActionEvent;
7 | import java.awt.event.ActionListener;
8 | import java.awt.event.MouseEvent;
9 | import java.util.EventObject;
10 |
11 | /**
12 | * Created by ton on 13.03.15.
13 | */
14 | class ParamValueTableCellEditor extends AbstractCellEditor implements TableCellEditor {
15 | private FlightPlot app;
16 | private TableCellEditor editor;
17 |
18 | public ParamValueTableCellEditor(FlightPlot app) {
19 | this.app = app;
20 | }
21 |
22 | public boolean isCellEditable(EventObject anEvent) {
23 | if (anEvent instanceof MouseEvent) {
24 | return ((MouseEvent)anEvent).getClickCount() >= 2;
25 | }
26 | return true;
27 | }
28 |
29 | @Override
30 | public Object getCellEditorValue() {
31 | return editor != null ? editor.getCellEditorValue() : null;
32 | }
33 |
34 | @Override
35 | public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
36 | app.setEditingProcessor();
37 | if (value instanceof Color) {
38 | editor = new ColorParamTableCellEditor(app.getColorSupplier());
39 | ((ColorParamTableCellEditor) editor).getComponent().addActionListener(new ActionDelegate());
40 | } else if (value instanceof String) {
41 | JTextField textField = new JTextField();
42 | textField.setFont(table.getFont());
43 | textField.setBorder(BorderFactory.createLineBorder(Color.BLACK));
44 | editor = new DefaultCellEditor(textField);
45 | ((JTextField) ((DefaultCellEditor) editor).getComponent()).addActionListener(new ActionDelegate());
46 | }
47 |
48 | return editor.getTableCellEditorComponent(table, value, isSelected, row, column);
49 | }
50 |
51 | private class ActionDelegate implements ActionListener {
52 | @Override
53 | public void actionPerformed(ActionEvent actionEvent) {
54 | ParamValueTableCellEditor.this.stopCellEditing();
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/ParamValueTableCellRenderer.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot;
2 |
3 | import javax.swing.*;
4 | import javax.swing.table.DefaultTableCellRenderer;
5 | import javax.swing.table.TableCellRenderer;
6 | import java.awt.*;
7 |
8 | /**
9 | * Created by ton on 13.03.15.
10 | */
11 | class ParamValueTableCellRenderer extends JLabel implements TableCellRenderer {
12 | private DefaultTableCellRenderer defaultTableCellRenderer = new DefaultTableCellRenderer();
13 |
14 | public ParamValueTableCellRenderer() {
15 | setOpaque(true);
16 | }
17 |
18 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected
19 | , boolean hasFocus, int row, int column) {
20 | if (value instanceof Color) {
21 | setBackground((Color) value);
22 | } else {
23 | return defaultTableCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
24 | }
25 | return this;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/PlotExportDialog.form:
--------------------------------------------------------------------------------
1 |
2 |
140 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/PlotExportDialog.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot;
2 |
3 | import javax.imageio.IIOImage;
4 | import javax.imageio.ImageIO;
5 | import javax.imageio.ImageWriteParam;
6 | import javax.imageio.ImageWriter;
7 | import javax.imageio.stream.FileImageOutputStream;
8 | import javax.imageio.stream.ImageOutputStream;
9 | import javax.swing.*;
10 | import javax.swing.filechooser.FileNameExtensionFilter;
11 | import java.awt.*;
12 | import java.awt.event.*;
13 | import java.awt.geom.AffineTransform;
14 | import java.awt.geom.Rectangle2D;
15 | import java.awt.image.BufferedImage;
16 | import java.io.File;
17 | import java.util.prefs.Preferences;
18 |
19 | public class PlotExportDialog extends JDialog {
20 | private static final String DIALOG_SETTING = "PlotExportDialog";
21 | private static final String LAST_EXPORT_DIRECTORY_SETTING = "LastExportDirectory";
22 |
23 | private JPanel contentPane;
24 | private JButton buttonExport;
25 | private JButton buttonClose;
26 | private JTextField widthField;
27 | private JTextField heightField;
28 | private JComboBox formatComboBox;
29 | private JTextField scaleField;
30 | private FlightPlot app;
31 | private File lastExportDirectory;
32 |
33 | public PlotExportDialog(FlightPlot app) {
34 | this.app = app;
35 | setContentPane(contentPane);
36 | setModal(true);
37 | getRootPane().setDefaultButton(buttonExport);
38 |
39 | buttonExport.addActionListener(new ActionListener() {
40 | public void actionPerformed(ActionEvent e) {
41 | onOK();
42 | }
43 | });
44 |
45 | buttonClose.addActionListener(new ActionListener() {
46 | public void actionPerformed(ActionEvent e) {
47 | onClose();
48 | }
49 | });
50 |
51 | // call onClose() when cross is clicked
52 | setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
53 | addWindowListener(new WindowAdapter() {
54 | public void windowClosing(WindowEvent e) {
55 | onClose();
56 | }
57 | });
58 |
59 | // call onClose() on ESCAPE
60 | contentPane.registerKeyboardAction(new ActionListener() {
61 | public void actionPerformed(ActionEvent e) {
62 | onClose();
63 | }
64 | }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
65 | pack();
66 | }
67 |
68 | private void onOK() {
69 | String format = ((String) formatComboBox.getSelectedItem()).toLowerCase();
70 | JFileChooser fc = new JFileChooser();
71 | if (lastExportDirectory != null) {
72 | fc.setCurrentDirectory(lastExportDirectory);
73 | }
74 | FileNameExtensionFilter extensionFilter = new FileNameExtensionFilter(format.toUpperCase() + " Image (*." + format + ")", format);
75 | fc.setFileFilter(extensionFilter);
76 | fc.setDialogTitle("Export Plot");
77 | int returnVal = fc.showDialog(null, "Export Plot");
78 | if (returnVal == JFileChooser.APPROVE_OPTION) {
79 | lastExportDirectory = fc.getCurrentDirectory();
80 | String fileName = fc.getSelectedFile().toString();
81 | if (extensionFilter == fc.getFileFilter() && !fileName.toLowerCase().endsWith("." + format)) {
82 | fileName += "." + format;
83 | }
84 | try {
85 | int width = Integer.parseInt(widthField.getText());
86 | int height = Integer.parseInt(heightField.getText());
87 |
88 | BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
89 | Graphics2D g2 = img.createGraphics();
90 | double scale = Double.parseDouble(scaleField.getText());
91 | AffineTransform st = AffineTransform.getScaleInstance(scale, scale);
92 | g2.transform(st);
93 | app.getChart().draw(g2, new Rectangle2D.Double(0.0D, 0.0D, width / scale, height / scale), null, null);
94 | g2.dispose();
95 |
96 | ImageWriter imgWriter = ImageIO.getImageWritersByFormatName(format).next();
97 | ImageWriteParam imgWriteParam = imgWriter.getDefaultWriteParam();
98 | if ("jpg".equals(format)) {
99 | imgWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
100 | imgWriteParam.setCompressionQuality(1.0f);
101 | }
102 | ImageOutputStream outputStream = new FileImageOutputStream(new File(fileName));
103 | imgWriter.setOutput(outputStream);
104 | IIOImage outputImage = new IIOImage(img, null, null);
105 | imgWriter.write(null, outputImage, imgWriteParam);
106 | imgWriter.dispose();
107 |
108 | app.setStatus(String.format("Exported to \"%s\"", fileName));
109 |
110 | } catch (Exception e) {
111 | app.setStatus("Error: " + e);
112 | e.printStackTrace();
113 | }
114 | }
115 | dispose();
116 | }
117 |
118 | private void onClose() {
119 | dispose();
120 | }
121 |
122 | public void savePreferences(Preferences preferences) {
123 | PreferencesUtil.saveWindowPreferences(this, preferences.node(DIALOG_SETTING));
124 | if (lastExportDirectory != null) {
125 | preferences.put(LAST_EXPORT_DIRECTORY_SETTING, lastExportDirectory.getAbsolutePath());
126 | }
127 | }
128 |
129 | public void loadPreferences(Preferences preferences) {
130 | PreferencesUtil.loadWindowPreferences(this, preferences.node(DIALOG_SETTING), -1, -1);
131 | String lastExportDirectoryPath = preferences.get(LAST_EXPORT_DIRECTORY_SETTING, null);
132 | if (null != lastExportDirectoryPath) {
133 | lastExportDirectory = new File(lastExportDirectoryPath);
134 | }
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/PlotItem.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot;
2 |
3 | /**
4 | * Created by ton on 29.09.15.
5 | */
6 | public interface PlotItem {
7 | String getTitle();
8 | }
9 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/PreferencesUtil.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot;
2 |
3 | import java.awt.*;
4 | import java.util.prefs.Preferences;
5 |
6 | /**
7 | * Created by ada on 25.01.14.
8 | */
9 | public class PreferencesUtil {
10 |
11 | public static void saveWindowPreferences(Component window, Preferences windowPreferences) {
12 | Dimension size = window.getSize();
13 | windowPreferences.putInt("Width", size.width);
14 | windowPreferences.putInt("Height", size.height);
15 | Point location = window.getLocation();
16 | windowPreferences.putInt("X", location.x);
17 | windowPreferences.putInt("Y", location.y);
18 | }
19 |
20 | public static void loadWindowPreferences(Component window, Preferences windowPreferences, int defaultWidth,
21 | int defaultHeight) {
22 | if (defaultWidth > 0) {
23 | window.setSize(windowPreferences.getInt("Width", defaultWidth),
24 | windowPreferences.getInt("Height", defaultHeight));
25 | }
26 | window.setLocation(windowPreferences.getInt("X", 0), windowPreferences.getInt("Y", 0));
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/Preset.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot;
2 |
3 | import org.json.JSONArray;
4 | import org.json.JSONObject;
5 |
6 | import java.io.IOException;
7 | import java.util.ArrayList;
8 | import java.util.List;
9 | import java.util.prefs.BackingStoreException;
10 | import java.util.prefs.Preferences;
11 |
12 | /**
13 | * User: ton Date: 22.06.13 Time: 15:05
14 | */
15 | public class Preset {
16 | private String title;
17 | private List processorPresets;
18 |
19 | public Preset() {
20 | this.title = "";
21 | this.processorPresets = new ArrayList();
22 | }
23 |
24 | public Preset(String title, List processorPresets) {
25 | this.title = title;
26 | this.processorPresets = processorPresets;
27 | }
28 |
29 | public String getTitle() {
30 | return title;
31 | }
32 |
33 | public void setTitle(String title) {
34 | this.title = title;
35 | }
36 |
37 | public List getProcessorPresets() {
38 | return processorPresets;
39 | }
40 |
41 | public JSONObject packJSONObject() throws IOException {
42 | JSONObject json = new JSONObject();
43 | json.put("Title", title);
44 | JSONArray jsonProcessorPresets = new JSONArray();
45 | for (ProcessorPreset pp : processorPresets) {
46 | jsonProcessorPresets.put(pp.packJSONObject());
47 | }
48 | json.put("ProcessorPresets", jsonProcessorPresets);
49 | return json;
50 | }
51 |
52 | public static Preset unpackJSONObject(JSONObject json) throws IOException {
53 | JSONArray jsonProcessorPresets = json.getJSONArray("ProcessorPresets");
54 | List processorPresets = new ArrayList();
55 | for (int i = 0; i < jsonProcessorPresets.length(); i++) {
56 | processorPresets.add(ProcessorPreset.unpackJSONObject(jsonProcessorPresets.getJSONObject(i)));
57 | }
58 | return new Preset(json.getString("Title"), processorPresets);
59 | }
60 |
61 | @Override
62 | public String toString() {
63 | return title;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/ProcessorPreset.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot;
2 |
3 | import org.json.JSONObject;
4 |
5 | import java.awt.*;
6 | import java.io.IOException;
7 | import java.util.*;
8 |
9 | /**
10 | * User: ton Date: 22.06.13 Time: 15:08
11 | */
12 | public class ProcessorPreset {
13 | private String title;
14 | private String processorType;
15 | private Map parameters;
16 | private Map colors;
17 | private boolean visible;
18 |
19 | public ProcessorPreset(String title, String processorType, Map parameters, Map colors, boolean visible) {
20 | this.title = title;
21 | this.processorType = processorType;
22 | this.parameters = parameters;
23 | this.colors = colors;
24 | this.visible = visible;
25 | }
26 |
27 | public static ProcessorPreset unpackJSONObject(JSONObject json) throws IOException {
28 | JSONObject jsonParameters = json.getJSONObject("Parameters");
29 | Map parametersNew = new HashMap();
30 | for (Object key : jsonParameters.keySet()) {
31 | String keyStr = (String) key;
32 | parametersNew.put(keyStr, jsonParameters.get(keyStr).toString());
33 | }
34 | JSONObject jsonColors = json.getJSONObject("Colors");
35 | Map colorsNew = new HashMap();
36 | for (Object key : jsonColors.keySet()) {
37 | String keyStr = (String) key;
38 | colorsNew.put(keyStr, new Color(Integer.parseInt(jsonColors.get(keyStr).toString(), 16)));
39 | }
40 | return new ProcessorPreset(json.getString("Title"), json.getString("ProcessorType"), parametersNew, colorsNew, json.optBoolean("visible", true));
41 | }
42 |
43 | public String getTitle() {
44 | return title;
45 | }
46 |
47 | public void setTitle(String title) {
48 | this.title = title;
49 | }
50 |
51 | public boolean isVisible() {
52 | return visible;
53 | }
54 |
55 | public void setVisible(boolean visible) {
56 | this.visible = visible;
57 | }
58 |
59 | public String getProcessorType() {
60 | return processorType;
61 | }
62 |
63 | public void setProcessorType(String processorType) {
64 | this.processorType = processorType;
65 | }
66 |
67 | public Map getParameters() {
68 | return parameters;
69 | }
70 |
71 | public void setParameters(Map parameters) {
72 | this.parameters = parameters;
73 | }
74 |
75 | public Map getColors() {
76 | return colors;
77 | }
78 |
79 | public void setColors(Map colors) {
80 | this.colors = colors;
81 | }
82 |
83 | public JSONObject packJSONObject() throws IOException {
84 | JSONObject json = new JSONObject();
85 | json.put("Title", title);
86 | json.put("ProcessorType", processorType);
87 | json.put("Parameters", new JSONObject(parameters));
88 | Map jsonColors = new HashMap();
89 | for (Map.Entry entry : colors.entrySet()) {
90 | jsonColors.put(entry.getKey(), Integer.toHexString(entry.getValue().getRGB()).substring(2, 8));
91 | }
92 | json.put("Colors", new JSONObject(jsonColors));
93 | json.put("visible", visible);
94 | return json;
95 | }
96 |
97 | public Map.Entry getParameter(int i) {
98 | java.util.List paramEntries = new ArrayList(parameters.entrySet());
99 | Collections.sort(paramEntries, new Comparator() {
100 | @Override
101 | public int compare(Map.Entry o1, Map.Entry o2) {
102 | return ((String) o1.getKey()).compareTo((String) o2.getKey());
103 | }
104 | });
105 | java.util.List colorsEntries = new ArrayList(colors.entrySet());
106 | Collections.sort(paramEntries, new Comparator() {
107 | @Override
108 | public int compare(Map.Entry o1, Map.Entry o2) {
109 | return ((String) o1.getKey()).compareTo((String) o2.getKey());
110 | }
111 | });
112 | paramEntries.addAll(colorsEntries);
113 | return paramEntries.get(i);
114 | }
115 |
116 | public ProcessorPreset clone() {
117 | return new ProcessorPreset(title, processorType, new HashMap(parameters), new HashMap(colors), visible);
118 | }
119 |
120 | @Override
121 | public String toString() {
122 | return title + " [" + processorType + "]";
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/Series.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot;
2 |
3 | import java.util.ArrayList;
4 |
5 | /**
6 | * Created by ton on 09.03.15.
7 | */
8 | public class Series extends ArrayList implements PlotItem {
9 | private final String title;
10 | private final double skipOut;
11 | private Double lastTime = null;
12 | private Double lastValue = null;
13 |
14 | public Series(String title, double skipOut) {
15 | this.title = title;
16 | this.skipOut = skipOut;
17 | }
18 |
19 | @Override
20 | public String getTitle() {
21 | return title;
22 | }
23 |
24 | public String getFullTitle(String processorTitle) {
25 | return processorTitle + (title.isEmpty() ? "" : (":" + title));
26 | }
27 |
28 | public void addPoint(double time, double value) {
29 | if (lastTime != null && time - lastTime < skipOut) {
30 | lastValue = value;
31 | return;
32 | }
33 | if (lastValue != null && lastTime != null && time - lastTime > skipOut * 2) {
34 | add(new XYPoint(lastTime, lastValue));
35 | }
36 | lastTime = time;
37 | lastValue = null;
38 | add(new XYPoint(time, value));
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/TaggedValueMarker.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot;
2 |
3 | import org.jfree.chart.plot.ValueMarker;
4 |
5 | /**
6 | * Created by ton on 29.09.15.
7 | */
8 | public class TaggedValueMarker extends ValueMarker {
9 | public final int tag;
10 |
11 | public TaggedValueMarker(int tag, double value) {
12 | super(value);
13 | this.tag = tag;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/XYPoint.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot;
2 |
3 | /**
4 | * Created by ton on 09.03.15.
5 | */
6 | public class XYPoint {
7 | public final double x;
8 | public final double y;
9 |
10 | public XYPoint(double x, double y) {
11 | this.x = x;
12 | this.y = y;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/export/AbstractTrackExporter.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.export;
2 |
3 | import java.io.*;
4 | import java.lang.reflect.Method;
5 |
6 | /**
7 | * Created by ada on 14.01.14.
8 | */
9 | public abstract class AbstractTrackExporter implements TrackExporter {
10 | protected TrackReader trackReader;
11 | protected TrackExporterConfiguration config;
12 | protected String title;
13 | protected Writer writer;
14 | protected int trackPart = 0;
15 | protected String flightMode = null;
16 |
17 | @Override
18 | public void export(TrackReader trackReader, TrackExporterConfiguration config, File file, String title) throws IOException {
19 | this.trackReader = trackReader;
20 | this.config = config;
21 | this.writer = new BufferedWriter(new FileWriter(file));
22 | this.title = title;
23 | boolean trackStarted = false;
24 | try {
25 | writeStart();
26 | while (true) {
27 | TrackPoint point = trackReader.readNextPoint();
28 | if (point == null) {
29 | break;
30 | }
31 | if (!trackStarted || (point.flightMode != null && !point.flightMode.equals(flightMode))) {
32 | if (trackStarted) {
33 | writePoint(point); // Write this point at the end of previous track to avoid interruption of track
34 | writeTrackPartEnd();
35 | }
36 | flightMode = point.flightMode;
37 | String trackPartName;
38 | if (point.flightMode != null) {
39 | trackPartName = String.format("%s: %s", trackPart, point.flightMode);
40 | trackPart++;
41 | } else {
42 | trackPartName = "Track";
43 | }
44 | writeTrackPartStart(trackPartName);
45 | trackStarted = true;
46 | }
47 | writePoint(point);
48 | }
49 | if (trackStarted) {
50 | writeTrackPartEnd();
51 | }
52 | writeEnd();
53 | } catch (Exception e) {
54 | e.printStackTrace();
55 | } finally {
56 | this.writer.close();
57 | }
58 | }
59 |
60 | protected abstract void writeStart() throws IOException;
61 |
62 | protected abstract void writeTrackPartStart(String trackPartName) throws IOException;
63 |
64 | protected abstract void writePoint(TrackPoint point) throws IOException;
65 |
66 | protected abstract void writeTrackPartEnd() throws IOException;
67 |
68 | protected abstract void writeEnd() throws IOException;
69 | }
70 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/export/AbstractTrackReader.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.export;
2 |
3 | import me.drton.jmavlib.log.FormatErrorException;
4 | import me.drton.jmavlib.log.LogReader;
5 |
6 | import java.io.EOFException;
7 | import java.io.IOException;
8 | import java.util.Map;
9 |
10 | /**
11 | * Created by ada on 23.12.13.
12 | */
13 | public abstract class AbstractTrackReader implements TrackReader {
14 | protected final LogReader reader;
15 | private long timeNext = 0;
16 | protected final TrackReaderConfiguration config;
17 |
18 | public AbstractTrackReader(LogReader reader, TrackReaderConfiguration config) throws IOException, FormatErrorException {
19 | this.reader = reader;
20 | this.config = config;
21 | this.reader.seek(this.config.getTimeStart());
22 | }
23 |
24 | protected long readUpdate(Map data) throws IOException, FormatErrorException {
25 | long t;
26 | while (true) {
27 | t = reader.readUpdate(data);
28 | if (t > config.getTimeEnd()) {
29 | throw new EOFException("Reached configured export limit.");
30 | }
31 | if (t >= timeNext) {
32 | if (timeNext == 0) {
33 | timeNext = t;
34 | }
35 | timeNext += config.getTimeInterval();
36 | return t;
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/export/GPXTrackExporter.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.export;
2 |
3 | import java.io.IOException;
4 | import java.text.SimpleDateFormat;
5 | import java.util.Locale;
6 |
7 | /**
8 | * Created by ada on 16.02.14.
9 | */
10 | public class GPXTrackExporter extends AbstractTrackExporter {
11 |
12 | private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
13 |
14 | protected void writeStart() throws IOException {
15 | writer.write("\n");
16 | writer.write("\n");
19 | writer.write("\n");
20 | writer.write(String.format("%s\n", this.title));
21 | writer.write("\n");
22 | writer.write("\n");
23 | }
24 |
25 | @Override
26 | protected void writeTrackPartStart(String trackPartName) throws IOException {
27 | writer.write("\n");
28 | }
29 |
30 | @Override
31 | protected void writePoint(TrackPoint point) throws IOException {
32 | writer.write(String.format(Locale.ROOT, "\n", point.lat, point.lon));
33 | writer.write(String.format(Locale.ROOT, "%.2f\n", point.alt));
34 | writer.write(String.format("\n", dateFormatter.format(point.time / 1000)));
35 | writer.write("\n");
36 | }
37 |
38 | protected void writeTrackPartEnd() throws IOException {
39 | writer.write("\n");
40 | }
41 |
42 | protected void writeEnd() throws IOException {
43 | writer.write("\n");
44 | writer.write("\n");
45 | }
46 |
47 | @Override
48 | public String getName() {
49 | return "GPX";
50 | }
51 |
52 | @Override
53 | public String getDescription() {
54 | return "GPS Exchange Format (GPX)";
55 | }
56 |
57 | @Override
58 | public String getFileExtension() {
59 | return "gpx";
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/export/KMLTrackExporter.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.export;
2 |
3 | import java.io.IOException;
4 | import java.text.SimpleDateFormat;
5 | import java.util.Locale;
6 |
7 | /**
8 | * Created by ada on 23.12.13.
9 | */
10 | public class KMLTrackExporter extends AbstractTrackExporter {
11 | private static final String LINE_STYLE_RED = "red";
12 | private static final String LINE_STYLE_GREEN = "green";
13 | private static final String LINE_STYLE_BLUE = "blue";
14 | private static final String LINE_STYLE_CYAN = "cyan";
15 | private static final String LINE_STYLE_MAGENTA = "magenta";
16 | private static final String LINE_STYLE_YELLOW = "yellow";
17 | private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
18 |
19 | protected String getStyleForFlightMode(String flightMode) {
20 | if (flightMode == null) {
21 | return LINE_STYLE_YELLOW;
22 | }
23 | if ("MANUAL".equals(flightMode)) {
24 | return LINE_STYLE_RED;
25 | } else if ("ALTCTL".equals(flightMode)) {
26 | return LINE_STYLE_YELLOW;
27 | } else if ("POSCTL".equals(flightMode)) {
28 | return LINE_STYLE_GREEN;
29 | } else if ("AUTO_MISSION".equals(flightMode)) {
30 | return LINE_STYLE_BLUE;
31 | } else if ("AUTO_LOITER".equals(flightMode)) {
32 | return LINE_STYLE_CYAN;
33 | } else if ("AUTO_RTL".equals(flightMode)) {
34 | return LINE_STYLE_MAGENTA;
35 | } else if ("AUTO_ACRO".equals(flightMode)) {
36 | return LINE_STYLE_RED;
37 | } else if ("AUTO_OFFBOARD".equals(flightMode)) {
38 | return LINE_STYLE_BLUE;
39 | } else {
40 | return LINE_STYLE_YELLOW;
41 | }
42 | }
43 |
44 | protected void writeStart() throws IOException {
45 | // TODO: maybe make some settings configurable
46 | writer.write("\n");
47 | writer.write("\n");
48 | writer.write("\n");
49 | writer.write("" + this.title + "\n");
50 | writer.write("\n");
51 | writer.write("\n");
57 | writer.write("\n");
63 | writer.write("\n");
69 | }
70 |
71 | @Override
72 | protected void writeTrackPartStart(String trackPartName) throws IOException {
73 | String styleId = getStyleForFlightMode(flightMode);
74 | writer.write("\n");
75 | writer.write("" + trackPartName + "\n");
76 | writer.write("\n");
77 | writer.write("#" + styleId + "\n");
78 | writer.write("\n");
79 | writer.write("absolute\n");
80 | writer.write("0\n");
81 | }
82 |
83 | protected void writePoint(TrackPoint point) throws IOException {
84 | writer.write(String.format("%s\n", dateFormatter.format(point.time / 1000)));
85 | writer.write(String.format(Locale.ROOT, "%.10f %.10f %.2f\n", point.lon, point.lat, point.alt));
86 | }
87 |
88 | protected void writeTrackPartEnd() throws IOException {
89 | writer.write("\n");
90 | writer.write("\n");
91 | }
92 |
93 | protected void writeEnd() throws IOException {
94 | writer.write("\n");
95 | writer.write("");
96 | }
97 |
98 | @Override
99 | public String getName() {
100 | return "KML";
101 | }
102 |
103 | @Override
104 | public String getDescription() {
105 | return "Google Earth Track (KML)";
106 | }
107 |
108 | @Override
109 | public String getFileExtension() {
110 | return "kml";
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/export/PX4TrackReader.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.export;
2 |
3 | import me.drton.jmavlib.log.FormatErrorException;
4 | import me.drton.jmavlib.log.px4.PX4LogReader;
5 |
6 | import java.io.EOFException;
7 | import java.io.IOException;
8 | import java.util.HashMap;
9 | import java.util.Map;
10 |
11 | /**
12 | * Created by ada on 23.12.13.
13 | */
14 | public class PX4TrackReader extends AbstractTrackReader {
15 | private static final String GPOS_LAT = "GPOS.Lat";
16 | private static final String GPOS_LON = "GPOS.Lon";
17 | private static final String GPOS_ALT = "GPOS.Alt";
18 | private static final String STAT_MAINSTATE = "STAT.MainState";
19 |
20 | private String flightMode = null;
21 |
22 | public PX4TrackReader(PX4LogReader reader, TrackReaderConfiguration config) throws IOException, FormatErrorException {
23 | super(reader, config);
24 | }
25 |
26 | @Override
27 | public TrackPoint readNextPoint() throws IOException, FormatErrorException {
28 | Map data = new HashMap();
29 | while (true) {
30 | data.clear();
31 | long t;
32 | try {
33 | t = readUpdate(data);
34 | } catch (EOFException e) {
35 | break; // End of file
36 | }
37 | String currentFlightMode = getFlightMode(data);
38 | if (currentFlightMode != null) {
39 | flightMode = currentFlightMode;
40 | }
41 | Number lat = (Number) data.get(GPOS_LAT);
42 | Number lon = (Number) data.get(GPOS_LON);
43 | Number alt = (Number) data.get(GPOS_ALT);
44 | if (lat != null && lon != null && alt != null) {
45 | return new TrackPoint(lat.doubleValue(), lon.doubleValue(), alt.doubleValue() + config.getAltitudeOffset(),
46 | t + reader.getUTCTimeReferenceMicroseconds(), flightMode);
47 | }
48 | }
49 | return null;
50 | }
51 |
52 | private String getFlightMode(Map data) {
53 | Number flightMode = (Number) data.get(STAT_MAINSTATE);
54 | if (flightMode != null) {
55 | switch (flightMode.intValue()) {
56 | case 0:
57 | return "MANUAL";
58 | case 1:
59 | return "ALTCTL";
60 | case 2:
61 | return "POSCTL";
62 | case 3:
63 | return "AUTO_MISSION";
64 | case 4:
65 | return "AUTO_LOITER";
66 | case 5:
67 | return "AUTO_RTL";
68 | case 6:
69 | return "AUTO_ACRO";
70 | case 7:
71 | return "AUTO_OFFBOARD";
72 | default:
73 | return String.format("UNKNOWN(%s)", flightMode.intValue());
74 | }
75 | }
76 | return null; // Not supported
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/export/TrackExporter.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.export;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 |
6 | /**
7 | * Created by ada on 19.01.14.
8 | */
9 | public interface TrackExporter {
10 | String getName();
11 |
12 | String getDescription();
13 |
14 | String getFileExtension();
15 |
16 | /**
17 | * Exports track to specified file, uses title if possible for current export format.
18 | *
19 | * @param file output file
20 | * @param title track title
21 | * @throws IOException
22 | */
23 | void export(TrackReader trackReader, TrackExporterConfiguration config, File file, String title) throws IOException;
24 | }
25 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/export/TrackExporterConfiguration.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.export;
2 |
3 | import java.util.prefs.Preferences;
4 |
5 | /**
6 | * Created by ada on 19.01.14.
7 | */
8 | public class TrackExporterConfiguration {
9 | private boolean splitTracksByFlightMode;
10 | private final static String SPLIT_TRACK_BY_FLIGHT_MODE_SETTING = "splitTracksByFlightMode";
11 | private String exportFormat;
12 | private final static String EXPORT_FORMAT_TYPE_SETTING = "exportFormat";
13 |
14 | public void saveConfiguration(Preferences preferences) {
15 | preferences.putBoolean(SPLIT_TRACK_BY_FLIGHT_MODE_SETTING, this.splitTracksByFlightMode);
16 | if (exportFormat != null) {
17 | preferences.put(EXPORT_FORMAT_TYPE_SETTING, exportFormat);
18 | }
19 | }
20 |
21 | public void loadConfiguration(Preferences preferences) {
22 | splitTracksByFlightMode = preferences.getBoolean(SPLIT_TRACK_BY_FLIGHT_MODE_SETTING, false);
23 | exportFormat = preferences.get(EXPORT_FORMAT_TYPE_SETTING, null);
24 | }
25 |
26 | public boolean isSplitTracksByFlightMode() {
27 | return splitTracksByFlightMode;
28 | }
29 |
30 | public void setSplitTracksByFlightMode(boolean splitTracksByFlightMode) {
31 | splitTracksByFlightMode = splitTracksByFlightMode;
32 | }
33 |
34 | public String getExportFormat() {
35 | return exportFormat;
36 | }
37 |
38 | public void setExportFormat(String exportFormat) {
39 | this.exportFormat = exportFormat;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/export/TrackPoint.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.export;
2 |
3 | /**
4 | * Created by ada on 23.12.13.
5 | */
6 | public class TrackPoint {
7 | public final double lat; /// latitude
8 | public final double lon; /// longitude
9 | public final double alt; /// altitude AMLS
10 | public final long time; /// unix time in milliseconds
11 | public final String flightMode; /// flight mode
12 |
13 | public TrackPoint(double lat, double lon, double alt, long time, String flightMode) {
14 | this.lat = lat;
15 | this.lon = lon;
16 | this.alt = alt;
17 | this.time = time;
18 | this.flightMode = flightMode;
19 | }
20 |
21 | public TrackPoint(double lat, double lon, double alt, long time) {
22 | this.lat = lat;
23 | this.lon = lon;
24 | this.alt = alt;
25 | this.time = time;
26 | this.flightMode = null;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/export/TrackReader.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.export;
2 |
3 | import me.drton.jmavlib.log.FormatErrorException;
4 |
5 | import java.io.IOException;
6 |
7 | /**
8 | * Created by ada on 24.12.13.
9 | */
10 | public interface TrackReader {
11 | /**
12 | * Reads next track point from LogReader.
13 | *
14 | * @return returns TrackPoint or null if no more points can be read.
15 | * @throws IOException
16 | * @throws FormatErrorException
17 | */
18 | TrackPoint readNextPoint() throws IOException, FormatErrorException;
19 | }
20 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/export/TrackReaderConfiguration.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.export;
2 |
3 | import java.util.prefs.Preferences;
4 |
5 | /**
6 | * Created by ada on 25.01.14.
7 | */
8 | public class TrackReaderConfiguration {
9 | private long timeInterval; /// Min time interval between generated points, [us]
10 | private final static String TIME_INTERVAL_SETTING = "timeInterval";
11 | private long timeStart; /// Start time, [us]
12 | private long timeEnd; /// End time, [us]
13 | private double altitudeOffset; /// Altitude offset, [m]
14 | private final static String ALTITUDE_OFFSET_SETTING = "altitudeOffset";
15 |
16 | public void saveConfiguration(Preferences preferences) {
17 | preferences.putLong(TIME_INTERVAL_SETTING, timeInterval);
18 | preferences.putDouble(ALTITUDE_OFFSET_SETTING, altitudeOffset);
19 | }
20 |
21 | public void loadConfiguration(Preferences preferences) {
22 | timeInterval = preferences.getLong(TIME_INTERVAL_SETTING, 0);
23 | altitudeOffset = preferences.getDouble(ALTITUDE_OFFSET_SETTING, 0.0);
24 | }
25 |
26 | public long getTimeInterval() {
27 | return timeInterval;
28 | }
29 |
30 | public void setTimeInterval(long timeInterval) {
31 | this.timeInterval = timeInterval;
32 | }
33 |
34 | public long getTimeStart() {
35 | return timeStart;
36 | }
37 |
38 | public void setTimeStart(long timeStart) {
39 | this.timeStart = timeStart;
40 | }
41 |
42 | public long getTimeEnd() {
43 | return timeEnd;
44 | }
45 |
46 | public void setTimeEnd(long timeEnd) {
47 | this.timeEnd = timeEnd;
48 | }
49 |
50 | public double getAltitudeOffset() {
51 | return altitudeOffset;
52 | }
53 |
54 | public void setAltitudeOffset(double altitudeOffset) {
55 | this.altitudeOffset = altitudeOffset;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/export/TrackReaderFactory.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.export;
2 |
3 | import me.drton.jmavlib.log.FormatErrorException;
4 | import me.drton.jmavlib.log.LogReader;
5 | import me.drton.jmavlib.log.px4.PX4LogReader;
6 | import me.drton.jmavlib.log.ulog.ULogReader;
7 |
8 | import java.io.IOException;
9 |
10 | /**
11 | * Created by ada on 24.12.13.
12 | */
13 | public class TrackReaderFactory {
14 | public static TrackReader getTrackReader(LogReader reader, TrackReaderConfiguration config) throws IOException, FormatErrorException {
15 | if (reader instanceof PX4LogReader) {
16 | return new PX4TrackReader((PX4LogReader) reader, config);
17 | } else if (reader instanceof ULogReader) {
18 | return new ULogTrackReader((ULogReader) reader, config);
19 | } else {
20 | throw new UnsupportedOperationException(
21 | String.format("No track reader for \"%s\" format available.", reader.getFormat()));
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/export/ULogTrackReader.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.export;
2 |
3 | import me.drton.jmavlib.log.FormatErrorException;
4 | import me.drton.jmavlib.log.ulog.ULogReader;
5 |
6 | import java.io.EOFException;
7 | import java.io.IOException;
8 | import java.util.HashMap;
9 | import java.util.Map;
10 |
11 | /**
12 | * Created by ada on 23.12.13.
13 | */
14 | public class ULogTrackReader extends AbstractTrackReader {
15 | private static final String POS_VALID = "ATTITUDE_POSITION.valid_pos";
16 | private static final String POS_LAT = "ATTITUDE_POSITION.lat";
17 | private static final String POS_LON = "ATTITUDE_POSITION.lon";
18 | private static final String POS_ALT = "ATTITUDE_POSITION.alt_msl";
19 | private static final String MODE = "SYSTEM_STATUS.mode";
20 |
21 | private String flightMode = null;
22 |
23 | public ULogTrackReader(ULogReader reader, TrackReaderConfiguration config) throws IOException, FormatErrorException {
24 | super(reader, config);
25 | }
26 |
27 | @Override
28 | public TrackPoint readNextPoint() throws IOException, FormatErrorException {
29 | Map data = new HashMap();
30 | while (true) {
31 | data.clear();
32 | long t;
33 | try {
34 | t = readUpdate(data);
35 | } catch (EOFException e) {
36 | break; // End of file
37 | }
38 | String currentFlightMode = getFlightMode(data);
39 | if (currentFlightMode != null) {
40 | flightMode = currentFlightMode;
41 | }
42 | Number valid = (Number) data.get(POS_VALID);
43 | Number lat = (Number) data.get(POS_LAT);
44 | Number lon = (Number) data.get(POS_LON);
45 | Number alt = (Number) data.get(POS_ALT);
46 | if (valid != null && lat != null && lon != null && alt != null && valid.intValue() != 0) {
47 | return new TrackPoint(lat.doubleValue(), lon.doubleValue(), alt.doubleValue() + config.getAltitudeOffset(),
48 | t + reader.getUTCTimeReferenceMicroseconds(), flightMode);
49 | }
50 | }
51 | return null;
52 | }
53 |
54 | private String getFlightMode(Map data) {
55 | Number flightMode = (Number) data.get(MODE);
56 | if (flightMode != null) {
57 | switch (flightMode.intValue()) {
58 | case 1:
59 | return "MANUAL";
60 | case 2:
61 | return "ALTCTL";
62 | case 3:
63 | return "POSCTL";
64 | case 4:
65 | return "RTH";
66 | default:
67 | return String.format("UNKNOWN(%s)", flightMode.intValue());
68 | }
69 | }
70 | return null; // Not supported
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/processors/ATan2.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.processors;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | /**
7 | * User: ton Date: 16.06.13 Time: 19:55
8 | */
9 | public class ATan2 extends PlotProcessor {
10 | protected String param_Field_X;
11 | protected String param_Field_Y;
12 | protected double param_Angle_Offset;
13 |
14 | @Override
15 | public Map getDefaultParameters() {
16 | Map params = new HashMap();
17 | params.put("Field_X", "LPOS.VX");
18 | params.put("Field_Y", "LPOS.VY");
19 | params.put("Angle Offset", 0.0);
20 | return params;
21 | }
22 |
23 | @Override
24 | public void init() {
25 | param_Field_X = (String) parameters.get("Field_X");
26 | param_Field_Y = (String) parameters.get("Field_Y");
27 | param_Angle_Offset = (Double) parameters.get("Angle Offset");
28 | addSeries();
29 | }
30 |
31 | @Override
32 | public void process(double time, Map update) {
33 | Object x = update.get(param_Field_X);
34 | Object y = update.get(param_Field_Y);
35 | if (x != null && y != null && x instanceof Number && y instanceof Number) {
36 | double a = Math.atan2(((Number) y).doubleValue(), ((Number) x).doubleValue());
37 | a += param_Angle_Offset + Math.PI;
38 | int a_2pi = (int) Math.round(a / 2.0 / Math.PI - 0.5);
39 | a -= (a_2pi * 2.0 + 1.0) * Math.PI;
40 | addPoint(0, time, a);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/processors/Abs.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.processors;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | /**
7 | * User: ton Date: 16.06.13 Time: 12:59
8 | */
9 | public class Abs extends PlotProcessor {
10 | protected String[] param_Fields;
11 | protected double param_Scale;
12 |
13 | @Override
14 | public Map getDefaultParameters() {
15 | Map params = new HashMap();
16 | params.put("Fields", "LPOS.VX LPOS.VY");
17 | params.put("Scale", 1.0);
18 | return params;
19 | }
20 |
21 | @Override
22 | public void init() {
23 | param_Fields = ((String) parameters.get("Fields")).split(WHITESPACE_RE);
24 | param_Scale = (Double) parameters.get("Scale");
25 | addSeries();
26 | }
27 |
28 | @Override
29 | public void process(double time, Map update) {
30 | double s = 0.0;
31 | for (String field : param_Fields) {
32 | Object v = update.get(field);
33 | if (v != null && v instanceof Number) {
34 | double d = ((Number) v).doubleValue();
35 | s += d * d;
36 | } else {
37 | return;
38 | }
39 | }
40 | addPoint(0, time, Math.sqrt(s) * param_Scale);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/processors/Battery.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.processors;
2 |
3 | import me.drton.flightplot.processors.tools.LowPassFilter;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | /**
9 | * User: ton Date: 10.11.13 Time: 16:50
10 | */
11 | public class Battery extends PlotProcessor {
12 | private String param_Field_Voltage;
13 | private String param_Field_Current;
14 | private String param_Field_Discharged;
15 | private double param_Capacity;
16 | private double param_Resistance;
17 | private double param_N_Cells;
18 | private double param_V_Empty;
19 | private double param_V_Full;
20 | private boolean showV;
21 | private boolean showRemainingV;
22 | private boolean showRemainingC;
23 | private LowPassFilter lpf;
24 |
25 | @Override
26 | public Map getDefaultParameters() {
27 | Map params = new HashMap();
28 | params.put("Field Voltage", "BATT.V");
29 | params.put("Field Current", "BATT.C");
30 | params.put("Field Discharged", "BATT.Discharged");
31 | params.put("Capacity", 2200.0);
32 | params.put("Resistance", 0.03);
33 | params.put("N Cells", 3);
34 | params.put("V Empty", 3.7);
35 | params.put("V Full", 4.0);
36 | params.put("LPF", 1.0);
37 | params.put("Show", "VC");
38 | return params;
39 | }
40 |
41 | @Override
42 | public void init() {
43 | param_Field_Voltage = (String) parameters.get("Field Voltage");
44 | param_Field_Current = (String) parameters.get("Field Current");
45 | param_Field_Discharged = (String) parameters.get("Field Discharged");
46 | param_Capacity = (Double) parameters.get("Capacity");
47 | param_Resistance = (Double) parameters.get("Resistance");
48 | param_N_Cells = (Integer) parameters.get("N Cells");
49 | param_V_Empty = (Double) parameters.get("V Empty");
50 | param_V_Full = (Double) parameters.get("V Full");
51 | lpf = new LowPassFilter();
52 | lpf.setF((Double) parameters.get("LPF"));
53 | String show = ((String) parameters.get("Show")).toUpperCase();
54 | showRemainingV = show.contains("V");
55 | showRemainingC = show.contains("C");
56 | addSeries("RemainingV");
57 | addSeries("RemainingC");
58 | }
59 |
60 | @Override
61 | public void process(double time, Map update) {
62 | Number voltageNum = (Number) update.get(param_Field_Voltage);
63 | Number currentNum = (Number) update.get(param_Field_Current);
64 | Number dischargedNum = (Number) update.get(param_Field_Discharged);
65 | if (voltageNum != null) {
66 | double v = voltageNum.doubleValue();
67 | double vFiltered = v;
68 | if (currentNum != null) {
69 | double current = currentNum.doubleValue();
70 | if (current > 0.0) {
71 | // current < 0 means not available
72 | vFiltered += current * param_Resistance;
73 | }
74 | }
75 | vFiltered = lpf.getOutput(time, vFiltered);
76 | double remainingV = Math.min(1.0,
77 | Math.max(0.0, (vFiltered / param_N_Cells - param_V_Empty) / (param_V_Full - param_V_Empty)));
78 | if (showRemainingV)
79 | addPoint(0, time, remainingV * 100.0);
80 | if (dischargedNum != null) {
81 | double discharged = dischargedNum.doubleValue();
82 | if (discharged > 0.0) {
83 | double remainingC = Math.min(1.0, Math.max(0.0, 1.0 - discharged / param_Capacity));
84 | if (showRemainingC)
85 | addPoint(1, time, remainingC * 100.0);
86 | }
87 | }
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/processors/Derivative.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.processors;
2 |
3 | import java.util.Map;
4 |
5 | /**
6 | * User: ton Date: 24.06.13 Time: 22:46
7 | */
8 | public class Derivative extends Simple {
9 | private double[] valuesPrev;
10 | private double[] timesPrev;
11 |
12 | @Override
13 | public Map getDefaultParameters() {
14 | Map params = super.getDefaultParameters();
15 | params.put("Fields", "LPOS.VX LPOS.VY");
16 | return params;
17 | }
18 |
19 | @Override
20 | public void init() {
21 | super.init();
22 | valuesPrev = new double[param_Fields.length];
23 | timesPrev = new double[param_Fields.length];
24 | for (int i = 0; i < param_Fields.length; i++) {
25 | valuesPrev[i] = Double.NaN;
26 | timesPrev[i] = Double.NaN;
27 | }
28 | }
29 |
30 | @Override
31 | protected double postProcessValue(int idx, double time, double in) {
32 | double out = Double.NaN;
33 | if (!Double.isNaN(timesPrev[idx])) {
34 | double dt = time - timesPrev[idx];
35 | if (dt > 1.0e-5) {
36 | out = (in - valuesPrev[idx]) / dt;
37 | }
38 | }
39 | valuesPrev[idx] = in;
40 | timesPrev[idx] = time;
41 | return out;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/processors/EulerFromQuaternion.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.processors;
2 |
3 | import me.drton.jmavlib.conversion.RotationConversion;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | /**
9 | * Created by ton on 05.01.15.
10 | */
11 | public class EulerFromQuaternion extends PlotProcessor {
12 | private String[] param_Fields;
13 | private double param_Scale;
14 | private boolean[] show;
15 | private double[] q;
16 |
17 | @Override
18 | public Map getDefaultParameters() {
19 | Map params = new HashMap();
20 | params.put("Fields", "vehicle_attitude_0.q[0] vehicle_attitude_0.q[1] vehicle_attitude_0.q[2] vehicle_attitude_0.q[3]");
21 | params.put("Show", "RPY");
22 | params.put("Scale", 1.0);
23 | return params;
24 | }
25 |
26 | @Override
27 | public void init() {
28 | q = new double[4];
29 | param_Fields = ((String) parameters.get("Fields")).split(WHITESPACE_RE);
30 | param_Scale = (Double) parameters.get("Scale");
31 | String showStr = ((String) parameters.get("Show")).toUpperCase();
32 | show = new boolean[]{false, false, false};
33 | String[] axes = new String[]{"Roll", "Pitch", "Yaw"};
34 | for (int axis = 0; axis < 3; axis++) {
35 | String axisName = axes[axis];
36 | show[axis] = showStr.contains(axisName.substring(0, 1));
37 | if (show[axis]) {
38 | addSeries(axisName);
39 | }
40 | }
41 | }
42 |
43 | @Override
44 | public void process(double time, Map update) {
45 | if (param_Fields.length < 4) {
46 | return;
47 | }
48 | for (int i = 0; i < 4; i++) {
49 | Number v = (Number) update.get(param_Fields[i]);
50 | if (v == null) {
51 | return;
52 | }
53 | q[i] = v.doubleValue();
54 | }
55 | double[] euler = RotationConversion.eulerAnglesByQuaternion(q);
56 | int plot_idx = 0;
57 | for (int axis = 0; axis < 3; axis++) {
58 | if (show[axis]) {
59 | addPoint(plot_idx++, time, euler[axis] * param_Scale);
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/processors/Expression.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.processors;
2 |
3 | import me.drton.flightplot.processors.tools.LowPassFilter;
4 | import net.objecthunter.exp4j.ExpressionBuilder;
5 |
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | /**
10 | * Created by markw on 1/22/15.
11 | */
12 | public class Expression extends PlotProcessor {
13 | protected final Map data = new HashMap();
14 | protected LowPassFilter lowPassFilter;
15 | private net.objecthunter.exp4j.Expression expr;
16 |
17 | @Override
18 | public Map getDefaultParameters() {
19 | Map params = new HashMap();
20 | params.put("Expression", "BATT.C * BATT.V");
21 | params.put("LPF", 0.0);
22 | return params;
23 | }
24 |
25 | @Override
26 | public void init() {
27 | String exprStr = (String) parameters.get("Expression");
28 | expr = null;
29 | ExpressionBuilder expBuilder = new ExpressionBuilder(exprStr);
30 | if (fieldsList != null) {
31 | expBuilder.variables(fieldsList.keySet());
32 | try {
33 | expr = expBuilder.build();
34 | } catch (Exception e) {
35 | e.printStackTrace();
36 | }
37 | }
38 | lowPassFilter = new LowPassFilter();
39 | lowPassFilter.setF((Double) parameters.get("LPF"));
40 | addSeries();
41 | }
42 |
43 | @Override
44 | public void process(double time, Map update) {
45 | if (expr == null) {
46 | return;
47 | }
48 | for (Map.Entry entry : update.entrySet()) {
49 | Object val = entry.getValue();
50 | if (val != null && val instanceof Number) {
51 | expr.setVariable(entry.getKey(), ((Number) val).doubleValue());
52 | }
53 | }
54 | double res;
55 | try {
56 | res = expr.evaluate();
57 | } catch (Exception e) {
58 | return;
59 | }
60 | addPoint(0, time, lowPassFilter.getOutput(time, res));
61 |
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/processors/GlobalPositionProjection.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.processors;
2 |
3 | import me.drton.jmavlib.geo.GlobalPositionProjector;
4 | import me.drton.jmavlib.geo.LatLonAlt;
5 |
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | /**
10 | * User: ton Date: 11.07.13 Time: 22:14
11 | */
12 | public class GlobalPositionProjection extends PlotProcessor {
13 | private GlobalPositionProjector positionProjector = new GlobalPositionProjector();
14 | private String[] param_Fields;
15 |
16 | @Override
17 | public Map getDefaultParameters() {
18 | Map params = new HashMap();
19 | params.put("Fields", "GPS.Lat GPS.Lon");
20 | params.put("Ref", "");
21 | return params;
22 | }
23 |
24 | @Override
25 | public void init() {
26 | positionProjector.reset();
27 | param_Fields = ((String) parameters.get("Fields")).split(WHITESPACE_RE);
28 | String[] ref = ((String) parameters.get("Ref")).split(WHITESPACE_RE);
29 | if (ref.length >= 2) {
30 | positionProjector.init(new LatLonAlt(Double.parseDouble(ref[0]), Double.parseDouble(ref[1]), 0.0));
31 | }
32 | addSeries("X");
33 | addSeries("Y");
34 | }
35 |
36 | @Override
37 | public void process(double time, Map update) {
38 | // GPS
39 | Number latNum = (Number) update.get(param_Fields[0]);
40 | Number lonNum = (Number) update.get(param_Fields[1]);
41 | if (latNum != null && lonNum != null) {
42 | LatLonAlt latLonAlt = new LatLonAlt(latNum.doubleValue(), lonNum.doubleValue(), 0.0);
43 | if (!positionProjector.isInited()) {
44 | positionProjector.init(latLonAlt);
45 | }
46 | double[] xyz = positionProjector.project(latLonAlt);
47 | addPoint(0, time, xyz[0]);
48 | addPoint(1, time, xyz[1]);
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/processors/Integral.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.processors;
2 |
3 | import java.util.Map;
4 |
5 | /**
6 | * User: ton Date: 04.11.13 Time: 23:11
7 | */
8 | public class Integral extends Simple {
9 | private double param_In_Offset;
10 | private double[] integrals;
11 | private double[] times;
12 |
13 | @Override
14 | public Map getDefaultParameters() {
15 | Map params = super.getDefaultParameters();
16 | params.put("In Offset", 0.0);
17 | return params;
18 | }
19 |
20 | @Override
21 | public void init() {
22 | super.init();
23 | param_In_Offset = (Double) parameters.get("In Offset");
24 | integrals = new double[param_Fields.length];
25 | times = new double[param_Fields.length];
26 | for (int i = 0; i < integrals.length; i++) {
27 | integrals[i] = 0.0;
28 | times[i] = Double.NaN;
29 | }
30 | }
31 |
32 | @Override
33 | protected double preProcessValue(int idx, double time, double in) {
34 | if (!Double.isNaN(times[idx])) {
35 | integrals[idx] += (in + param_In_Offset) * (time - times[idx]);
36 | }
37 | times[idx] = time;
38 | return integrals[idx];
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/processors/LandDetector.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.processors;
2 |
3 | import me.drton.flightplot.processors.tools.LowPassFilter;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | /**
9 | * User: ton Date: 25.07.13 Time: 14:20
10 | */
11 | public class LandDetector extends PlotProcessor {
12 | private String param_Field_Baro;
13 | private String param_Field_Thrust;
14 | private double param_Filter_Time;
15 | private double param_Threshold_Alt2;
16 | private double param_Threshold_Thrust;
17 | private double timePrev;
18 | private double baro;
19 | private double thrust;
20 | private LowPassFilter baroLPF;
21 | private double landDetectedTime;
22 | private boolean landed;
23 | private boolean initialized;
24 | private double altAvg;
25 | private double altDisp;
26 |
27 | @Override
28 | public Map getDefaultParameters() {
29 | Map params = new HashMap();
30 | params.put("Field Baro", "SENS.BaroAlt");
31 | params.put("Field Thrust", "ATTC.Thrust");
32 | params.put("Baro LPF", 20.0);
33 | params.put("Filter Time", 1.0);
34 | params.put("Threshold Alt", 0.3);
35 | params.put("Threshold Thrust", 0.25);
36 | return params;
37 | }
38 |
39 | @Override
40 | public void init() {
41 | timePrev = Double.NaN;
42 | landed = true;
43 | landDetectedTime = Double.NaN;
44 | initialized = false;
45 | altAvg = 0.0;
46 | altDisp = 0.0;
47 | baro = 0.0;
48 | baroLPF = new LowPassFilter();
49 | thrust = 0.0;
50 | param_Field_Baro = (String) parameters.get("Field Baro");
51 | param_Field_Thrust = (String) parameters.get("Field Thrust");
52 | param_Filter_Time = (Double) parameters.get("Filter Time");
53 | baroLPF.setF((Double) parameters.get("Baro LPF"));
54 | param_Threshold_Alt2 = (Double) parameters.get("Threshold Alt");
55 | param_Threshold_Alt2 = param_Threshold_Alt2 * param_Threshold_Alt2;
56 | param_Threshold_Thrust = (Double) parameters.get("Threshold Thrust");
57 | addSeries("Landed");
58 | addSeries("AltDisp");
59 | }
60 |
61 | @Override
62 | public void process(double time, Map update) {
63 | Number baroNum = (Number) update.get(param_Field_Baro);
64 | if (baroNum != null) {
65 | baro = baroLPF.getOutput(time, baroNum.doubleValue());
66 | if (!initialized) {
67 | initialized = true;
68 | altAvg = baro;
69 | }
70 | }
71 | Number thrustNum = (Number) update.get(param_Field_Thrust);
72 | if (thrustNum != null) {
73 | thrust = thrustNum.doubleValue();
74 | }
75 | if (initialized && !Double.isNaN(timePrev)) {
76 | double dt = time - timePrev;
77 | altAvg += (baro - altAvg) * dt / param_Filter_Time;
78 | altDisp = baro - altAvg;
79 | altDisp = altDisp * altDisp;
80 | if (landed) {
81 | if (altDisp > param_Threshold_Alt2 && thrust > param_Threshold_Thrust) {
82 | landed = false;
83 | landDetectedTime = Double.NaN;
84 | }
85 | } else {
86 | if (altDisp < param_Threshold_Alt2 && thrust < param_Threshold_Thrust) {
87 | if (Double.isNaN(landDetectedTime)) {
88 | landDetectedTime = time; // land detected first time
89 | } else {
90 | if (time - landDetectedTime > param_Filter_Time) {
91 | landed = true;
92 | landDetectedTime = Double.NaN;
93 | }
94 | }
95 | } else {
96 | landDetectedTime = Double.NaN;
97 | }
98 | }
99 | }
100 | addPoint(0, time, landed ? 1.0 : 0.0);
101 | addPoint(1, time, Math.sqrt(altDisp));
102 | timePrev = time;
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/processors/NEDFromBodyProjection.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.processors;
2 |
3 | import me.drton.flightplot.processors.tools.LowPassFilter;
4 | import me.drton.jmavlib.conversion.RotationConversion;
5 |
6 | import javax.vecmath.Matrix3d;
7 | import javax.vecmath.Vector3d;
8 | import java.util.HashMap;
9 | import java.util.Map;
10 |
11 | /**
12 | * User: ton Date: 14.09.13 Time: 23:45
13 | */
14 | public class NEDFromBodyProjection extends PlotProcessor {
15 | private String[] param_Fields;
16 | private String[] param_Fields_Att;
17 | private double param_Scale;
18 | private double param_Offset;
19 | private boolean param_Backward;
20 | private double[] param_Att_Offsets;
21 | private boolean[] show;
22 | private LowPassFilter[] lowPassFilters;
23 | private Matrix3d r;
24 | private double[] vArr;
25 | private Vector3d v;
26 | private double[] vNEDArr;
27 | private Vector3d vNED;
28 |
29 | @Override
30 | public Map getDefaultParameters() {
31 | Map params = new HashMap();
32 | params.put("Fields", "IMU.AccX IMU.AccY IMU.AccZ");
33 | params.put("Fields Att", "ATT.Roll ATT.Pitch ATT.Yaw");
34 | params.put("Att Offsets", "0.0 0.0 0.0");
35 | params.put("Show", "XYZ");
36 | params.put("LPF", 0.0);
37 | params.put("Scale", 1.0);
38 | params.put("Offset", 0.0);
39 | params.put("Backward", false);
40 | return params;
41 | }
42 |
43 | @Override
44 | public void init() {
45 | param_Fields = ((String) parameters.get("Fields")).split(WHITESPACE_RE);
46 | param_Fields_Att = ((String) parameters.get("Fields Att")).split(WHITESPACE_RE);
47 | String[] attOffsStr = ((String) parameters.get("Att Offsets")).split(WHITESPACE_RE);
48 | param_Scale = (Double) parameters.get("Scale");
49 | param_Offset = (Double) parameters.get("Offset");
50 | param_Backward = (Boolean) parameters.get("Backward");
51 | String showStr = ((String) parameters.get("Show")).toUpperCase();
52 | show = new boolean[]{false, false, false};
53 | lowPassFilters = new LowPassFilter[3];
54 | vArr = new double[3];
55 | v = new Vector3d();
56 | vNEDArr = new double[3];
57 | vNED = new Vector3d();
58 | r = new Matrix3d();
59 | param_Att_Offsets = new double[3];
60 | for (int i = 0; i < 3; i++) {
61 | if (attOffsStr.length > i) {
62 | param_Att_Offsets[i] = Double.parseDouble(attOffsStr[i]);
63 | } else {
64 | param_Att_Offsets[i] = 0.0;
65 | }
66 | String axisName = "XYZ".substring(i, i + 1);
67 | show[i] = showStr.contains(axisName);
68 | if (show[i]) {
69 | LowPassFilter lowPassFilter = new LowPassFilter();
70 | lowPassFilter.setF((Double) parameters.get("LPF"));
71 | lowPassFilters[i] = lowPassFilter;
72 | addSeries(axisName);
73 | }
74 | }
75 | }
76 |
77 | @Override
78 | public void process(double time, Map update) {
79 | boolean act = false;
80 |
81 | Number roll = (Number) update.get(param_Fields_Att[0]);
82 | Number pitch = (Number) update.get(param_Fields_Att[1]);
83 | Number yaw = (Number) update.get(param_Fields_Att[2]);
84 |
85 | if (roll != null && pitch != null && yaw != null) {
86 | // Update rotation matrix
87 | r.set(RotationConversion.rotationMatrixByEulerAngles(roll.doubleValue() + param_Att_Offsets[0],
88 | pitch.doubleValue() + param_Att_Offsets[1], yaw.doubleValue() + param_Att_Offsets[2]));
89 | if (param_Backward) {
90 | r.transpose();
91 | }
92 | act = true;
93 | }
94 |
95 | for (int i = 0; i < 3; i++) {
96 | Number vNum = (Number) update.get(param_Fields[i]);
97 | if (vNum != null) {
98 | // Update source vector
99 | vArr[i] = vNum.doubleValue();
100 | act = true;
101 | }
102 | }
103 | v.set(vArr);
104 |
105 | if (act) {
106 | vNED.set(v);
107 | r.transform(vNED);
108 | int seriesIdx = 0;
109 | vNED.get(vNEDArr);
110 | for (int i = 0; i < 3; i++) {
111 | if (show[i]) {
112 | double out = lowPassFilters[i].getOutput(time, vNEDArr[i]);
113 | addPoint(seriesIdx, time, out * param_Scale + param_Offset);
114 | seriesIdx++;
115 | }
116 | }
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/processors/PlotProcessor.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.processors;
2 |
3 | import me.drton.flightplot.MarkersList;
4 | import me.drton.flightplot.PlotItem;
5 | import me.drton.flightplot.Series;
6 | import me.drton.flightplot.XYPoint;
7 |
8 | import java.util.ArrayList;
9 | import java.util.HashMap;
10 | import java.util.List;
11 | import java.util.Map;
12 |
13 | /**
14 | * User: ton Date: 12.06.13 Time: 18:25
15 | */
16 | public abstract class PlotProcessor {
17 | protected static final String WHITESPACE_RE = "[ \t]+";
18 | protected Map parameters;
19 | protected Map fieldsList = new HashMap();
20 | private double skipOut = 0.0;
21 | private List seriesList = new ArrayList();
22 | private List lastPoints = new ArrayList();
23 |
24 | protected PlotProcessor() {
25 | this.parameters = getDefaultParameters();
26 | }
27 |
28 | public abstract void init();
29 |
30 | public void setSkipOut(double skipOut) {
31 | this.skipOut = skipOut;
32 | }
33 |
34 | public void setFieldsList(Map fieldsList) {
35 | this.fieldsList = fieldsList;
36 | }
37 |
38 | private static Object castValue(Object valueOld, Object valueNewObj) {
39 | String valueNewStr = valueNewObj.toString();
40 | Object valueNew = valueNewObj;
41 | if (valueOld instanceof String) {
42 | valueNew = valueNewStr;
43 | } else if (valueOld instanceof Double) {
44 | valueNew = Double.parseDouble(valueNewStr);
45 | } else if (valueOld instanceof Float) {
46 | valueNew = Float.parseFloat(valueNewStr);
47 | } else if (valueOld instanceof Integer) {
48 | valueNew = Integer.parseInt(valueNewStr);
49 | } else if (valueOld instanceof Long) {
50 | valueNew = Long.parseLong(valueNewStr);
51 | } else if (valueOld instanceof Boolean) {
52 | char firstChar = valueNewStr.toLowerCase().charAt(0);
53 | if (firstChar == 'f' || firstChar == 'n' || "0".equals(valueNewStr))
54 | valueNew = false;
55 | else
56 | valueNew = true;
57 | }
58 | return valueNew;
59 | }
60 |
61 | public abstract Map getDefaultParameters();
62 |
63 | public Map getParameters() {
64 | return parameters;
65 | }
66 |
67 | public void setParameters(Map parametersNew) {
68 | for (Map.Entry entry : parametersNew.entrySet()) {
69 | String key = entry.getKey();
70 | Object oldValue = parameters.get(key);
71 | Object newValue = parametersNew.get(key);
72 | if (oldValue != null) {
73 | parameters.put(key, castValue(oldValue, newValue));
74 | }
75 | }
76 | }
77 |
78 | protected int addSeries() {
79 | int idx = seriesList.size();
80 | seriesList.add(new Series("", skipOut));
81 | lastPoints.add(null);
82 | return idx;
83 | }
84 |
85 | protected int addSeries(String label) {
86 | int idx = seriesList.size();
87 | seriesList.add(new Series(label, skipOut));
88 | lastPoints.add(null);
89 | return idx;
90 | }
91 |
92 | protected int addMarkersList() {
93 | int idx = seriesList.size();
94 | seriesList.add(new MarkersList(""));
95 | return idx;
96 | }
97 |
98 | protected int addMarkersList(String label) {
99 | int idx = seriesList.size();
100 | seriesList.add(new MarkersList(label));
101 | return idx;
102 | }
103 |
104 | public List getSeriesList() {
105 | return seriesList;
106 | }
107 |
108 | protected void addPoint(int seriesIdx, double time, double value) {
109 | ((Series) seriesList.get(seriesIdx)).addPoint(time, value);
110 | }
111 |
112 | protected void addMarker(int seriesIdx, double time, String label) {
113 | ((MarkersList) seriesList.get(seriesIdx)).addMarker(time, label);
114 | }
115 |
116 | public abstract void process(double time, Map update);
117 |
118 | public String getProcessorType() {
119 | return getClass().getSimpleName();
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/processors/PosPIDControlSimulator.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.processors;
2 |
3 | import me.drton.flightplot.processors.tools.LowPassFilter;
4 | import me.drton.flightplot.processors.tools.PID;
5 |
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | /**
10 | * User: ton Date: 20.06.13 Time: 6:06
11 | */
12 | public class PosPIDControlSimulator extends PlotProcessor {
13 | private double startTime;
14 | private double startSP;
15 | private double startSPRate;
16 | private double thrustK;
17 | private double accScale;
18 | private double drag;
19 | private double awuRate;
20 |
21 | private LowPassFilter propeller = new LowPassFilter();
22 | private PID pidPos = new PID();
23 | private double pos;
24 | private double rate;
25 | private double posSP;
26 | private double timePrev;
27 |
28 | @Override
29 | public Map getDefaultParameters() {
30 | Map params = new HashMap();
31 | params.put("Start Time", 100.0);
32 | params.put("Start SP", 1.0);
33 | params.put("Start SP Rate", 1.0);
34 | params.put("Thrust T", 0.03);
35 | params.put("Thrust K", 40.0);
36 | params.put("Ctrl P", 0.1);
37 | params.put("Ctrl I", 0.05);
38 | params.put("Ctrl D", 0.1);
39 | params.put("Ctrl Limit", 0.2);
40 | params.put("AWU Rate", 1.0);
41 | params.put("Acc Scale", 1.0);
42 | params.put("Drag", 0.0);
43 | return params;
44 | }
45 |
46 | @Override
47 | public void init() {
48 | propeller.reset();
49 | pos = 0.0;
50 | rate = 0.0;
51 | posSP = 0.0;
52 | timePrev = Double.NaN;
53 | startTime = (Double) parameters.get("Start Time");
54 | startSP = (Double) parameters.get("Start SP");
55 | startSPRate = (Double) parameters.get("Start SP Rate");
56 | thrustK = (Double) parameters.get("Thrust K");
57 | accScale = (Double) parameters.get("Acc Scale");
58 | drag = (Double) parameters.get("Drag");
59 | awuRate = (Double) parameters.get("AWU Rate");
60 | propeller.setT((Double) parameters.get("Thrust T"));
61 | pidPos.reset();
62 | pidPos.setK((Double) parameters.get("Ctrl P"), (Double) parameters.get("Ctrl I"),
63 | (Double) parameters.get("Ctrl D"), (Double) parameters.get("Ctrl Limit"), PID.MODE.DERIVATIVE_CALC);
64 | addSeries("Pos");
65 | addSeries("Rate");
66 | addSeries("Acc");
67 | addSeries("Ctrl");
68 | }
69 |
70 | @Override
71 | public void process(double time, Map update) {
72 | if (update.containsKey("ATT.Roll")) { // Act only on attitude updates
73 | if (!Double.isNaN(timePrev)) {
74 | double dt = time - timePrev;
75 | double spRate = 0.0;
76 | if (time > startTime && posSP < startSP) {
77 | spRate = startSPRate;
78 | posSP += startSPRate * dt;
79 | }
80 | double force = propeller.getOutput(time, 0.0);
81 | double acc = force * thrustK - drag * rate;
82 | rate += acc * dt;
83 | pos += rate * dt;
84 | double awuW =
85 | awuRate == 0.0 ? 1.0 : Math.exp(-(spRate * spRate + rate * rate) / 2.0 / awuRate / awuRate);
86 | double thrustControl = pidPos.getOutput(posSP, pos, spRate - rate, dt, awuW);
87 | propeller.setInput(thrustControl);
88 | addPoint(0, time, pos);
89 | addPoint(1, time, rate);
90 | if (accScale != 0.0)
91 | addPoint(2, time, acc * accScale);
92 | addPoint(3, time, pidPos.getIntegral() * 10);
93 | }
94 | timePrev = time;
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/processors/PosRatePIDControlSimulator.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.processors;
2 |
3 | import me.drton.flightplot.processors.tools.LowPassFilter;
4 | import me.drton.flightplot.processors.tools.PID;
5 | import me.drton.jmavlib.conversion.RotationConversion;
6 | import me.drton.jmavlib.processing.Batterworth2pLPF;
7 | import me.drton.jmavlib.processing.DelayLine;
8 | import me.drton.jmavlib.processing.Filter;
9 |
10 | import java.util.HashMap;
11 | import java.util.Map;
12 |
13 | /**
14 | * User: ton Date: 20.06.13 Time: 6:06
15 | */
16 | public class PosRatePIDControlSimulator extends PlotProcessor {
17 | private double timeStep;
18 | private double thrustK;
19 | private double accScale;
20 | private double drag;
21 | private DelayLine delayLine = new DelayLine();
22 | private Filter rateLPF = new Batterworth2pLPF();
23 | private LowPassFilter controlLPF = new LowPassFilter();
24 | private PID pidPos = new PID();
25 | private PID pidRate = new PID();
26 | private double pos;
27 | private double rate;
28 | private double posSP;
29 | private boolean useRateSP;
30 | private double rateSP;
31 | private double timePrev;
32 | private String spField;
33 | private String rateSpField;
34 |
35 | @Override
36 | public Map getDefaultParameters() {
37 | Map params = new HashMap();
38 | params.put("Time Step", 0.004);
39 | params.put("Thrust T", 0.03);
40 | params.put("Thrust Delay", 0.05);
41 | params.put("Thrust K", 200.0);
42 | params.put("Ctrl P", 5.0);
43 | params.put("Ctrl D", 0.0);
44 | params.put("Ctrl Limit", 0.0);
45 | params.put("Ctrl Rate P", 0.1);
46 | params.put("Ctrl Rate I", 0.0);
47 | params.put("Ctrl Rate D", 0.0);
48 | params.put("Ctrl Rate D SP", false);
49 | params.put("Ctrl Rate Limit", 0.0);
50 | params.put("Acc Scale", 1.0);
51 | params.put("Drag", 0.0);
52 | params.put("Use Rate SP", false);
53 | params.put("Rate LPF", 0.0);
54 | params.put("Field SP", "");
55 | params.put("Field Rate SP", "");
56 | return params;
57 | }
58 |
59 | @Override
60 | public void init() {
61 | pos = 0.0;
62 | rate = 0.0;
63 | posSP = 0.0;
64 | rateSP = 0.0;
65 | timePrev = -1.0;
66 | timeStep = (Double) parameters.get("Time Step");
67 | thrustK = (Double) parameters.get("Thrust K");
68 | accScale = (Double) parameters.get("Acc Scale");
69 | drag = (Double) parameters.get("Drag");
70 | useRateSP = (Boolean) parameters.get("Use Rate SP");
71 | delayLine.reset();
72 | delayLine.setDelay((Double) parameters.get("Thrust Delay"));
73 | controlLPF.reset();
74 | controlLPF.setT((Double) parameters.get("Thrust T"));
75 | pidPos.reset();
76 | pidPos.setK((Double) parameters.get("Ctrl P"), 0.0, (Double) parameters.get("Ctrl D"),
77 | (Double) parameters.get("Ctrl Limit"), PID.MODE.DERIVATIVE_SET);
78 | pidRate.reset();
79 | PID.MODE pidRateMode = (Boolean) parameters.get(
80 | "Ctrl Rate D SP") ? PID.MODE.DERIVATIVE_CALC : PID.MODE.DERIVATIVE_CALC_NO_SP;
81 | pidRate.setK((Double) parameters.get("Ctrl Rate P"), (Double) parameters.get("Ctrl Rate I"),
82 | (Double) parameters.get("Ctrl Rate D"), (Double) parameters.get("Ctrl Rate Limit"), pidRateMode);
83 | rateLPF.setCutoffFreqFactor(((Double) parameters.get("Rate LPF")) * timeStep);
84 | spField = (String) parameters.get("Field SP");
85 | rateSpField = (String) parameters.get("Field Rate SP");
86 | addSeries("Rate");
87 | addSeries("Ctrl");
88 | addSeries("Acc");
89 | if (useRateSP) {
90 | addSeries("Rate SP");
91 | } else {
92 | addSeries("Pos SP");
93 | addSeries("Pos");
94 | }
95 | }
96 |
97 | @Override
98 | public void process(double time, Map update) {
99 | if (timePrev < 0.0) {
100 | timePrev = time;
101 | return;
102 | }
103 | while (time > timePrev + timeStep) {
104 | timePrev += timeStep;
105 | updateSimulation(timePrev, timeStep);
106 | }
107 | updateSP(update);
108 | }
109 |
110 | private void updateSP(Map update) {
111 | if (useRateSP) {
112 | Number v = (Number) update.get(rateSpField);
113 | if (v != null) {
114 | rateSP = v.doubleValue();
115 | }
116 | } else {
117 | String[] p = spField.split(" ");
118 | if (p.length > 1) {
119 | int axis = "RPY".indexOf(p[0]);
120 | if (axis >= 0 && axis < 3) {
121 | double[] q = new double[4];
122 | for (int i = 0; i < 4; i++) {
123 | Number v = (Number) update.get(p[i + 1]);
124 | if (v == null) {
125 | return;
126 | }
127 | q[i] = v.doubleValue();
128 | }
129 | double[] euler = RotationConversion.eulerAnglesByQuaternion(q);
130 | posSP = euler[axis];
131 | }
132 | } else {
133 | Number v = (Number) update.get(spField);
134 | if (v != null) {
135 | posSP = v.doubleValue();
136 | }
137 | }
138 | }
139 | }
140 |
141 | private void updateSimulation(double time, double dt) {
142 | Double force = delayLine.getOutput(time, controlLPF.getOutput(time, 0.0));
143 | if (force == null) {
144 | force = 0.0;
145 | }
146 | double acc = force * thrustK - drag * Math.abs(rate) * rate;
147 | rate += acc * dt;
148 | pos += rate * dt;
149 | double rateFiltered = rateLPF.apply(rate);
150 | if (!useRateSP) {
151 | rateSP = pidPos.getOutput(posSP - pos, - rateFiltered, dt);
152 | }
153 | double control = pidRate.getOutput(rateSP, rateFiltered, 0.0, dt, 1.0);
154 | controlLPF.setInput(control);
155 |
156 | addPoint(0, time, rate);
157 | addPoint(1, time, control);
158 | if (accScale != 0.0) {
159 | addPoint(2, time, acc * accScale);
160 | }
161 | if (useRateSP) {
162 | addPoint(3, time, rateSP);
163 | } else {
164 | addPoint(3, time, posSP);
165 | addPoint(4, time, pos);
166 | }
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/processors/ProcessorsList.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.processors;
2 |
3 | import me.drton.flightplot.ColorSupplier;
4 | import me.drton.flightplot.ProcessorPreset;
5 |
6 | import java.lang.reflect.InvocationTargetException;
7 | import java.util.HashMap;
8 | import java.util.Map;
9 | import java.util.Set;
10 |
11 | /**
12 | * User: ton Date: 15.06.13 Time: 12:21
13 | */
14 | public class ProcessorsList {
15 | private Map> processors
16 | = new HashMap>();
17 |
18 | public ProcessorsList() throws InstantiationException, IllegalAccessException {
19 | addProcessorClass(Simple.class);
20 | addProcessorClass(Derivative.class);
21 | addProcessorClass(Abs.class);
22 | addProcessorClass(ATan2.class);
23 | addProcessorClass(PosPIDControlSimulator.class);
24 | addProcessorClass(PosRatePIDControlSimulator.class);
25 | addProcessorClass(PositionEstimator.class);
26 | addProcessorClass(GlobalPositionProjection.class);
27 | addProcessorClass(LandDetector.class);
28 | addProcessorClass(Expression.class);
29 | addProcessorClass(NEDFromBodyProjection.class);
30 | addProcessorClass(Integral.class);
31 | addProcessorClass(Battery.class);
32 | addProcessorClass(PositionEstimatorKF.class);
33 | addProcessorClass(EulerFromQuaternion.class);
34 | addProcessorClass(Text.class);
35 | }
36 |
37 | private void addProcessorClass(Class extends PlotProcessor> processorClass) {
38 | processors.put(processorClass.getSimpleName(), processorClass);
39 | }
40 |
41 | public Set getProcessorsList() {
42 | return processors.keySet();
43 | }
44 |
45 | public PlotProcessor getProcessorInstance(ProcessorPreset processorPreset, double skipOut, Map fieldsList)
46 | throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
47 | Class extends PlotProcessor> procClass = processors.get(processorPreset.getProcessorType());
48 | if (procClass != null) {
49 | PlotProcessor processor = procClass.newInstance();
50 | processor.setSkipOut(skipOut);
51 | processor.setFieldsList(fieldsList);
52 | processor.setParameters(processorPreset.getParameters());
53 | processor.init();
54 | return processor;
55 | } else {
56 | return null;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/processors/Simple.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.processors;
2 |
3 | import me.drton.flightplot.processors.tools.LowPassFilter;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | /**
9 | * User: ton Date: 15.06.13 Time: 12:04
10 | */
11 | public class Simple extends PlotProcessor {
12 | protected String[] param_Fields;
13 | protected double param_Scale;
14 | protected double param_Offset;
15 | protected double param_Delay;
16 | protected LowPassFilter[] lowPassFilters;
17 |
18 | @Override
19 | public Map getDefaultParameters() {
20 | Map params = new HashMap();
21 | params.put("Fields", "ATT.Pitch ATT.Roll");
22 | params.put("Delay", 0.0);
23 | params.put("LPF", 0.0);
24 | params.put("Scale", 1.0);
25 | params.put("Offset", 0.0);
26 | return params;
27 | }
28 |
29 | @Override
30 | public void init() {
31 | param_Fields = ((String) parameters.get("Fields")).split(WHITESPACE_RE);
32 | param_Scale = (Double) parameters.get("Scale");
33 | param_Offset = (Double) parameters.get("Offset");
34 | param_Delay = (Double) parameters.get("Delay");
35 | lowPassFilters = new LowPassFilter[param_Fields.length];
36 | for (int i = 0; i < param_Fields.length; i++) {
37 | LowPassFilter lowPassFilter = new LowPassFilter();
38 | lowPassFilter.setF((Double) parameters.get("LPF"));
39 | lowPassFilters[i] = lowPassFilter;
40 | }
41 | for (String field : param_Fields) {
42 | addSeries(field);
43 | }
44 | }
45 |
46 | protected double preProcessValue(int idx, double time, double in) {
47 | return in;
48 | }
49 |
50 | protected double postProcessValue(int idx, double time, double in) {
51 | return in;
52 | }
53 |
54 | @Override
55 | public void process(double time, Map update) {
56 | for (int i = 0; i < param_Fields.length; i++) {
57 | String field = param_Fields[i];
58 | Object v = update.get(field);
59 | if (v != null && v instanceof Number) {
60 | double out = preProcessValue(i, time, ((Number) v).doubleValue());
61 | if (Double.isNaN(out)) {
62 | addPoint(i, time, Double.NaN);
63 | } else {
64 | out = lowPassFilters[i].getOutput(time, out);
65 | out = postProcessValue(i, time, out);
66 | addPoint(i, time + param_Delay, out * param_Scale + param_Offset);
67 | }
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/processors/Text.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.processors;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | /**
7 | * Created by ton on 29.09.15.
8 | */
9 | public class Text extends PlotProcessor {
10 | protected String param_Field;
11 |
12 | @Override
13 | public Map getDefaultParameters() {
14 | Map params = new HashMap();
15 | params.put("Field", "STATUS_TEXT.text");
16 | return params;
17 | }
18 |
19 | @Override
20 | public void init() {
21 | param_Field = (String) parameters.get("Field");
22 | addMarkersList();
23 | }
24 |
25 | @Override
26 | public void process(double time, Map update) {
27 | Object v = update.get(param_Field);
28 | if (v != null && v instanceof String) {
29 | addMarker(0, time, (String) v);
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/processors/tools/LowPassFilter.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.processors.tools;
2 |
3 | /**
4 | * User: ton Date: 09.03.13 Time: 15:07
5 | */
6 | public class LowPassFilter {
7 | private double inLast = 0.0;
8 | private double valueFiltered = 0.0;
9 | private double tLast = Double.NaN;
10 | private double f = 1.0;
11 | private double rc_inv = f * 2 * Math.PI;
12 |
13 | public void setF(double f) {
14 | this.f = f;
15 | this.rc_inv = f * 2 * Math.PI;
16 | }
17 |
18 | public void setT(double t) {
19 | if (t == 0.0) {
20 | this.f = 0.0;
21 | this.rc_inv = 0.0;
22 | } else {
23 | this.f = 1 / t;
24 | this.rc_inv = f * 2 * Math.PI;
25 | }
26 | }
27 |
28 | public void reset() {
29 | tLast = Double.NaN;
30 | }
31 |
32 | public double getOutput(double t, double in) {
33 | if (rc_inv == 0.0) {
34 | this.valueFiltered = in;
35 | return in;
36 | } else {
37 | if (Double.isNaN(tLast)) {
38 | this.tLast = t;
39 | this.inLast = in;
40 | this.valueFiltered = in;
41 | return in;
42 | } else {
43 | double dt = t - tLast;
44 | this.valueFiltered += (1.0 - Math.exp(-dt * rc_inv)) * (inLast - valueFiltered);
45 | this.inLast = in;
46 | this.tLast = t;
47 | return valueFiltered;
48 | }
49 | }
50 | }
51 |
52 | public void setInput(double in) {
53 | this.inLast = in;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/me/drton/flightplot/processors/tools/PID.java:
--------------------------------------------------------------------------------
1 | package me.drton.flightplot.processors.tools;
2 |
3 | /**
4 | * User: ton Date: 20.06.13 Time: 7:13
5 | */
6 | public class PID {
7 | public static enum MODE {
8 | DERIVATIVE_SET,
9 | DERIVATIVE_CALC,
10 | DERIVATIVE_CALC_NO_SP,
11 | }
12 |
13 | private double kP = 0.0;
14 | private double kI = 0.0;
15 | private double kD = 0.0;
16 | private double limit = 0.0;
17 | private double integral = 0.0;
18 | private double errorLast = 0.0;
19 | private MODE mode = MODE.DERIVATIVE_CALC;
20 |
21 | public void setK(double kP, double kI, double kD, double limit, MODE mode) {
22 | this.kP = kP;
23 | this.kI = kI;
24 | this.kD = kD;
25 | this.limit = limit;
26 | this.mode = mode;
27 | }
28 |
29 | public void reset() {
30 | errorLast = 0.0;
31 | integral = 0.0;
32 | }
33 |
34 | public double getIntegral() {
35 | return integral;
36 | }
37 |
38 | public void setIntegral(double integral) {
39 | this.integral = integral;
40 | }
41 |
42 | private double limitValue(double value) {
43 | if (limit == 0.0)
44 | return value;
45 | else
46 | return Math.max(-limit, Math.min(limit, value));
47 | }
48 |
49 | public double getOutput(double err, double dt) {
50 | if (mode != MODE.DERIVATIVE_CALC)
51 | throw new RuntimeException("Can't use this method in mode " + mode);
52 | double d = (err - errorLast) / dt;
53 | errorLast = err;
54 | double pd = err * kP + d * kD;
55 | integral += pd / kP * kI * dt;
56 | integral = limitValue(integral);
57 | return limitValue(pd + integral);
58 | }
59 |
60 | public double getOutput(double err, double derivative, double dt) {
61 | if (mode != MODE.DERIVATIVE_SET)
62 | throw new RuntimeException("Can't use this method in mode " + mode);
63 | errorLast = err;
64 | double pd = err * kP + derivative * kD;
65 | integral += pd / kP * kI * dt;
66 | integral = limitValue(integral);
67 | return limitValue(pd + integral);
68 | }
69 |
70 | public double getOutput(double sp, double value, double derivative, double dt, double awuW) {
71 | double err = sp - value;
72 | double d;
73 | if (mode == MODE.DERIVATIVE_SET) {
74 | d = derivative;
75 | errorLast = err;
76 | } else if (mode == MODE.DERIVATIVE_CALC) {
77 | d = (err - errorLast) / dt;
78 | errorLast = err;
79 | } else if (mode == MODE.DERIVATIVE_CALC_NO_SP) {
80 | d = (-value - errorLast) / dt;
81 | errorLast = -value;
82 | } else {
83 | d = 0.0;
84 | errorLast = 0.0;
85 | }
86 | double pd = err * kP + d * kD;
87 | double i = integral + err * kI * dt * awuW;
88 | if (limit == 0.0 || (Math.abs(i) < limit && Math.abs(pd + i) < limit))
89 | integral = limitValue(i);
90 | return limitValue(pd + integral);
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/net/objecthunter/exp4j/Expression.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Frank Asseg
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package net.objecthunter.exp4j;
17 |
18 | import java.util.*;
19 | import java.util.concurrent.*;
20 |
21 | import net.objecthunter.exp4j.function.Function;
22 | import net.objecthunter.exp4j.operator.Operator;
23 | import net.objecthunter.exp4j.tokenizer.*;
24 |
25 | public class Expression {
26 |
27 | private final Token[] tokens;
28 |
29 | private final Map variables;
30 |
31 | private final Set userFunctionNames;
32 |
33 |
34 | Expression(final Token[] tokens) {
35 | this.tokens = tokens;
36 | this.variables = new HashMap(4);
37 | this.userFunctionNames = Collections.emptySet();
38 | }
39 |
40 | Expression(final Token[] tokens, Set userFunctionNames) {
41 | this.tokens = tokens;
42 | this.variables = new HashMap(4);
43 | this.userFunctionNames = userFunctionNames;
44 | }
45 |
46 | public Expression setVariable(final String name, final double value) {
47 | this.checkVariableName(name);
48 | this.variables.put(name, value);
49 | return this;
50 | }
51 |
52 | private void checkVariableName(String name) {
53 | if (this.userFunctionNames.contains(name)) {
54 | throw new IllegalArgumentException("The setVariable name '" + name + "' is invalid. Since there exists a function with the same name");
55 | }
56 | }
57 |
58 | public Expression setVariables(Map variables) {
59 | for (Map.Entry v : variables.entrySet()) {
60 | this.setVariable(v.getKey(), v.getValue());
61 | }
62 | return this;
63 | }
64 |
65 | public ValidationResult validate(boolean checkVariablesSet) {
66 | final List errors = new ArrayList(0);
67 | if (checkVariablesSet) {
68 | /* check that all vars have a value set */
69 | for (final Token t : this.tokens) {
70 | if (t.getType() == Token.TOKEN_VARIABLE) {
71 | final String var = ((VariableToken) t).getName();
72 | if (!variables.containsKey(var)) {
73 | errors.add("The setVariable '" + var + "' has not been set");
74 | }
75 | }
76 | }
77 | }
78 |
79 | /* Check if the number of operands, functions and operators match.
80 | The idea is to increment a counter for operands and decrease it for operators.
81 | When a function occurs the number of available arguments has to be greater
82 | than or equals to the function's expected number of arguments.
83 | The count has to be larger than 1 at all times and exactly 1 after all tokens
84 | have been processed */
85 | int count = 0;
86 | for (Token tok : this.tokens) {
87 | switch (tok.getType()) {
88 | case Token.TOKEN_NUMBER:
89 | case Token.TOKEN_VARIABLE:
90 | count++;
91 | break;
92 | case Token.TOKEN_FUNCTION:
93 | final Function func = ((FunctionToken) tok).getFunction();
94 | if (func.getNumArguments() > count) {
95 | errors.add("Not enough arguments for '" + func.getName() + "'");
96 | }
97 | break;
98 | case Token.TOKEN_OPERATOR:
99 | Operator op = ((OperatorToken) tok).getOperator();
100 | if (op.getNumOperands() == 2) {
101 | count--;
102 | }
103 | break;
104 | }
105 | if (count < 1) {
106 | errors.add("Too many operators");
107 | return new ValidationResult(false, errors);
108 | }
109 | }
110 | if (count > 1) {
111 | errors.add("Too many operands");
112 | }
113 | return errors.size() == 0 ? ValidationResult.SUCCESS : new ValidationResult(false, errors);
114 |
115 | }
116 |
117 | public ValidationResult validate() {
118 | return validate(true);
119 | }
120 |
121 | public Future evaluateAsync(ExecutorService executor) {
122 | return executor.submit(new Callable() {
123 | @Override
124 | public Double call() throws Exception {
125 | return evaluate();
126 | }
127 | });
128 | }
129 |
130 | public double evaluate() {
131 | final Stack output = new Stack();
132 | for (int i = 0; i < tokens.length; i++) {
133 | Token t = tokens[i];
134 | if (t.getType() == Token.TOKEN_NUMBER) {
135 | output.push(((NumberToken) t).getValue());
136 | } else if (t.getType() == Token.TOKEN_VARIABLE) {
137 | final String name = ((VariableToken) t).getName();
138 | final Double value = this.variables.get(name);
139 | if (value == null) {
140 | throw new IllegalArgumentException("No value has been set for the setVariable '" + name + "'.");
141 | }
142 | output.push(value);
143 | } else if (t.getType() == Token.TOKEN_OPERATOR) {
144 | OperatorToken op = (OperatorToken) t;
145 | if (output.size() < op.getOperator().getNumOperands()) {
146 | throw new IllegalArgumentException("Invalid number of operands available");
147 | }
148 | if (op.getOperator().getNumOperands() == 2) {
149 | /* pop the operands and push the result of the operation */
150 | double rightArg = output.pop();
151 | double leftArg = output.pop();
152 | output.push(op.getOperator().apply(leftArg, rightArg));
153 | } else if (op.getOperator().getNumOperands() == 1) {
154 | /* pop the operand and push the result of the operation */
155 | double arg = output.pop();
156 | output.push(op.getOperator().apply(arg));
157 | }
158 | } else if (t.getType() == Token.TOKEN_FUNCTION) {
159 | FunctionToken func = (FunctionToken) t;
160 | if (output.size() < func.getFunction().getNumArguments()) {
161 | throw new IllegalArgumentException("Invalid number of arguments available");
162 | }
163 | /* collect the arguments from the stack */
164 | double[] args = new double[func.getFunction().getNumArguments()];
165 | for (int j = 0; j < func.getFunction().getNumArguments(); j++) {
166 | args[j] = output.pop();
167 | }
168 | output.push(func.getFunction().apply(this.reverseInPlace(args)));
169 | }
170 | }
171 | if (output.size() > 1) {
172 | throw new IllegalArgumentException("Invalid number of items on the output queue. Might be caused by an invalid number of arguments for a function.");
173 | }
174 | return output.pop();
175 | }
176 |
177 | private double[] reverseInPlace(double[] args) {
178 | int len = args.length;
179 | for (int i = 0; i < len / 2; i++) {
180 | double tmp = args[i];
181 | args[i] = args[len - i - 1];
182 | args[len - i - 1] = tmp;
183 | }
184 | return args;
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/src/net/objecthunter/exp4j/ExpressionBuilder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Frank Asseg
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.objecthunter.exp4j;
18 |
19 | import java.util.*;
20 |
21 | import net.objecthunter.exp4j.function.Function;
22 | import net.objecthunter.exp4j.operator.Operator;
23 | import net.objecthunter.exp4j.shuntingyard.ShuntingYard;
24 |
25 | /**
26 | * Factory class for {@link Expression} instances. This class is the main API entrypoint. Users should create new
27 | * {@link Expression} instances using this factory class.
28 | */
29 | public class ExpressionBuilder {
30 |
31 | private final String expression;
32 |
33 | private final Map userFunctions;
34 |
35 | private final Map userOperators;
36 |
37 | private final Set variableNames;
38 |
39 | /**
40 | * Create a new ExpressionBuilder instance and initialize it with a given expression string.
41 | * @param expression the expression to be parsed
42 | */
43 | public ExpressionBuilder(String expression) {
44 | if (expression == null || expression.trim().length() == 0) {
45 | throw new IllegalArgumentException("Expression can not be empty");
46 | }
47 | this.expression = expression;
48 | this.userOperators = new HashMap(4);
49 | this.userFunctions = new HashMap(4);
50 | this.variableNames = new HashSet(4);
51 | }
52 |
53 | /**
54 | * Add a {@link net.objecthunter.exp4j.function.Function} implementation available for use in the expression
55 | * @param function the custom {@link net.objecthunter.exp4j.function.Function} implementation that should be available for use in the expression.
56 | * @return the ExpressionBuilder instance
57 | */
58 | public ExpressionBuilder function(Function function) {
59 | this.userFunctions.put(function.getName(), function);
60 | return this;
61 | }
62 |
63 | /**
64 | * Add multiple {@link net.objecthunter.exp4j.function.Function} implementations available for use in the expression
65 | * @param functions the custom {@link net.objecthunter.exp4j.function.Function} implementations
66 | * @return the ExpressionBuilder instance
67 | */
68 | public ExpressionBuilder functions(Function... functions) {
69 | for (Function f : functions) {
70 | this.userFunctions.put(f.getName(), f);
71 | }
72 | return this;
73 | }
74 |
75 | /**
76 | * Add multiple {@link net.objecthunter.exp4j.function.Function} implementations available for use in the expression
77 | * @param functions A {@link java.util.List} of custom {@link net.objecthunter.exp4j.function.Function} implementations
78 | * @return the ExpressionBuilder instance
79 | */
80 | public ExpressionBuilder functions(List functions) {
81 | for (Function f : functions) {
82 | this.userFunctions.put(f.getName(), f);
83 | }
84 | return this;
85 | }
86 |
87 | public ExpressionBuilder variables(Set variableNames) {
88 | this.variableNames.addAll(variableNames);
89 | return this;
90 | }
91 |
92 | public ExpressionBuilder variables(String ... variableNames) {
93 | Collections.addAll(this.variableNames, variableNames);
94 | return this;
95 | }
96 |
97 | public ExpressionBuilder variable(String variableName) {
98 | this.variableNames.add(variableName);
99 | return this;
100 | }
101 |
102 | /**
103 | * Add an {@link net.objecthunter.exp4j.operator.Operator} which should be available for use in the expression
104 | * @param operator the custom {@link net.objecthunter.exp4j.operator.Operator} to add
105 | * @return the ExpressionBuilder instance
106 | */
107 | public ExpressionBuilder operator(Operator operator) {
108 | this.checkOperatorSymbol(operator);
109 | this.userOperators.put(operator.getSymbol(), operator);
110 | return this;
111 | }
112 |
113 | private void checkOperatorSymbol(Operator op) {
114 | String name = op.getSymbol();
115 | for (char ch : name.toCharArray()) {
116 | if (!Operator.isAllowedOperatorChar(ch)) {
117 | throw new IllegalArgumentException("The operator symbol '" + name + "' is invalid");
118 | }
119 | }
120 | }
121 |
122 | /**
123 | * Add multiple {@link net.objecthunter.exp4j.operator.Operator} implementations which should be available for use in the expression
124 | * @param operators the set of custom {@link net.objecthunter.exp4j.operator.Operator} implementations to add
125 | * @return the ExpressionBuilder instance
126 | */
127 | public ExpressionBuilder operator(Operator... operators) {
128 | for (Operator o : operators) {
129 | this.operator(o);
130 | }
131 | return this;
132 | }
133 |
134 | /**
135 | * Add multiple {@link net.objecthunter.exp4j.operator.Operator} implementations which should be available for use in the expression
136 | * @param operators the {@link java.util.List} of custom {@link net.objecthunter.exp4j.operator.Operator} implementations to add
137 | * @return the ExpressionBuilder instance
138 | */
139 | public ExpressionBuilder operator(List operators) {
140 | for (Operator o : operators) {
141 | this.operator(o);
142 | }
143 | return this;
144 | }
145 |
146 | /**
147 | * Build the {@link Expression} instance using the custom operators and functions set.
148 | * @return an {@link Expression} instance which can be used to evaluate the result of the expression
149 | */
150 | public Expression build() {
151 | if (expression.length() == 0) {
152 | throw new IllegalArgumentException("The expression can not be empty");
153 | }
154 | return new Expression(ShuntingYard.convertToRPN(this.expression, this.userFunctions, this.userOperators, this.variableNames),
155 | this.userFunctions.keySet());
156 | }
157 |
158 | }
159 |
--------------------------------------------------------------------------------
/src/net/objecthunter/exp4j/ValidationResult.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Frank Asseg
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package net.objecthunter.exp4j;
17 |
18 | import java.util.List;
19 |
20 | /**
21 | * Contains the validation result for a given {@link Expression}
22 | */
23 | public class ValidationResult {
24 | private final boolean valid;
25 | private final List errors;
26 |
27 | /**
28 | * Create a new instance
29 | * @param valid Whether the validation of the expression was successful
30 | * @param errors The list of errors returned if the validation was unsuccessful
31 | */
32 | public ValidationResult(boolean valid, List errors) {
33 | this.valid = valid;
34 | this.errors = errors;
35 | }
36 |
37 | /**
38 | * Check if an expression has been validated successfully
39 | * @return true if the validation was successful, false otherwise
40 | */
41 | public boolean isValid() {
42 | return valid;
43 | }
44 |
45 | /**
46 | * Get the list of errors describing the issues while validating the expression
47 | * @return The List of errors
48 | */
49 | public List getErrors() {
50 | return errors;
51 | }
52 |
53 | /**
54 | * A static class representing a successful validation result
55 | */
56 | public static final ValidationResult SUCCESS = new ValidationResult(true, null);
57 | }
58 |
--------------------------------------------------------------------------------
/src/net/objecthunter/exp4j/function/Function.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Frank Asseg
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package net.objecthunter.exp4j.function;
17 |
18 | /**
19 | * A class representing a Function which can be used in an expression
20 | */
21 | public abstract class Function {
22 |
23 | protected final String name;
24 |
25 | protected final int numArguments;
26 |
27 | /**
28 | * Create a new Function with a given name and number of arguments
29 | * @param name the name of the Function
30 | * @param numArguments the number of arguments the function takes
31 | */
32 | public Function(String name, int numArguments) {
33 | if (numArguments < 0) {
34 | throw new IllegalArgumentException("The number of function arguments can not be less than 0 for '" +
35 | name + "'");
36 | }
37 | if (!isValidFunctionName(name)) {
38 | throw new IllegalArgumentException("The function name '" + name +"' is invalid");
39 | }
40 | this.name = name;
41 | this.numArguments = numArguments;
42 |
43 | }
44 |
45 |
46 | /**
47 | * Create a new Function with a given name that takes a single argument
48 | * @param name the name of the Function
49 | */
50 | public Function(String name) {
51 | this(name, 1);
52 | }
53 |
54 | /**
55 | * Get the name of the Function
56 | * @return the name
57 | */
58 | public String getName() {
59 | return name;
60 | }
61 |
62 | /**
63 | * Get the number of arguments for this function
64 | * @return the number of arguments
65 | */
66 | public int getNumArguments() {
67 | return numArguments;
68 | }
69 |
70 | /**
71 | * Method that does the actual calculation of the function value given the arguments
72 | * @param args the set of arguments used for calculating the function
73 | * @return the result of the function evaluation
74 | */
75 | public abstract double apply(double... args);
76 |
77 | /**
78 | * Get the set of characters which are allowed for use in Function names.
79 | * @return the set of characters allowed
80 | */
81 | public static char[] getAllowedFunctionCharacters() {
82 | char[] chars = new char[53];
83 | int count = 0;
84 | for (int i = 65; i < 91; i++) {
85 | chars[count++] = (char) i;
86 | }
87 | for (int i = 97; i < 123; i++) {
88 | chars[count++] = (char) i;
89 | }
90 | chars[count] = '_';
91 | return chars;
92 | }
93 |
94 | public static boolean isValidFunctionName(final String name) {
95 | if (name == null) {
96 | return false;
97 | }
98 |
99 | final int size = name.length();
100 |
101 | if (size == 0) {
102 | return false;
103 | }
104 |
105 | for (int i=0;i< size;i++) {
106 | final char c = name.charAt(i);
107 | if (Character.isLetter(c) || c == '_') {
108 | continue;
109 | } else if (Character.isDigit(c) && i > 0) {
110 | continue;
111 | }
112 | return false;
113 | }
114 | return true;
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/net/objecthunter/exp4j/function/Functions.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Frank Asseg
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package net.objecthunter.exp4j.function;
17 |
18 | /**
19 | * Class representing the builtin functions available for use in expressions
20 | */
21 | public class Functions {
22 | private static final int INDEX_SIN = 0;
23 | private static final int INDEX_COS = 1;
24 | private static final int INDEX_TAN = 2;
25 | private static final int INDEX_LOG = 3;
26 | private static final int INDEX_LOG1P = 4;
27 | private static final int INDEX_ABS = 5;
28 | private static final int INDEX_ACOS = 6;
29 | private static final int INDEX_ASIN = 7;
30 | private static final int INDEX_ATAN = 8;
31 | private static final int INDEX_CBRT = 9;
32 | private static final int INDEX_CEIL = 10;
33 | private static final int INDEX_FLOOR = 11;
34 | private static final int INDEX_SINH = 12;
35 | private static final int INDEX_SQRT = 13;
36 | private static final int INDEX_TANH = 14;
37 | private static final int INDEX_COSH = 15;
38 | private static final int INDEX_POW = 16;
39 | private static final int INDEX_EXP = 17;
40 | private static final int INDEX_EXPM1 = 18;
41 | private static final int INDEX_LOG10 = 19;
42 | private static final int INDEX_LOG2 = 20;
43 |
44 | private static final Function[] builtinFunctions = new Function[21];
45 |
46 | static {
47 | builtinFunctions[INDEX_SIN] = new Function("sin") {
48 | @Override
49 | public double apply(double... args) {
50 | return Math.sin(args[0]);
51 | }
52 | };
53 | builtinFunctions[INDEX_COS] = new Function("cos") {
54 | @Override
55 | public double apply(double... args) {
56 | return Math.cos(args[0]);
57 | }
58 | };
59 | builtinFunctions[INDEX_TAN] = new Function("tan") {
60 | @Override
61 | public double apply(double... args) {
62 | return Math.tan(args[0]);
63 | }
64 | };
65 | builtinFunctions[INDEX_LOG] = new Function("log") {
66 | @Override
67 | public double apply(double... args) {
68 | return Math.log(args[0]);
69 | }
70 | };
71 | builtinFunctions[INDEX_LOG2] = new Function("log2") {
72 | @Override
73 | public double apply(double... args) {
74 | return Math.log(args[0]) / Math.log(2d);
75 | }
76 | };
77 | builtinFunctions[INDEX_LOG10] = new Function("log10") {
78 | @Override
79 | public double apply(double... args) {
80 | return Math.log10(args[0]);
81 | }
82 | };
83 | builtinFunctions[INDEX_LOG1P] = new Function("log1p") {
84 | @Override
85 | public double apply(double... args) {
86 | return Math.log1p(args[0]);
87 | }
88 | };
89 | builtinFunctions[INDEX_ABS] = new Function("abs") {
90 | @Override
91 | public double apply(double... args) {
92 | return Math.abs(args[0]);
93 | }
94 | };
95 | builtinFunctions[INDEX_ACOS] = new Function("acos") {
96 | @Override
97 | public double apply(double... args) {
98 | return Math.acos(args[0]);
99 | }
100 | };
101 | builtinFunctions[INDEX_ASIN] = new Function("asin") {
102 | @Override
103 | public double apply(double... args) {
104 | return Math.asin(args[0]);
105 | }
106 | };
107 | builtinFunctions[INDEX_ATAN] = new Function("atan") {
108 | @Override
109 | public double apply(double... args) {
110 | return Math.atan(args[0]);
111 | }
112 | };
113 | builtinFunctions[INDEX_CBRT] = new Function("cbrt") {
114 | @Override
115 | public double apply(double... args) {
116 | return Math.cbrt(args[0]);
117 | }
118 | };
119 | builtinFunctions[INDEX_FLOOR] = new Function("floor") {
120 | @Override
121 | public double apply(double... args) {
122 | return Math.floor(args[0]);
123 | }
124 | };
125 | builtinFunctions[INDEX_SINH] = new Function("sinh") {
126 | @Override
127 | public double apply(double... args) {
128 | return Math.sinh(args[0]);
129 | }
130 | };
131 | builtinFunctions[INDEX_SQRT] = new Function("sqrt") {
132 | @Override
133 | public double apply(double... args) {
134 | return Math.sqrt(args[0]);
135 | }
136 | };
137 | builtinFunctions[INDEX_TANH] = new Function("tanh") {
138 | @Override
139 | public double apply(double... args) {
140 | return Math.tanh(args[0]);
141 | }
142 | };
143 | builtinFunctions[INDEX_COSH] = new Function("cosh") {
144 | @Override
145 | public double apply(double... args) {
146 | return Math.cosh(args[0]);
147 | }
148 | };
149 | builtinFunctions[INDEX_CEIL] = new Function("ceil") {
150 | @Override
151 | public double apply(double... args) {
152 | return Math.ceil(args[0]);
153 | }
154 | };
155 | builtinFunctions[INDEX_POW] = new Function("pow", 2) {
156 | @Override
157 | public double apply(double... args) {
158 | return Math.pow(args[0], args[1]);
159 | }
160 | };
161 | builtinFunctions[INDEX_EXP] = new Function("exp", 1) {
162 | @Override
163 | public double apply(double... args) {
164 | return Math.exp(args[0]);
165 | }
166 | };
167 | builtinFunctions[INDEX_EXPM1] = new Function("expm1", 1) {
168 | @Override
169 | public double apply(double... args) {
170 | return Math.expm1(args[0]);
171 | }
172 | };
173 | }
174 |
175 | /**
176 | * Get the builtin function for a given name
177 | * @param name te name of the function
178 | * @return a Function instance
179 | */
180 | public static Function getBuiltinFunction(final String name) {
181 |
182 | if (name.equals("sin")) {
183 | return builtinFunctions[INDEX_SIN];
184 | } else if (name.equals("cos")) {
185 | return builtinFunctions[INDEX_COS];
186 | } else if (name.equals("tan")) {
187 | return builtinFunctions[INDEX_TAN];
188 | } else if (name.equals("asin")) {
189 | return builtinFunctions[INDEX_ASIN];
190 | } else if (name.equals("acos")) {
191 | return builtinFunctions[INDEX_ACOS];
192 | } else if (name.equals("atan")) {
193 | return builtinFunctions[INDEX_ATAN];
194 | } else if (name.equals("sinh")) {
195 | return builtinFunctions[INDEX_SINH];
196 | } else if (name.equals("cosh")) {
197 | return builtinFunctions[INDEX_COSH];
198 | } else if (name.equals("tanh")) {
199 | return builtinFunctions[INDEX_TANH];
200 | } else if (name.equals("abs")) {
201 | return builtinFunctions[INDEX_ABS];
202 | } else if (name.equals("log")) {
203 | return builtinFunctions[INDEX_LOG];
204 | } else if (name.equals("log10")) {
205 | return builtinFunctions[INDEX_LOG10];
206 | } else if (name.equals("log2")) {
207 | return builtinFunctions[INDEX_LOG2];
208 | } else if (name.equals("log1p")) {
209 | return builtinFunctions[INDEX_LOG1P];
210 | } else if (name.equals("ceil")) {
211 | return builtinFunctions[INDEX_CEIL];
212 | } else if (name.equals("floor")) {
213 | return builtinFunctions[INDEX_FLOOR];
214 | } else if (name.equals("sqrt")) {
215 | return builtinFunctions[INDEX_SQRT];
216 | } else if (name.equals("cbrt")) {
217 | return builtinFunctions[INDEX_CBRT];
218 | } else if (name.equals("pow")) {
219 | return builtinFunctions[INDEX_POW];
220 | } else if (name.equals("exp")) {
221 | return builtinFunctions[INDEX_EXP];
222 | } else if (name.equals("expm1")) {
223 | return builtinFunctions[INDEX_EXPM1];
224 | } else {
225 | return null;
226 | }
227 | }
228 |
229 | }
230 |
--------------------------------------------------------------------------------
/src/net/objecthunter/exp4j/operator/Operator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Frank Asseg
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package net.objecthunter.exp4j.operator;
17 |
18 | /**
19 | * Class representing operators that can be used in an expression
20 | */
21 | public abstract class Operator {
22 | /**
23 | * The precedence value for the addition operation
24 | */
25 | public static final int PRECEDENCE_ADDITION = 500;
26 | /**
27 | * The precedence value for the subtraction operation
28 | */
29 | public static final int PRECEDENCE_SUBTRACTION = PRECEDENCE_ADDITION;
30 | /**
31 | * The precedence value for the multiplication operation
32 | */
33 | public static final int PRECEDENCE_MULTIPLICATION = 1000;
34 | /**
35 | * The precedence value for the division operation
36 | */
37 | public static final int PRECEDENCE_DIVISION = PRECEDENCE_MULTIPLICATION;
38 | /**
39 | * The precedence value for the modulo operation
40 | */
41 | public static final int PRECEDENCE_MODULO = PRECEDENCE_DIVISION;
42 | /**
43 | * The precedence value for the power operation
44 | */
45 | public static final int PRECEDENCE_POWER = 10000;
46 | /**
47 | * The precedence value for the unary minus operation
48 | */
49 | public static final int PRECEDENCE_UNARY_MINUS = 5000;
50 | /**
51 | * The precedence value for the unary plus operation
52 | */
53 | public static final int PRECEDENCE_UNARY_PLUS = PRECEDENCE_UNARY_MINUS;
54 |
55 | /**
56 | * The set of allowed operator chars
57 | */
58 | public static final char[] ALLOWED_OPERATOR_CHARS = { '+', '-', '*', '/',
59 | '%', '^', '!', '#', '§', '$', '&', ';', ':', '~', '<', '>', '|',
60 | '='};
61 |
62 | protected final int numOperands;
63 | protected final boolean leftAssociative;
64 | protected final String symbol;
65 | protected final int precedence;
66 |
67 | /**
68 | * Create a new operator for use in expressions
69 | * @param symbol the symbol of the operator
70 | * @param numberOfOperands the number of operands the operator takes (1 or 2)
71 | * @param leftAssociative set to true if the operator is left associative, false if it is right associative
72 | * @param precedence the precedence value of the operator
73 | */
74 | public Operator(String symbol, int numberOfOperands, boolean leftAssociative,
75 | int precedence) {
76 | super();
77 | this.numOperands = numberOfOperands;
78 | this.leftAssociative = leftAssociative;
79 | this.symbol = symbol;
80 | this.precedence = precedence;
81 | }
82 |
83 | /**
84 | * Check if a character is an allowed operator char
85 | * @param ch the char to check
86 | * @return true if the char is allowed an an operator symbol, false otherwise
87 | */
88 | public static boolean isAllowedOperatorChar(char ch) {
89 | for (char allowed: ALLOWED_OPERATOR_CHARS) {
90 | if (ch == allowed) {
91 | return true;
92 | }
93 | }
94 | return false;
95 | }
96 |
97 | /**
98 | * Check if the operator is left associative
99 | * @return true os the operator is left associative, false otherwise
100 | */
101 | public boolean isLeftAssociative() {
102 | return leftAssociative;
103 | }
104 |
105 | /**
106 | * Check the precedence value for the operator
107 | * @return the precedence value
108 | */
109 | public int getPrecedence() {
110 | return precedence;
111 | }
112 |
113 | /**
114 | * Apply the operation on the given operands
115 | * @param args the operands for the operation
116 | * @return the calculated result of the operation
117 | */
118 | public abstract double apply(double ... args);
119 |
120 | /**
121 | * Get the operator symbol
122 | * @return the symbol
123 | */
124 | public String getSymbol() {
125 | return symbol;
126 | }
127 |
128 | /**
129 | * Get the number of operands
130 | * @return the number of operands
131 | */
132 | public int getNumOperands() {
133 | return numOperands;
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/net/objecthunter/exp4j/operator/Operators.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Frank Asseg
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package net.objecthunter.exp4j.operator;
17 |
18 | public abstract class Operators {
19 | private static final int INDEX_ADDITION = 0;
20 | private static final int INDEX_SUBTRACTION = 1;
21 | private static final int INDEX_MUTLIPLICATION = 2;
22 | private static final int INDEX_DIVISION = 3;
23 | private static final int INDEX_POWER = 4;
24 | private static final int INDEX_MODULO = 5;
25 | private static final int INDEX_UNARYMINUS = 6;
26 | private static final int INDEX_UNARYPLUS = 7;
27 |
28 | private static final Operator[] builtinOperators = new Operator[8];
29 |
30 | static {
31 | builtinOperators[INDEX_ADDITION]= new Operator("+", 2, true, Operator.PRECEDENCE_ADDITION) {
32 | @Override
33 | public double apply(final double... args) {
34 | return args[0] + args[1];
35 | }
36 | };
37 | builtinOperators[INDEX_SUBTRACTION]= new Operator("-", 2, true, Operator.PRECEDENCE_ADDITION) {
38 | @Override
39 | public double apply(final double... args) {
40 | return args[0] - args[1];
41 | }
42 | };
43 | builtinOperators[INDEX_UNARYMINUS]= new Operator("-", 1, false, Operator.PRECEDENCE_UNARY_MINUS) {
44 | @Override
45 | public double apply(final double... args) {
46 | return -args[0];
47 | }
48 | };
49 | builtinOperators[INDEX_UNARYPLUS]= new Operator("+", 1, false, Operator.PRECEDENCE_UNARY_PLUS) {
50 | @Override
51 | public double apply(final double... args) {
52 | return args[0];
53 | }
54 | };
55 | builtinOperators[INDEX_MUTLIPLICATION]= new Operator("*", 2, true, Operator.PRECEDENCE_MULTIPLICATION) {
56 | @Override
57 | public double apply(final double... args) {
58 | return args[0] * args[1];
59 | }
60 | };
61 | builtinOperators[INDEX_DIVISION]= new Operator("/", 2, true, Operator.PRECEDENCE_DIVISION) {
62 | @Override
63 | public double apply(final double... args) {
64 | if (args[1] == 0d) {
65 | throw new ArithmeticException("Division by zero!");
66 | }
67 | return args[0] / args[1];
68 | }
69 | };
70 | builtinOperators[INDEX_POWER]= new Operator("^", 2, false, Operator.PRECEDENCE_POWER) {
71 | @Override
72 | public double apply(final double... args) {
73 | return Math.pow(args[0], args[1]);
74 | }
75 | };
76 | builtinOperators[INDEX_MODULO]= new Operator("%", 2, true, Operator.PRECEDENCE_MODULO) {
77 | @Override
78 | public double apply(final double... args) {
79 | if (args[1] == 0d) {
80 | throw new ArithmeticException("Division by zero!");
81 | }
82 | return args[0] % args[1];
83 | }
84 | };
85 | }
86 |
87 | public static Operator getBuiltinOperator(final char symbol, final int numArguments) {
88 | switch(symbol) {
89 | case '+':
90 | if (numArguments != 1) {
91 | return builtinOperators[INDEX_ADDITION];
92 | }else{
93 | return builtinOperators[INDEX_UNARYPLUS];
94 | }
95 | case '-':
96 | if (numArguments != 1) {
97 | return builtinOperators[INDEX_SUBTRACTION];
98 | }else{
99 | return builtinOperators[INDEX_UNARYMINUS];
100 | }
101 | case '*':
102 | return builtinOperators[INDEX_MUTLIPLICATION];
103 | case '/':
104 | return builtinOperators[INDEX_DIVISION];
105 | case '^':
106 | return builtinOperators[INDEX_POWER];
107 | case '%':
108 | return builtinOperators[INDEX_MODULO];
109 | default:
110 | return null;
111 | }
112 | }
113 |
114 | }
115 |
--------------------------------------------------------------------------------
/src/net/objecthunter/exp4j/shuntingyard/ShuntingYard.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Frank Asseg
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package net.objecthunter.exp4j.shuntingyard;
17 |
18 | import java.util.*;
19 |
20 | import net.objecthunter.exp4j.function.Function;
21 | import net.objecthunter.exp4j.operator.Operator;
22 | import net.objecthunter.exp4j.tokenizer.OperatorToken;
23 | import net.objecthunter.exp4j.tokenizer.Token;
24 | import net.objecthunter.exp4j.tokenizer.Tokenizer;
25 |
26 | /**
27 | * Shunting yard implementation to convert infix to reverse polish notation
28 | */
29 | public class ShuntingYard {
30 |
31 | /**
32 | * Convert a Set of tokens from infix to reverse polish notation
33 | * @param expression the expression to convert
34 | * @param userFunctions the custom functions used
35 | * @param userOperators the custom operators used
36 | * @param variableNames the variable names used in the expression
37 | * @return a {@link net.objecthunter.exp4j.tokenizer.Token} array containing the result
38 | */
39 | public static Token[] convertToRPN(final String expression, final Map userFunctions,
40 | final Map userOperators, final Set variableNames){
41 | final Stack stack = new Stack();
42 | final List output = new ArrayList();
43 |
44 | final Tokenizer tokenizer = new Tokenizer(expression, userFunctions, userOperators, variableNames);
45 | while (tokenizer.hasNext()) {
46 | Token token = tokenizer.nextToken();
47 | switch (token.getType()) {
48 | case Token.TOKEN_NUMBER:
49 | case Token.TOKEN_VARIABLE:
50 | output.add(token);
51 | break;
52 | case Token.TOKEN_FUNCTION:
53 | stack.add(token);
54 | break;
55 | case Token.TOKEN_SEPARATOR:
56 | while (!stack.empty() && stack.peek().getType() != Token.TOKEN_PARENTHESES_OPEN) {
57 | output.add(stack.pop());
58 | }
59 | if (stack.empty() || stack.peek().getType() != Token.TOKEN_PARENTHESES_OPEN) {
60 | throw new IllegalArgumentException("Misplaced function separator ',' or mismatched parentheses");
61 | }
62 | break;
63 | case Token.TOKEN_OPERATOR:
64 | while (!stack.empty() && stack.peek().getType() == Token.TOKEN_OPERATOR) {
65 | OperatorToken o1 = (OperatorToken) token;
66 | OperatorToken o2 = (OperatorToken) stack.peek();
67 | if (o1.getOperator().getNumOperands() == 1 && o2.getOperator().getNumOperands() == 2) {
68 | break;
69 | } else if ((o1.getOperator().isLeftAssociative() && o1.getOperator().getPrecedence() <= o2.getOperator().getPrecedence())
70 | || (o1.getOperator().getPrecedence() < o2.getOperator().getPrecedence())) {
71 | output.add(stack.pop());
72 | }else {
73 | break;
74 | }
75 | }
76 | stack.push(token);
77 | break;
78 | case Token.TOKEN_PARENTHESES_OPEN:
79 | stack.push(token);
80 | break;
81 | case Token.TOKEN_PARENTHESES_CLOSE:
82 | while (stack.peek().getType() != Token.TOKEN_PARENTHESES_OPEN) {
83 | output.add(stack.pop());
84 | }
85 | stack.pop();
86 | if (!stack.isEmpty() && stack.peek().getType() == Token.TOKEN_FUNCTION) {
87 | output.add(stack.pop());
88 | }
89 | break;
90 | default:
91 | throw new IllegalArgumentException("Unknown Token type encountered. This should not happen");
92 | }
93 | }
94 | while (!stack.empty()) {
95 | Token t = stack.pop();
96 | if (t.getType() == Token.TOKEN_PARENTHESES_CLOSE || t.getType() == Token.TOKEN_PARENTHESES_OPEN) {
97 | throw new IllegalArgumentException("Mismatched parentheses detected. Please check the expression");
98 | } else {
99 | output.add(t);
100 | }
101 | }
102 | return (Token[]) output.toArray(new Token[output.size()]);
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/net/objecthunter/exp4j/tokenizer/ArgumentSeparatorToken.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Frank Asseg
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package net.objecthunter.exp4j.tokenizer;
17 |
18 | /**
19 | * Represents an argument separator in functions i.e: ','
20 | */
21 | class ArgumentSeparatorToken extends Token{
22 | /**
23 | * Create a new instance
24 | */
25 | ArgumentSeparatorToken() {
26 | super(Token.TOKEN_SEPARATOR);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/net/objecthunter/exp4j/tokenizer/CloseParenthesesToken.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Frank Asseg
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package net.objecthunter.exp4j.tokenizer;
17 |
18 | /**
19 | * represents closed parentheses
20 | */
21 | class CloseParenthesesToken extends Token {
22 |
23 | /**
24 | * Creare a new instance
25 | */
26 | CloseParenthesesToken() {
27 | super(Token.TOKEN_PARENTHESES_CLOSE);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/net/objecthunter/exp4j/tokenizer/FunctionToken.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Frank Asseg
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package net.objecthunter.exp4j.tokenizer;
17 |
18 | import net.objecthunter.exp4j.function.Function;
19 |
20 | public class FunctionToken extends Token{
21 | private final Function function;
22 | public FunctionToken(final Function function) {
23 | super(Token.TOKEN_FUNCTION);
24 | this.function = function;
25 | }
26 |
27 | public Function getFunction() {
28 | return function;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/net/objecthunter/exp4j/tokenizer/NumberToken.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Frank Asseg
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package net.objecthunter.exp4j.tokenizer;
17 |
18 | /**
19 | * Represents a number in the expression
20 | */
21 | public final class NumberToken extends Token {
22 | private final double value;
23 |
24 | /**
25 | * Create a new instance
26 | * @param value the value of the number
27 | */
28 | public NumberToken(double value) {
29 | super(TOKEN_NUMBER);
30 | this.value = value;
31 | }
32 |
33 | NumberToken(final char[] expression, final int offset, final int len) {
34 | this(Double.parseDouble(String.valueOf(expression, offset, len)));
35 | }
36 |
37 | /**
38 | * Get the value of the number
39 | * @return the value
40 | */
41 | public double getValue() {
42 | return value;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/net/objecthunter/exp4j/tokenizer/OpenParenthesesToken.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Frank Asseg
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package net.objecthunter.exp4j.tokenizer;
17 |
18 | class OpenParenthesesToken extends Token{
19 |
20 | OpenParenthesesToken() {
21 | super(TOKEN_PARENTHESES_OPEN);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/net/objecthunter/exp4j/tokenizer/OperatorToken.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Frank Asseg
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package net.objecthunter.exp4j.tokenizer;
17 |
18 | import net.objecthunter.exp4j.operator.Operator;
19 |
20 | /**
21 | * Represents an operator used in expressions
22 | */
23 | public class OperatorToken extends Token{
24 | private final Operator operator;
25 |
26 | /**
27 | * Create a new instance
28 | * @param op the operator
29 | */
30 | public OperatorToken(Operator op) {
31 | super(Token.TOKEN_OPERATOR);
32 | if (op == null) {
33 | throw new IllegalArgumentException("Operator is unknown for token.");
34 | }
35 | this.operator = op;
36 | }
37 |
38 | /**
39 | * Get the operator for that token
40 | * @return the operator
41 | */
42 | public Operator getOperator() {
43 | return operator;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/net/objecthunter/exp4j/tokenizer/Token.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Frank Asseg
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package net.objecthunter.exp4j.tokenizer;
17 |
18 | /**
19 | * Abstract class for tokens used by exp4j to tokenize expressions
20 | */
21 | public abstract class Token {
22 | public static final short TOKEN_NUMBER = 1;
23 | public static final short TOKEN_OPERATOR = 2;
24 | public static final short TOKEN_FUNCTION = 3;
25 | public static final short TOKEN_PARENTHESES_OPEN = 4;
26 | public static final short TOKEN_PARENTHESES_CLOSE = 5;
27 | public static final short TOKEN_VARIABLE = 6;
28 | public static final short TOKEN_SEPARATOR = 7;
29 |
30 | protected final int type;
31 |
32 | Token(int type) {
33 | this.type = type;
34 | }
35 |
36 | public int getType() {
37 | return type;
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/net/objecthunter/exp4j/tokenizer/Tokenizer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Frank Asseg
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | Includes change to allow variables with dot (e.g. "XXX.YYY") to use with FlightPlot fields.
19 | */
20 | package net.objecthunter.exp4j.tokenizer;
21 |
22 | import java.util.Map;
23 | import java.util.Set;
24 |
25 | import net.objecthunter.exp4j.function.Function;
26 | import net.objecthunter.exp4j.function.Functions;
27 | import net.objecthunter.exp4j.operator.Operator;
28 | import net.objecthunter.exp4j.operator.Operators;
29 |
30 | public class Tokenizer {
31 |
32 | private final char[] expression;
33 |
34 | private final int expressionLength;
35 |
36 | private final Map userFunctions;
37 |
38 | private final Map userOperators;
39 |
40 | private final Set variableNames;
41 |
42 | private int pos = 0;
43 |
44 | private Token lastToken;
45 |
46 | public Tokenizer(String expression, final Map userFunctions,
47 | final Map userOperators, final Set variableNames) {
48 | this.expression = expression.trim().toCharArray();
49 | this.expressionLength = this.expression.length;
50 | this.userFunctions = userFunctions;
51 | this.userOperators = userOperators;
52 | this.variableNames = variableNames;
53 | }
54 |
55 | public boolean hasNext() {
56 | return this.expression.length > pos;
57 | }
58 |
59 | public Token nextToken(){
60 | char ch = expression[pos];
61 | while (Character.isWhitespace(ch)) {
62 | ch = expression[++pos];
63 | }
64 | if (Character.isDigit(ch) || ch == '.') {
65 | if (lastToken != null) {
66 | if (lastToken.getType() == Token.TOKEN_NUMBER) {
67 | throw new IllegalArgumentException("Unable to parse char '" + ch + "' (Code:" + (int) ch + ") at [" + pos + "]");
68 | } else if ((lastToken.getType() != Token.TOKEN_OPERATOR
69 | && lastToken.getType() != Token.TOKEN_PARENTHESES_OPEN
70 | && lastToken.getType() != Token.TOKEN_FUNCTION
71 | && lastToken.getType() != Token.TOKEN_SEPARATOR)) {
72 | // insert an implicit multiplication token
73 | lastToken = new OperatorToken(Operators.getBuiltinOperator('*', 2));
74 | return lastToken;
75 | }
76 | }
77 | return parseNumberToken(ch);
78 | } else if (isArgumentSeparator(ch)) {
79 | return parseArgumentSeparatorToken(ch);
80 | } else if (isOpenParentheses(ch)) {
81 | if (lastToken != null &&
82 | (lastToken.getType() != Token.TOKEN_OPERATOR
83 | && lastToken.getType() != Token.TOKEN_PARENTHESES_OPEN
84 | && lastToken.getType() != Token.TOKEN_FUNCTION
85 | && lastToken.getType() != Token.TOKEN_SEPARATOR)) {
86 | // insert an implicit multiplication token
87 | lastToken = new OperatorToken(Operators.getBuiltinOperator('*', 2));
88 | return lastToken;
89 | }
90 | return parseParentheses(true);
91 | } else if (isCloseParentheses(ch)) {
92 | return parseParentheses(false);
93 | } else if (Operator.isAllowedOperatorChar(ch)) {
94 | return parseOperatorToken(ch);
95 | } else if (isAlphabetic(ch) || ch == '_') {
96 | // parse the name which can be a setVariable or a function
97 | if (lastToken != null &&
98 | (lastToken.getType() != Token.TOKEN_OPERATOR
99 | && lastToken.getType() != Token.TOKEN_PARENTHESES_OPEN
100 | && lastToken.getType() != Token.TOKEN_FUNCTION
101 | && lastToken.getType() != Token.TOKEN_SEPARATOR)) {
102 | // insert an implicit multiplication token
103 | lastToken = new OperatorToken(Operators.getBuiltinOperator('*', 2));
104 | return lastToken;
105 | }
106 | return parseFunctionOrVariable();
107 |
108 | }
109 | throw new IllegalArgumentException("Unable to parse char '" + ch + "' (Code:" + (int) ch + ") at [" + pos + "]");
110 | }
111 |
112 | private Token parseArgumentSeparatorToken(char ch) {
113 | this.pos++;
114 | this.lastToken = new ArgumentSeparatorToken();
115 | return lastToken;
116 | }
117 |
118 | private boolean isArgumentSeparator(char ch) {
119 | return ch == ',';
120 | }
121 |
122 | private Token parseParentheses(final boolean open) {
123 | if (open) {
124 | this.lastToken = new OpenParenthesesToken();
125 | } else {
126 | this.lastToken = new CloseParenthesesToken();
127 | }
128 | this.pos++;
129 | return lastToken;
130 | }
131 |
132 | private boolean isOpenParentheses(char ch) {
133 | return ch == '(' || ch == '{' || ch == '[';
134 | }
135 |
136 | private boolean isCloseParentheses(char ch) {
137 | return ch == ')' || ch == '}' || ch == ']';
138 | }
139 |
140 | private Token parseFunctionOrVariable() {
141 | final int offset = this.pos;
142 | int lastValidLen = 1;
143 | Token lastValidToken = null;
144 | int len = 1;
145 | if (isEndOfExpression(offset)) {
146 | this.pos++;
147 | }
148 | while (!isEndOfExpression(offset + len - 1) &&
149 | (isAlphabetic(expression[offset + len - 1]) ||
150 | Character.isDigit(expression[offset + len - 1]) ||
151 | expression[offset + len - 1] == '[' ||
152 | expression[offset + len - 1] == ']' ||
153 | expression[offset + len - 1] == '_' ||
154 | expression[offset + len - 1] == '.')) {
155 | String name = new String(expression, offset, len);
156 | if (variableNames != null && variableNames.contains(name)) {
157 | lastValidLen = len;
158 | lastValidToken = new VariableToken(name);
159 | } else {
160 | final Function f = getFunction(name);
161 | if (f != null) {
162 | lastValidLen = len;
163 | lastValidToken = new FunctionToken(f);
164 | }
165 | }
166 | len++;
167 | }
168 | if (lastValidToken == null) {
169 | throw new IllegalArgumentException("Unable to parse setVariable or function starting at pos " + pos + " in expression '" + new String(expression) + "'");
170 | }
171 | pos += lastValidLen;
172 | lastToken = lastValidToken;
173 | return lastToken;
174 | }
175 |
176 | private Function getFunction(String name) {
177 | Function f = null;
178 | if (this.userFunctions != null) {
179 | f = this.userFunctions.get(name);
180 | }
181 | if (f == null) {
182 | f = Functions.getBuiltinFunction(name);
183 | }
184 | return f;
185 | }
186 |
187 | private Token parseOperatorToken(char firstChar) {
188 | final int offset = this.pos;
189 | int len = 1;
190 | final StringBuilder symbol = new StringBuilder();
191 | Operator lastValid = null;
192 | symbol.append(firstChar);
193 |
194 | while (!isEndOfExpression(offset + len) && Operator.isAllowedOperatorChar(expression[offset + len])) {
195 | symbol.append(expression[offset + len++]);
196 | }
197 |
198 | while (symbol.length() > 0) {
199 | Operator op = this.getOperator(symbol.toString());
200 | if (op == null) {
201 | symbol.setLength(symbol.length() - 1);
202 | }else{
203 | lastValid = op;
204 | break;
205 | }
206 | }
207 |
208 | pos += symbol.length();
209 | lastToken = new OperatorToken(lastValid);
210 | return lastToken;
211 | }
212 |
213 | private Operator getOperator(String symbol) {
214 | Operator op = null;
215 | if (this.userOperators != null) {
216 | op = this.userOperators.get(symbol);
217 | }
218 | if (op == null && symbol.length() == 1) {
219 | final int argc =
220 | (lastToken == null ||
221 | lastToken.getType() == Token.TOKEN_OPERATOR ||
222 | lastToken.getType() == Token.TOKEN_PARENTHESES_OPEN ||
223 | lastToken.getType() == Token.TOKEN_SEPARATOR)
224 | ? 1 : 2;
225 | op = Operators.getBuiltinOperator(symbol.charAt(0), argc);
226 | }
227 | return op;
228 | }
229 |
230 | private Token parseNumberToken(final char firstChar) {
231 | final int offset = this.pos;
232 | int len = 1;
233 | this.pos++;
234 | if (isEndOfExpression(offset + len)) {
235 | lastToken = new NumberToken(Double.parseDouble(String.valueOf(firstChar)));
236 | return lastToken;
237 | }
238 | while (!isEndOfExpression(offset + len) &&
239 | isNumeric(expression[offset + len], expression[offset + len - 1] == 'e' ||
240 | expression[offset + len - 1] == 'E')) {
241 | len++;
242 | this.pos++;
243 | }
244 | // check if the e is at the end
245 | if (expression[offset + len - 1] == 'e' || expression[offset + len - 1] == 'E') {
246 | // since the e is at the end it's not part of the number and a rollback is necessary
247 | len--;
248 | pos--;
249 | }
250 | lastToken = new NumberToken(expression, offset, len);
251 | return lastToken;
252 | }
253 |
254 | private static boolean isNumeric(char ch, boolean lastCharE) {
255 | return Character.isDigit(ch) || ch == '.' || ch == 'e' || ch == 'E' ||
256 | (lastCharE && (ch == '-' || ch == '+'));
257 | }
258 |
259 | public static boolean isAlphabetic(int codePoint) {
260 | return Character.isLetter(codePoint);
261 | }
262 |
263 | private boolean isEndOfExpression(int offset) {
264 | return this.expressionLength <= offset;
265 | }
266 | }
267 |
--------------------------------------------------------------------------------
/src/net/objecthunter/exp4j/tokenizer/VariableToken.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Frank Asseg
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package net.objecthunter.exp4j.tokenizer;
17 |
18 | /**
19 | * represents a setVariable used in an expression
20 | */
21 | public class VariableToken extends Token {
22 | private final String name;
23 |
24 | /**
25 | * Get the name of the setVariable
26 | * @return the name
27 | */
28 | public String getName() {
29 | return name;
30 | }
31 |
32 | /**
33 | * Create a new instance
34 | * @param name the name of the setVariable
35 | */
36 | public VariableToken(String name) {
37 | super(TOKEN_VARIABLE);
38 | this.name = name;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/org/json/JSONException.java:
--------------------------------------------------------------------------------
1 | package org.json;
2 |
3 | /**
4 | * The JSONException is thrown by the JSON.org classes when things are amiss.
5 | *
6 | * @author JSON.org
7 | * @version 2013-02-10
8 | */
9 | public class JSONException extends RuntimeException {
10 | private static final long serialVersionUID = 0;
11 | private Throwable cause;
12 |
13 | /**
14 | * Constructs a JSONException with an explanatory message.
15 | *
16 | * @param message
17 | * Detail about the reason for the exception.
18 | */
19 | public JSONException(String message) {
20 | super(message);
21 | }
22 |
23 | /**
24 | * Constructs a new JSONException with the specified cause.
25 | */
26 | public JSONException(Throwable cause) {
27 | super(cause.getMessage());
28 | this.cause = cause;
29 | }
30 |
31 | /**
32 | * Returns the cause of this exception or null if the cause is nonexistent
33 | * or unknown.
34 | *
35 | * @returns the cause of this exception or null if the cause is nonexistent
36 | * or unknown.
37 | */
38 | public Throwable getCause() {
39 | return this.cause;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/org/json/JSONString.java:
--------------------------------------------------------------------------------
1 | package org.json;
2 | /**
3 | * The JSONString
interface allows a toJSONString()
4 | * method so that a class can change the behavior of
5 | * JSONObject.toString()
, JSONArray.toString()
,
6 | * and JSONWriter.value(
Object)
. The
7 | * toJSONString
method will be used instead of the default behavior
8 | * of using the Object's toString()
method and quoting the result.
9 | */
10 | public interface JSONString {
11 | /**
12 | * The toJSONString
method allows a class to produce its own JSON
13 | * serialization.
14 | *
15 | * @return A strictly syntactically correct JSON text.
16 | */
17 | public String toJSONString();
18 | }
19 |
--------------------------------------------------------------------------------