Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

What is the most elegant way to draw a timing diagram from a "0 and 1" string list? Here I started to draw the clock pulse diagram, and like to continue to draw the timing diagram for a given 0 and 1 string list, say, {11011011,11100110}. The result is supposed as the figure shown below. Pls help.

clockWidth = 0.2;
pulseHgh = 0.1;
rowHgh = pulseHgh + 0.1;
pNum = 8;
diagWidth = 2;
yb = 0;
clock = Table[
   Line[{ {xb, 
      yb} , {xb, yb} + {0, pulseHgh}, {xb, yb} + {clockWidth/2, 
       pulseHgh}, {xb, yb} + {clockWidth/2, 0}, {xb, 
       yb} + {clockWidth, 0}}], {xb, 0, clockWidth*pNum, clockWidth}];
txt = Table[
   Text[Style[n, 12, Italic], {n*clockWidth + clockWidth/2, 
     yb + 0.13}], {n, 0, pNum}];
Graphics[{clock}, Epilog -> {Text["Clock", {2, yb}], txt}, 
 PlotRange -> {-0.2, 0.2} ]

enter image description here

share|improve this question
5  
ListLinePlot[IntegerDigits@11011011, InterpolationOrder -> 0, AspectRatio -> 1/10] is a good start :) –  rm -rf Jul 14 at 2:06
    
@rm-rf I believe the way I created the clock pulse is clumsy enough. –  Putterboy Jul 14 at 2:07

3 Answers 3

Perhaps (as per rm-rf):

str = {11011011, 11100110}
sig = {#1, #2 + 
      1.5} & @@ ((IntegerDigits /@ str) /. {1 -> Sequence[1, 1], 
      0 -> Sequence[0, 0]});
clock = Join @@ Table[{4, 3}, {Length[First@sig]/2}];
plt = ListLinePlot[Join[sig, {clock}], InterpolationOrder -> 0, 
  PlotRange -> {{0, 17}, Automatic}, 
  FrameTicks -> {Thread[{Range[1.5, 15.5, 2], Range[8]}], 
    Thread[{{0.5, 2, 3.5}, {str[[1]], str[[2]], "clock"}}], None, 
    None}, Frame -> True, FrameLabel -> {"Ticks", "Signals"}, 
  ImageSize -> 500]

enter image description here

UPDATE

As m_goldberg observes the above is an approach only for the test strings. To deal with other binary strings including starting zeros and to end on full clock cycle:

