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