Plotting A Sine Curve Inside A Bash Shell...

Hi all...

After mentioning the generation of a sinewave sweep generator in a previous thread in this forum this is the method I decided upon.

It plots a sinewave inside an 80 x 24 terminal window.
Although the original used bc (and the line is in the code but commented out) it is now changed to use awk as CygWin has NOT got "bc" in a default install.

It is __relatively__ simple now to create a swept frequency generator from 4 KHz to around 50 Hz and back using the default '/dev/dsp' mode for CygWin and/or 'aplay' or 'afplay' using a .WAV variant...

Enjoy finding simple solutions to often very difficult problems...

#!/bin/bash
# plotsine.sh
# A DEMO to display a sinewave inside a standard bash terminal.
# Issued as Public Domain, 2014, B.Walker, G0LCU.
# Device: Macbook Pro 13", OSX 10.7.5, default bash terminal.
# Use variables so that you can see how it works.
angle=0
step_angle=5
vert_plot=0
horiz_plot=5
centreline=12
amplitude=11
PI=3.14159
clear
# Do a single cycle, quantised graph.
while [ $angle -le 359 ]
do
	# Create each floating point value...
	# CygWin does not have the 'bc' command but it is now catered for... ;o)
	vert_plot=$(awk "BEGIN{ printf \"%.12f\", ((sin($angle*($PI/180))*$amplitude)+$centreline)}")
	# vert_plot=$(bc -l <<< "{print ((s($angle*($PI/180))*$amplitude)+$centreline)}")
	# Truncate the floating point value to an integer then invert the plot to suit the x y co-ordinates inside a terminal...
	vert_plot=$((24-${vert_plot/.*}))
	# Plot the point(s) and print the angle at that point...
	printf "\x1B["$vert_plot";"$horiz_plot"f*"
	printf "\x1B[22;1fAngle is $angle degrees... "
	sleep 0.1
	# Increment values...
	angle=$((angle+step_angle))
	horiz_plot=$((horiz_plot+1))
done
printf "\x1B[23;1fSinewave plotted as a quantised text mode graph.\n"
exit 0
# The results printed inside an 80 x 24 terminal window...
#
#                  *********
#               ***         ***
#              *               *
#            **                 **
#           *                     *
#          *                       *
#         *                         *
#        *                           *
#       *                             *
#      *                               *
#    **                                 **
#                                         *                                 *
#                                          *                               *
#                                           *                             *
#                                            *                           *
#                                             *                         *
#                                              *                       *
#                                               *                     *
#                                                **                 **
#                                                  *               *
#Angle is 355 degrees...                            ***         ***
#Sinewave plotted as a quantised text mode graph.      *********

Interesting. You could roll it all up in awk, which I think is preferable, especially in Windows, which reacts badly to launching thousands of tiny short-lived processes:

$ awk '
 function qsin(X) {
         return(int((G/2)+(sin(X*(3.14159/180))*(G/2))))
 }

 BEGIN {
         if(!Q) Q=5; # 360/5 width
         if(!G) G=20; # 20 rows height

         for(M=0; M<G; M++) for(N=0; N<360; N+=Q) A[N,M]=" ";
         for(N=0; N<360; N+=Q) A[N,qsin(N)]="*";
         for(M=0; M<G; printf("\n", M++) ) for(N=0; N<360; N+=Q)
                 printf("%s", A[N,M]);
 }' /dev/null

                                                 ***********
                                               **           **
                                             **               **
                                            *                   *
                                           *                     **
                                         **                        *
                                        *                           *
                                       *                             *
                                      *                               *
                                     *                                 *
**                                 **
  *                               *
   *                             *
    *                           *
     **                        *
       *                     **
        *                   *
         **               **
           **           **
             ***********

$

awk can also output binary via printf("%c", 65); and the like.

1 Like

Hi Corona688...

Hmm, neat.

I never considered using 'awk' in its entirety.

Although I have used 'amplitude' and 'centreline' values to plot inside the terminal I intend to use character 32 (space) and 126 (tilde) as the pk-pk amplitude(s) and "N" or "O" as the centreline. This then makes the whole waveform fully editable in any text mode editor. ;o)

I never even considered how Windows would react under my version as I was more interested in a calculator as a 'bc' replacement for CygWin.

I seem to remember you pointing out how badly Windows reacts to many fork()s in a thread since gone.

Thanks for your creation. Once I have gotten to grips with it consider it stolen... ;oD

I am lost here.

Where are you getting the three byte binary character(s) from?

Space and tllde are valid shell values as I didn't want to use the full binary range...

I was trying to output direct 8-bit binary in awk. It kept converting my ff's into two-byte sequences.

You can, in GNU awk, with -b, which I was missing before:

$ # F is frequency, R is samplerate, S is number of samples
$ awk -b -v F=4000 -v R=8000 -v S=20 'BEGIN { for(N=0; N<S; N++) printf("%c", int(255*(sin(3.14159/2+2*3.14159*N*(F/R))/2)+128)) }' /dev/null | hexdump -C

00000000  ff 00 ff 00 ff 00 ff 00  ff 00 ff 00 ff 00 ff 00  |................|
00000010  ff 00 ff 00                                       |....|
00000014

$

Ordinary awk should be able to manage 1-127 with %c, but no more.