Home | History | Annotate | Download | only in app
      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