import numpy as np
+import math
+import time
 import cv2 as cv
+import matplotlib.pyplot as plt
+
+"""
+Notes:
+       The data is receved in int16 format (limits -32768 to 32767). It is converted to a complex array from which we can work out power. This is from 0 to over 10,000. It is converted to decibels [1].
+       This decibels can be bounded to some upper and lower limit of both volume and noise floor. This also makes our calculations more predictable.
+
+[1] We convert to decibels via the function 20*log10(power) followed by some scaling to the required limits. We covert BACK to power via a lookup table. This is not efficient for the first conversion as the lookup table would have to be huge and have incies in the floating points.
+"""
 
 class fft():
        def __init__(
                window_size: int,
                hop_size: int
        ):
+               # calculate the window and hop size, use to calulate the cosine window
                self.window_size = window_size
                self.hop_size = hop_size
                self.window = np.hanning(window_size)
 
-               self.lower_limit = -40
-               self.upper_limit = 100
 
+               # set the max and min numerical values for amplitude and angle to allow for easier combinations of them both
                self.amplitude_max = 254
                self.amplitude_min = 0
                self.angle_max = 254
                self.angle_min = 0
+               # set the upper and lower limits (in dB) that are to be displayed on the screen
+               self.volume_max = 100
+               self.volume_min = -40
 
+               # calulate the range of each amplitude and angle
                self.amplitude_relative = self.amplitude_max - self.amplitude_min
                self.angle_relative = self.angle_max - self.angle_min
+               self.volume_relative = self.volume_max - self.volume_min
+
+               # generate lookup table for the converstion from decibels to power
+               a = self.volume_min
+               b = self.volume_relative / self.amplitude_relative
 
+               # this is the parameterized inverted function of y = (20 * log10(x) - 40) * (255/140)
+               log_lookup = [10 ** (((x * b) + a) / 20) for x in range(0, 256)]
+               self.log_lookup = np.array(log_lookup)
 
        def stft(
                self,
                data: np.ndarray
        ) -> np.ndarray:
 
+               # apply window and perform the fft
                segment = data * self.window
                spectrum = np.fft.fft(segment) / self.window_size
 
+               # convert the vector length to decimals and confine
                amplitude = np.abs(spectrum)
+
                amplitude = 20*np.log10(amplitude)
-               amplitude = np.clip(amplitude, self.lower_limit, self.upper_limit)
-               amplitude -= self.lower_limit
-               amplitude *= (self.amplitude_relative / self.upper_limit) + self.amplitude_min
-               amplitude = np.clip(amplitude, self.amplitude_min, self.amplitude_max)
+               amplitude = np.clip(amplitude, self.volume_min, self.volume_max)
+
+               # confine the amplitude within the limits specified
+               a = self.volume_min
+               b = self.amplitude_relative / self.volume_relative
+               c = self.amplitude_min
+               amplitude = ((amplitude - a) * b) + c
 
+               # convert x and y to the angle and confine
                angle = np.angle(spectrum)
+
+               # confine the angle within the limits specified
                angle = ((angle + np.pi) * (self.angle_relative / (2 * np.pi))) + self.angle_min
                angle = np.clip(angle, self.angle_min, self.angle_max)
 
+               # rearrange to image format
                full = np.full(angle.shape, fill_value=60)
-
                image = np.stack((full, angle, amplitude), axis=-1)
                image = np.array([image], dtype=np.uint8)
-
                image = cv.cvtColor(image, cv.COLOR_HSV2BGR)
 
                return image
                image: np.ndarray
        ) -> np.ndarray:
 
+               # split the image into constituant parts
                image = cv.cvtColor(image, cv.COLOR_BGR2HSV)
-
-               amplitude = image[0][...,2].astype(np.float64)
+               amplitude = image[0][...,2].astype(np.uint8)
                angle = image[0][...,1].astype(np.float64)
 
-               amplitude -= self.amplitude_min
-               amplitude /= (self.amplitude_relative / self.upper_limit)
-               amplitude += self.lower_limit
-               amplitude = np.power(10, amplitude / 20)
+               # convert amplitude back into vector length
+               amplitude = self.log_lookup[amplitude]
 
+               # convert angle back into x and y
                angle = ((angle - self.angle_min) / (self.angle_relative / (2 * np.pi))) - np.pi
 
+               # rearrange back into fft result
                real = np.cos(angle) * amplitude
                imag = np.sin(angle) * amplitude
                segment = real + (1j * imag)
 
-               data = np.fft.ifft(segment * self.window_size).real
+               data = np.fft.ifft(segment * self.window_size).real.astype(np.int16)
 
                return data
 
-