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