1
+ /*
2
+ * Copyright 2020 Robert Bosch GmbH
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
+ * SPDX-License-Identifier: Apache-2.0
17
+ */
18
+ /* *
19
+ * \file noise_data.hpp
20
+ */
21
+
22
+ #pragma once
23
+
24
+ #include < memory> // for shared_ptr<>
25
+ #include < random> // for default_random_engine, normal_distribution<>
26
+ #include < string> // for string
27
+ #include < utility> // for move
28
+
29
+ #include < cloe/component.hpp> // for Component, ComponentFactory, ...
30
+ #include < cloe/core.hpp> // for Confable, Schema
31
+ #include < cloe/entity.hpp> // for Entity
32
+
33
+ namespace cloe {
34
+ namespace component {
35
+
36
+ using Generator = std::default_random_engine;
37
+
38
+ template <typename T>
39
+ class Distribution : public Confable , public Entity {
40
+ public:
41
+ using Entity::Entity;
42
+ virtual ~Distribution () noexcept = default ;
43
+
44
+ virtual T get (Generator&) const = 0;
45
+
46
+ void to_json (Json& j) const override {
47
+ j = Json{
48
+ {" binding" , name ()},
49
+ };
50
+ }
51
+
52
+ virtual void reset () {}
53
+
54
+ CONFABLE_FRIENDS (Distribution)
55
+ };
56
+
57
+ template <typename T>
58
+ class NormalDistribution : public Distribution <T> {
59
+ public:
60
+ NormalDistribution () : Distribution<T>(" normal" ) {}
61
+ virtual ~NormalDistribution () noexcept = default ;
62
+
63
+ T get (Generator& g) const override { return distribution (g); }
64
+
65
+ void reset () override { distribution = std::normal_distribution<T>{mean, std_deviation}; }
66
+
67
+ void to_json (Json& j) const override {
68
+ Distribution<T>::to_json (j);
69
+ j[" args" ] = Json{
70
+ {" mean" , mean},
71
+ {" std_deviation" , std_deviation},
72
+ };
73
+ }
74
+
75
+ void from_conf (const Conf& c) override {
76
+ Confable::from_conf (c);
77
+ reset ();
78
+ }
79
+
80
+ protected:
81
+ Schema schema_impl () override {
82
+ // clang-format off
83
+ return Schema{
84
+ {" binding" , make_const_schema (this ->name (), " identifier of this distribution" ).require ()},
85
+ {" args" , Schema{
86
+ {" mean" , make_schema (&mean, " mean value of normal distribution" )},
87
+ {" std_deviation" , make_schema (&std_deviation, " standard deviation of normal distribution" )},
88
+ }},
89
+ };
90
+ // clang-format on
91
+ }
92
+
93
+ private:
94
+ // Configuration
95
+ double mean = 0.0 ;
96
+ double std_deviation = 0.1 ;
97
+
98
+ // State
99
+ mutable std::normal_distribution<double > distribution{mean, std_deviation};
100
+ };
101
+
102
+ using DistributionPtr = std::shared_ptr<Distribution<double >>;
103
+
104
+ template <typename T = double , typename P = std::shared_ptr<Distribution<T>>>
105
+ class DistributionSchema : public schema ::Base<DistributionSchema<T, P>> {
106
+ using Base = schema::Base<DistributionSchema<T, P>>;
107
+ using Type = P;
108
+
109
+ const std::map<std::string, std::function<Distribution<T>*(const Conf& c)>> distributions{
110
+ {" normal" ,
111
+ [](const Conf& c) {
112
+ auto d = new NormalDistribution<T>();
113
+ if (c.has (" args" )) {
114
+ d->from_conf (c);
115
+ }
116
+ return d;
117
+ }},
118
+ };
119
+
120
+ public: // Constructors
121
+ DistributionSchema (Type* ptr, std::string&& desc)
122
+ : Base(JsonType::object, std::move(desc)), ptr_(ptr) {}
123
+
124
+ public: // Special
125
+ const std::vector<Schema>& schemas () const { return schemas_; }
126
+ Json json_schemas () const {
127
+ Json vec = Json::array ();
128
+ for (const auto & s : schemas_) {
129
+ vec.push_back (s.json_schema ());
130
+ }
131
+ return vec;
132
+ }
133
+
134
+ public: // Overrides
135
+ Json json_schema () const override {
136
+ Json j{
137
+ {" oneOf" , json_schemas ()},
138
+ };
139
+ this ->augment_schema (j);
140
+ return j;
141
+ }
142
+
143
+ void validate (const Conf& c) const override {
144
+ size_t valid = 0 ;
145
+ for (const auto & s : schemas_) {
146
+ if (s.is_valid (c)) {
147
+ valid++;
148
+ }
149
+ }
150
+ if (valid != 1 ) {
151
+ this ->throw_error (c, " require exactly one sub-schema to match" );
152
+ }
153
+ }
154
+
155
+ void to_json (Json& j) const override { j = serialize (*ptr_); }
156
+
157
+ void from_conf (const Conf& c) override {
158
+ assert (ptr_ != nullptr );
159
+ *ptr_ = deserialize (c);
160
+ }
161
+
162
+ Json serialize (const Type& x) const { return x; }
163
+
164
+ Type deserialize (const Conf& c) const {
165
+ auto binding = c.get <std::string>(" binding" );
166
+ return Type (distributions.at (binding)(c));
167
+ }
168
+
169
+ void reset_ptr () override {
170
+ ptr_ = nullptr ;
171
+ for (auto & s : schemas_) {
172
+ s.reset_ptr ();
173
+ }
174
+ }
175
+
176
+ private:
177
+ Type* ptr_{nullptr };
178
+ std::vector<Schema> schemas_{
179
+ NormalDistribution<T>().schema (),
180
+ };
181
+ };
182
+
183
+ template <typename T>
184
+ class Random {
185
+ public:
186
+ Random (const unsigned long & seed, DistributionPtr dist) : engine_(seed), d(dist) {}
187
+
188
+ virtual ~Random () noexcept = default ;
189
+
190
+ T get () const { return d->get (engine_); }
191
+
192
+ void reset (const unsigned long & seed) { engine_ = std::default_random_engine (seed); }
193
+
194
+ void reset (DistributionPtr dist) { d = dist; }
195
+
196
+ private:
197
+ mutable std::default_random_engine engine_;
198
+ DistributionPtr d;
199
+ };
200
+
201
+ class NoiseConf : public Confable {
202
+ public:
203
+ NoiseConf () = default ;
204
+
205
+ virtual ~NoiseConf () noexcept = default ;
206
+
207
+ double get () const { return rnd_.get (); }
208
+
209
+ virtual void reset (unsigned long seed) {
210
+ rnd_.reset (distr_default);
211
+ rnd_.reset (seed);
212
+ // In case of multiple random number generators, a different seed must
213
+ // be used for each generator (e.g. increment after each rnd_.reset).
214
+ }
215
+
216
+ CONFABLE_SCHEMA (NoiseConf) {
217
+ return Schema{
218
+ // clang-format off
219
+ {" distribution" , DistributionSchema<>(&distr_default, " set distribution binding and arguments" )},
220
+ // clang-format on
221
+ };
222
+ }
223
+
224
+ void to_json (Json& j) const override {
225
+ j = Json{
226
+ {" distribution" , distr_default},
227
+ };
228
+ }
229
+
230
+ private:
231
+ DistributionPtr distr_default{nullptr };
232
+ Random<double > rnd_ = Random<double >(0 , distr_default);
233
+ };
234
+
235
+ struct NoisySensorConf : public Confable {
236
+ /* *
237
+ * This flag exists so that an action can modify it at runtime.
238
+ */
239
+ bool enabled = true ;
240
+
241
+ /* *
242
+ * If reuse_seed is true, then in every reset we want to use the same
243
+ * random seed. This is generally the behavior that we want when
244
+ * restarting a simulation, as this preserves the same noise pattern.
245
+ */
246
+ bool reuse_seed = true ;
247
+
248
+ /* *
249
+ * When set to 0, a new random seed is retrieved.
250
+ */
251
+ unsigned long seed = 0 ;
252
+
253
+ CONFABLE_SCHEMA (NoisySensorConf) {
254
+ return Schema{
255
+ {" enable" , Schema (&enabled, " enable or disable component" )},
256
+ {" reuse_seed" , Schema (&reuse_seed, " whether to get a new seed on reset" )},
257
+ {" seed" , Schema (&seed, " set random engine seed (effective on reset)" )},
258
+ };
259
+ }
260
+
261
+ void to_json (Json& j) const override {
262
+ j = Json{
263
+ {" enable" , enabled},
264
+ {" reuse_seed" , reuse_seed},
265
+ {" seed" , seed},
266
+ };
267
+ }
268
+ };
269
+
270
+ } // namespace component
271
+ } // namespace cloe
0 commit comments