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 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 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 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 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