@@ -0,0 +1,17 | |||||
|
1 | #ifndef SCIQLOP_SPECTROGRAMTIMESERIE_H | |||
|
2 | #define SCIQLOP_SPECTROGRAMTIMESERIE_H | |||
|
3 | ||||
|
4 | #include "CoreGlobal.h" | |||
|
5 | ||||
|
6 | #include <TimeSeries.h> | |||
|
7 | ||||
|
8 | class SCIQLOP_CORE_EXPORT SpectrogramTimeSerie | |||
|
9 | : public TimeSeries::TimeSerie<double, SpectrogramTimeSerie, 2> | |||
|
10 | { | |||
|
11 | public: | |||
|
12 | SpectrogramTimeSerie() {} | |||
|
13 | ~SpectrogramTimeSerie() = default; | |||
|
14 | using TimeSerie::TimeSerie; | |||
|
15 | }; | |||
|
16 | ||||
|
17 | #endif // SCIQLOP_SPECTROGRAMTIMESERIE_H |
@@ -0,0 +1,41 | |||||
|
1 | import sys | |||
|
2 | import os | |||
|
3 | ||||
|
4 | import sciqlopqt | |||
|
5 | import pysciqlopcore | |||
|
6 | ||||
|
7 | import numpy as np | |||
|
8 | import datetime | |||
|
9 | import time | |||
|
10 | import unittest | |||
|
11 | import ddt | |||
|
12 | ||||
|
13 | def listify(obj): | |||
|
14 | if hasattr(obj, "__getitem__"): | |||
|
15 | return obj | |||
|
16 | return [obj] | |||
|
17 | ||||
|
18 | @ddt.ddt | |||
|
19 | class TimeSeriesCtors(unittest.TestCase): | |||
|
20 | @ddt.data( | |||
|
21 | (pysciqlopcore.ScalarTimeSerie,10), | |||
|
22 | (pysciqlopcore.VectorTimeSerie,10), | |||
|
23 | (pysciqlopcore.SpectrogramTimeSerie,[10,10]), | |||
|
24 | ) | |||
|
25 | def test_construct(self, case): | |||
|
26 | ts = case[0](case[1]) | |||
|
27 | self.assertEqual(ts.shape,listify(case[1])) | |||
|
28 | ||||
|
29 | class TimeSeriesData(unittest.TestCase): | |||
|
30 | def test_set_ScalarTimeSerie_values(self): | |||
|
31 | ts = pysciqlopcore.ScalarTimeSerie(10) | |||
|
32 | ts.t[0]=111. | |||
|
33 | self.assertEqual(ts.t[0],111.) | |||
|
34 | ts[0]=123. | |||
|
35 | self.assertEqual(ts[0],123.) | |||
|
36 | ||||
|
37 | def test_build_ScalarTimeSerie_from_np_arrays(self): | |||
|
38 | ts = pysciqlopcore.ScalarTimeSerie(np.arange(10),np.zeros(10)) | |||
|
39 | ||||
|
40 | if __name__ == '__main__': | |||
|
41 | unittest.main() |
@@ -95,6 +95,7 FILE (GLOB_RECURSE core_SRCS | |||||
95 | ./include/Data/DataSeriesIterator.h |
|
95 | ./include/Data/DataSeriesIterator.h | |
96 | ./include/Data/DataSeriesUtils.h |
|
96 | ./include/Data/DataSeriesUtils.h | |
97 | ./include/Data/SpectrogramSeries.h |
|
97 | ./include/Data/SpectrogramSeries.h | |
|
98 | ./include/Data/SpectrogramTimeSerie.h | |||
98 | ./include/Data/Unit.h |
|
99 | ./include/Data/Unit.h | |
99 | ./include/Data/DataProviderParameters.h |
|
100 | ./include/Data/DataProviderParameters.h | |
100 | ./include/Data/OptionalAxis.h |
|
101 | ./include/Data/OptionalAxis.h |
@@ -1,1 +1,1 | |||||
1 | Subproject commit ce006cf8df2b0bb3d433f901b6e80ce69119a430 |
|
1 | Subproject commit b8442947c778c24a35d33c5f8eed8548cbf9fead |
@@ -1,221 +1,219 | |||||
1 | #ifndef SCIQLOP_DATETIMERANGE_H |
|
1 | #ifndef SCIQLOP_DATETIMERANGE_H | |
2 | #define SCIQLOP_DATETIMERANGE_H |
|
2 | #define SCIQLOP_DATETIMERANGE_H | |
3 |
|
3 | |||
4 | #include <cmath> |
|
|||
5 | #include <QObject> |
|
|||
6 |
|
||||
7 | #include <QDebug> |
|
|||
8 |
|
||||
9 | #include <opaque/numeric_typedef.hpp> |
|
|||
10 | #include <Common/DateUtils.h> |
|
4 | #include <Common/DateUtils.h> | |
11 | #include <Common/MetaTypes.h> |
|
5 | #include <Common/MetaTypes.h> | |
12 | #include <Common/Numeric.h> |
|
6 | #include <Common/Numeric.h> | |
|
7 | #include <QDebug> | |||
|
8 | #include <QObject> | |||
|
9 | #include <cmath> | |||
|
10 | #include <opaque/numeric_typedef.hpp> | |||
13 |
|
11 | |||
14 |
|
12 | template<typename T> | ||
15 | template <typename T> |
|
13 | struct Seconds | |
16 |
|
|
14 | : opaque::numeric_typedef<T, Seconds<T>>, | |
17 |
|
|
15 | opaque::binop::multipliable<Seconds<T>, true, Seconds<T>, T, T>, | |
18 |
|
|
16 | opaque::binop::dividable<Seconds<T>, true, Seconds<T>, T, T>, | |
19 |
|
|
17 | opaque::binop::addable<Seconds<T>, true, Seconds<T>, T, T>, | |
20 |
|
|
18 | opaque::binop::subtractable<Seconds<T>, true, Seconds<T>, T, T> | |
21 |
|
19 | |||
22 | { |
|
20 | { | |
23 |
using base |
|
21 | using base = opaque::numeric_typedef<T, Seconds<T>>; | |
24 | using base::base; |
|
22 | using base::base; | |
25 |
operator T |
|
23 | operator T() const { return this->value; } | |
26 | }; |
|
24 | }; | |
27 |
|
25 | |||
28 |
struct InvalidDateTimeRangeTransformation |
|
26 | struct InvalidDateTimeRangeTransformation | |
|
27 | {}; | |||
29 |
|
28 | |||
30 | struct DateTimeRangeTransformation |
|
29 | struct DateTimeRangeTransformation | |
31 | { |
|
30 | { | |
32 |
|
|
31 | double zoom; | |
33 |
|
|
32 | Seconds<double> shift; | |
34 |
|
|
33 | bool operator==(const DateTimeRangeTransformation& other) const | |
35 |
|
|
34 | { | |
36 |
|
|
35 | return SciQLop::numeric::almost_equal(zoom, other.zoom, 1) && | |
37 |
|
|
36 | SciQLop::numeric::almost_equal<double>(shift, other.shift, 1); | |
38 |
|
|
37 | } | |
39 | DateTimeRangeTransformation merge(const DateTimeRangeTransformation& other) const |
|
38 | DateTimeRangeTransformation | |
40 | { |
|
39 | merge(const DateTimeRangeTransformation& other) const | |
41 | return DateTimeRangeTransformation{zoom*other.zoom,shift+other.shift}; |
|
40 | { | |
42 | } |
|
41 | return DateTimeRangeTransformation{zoom * other.zoom, shift + other.shift}; | |
|
42 | } | |||
43 | }; |
|
43 | }; | |
44 |
|
44 | |||
45 | /** |
|
45 | /** | |
46 | * @brief The SqpRange struct holds the information of time parameters |
|
46 | * @brief The SqpRange struct holds the information of time parameters | |
47 | */ |
|
47 | */ | |
48 |
struct DateTimeRange |
|
48 | struct DateTimeRange | |
49 | DateTimeRange() |
|
49 | { | |
50 |
|
|
50 | DateTimeRange() : m_TStart(std::nan("")), m_TEnd(std::nan("")) {} | |
51 | {} |
|
51 | DateTimeRange(double TStart, double TEnd) : m_TStart(TStart), m_TEnd(TEnd) {} | |
52 | DateTimeRange(double TStart, double TEnd) |
|
52 | /// Creates SqpRange from dates and times | |
53 | :m_TStart(TStart), m_TEnd(TEnd) |
|
53 | static DateTimeRange fromDateTime(const QDate& startDate, | |
54 | {} |
|
54 | const QTime& startTime, | |
55 | /// Creates SqpRange from dates and times |
|
55 | const QDate& endDate, const QTime& endTime) | |
56 | static DateTimeRange fromDateTime(const QDate &startDate, const QTime &startTime, |
|
56 | { | |
57 | const QDate &endDate, const QTime &endTime) |
|
57 | return { | |
58 | { |
|
58 | DateUtils::secondsSinceEpoch(QDateTime{startDate, startTime, Qt::UTC}), | |
59 |
|
|
59 | DateUtils::secondsSinceEpoch(QDateTime{endDate, endTime, Qt::UTC})}; | |
60 | DateUtils::secondsSinceEpoch(QDateTime{endDate, endTime, Qt::UTC})}; |
|
60 | } | |
61 | } |
|
61 | ||
62 |
|
62 | static DateTimeRange fromDateTime(const QDateTime& start, | ||
63 | static DateTimeRange fromDateTime(const QDateTime &start, const QDateTime &end) |
|
63 | const QDateTime& end) | |
64 |
|
|
64 | { | |
65 |
|
|
65 | return {DateUtils::secondsSinceEpoch(start), | |
66 |
|
|
66 | DateUtils::secondsSinceEpoch(end)}; | |
67 |
|
|
67 | } | |
68 |
|
68 | |||
69 |
|
|
69 | /// Start time (UTC) | |
70 |
|
|
70 | double m_TStart; | |
71 |
|
|
71 | /// End time (UTC) | |
72 |
|
|
72 | double m_TEnd; | |
73 |
|
73 | |||
74 | Seconds<double> delta()const noexcept{return Seconds<double>{this->m_TEnd - this->m_TStart};} |
|
74 | Seconds<double> delta() const noexcept | |
75 |
|
75 | { | ||
76 | bool contains(const DateTimeRange &dateTime) const noexcept |
|
76 | return Seconds<double>{this->m_TEnd - this->m_TStart}; | |
77 | { |
|
77 | } | |
78 | return (m_TStart <= dateTime.m_TStart && m_TEnd >= dateTime.m_TEnd); |
|
78 | ||
79 | } |
|
79 | bool contains(const DateTimeRange& dateTime) const noexcept | |
80 |
|
80 | { | ||
81 | Seconds<double> center() const noexcept |
|
81 | return (m_TStart <= dateTime.m_TStart && m_TEnd >= dateTime.m_TEnd); | |
82 | { |
|
82 | } | |
83 | return Seconds<double>((m_TStart + m_TEnd) / 2.); |
|
83 | ||
84 | } |
|
84 | Seconds<double> center() const noexcept | |
85 |
|
85 | { | ||
86 | bool intersect(const DateTimeRange &dateTime) const noexcept |
|
86 | return Seconds<double>((m_TStart + m_TEnd) / 2.); | |
87 | { |
|
87 | } | |
88 | return (m_TEnd >= dateTime.m_TStart && m_TStart <= dateTime.m_TEnd); |
|
88 | ||
89 | } |
|
89 | bool intersect(const DateTimeRange& dateTime) const noexcept | |
90 |
|
90 | { | ||
91 | inline DateTimeRange transform(const DateTimeRangeTransformation& tr)const noexcept; |
|
91 | return (m_TEnd >= dateTime.m_TStart && m_TStart <= dateTime.m_TEnd); | |
92 |
|
92 | } | ||
93 | bool operator==(const DateTimeRange &other) const |
|
93 | ||
94 | { |
|
94 | inline DateTimeRange transform(const DateTimeRangeTransformation& tr) const | |
95 | return SciQLop::numeric::almost_equal(m_TStart, other.m_TStart, 1) && |
|
95 | noexcept; | |
96 | SciQLop::numeric::almost_equal(m_TEnd, other.m_TEnd, 1); |
|
96 | ||
97 | } |
|
97 | bool operator==(const DateTimeRange& other) const | |
98 |
|
98 | { | ||
99 | bool operator!=(const DateTimeRange &other) const { return !(*this == other); } |
|
99 | return SciQLop::numeric::almost_equal(m_TStart, other.m_TStart, 1) && | |
100 |
|
100 | SciQLop::numeric::almost_equal(m_TEnd, other.m_TEnd, 1); | ||
101 | void grow(double factor)noexcept |
|
101 | } | |
102 | { |
|
102 | ||
103 | double grow_v{delta()*(factor - 1.)/2.}; |
|
103 | bool operator!=(const DateTimeRange& other) const | |
104 | m_TStart -= grow_v; |
|
104 | { | |
105 | m_TEnd += grow_v; |
|
105 | return !(*this == other); | |
106 |
|
|
106 | } | |
107 |
|
107 | |||
108 |
|
|
108 | void grow(double factor) noexcept | |
109 |
|
|
109 | { | |
110 |
|
|
110 | double grow_v{delta() * (factor - 1.) / 2.}; | |
111 |
|
|
111 | m_TStart -= grow_v; | |
112 |
|
|
112 | m_TEnd += grow_v; | |
113 |
|
|
113 | } | |
114 |
|
114 | |||
115 | DateTimeRange& operator*=(double k) |
|
115 | void shrink(double factor) noexcept | |
116 |
|
|
116 | { | |
117 | this->grow(k); |
|
117 | double shrink_v{this->delta() * (1. - factor) / 2.}; | |
118 | return *this; |
|
118 | m_TStart += shrink_v; | |
119 | } |
|
119 | m_TEnd -= shrink_v; | |
120 |
|
120 | } | ||
121 | DateTimeRange& operator/=(double k) |
|
121 | ||
122 | { |
|
122 | DateTimeRange& operator*=(double k) | |
123 | this->shrink(k); |
|
123 | { | |
124 | return *this; |
|
124 | this->grow(k); | |
125 | } |
|
125 | return *this; | |
126 |
|
126 | } | ||
127 | // compute set difference |
|
127 | ||
128 | std::vector<DateTimeRange> operator-(const DateTimeRange& other)const |
|
128 | DateTimeRange& operator/=(double k) | |
|
129 | { | |||
|
130 | this->shrink(k); | |||
|
131 | return *this; | |||
|
132 | } | |||
|
133 | ||||
|
134 | // compute set difference | |||
|
135 | std::vector<DateTimeRange> operator-(const DateTimeRange& other) const | |||
|
136 | { | |||
|
137 | std::vector<DateTimeRange> result; | |||
|
138 | if(std::isnan(other.m_TStart) || std::isnan(other.m_TEnd) || | |||
|
139 | !this->intersect(other)) | |||
|
140 | { result.emplace_back(m_TStart, m_TEnd); } | |||
|
141 | else | |||
129 | { |
|
142 | { | |
130 | std::vector<DateTimeRange> result; |
|
143 | if(this->m_TStart < other.m_TStart) | |
131 | if(std::isnan(other.m_TStart)||std::isnan(other.m_TEnd)||!this->intersect(other)) |
|
144 | { result.emplace_back(this->m_TStart, other.m_TStart); } | |
132 | { |
|
145 | if(this->m_TEnd > other.m_TEnd) | |
133 |
|
|
146 | { result.emplace_back(other.m_TEnd, this->m_TEnd); } | |
134 | } |
|
|||
135 | else |
|
|||
136 | { |
|
|||
137 | if(this->m_TStart<other.m_TStart) |
|
|||
138 | { |
|
|||
139 | result.emplace_back(this->m_TStart, other.m_TStart); |
|
|||
140 | } |
|
|||
141 | if(this->m_TEnd>other.m_TEnd) |
|
|||
142 | { |
|
|||
143 | result.emplace_back(other.m_TEnd, this->m_TEnd); |
|
|||
144 | } |
|
|||
145 | } |
|
|||
146 | return result; |
|
|||
147 | } |
|
147 | } | |
148 |
|
148 | return result; | ||
|
149 | } | |||
149 | }; |
|
150 | }; | |
150 |
|
151 | |||
151 | template <class T> |
|
152 | template<class T> DateTimeRange& operator+=(DateTimeRange& r, Seconds<T> offset) | |
152 | DateTimeRange& operator+=(DateTimeRange&r, Seconds<T> offset) |
|
|||
153 | { |
|
153 | { | |
154 |
|
|
154 | shift(r, offset); | |
155 |
|
|
155 | return r; | |
156 | } |
|
156 | } | |
157 |
|
157 | |||
158 | template <class T> |
|
158 | template<class T> DateTimeRange& operator-=(DateTimeRange& r, Seconds<T> offset) | |
159 | DateTimeRange& operator-=(DateTimeRange&r, Seconds<T> offset) |
|
|||
160 | { |
|
159 | { | |
161 |
|
|
160 | shift(r, -offset); | |
162 |
|
|
161 | return r; | |
163 | } |
|
162 | } | |
164 |
|
163 | |||
165 | template <class T> |
|
164 | template<class T> void shift(DateTimeRange& r, Seconds<T> offset) | |
166 | void shift(DateTimeRange& r, Seconds<T> offset) |
|
|||
167 | { |
|
165 | { | |
168 |
|
|
166 | r.m_TEnd += static_cast<double>(offset); | |
169 |
|
|
167 | r.m_TStart += static_cast<double>(offset); | |
170 | } |
|
168 | } | |
171 |
|
169 | |||
172 | inline DateTimeRange operator*(const DateTimeRange& r, double k) |
|
170 | inline DateTimeRange operator*(const DateTimeRange& r, double k) | |
173 | { |
|
171 | { | |
174 |
|
|
172 | DateTimeRange result{r}; | |
175 |
|
|
173 | result.grow(k); | |
176 |
|
|
174 | return result; | |
177 | } |
|
175 | } | |
178 |
|
176 | |||
179 | inline DateTimeRange operator/(const DateTimeRange& r, double k) |
|
177 | inline DateTimeRange operator/(const DateTimeRange& r, double k) | |
180 | { |
|
178 | { | |
181 |
|
|
179 | DateTimeRange result{r}; | |
182 |
|
|
180 | result.shrink(k); | |
183 |
|
|
181 | return result; | |
184 | } |
|
182 | } | |
185 |
|
183 | |||
186 | template<class T> |
|
184 | template<class T> | |
187 | DateTimeRange operator+(const DateTimeRange& r, Seconds<T> offset) |
|
185 | DateTimeRange operator+(const DateTimeRange& r, Seconds<T> offset) | |
188 | { |
|
186 | { | |
189 |
|
|
187 | DateTimeRange result{r}; | |
190 |
|
|
188 | shift(result, offset); | |
191 |
|
|
189 | return result; | |
192 | } |
|
190 | } | |
193 |
|
191 | |||
194 | template<class T> |
|
192 | template<class T> | |
195 | DateTimeRange operator-(const DateTimeRange& r, Seconds<T> offset) |
|
193 | DateTimeRange operator-(const DateTimeRange& r, Seconds<T> offset) | |
196 | { |
|
194 | { | |
197 |
|
|
195 | DateTimeRange result{r}; | |
198 |
|
|
196 | shift(result, -offset); | |
199 |
|
|
197 | return result; | |
200 | } |
|
198 | } | |
201 |
|
199 | |||
202 | const auto INVALID_RANGE |
|
200 | const auto INVALID_RANGE = | |
203 |
|
|
201 | DateTimeRange{std::numeric_limits<double>::quiet_NaN(), | |
|
202 | std::numeric_limits<double>::quiet_NaN()}; | |||
204 |
|
203 | |||
205 | inline QDebug operator<<(QDebug d, DateTimeRange obj) |
|
204 | inline QDebug operator<<(QDebug d, DateTimeRange obj) | |
206 | { |
|
205 | { | |
207 |
|
|
206 | auto tendDateTimeStart = DateUtils::dateTime(obj.m_TStart); | |
208 |
|
|
207 | auto tendDateTimeEnd = DateUtils::dateTime(obj.m_TEnd); | |
209 |
|
208 | |||
210 |
|
|
209 | d << "ts: " << tendDateTimeStart << " te: " << tendDateTimeEnd; | |
211 |
|
|
210 | return d; | |
212 | } |
|
211 | } | |
213 |
|
212 | |||
214 |
|
213 | DateTimeRange | ||
215 |
|
214 | DateTimeRange::transform(const DateTimeRangeTransformation& tr) const noexcept | ||
216 | DateTimeRange DateTimeRange::transform(const DateTimeRangeTransformation &tr) const noexcept |
|
|||
217 | { |
|
215 | { | |
218 |
|
|
216 | return DateTimeRange{*this} * tr.zoom + tr.shift; | |
219 | } |
|
217 | } | |
220 |
|
218 | |||
221 | // Required for using shared_ptr in signals/slots |
|
219 | // Required for using shared_ptr in signals/slots |
@@ -10,6 +10,7 | |||||
10 | #include <Data/DataSeriesType.h> |
|
10 | #include <Data/DataSeriesType.h> | |
11 | #include <Data/DateTimeRange.h> |
|
11 | #include <Data/DateTimeRange.h> | |
12 | #include <Data/ScalarTimeSerie.h> |
|
12 | #include <Data/ScalarTimeSerie.h> | |
|
13 | #include <Data/SpectrogramTimeSerie.h> | |||
13 | #include <Data/VectorTimeSerie.h> |
|
14 | #include <Data/VectorTimeSerie.h> | |
14 | #include <QDataStream> |
|
15 | #include <QDataStream> | |
15 | #include <QObject> |
|
16 | #include <QObject> | |
@@ -18,9 +19,10 | |||||
18 | #include <TimeSeries.h> |
|
19 | #include <TimeSeries.h> | |
19 | #include <optional> |
|
20 | #include <optional> | |
20 |
|
21 | |||
21 |
using AnyTimeSerie = |
|
22 | using AnyTimeSerie = | |
22 | TimeSeries::ITimeSerie, |
|
23 | variant_w_base<TimeSeries::ITimeSerie, | |
23 |
std::variant<std::monostate, ScalarTimeSerie, |
|
24 | std::variant<std::monostate, ScalarTimeSerie, | |
|
25 | VectorTimeSerie, SpectrogramTimeSerie>>; | |||
24 |
|
26 | |||
25 | class SCIQLOP_CORE_EXPORT Variable2 : public QObject |
|
27 | class SCIQLOP_CORE_EXPORT Variable2 : public QObject | |
26 | { |
|
28 | { | |
@@ -52,6 +54,9 public: | |||||
52 | void setData(const std::vector<AnyTimeSerie*>& dataSeries, |
|
54 | void setData(const std::vector<AnyTimeSerie*>& dataSeries, | |
53 | const DateTimeRange& range, bool notify = true); |
|
55 | const DateTimeRange& range, bool notify = true); | |
54 |
|
56 | |||
|
57 | void setData(const std::vector<TimeSeries::ITimeSerie*>& dataSeries, | |||
|
58 | const DateTimeRange& range, bool notify = true); | |||
|
59 | ||||
55 | static QByteArray |
|
60 | static QByteArray | |
56 | mimeData(const std::vector<std::shared_ptr<Variable2>>& variables) |
|
61 | mimeData(const std::vector<std::shared_ptr<Variable2>>& variables) | |
57 | { |
|
62 | { |
@@ -42,7 +42,7 struct Variable2::VariablePrivate | |||||
42 | PROPERTY_(m_Name, name, setName, QString) |
|
42 | PROPERTY_(m_Name, name, setName, QString) | |
43 | PROPERTY_(m_Range, range, setRange, DateTimeRange) |
|
43 | PROPERTY_(m_Range, range, setRange, DateTimeRange) | |
44 | PROPERTY_(m_Metadata, metadata, setMetadata, QVariantHash) |
|
44 | PROPERTY_(m_Metadata, metadata, setMetadata, QVariantHash) | |
45 |
AnyTimeSerie |
|
45 | AnyTimeSerie* dataSeries() { return m_TimeSerie.get(); } | |
46 | void setDataSeries(std::unique_ptr<AnyTimeSerie>&& timeSerie) |
|
46 | void setDataSeries(std::unique_ptr<AnyTimeSerie>&& timeSerie) | |
47 | { |
|
47 | { | |
48 | QWriteLocker lock{&m_Lock}; |
|
48 | QWriteLocker lock{&m_Lock}; | |
@@ -73,45 +73,87 DateTimeRange Variable2::range() const noexcept { return impl->range(); } | |||||
73 |
|
73 | |||
74 | std::size_t Variable2::nbPoints() const noexcept { return impl->nbPoints(); } |
|
74 | std::size_t Variable2::nbPoints() const noexcept { return impl->nbPoints(); } | |
75 |
|
75 | |||
76 | AnyTimeSerie* Variable2::data() const noexcept {} |
|
76 | AnyTimeSerie* Variable2::data() const noexcept { return impl->dataSeries(); } | |
77 |
|
77 | |||
78 | DataSeriesType Variable2::type() const noexcept { return impl->type(); } |
|
78 | DataSeriesType Variable2::type() const noexcept { return impl->type(); } | |
79 |
|
79 | |||
80 | QVariantHash Variable2::metadata() const noexcept {} |
|
80 | QVariantHash Variable2::metadata() const noexcept {} | |
81 |
|
81 | |||
|
82 | // template<typename T> | |||
|
83 | // std::unique_ptr<AnyTimeSerie> _merge(std::vector<AnyTimeSerie*> source) | |||
|
84 | //{ | |||
|
85 | // std::unique_ptr<AnyTimeSerie> dest = std::make_unique<AnyTimeSerie>(); | |||
|
86 | // std::sort(std::begin(source), std::end(source), | |||
|
87 | // [](AnyTimeSerie* a, AnyTimeSerie* b) { | |||
|
88 | // return a->get<T>().front().t() < b->get<T>().front().t(); | |||
|
89 | // }); | |||
|
90 | // *dest = std::move(*source.front()); | |||
|
91 | // std::for_each( | |||
|
92 | // std::begin(source) + 1, std::end(source), [&dest](AnyTimeSerie* serie) { | |||
|
93 | // std::copy(std::begin(serie->get<T>()), std::end(serie->get<T>()), | |||
|
94 | // std::back_inserter(dest->get<T>())); | |||
|
95 | // }); | |||
|
96 | // return dest; | |||
|
97 | //} | |||
|
98 | ||||
82 | template<typename T> |
|
99 | template<typename T> | |
83 | void _merge(const std::vector<AnyTimeSerie*>& source, AnyTimeSerie* dest) |
|
100 | std::unique_ptr<AnyTimeSerie> | |
|
101 | _merge(std::vector<TimeSeries::ITimeSerie*> source) | |||
84 | { |
|
102 | { | |
85 | // std::for_each( |
|
103 | std::unique_ptr<AnyTimeSerie> dest = std::make_unique<AnyTimeSerie>(); | |
86 |
|
|
104 | std::sort(std::begin(source), std::end(source), | |
87 | // serie) { |
|
105 | [](TimeSeries::ITimeSerie* a, TimeSeries::ITimeSerie* b) { | |
88 | // std::copy(std::begin(serie->get<T>()), std::end(serie->get<T>()), |
|
106 | if(a->size() && b->size()) return a->t(0) < b->t(0); | |
89 | // std::back_inserter(dest->get<T>())); |
|
107 | return false; | |
90 |
|
|
108 | }); | |
|
109 | *dest = std::move(*static_cast<T*>(source.front())); | |||
|
110 | std::for_each(std::begin(source) + 1, std::end(source), | |||
|
111 | [&dest](TimeSeries::ITimeSerie* serie) { | |||
|
112 | std::copy(std::begin(*static_cast<T*>(serie)), | |||
|
113 | std::end(*static_cast<T*>(serie)), | |||
|
114 | std::back_inserter(dest->get<T>())); | |||
|
115 | }); | |||
|
116 | return dest; | |||
91 | } |
|
117 | } | |
92 |
|
118 | |||
|
119 | // std::unique_ptr<AnyTimeSerie> | |||
|
120 | // merge(const std::vector<AnyTimeSerie*>& dataSeries) | |||
|
121 | //{ | |||
|
122 | // switch(DataSeriesType(dataSeries.front()->index())) | |||
|
123 | // { | |||
|
124 | // case DataSeriesType::NONE: break; | |||
|
125 | // case DataSeriesType::SCALAR: return _merge<ScalarTimeSerie>(dataSeries); | |||
|
126 | // case DataSeriesType::VECTOR: return _merge<VectorTimeSerie>(dataSeries); | |||
|
127 | // case DataSeriesType::SPECTROGRAM: | |||
|
128 | // return _merge<SpectrogramTimeSerie>(dataSeries); | |||
|
129 | // } | |||
|
130 | // return std::unique_ptr<AnyTimeSerie>{}; | |||
|
131 | //} | |||
|
132 | ||||
93 | std::unique_ptr<AnyTimeSerie> |
|
133 | std::unique_ptr<AnyTimeSerie> | |
94 |
merge(const std::vector< |
|
134 | merge(const std::vector<TimeSeries::ITimeSerie*>& dataSeries) | |
95 | { |
|
135 | { | |
96 | std::unique_ptr<AnyTimeSerie> ts; |
|
136 | if(dynamic_cast<ScalarTimeSerie*>(dataSeries.front())) | |
97 | *ts = *dataSeries.front(); |
|
137 | return _merge<ScalarTimeSerie>(dataSeries); | |
98 | switch(DataSeriesType(ts->index())) |
|
138 | if(dynamic_cast<VectorTimeSerie*>(dataSeries.front())) | |
99 | { |
|
139 | return _merge<VectorTimeSerie>(dataSeries); | |
100 | case DataSeriesType::NONE: break; |
|
140 | if(dynamic_cast<SpectrogramTimeSerie*>(dataSeries.front())) | |
101 | case DataSeriesType::SCALAR: |
|
141 | return _merge<SpectrogramTimeSerie>(dataSeries); | |
102 | _merge<ScalarTimeSerie>(dataSeries, ts.get()); |
|
142 | return std::unique_ptr<AnyTimeSerie>{}; | |
103 | break; |
|
|||
104 | case DataSeriesType::VECTOR: |
|
|||
105 | _merge<VectorTimeSerie>(dataSeries, ts.get()); |
|
|||
106 | break; |
|
|||
107 | case DataSeriesType::SPECTROGRAM: |
|
|||
108 | // merge<Spe>(dataSeries, ts.get()); |
|
|||
109 | break; |
|
|||
110 | } |
|
|||
111 | return ts; |
|
|||
112 | } |
|
143 | } | |
113 |
|
144 | |||
114 | void Variable2::setData(const std::vector<AnyTimeSerie*>& dataSeries, |
|
145 | // void Variable2::setData(const std::vector<AnyTimeSerie*>& dataSeries, | |
|
146 | // const DateTimeRange& range, bool notify) | |||
|
147 | //{ | |||
|
148 | // if(dataSeries.size()) | |||
|
149 | // { | |||
|
150 | // impl->setDataSeries(merge(dataSeries)); | |||
|
151 | // impl->setRange(range); | |||
|
152 | // if(notify) emit this->updated(this->ID()); | |||
|
153 | // } | |||
|
154 | //} | |||
|
155 | ||||
|
156 | void Variable2::setData(const std::vector<TimeSeries::ITimeSerie*>& dataSeries, | |||
115 | const DateTimeRange& range, bool notify) |
|
157 | const DateTimeRange& range, bool notify) | |
116 | { |
|
158 | { | |
117 | if(dataSeries.size()) |
|
159 | if(dataSeries.size()) |
@@ -157,6 +157,8 PYBIND11_MODULE(pysciqlopcore, m) | |||||
157 | .def_property_readonly( |
|
157 | .def_property_readonly( | |
158 | "size", [](const TimeSeries::ITimeSerie& ts) { return ts.size(); }) |
|
158 | "size", [](const TimeSeries::ITimeSerie& ts) { return ts.size(); }) | |
159 | .def_property_readonly( |
|
159 | .def_property_readonly( | |
|
160 | "shape", [](const TimeSeries::ITimeSerie& ts) { return ts.shape(); }) | |||
|
161 | .def_property_readonly( | |||
160 | "t", |
|
162 | "t", | |
161 | [](TimeSeries::ITimeSerie& ts) -> decltype(ts.axis(0))& { |
|
163 | [](TimeSeries::ITimeSerie& ts) -> decltype(ts.axis(0))& { | |
162 | return ts.axis(0); |
|
164 | return ts.axis(0); | |
@@ -166,6 +168,19 PYBIND11_MODULE(pysciqlopcore, m) | |||||
166 | py::class_<ScalarTimeSerie, TimeSeries::ITimeSerie>(m, "ScalarTimeSerie") |
|
168 | py::class_<ScalarTimeSerie, TimeSeries::ITimeSerie>(m, "ScalarTimeSerie") | |
167 | .def(py::init<>()) |
|
169 | .def(py::init<>()) | |
168 | .def(py::init<std::size_t>()) |
|
170 | .def(py::init<std::size_t>()) | |
|
171 | .def(py::init([](py::array_t<double> t, py::array_t<double> values) { | |||
|
172 | assert(t.size() == values.size()); | |||
|
173 | ScalarTimeSerie::axis_t _t(t.size()); | |||
|
174 | ScalarTimeSerie::axis_t _values(t.size()); | |||
|
175 | auto t_vew = t.unchecked<1>(); | |||
|
176 | auto values_vew = values.unchecked<1>(); | |||
|
177 | for(int i = 0; i < t.size(); i++) | |||
|
178 | { | |||
|
179 | _t[i] = t_vew[i]; | |||
|
180 | _values[i] = values_vew[i]; | |||
|
181 | } | |||
|
182 | return ScalarTimeSerie(_t, _values); | |||
|
183 | })) | |||
169 | .def("__getitem__", |
|
184 | .def("__getitem__", | |
170 | [](ScalarTimeSerie& ts, std::size_t key) { return ts[key]; }) |
|
185 | [](ScalarTimeSerie& ts, std::size_t key) { return ts[key]; }) | |
171 | .def("__setitem__", [](ScalarTimeSerie& ts, std::size_t key, |
|
186 | .def("__setitem__", [](ScalarTimeSerie& ts, std::size_t key, | |
@@ -191,14 +206,26 PYBIND11_MODULE(pysciqlopcore, m) | |||||
191 | *(ts.begin() + key) = value; |
|
206 | *(ts.begin() + key) = value; | |
192 | }); |
|
207 | }); | |
193 |
|
208 | |||
|
209 | py::class_<SpectrogramTimeSerie, TimeSeries::ITimeSerie>( | |||
|
210 | m, "SpectrogramTimeSerie") | |||
|
211 | .def(py::init<>()) | |||
|
212 | .def(py::init<const std::vector<std::size_t>>()); | |||
|
213 | ||||
194 | py::class_<Variable2, std::shared_ptr<Variable2>>(m, "Variable2") |
|
214 | py::class_<Variable2, std::shared_ptr<Variable2>>(m, "Variable2") | |
195 | .def(py::init<const QString&>()) |
|
215 | .def(py::init<const QString&>()) | |
196 | .def_property("name", &Variable2::name, &Variable2::setName) |
|
216 | .def_property("name", &Variable2::name, &Variable2::setName) | |
197 | .def_property_readonly("range", &Variable2::range) |
|
217 | .def_property_readonly("range", &Variable2::range) | |
198 | .def_property_readonly("nbPoints", &Variable2::nbPoints) |
|
218 | .def_property_readonly("nbPoints", &Variable2::nbPoints) | |
199 | .def_property_readonly( |
|
219 | .def_property_readonly( | |
200 | "dataSeries", [](const Variable2& var) { return var.data()->base(); }) |
|
220 | "data", | |
201 | .def("set_data", &Variable2::setData) |
|
221 | [](const Variable2& var) -> TimeSeries::ITimeSerie* { | |
|
222 | auto data = var.data(); | |||
|
223 | if(data) return data->base(); | |||
|
224 | return nullptr; | |||
|
225 | }) | |||
|
226 | .def("set_data", | |||
|
227 | [](Variable2& var, std::vector<TimeSeries::ITimeSerie*> ts_list, | |||
|
228 | const DateTimeRange& range) { var.setData(ts_list, range); }) | |||
202 | .def("__len__", &Variable2::nbPoints) |
|
229 | .def("__len__", &Variable2::nbPoints) | |
203 | .def("__repr__", __repr__<Variable2>); |
|
230 | .def("__repr__", __repr__<Variable2>); | |
204 |
|
231 |
@@ -49,3 +49,12 declare_test(TestVariableController2Async TestVariableController2Async Variable/ | |||||
49 | declare_test(TestVariableController2WithSync TestVariableController2WithSync Variable/TestVariableController2WithSync.cpp "sciqlopcore;TestUtils;Qt5::Test") |
|
49 | declare_test(TestVariableController2WithSync TestVariableController2WithSync Variable/TestVariableController2WithSync.cpp "sciqlopcore;TestUtils;Qt5::Test") | |
50 |
|
50 | |||
51 | declare_test(TestCatalogueController TestCatalogueController CatalogueController/TestCatalogueController.cpp "sciqlopcore;TestUtils;Qt5::Test") |
|
51 | declare_test(TestCatalogueController TestCatalogueController CatalogueController/TestCatalogueController.cpp "sciqlopcore;TestUtils;Qt5::Test") | |
|
52 | ||||
|
53 | ||||
|
54 | find_package(PythonInterp 3 REQUIRED) | |||
|
55 | ||||
|
56 | add_test(NAME TestVariables | |||
|
57 | COMMAND ${PYTHON_EXECUTABLE} | |||
|
58 | ${CMAKE_CURRENT_LIST_DIR}/TestVariables.py | |||
|
59 | TestVariables) | |||
|
60 | set_tests_properties(TestVariables PROPERTIES ENVIRONMENT PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}/../) |
@@ -1,223 +1,248 | |||||
|
1 | #include <Common/Numeric.h> | |||
|
2 | #include <Data/DateTimeRange.h> | |||
|
3 | #include <Data/DateTimeRangeHelper.h> | |||
1 | #include <QObject> |
|
4 | #include <QObject> | |
2 | #include <QtTest> |
|
|||
3 | #include <QUuid> |
|
5 | #include <QUuid> | |
|
6 | #include <QtTest> | |||
4 | #include <limits> |
|
7 | #include <limits> | |
5 |
|
8 | |||
6 |
|
||||
7 | #include <Data/DateTimeRange.h> |
|
|||
8 | #include <Data/DateTimeRangeHelper.h> |
|
|||
9 | #include <Common/Numeric.h> |
|
|||
10 |
|
||||
11 | Q_DECLARE_METATYPE(Seconds<double>); |
|
9 | Q_DECLARE_METATYPE(Seconds<double>); | |
12 | Q_DECLARE_METATYPE(DateTimeRangeTransformation); |
|
10 | Q_DECLARE_METATYPE(DateTimeRangeTransformation); | |
13 |
|
11 | |||
14 | DateTimeRange computeZoom(QDateTime start, QDateTime stop, double zoom) |
|
12 | DateTimeRange computeZoom(QDateTime start, QDateTime stop, double zoom) | |
15 | { |
|
13 | { | |
16 |
|
|
14 | double start_epoch = start.toMSecsSinceEpoch(); | |
17 |
|
|
15 | double stop_epoch = stop.toMSecsSinceEpoch(); | |
18 |
|
|
16 | auto delta = stop_epoch - start_epoch; | |
19 |
|
|
17 | auto dt_ms = (zoom - 1.) * (double(delta)) / 2.; | |
20 |
|
|
18 | return DateTimeRange{(start_epoch - dt_ms) / 1000., | |
|
19 | (stop_epoch + dt_ms) / 1000.}; | |||
21 | } |
|
20 | } | |
22 |
|
21 | |||
23 |
class TestDateTimeRange: public QObject |
|
22 | class TestDateTimeRange : public QObject | |
24 | Q_OBJECT |
|
23 | { | |
|
24 | Q_OBJECT | |||
25 |
|
25 | |||
26 | private slots: |
|
26 | private slots: | |
27 |
|
|
27 | void testRangeDelta_data() | |
28 |
|
|
28 | { | |
29 |
|
|
29 | QTest::addColumn<QDateTime>("tstart"); | |
30 |
|
|
30 | QTest::addColumn<QDateTime>("tend"); | |
31 |
|
|
31 | QTest::addColumn<double>("expected"); | |
32 |
|
|
32 | auto now = QDateTime::currentDateTime(); | |
33 |
|
|
33 | auto yesterday = QDateTime::currentDateTime().addDays(-1); | |
34 |
|
|
34 | QTest::newRow("No delta") << now << now << 0.; | |
35 |
|
|
35 | QTest::newRow("One day delta") << yesterday << now << 60. * 60. * 24.; | |
36 |
|
|
36 | QTest::newRow("Minus one day delta") | |
37 | } |
|
37 | << now << yesterday << -60. * 60. * 24.; | |
38 |
|
38 | } | ||
39 | void testRangeDelta() |
|
39 | ||
|
40 | void testRangeDelta() | |||
|
41 | { | |||
|
42 | QFETCH(QDateTime, tstart); | |||
|
43 | QFETCH(QDateTime, tend); | |||
|
44 | QFETCH(double, expected); | |||
|
45 | auto range = DateTimeRange::fromDateTime(tstart, tend); | |||
|
46 | double delta = range.delta(); | |||
|
47 | // Since it is built from QDateTime don't expect better resolution | |||
|
48 | QVERIFY((delta - expected) <= 0.002); | |||
|
49 | } | |||
|
50 | ||||
|
51 | void testRangeShift_data() | |||
|
52 | { | |||
|
53 | QTest::addColumn<DateTimeRange>("initial"); | |||
|
54 | QTest::addColumn<Seconds<double>>("shift"); | |||
|
55 | QTest::addColumn<DateTimeRange>("expected"); | |||
|
56 | auto now = QDateTime::currentDateTime(); | |||
|
57 | auto yestd = QDateTime::currentDateTime().addDays(-1); | |||
|
58 | auto range = DateTimeRange::fromDateTime(yestd, now); | |||
|
59 | QTest::newRow("No shift") << range << Seconds<double>{0.} << range; | |||
|
60 | ||||
|
61 | QTest::newRow("One milisecond left") | |||
|
62 | << range << Seconds<double>{-.001} | |||
|
63 | << DateTimeRange::fromDateTime(yestd.addMSecs(-1.), now.addMSecs(-1.)); | |||
|
64 | QTest::newRow("One milisecond right") | |||
|
65 | << range << Seconds<double>{.001} | |||
|
66 | << DateTimeRange::fromDateTime(yestd.addMSecs(1.), now.addMSecs(1.)); | |||
|
67 | QTest::newRow("One second left") | |||
|
68 | << range << Seconds<double>{-1.} | |||
|
69 | << DateTimeRange::fromDateTime(yestd.addSecs(-1.), now.addSecs(-1.)); | |||
|
70 | QTest::newRow("One second right") | |||
|
71 | << range << Seconds<double>{1.} | |||
|
72 | << DateTimeRange::fromDateTime(yestd.addSecs(1.), now.addSecs(1.)); | |||
|
73 | QTest::newRow("One year left") | |||
|
74 | << range << Seconds<double>{-365. * 24. * 60. * 60.} | |||
|
75 | << DateTimeRange::fromDateTime(yestd.addSecs(-365 * 24 * 60 * 60), | |||
|
76 | now.addSecs(-365 * 24 * 60 * 60)); | |||
|
77 | QTest::newRow("One year right") | |||
|
78 | << range << Seconds<double>{365. * 24. * 60. * 60.} | |||
|
79 | << DateTimeRange::fromDateTime(yestd.addSecs(365 * 24 * 60 * 60), | |||
|
80 | now.addSecs(365 * 24 * 60 * 60)); | |||
|
81 | } | |||
|
82 | ||||
|
83 | void testRangeShift() | |||
|
84 | { | |||
|
85 | QFETCH(DateTimeRange, initial); | |||
|
86 | QFETCH(Seconds<double>, shift); | |||
|
87 | QFETCH(DateTimeRange, expected); | |||
|
88 | QCOMPARE(initial + shift, expected); | |||
|
89 | } | |||
|
90 | ||||
|
91 | void testRangeZoom_data() | |||
|
92 | { | |||
|
93 | QTest::addColumn<DateTimeRange>("initial"); | |||
|
94 | QTest::addColumn<double>("zoom"); | |||
|
95 | QTest::addColumn<DateTimeRange>("expected"); | |||
|
96 | auto now = QDateTime::currentDateTime(); | |||
|
97 | auto yestd = QDateTime::currentDateTime().addDays(-1); | |||
|
98 | auto range = DateTimeRange::fromDateTime(yestd, now); | |||
|
99 | QTest::newRow("No zoom") << range << 1. << range; | |||
|
100 | ||||
|
101 | QTest::newRow("Zoom IN 0.001") | |||
|
102 | << range << 1.001 << computeZoom(yestd, now, 1.001); | |||
|
103 | QTest::newRow("Zoom OUT 0.001") | |||
|
104 | << range << 0.999 << computeZoom(yestd, now, 0.999); | |||
|
105 | } | |||
|
106 | void testRangeZoom() | |||
|
107 | { | |||
|
108 | QFETCH(DateTimeRange, initial); | |||
|
109 | QFETCH(double, zoom); | |||
|
110 | QFETCH(DateTimeRange, expected); | |||
|
111 | QCOMPARE(initial * zoom, expected); | |||
|
112 | } | |||
|
113 | ||||
|
114 | void testRangeContains_data() | |||
|
115 | { | |||
|
116 | QTest::addColumn<DateTimeRange>("range"); | |||
|
117 | QTest::addColumn<DateTimeRange>("range2"); | |||
|
118 | QTest::addColumn<bool>("contains"); | |||
|
119 | auto now = QDateTime::currentDateTime(); | |||
|
120 | auto yestd = QDateTime::currentDateTime().addDays(-1); | |||
|
121 | auto range = DateTimeRange::fromDateTime(yestd, now); | |||
|
122 | QTest::newRow("Same range") << range << range << true; | |||
|
123 | QTest::newRow("Smaller range") << range << range * 0.8 << true; | |||
|
124 | QTest::newRow("Bigger range") << range << range * 1.2 << false; | |||
|
125 | QTest::newRow("Shifted range with overlap") | |||
|
126 | << range << range + Seconds<double>{1000.} << false; | |||
|
127 | QTest::newRow("Shifted range without overlap") | |||
|
128 | << range << range + Seconds<double>{24. * 60. * 60. * 10} << false; | |||
|
129 | } | |||
|
130 | ||||
|
131 | void testRangeContains() | |||
|
132 | { | |||
|
133 | QFETCH(DateTimeRange, range); | |||
|
134 | QFETCH(DateTimeRange, range2); | |||
|
135 | QFETCH(bool, contains); | |||
|
136 | QCOMPARE(range.contains(range2), contains); | |||
|
137 | } | |||
|
138 | ||||
|
139 | void testRangeIntersect_data() | |||
|
140 | { | |||
|
141 | QTest::addColumn<DateTimeRange>("range"); | |||
|
142 | QTest::addColumn<DateTimeRange>("range2"); | |||
|
143 | QTest::addColumn<bool>("contains"); | |||
|
144 | auto now = QDateTime::currentDateTime(); | |||
|
145 | auto yestd = QDateTime::currentDateTime().addDays(-1); | |||
|
146 | auto tomorrow = QDateTime::currentDateTime().addDays(1); | |||
|
147 | auto range = DateTimeRange::fromDateTime(yestd, now); | |||
|
148 | auto range2 = DateTimeRange::fromDateTime(now, tomorrow); | |||
|
149 | QTest::newRow("Same range") << range << range << true; | |||
|
150 | QTest::newRow("Smaller range") << range << range * 0.8 << true; | |||
|
151 | QTest::newRow("Bigger range") << range << range * 1.2 << true; | |||
|
152 | QTest::newRow("Shifted range with overlap") | |||
|
153 | << range << range + Seconds<double>{1000.} << true; | |||
|
154 | QTest::newRow("Shifted range with overlaping boundary") | |||
|
155 | << range << range2 << true; | |||
|
156 | QTest::newRow("Shifted range .1 seonds outside") | |||
|
157 | << range << range2 + Seconds<double>{.1} << false; | |||
|
158 | QTest::newRow("Shifted range without overlap") | |||
|
159 | << range << range + Seconds<double>{24. * 60. * 60. * 10} << false; | |||
|
160 | } | |||
|
161 | ||||
|
162 | void testRangeIntersect() | |||
|
163 | { | |||
|
164 | QFETCH(DateTimeRange, range); | |||
|
165 | QFETCH(DateTimeRange, range2); | |||
|
166 | QFETCH(bool, contains); | |||
|
167 | QCOMPARE(range.intersect(range2), contains); | |||
|
168 | } | |||
|
169 | ||||
|
170 | void testRangeTransformations_data() | |||
|
171 | { | |||
|
172 | QTest::addColumn<DateTimeRange>("range1"); | |||
|
173 | QTest::addColumn<DateTimeRange>("range2"); | |||
|
174 | QTest::addColumn<DateTimeRangeTransformation>("transformation"); | |||
|
175 | auto now = QDateTime::currentDateTime(); | |||
|
176 | auto yestd = QDateTime::currentDateTime().addDays(-1); | |||
|
177 | auto range = DateTimeRange::fromDateTime(yestd, now); | |||
|
178 | ||||
|
179 | QTest::newRow("Same range") | |||
|
180 | << range << range | |||
|
181 | << DateTimeRangeTransformation{1., Seconds<double>{0.}}; | |||
|
182 | ||||
|
183 | QTest::newRow("Transformatio 1.1x + 10s") | |||
|
184 | << range << range * 1.1 + Seconds<double>{10.} | |||
|
185 | << DateTimeRangeTransformation{1.1, Seconds<double>{10.}}; | |||
|
186 | ||||
|
187 | QTest::newRow("Transformatio 1.x + 10s") | |||
|
188 | << range << range * 1. + Seconds<double>{10.} | |||
|
189 | << DateTimeRangeTransformation{1., Seconds<double>{10.}}; | |||
|
190 | ||||
|
191 | QTest::newRow("Transformatio 1.1x + 0s") | |||
|
192 | << range << range * 1.1 + Seconds<double>{0.} | |||
|
193 | << DateTimeRangeTransformation{1.1, Seconds<double>{0.}}; | |||
|
194 | ||||
|
195 | QTest::newRow("Transformatio 0.9x - 10s") | |||
|
196 | << range << range * 0.9 + Seconds<double>{-10.} | |||
|
197 | << DateTimeRangeTransformation{0.9, Seconds<double>{-10.}}; | |||
|
198 | } | |||
|
199 | void testRangeTransformations() | |||
|
200 | { | |||
|
201 | QFETCH(DateTimeRange, range1); | |||
|
202 | QFETCH(DateTimeRange, range2); | |||
|
203 | QFETCH(DateTimeRangeTransformation, transformation); | |||
|
204 | auto computed_tr = | |||
|
205 | DateTimeRangeHelper::computeTransformation(range1, range2).value(); | |||
|
206 | QCOMPARE(computed_tr, transformation); | |||
|
207 | QCOMPARE(range1.transform(transformation), range2); | |||
|
208 | QCOMPARE(range1.transform(computed_tr), range2); | |||
|
209 | } | |||
|
210 | ||||
|
211 | void testRangeDiff_data() | |||
|
212 | { | |||
|
213 | QTest::addColumn<DateTimeRange>("source"); | |||
|
214 | QTest::addColumn<DateTimeRange>("destination"); | |||
|
215 | QTest::addColumn<int>("count"); | |||
|
216 | auto now = QDateTime::currentDateTime(); | |||
|
217 | auto yestd = QDateTime::currentDateTime().addDays(-1); | |||
|
218 | auto range = DateTimeRange::fromDateTime(yestd, now); | |||
|
219 | QTest::newRow("Same range") << range << range << 0; | |||
|
220 | QTest::newRow("No missing data (zoom in)") << range << range * 0.9 << 0; | |||
|
221 | QTest::newRow("Missing data on both sides (zoom out)") | |||
|
222 | << range << range * 2. << 2; | |||
|
223 | QTest::newRow("Missing data on left side (pan left)") | |||
|
224 | << range << range - Seconds<double>{1000.} << 1; | |||
|
225 | QTest::newRow("Missing data on right side (pan right)") | |||
|
226 | << range << range + Seconds<double>{1000.} << 1; | |||
|
227 | QTest::newRow("Missing data on right side no intersect (big pan right)") | |||
|
228 | << range << range + Seconds<double>{24. * 60. * 60. * 2} << 1; | |||
|
229 | QTest::newRow("Missing data on left side no intersect (big pan left)") | |||
|
230 | << range << range - Seconds<double>{24. * 60. * 60. * 2} << 1; | |||
|
231 | } | |||
|
232 | ||||
|
233 | void testRangeDiff() | |||
|
234 | { | |||
|
235 | QFETCH(DateTimeRange, source); | |||
|
236 | QFETCH(DateTimeRange, destination); | |||
|
237 | QFETCH(int, count); | |||
|
238 | auto diff = destination - source; | |||
|
239 | QCOMPARE(diff.size(), count); | |||
|
240 | for(const auto& range : diff) | |||
40 | { |
|
241 | { | |
41 | QFETCH(QDateTime,tstart); |
|
242 | QVERIFY(range.delta().value > 0.); | |
42 | QFETCH(QDateTime,tend); |
|
|||
43 | QFETCH(double,expected); |
|
|||
44 | auto range = DateTimeRange::fromDateTime(tstart, tend); |
|
|||
45 | double delta = range.delta(); |
|
|||
46 | // Since it is built from QDateTime don't expect better resolution |
|
|||
47 | QVERIFY((delta-expected) <= 0.002); |
|
|||
48 | } |
|
|||
49 |
|
||||
50 | void testRangeShift_data() |
|
|||
51 | { |
|
|||
52 | QTest::addColumn<DateTimeRange>("initial"); |
|
|||
53 | QTest::addColumn<Seconds<double>>("shift"); |
|
|||
54 | QTest::addColumn<DateTimeRange>("expected"); |
|
|||
55 | auto now = QDateTime::currentDateTime(); |
|
|||
56 | auto yestd = QDateTime::currentDateTime().addDays(-1); |
|
|||
57 | auto range = DateTimeRange::fromDateTime(yestd, now); |
|
|||
58 | QTest::newRow("No shift") << range << Seconds<double>{0.} << range; |
|
|||
59 |
|
||||
60 | QTest::newRow("One milisecond left") << range << Seconds<double>{-.001} << |
|
|||
61 | DateTimeRange::fromDateTime(yestd.addMSecs(-1.), now.addMSecs(-1.)); |
|
|||
62 | QTest::newRow("One milisecond right") << range << Seconds<double>{.001} << |
|
|||
63 | DateTimeRange::fromDateTime(yestd.addMSecs(1.), now.addMSecs(1.)); |
|
|||
64 | QTest::newRow("One second left") << range << Seconds<double>{-1.} << |
|
|||
65 | DateTimeRange::fromDateTime(yestd.addSecs(-1.), now.addSecs(-1.)); |
|
|||
66 | QTest::newRow("One second right") << range << Seconds<double>{1.} << |
|
|||
67 | DateTimeRange::fromDateTime(yestd.addSecs(1.), now.addSecs(1.)); |
|
|||
68 | QTest::newRow("One year left") << range << Seconds<double>{-365.*24.*60.*60.} << |
|
|||
69 | DateTimeRange::fromDateTime(yestd.addYears(-1.), now.addYears(-1.)); |
|
|||
70 | QTest::newRow("One year right") << range << Seconds<double>{365.*24.*60.*60.} << |
|
|||
71 | DateTimeRange::fromDateTime(yestd.addYears(1.), now.addYears(1.)); |
|
|||
72 | } |
|
|||
73 | void testRangeShift() |
|
|||
74 | { |
|
|||
75 | QFETCH(DateTimeRange,initial); |
|
|||
76 | QFETCH(Seconds<double>,shift); |
|
|||
77 | QFETCH(DateTimeRange,expected); |
|
|||
78 | QCOMPARE(initial+shift, expected); |
|
|||
79 | } |
|
|||
80 |
|
||||
81 | void testRangeZoom_data() |
|
|||
82 | { |
|
|||
83 | QTest::addColumn<DateTimeRange>("initial"); |
|
|||
84 | QTest::addColumn<double>("zoom"); |
|
|||
85 | QTest::addColumn<DateTimeRange>("expected"); |
|
|||
86 | auto now = QDateTime::currentDateTime(); |
|
|||
87 | auto yestd = QDateTime::currentDateTime().addDays(-1); |
|
|||
88 | auto range = DateTimeRange::fromDateTime(yestd, now); |
|
|||
89 | QTest::newRow("No zoom") << range << 1. << range; |
|
|||
90 |
|
||||
91 | QTest::newRow("Zoom IN 0.001") << range << 1.001 << |
|
|||
92 | computeZoom(yestd, now, 1.001); |
|
|||
93 | QTest::newRow("Zoom OUT 0.001") << range << 0.999 << |
|
|||
94 | computeZoom(yestd, now, 0.999); |
|
|||
95 | } |
|
|||
96 | void testRangeZoom() |
|
|||
97 | { |
|
|||
98 | QFETCH(DateTimeRange,initial); |
|
|||
99 | QFETCH(double,zoom); |
|
|||
100 | QFETCH(DateTimeRange,expected); |
|
|||
101 | QCOMPARE(initial*zoom, expected); |
|
|||
102 | } |
|
|||
103 |
|
||||
104 | void testRangeContains_data() |
|
|||
105 | { |
|
|||
106 | QTest::addColumn<DateTimeRange>("range"); |
|
|||
107 | QTest::addColumn<DateTimeRange>("range2"); |
|
|||
108 | QTest::addColumn<bool>("contains"); |
|
|||
109 | auto now = QDateTime::currentDateTime(); |
|
|||
110 | auto yestd = QDateTime::currentDateTime().addDays(-1); |
|
|||
111 | auto range = DateTimeRange::fromDateTime(yestd, now); |
|
|||
112 | QTest::newRow("Same range") << range << range << true; |
|
|||
113 | QTest::newRow("Smaller range") << range << range * 0.8 << true; |
|
|||
114 | QTest::newRow("Bigger range") << range << range * 1.2 << false; |
|
|||
115 | QTest::newRow("Shifted range with overlap") << range << range + Seconds<double>{1000.} << false; |
|
|||
116 | QTest::newRow("Shifted range without overlap") << range << range + Seconds<double>{24.*60.*60.*10} << false; |
|
|||
117 | } |
|
|||
118 |
|
||||
119 | void testRangeContains() |
|
|||
120 | { |
|
|||
121 | QFETCH(DateTimeRange,range); |
|
|||
122 | QFETCH(DateTimeRange,range2); |
|
|||
123 | QFETCH(bool,contains); |
|
|||
124 | QCOMPARE(range.contains(range2), contains); |
|
|||
125 | } |
|
|||
126 |
|
||||
127 | void testRangeIntersect_data() |
|
|||
128 | { |
|
|||
129 | QTest::addColumn<DateTimeRange>("range"); |
|
|||
130 | QTest::addColumn<DateTimeRange>("range2"); |
|
|||
131 | QTest::addColumn<bool>("contains"); |
|
|||
132 | auto now = QDateTime::currentDateTime(); |
|
|||
133 | auto yestd = QDateTime::currentDateTime().addDays(-1); |
|
|||
134 | auto tomorrow = QDateTime::currentDateTime().addDays(1); |
|
|||
135 | auto range = DateTimeRange::fromDateTime(yestd, now); |
|
|||
136 | auto range2 = DateTimeRange::fromDateTime(now, tomorrow); |
|
|||
137 | QTest::newRow("Same range") << range << range << true; |
|
|||
138 | QTest::newRow("Smaller range") << range << range * 0.8 << true; |
|
|||
139 | QTest::newRow("Bigger range") << range << range * 1.2 << true; |
|
|||
140 | QTest::newRow("Shifted range with overlap") << range << range + Seconds<double>{1000.} << true; |
|
|||
141 | QTest::newRow("Shifted range with overlaping boundary") << range << range2 << true; |
|
|||
142 | QTest::newRow("Shifted range .1 seonds outside") << range << range2 + Seconds<double>{.1} << false; |
|
|||
143 | QTest::newRow("Shifted range without overlap") << range << range + Seconds<double>{24.*60.*60.*10} << false; |
|
|||
144 | } |
|
|||
145 |
|
||||
146 | void testRangeIntersect() |
|
|||
147 | { |
|
|||
148 | QFETCH(DateTimeRange,range); |
|
|||
149 | QFETCH(DateTimeRange,range2); |
|
|||
150 | QFETCH(bool,contains); |
|
|||
151 | QCOMPARE(range.intersect(range2), contains); |
|
|||
152 | } |
|
|||
153 |
|
||||
154 | void testRangeTransformations_data() |
|
|||
155 | { |
|
|||
156 | QTest::addColumn<DateTimeRange>("range1"); |
|
|||
157 | QTest::addColumn<DateTimeRange>("range2"); |
|
|||
158 | QTest::addColumn<DateTimeRangeTransformation>("transformation"); |
|
|||
159 | auto now = QDateTime::currentDateTime(); |
|
|||
160 | auto yestd = QDateTime::currentDateTime().addDays(-1); |
|
|||
161 | auto range = DateTimeRange::fromDateTime(yestd, now); |
|
|||
162 |
|
||||
163 | QTest::newRow("Same range") << range << range << DateTimeRangeTransformation{1.,Seconds<double>{0.}}; |
|
|||
164 |
|
||||
165 | QTest::newRow("Transformatio 1.1x + 10s") << range << range*1.1 + Seconds<double>{10.} |
|
|||
166 | << DateTimeRangeTransformation{1.1, Seconds<double>{10.}}; |
|
|||
167 |
|
||||
168 | QTest::newRow("Transformatio 1.x + 10s") << range << range*1. + Seconds<double>{10.} |
|
|||
169 | << DateTimeRangeTransformation{1., Seconds<double>{10.}}; |
|
|||
170 |
|
||||
171 | QTest::newRow("Transformatio 1.1x + 0s") << range << range*1.1 + Seconds<double>{0.} |
|
|||
172 | << DateTimeRangeTransformation{1.1,Seconds<double>{0.}}; |
|
|||
173 |
|
||||
174 | QTest::newRow("Transformatio 0.9x - 10s") << range << range*0.9 + Seconds<double>{-10.} |
|
|||
175 | << DateTimeRangeTransformation{0.9, Seconds<double>{-10.}}; |
|
|||
176 |
|
||||
177 | } |
|
|||
178 | void testRangeTransformations() |
|
|||
179 | { |
|
|||
180 | QFETCH(DateTimeRange,range1); |
|
|||
181 | QFETCH(DateTimeRange,range2); |
|
|||
182 | QFETCH(DateTimeRangeTransformation, transformation); |
|
|||
183 | auto computed_tr = DateTimeRangeHelper::computeTransformation(range1,range2).value(); |
|
|||
184 | QCOMPARE(computed_tr, transformation); |
|
|||
185 | QCOMPARE(range1.transform(transformation),range2); |
|
|||
186 | QCOMPARE(range1.transform(computed_tr),range2); |
|
|||
187 | } |
|
|||
188 |
|
||||
189 | void testRangeDiff_data() |
|
|||
190 | { |
|
|||
191 | QTest::addColumn<DateTimeRange>("source"); |
|
|||
192 | QTest::addColumn<DateTimeRange>("destination"); |
|
|||
193 | QTest::addColumn<int>("count"); |
|
|||
194 | auto now = QDateTime::currentDateTime(); |
|
|||
195 | auto yestd = QDateTime::currentDateTime().addDays(-1); |
|
|||
196 | auto range = DateTimeRange::fromDateTime(yestd, now); |
|
|||
197 | QTest::newRow("Same range") << range << range << 0; |
|
|||
198 | QTest::newRow("No missing data (zoom in)") << range << range*0.9 << 0; |
|
|||
199 | QTest::newRow("Missing data on both sides (zoom out)") << range << range*2. << 2; |
|
|||
200 | QTest::newRow("Missing data on left side (pan left)") << range << range-Seconds<double>{1000.} << 1; |
|
|||
201 | QTest::newRow("Missing data on right side (pan right)") << range << range+Seconds<double>{1000.} << 1; |
|
|||
202 | QTest::newRow("Missing data on right side no intersect (big pan right)") << range << range+Seconds<double>{24.*60.*60.*2} << 1; |
|
|||
203 | QTest::newRow("Missing data on left side no intersect (big pan left)") << range << range-Seconds<double>{24.*60.*60.*2} << 1; |
|
|||
204 |
|
||||
205 | } |
|
|||
206 |
|
||||
207 | void testRangeDiff() |
|
|||
208 | { |
|
|||
209 | QFETCH(DateTimeRange,source); |
|
|||
210 | QFETCH(DateTimeRange,destination); |
|
|||
211 | QFETCH(int, count); |
|
|||
212 | auto diff = destination-source; |
|
|||
213 | QCOMPARE(diff.size(),count); |
|
|||
214 | for(const auto& range:diff) |
|
|||
215 | { |
|
|||
216 | QVERIFY(range.delta().value>0.); |
|
|||
217 | } |
|
|||
218 | } |
|
243 | } | |
|
244 | } | |||
219 | }; |
|
245 | }; | |
220 | QTEST_MAIN(TestDateTimeRange) |
|
246 | QTEST_MAIN(TestDateTimeRange) | |
221 |
|
247 | |||
222 |
|
||||
223 | #include "TestDateTimeRange.moc" |
|
248 | #include "TestDateTimeRange.moc" |
General Comments 0
You need to be logged in to leave comments.
Login now