/** Read MotionNode binary take stream files and output a plain text file. The intended purpose is to provide raw and sensor data export to programs like Excel. @file tools/sdk/cpp/binary_to_text/binary_to_text.cpp @author Luke Tokheim, luke@motionnode.com @version 1.4 (C) Copyright GLI Interactive LLC 2011. All rights reserved. The coded instructions, statements, computer programs, and/or related material (collectively the "Data") in these files contain unpublished information proprietary to GLI Interactive LLC, which is protected by US federal copyright law and by international treaties. The Data may not be disclosed or distributed to third parties, in whole or in part, without the prior written consent of GLI Interactive LLC. The Data is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose. */ #include #include #include #include #include #include #include const std::size_t MaxOptionLength = 1024; const std::size_t MinChannel = 9; const std::size_t MaxChannel = 10; const std::string ChannelName[MaxChannel] = { "ax", "ay", "az", "mx", "my", "mz", "gx", "gy", "gz", "temp" }; template void print_fields(std::ostream & out, const T & data, const std::size_t & data_size, const std::string & separator, bool is_accel) { if ((data_size >= MinChannel) && (data_size <= MaxChannel)) { for (std::size_t i=0; i 9))) { out << data[i]; if (i < data_size - 1) { out << separator; } } } out << std::endl; } } void print_header(std::ostream & out, const std::size_t & data_size, const std::string & separator, bool is_accel) { print_fields(out, ChannelName, data_size, separator, is_accel); } template void print_container(std::ostream & out, const T & data, const std::string & separator, bool is_accel) { print_fields(out, data, data.size(), separator, is_accel); } template bool binary_to_text(const std::string & pathname, std::ostream & out, bool degree_format, bool show_channel_names, const std::string & separator) { bool result = true; try { using MotionNode::SDK::File; using MotionNode::SDK::Format; File file(pathname); typename ElementT::data_type data(ElementT::Length + 1); if (file.readData(data)) { if (0 == data.back()) { if (show_channel_names) { print_header(out, data.size(), separator, false); } } else { typename ElementT::data_type::value_type tmp = data.back(); data.resize(ElementT::Length); if (show_channel_names) { print_header(out, data.size(), separator, false); } print_container(out, data, separator, false); typename ElementT::data_type next_data(ElementT::Length - 1); if (file.readData(next_data)) { data.front() = tmp; std::copy( next_data.begin(), next_data.end(), data.begin() + 1); } else { data.clear(); } } if (!data.empty()) { do { print_container(out, data, separator, false); } while (file.readData(data)); } } } catch (std::runtime_error & e) { std::cerr << e.what() << std::endl; result = false; } return result; } bool binary_to_text(const std::string & input_file, std::ostream & out, bool degree_format, bool raw_format, bool show_channel_names, const std::string & separator) { using MotionNode::SDK::Format; bool result = false; if (raw_format) { if (binary_to_text(input_file, out, false, show_channel_names, separator)) { result = true; } } else { if (binary_to_text(input_file, out, degree_format, show_channel_names, separator)) { result = true; } } return result; } void print_usage(const std::string & name, std::ostream & out) { // Print usage. out << "Usage: " << name << " [OPTION]... FILENAME[...]" << std::endl << "Read a MotionNode binary sensor or raw stream file and output a plain text, comma separated version." << std::endl << std::endl << "Options" << std::endl << "-f, --file FILENAME output results to a file, use - for standard output" << std::endl << "-h, --help prints this message" << std::endl << "-r, --raw input files are raw format data files, default is sensor format" << std::endl << "-n, --nonames do not print the channel name headers" << std::endl << "-s, --separator STRING element delimiter string, default is \", \" (CSV)" << std::endl << std::endl; } // Entry point. Parse arguments and call worker function // for the intended input and output data formats. int main(int argc, char * argv[]) { int result = 1; // Initialize all options. std::string name; if (argc > 0) { name.assign(argv[0]); } std::vector input_file; std::string separator = ", "; bool degree_format = false; bool raw_format = false; bool show_channel_names = true; bool output_stdout = false; std::string output_file; // Parse command line options. Override the defaults we // just set above. bool valid_command_line = true; for (int i=1; i 1) && (('-' == *argv[i]) || ('/' == *argv[i]))) { std::string option(argv[i] + 1, length-1); std::transform( option.begin(), option.end(), option.begin(), tolower); // Remove a leading '-' character if it exists. if (!option.empty() && ('-' == option[0])) { option = option.substr(1); } // Do the actual option handling and assign // local flags. if ("file" == option || "f" == option) { if (argc >= i) { i++; std::string filename(argv[i]); if ("-" == filename) { output_stdout = true; } else { output_file.assign(argv[i]); } } else { std::cerr << "invalid option, missing argument: " << std::string(argv[i], length) << std::endl; valid_command_line = false; } } else if ("separator" == option || "s" == option) { if (argc >= i) { i++; separator.assign(argv[i]); } else { std::cerr << "invalid option, missing argument: " << std::string(argv[i], length) << std::endl; valid_command_line = false; } //} else if ("degree" == option || "d" == option) { // degree_format = true; } else if ("raw" == option || "r" == option) { raw_format = true; } else if ("nonames" == option || "n" == option) { show_channel_names = false; } else if ("help" == option || "h" == option) { valid_command_line = false; } else { std::cerr << "unknown option: " << std::string(argv[i], length) << std::endl; valid_command_line = false; } } else if (length > 0) { // Anything no captured by an option is considered to be a file name. input_file.push_back(std::string(argv[i], length)); } } // If we have valid options and at least one input file, run the conversion loop. // Otherwise, print out the usage information. if (valid_command_line && !input_file.empty()) { std::ofstream * fout = NULL; if (!output_stdout && !output_file.empty()) { fout = new std::ofstream(output_file.c_str(), std::ios_base::binary | std::ios_base::out); if (!fout->is_open()) { delete fout; fout = NULL; std::cerr << "failed to open output file, \"" << output_file << "\"" << std::endl; } } // There is no output file name list and we did not select the // standard output. Generate output file names based on the input // file names. const bool auto_file_name = !output_stdout && (NULL == fout); result = 0; for (std::vector::const_iterator itr=input_file.begin(); itr!=input_file.end(); ++itr) { if (auto_file_name) { output_file = (*itr) + ".csv"; fout = new std::ofstream(output_file.c_str(), std::ios_base::binary | std::ios_base::out); if (!fout->is_open()) { delete fout; fout = NULL; } } if (!binary_to_text(*itr, (NULL != fout) ? *fout : std::cout, degree_format, raw_format, show_channel_names, separator)) { result = 1; } if (auto_file_name) { if (NULL != fout) { fout->close(); delete fout; fout = NULL; } } } if (NULL != fout) { fout->close(); delete fout; fout = NULL; } } else { print_usage(name, std::cerr); } return result; }