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