fun[str_] := Module[{dig, fs, clock, n, timd},
  dig = (IntegerDigits[ToExpression@#, 10, StringLength[#]] & /@ 
      str) /. {1 -> Sequence[1, 1], 0 -> Sequence[0, 0]};
  fs = MapThread[Join[#1, {#2}] &, {dig, Last /@ dig}];
  n = Length[First@dig]/2;
  clock = Append[Join @@ Table[{1, 0}, {n}], 0];
  timd = Join[fs, {clock}];
  ListLinePlot[MapIndexed[#1 + 2 First@#2 - 2 &, timd], 
   InterpolationOrder -> 0, Frame -> True, 
   FrameTicks -> {Thread[{Range[1.5, 2 n - 0.5, 2], Range[n]}], 
     Thread[{Range[0.5, 2 Length@timd, 2], Join[str, {"clock"}]}], 
     None, None}, PlotRange -> {{0, 2 n + 2}, Automatic}]
  ]

This is not as nice as I would like, e.g.(i) I have not accounted for variable string lengths but this could be easily dealt with by just scaling to maximal string length or I could just truncate to shortest, or just only accept equal length...but I leave to needs/aims user (ii)have not programmed different clock rates relative to signal pulses (have just kept original).

An example:

fun[{"001101", "110111", "010101", "100101"}]

enter image description here


Mr.Wizard here, for a bit of refactoring:

fun[str : {__String}] :=
 Module[{fs, n, timd},
  fs = Riffle[#, #] ~Append~ Last[#] & /@ ToExpression @ Characters @ str;
  n = Length @ First @ fs;
  timd = fs ~Append~ PadLeft[{0}, n, {0, 1}];

  ListLinePlot[
   MapIndexed[#1 + 2 First@#2 - 2 &, timd],
   InterpolationOrder -> 0,
   Frame -> True, 
   FrameTicks -> {
     Thread[{Range[1.5, n - 1.5, 2], Range[n/2]}], 
     Thread[{Range[0.5, 2 Length@timd, 2], Join[str, {"clock"}]}],
     None,
     None
    },
   PlotRange -> {{0, n + 1}, Automatic}
  ]
 ]
share|improve this answer
    
@rm-rf, ubpdqn Beautiful, Thanks a lot. –  Putterboy Jul 14 at 3:29
    
@user16069 glad it was helpful... i merely applied window dressing to rm-rf 's answer...you can adapt and modify to your stylistic preferences –  ubpdqn Jul 14 at 3:38
    
My only problem with this answer is that won't work for cases where the signal stream starts with one or more zeros. –  m_goldberg Jul 16 at 14:08
    
@m_goldberg see edit –  ubpdqn Jul 17 at 10:24
    
+1 for your edit –  m_goldberg Jul 17 at 10:45

Edit

I found this a very interesting question, and I developed a solution at the Graphics level rather that the Plot level. My implementation approach is classic divide-and-conquer, building up the main function from a number of small ones. This made development straightforward, but also makes the code rather long for answers posted on this site.

Component code

Binary signals

Binary signals (bit streams) are represented by lists of ones and zeros. Signal data are expected to be string representations of binary numbers, e.g., "01100100001101000100".

bitStringToDigits[bitstr_String] := ToExpression @ Characters@ bitstr

The clock's bit stream is standardized as alternating ones and zeros beginning with a one. It only needs to be given the length of the stream.

clockDigits[tMax_Integer /; tMax > 0] := Table[Mod[i, 2], {i, tMax}]

Graphics items

What is drawn on the screen to represent the bit at time t depends on both the value of the bit and the value of the bit that precedes it. The first argument to bit distinguishes the four possible cases. The second argument is the bit's position, t.

  • {0, 0}$\ \ \ \ \ $zero-bit preceded by a zero-bit
  • {1, 0}$\ \ \ \ \ $zero-bit preceded by a one-bit
  • {1, 1}$\ \ \ \ \ $one-bit preceded by a one-bit
  • {0, 1}$\ \ \ \ \ $one-bit preceded by a zero-bit

Note that bit draws zeros at $y=0$ and ones at $y=1$. In a chart with multiple bit streams, the bits in any given stream will be translated vertically to their proper height in the chart.

bit[{0, 0}, t_Integer] := {Line[{{t, 0}, {t + 1, 0}}]}
bit[{1, 0}, t_Integer] := {Line[{{t, 1}, {t, 0}}], Line[{{t, 0}, {t + 1, 0}}]}
bit[{1, 1}, t_Integer] := {Line[{{t, 1}, {t + 1, 1}}]}
bit[{0, 1}, t_Integer] := {Line[{{t, 0}, {t, 1}}], Line[{{t, 1}, {t + 1, 1}}]}

Given a list where every element is either zero or one, signalGraphicsItem returns a list of Line expressions which will draw the bit stream represented by the input list.

signalGraphicsItem[bits_List] :=
  Module[{pairs},
    pairs = Partition[Join[{bits[[1]]}, bits], 2, 1];
    MapIndexed[bit[#1, #2[[1]] - 1] &, pairs]]

Given an integer, tMax, clockGraphicsItem returns a list of graphics primitives that draws the bit train of a reference over the interval 0 to tMax time units. The bit train is labeled CLOCK to its left.

clockGraphicsItem[tMax_Integer /; tMax > 0] :=
  {Text[Style["CLOCK", 14], {-.25, .5}, {1, 0}],  signalGraphicsItem[clockDigits[tMax]]}

Given a string representing a valid binary signal, bitsGraphicsItem returns a list of graphics primitives that draws the bit train of the signal. Otherwise, bitsGraphicsItem issues an error message and returns an empty list.

bitsGraphicsItem::badbits = "`1` does not represent a valid binary signal";
With[{textHt = 14},
  bitsGraphicsItem[bitstr_String] /; StringFreeQ[bitstr, Except["0" | "1"]] :=
    {Text[Style[bitstr, textHt], {-.25, .5}, {1, 0}], 
       signalGraphicsItem[bitStringToDigits[bitstr]]}]
bitsGraphicsItem[args___] := (Message[bitsGraphicsItem::badbits, Defer[args]]; {})

Put vertical dashed lines in the background, marking off the time steps.

With[{color = Red},
  backgroundLines[tMax_Integer, nSignals_Integer, spacer_ /; spacer >= 0] :=
    {color, Dashed, 
      Table[Line[{{i, 0}, {i, (1 + spacer) nSignals - spacer}}], {i, 0, tMax}]}]

Label the background lines with time indicators, top and bottom

timeLabel[tMax_Integer] := Table[Text[i, {i, 0}], {i, 0, tMax}]

Binary signal chart

This is the main function. Given a list of strings representing valid binary signals, binarySignalChart draws charts like

chart

binarySignalChart takes two options.

  • clock$\ \ \ \ \ \ \ \ \ \ \ \ $default is True; if False, suppresses drawing of the clock bit train
  • ImageSize$\ \ \ \ \ $passed on to Graphics for controlling chart size

Protect[clock];
Options[binarySignalChart] = {clock -> True, ImageSize -> Medium};
With[{spacer = .5},
  binarySignalChart[signals : {_String ..}, OptionsPattern[]] :=
    Module[{n, items, tMax, displacements},
      items = bitsGraphicsItem /@ signals;
      tMax = Max[StringLength /@ signals];
      If[OptionValue[clock],
        PrependTo[items, clockGraphicsItem[tMax]]];
      n = Length@items;
      displacements = {0, 1 + spacer} # & /@ Range[0, n - 1];
      Graphics[{
        backgroundLines[tMax, n, spacer],
        Thread[Translate[items, displacements]], 
        Translate[timeLabel[tMax], {0, -.25}],
        Translate[timeLabel[tMax], displacements[[-1]] + {0, 1.2}]},
        ImageSize -> OptionValue[ImageSize]]]]
binarySignalChart[signal_String, opt : OptionsPattern[]] := 
  binarySignalChart[{signal}, opt]

Examples

This is a minimal chart. The clock display is suppressed and because there is only one signal to display, there is no need to wrap it in a list.

With[{signal = "10110101011001"}, 
  binarySignalChart[signal, clock -> False, ImageSize -> 500]]

min-chart

Any number of signals can be given. They do not have to be the same length. The vertical timing lines and the clock train are automatically extended to cover the longest signal train.

With[{signals = {"11100110", "0101100101100", "11011011"}}, 
  binarySignalChart[signals, ImageSize -> Large]]

max-chart

An invalid input string is rejected, but the valid ones are drawn. An error message is printed.

With[{signals = {"11100110", "invalid", "11011011"}}, binarySignalChart[signals]]

bitsGraphicsItem::badbits: invalid does not represent a valid binary signal

invalid

share|improve this answer
    
Alternatives are always good in my opinion. Even as a terse code fanatic I'd like to see it. –  Mr.Wizard Jul 17 at 12:02
    
Believe me, it's about as far from your style as one can get in Mathematica ;D –  m_goldberg Jul 17 at 17:43
    
@m_goldberg I would like take a step further to ask, how to implement a program that can show a continuously flowing in inputs of random "0 and 1" strings. Just like the screen shown on an oscilloscope. –  Putterboy Jul 17 at 21:31
    
That would be a nice feature, and it's certainly doable. Would have to give it some thought, though. Biggest challenge is keeping the left-hand labels static while the bits scroll by. I can see the use of what you ask for displaying long but given signals. However, don't see the worth of displaying random, dynamically generated bit streams. Could you explain? –  m_goldberg Jul 17 at 21:55
    
@m_goldberg There are much to learn in MM, the most important is to grasp the algorithms from the solutions made by the gurus, I believe. Random signals can mean anything, so it can be replaced by anything meaningful if necessary. –  Putterboy Jul 17 at 22:17

I'm not going to do the red lines and the numbers but here's a way to generate the coordinates of the horizontal and vertical lines that make up the graphs and to draw these:

drawGraph[str_, width_, height_] := 
 Module[{values, vertical, horizontal},
  values = Join[{First@#}, #, {Last@#}] &[ToExpression /@ Characters@str];
  vertical = MapIndexed[
    Transpose[{width {First@#2, First@#2}, height #}] &,
    Partition[values, 2, 1]
    ];
  horizontal = Partition[Rest@Flatten[vertical, 1], 2];
  Graphics[{
    Line[vertical],
    Line[horizontal]
    }, ImageSize -> 500]
  ]

Example:

Grid@Transpose[{
   {"Clock", "11011011", "11100110"},
   drawGraph[#, 1, 1] & /@ {"10101010", "11011011", "11100110"}
   }]

example

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.