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 bool ConfigManager::initialize(const char* configFileName) 53 { 54 bool complete = true; 55 56 // Set up a stream to read in the input file 57 std::ifstream configStream(configFileName); 58 59 // Parse the stream into JSON objects 60 Json::Reader reader; 61 Json::Value rootNode; 62 bool parseOk = reader.parse(configStream, rootNode, false /* don't need comments */); 63 if (!parseOk) { 64 printf("Failed to read configuration file %s\n", configFileName); 65 printf("%s\n", reader.getFormatedErrorMessages().c_str()); 66 return false; 67 } 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 // 87 // Read display layout information 88 // 89 { 90 Json::Value displayNode = rootNode["display"]; 91 if (!displayNode.isObject()) { 92 printf("Invalid configuration format -- we expect a display description\n"); 93 return false; 94 } 95 complete &= readChildNodeAsFloat("display", displayNode, "frontRange", &mFrontRangeInCarSpace); 96 complete &= readChildNodeAsFloat("display", displayNode, "rearRange", &mRearRangeInCarSpace); 97 } 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 &= readChildNodeAsFloat("graphic", graphicNode, "frontPixel", &mCarGraphicFrontPixel); 110 complete &= readChildNodeAsFloat("display", graphicNode, "rearPixel", &mCarGraphicRearPixel); 111 } 112 113 114 // 115 // Read camera information 116 // NOTE: Missing positions and angles are not reported, but instead default to zero 117 // 118 { 119 Json::Value cameraArray = rootNode["cameras"]; 120 if (!cameraArray.isArray()) { 121 printf("Invalid configuration format -- we expect an array of cameras\n"); 122 return false; 123 } 124 125 mCameras.reserve(cameraArray.size()); 126 for (auto&& node: cameraArray) { 127 // Get data from the configuration file 128 Json::Value nameNode = node.get("cameraId", "MISSING"); 129 const char *cameraId = nameNode.asCString(); 130 131 Json::Value usageNode = node.get("function", ""); 132 const char *function = usageNode.asCString(); 133 134 float yaw = node.get("yaw", 0).asFloat(); 135 float pitch = node.get("pitch", 0).asFloat(); 136 float hfov = node.get("hfov", 0).asFloat(); 137 float vfov = node.get("vfov", 0).asFloat(); 138 139 // Wrap the direction angles to be in the 180deg to -180deg range 140 // Rotate 180 in yaw if necessary to flip the pitch into the +/-90degree range 141 pitch = normalizeToPlusMinus180degrees(pitch); 142 if (pitch > 90.0f) { 143 yaw += 180.0f; 144 pitch = 180.0f - pitch; 145 } 146 if (pitch < -90.0f) { 147 yaw += 180.0f; 148 pitch = -180.0f + pitch; 149 } 150 yaw = normalizeToPlusMinus180degrees(yaw); 151 152 // Range check the FOV values to ensure they are postive and less than 180degrees 153 if (hfov > 179.0f) { 154 printf("Pathological horizontal field of view %f clamped to 179 degrees\n", hfov); 155 hfov = 179.0f; 156 } 157 if (hfov < 1.0f) { 158 printf("Pathological horizontal field of view %f clamped to 1 degree\n", hfov); 159 hfov = 1.0f; 160 } 161 if (vfov > 179.0f) { 162 printf("Pathological horizontal field of view %f clamped to 179 degrees\n", vfov); 163 vfov = 179.0f; 164 } 165 if (vfov < 1.0f) { 166 printf("Pathological horizontal field of view %f clamped to 1 degree\n", vfov); 167 vfov = 1.0f; 168 } 169 170 // Store the camera info (converting degrees to radians in the process) 171 CameraInfo info; 172 info.position[0] = node.get("x", 0).asFloat(); 173 info.position[1] = node.get("y", 0).asFloat(); 174 info.position[2] = node.get("z", 0).asFloat(); 175 info.yaw = yaw * kDegreesToRadians; 176 info.pitch = pitch * kDegreesToRadians; 177 info.hfov = hfov * kDegreesToRadians; 178 info.vfov = vfov * kDegreesToRadians; 179 info.cameraId = cameraId; 180 info.function = function; 181 182 mCameras.push_back(info); 183 } 184 } 185 186 // If we got this far, we were successful as long as we found all our child fields 187 return complete; 188 } 189