1 /*
2 * Copyright (C) 2017 The Android Open Source Project
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 #include "ConfigManager.h"
17
18 #include "json/json.h"
19
20 #include <fstream>
21 #include <math.h>
22 #include <assert.h>
23
24
25 static const float kDegreesToRadians = M_PI / 180.0f;
26
27
normalizeToPlusMinus180degrees(float theta)28 static float normalizeToPlusMinus180degrees(float theta) {
29 const float wraps = floor((theta+180.0f) / 360.0f);
30 return theta - wraps*360.0f;
31 }
32
33
readChildNodeAsFloat(const char * groupName,const Json::Value & parentNode,const char * childName,float * value)34 static bool readChildNodeAsFloat(const char* groupName,
35 const Json::Value& parentNode,
36 const char* childName,
37 float* value) {
38 // Must have a place to put the value!
39 assert(value);
40
41 Json::Value childNode = parentNode[childName];
42 if (!childNode.isNumeric()) {
43 printf("Missing or invalid field %s in record %s", childName, groupName);
44 return false;
45 }
46
47 *value = childNode.asFloat();
48 return true;
49 }
50
51
ReadChildNodeAsUint(const char * groupName,const Json::Value & parentNode,const char * childName,unsigned * value)52 static bool ReadChildNodeAsUint(const char* groupName,
53 const Json::Value& parentNode,
54 const char* childName,
55 unsigned* value) {
56 // Must have a place to put the value!
57 assert(value);
58
59 Json::Value childNode = parentNode[childName];
60 if (!childNode.isNumeric()) {
61 printf("Missing or invalid field %s in record %s", childName, groupName);
62 return false;
63 }
64
65 *value = childNode.asUInt();
66 return true;
67 }
68
69
initialize(const char * configFileName)70 bool ConfigManager::initialize(const char* configFileName)
71 {
72 bool complete = true;
73
74 // Set up a stream to read in the input file
75 std::ifstream configStream(configFileName);
76
77 // Parse the stream into JSON objects
78 Json::Reader reader;
79 Json::Value rootNode;
80 bool parseOk = reader.parse(configStream, rootNode, false /* don't need comments */);
81 if (!parseOk) {
82 printf("Failed to read configuration file %s\n", configFileName);
83 printf("%s\n", reader.getFormatedErrorMessages().c_str());
84 return false;
85 }
86
87
88 //
89 // Read car information
90 //
91 {
92 Json::Value car = rootNode["car"];
93 if (!car.isObject()) {
94 printf("Invalid configuration format -- we expect a car description\n");
95 return false;
96 }
97 complete &= readChildNodeAsFloat("car", car, "width", &mCarWidth);
98 complete &= readChildNodeAsFloat("car", car, "wheelBase", &mWheelBase);
99 complete &= readChildNodeAsFloat("car", car, "frontExtent", &mFrontExtent);
100 complete &= readChildNodeAsFloat("car", car, "rearExtent", &mRearExtent);
101 }
102
103
104 //
105 // Read display layout information
106 //
107 {
108 Json::Value displayNode = rootNode["display"];
109 if (!displayNode.isObject()) {
110 printf("Invalid configuration format -- we expect a display description\n");
111 return false;
112 }
113 complete &= ReadChildNodeAsUint("display", displayNode, "width", &mPixelWidth);
114 complete &= ReadChildNodeAsUint("display", displayNode, "height", &mPixelHeight);
115 complete &= readChildNodeAsFloat("display", displayNode, "frontRange", &mFrontRangeInCarSpace);
116 complete &= readChildNodeAsFloat("display", displayNode, "rearRange", &mRearRangeInCarSpace);
117 }
118
119
120 //
121 // Car top view texture properties for top down view
122 //
123 {
124 Json::Value graphicNode = rootNode["graphic"];
125 if (!graphicNode.isObject()) {
126 printf("Invalid configuration format -- we expect a graphic description\n");
127 return false;
128 }
129 complete &= readChildNodeAsFloat("graphic", graphicNode, "frontPixel", &mCarGraphicFrontPixel);
130 complete &= readChildNodeAsFloat("display", graphicNode, "rearPixel", &mCarGraphicRearPixel);
131 }
132
133
134 //
135 // Read camera information
136 // NOTE: Missing positions and angles are not reported, but instead default to zero
137 //
138 {
139 Json::Value cameraArray = rootNode["cameras"];
140 if (!cameraArray.isArray()) {
141 printf("Invalid configuration format -- we expect an array of cameras\n");
142 return false;
143 }
144
145 mCameras.reserve(cameraArray.size());
146 for (auto&& node: cameraArray) {
147 // Get data from the configuration file
148 Json::Value nameNode = node.get("cameraId", "MISSING");
149 const char *cameraId = nameNode.asCString();
150 printf("Loading camera %s\n", cameraId);
151
152 Json::Value usageNode = node.get("function", "");
153 const char *function = usageNode.asCString();
154
155 float yaw = node.get("yaw", 0).asFloat();
156 float pitch = node.get("pitch", 0).asFloat();
157 float hfov = node.get("hfov", 0).asFloat();
158 float vfov = node.get("vfov", 0).asFloat();
159
160 // Wrap the direction angles to be in the 180deg to -180deg range
161 // Rotate 180 in yaw if necessary to flip the pitch into the +/-90degree range
162 pitch = normalizeToPlusMinus180degrees(pitch);
163 if (pitch > 90.0f) {
164 yaw += 180.0f;
165 pitch = 180.0f - pitch;
166 }
167 if (pitch < -90.0f) {
168 yaw += 180.0f;
169 pitch = -180.0f + pitch;
170 }
171 yaw = normalizeToPlusMinus180degrees(yaw);
172
173 // Range check the FOV values to ensure they are postive and less than 180degrees
174 if (hfov > 179.0f) {
175 printf("Pathological horizontal field of view %f clamped to 179 degrees\n", hfov);
176 hfov = 179.0f;
177 }
178 if (hfov < 1.0f) {
179 printf("Pathological horizontal field of view %f clamped to 1 degree\n", hfov);
180 hfov = 1.0f;
181 }
182 if (vfov > 179.0f) {
183 printf("Pathological horizontal field of view %f clamped to 179 degrees\n", vfov);
184 vfov = 179.0f;
185 }
186 if (vfov < 1.0f) {
187 printf("Pathological horizontal field of view %f clamped to 1 degree\n", vfov);
188 vfov = 1.0f;
189 }
190
191 // Store the camera info (converting degrees to radians in the process)
192 CameraInfo info;
193 info.position[0] = node.get("x", 0).asFloat();
194 info.position[1] = node.get("y", 0).asFloat();
195 info.position[2] = node.get("z", 0).asFloat();
196 info.yaw = yaw * kDegreesToRadians;
197 info.pitch = pitch * kDegreesToRadians;
198 info.hfov = hfov * kDegreesToRadians;
199 info.vfov = vfov * kDegreesToRadians;
200 info.cameraId = cameraId;
201 info.function = function;
202
203 mCameras.push_back(info);
204 }
205 }
206
207 // If we got this far, we were successful as long as we found all our child fields
208 return complete;
209 }
210