Preliminaries
I have OWON oscilloscope that is able to record waveform in file in three formats:
- Text format
.txt
: the first lines are some general information such as peak-to-peak value, period, frequency etc. Remaining lines are waveform points that is space-separated values. - Comma-separated-values format
.csv
: the first lines are some general information such as peak-to-peak value, period, frequency etc. Remaining lines are waveform points that is comma-separated values. - Binary format
.bin
: binary file with unknown (for now) with the following structure:- Prefix. Several bytes of some information such as header and setup information. Unknown for now.
- Data bytes. 8-bit signed integers that represent ordinates of waveform points.
- New line character
0x0a
.
I am not interested in the first two options (at least for know) so I will concentrate on dealing with binary data only.
Another fact is you are able to choose how to record the data. There are two options:
- Using a software.
- Using an oscilloscope itself.
The structure of a binary file is different depending on what option was used. For example the size of the prefix is 78 and 278 bits respectively.
My purpose is to provide user-friendly interface to parse data from oscilloscope.
Result
I chose to place all stuff in the namespace
which I named owon
.
1. owon namespace structure
The following is just a short description of the owon
namespace.
namespace owon
{
/*Wafeform point*/
struct Point;
/*Storage of useful information such as position in a file where data
*starts from or where it ends*/
struct FilePosition;
/*Binary data is being produced either by the OWON software
*or directly by pushing the button on an oscilloscope*/
enum FileCreator { Software, Device };
/*Abstract class to parse file*/
class Parser;
/*Concrete parser
*that parses only binary files
*though depending on source of the file (software or device)
*it uses different parse algorithm*/
template<FileCreator creator>
class BinaryParser : public Parser;
/*Iterator class
*that iterates through points in the waveform
*Does not depend on concrete parser (format) or source of the file (file creator)*/
class PointIterator;
2. owoh.h
2.1 Preamble
//owon.h
#ifndef OWON_H
#define OWON_H
#include <iostream>
#include <fstream>
#include <string>
#include <stdexcept>//for std::runtime_error
#include <stdint.h>//for int8_t
#include <stddef.h>//for NULL
#define MY_WARNING(msg) std::cout << "WARNING: " << msg << std::endl
namespace owon
{
//continued below...
2.2 struct Point
This structure represents a data point.
//owoh.h continued
struct Point
{
float x;
float y;
};
//continued below...
2.3 struct FilePosition
This structure is to store some useful info about bytes in a file.
//owon.h continued
struct FilePosition
{
size_t dataPosition;//position in file where data begins from
size_t lastPosition;//position in file where data ends
};
//continued below...
2.4 enum FileCreator
This enumearation represents two possible option how to record data: through the software or from the oscilloscope.
//owon.h continued
/*Binary data is being produced either by the OWON software
*or directly by pushing the button on an oscilloscope*/
enum FileCreator { Software, Device };
//continued below...
2.5 class Parser
Abstract class that provides a protocol for all parsers (there is only one for now). It
//owon.h continued
class Parser
{
protected :
/*CTORS (objects of this class are not copiable and assignable)*/
Parser( std::string fileName ) :
_fileName( fileName ),
_isParsed( false )
{ }
Parser( const Parser& );
Parser& operator=( const Parser& );
//DTOR
~Parser()
{
if( _fStream.is_open() ) { _fStream.close(); }
}
/*Member data: */
std::string _fileName;//name of file to be parsed
FilePosition _filePosition;//storage of useful info about data position in file
std::ifstream _fStream;//file stream
/*Flags*/
bool _isParsed;
/*This method should not be accessible from the outside*/
virtual Point ConstructPoint( size_t index ) = 0;
public :
inline std::string GetFileName() { return _fileName; }
PointIterator* CreatePointIterator();
/*Main method that parse file
*used for determine data positions in file
*depending on in what format (bin, csv or txt)
*and from what source (software or device) file is*/
virtual void Parse() = 0;
friend class PointIterator;
};//Parser
//continued below...
2.6 template class BinaryParser
This is concrete parser - binary data parser.
//owon.h continued
/*Concrete parser
*that parses only binary files
*though depending on source of the file (software or device)
*it uses different parse algorithm*/
template<FileCreator creator>
class BinaryParser : public Parser
{
protected :
FileCreator _creator;
Point ConstructPoint( size_t index );
public :
BinaryParser( std::string fileName ) : Parser( fileName ),
_creator( creator )
{
/*Check if fileName contains an extension suffix*/
if( fileName.rfind( ".bin" ) == std::string::npos )
{
MY_WARNING( "Binary file without '.bin' extension was specified." );
}
}
void Parse();
};//BinaryParser
//continued below...
2.7 class PointIterator
This class is developed in order to provide user-friendly iteration over the data points e.g. inside a for
loop.
//owon.h continued
/*Iterator class
*that iterates through points in the waveform
*Does not depend on concrete parser (format) or source of the file (file creator)*/
class PointIterator
{
protected :
Parser* _parser;
size_t _index;
public :
PointIterator( Parser* parser ) : _parser( parser ) { }
void First() { _index = _parser->_filePosition.dataPosition; }
void Next() { _index++; }
bool Last() { return ( _index == _parser->_filePosition.lastPosition ); }
Point CurrentPoint() { return _parser->ConstructPoint( _index ); }
};//PointIterator
};//owon
3. owon.cpp
3.1 BinaryParser::Parse method
Parsing algorithm for binary data
//owon.cpp
#include "owon.h"
namespace owon
{
/*Main method that parses the file*/
template<FileCreator creator>
void BinaryParser<creator>::Parse()
{
_isParsed = true;
/*Try to open file in binary mod*/
_fStream.open( _fileName.c_str(), std::ios::out | std::ios::binary );
if( !_fStream )
{
_isParsed = false;
throw std::runtime_error( "Could not open file.\n" );
}
else
{
/*Set the start position depending on the source of the file*/
if ( creator == Software ) { _filePosition.dataPosition = 278; }
else if( creator == Device ) { _filePosition.dataPosition = 78; }
/*Determine the end of the data
*NOTE: the very last byte is omitted*/
_fStream.seekg(-1, _fStream.end );
_filePosition.lastPosition = _fStream.tellg();
if( _filePosition.lastPosition < _filePosition.dataPosition )
{
_isParsed = false;
throw std::runtime_error( "No data in the file.\n" );
}
/*Place cursor at the start of the data*/
_fStream.seekg( _filePosition.dataPosition, _fStream.beg );
}
}
//continued below...
3.2 BinaryParser::ConstructPoint method
This method constructs Point
object from binary data
//owon.cpp continued
/*Method that returns current point of parsed waveform*/
template<FileCreator creator>
Point BinaryParser<creator>::ConstructPoint( size_t index )
{
Point a = { 0., 0. };
/*check if index is out of range*/
if( index < _filePosition.dataPosition || index > _filePosition.lastPosition )
{
MY_WARNING( "Requesting position is out of range. Returned (0,0)" );
return a;
}
else if( _fStream.is_open() )
{
/*Read a byte from file*/
int8_t intY;
_fStream.read( reinterpret_cast< char* >(&intY), sizeof(intY) );
/*Assign coordinates
*using a strange conversion rule :D*/
a.x = (float)index - _filePosition.dataPosition;//User does not care about position of data, let it start from zero
a.y = 0.4 * intY + 53.2;//No comments for a while
}
else
{
MY_WARNING( "File is not open." );
}
return a;
}
//continued below...
3.3 Parser::CreatePointIterator method
This method creates a PointIterator
object.
PointIterator* Parser::CreatePointIterator()
{
if( _isParsed )
{
return new PointIterator( this );
}
else
{
MY_WARNING( "Bad parsing occured." );
return NULL;
}
}
};//owon
4. Using
This is how I use it.
//Test.cpp
#include <iostream>
#include "owon_parser.cpp"
using namespace owon;
int main()
{
BinaryParser<Software> bps( "wf_from_software.bin" );
try
{
bps.Parse();
}
catch (const std::runtime_error& e )
{
std::cout << e.what();
}
PointIterator* it = bps.CreatePointIterator();
for( it->First(); !it->Last(); it->Next() )
{
Point a = it->CurrentPoint();
std::cout << a.x << " " << a.y << std::endl;
}
return 0;
}
Problem
The problem that I can see is that PointIterator
class has access to all stuff of class Parser
. I cannot specify const
pointer to Parser
class inside PointIterator
class because of method ConstructPoint
(sec. 3.2) that reads from a file and therefore cannot be declared as const
.
Sorry for a lot of code and what is your advice to me?