An ASCII TEXT mode Spec-An in Python 2.0.1 to 3.13.0 without modification

****************************** !!!IMPORTANT!!! ******************************

This code uses some escape facilities of the majority of *NIX Terminals!

(Apologies for any typos, etc...)

  1. This works on standard terminals in the *NIX family of home computers.

  2. On startup the script will attempt to expand the terminal to 40 lines
    by 144 columns.

  3. If it does NOT do this CORRECTLY, or at all, on the first run then make
    the terminal window full screen and re[-]run.

  4. This old HP laptop would only do 39 lines and the display was YUCK!
    The image is this laptop running in FULL screen mode.

  5. The clear screen is all done via terminal escape codes.

  6. The display also uses escape codes for the colours, etc...

  7. The recursive FFT is a shortened version of my upload to AMINET, here:
    Aminet - dev/src/FFT_AMIGA.py.txt

  8. I timed it and it finshed after around 3-4 seconds on the VERY FIRST run
    on this very old HP laptop.
    This included, python startup time, auto-expansion, clearing and drawing
    the display, doing all the calculations for 65536 data points, and
    finally plotting the results <- Plotting was the slowest!

  9. Although UNCALIBRATED in the vertical axis, it does not mean that with
    careful thought it cannot be. I just use it as a DEMO.

  10. AND FINALLY, this is designed to work on any Python Version from 2.0.1
    to the current, as of 17-10-2024, 3.13.0...

                            ENJOY.
    

# SpecAn_100Hz.py
#
# (C)2023-2024, Barry Walker, G0LCU.
# Works on Python 2.5.x to the current, as of 17-10-2024, 3.13.0 without
# modificationon Linux Mint 20.3, 21.3, and Apple OSX 10.15.7.

# Pure text mode UNCALIBRATED log scaled AF Spectrum Analyser DEMO.

# Start a terminal and place as close to the upper left hand corner of the
# screen as possible.
# Called as: python3 [[/full/]path/to/]SpecAn_100Hz.py

# Auto resize the terminal to 40 lines x 144 columns. ONLY for NON-AMIGA
# platforms.
print("\033[8;40;144t")
# Reset the Terminal and clear the screen.
print("\033c\033[2J\033[H")

import sys
import cmath

# This is the recursive FFT that works on Python 2.0.1 minimum for the AMIGA
# without modification...
def fft(DATA):
	N=len(DATA)
	if N<=1: return(DATA)
	EVEN=fft([DATA[K] for K in range(0,N,2)])
	ODD=fft([DATA[K] for K in range(1,N,2)])
	return([EVEN[K]+cmath.exp(-2j*cmath.pi*K/N)*ODD[K] for K in range(int(N/2))]+[EVEN[K]-cmath.exp(-2j*cmath.pi*K/N)*ODD[K] for K in range(int(N/2))])

print("                                [Audio Spectrum Analyser DEMO, (C)2023 B.Walker, G0LCU. Issued as Public Domain.]")
print("           .+---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---+.")
print("       100-+\033[1;31m+---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---+\033[0m+-100")
print("    U      ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   ||")
print("    N      ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   ||")
print("    C   90-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---++-90")
print("    A      ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   ||")
print("    L      ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   ||")
print("    I   80-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---++-80")
print("    B      ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   ||")
print("    R      ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   ||")
print("    A   70-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---++-70")
print("    T      ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   ||")
print("    E      ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   ||")
print("    D   60-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---++-60")
print("           ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   ||")
print("    A      ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   ||")
print("    M   50-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---++-50")
print("    P      ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   ||")
print("    L      ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   ||")
print("    I   40-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---++-40")
print("    T      ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   ||")
print("    U      ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   ||")
print("    D   30-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---++-30")
print("    E      ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   ||")
print("           ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   ||")
print("    ^   20-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---++-20")
print("    L      ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   ||")
print("    O      ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   ||")
print("    G   10-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---++-10")
print("   (Y)     ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   ||")
print("           ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   |  |  | | | ||         |     |   ||")
print("         0-+\033[1;31m+---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---+\033[0m+-0")
print("LOG10(X) > '+---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---+--+--+-+-+-++---------+-----+---+'")
print("            |         |         |         |  |         |         |         |  |         |         |         |  |         |         |")
print("           10        20        40        80 100       200       400       800 1K        2K        4K        8K 10K       20K       40K")
print("                                                                 Frequency in Hz.")

# Create an almost 100Hz square wave at 65536 samples per second.
array_list=[]
for n in range(0,100):
	x=1.0
	for m in range(0,327):
		array_list.append(x)
	x=-1.0
	for m in range(0,327):
		array_list.append(x)
# Add 136 bytes of 0.0 padding, making 65536 samples total.
x=0.0
for m in range(0,136):
	array_list.append(x)

# Create a[n] FFT list without numpy, the AMIGA doesn't have it.
FFT=fft(array_list)

# Create the correct spacing on the X axis.
multiplierX=33.19
# Create a multiplier for the Y axis.
multiplierY=2.83
# Create an integer range for relative amplitude in text mode.
for n in range(0,65536):
	FFT[n]=int(abs(multiplierY*cmath.log(1+FFT[n])))

# Plot amplitude relative values only, totally UNcalibrated.
for n in range(10,32768):
	X_AXIS=int(abs(multiplierX*cmath.log10(n))-20)
	# Allow for a gotcha.
	if n==14: X_AXIS=17
	if n==15: X_AXIS=18
	if FFT[n]>0:
		# Invert the value to display correctly in the terminal......
		PEAK=(34-FFT[n])
		# ......and ensure boundaries are not exceeded.
		if PEAK<4: PEAK=4
		if PEAK>34: PEAK=34
		# And finally plot it.
		for Y_AXIS in range(PEAK,35):
			print("\033[1;34m\033["+str(Y_AXIS)+";"+str(X_AXIS)+"f*\033[0m\033[39;0f")

sys.exit(0)

1 Like