patterncppMinor
ASCII graphic waveform generator
Viewed 0 times
waveformgraphicasciigenerator
Problem
Working on some documentation lately, I had a need to convert a number of asynchronous serial waveforms into ASCII graphics characters. Rather than draw them by hand, I decided to write a program to do it for me. I'm interested in a code review, and particularly if there are smarter/shorter ways of coding the class.
Sample driver
Sample output
Note that in this diagram,
#include
#include
#include
#include
class waveform
{
public:
waveform(std::string &s) : data_(s) {}
std::vector to_bits() const
{
std::vector bits;
bits.reserve(data_.size() * 10);
for (auto &ch : data_) {
bits.push_back(0); // start bit
for (int mask=1u; mask != 0x100; mask >= 1;
if (special == 0)
special = 0x201;
}
out << '\n';
return out;
}
private:
std::string data_;
};Sample driver
int main()
{
std::string msg{"ST"};
std::cout << waveform(msg) << std::endl;
}Sample output
+ +---+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---
| | | | | | | | | | | | | | | |
+---+ +---+---+ +---+ +---+ +---+---+---+ +---+ +---+ +---+
S 1 1 0 0 1 0 1 0 P S 0 0 1 0 1 0 1 0 PNote that in this diagram,
S stands for a start bit, P stands for a stop bit and the 8 data bits are sent least significant bit first.Solution
The OOP design doesn't seem to be doing much for you. It feels like you could get basically the same effect using just a function. On the other hand, the
Assuming that the
operator<< method is basically a state machine, and it looks somewhat nastier than it needs to be. There is copy-and-paste code, with bits of ASCII art all over the place, and a mini-Turing machine using the magic number 0x201.Assuming that the
to_bits() helper function isn't important to you (I assume it isn't that important, because you wrote #include then changed your mind), you could do better by defining a filtering output stream. Here's my attempt to implement such a thing:#include
#include
#include
#include
#include
class waveform_ostream : public std::streambuf
{
public:
explicit waveform_ostream(std::ostream &sink) : sink(sink) {}
protected:
// Accept a byte of data
virtual std::streambuf::int_type overflow(std::streambuf::int_type c) {
output_bit(0, 'S'); // Start bit
for (int i = 0; i > i) & 1); // Data bits, LSB first
}
output_bit(1, 'P'); // Stop bit
return c;
}
// Flush
int sync() {
for (int row = 0; row str();
sink str("");
}
sink << std::flush;
return 0; // success
}
private:
bool state = 1;
std::ostream &sink;
std::stringstream level[2], risefall, captions;
std::stringstream *const lines[4] = {
&level[1],
&risefall,
&level[0],
&captions
};
void output_bit(bool b, char caption='\0') {
level[!b] << ((b == state) ? " " : "+ ");
risefall << ((b == state) ? " " : "| ");
level[ b] << "+---";
captions << " " << (char)(caption ? caption : b ? '1' : '0') << ' ';
state = b;
}
};
int main()
{
waveform_ostream wout(std::cout);
std::ostream out(&wout);
out << std::string("ST") << std::flush;
}Code Snippets
#include <climits>
#include <iostream>
#include <sstream>
#include <streambuf>
#include <string>
class waveform_ostream : public std::streambuf
{
public:
explicit waveform_ostream(std::ostream &sink) : sink(sink) {}
protected:
// Accept a byte of data
virtual std::streambuf::int_type overflow(std::streambuf::int_type c) {
output_bit(0, 'S'); // Start bit
for (int i = 0; i < CHAR_BIT; ++i) {
output_bit((c >> i) & 1); // Data bits, LSB first
}
output_bit(1, 'P'); // Stop bit
return c;
}
// Flush
int sync() {
for (int row = 0; row < 4; ++row) {
std::string s = lines[row]->str();
sink << s << '\n';
lines[row]->str("");
}
sink << std::flush;
return 0; // success
}
private:
bool state = 1;
std::ostream &sink;
std::stringstream level[2], risefall, captions;
std::stringstream *const lines[4] = {
&level[1],
&risefall,
&level[0],
&captions
};
void output_bit(bool b, char caption='\0') {
level[!b] << ((b == state) ? " " : "+ ");
risefall << ((b == state) ? " " : "| ");
level[ b] << "+---";
captions << " " << (char)(caption ? caption : b ? '1' : '0') << ' ';
state = b;
}
};
int main()
{
waveform_ostream wout(std::cout);
std::ostream out(&wout);
out << std::string("ST") << std::flush;
}Context
StackExchange Code Review Q#88148, answer score: 4
Revisions (0)
No revisions yet.