From 59cb5ccc7ee0191194aa2d2c6e2fbc0cbf31f7a7 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 19 Sep 2023 14:49:43 +0100 Subject: [PATCH 01/15] adding python to generate rst charts --- doc/python/README.txt | 41 ++++ doc/python/asrc_utils.py | 516 +++++++++++++++++++++++++++++++++++++++ doc/python/condaEnv.yml | 111 +++++++++ doc/python/doc_asrc.py | 141 +++++++++++ 4 files changed, 809 insertions(+) create mode 100644 doc/python/README.txt create mode 100644 doc/python/asrc_utils.py create mode 100644 doc/python/condaEnv.yml create mode 100644 doc/python/doc_asrc.py diff --git a/doc/python/README.txt b/doc/python/README.txt new file mode 100644 index 00000000..f32ff2fc --- /dev/null +++ b/doc/python/README.txt @@ -0,0 +1,41 @@ +INTRODUCTION +============ + +This folder includes a python script which runs the C emulators for the ASRC, SSRC, OS3 and DS3 modules. +For each model we present test signals - both a single tone to test the THD performance and a range of tones to +observe roll-off at the top of the band. +Using an un-windowed FFT we calculate the SNR. +We scrape the logs to extract MIPS/Utilization where supported. + +Tests are run for all suported input and output sample freqencies, and for worst case frequency deviation. +Note that not all modules support all input scenarios - these are ignored. + +The result of this script is the generation an rst file which is inclorporated into the documentation, +incorporating a full set of charts and summary table. + + + +HOW TO +====== + +This has been developed and tested under WSL2 linux for Windows. +In a WSL2 command window (e.g., Ubuntu20.04): + + +To create and setup the conda environment to run the asrc_test.py script + + conda env create -f condaEnv.yml + conda activate .conda-env + + + +To run the script: + + python3 doc_asrc.py + + +By default, this will create a "_build" folder, containing sub-folders: + ./input all generated test files + ./expected simulation output from the models + ./output plots and a csv format log file + ./rst a single rst file which incorporates all the plots and log file as a table \ No newline at end of file diff --git a/doc/python/asrc_utils.py b/doc/python/asrc_utils.py new file mode 100644 index 00000000..0960bdaf --- /dev/null +++ b/doc/python/asrc_utils.py @@ -0,0 +1,516 @@ +from subprocess import Popen, PIPE +from subprocess import run as srun +import os +import shutil +import matplotlib.pyplot as plt +from scipy.fft import fft, ifft +import scipy.signal as signal +import numpy as np +from scipy.io import wavfile +import re +from math import gcd +from mpmath import mp +from scipy.io.wavfile import write as writewav + + +class asrc_util: + def __init__(self, path, relative, fftPoints, ch0_bins, ch1_bins): + self.path = path + self.relative = relative + + # locations for results - note they are purged every time this is run + self.cleanFolder("_build") # create temp location for results + self.outputFolder = self.cleanFolder("_build/output") # put all results of simulation and plots here + self.inputFolder = self.cleanFolder("_build/input") # put any input test files here + self.expectedFolder = self.cleanFolder("_build/expected") # put C ref model o/p here + self.rstFolder = self.cleanFolder("_build/rst") # put RST o/p here + + self.logFile = "/log.csv" # tabulated results + + # the C-models and XSIM models which get run + self.asrc_model ="{}/../tmp_models/asrc_demo".format(self.path) # path and file of the reference c model executable + self.ssrc_model ="{}/../tmp_models/ssrc_demo".format(self.path) # path and file of the reference c model executable + self.ds3_model ="{}/../tmp_models/ds3_demo".format(self.path) # path and file of the reference c model executable + self.os3_model ="{}/../tmp_models/os3_demo".format(self.path) # path and file of the reference c model executable + self.xePath = "{}/XSIM/ASRC/asrc_test.xe".format(self.path) # path and file of the xe xsim executable + + # definitions of sample ratea + self.allRates =["16", "32", "44", "48", "88", "96", "176", "192"] + self.srcRates =["44", "48", "88", "96", "176", "192"] # supported by the aasrc and ssrc + self.factors = {"44":0, "48":1, "88":2, "96":3, "176":4, "192":5} # look up to convert rate to the filter bank id needed by the ASRC + self.sampleRates = {"16":16000, "32":32000, "44":44100, "48":48000, "88":88200, "96":96000, "176":176400, "192":192000} #convinience to save typing out sample rates in full + self.sigMax = {"16":7300, "32":14600, "44":20200, "48":21800, "88":40000, "96":47990, "176":80000, "192":85000} # upper limit on input freq for given sample rate + + self.numSamples = {} #populated later based on oprate to ensure sufficient samples for fft + self.ignoreSamples = 2000 #worst case for high up-sampling + self.fftPoints = fftPoints + + self.sig = [ch0_bins, ch1_bins] # defines the test signals for each of 2 channels, each is a list so more than one tine can be genertaed and combined + self.log=[] + self.plots=[] + mp.prec = 1024 + self.fudge = 0.5**128 + self.plot_data = [] + self.plot_label = [] + self.plot_text = [] + self.skip_xsim = True # FIX THIS LATER + self.rstFile = "" + + + # update the input frequencies + # allows input of frequencies as a list for each channel, or use the autoFill which + # populates channel 1 with mutiple tones, spaced logrithmically, most dense at higher freq, and ch0 is always 10% in + # set in 'sigMax' beyond which reflections degrade the SNR. + def updateSig(self, ipRate, ch0_bins, ch1_bins, autoFill=False): + # this manipultaes the fft size to attempt to force any reflections around the ip sample rate nyquist also ending up + # in an integer fft bin + self.fftPoints = int(10000 *(self.sampleRates[ipRate] * self.fDev) / self.sampleRates[self.opRate]) + print("Over-wrote the fft sive to {} points".format(self.fftPoints)) + + #for channel 0 + ch0_ratio = 0.05 # 5% of the way into the fft bins, so 10% on the plot + ch0bin = int(self.fftPoints * ch0_ratio) + print("For channel 0: Creating test tone at FFT bin: {}".format(ch0bin)) + # for channel 1 + sigMax = self.sigMax # upper limit on ip frequency to be safely inside the filter cutoff + maxBin = int((min(sigMax[ipRate], sigMax[self.opRate]) / (self.sampleRates[self.opRate])) * self.fftPoints) + i=1 + r=2 # spread factor + ch1_range = [maxBin] + while maxBin-r**i > 0: # this bit spreads the bins out exponentially, concentrated at the top + ch1_range.append(int(maxBin-r**i)) + i+=1 + print("For channel 1: Creating data set for ipRate {} and opRate {} which resulted in\n{}\n".format(ipRate, self.opRate, ch1_range )) + if autoFill: + self.sig = [[ch0bin], ch1_range] + else: + self.sig = [ch0_bins, ch1_bins] + + + + # handy util to add data into an array + def pushPlotInfo(self, a,b,c): + self.plot_data.append(a) + self.plot_label.append(b) + self.plot_text.append(c) + + def resetPlotInfo(self): + self.plot_data = [] + self.plot_label = [] + self.plot_text = [] + + def makePlotTitle(self, simLog, channel): + firstKey = list(simLog[channel].keys())[0] + title = simLog[channel][firstKey][0].replace(self.expectedFolder, '')[1:] + title = title.split('_')[:-1] + for k in simLog[channel]: title.append(k) + title = "_".join(title) + return title + + + def logResults(self, source, ipRate, opRate, fDev, ch, SNR, THD, totalmips, ch0mips=None, ch1mips=None, txt=None, snr2=None, mips2=None): + # simple function to append information to a log, which is kept as an array of dictionaries + realRate = self.sampleRates[opRate] / self.fDev + sigList = [] + for s in self.sig[ch]: + sigList.append("{:.3f}".format(float(mp.fmul(realRate ,mp.fdiv(s, self.fftPoints))))) + signals = ";".join(sigList) + #info = {"source":source, "ipRate(Hz)":self.sampleRates[ipRate], "opRate(Hz)":self.sampleRates[opRate], "fDev":fDev, "ch":ch, "signals(Hz)":signals, "SNR(dB)":SNR, "THD(dB)":THD,"Total MIPS":totalmips, "MIPS(ch0)":ch0mips, "MIPS(ch1)":ch1mips, "Text":txt} + info = {"source":source, "ipRate(Hz)":str(self.sampleRates[ipRate]), "opRate(Hz)":str(self.sampleRates[opRate]), "fDev":str(fDev), "ch":str(ch), "signals(Hz)":signals, "SNR(dB)":"{:.1f}".format(SNR), "THD(dB)":THD,"Total MIPS":"{:.0f}".format(totalmips), "MIPS(ch0)":"{:.0f}".format(ch0mips), "MIPS(ch1)":"{:.0f}".format(ch1mips)} + self.log.append(info) + + + def log2csv(self): + # export the log array as a csv file, using the keys as headings + logfile = open(self.outputFolder + self.logFile, "w") + logfile.write(",".join(self.log[0].keys()) + "\n") # write headings + for i in self.log: + logfile.write((','.join(i.values())).replace("\n", ";") + "\n") # write data + logfile.close() + + + def setOpRate(self, opRate, fDev): + self.opRate = opRate + self.fDev = fDev + + def cleanFolder(self, folder): + # utility to delete and recreate folders used for input and output files + p = "{}/{}".format(self.path, folder) + if os.path.isdir(p): + shutil.rmtree(p) + os.mkdir(p) + if self.relative: + p = p.replace(self.path, ".") + return p + + def listFactors(self, x): + factors=[] + for i in range(1,x+1): + if x%i==0: + factors.append(i) + return factors + + def makeSignal(self, fsamp, fsig, asig, l, ferr=1.0): + # populates ipdata with sinewave + # fsamp: sample rate, as abbevaited string (e.g., "48", interpreted as 48,000Hz) + # fsig: signal freq, in Hz + # asig: amplitude, in range 0-1 + # l: number of samples + fsamphz = mp.fmul(mp.fmul(float(self.sampleRates[fsamp]), self.fDev), ferr) + f = mp.fdiv(fsig, fsamphz) + print("Make signal: {:.2f}".format(float(fsig))) + a = asig * 2**31 + x = np.linspace(0, float(2*mp.pi*f*(l-1)), l) + ipdata = a * np.sin(x) + return ipdata + + + def saveInputFile(self, signals, fileName): + # sums the signals into one signal and saves as a file + # signals: an array of arrays [data0[], data[1], ...] + # return the file with path + data = np.sum(signals, axis=0).astype('int32') + file = self.inputFolder + "/" + fileName + np.savetxt(file, data, fmt='%d', delimiter='\n') + #writewav("/mnt/c/Users/andrewdewhurst/OneDrive - Xmos/Temp/rawdata/test.wav", 44100, (data[0]/2**16).astype(np.int16)) + return file, data + + def makeInputFiles(self, test_rates, ferr=1.0): + # create two channels of input + # define some test signal frequencies w.r.t. op rate + realRate = self.sampleRates[self.opRate] # mp.fdiv(self.sampleRates[self.opRate], self.fDev) + # + ipFiles=np.empty((0,3), str) + for ipRate in test_rates: + self.numSamples[ipRate] = int( ((self.sampleRates[ipRate]/self.sampleRates[self.opRate]) * ((self.fftPoints*1.5) + self.ignoreSamples ))) + chan=[] + chanSigData=[] + i=0 + for ch in self.sig: #itterates over channels + sig=[] + for s in ch: + f = mp.fmul(realRate ,mp.fdiv(s, self.fftPoints)) + data = self.makeSignal(ipRate, f, 0.98/len(ch), self.numSamples[ipRate], ferr) + sig.append([data]) + #freqs = "-".join(str(round(realRate * x / self.fftPoints)) for x in ch)[0:32] #crop filename + if len(ch)==1: + freqs = str(round(realRate * ch[0] / self.fftPoints)) + else: + freqs = str(round(realRate * ch[0] / self.fftPoints)) + "_to_" + str(round(realRate * ch[-1] / self.fftPoints)) + if ferr==1: + filename = "ch{}_{}_fsi{}.dat".format(str(i), freqs, ipRate) + else: + filename = "ch{}_{}_fsi{}_err{}.dat".format(str(i), freqs, ipRate, ferr) + print("Making {} with F={:.6f} using {} points".format(filename, float(f), self.numSamples[ipRate])) + sigFile, sigData = self.saveInputFile(sig, filename) + chan.append(sigFile) + chanSigData.append(sigData) + i+=1 + ipFiles = np.append(ipFiles, np.array([[chan[0], chan[1], ipRate]]), axis=0) + return ipFiles, chanSigData + + + def doFFT(self, data, window=False): + # convinient way to select between fft styles. Note that the periodic one will need a lot more samples, so + # use window=True for debuging. + if window: + return self.winFFT(data) + else: + return self.rawFFT(data) + + + def mpAbs(self, data): + result = np.zeros(len(data)) + for i in range(0, len(data)): + result[i] = mp.fabs(data[i]) + return result + + + def rawFFT(self, data): + # fft assuming signal is already an integer fraction of the true output rate + min = np.argmin(np.abs(data[self.ignoreSamples:-(self.fftPoints)])) + self.ignoreSamples #investigating if picking a data set that looks like it is nearly zero crossing at the ends is better + samples = data[min:min+self.fftPoints] + l=self.fftPoints + fftData = self.mpAbs(np.fft.fft(samples)) + fftDataDB = 20 * np.log10((fftData/np.max(fftData)) + self.fudge) + realRate = self.sampleRates[self.opRate] * float(self.fDev) + x = np.linspace(0,realRate/2, num=int(l/2) ) / 1000 + return [x, fftDataDB[0:int(l/2)], fftData[0:int(l/2)]/np.max(fftData[0:int(l/2)])] + + + def plotFFT(self, xydata, combine=False, title=None, subtitles=None, log=True, text=None): + # Plot style setup for the FFT plots, labels x asis as KHz and y axis as dB. + # xydata: an array of datasets, each dataset is a 3 element array containing the xdata array and ydata_dB array and ydata_lin array + # combine: (optional) forces all plots ontot the same chart, otherwise it will create a grid of plots + # title: (optional) the chart title at the top, common to any subplots + # subtitles: (optional) an array of subtitles, used for each subplot. + # log: plots the dB data in the input + grid = {1:[1,1], 2:[1,2], 3:[1,3], 4:[2,2], 5:[2,3], 6:[2,3]} + clut = ['lawngreen','deepskyblue','orange','dimgray'] + slut = [(0,(1,1)), (0,(5,1))] if combine else ['solid'] + n = len(xydata) + if n in grid and not(combine): # preffered layout of multiple subplots + [prows, pcols] = grid[n] + else: + prows = int(1 if combine else int(n/3) + 1) + pcols = int(1 if combine else np.ceil(n/prows)) + fig = plt.figure(figsize=(6*pcols, 6*prows)) + i = 0 + for xy in xydata: + if not(combine) or i<1: + ax = fig.add_subplot(prows, pcols, i+1) + ax.plot(xy[0], xy[1] if log else xy[2], linestyle=slut[i%len(slut)], color=clut[i%len(clut)], linewidth=1, label=subtitles[i]) + ax.set_xlabel("Frequency (KHz)") + ax.set_ylabel("dB") + ax.set_ylim(-200,0) + ax.grid() + if text != None: + tl = np.sum([text[x].count("\n") + 1 for x in range(i)]) if combine else 0 # this stacks up the text comments in the case of combined plots + ax.text(0.95, 0.95-(0.025*tl), text[i], transform=ax.transAxes, fontsize=6, fontweight='normal', va='top', horizontalalignment='right') + if subtitles != None and not(combine) and i<=n: + ax.set_title(subtitles[i], fontsize=8, x=0, y=1.0, ha='left') #subtitle above subplots when not combined + i += 1 + fig.suptitle(title, fontsize=12, x=0.5, y=0.97, ha='center') # title top centre + if title != None: + filename = title + "-combined" if combine else title + else: + "-".join(subtitles) + "-combined" if combine else "-".join(subtitles) + if combine: + fig.legend(loc='lower left') + plotFile = "{}/{}.png".format(self.outputFolder, filename) + plt.savefig(plotFile, dpi=100) + plt.close() + return "{}.png".format(filename) #local filename only as rst file that uses it already in same folder + + + def opFileName(self, fin, label, fDev, opRate): + #a simple utility to convert an input file pat+name to an output version, appending the frequency deviation + newName = fin.replace(self.inputFolder, self.outputFolder).replace(".dat", "_fso{}_fdev{:f}_{}.dat".format(opRate, fDev, label)) + return newName + + def exFileName(self, fin, label, fDev, opRate): + #a simple utility to convert an input file pat+name to an output version, appending the frequency deviation + newName = fin.replace(self.inputFolder, self.expectedFolder).replace(".dat", "_fso{}_fdev{:f}_{}.dat".format(opRate, fDev, label)) + return newName + + + def run_c_model(self, ipFiles, opRate, blocksize, fDev): + # Runs the various simulators across the input files, for the provided opRate and frequency deviation + # ipFiles: an array of arrays, each sub array in format [Ch0 file name, Ch1 filename, ipRate] + # generates the output filename based on input and returns another array of files in the same format as the input files. + # also returns the "simLog" which is a dictionary which links every input file to all available output results for that input, + # since we can't run all sims on all data (e.g. DS3 needs a factor of 3) + dither=0 + opFiles=np.empty((0,6), str) + simLog = {} + for ipFile in ipFiles: + simLog[ipFile[0]]={} + simLog[ipFile[1]]={} + + # ASRC C Model + # ------------ + if ipFile[2] in self.srcRates and self.opRate in self.srcRates: + ch0 = self.exFileName(ipFile[0], 'c-asrc', fDev, opRate) + ch1 = self.exFileName(ipFile[1], 'c-asrc', fDev, opRate) + + cmd = "{} -i{} -j{} -k{} -o{} -p{} -q{} -d{} -l{} -n{} -e{}".format( + self.asrc_model, ipFile[0], ipFile[1] , self.factors[ipFile[2]], ch0, ch1, self.factors[opRate], dither, self.numSamples[ipFile[2]], blocksize, fDev) + p = Popen([cmd], stdin=PIPE, stdout=PIPE, shell=True) + output, error = p.communicate(input=b'\n') + opFiles = np.append(opFiles, np.array([[ch0, ch1, opRate, str(fDev), output, "c-asrc"]]), axis=0) + simLog[ipFile[0]]['asrc']=[str(ch0), 0, opRate, str(fDev), output, "c-asrc"] + simLog[ipFile[1]]['asrc']=[str(ch1), 1, opRate, str(fDev), output, "c-asrc"] + + # SSRC C Model + # ------------ + if ipFile[2] in self.srcRates and self.opRate in self.srcRates and fDev==1: + ch0 = self.exFileName(ipFile[0], 'c-ssrc', fDev, opRate) + ch1 = self.exFileName(ipFile[1], 'c-ssrc', fDev, opRate) + + cmd = "{} -i{} -j{} -k{} -o{} -p{} -q{} -d{} -l{} -n{}".format( + self.ssrc_model, ipFile[0], ipFile[1] , self.factors[ipFile[2]], ch0, ch1, self.factors[opRate], dither, self.numSamples[ipFile[2]], blocksize) + p = Popen([cmd], stdin=PIPE, stdout=PIPE, shell=True) + output, error = p.communicate(input=b'\n') + opFiles = np.append(opFiles, np.array([[ch0, ch1, opRate, "1.0", output, "c-ssrc"]]), axis=0) + simLog[ipFile[0]]['ssrc']=[str(ch0), 0, opRate, str(fDev), output, "c-ssrc"] + simLog[ipFile[1]]['ssrc']=[str(ch1), 1, opRate, str(fDev), output, "c-ssrc"] + + # DS3 C Model + # ------------ + if self.sampleRates[opRate] * 3 == self.sampleRates[ipFile[2]] and fDev==1.0: + opf=["na", "na"] + for c in [0,1]: #do both channels seperately + if os.path.isfile("./input.dat"): # this only proceses a single channel and assumes an input file called "input.dat" and generates "output.dat" + os.remove("./input.dat") + os.link(ipFile[c], "./input.dat") + opf[c] = self.exFileName(ipFile[c], 'c-ds3', fDev, opRate) + cmd = '{}'.format(self.ds3_model) + p = Popen([cmd], stdin=PIPE, stdout=PIPE, shell=True) + output, error = p.communicate(input=b'\n') + shutil.copy("output.dat", opf[c]) + simLog[ipFile[c]]['ds3']=[str(opf[c]), c, opRate, str(fDev), output, "c-ds3"] + opFiles = np.append(opFiles, np.array([[opf[0], opf[1], opRate, "1.0", output, "c-ds3"]]), axis=0) + os.remove("./input.dat") + os.remove("./output.dat") + + # OS3 C Model + # ------------ + if self.sampleRates[opRate] == self.sampleRates[ipFile[2]] * 3 and fDev==1.0: + opf=["na", "na"] + for c in [0,1]: #do both channels seperately + if os.path.isfile("./input.dat"): # this only proceses a single channel and assumes an input file called "input.dat" and generates "output.dat" + os.remove("./input.dat") + os.link(ipFile[c], "./input.dat") + opf[c] = self.exFileName(ipFile[c], 'c-os3', fDev, opRate) + cmd = '{}'.format(self.os3_model) + p = Popen([cmd], stdin=PIPE, stdout=PIPE, shell=True) + output, error = p.communicate(input=b'\n') + shutil.copy("output.dat", opf[c]) + simLog[ipFile[c]]['os3']=[str(opf[c]), c, opRate, str(fDev), output, "c-os3"] + opFiles = np.append(opFiles, np.array([[opf[0], opf[1], opRate, "1.0", output, "c-os3"]]), axis=0) + os.remove("./input.dat") + os.remove("./output.dat") + + # ASRC XSIM Model + # --------------- + if ipFile[2] in self.srcRates and self.opRate in self.srcRates and not self.skip_xsim: + xsimFiles = self.run_xsim(ipFiles, opRate, fDev, simLog) + opFiles = np.append(opFiles, xsimFiles, axis=0) + + return opFiles, simLog + + + def run_xsim(self, ipFiles, opRate, fDev, simLog): + if os.path.isfile("./asrc_test.lnk"): + os.remove("./asrc_test.lnk") + os.link(self.xePath, "./asrc_test.lnk") + opFiles=np.empty((0,6), str) + for ipFile in ipFiles: + ch0 = self.opFileName(ipFile[0], 'x-asrc', fDev, opRate) + ch1 = self.opFileName(ipFile[1], 'x-asrc', fDev, opRate) + + cmd0 = "cd {}".format(self.path) + cmd1 = "xsim --args asrc_test.lnk -i {} {} -o {} {} -f {} -g {} -n {} -e {}".format( + ipFile[0], ipFile[1], ch0, ch1, self.sampleRates[ipFile[2]], self.sampleRates[opRate], self.numSamples[ipFile[2]], fDev) + cmd = cmd0 + ";pwd;" + cmd1 + p = Popen(cmd, stdin=PIPE, stdout=PIPE, shell=True) + output, error = p.communicate(input=b'\n') + p.wait() + opFiles = np.append(opFiles, np.array([[ch0, ch1, opRate, str(fDev), output, "x-asrc"]]), axis=0) + simLog[ipFile[0]]['xsim-asrc']=[str(ch0), 0, opRate, str(fDev), output, "x-asrc"] + simLog[ipFile[1]]['xsim-asrc']=[str(ch1), 1, opRate, str(fDev), output, "x-asrc"] + return opFiles + + + def scrapeXsimLog(self, data): + # This bit scrapes the output of xsim and takes the average of all reported CPU utilization + log = data['log'] + p = re.compile("Thread util=\d*%") + q = re.compile("\d+") + u=re.findall(p,log) + tu=0 + for i in range(0, len(u)): + tu += int(re.findall(q, u[i])[0]) + tu = tu / len(u) + return tu #thread utilization % + + + def scrapeCLog(self, data): + # This scrapes the CPU data from the C model + ch0mips = 0 + ch1mips = 0 + totalmips = 0 + txtmips = "" + if data['src'] == "c-asrc": + p = re.compile("MIPS.*?\\\\n") + m = re.findall(p, str(data['log'])) + ch0mips = float(m[0].replace("\\n", "").split(":")[1].strip()) + ch1mips = float(m[5].replace("\\n", "").split(":")[1].strip()) + totalmips = ch0mips + ch1mips + txtmips = "\n".join(m) + if data['src'] == "c-ssrc": + p = re.compile("MIPS.*?\n") + m = re.findall(p, str(data['log']).replace("\\n","\n")) + if len(m) > 0: + totalmips = float(m[0].split(":")[1].strip()) + + return {"log":txtmips, "total_mips":totalmips, "ch0_mips":ch0mips, "ch1_mips":ch1mips} + + + def makeLabel(self, item): + #utility to generate a title / filename to use in plots from an item from a file list which is in the foramt [file1, file2, rate as str] + #and should work for ipFiles and opFiles + a = item[0][item[0].rfind("/")+1:] + label = a[0:a.rfind(".")] + return label + + + def loadDataFromOutputFile(self, file): + #loads an array of files in the format returned by running 'run_c_model' and returns an array of elements, each of which is a dictionary of [ 2 channels of samples], [2 channels of labels], o/p rate + ch0 = np.loadtxt(file[0]).astype("int32") + label = self.makeLabel(file) + return {'samples':ch0, 'labels':label, 'chan':file[-5], 'rate':file[-4], 'fdev':file[-3], 'log':file[-2], 'src':file[-1]} + + + def calcSNR(self, fftdata, ch): + signal=0 + tmpNoise=np.copy(fftdata[2]) + #calculates the SNR from fft data + for s in self.sig[ch]: #these are the expected bins in the output + bin = int(s) + signal += fftdata[2][bin] + tmpNoise[bin] = 0 + noise = np.sum(tmpNoise) + snr = 20 * np.log10((signal / noise) + self.fudge) + return snr + + + # calculates the THD, but limited to cases where there is PRECISELY one signal tone (i.e., in one fft bin) + def calcTHD(self, fftdata, ch): + if len(self.sig[ch]) > 1: + thd = "NA" + else: + sigbin = self.sig[ch][0] + signal = np.abs(fftdata[2][sigbin]) ** 2 + noisebins = np.arange(int(2*sigbin), (self.fftPoints/2)-1, sigbin).astype(int) # harmonic bins, which are the noise + noise = 0 + for nb in noisebins: + noise += np.abs(fftdata[2][int(nb)]) ** 2 + thd = 10*np.log10((noise/signal)) + thd="{:.1f}dB".format(thd) #returned as string so we can pass the "NA" for muti-tone cases + return thd + + + def makeRST(self, file, ipRate, opRate, fDev, sims): + fsi = "{:,d}Hz".format(self.sampleRates[ipRate]) + fso = "{:,d}Hz".format(self.sampleRates[opRate]) + ferr = "{:f}".format(fDev) + plots = ", ".join(sims) + self.rstFile = self.rstFile + "\n\n\n" + ".. figure:: {}".format(file) + self.rstFile = self.rstFile + "\n" + " :scale: {}".format("90%") + self.rstFile = self.rstFile + "\n\n" + " Input sample rate: {}, Output sample rate: {}, Sample rate error: {}, Results for: {}".format(fsi, fso, ferr, plots) + + + def addRSTHeader(self, title, level): + underline=["=", "+", "-", "."] + self.rstFile = self.rstFile + "\n\n\n" + title + self.rstFile = self.rstFile + "\n" + underline[level-1] * len(title) + "\n\n" + + def addRSTText(self, text): + self.rstFile = self.rstFile + "\n\n" + text + + + def addLog2RST(self): + # source ipRate(Hz) opRate(Hz) fDev ch signals(Hz) SNR(dB) THD(dB) Total MIPS MIPS(ch0) MIPS(ch1) Text + self.rstFile = self.rstFile + "\n\n\n" + ".. csv-table:: Data table" + self.rstFile = self.rstFile + "\n" + " :file: .{}".format(self.logFile) + self.rstFile = self.rstFile + "\n" + " :widths: 8, 9, 9, 9, 6, 14, 9, 9, 9, 9, 9" + self.rstFile = self.rstFile + "\n" + " :header-rows: 1" + self.rstFile = self.rstFile + "\n" + + + + def saveRST(self, file): + rst = open("{}/{}".format(self.rstFolder, file), "w") + rst.write(self.rstFile) + rst.close() \ No newline at end of file diff --git a/doc/python/condaEnv.yml b/doc/python/condaEnv.yml new file mode 100644 index 00000000..afc5c059 --- /dev/null +++ b/doc/python/condaEnv.yml @@ -0,0 +1,111 @@ +name: .conda-env +channels: + - anaconda + - defaults +dependencies: + - _libgcc_mutex=0.1=main + - _openmp_mutex=5.1=1_gnu + - appdirs=1.4.4=pyhd3eb1b0_0 + - blas=1.0=mkl + - brotli=1.0.9=h5eee18b_7 + - brotli-bin=1.0.9=h5eee18b_7 + - brotlipy=0.7.0=py311h5eee18b_1002 + - bzip2=1.0.8=h7b6447c_0 + - ca-certificates=2023.01.10=h06a4308_0 + - certifi=2020.6.20=pyhd3eb1b0_3 + - cffi=1.15.1=py311h5eee18b_3 + - charset-normalizer=2.0.4=pyhd3eb1b0_0 + - contourpy=1.0.5=py311hdb19cb5_0 + - cryptography=41.0.2=py311h22a60cf_0 + - cycler=0.11.0=pyhd3eb1b0_0 + - dbus=1.13.18=hb2f20db_0 + - expat=2.4.9=h6a678d5_0 + - fontconfig=2.14.1=h52c9d5c_1 + - fonttools=4.25.0=pyhd3eb1b0_0 + - freetype=2.12.1=h4a9f257_0 + - giflib=5.2.1=h5eee18b_3 + - glib=2.69.1=he621ea3_2 + - gst-plugins-base=1.14.1=h6a678d5_1 + - gstreamer=1.14.1=h5eee18b_1 + - icu=58.2=he6710b0_3 + - idna=3.3=pyhd3eb1b0_0 + - intel-openmp=2023.1.0=hdb19cb5_46305 + - jpeg=9e=h5eee18b_1 + - kiwisolver=1.4.4=py311h6a678d5_0 + - krb5=1.20.1=h143b758_1 + - lcms2=2.12=h3be6417_0 + - ld_impl_linux-64=2.38=h1181459_1 + - lerc=3.0=h295c915_0 + - libbrotlicommon=1.0.9=h5eee18b_7 + - libbrotlidec=1.0.9=h5eee18b_7 + - libbrotlienc=1.0.9=h5eee18b_7 + - libclang=10.0.1=default_hb85057a_2 + - libdeflate=1.17=h5eee18b_0 + - libedit=3.1.20221030=h5eee18b_0 + - libevent=2.1.12=hdbd6064_1 + - libffi=3.4.4=h6a678d5_0 + - libgcc-ng=11.2.0=h1234567_1 + - libgfortran-ng=11.2.0=h00389a5_1 + - libgfortran5=11.2.0=h1234567_1 + - libgomp=11.2.0=h1234567_1 + - libllvm10=10.0.1=hbcb73fb_5 + - libpng=1.6.39=h5eee18b_0 + - libpq=12.15=hdbd6064_1 + - libstdcxx-ng=11.2.0=h1234567_1 + - libtiff=4.5.0=h6a678d5_2 + - libuuid=1.41.5=h5eee18b_0 + - libwebp=1.2.4=h11a3e52_1 + - libwebp-base=1.2.4=h5eee18b_1 + - libxcb=1.15=h7f8727e_0 + - libxkbcommon=1.0.1=hfa300c1_0 + - libxml2=2.9.14=h74e7548_0 + - libxslt=1.1.35=h4e12654_0 + - lz4-c=1.9.4=h6a678d5_0 + - matplotlib=3.7.1=py311h06a4308_1 + - matplotlib-base=3.7.1=py311ha02d727_1 + - mkl=2023.1.0=h6d00ec8_46342 + - mkl-service=2.4.0=py311h5eee18b_1 + - mkl_fft=1.3.6=py311ha02d727_1 + - mkl_random=1.2.2=py311ha02d727_1 + - munkres=1.1.4=py_0 + - ncurses=6.4=h6a678d5_0 + - nspr=4.35=h6a678d5_0 + - nss=3.89.1=h6a678d5_0 + - numpy=1.25.0=py311h08b1b3b_0 + - numpy-base=1.25.0=py311hf175353_0 + - openssl=3.0.10=h7f8727e_0 + - packaging=23.0=py311h06a4308_0 + - pcre=8.45=h295c915_0 + - pillow=9.4.0=py311h6a678d5_0 + - pip=23.2.1=py311h06a4308_0 + - ply=3.11=py311h06a4308_0 + - pooch=1.4.0=pyhd3eb1b0_0 + - pycparser=2.21=pyhd3eb1b0_0 + - pyopenssl=23.2.0=py311h06a4308_0 + - pyparsing=3.0.9=py311h06a4308_0 + - pyqt=5.15.7=py311h6a678d5_0 + - pyqt5-sip=12.11.0=py311h6a678d5_0 + - pysocks=1.7.1=py311h06a4308_0 + - python=3.11.4=h955ad1f_0 + - python-dateutil=2.8.2=pyhd3eb1b0_0 + - qt-main=5.15.2=h327a75a_7 + - qt-webengine=5.15.9=hd2b0992_4 + - qtwebkit=5.212=h4eab89a_4 + - readline=8.2=h5eee18b_0 + - requests=2.27.1=pyhd3eb1b0_0 + - scipy=1.10.1=py311h08b1b3b_1 + - setuptools=68.0.0=py311h06a4308_0 + - sip=6.6.2=py311h6a678d5_0 + - six=1.16.0=pyhd3eb1b0_1 + - sqlite=3.41.2=h5eee18b_0 + - tbb=2021.8.0=hdb19cb5_0 + - tk=8.6.12=h1ccaba5_0 + - toml=0.10.2=pyhd3eb1b0_0 + - tornado=6.3.2=py311h5eee18b_0 + - tzdata=2023c=h04d1e81_0 + - urllib3=1.26.8=pyhd3eb1b0_0 + - wheel=0.38.4=py311h06a4308_0 + - xz=5.4.2=h5eee18b_0 + - zlib=1.2.13=h5eee18b_0 + - zstd=1.5.5=hc292b87_0 +prefix: /home/andrew/asrc/lib_src_develop/lib_src/tests/Doc_utils/.conda-env diff --git a/doc/python/doc_asrc.py b/doc/python/doc_asrc.py new file mode 100644 index 00000000..de80b135 --- /dev/null +++ b/doc/python/doc_asrc.py @@ -0,0 +1,141 @@ +from asrc_utils import asrc_util +import numpy as np +import matplotlib.pyplot as plt +from mpmath import mp + +################################################################################################################ +# OVERVIEW +# +# This script generates a set of test files for the supported input and output rates. +# It passes these through the golden reference "C" models and xsim, and +# Plots the FFTs, extracts the SNR, THD, and extracts MIPS estimate. +# All this info is annotated on the Plot which is saved to the otput folder. +# +# Note that both the C model is a dual-channel iplementations, so we pass them a pair of source files +# and since this is ASRC, we vary the frequency deviation parameter fDev. +# But, OS3 and DS3 are single channel apps - which is dealt with! +# +################################################################################################################ + + +# Setup some basics +path = '.' +xsim = False +fftPoints=1024 # note when updating the test tones with "updateSig" this is overwritten +FERR = 1.0 #this is an additional sample rate deviation applied to the inpt signal to test effect of errors + +U = asrc_util(path, True, fftPoints,[200], [500]) # create the util object +U.addRSTHeader("Performance information", 1) # start the RST it generates with a title +U.addRSTHeader("Chart data", 2) # start the RST it generates with a title + +for fDev in [0.9999, 1.0, 1.0001]: # for a set of different frequency deviations + U.addRSTHeader("Frequency error: {:.6f}Hz".format(fDev), 3) #add a title to the RST for the freq deviation + + for opRate in U.allRates: # for each of the possible output sample rates + U.addRSTHeader("Output sample frequency : {:,d}".format(U.sampleRates[opRate]), 4) # add a subtitle to RST for the opRate + + # Choose the o/p rate and freq deviation + U.setOpRate(opRate, fDev) + + no_results = True # the RST contains headings for all fDev, opRate and ipRate - so if this is an unsupported combination, add a warning to thr RST. + for ipRate in U.allRates:# for each of the possible input sample rates + # opportunity to choose different test freq, which also sets a safer fft size + U.updateSig(ipRate, [int(fftPoints/5)], [int(fftPoints/4),int(fftPoints/6)], True) # specified in temrs of FFT o/p bin, when flag set true it auto-fills a logrithmic range for ch1 and 5% in for ch0 + + # Make a set of input files based on range of sample rates supported + ipFiles, sig = U.makeInputFiles([ipRate], FERR) # makes the signals, saves data as files and returns some info about them + + # Put the input data through the golden "C" emulators + opFiles, simLog = U.run_c_model(ipFiles, opRate, 4, fDev) + + # Itterate over all the input data files and channels + for channels in ipFiles: # For each input sata set there will be an output one for each channel + for channel in channels[0:2]: + print(channel) + + if len(simLog[channel]) > 0: #at least one simulation ran for this combination of iprate, oprate and fdev + no_results = False + for sim in simLog[channel]: + opData = U.loadDataFromOutputFile(simLog[channel][sim]) + + # fft + opfft = U.rawFFT(opData['samples']) + oplabel = opData['labels'] + + # Get MHz utilization info from the log + info = U.scrapeCLog(opData) + + # Calculate the SNR for each fft + opSNR = U.calcSNR(opfft, opData['chan']) + + # Calculate the THD for each fft + opTHD = U.calcTHD(opfft, opData['chan']) + + # Keep the results to plot later + text="SNR:{:.1f}dB\nTHD:{}dB\nMIPS:{}\n{}".format(opSNR, opTHD, info['total_mips'], info['log']) + U.pushPlotInfo(opfft, oplabel, text) + + # Update log with these results + U.logResults(sim, ipRate, opRate, fDev, opData['chan'], opSNR, opTHD, info['total_mips'], info['ch0_mips'], info['ch1_mips'], info['log']) + + + #Plot the results + plotFile = U.plotFFT(U.plot_data, combine=False, title=U.makePlotTitle(simLog, channel), subtitles=U.plot_label, log=True, text=U.plot_text) # plots a grid of charts, one per cimulation model + U.makeRST(plotFile, ipRate, opRate, fDev, simLog[channel].keys()) # add this plot to a list to save as an RST file later + U.resetPlotInfo() # otherwise the next itteration of rates etc will add more plots to this instead of starting a new plot. + if no_results: + U.addRSTText("No SRC available for this scenario.") + +U.log2csv() # Save the log file +U.addRSTHeader("Tabulated data", 2) # start the RST it generates with a title +U.addLog2RST() # adds the log, which is the tabulated results, to the RST file +U.saveRST("allPlots.rst") # Save the log file to the output folder + +print("Done") +quit() + +''' + # if xsim + if xsim: + # Put the input data through the XSIM test program + xFiles= U.run_xsim(ipFiles, opRate, fDev) + xdata = U.loadDataFromFiles(xFiles) # and load the result + + # xsim model fft data + xfft = U.doFFT(xdata[r], ch) + xlabel = xdata[r]['labels'][ch] + + # Get MHz utilization info from the log + util = U.scrapeXsimLog(xdata[r]) + + # Check the c-model results against the xsim results + matches = np.array_equal(xdata[r]['samples'], opdata[r]['samples']) + isClose = np.allclose(xdata[r]['samples'], opdata[r]['samples'], rtol=0.5**16, atol=0.5**28) + match = "Pass" if matches else "Close" if isClose else "Fail" + + # Calculate the SNR for each fft + xSNR = U.calcSNR(xfft, xdata[r]['rate'], ch) + + # Plot the result, and save the chart - just the "C" results here + text=["C SNR:{:.1f}dB\nMIPS:{:.1f}".format(opSNR, ch0mips + ch1mips), + "XSIM SNR:{:.1f}dB\nMIPS:2Ch CPU:{:.1f}\nPass/Fail:{}".format(xSNR, util, match)] + title = "Channel={}, IP={}, OP={}, FDEV={}, XSIM".format(ch,ipdata[r]['rate'], opRate, fDev) + U.plotFFT([opfft, xfft], combine=False, title=title, subtitles=["FIX ME", xlabel], log=True, text=text) + + # Update log with these results + U.logResults("XSIM", ipdata[r]['rate'], opRate, fDev, ch, xSNR, util, txt=match) + +''' + +# TODO + +# XSIM only ever supported for the ASRC XC implementation - do we need to support the others? There may be no existing test app... +# check log scraping for xsim, and is this OK on the chart, and in the log file +# add back the "pass/fail" test for xsim vs c +# the skip_xsim is just hard coded in the init, need to think about applynig to a subset? + +# tidy up call to fft to make windowing a default off option +# go through the functions to use self.opRate, self.fDev everywhere +# legacy "list-factors" code can be removed + + From 8f4dae3aad6d8a99d77b9b4833190defa937c3b7 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 19 Sep 2023 16:00:47 +0100 Subject: [PATCH 02/15] Script now genertaing a new rst file of plots, which is now included in the top level index. --- .gitignore | 3 +++ doc/python/asrc_utils.py | 10 ++++++---- doc/python/doc_asrc.py | 2 +- index.rst | 3 ++- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 4e187a4b..5409e72c 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ tests/vpu_ff3_test/autogen tests/vpu_rat_test/autogen *.csv python/lib_src.egg-info +**/tmp_models/ +*.bak +make_docs.sh \ No newline at end of file diff --git a/doc/python/asrc_utils.py b/doc/python/asrc_utils.py index 0960bdaf..51c8b21c 100644 --- a/doc/python/asrc_utils.py +++ b/doc/python/asrc_utils.py @@ -280,7 +280,7 @@ def plotFFT(self, xydata, combine=False, title=None, subtitles=None, log=True, t plotFile = "{}/{}.png".format(self.outputFolder, filename) plt.savefig(plotFile, dpi=100) plt.close() - return "{}.png".format(filename) #local filename only as rst file that uses it already in same folder + return "{}.png".format(filename) def opFileName(self, fin, label, fDev, opRate): @@ -482,13 +482,14 @@ def calcTHD(self, fftdata, ch): def makeRST(self, file, ipRate, opRate, fDev, sims): + relFile = os.path.relpath(self.outputFolder, self.rstFolder) + "/" + file fsi = "{:,d}Hz".format(self.sampleRates[ipRate]) fso = "{:,d}Hz".format(self.sampleRates[opRate]) ferr = "{:f}".format(fDev) plots = ", ".join(sims) - self.rstFile = self.rstFile + "\n\n\n" + ".. figure:: {}".format(file) + self.rstFile = self.rstFile + "\n\n\n" + ".. figure:: {}".format(relFile) self.rstFile = self.rstFile + "\n" + " :scale: {}".format("90%") - self.rstFile = self.rstFile + "\n\n" + " Input sample rate: {}, Output sample rate: {}, Sample rate error: {}, Results for: {}".format(fsi, fso, ferr, plots) + self.rstFile = self.rstFile + "\n\n" + " Input Fs: {}, Output Fs: {}, Fs error: {}, Results for: {}".format(fsi, fso, ferr, plots) def addRSTHeader(self, title, level): @@ -501,9 +502,10 @@ def addRSTText(self, text): def addLog2RST(self): + relFile = os.path.relpath(self.outputFolder, self.rstFolder) + self.logFile # source ipRate(Hz) opRate(Hz) fDev ch signals(Hz) SNR(dB) THD(dB) Total MIPS MIPS(ch0) MIPS(ch1) Text self.rstFile = self.rstFile + "\n\n\n" + ".. csv-table:: Data table" - self.rstFile = self.rstFile + "\n" + " :file: .{}".format(self.logFile) + self.rstFile = self.rstFile + "\n" + " :file: {}".format(relFile) self.rstFile = self.rstFile + "\n" + " :widths: 8, 9, 9, 9, 6, 14, 9, 9, 9, 9, 9" self.rstFile = self.rstFile + "\n" + " :header-rows: 1" self.rstFile = self.rstFile + "\n" diff --git a/doc/python/doc_asrc.py b/doc/python/doc_asrc.py index de80b135..8172c10b 100644 --- a/doc/python/doc_asrc.py +++ b/doc/python/doc_asrc.py @@ -32,7 +32,7 @@ U.addRSTHeader("Frequency error: {:.6f}Hz".format(fDev), 3) #add a title to the RST for the freq deviation for opRate in U.allRates: # for each of the possible output sample rates - U.addRSTHeader("Output sample frequency : {:,d}".format(U.sampleRates[opRate]), 4) # add a subtitle to RST for the opRate + U.addRSTHeader("Output Fs : {:,d}Hz".format(U.sampleRates[opRate]), 4) # add a subtitle to RST for the opRate # Choose the o/p rate and freq deviation U.setOpRate(opRate, fDev) diff --git a/index.rst b/index.rst index 14e6c13e..e94f82ba 100644 --- a/index.rst +++ b/index.rst @@ -6,4 +6,5 @@ SAMPLE RATE CONVERSION .. toctree:: :maxdepth: 5 - ./doc/rst/programming_guide/index.rst \ No newline at end of file + ./doc/rst/programming_guide/index.rst + ./doc/python/_build/rst/allPlots.rst \ No newline at end of file From 5f957e6af98b2ff82f55f1dd00eea48bd16c9438 Mon Sep 17 00:00:00 2001 From: Shuchita Khare Date: Fri, 22 Sep 2023 09:55:13 +0100 Subject: [PATCH 03/15] Fix demo executables path. Remove MIPS info from the plot and the csv log --- doc/python/asrc_utils.py | 74 +++++++++++++++++++++------------------- doc/python/doc_asrc.py | 24 ++++++------- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/doc/python/asrc_utils.py b/doc/python/asrc_utils.py index 51c8b21c..b5d54728 100644 --- a/doc/python/asrc_utils.py +++ b/doc/python/asrc_utils.py @@ -15,7 +15,7 @@ class asrc_util: def __init__(self, path, relative, fftPoints, ch0_bins, ch1_bins): - self.path = path + self.path = str(path) self.relative = relative # locations for results - note they are purged every time this is run @@ -24,14 +24,14 @@ def __init__(self, path, relative, fftPoints, ch0_bins, ch1_bins): self.inputFolder = self.cleanFolder("_build/input") # put any input test files here self.expectedFolder = self.cleanFolder("_build/expected") # put C ref model o/p here self.rstFolder = self.cleanFolder("_build/rst") # put RST o/p here - + self.logFile = "/log.csv" # tabulated results # the C-models and XSIM models which get run - self.asrc_model ="{}/../tmp_models/asrc_demo".format(self.path) # path and file of the reference c model executable - self.ssrc_model ="{}/../tmp_models/ssrc_demo".format(self.path) # path and file of the reference c model executable - self.ds3_model ="{}/../tmp_models/ds3_demo".format(self.path) # path and file of the reference c model executable - self.os3_model ="{}/../tmp_models/os3_demo".format(self.path) # path and file of the reference c model executable + self.asrc_model ="{}/../../build/tests/asrc_test/asrc_golden".format(self.path) # path and file of the reference c model executable + self.ssrc_model ="{}/../../build/tests/ssrc_test/ssrc_golden".format(self.path) # path and file of the reference c model executable + self.ds3_model ="{}/../../build/tests/ds3_test/ds3_golden".format(self.path) # path and file of the reference c model executable + self.os3_model ="{}/../../build/tests/os3_test/os3_golden".format(self.path) # path and file of the reference c model executable self.xePath = "{}/XSIM/ASRC/asrc_test.xe".format(self.path) # path and file of the xe xsim executable # definitions of sample ratea @@ -40,7 +40,7 @@ def __init__(self, path, relative, fftPoints, ch0_bins, ch1_bins): self.factors = {"44":0, "48":1, "88":2, "96":3, "176":4, "192":5} # look up to convert rate to the filter bank id needed by the ASRC self.sampleRates = {"16":16000, "32":32000, "44":44100, "48":48000, "88":88200, "96":96000, "176":176400, "192":192000} #convinience to save typing out sample rates in full self.sigMax = {"16":7300, "32":14600, "44":20200, "48":21800, "88":40000, "96":47990, "176":80000, "192":85000} # upper limit on input freq for given sample rate - + self.numSamples = {} #populated later based on oprate to ensure sufficient samples for fft self.ignoreSamples = 2000 #worst case for high up-sampling self.fftPoints = fftPoints @@ -56,7 +56,7 @@ def __init__(self, path, relative, fftPoints, ch0_bins, ch1_bins): self.skip_xsim = True # FIX THIS LATER self.rstFile = "" - + # update the input frequencies # allows input of frequencies as a list for each channel, or use the autoFill which # populates channel 1 with mutiple tones, spaced logrithmically, most dense at higher freq, and ch0 is always 10% in @@ -65,7 +65,7 @@ def updateSig(self, ipRate, ch0_bins, ch1_bins, autoFill=False): # this manipultaes the fft size to attempt to force any reflections around the ip sample rate nyquist also ending up # in an integer fft bin self.fftPoints = int(10000 *(self.sampleRates[ipRate] * self.fDev) / self.sampleRates[self.opRate]) - print("Over-wrote the fft sive to {} points".format(self.fftPoints)) + print(f"Over-wrote the fft size to {self.fftPoints} points. ipRate = {ipRate}") #for channel 0 ch0_ratio = 0.05 # 5% of the way into the fft bins, so 10% on the plot @@ -110,13 +110,13 @@ def makePlotTitle(self, simLog, channel): def logResults(self, source, ipRate, opRate, fDev, ch, SNR, THD, totalmips, ch0mips=None, ch1mips=None, txt=None, snr2=None, mips2=None): # simple function to append information to a log, which is kept as an array of dictionaries - realRate = self.sampleRates[opRate] / self.fDev + realRate = self.sampleRates[opRate] / self.fDev sigList = [] for s in self.sig[ch]: sigList.append("{:.3f}".format(float(mp.fmul(realRate ,mp.fdiv(s, self.fftPoints))))) signals = ";".join(sigList) #info = {"source":source, "ipRate(Hz)":self.sampleRates[ipRate], "opRate(Hz)":self.sampleRates[opRate], "fDev":fDev, "ch":ch, "signals(Hz)":signals, "SNR(dB)":SNR, "THD(dB)":THD,"Total MIPS":totalmips, "MIPS(ch0)":ch0mips, "MIPS(ch1)":ch1mips, "Text":txt} - info = {"source":source, "ipRate(Hz)":str(self.sampleRates[ipRate]), "opRate(Hz)":str(self.sampleRates[opRate]), "fDev":str(fDev), "ch":str(ch), "signals(Hz)":signals, "SNR(dB)":"{:.1f}".format(SNR), "THD(dB)":THD,"Total MIPS":"{:.0f}".format(totalmips), "MIPS(ch0)":"{:.0f}".format(ch0mips), "MIPS(ch1)":"{:.0f}".format(ch1mips)} + info = {"source":source, "ipRate(Hz)":str(self.sampleRates[ipRate]), "opRate(Hz)":str(self.sampleRates[opRate]), "fDev":str(fDev), "ch":str(ch), "signals(Hz)":signals, "SNR(dB)":"{:.1f}".format(SNR), "THD(dB)":THD} self.log.append(info) @@ -125,7 +125,7 @@ def log2csv(self): logfile = open(self.outputFolder + self.logFile, "w") logfile.write(",".join(self.log[0].keys()) + "\n") # write headings for i in self.log: - logfile.write((','.join(i.values())).replace("\n", ";") + "\n") # write data + logfile.write((','.join(i.values())).replace("\n", ";") + "\n") # write data logfile.close() @@ -142,7 +142,7 @@ def cleanFolder(self, folder): if self.relative: p = p.replace(self.path, ".") return p - + def listFactors(self, x): factors=[] for i in range(1,x+1): @@ -161,9 +161,9 @@ def makeSignal(self, fsamp, fsig, asig, l, ferr=1.0): print("Make signal: {:.2f}".format(float(fsig))) a = asig * 2**31 x = np.linspace(0, float(2*mp.pi*f*(l-1)), l) - ipdata = a * np.sin(x) + ipdata = a * np.sin(x) return ipdata - + def saveInputFile(self, signals, fileName): # sums the signals into one signal and saves as a file @@ -174,9 +174,9 @@ def saveInputFile(self, signals, fileName): np.savetxt(file, data, fmt='%d', delimiter='\n') #writewav("/mnt/c/Users/andrewdewhurst/OneDrive - Xmos/Temp/rawdata/test.wav", 44100, (data[0]/2**16).astype(np.int16)) return file, data - + def makeInputFiles(self, test_rates, ferr=1.0): - # create two channels of input + # create two channels of input # define some test signal frequencies w.r.t. op rate realRate = self.sampleRates[self.opRate] # mp.fdiv(self.sampleRates[self.opRate], self.fDev) # @@ -211,7 +211,7 @@ def makeInputFiles(self, test_rates, ferr=1.0): def doFFT(self, data, window=False): - # convinient way to select between fft styles. Note that the periodic one will need a lot more samples, so + # convinient way to select between fft styles. Note that the periodic one will need a lot more samples, so # use window=True for debuging. if window: return self.winFFT(data) @@ -262,7 +262,7 @@ def plotFFT(self, xydata, combine=False, title=None, subtitles=None, log=True, t ax.plot(xy[0], xy[1] if log else xy[2], linestyle=slut[i%len(slut)], color=clut[i%len(clut)], linewidth=1, label=subtitles[i]) ax.set_xlabel("Frequency (KHz)") ax.set_ylabel("dB") - ax.set_ylim(-200,0) + ax.set_ylim(-250,0) ax.grid() if text != None: tl = np.sum([text[x].count("\n") + 1 for x in range(i)]) if combine else 0 # this stacks up the text comments in the case of combined plots @@ -292,7 +292,7 @@ def exFileName(self, fin, label, fDev, opRate): #a simple utility to convert an input file pat+name to an output version, appending the frequency deviation newName = fin.replace(self.inputFolder, self.expectedFolder).replace(".dat", "_fso{}_fdev{:f}_{}.dat".format(opRate, fDev, label)) return newName - + def run_c_model(self, ipFiles, opRate, blocksize, fDev): # Runs the various simulators across the input files, for the provided opRate and frequency deviation @@ -314,13 +314,14 @@ def run_c_model(self, ipFiles, opRate, blocksize, fDev): ch1 = self.exFileName(ipFile[1], 'c-asrc', fDev, opRate) cmd = "{} -i{} -j{} -k{} -o{} -p{} -q{} -d{} -l{} -n{} -e{}".format( - self.asrc_model, ipFile[0], ipFile[1] , self.factors[ipFile[2]], ch0, ch1, self.factors[opRate], dither, self.numSamples[ipFile[2]], blocksize, fDev) + self.asrc_model, ipFile[0], ipFile[1] , self.factors[ipFile[2]], ch0, ch1, self.factors[opRate], dither, self.numSamples[ipFile[2]], blocksize, fDev) p = Popen([cmd], stdin=PIPE, stdout=PIPE, shell=True) output, error = p.communicate(input=b'\n') opFiles = np.append(opFiles, np.array([[ch0, ch1, opRate, str(fDev), output, "c-asrc"]]), axis=0) simLog[ipFile[0]]['asrc']=[str(ch0), 0, opRate, str(fDev), output, "c-asrc"] simLog[ipFile[1]]['asrc']=[str(ch1), 1, opRate, str(fDev), output, "c-asrc"] + # SSRC C Model # ------------ if ipFile[2] in self.srcRates and self.opRate in self.srcRates and fDev==1: @@ -328,7 +329,7 @@ def run_c_model(self, ipFiles, opRate, blocksize, fDev): ch1 = self.exFileName(ipFile[1], 'c-ssrc', fDev, opRate) cmd = "{} -i{} -j{} -k{} -o{} -p{} -q{} -d{} -l{} -n{}".format( - self.ssrc_model, ipFile[0], ipFile[1] , self.factors[ipFile[2]], ch0, ch1, self.factors[opRate], dither, self.numSamples[ipFile[2]], blocksize) + self.ssrc_model, ipFile[0], ipFile[1] , self.factors[ipFile[2]], ch0, ch1, self.factors[opRate], dither, self.numSamples[ipFile[2]], blocksize) p = Popen([cmd], stdin=PIPE, stdout=PIPE, shell=True) output, error = p.communicate(input=b'\n') opFiles = np.append(opFiles, np.array([[ch0, ch1, opRate, "1.0", output, "c-ssrc"]]), axis=0) @@ -372,13 +373,14 @@ def run_c_model(self, ipFiles, opRate, blocksize, fDev): os.remove("./output.dat") # ASRC XSIM Model - # --------------- + # --------------- if ipFile[2] in self.srcRates and self.opRate in self.srcRates and not self.skip_xsim: xsimFiles = self.run_xsim(ipFiles, opRate, fDev, simLog) opFiles = np.append(opFiles, xsimFiles, axis=0) + return opFiles, simLog - + def run_xsim(self, ipFiles, opRate, fDev, simLog): if os.path.isfile("./asrc_test.lnk"): @@ -388,10 +390,10 @@ def run_xsim(self, ipFiles, opRate, fDev, simLog): for ipFile in ipFiles: ch0 = self.opFileName(ipFile[0], 'x-asrc', fDev, opRate) ch1 = self.opFileName(ipFile[1], 'x-asrc', fDev, opRate) - + cmd0 = "cd {}".format(self.path) cmd1 = "xsim --args asrc_test.lnk -i {} {} -o {} {} -f {} -g {} -n {} -e {}".format( - ipFile[0], ipFile[1], ch0, ch1, self.sampleRates[ipFile[2]], self.sampleRates[opRate], self.numSamples[ipFile[2]], fDev) + ipFile[0], ipFile[1], ch0, ch1, self.sampleRates[ipFile[2]], self.sampleRates[opRate], self.numSamples[ipFile[2]], fDev) cmd = cmd0 + ";pwd;" + cmd1 p = Popen(cmd, stdin=PIPE, stdout=PIPE, shell=True) output, error = p.communicate(input=b'\n') @@ -427,16 +429,16 @@ def scrapeCLog(self, data): ch0mips = float(m[0].replace("\\n", "").split(":")[1].strip()) ch1mips = float(m[5].replace("\\n", "").split(":")[1].strip()) totalmips = ch0mips + ch1mips - txtmips = "\n".join(m) + txtmips = "\n".join(m) if data['src'] == "c-ssrc": p = re.compile("MIPS.*?\n") m = re.findall(p, str(data['log']).replace("\\n","\n")) if len(m) > 0: - totalmips = float(m[0].split(":")[1].strip()) + totalmips = float(m[0].split(":")[1].strip()) + + return {"log":txtmips, "total_mips":totalmips, "ch0_mips":ch0mips, "ch1_mips":ch1mips} + - return {"log":txtmips, "total_mips":totalmips, "ch0_mips":ch0mips, "ch1_mips":ch1mips} - - def makeLabel(self, item): #utility to generate a title / filename to use in plots from an item from a file list which is in the foramt [file1, file2, rate as str] #and should work for ipFiles and opFiles @@ -448,7 +450,7 @@ def makeLabel(self, item): def loadDataFromOutputFile(self, file): #loads an array of files in the format returned by running 'run_c_model' and returns an array of elements, each of which is a dictionary of [ 2 channels of samples], [2 channels of labels], o/p rate ch0 = np.loadtxt(file[0]).astype("int32") - label = self.makeLabel(file) + label = self.makeLabel(file) return {'samples':ch0, 'labels':label, 'chan':file[-5], 'rate':file[-4], 'fdev':file[-3], 'log':file[-2], 'src':file[-1]} @@ -479,8 +481,8 @@ def calcTHD(self, fftdata, ch): thd = 10*np.log10((noise/signal)) thd="{:.1f}dB".format(thd) #returned as string so we can pass the "NA" for muti-tone cases return thd - - + + def makeRST(self, file, ipRate, opRate, fDev, sims): relFile = os.path.relpath(self.outputFolder, self.rstFolder) + "/" + file fsi = "{:,d}Hz".format(self.sampleRates[ipRate]) @@ -490,7 +492,7 @@ def makeRST(self, file, ipRate, opRate, fDev, sims): self.rstFile = self.rstFile + "\n\n\n" + ".. figure:: {}".format(relFile) self.rstFile = self.rstFile + "\n" + " :scale: {}".format("90%") self.rstFile = self.rstFile + "\n\n" + " Input Fs: {}, Output Fs: {}, Fs error: {}, Results for: {}".format(fsi, fso, ferr, plots) - + def addRSTHeader(self, title, level): underline=["=", "+", "-", "."] @@ -515,4 +517,4 @@ def addLog2RST(self): def saveRST(self, file): rst = open("{}/{}".format(self.rstFolder, file), "w") rst.write(self.rstFile) - rst.close() \ No newline at end of file + rst.close() diff --git a/doc/python/doc_asrc.py b/doc/python/doc_asrc.py index 8172c10b..d1fe882a 100644 --- a/doc/python/doc_asrc.py +++ b/doc/python/doc_asrc.py @@ -1,7 +1,7 @@ from asrc_utils import asrc_util import numpy as np import matplotlib.pyplot as plt -from mpmath import mp +from pathlib import Path ################################################################################################################ # OVERVIEW @@ -19,12 +19,12 @@ # Setup some basics -path = '.' +pkg_dir = Path(__file__).parent xsim = False fftPoints=1024 # note when updating the test tones with "updateSig" this is overwritten FERR = 1.0 #this is an additional sample rate deviation applied to the inpt signal to test effect of errors -U = asrc_util(path, True, fftPoints,[200], [500]) # create the util object +U = asrc_util(pkg_dir, True, fftPoints,[200], [500]) # create the util object U.addRSTHeader("Performance information", 1) # start the RST it generates with a title U.addRSTHeader("Chart data", 2) # start the RST it generates with a title @@ -44,15 +44,15 @@ # Make a set of input files based on range of sample rates supported ipFiles, sig = U.makeInputFiles([ipRate], FERR) # makes the signals, saves data as files and returns some info about them - + # Put the input data through the golden "C" emulators - opFiles, simLog = U.run_c_model(ipFiles, opRate, 4, fDev) + opFiles, simLog = U.run_c_model(ipFiles, opRate, 4, fDev) # Itterate over all the input data files and channels for channels in ipFiles: # For each input sata set there will be an output one for each channel for channel in channels[0:2]: print(channel) - + if len(simLog[channel]) > 0: #at least one simulation ran for this combination of iprate, oprate and fdev no_results = False for sim in simLog[channel]: @@ -72,11 +72,11 @@ opTHD = U.calcTHD(opfft, opData['chan']) # Keep the results to plot later - text="SNR:{:.1f}dB\nTHD:{}dB\nMIPS:{}\n{}".format(opSNR, opTHD, info['total_mips'], info['log']) + text="SNR:{:.1f}dB\nTHD:{}dB\n".format(opSNR, opTHD) U.pushPlotInfo(opfft, oplabel, text) # Update log with these results - U.logResults(sim, ipRate, opRate, fDev, opData['chan'], opSNR, opTHD, info['total_mips'], info['ch0_mips'], info['ch1_mips'], info['log']) + U.logResults(sim, ipRate, opRate, fDev, opData['chan'], opSNR, opTHD, info['total_mips'], info['ch0_mips'], info['ch1_mips'], info['log']) #Plot the results @@ -104,7 +104,7 @@ # xsim model fft data xfft = U.doFFT(xdata[r], ch) xlabel = xdata[r]['labels'][ch] - + # Get MHz utilization info from the log util = U.scrapeXsimLog(xdata[r]) @@ -118,12 +118,12 @@ # Plot the result, and save the chart - just the "C" results here text=["C SNR:{:.1f}dB\nMIPS:{:.1f}".format(opSNR, ch0mips + ch1mips), - "XSIM SNR:{:.1f}dB\nMIPS:2Ch CPU:{:.1f}\nPass/Fail:{}".format(xSNR, util, match)] + "XSIM SNR:{:.1f}dB\nMIPS:2Ch CPU:{:.1f}\nPass/Fail:{}".format(xSNR, util, match)] title = "Channel={}, IP={}, OP={}, FDEV={}, XSIM".format(ch,ipdata[r]['rate'], opRate, fDev) U.plotFFT([opfft, xfft], combine=False, title=title, subtitles=["FIX ME", xlabel], log=True, text=text) # Update log with these results - U.logResults("XSIM", ipdata[r]['rate'], opRate, fDev, ch, xSNR, util, txt=match) + U.logResults("XSIM", ipdata[r]['rate'], opRate, fDev, ch, xSNR, util, txt=match) ''' @@ -137,5 +137,3 @@ # tidy up call to fft to make windowing a default off option # go through the functions to use self.opRate, self.fDev everywhere # legacy "list-factors" code can be removed - - From 10583d27d6be7c37fdb890cfe9ad76fc7fbfb941 Mon Sep 17 00:00:00 2001 From: Shuchita Khare Date: Fri, 22 Sep 2023 10:40:06 +0100 Subject: [PATCH 04/15] -added support for building the model exexutable in the doc_python script. - Remove xsim run support from the doc_python script --- Jenkinsfile | 15 ++++++++++++ doc/python/asrc_utils.py | 52 ++++++++++++++-------------------------- doc/python/doc_asrc.py | 44 ---------------------------------- 3 files changed, 33 insertions(+), 78 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 4b9e3175..e55133ec 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -80,6 +80,21 @@ pipeline { } } } + stage('Run doc python') { + steps { + runningOn(env.NODE_NAME) + dir("${REPO}") { + withTools(params.TOOLS_VERSION) { + withVenv { + dir("doc/python") { + sh "python -m doc_asrc.py" + archiveArtifacts artifacts: "_build/*", allowEmptyArchive: true + } + } + } + } + } + } stage('Test xmake build') { steps { runningOn(env.NODE_NAME) diff --git a/doc/python/asrc_utils.py b/doc/python/asrc_utils.py index b5d54728..2381ebe8 100644 --- a/doc/python/asrc_utils.py +++ b/doc/python/asrc_utils.py @@ -11,6 +11,8 @@ from math import gcd from mpmath import mp from scipy.io.wavfile import write as writewav +from pathlib import Path +import subprocess class asrc_util: @@ -28,11 +30,10 @@ def __init__(self, path, relative, fftPoints, ch0_bins, ch1_bins): self.logFile = "/log.csv" # tabulated results # the C-models and XSIM models which get run - self.asrc_model ="{}/../../build/tests/asrc_test/asrc_golden".format(self.path) # path and file of the reference c model executable - self.ssrc_model ="{}/../../build/tests/ssrc_test/ssrc_golden".format(self.path) # path and file of the reference c model executable - self.ds3_model ="{}/../../build/tests/ds3_test/ds3_golden".format(self.path) # path and file of the reference c model executable - self.os3_model ="{}/../../build/tests/os3_test/os3_golden".format(self.path) # path and file of the reference c model executable - self.xePath = "{}/XSIM/ASRC/asrc_test.xe".format(self.path) # path and file of the xe xsim executable + self.asrc_model = self.build_model_exe("asrc") + self.ssrc_model = self.build_model_exe("ssrc") + self.ds3_model = self.build_model_exe("ds3") + self.os3_model = self.build_model_exe("os3") # definitions of sample ratea self.allRates =["16", "32", "44", "48", "88", "96", "176", "192"] @@ -56,6 +57,18 @@ def __init__(self, path, relative, fftPoints, ch0_bins, ch1_bins): self.skip_xsim = True # FIX THIS LATER self.rstFile = "" + def build_model_exe(self, target): + file_dir = Path(__file__).resolve().parent + build_path = file_dir / "../../build" + build_path.mkdir(exist_ok=True) + subprocess.run("rm -rf CMakeCache.txt CMakeFiles/", shell=True, cwd=str(build_path)) + subprocess.run("cmake ..", shell=True, cwd=str(build_path)) + bin_path = file_dir / f"{target}_/model" + subprocess.run(f"make {target}_golden", shell=True, cwd=str(build_path)) + + return f"{build_path}/tests/{target}_test/{target}_golden" + + # update the input frequencies # allows input of frequencies as a list for each channel, or use the autoFill which @@ -372,38 +385,9 @@ def run_c_model(self, ipFiles, opRate, blocksize, fDev): os.remove("./input.dat") os.remove("./output.dat") - # ASRC XSIM Model - # --------------- - if ipFile[2] in self.srcRates and self.opRate in self.srcRates and not self.skip_xsim: - xsimFiles = self.run_xsim(ipFiles, opRate, fDev, simLog) - opFiles = np.append(opFiles, xsimFiles, axis=0) - return opFiles, simLog - - def run_xsim(self, ipFiles, opRate, fDev, simLog): - if os.path.isfile("./asrc_test.lnk"): - os.remove("./asrc_test.lnk") - os.link(self.xePath, "./asrc_test.lnk") - opFiles=np.empty((0,6), str) - for ipFile in ipFiles: - ch0 = self.opFileName(ipFile[0], 'x-asrc', fDev, opRate) - ch1 = self.opFileName(ipFile[1], 'x-asrc', fDev, opRate) - - cmd0 = "cd {}".format(self.path) - cmd1 = "xsim --args asrc_test.lnk -i {} {} -o {} {} -f {} -g {} -n {} -e {}".format( - ipFile[0], ipFile[1], ch0, ch1, self.sampleRates[ipFile[2]], self.sampleRates[opRate], self.numSamples[ipFile[2]], fDev) - cmd = cmd0 + ";pwd;" + cmd1 - p = Popen(cmd, stdin=PIPE, stdout=PIPE, shell=True) - output, error = p.communicate(input=b'\n') - p.wait() - opFiles = np.append(opFiles, np.array([[ch0, ch1, opRate, str(fDev), output, "x-asrc"]]), axis=0) - simLog[ipFile[0]]['xsim-asrc']=[str(ch0), 0, opRate, str(fDev), output, "x-asrc"] - simLog[ipFile[1]]['xsim-asrc']=[str(ch1), 1, opRate, str(fDev), output, "x-asrc"] - return opFiles - - def scrapeXsimLog(self, data): # This bit scrapes the output of xsim and takes the average of all reported CPU utilization log = data['log'] diff --git a/doc/python/doc_asrc.py b/doc/python/doc_asrc.py index d1fe882a..d30d01ad 100644 --- a/doc/python/doc_asrc.py +++ b/doc/python/doc_asrc.py @@ -93,47 +93,3 @@ print("Done") quit() - -''' - # if xsim - if xsim: - # Put the input data through the XSIM test program - xFiles= U.run_xsim(ipFiles, opRate, fDev) - xdata = U.loadDataFromFiles(xFiles) # and load the result - - # xsim model fft data - xfft = U.doFFT(xdata[r], ch) - xlabel = xdata[r]['labels'][ch] - - # Get MHz utilization info from the log - util = U.scrapeXsimLog(xdata[r]) - - # Check the c-model results against the xsim results - matches = np.array_equal(xdata[r]['samples'], opdata[r]['samples']) - isClose = np.allclose(xdata[r]['samples'], opdata[r]['samples'], rtol=0.5**16, atol=0.5**28) - match = "Pass" if matches else "Close" if isClose else "Fail" - - # Calculate the SNR for each fft - xSNR = U.calcSNR(xfft, xdata[r]['rate'], ch) - - # Plot the result, and save the chart - just the "C" results here - text=["C SNR:{:.1f}dB\nMIPS:{:.1f}".format(opSNR, ch0mips + ch1mips), - "XSIM SNR:{:.1f}dB\nMIPS:2Ch CPU:{:.1f}\nPass/Fail:{}".format(xSNR, util, match)] - title = "Channel={}, IP={}, OP={}, FDEV={}, XSIM".format(ch,ipdata[r]['rate'], opRate, fDev) - U.plotFFT([opfft, xfft], combine=False, title=title, subtitles=["FIX ME", xlabel], log=True, text=text) - - # Update log with these results - U.logResults("XSIM", ipdata[r]['rate'], opRate, fDev, ch, xSNR, util, txt=match) - -''' - -# TODO - -# XSIM only ever supported for the ASRC XC implementation - do we need to support the others? There may be no existing test app... -# check log scraping for xsim, and is this OK on the chart, and in the log file -# add back the "pass/fail" test for xsim vs c -# the skip_xsim is just hard coded in the init, need to think about applynig to a subset? - -# tidy up call to fft to make windowing a default off option -# go through the functions to use self.opRate, self.fDev everywhere -# legacy "list-factors" code can be removed From da7f61a7c250ec4bf6c3c764cd6f4ad8594ea2ba Mon Sep 17 00:00:00 2001 From: Shuchita Khare Date: Fri, 22 Sep 2023 10:53:01 +0100 Subject: [PATCH 05/15] Added mpmath to dependencies --- doc/python/README.txt | 10 +++++----- python/setup.py | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/python/README.txt b/doc/python/README.txt index f32ff2fc..c0cd72bc 100644 --- a/doc/python/README.txt +++ b/doc/python/README.txt @@ -2,7 +2,7 @@ INTRODUCTION ============ This folder includes a python script which runs the C emulators for the ASRC, SSRC, OS3 and DS3 modules. -For each model we present test signals - both a single tone to test the THD performance and a range of tones to +For each model we present test signals - both a single tone to test the THD performance and a range of tones to observe roll-off at the top of the band. Using an un-windowed FFT we calculate the SNR. We scrape the logs to extract MIPS/Utilization where supported. @@ -26,16 +26,16 @@ To create and setup the conda environment to run the asrc_test.py script conda env create -f condaEnv.yml conda activate .conda-env - + To run the script: python3 doc_asrc.py - - + + By default, this will create a "_build" folder, containing sub-folders: ./input all generated test files ./expected simulation output from the models ./output plots and a csv format log file - ./rst a single rst file which incorporates all the plots and log file as a table \ No newline at end of file + ./rst a single rst file which incorporates all the plots and log file as a table diff --git a/python/setup.py b/python/setup.py index f452b030..58f269bf 100644 --- a/python/setup.py +++ b/python/setup.py @@ -19,6 +19,7 @@ 'soundfile', 'pytest-xdist', 'scipy' + 'mpmath' ], dependency_links=[ ], From 52743ea6064e90175bd03fa3320f023fd7c08168 Mon Sep 17 00:00:00 2001 From: Shuchita Khare Date: Fri, 22 Sep 2023 10:59:13 +0100 Subject: [PATCH 06/15] fix typo --- doc/python/README.txt | 14 +++++--------- python/setup.py | 2 +- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/doc/python/README.txt b/doc/python/README.txt index c0cd72bc..e7ea1d1f 100644 --- a/doc/python/README.txt +++ b/doc/python/README.txt @@ -15,17 +15,13 @@ incorporating a full set of charts and summary table. -HOW TO -====== - -This has been developed and tested under WSL2 linux for Windows. -In a WSL2 command window (e.g., Ubuntu20.04): - +REQUIREMENTS +============ -To create and setup the conda environment to run the asrc_test.py script +Python 3.9 or above. Make sure the dependencies listed in lib_src/requirements.txt are installed. - conda env create -f condaEnv.yml - conda activate .conda-env + cd ../../ + pip install -r requirements.txt diff --git a/python/setup.py b/python/setup.py index 58f269bf..1537b2af 100644 --- a/python/setup.py +++ b/python/setup.py @@ -18,7 +18,7 @@ 'numpy', 'soundfile', 'pytest-xdist', - 'scipy' + 'scipy', 'mpmath' ], dependency_links=[ From 85edcbe87541ca25945bdfb8ea3d95933ea4e0d0 Mon Sep 17 00:00:00 2001 From: Shuchita Khare Date: Fri, 22 Sep 2023 11:00:45 +0100 Subject: [PATCH 07/15] Remove condaEnv.yml --- doc/python/condaEnv.yml | 111 ---------------------------------------- 1 file changed, 111 deletions(-) delete mode 100644 doc/python/condaEnv.yml diff --git a/doc/python/condaEnv.yml b/doc/python/condaEnv.yml deleted file mode 100644 index afc5c059..00000000 --- a/doc/python/condaEnv.yml +++ /dev/null @@ -1,111 +0,0 @@ -name: .conda-env -channels: - - anaconda - - defaults -dependencies: - - _libgcc_mutex=0.1=main - - _openmp_mutex=5.1=1_gnu - - appdirs=1.4.4=pyhd3eb1b0_0 - - blas=1.0=mkl - - brotli=1.0.9=h5eee18b_7 - - brotli-bin=1.0.9=h5eee18b_7 - - brotlipy=0.7.0=py311h5eee18b_1002 - - bzip2=1.0.8=h7b6447c_0 - - ca-certificates=2023.01.10=h06a4308_0 - - certifi=2020.6.20=pyhd3eb1b0_3 - - cffi=1.15.1=py311h5eee18b_3 - - charset-normalizer=2.0.4=pyhd3eb1b0_0 - - contourpy=1.0.5=py311hdb19cb5_0 - - cryptography=41.0.2=py311h22a60cf_0 - - cycler=0.11.0=pyhd3eb1b0_0 - - dbus=1.13.18=hb2f20db_0 - - expat=2.4.9=h6a678d5_0 - - fontconfig=2.14.1=h52c9d5c_1 - - fonttools=4.25.0=pyhd3eb1b0_0 - - freetype=2.12.1=h4a9f257_0 - - giflib=5.2.1=h5eee18b_3 - - glib=2.69.1=he621ea3_2 - - gst-plugins-base=1.14.1=h6a678d5_1 - - gstreamer=1.14.1=h5eee18b_1 - - icu=58.2=he6710b0_3 - - idna=3.3=pyhd3eb1b0_0 - - intel-openmp=2023.1.0=hdb19cb5_46305 - - jpeg=9e=h5eee18b_1 - - kiwisolver=1.4.4=py311h6a678d5_0 - - krb5=1.20.1=h143b758_1 - - lcms2=2.12=h3be6417_0 - - ld_impl_linux-64=2.38=h1181459_1 - - lerc=3.0=h295c915_0 - - libbrotlicommon=1.0.9=h5eee18b_7 - - libbrotlidec=1.0.9=h5eee18b_7 - - libbrotlienc=1.0.9=h5eee18b_7 - - libclang=10.0.1=default_hb85057a_2 - - libdeflate=1.17=h5eee18b_0 - - libedit=3.1.20221030=h5eee18b_0 - - libevent=2.1.12=hdbd6064_1 - - libffi=3.4.4=h6a678d5_0 - - libgcc-ng=11.2.0=h1234567_1 - - libgfortran-ng=11.2.0=h00389a5_1 - - libgfortran5=11.2.0=h1234567_1 - - libgomp=11.2.0=h1234567_1 - - libllvm10=10.0.1=hbcb73fb_5 - - libpng=1.6.39=h5eee18b_0 - - libpq=12.15=hdbd6064_1 - - libstdcxx-ng=11.2.0=h1234567_1 - - libtiff=4.5.0=h6a678d5_2 - - libuuid=1.41.5=h5eee18b_0 - - libwebp=1.2.4=h11a3e52_1 - - libwebp-base=1.2.4=h5eee18b_1 - - libxcb=1.15=h7f8727e_0 - - libxkbcommon=1.0.1=hfa300c1_0 - - libxml2=2.9.14=h74e7548_0 - - libxslt=1.1.35=h4e12654_0 - - lz4-c=1.9.4=h6a678d5_0 - - matplotlib=3.7.1=py311h06a4308_1 - - matplotlib-base=3.7.1=py311ha02d727_1 - - mkl=2023.1.0=h6d00ec8_46342 - - mkl-service=2.4.0=py311h5eee18b_1 - - mkl_fft=1.3.6=py311ha02d727_1 - - mkl_random=1.2.2=py311ha02d727_1 - - munkres=1.1.4=py_0 - - ncurses=6.4=h6a678d5_0 - - nspr=4.35=h6a678d5_0 - - nss=3.89.1=h6a678d5_0 - - numpy=1.25.0=py311h08b1b3b_0 - - numpy-base=1.25.0=py311hf175353_0 - - openssl=3.0.10=h7f8727e_0 - - packaging=23.0=py311h06a4308_0 - - pcre=8.45=h295c915_0 - - pillow=9.4.0=py311h6a678d5_0 - - pip=23.2.1=py311h06a4308_0 - - ply=3.11=py311h06a4308_0 - - pooch=1.4.0=pyhd3eb1b0_0 - - pycparser=2.21=pyhd3eb1b0_0 - - pyopenssl=23.2.0=py311h06a4308_0 - - pyparsing=3.0.9=py311h06a4308_0 - - pyqt=5.15.7=py311h6a678d5_0 - - pyqt5-sip=12.11.0=py311h6a678d5_0 - - pysocks=1.7.1=py311h06a4308_0 - - python=3.11.4=h955ad1f_0 - - python-dateutil=2.8.2=pyhd3eb1b0_0 - - qt-main=5.15.2=h327a75a_7 - - qt-webengine=5.15.9=hd2b0992_4 - - qtwebkit=5.212=h4eab89a_4 - - readline=8.2=h5eee18b_0 - - requests=2.27.1=pyhd3eb1b0_0 - - scipy=1.10.1=py311h08b1b3b_1 - - setuptools=68.0.0=py311h06a4308_0 - - sip=6.6.2=py311h6a678d5_0 - - six=1.16.0=pyhd3eb1b0_1 - - sqlite=3.41.2=h5eee18b_0 - - tbb=2021.8.0=hdb19cb5_0 - - tk=8.6.12=h1ccaba5_0 - - toml=0.10.2=pyhd3eb1b0_0 - - tornado=6.3.2=py311h5eee18b_0 - - tzdata=2023c=h04d1e81_0 - - urllib3=1.26.8=pyhd3eb1b0_0 - - wheel=0.38.4=py311h06a4308_0 - - xz=5.4.2=h5eee18b_0 - - zlib=1.2.13=h5eee18b_0 - - zstd=1.5.5=hc292b87_0 -prefix: /home/andrew/asrc/lib_src_develop/lib_src/tests/Doc_utils/.conda-env From 766f9e7f803cd71e5981d7e39b5ce545646e4b8d Mon Sep 17 00:00:00 2001 From: Shuchita Khare Date: Fri, 22 Sep 2023 11:08:09 +0100 Subject: [PATCH 08/15] copyright check --- Jenkinsfile | 30 +++++++++++++++--------------- doc/python/asrc_utils.py | 2 ++ doc/python/doc_asrc.py | 2 ++ 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index e55133ec..1d26de10 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -80,21 +80,6 @@ pipeline { } } } - stage('Run doc python') { - steps { - runningOn(env.NODE_NAME) - dir("${REPO}") { - withTools(params.TOOLS_VERSION) { - withVenv { - dir("doc/python") { - sh "python -m doc_asrc.py" - archiveArtifacts artifacts: "_build/*", allowEmptyArchive: true - } - } - } - } - } - } stage('Test xmake build') { steps { runningOn(env.NODE_NAME) @@ -161,6 +146,21 @@ pipeline { } } } + stage('Run doc python') { + steps { + runningOn(env.NODE_NAME) + dir("${REPO}") { + withTools(params.TOOLS_VERSION) { + withVenv { + dir("doc/python") { + sh "python -m doc_asrc.py" + archiveArtifacts artifacts: "_build/*", allowEmptyArchive: true + } + } + } + } + } + } } post { cleanup { diff --git a/doc/python/asrc_utils.py b/doc/python/asrc_utils.py index 2381ebe8..388b5f14 100644 --- a/doc/python/asrc_utils.py +++ b/doc/python/asrc_utils.py @@ -1,3 +1,5 @@ +# Copyright 2023 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. from subprocess import Popen, PIPE from subprocess import run as srun import os diff --git a/doc/python/doc_asrc.py b/doc/python/doc_asrc.py index d30d01ad..312b702d 100644 --- a/doc/python/doc_asrc.py +++ b/doc/python/doc_asrc.py @@ -1,3 +1,5 @@ +# Copyright 2023 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. from asrc_utils import asrc_util import numpy as np import matplotlib.pyplot as plt From 3140cd88826e4d98e361155166373c210280562f Mon Sep 17 00:00:00 2001 From: Shuchita Khare Date: Fri, 22 Sep 2023 12:43:42 +0100 Subject: [PATCH 09/15] artifcats --- Jenkinsfile | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 1d26de10..8605614c 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -94,6 +94,21 @@ pipeline { } } } + stage('Run doc python') { + steps { + runningOn(env.NODE_NAME) + dir("${REPO}") { + withTools(params.TOOLS_VERSION) { + withVenv { + dir("doc/python") { + sh "python -m doc_asrc.py" + archiveArtifacts artifacts: "_build", allowEmptyArchive: true + } + } + } + } + } + } stage('Tests XS2') { steps { runningOn(env.NODE_NAME) @@ -146,21 +161,6 @@ pipeline { } } } - stage('Run doc python') { - steps { - runningOn(env.NODE_NAME) - dir("${REPO}") { - withTools(params.TOOLS_VERSION) { - withVenv { - dir("doc/python") { - sh "python -m doc_asrc.py" - archiveArtifacts artifacts: "_build/*", allowEmptyArchive: true - } - } - } - } - } - } } post { cleanup { From 41b993656fb338329aaa2eacae4279a6cfd4b321 Mon Sep 17 00:00:00 2001 From: Shuchita Khare Date: Fri, 22 Sep 2023 12:57:34 +0100 Subject: [PATCH 10/15] mkdir -p --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 8605614c..df30cd8b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -115,7 +115,7 @@ pipeline { dir("${REPO}") { withTools(params.TOOLS_VERSION) { withVenv { - sh 'mkdir build' + sh 'mkdir -p build' dir("build") { sh 'rm -rf' sh 'cmake --toolchain ../xmos_cmake_toolchain/xs2a.cmake ..' From d6c6fc2ccf512853aca59c32ab347bae21eb7a1f Mon Sep 17 00:00:00 2001 From: Shuchita Khare Date: Fri, 22 Sep 2023 13:19:04 +0100 Subject: [PATCH 11/15] mkdir -p --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index df30cd8b..1a002f3d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -117,7 +117,7 @@ pipeline { withVenv { sh 'mkdir -p build' dir("build") { - sh 'rm -rf' + sh 'rm CMakeCache.txt' sh 'cmake --toolchain ../xmos_cmake_toolchain/xs2a.cmake ..' sh 'make test_ds3_voice test_us3_voice test_unity_gain_voice -j' } @@ -125,7 +125,7 @@ pipeline { localRunPytest('-n auto -k "xs2" -vv') } dir("build") { - sh 'rm -rf' // Cleanup XS2 cmake cache for next stage + sh 'rm CMakeCache.txt' // Cleanup XS2 cmake cache for next stage } } } From 0ff90a175f8cedc0c5574a38a679600d73677517 Mon Sep 17 00:00:00 2001 From: Shuchita Khare Date: Fri, 22 Sep 2023 13:44:36 +0100 Subject: [PATCH 12/15] Jenkinsfile --- Jenkinsfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 1a002f3d..13535d93 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -102,7 +102,8 @@ pipeline { withVenv { dir("doc/python") { sh "python -m doc_asrc.py" - archiveArtifacts artifacts: "_build", allowEmptyArchive: true + sh "ls -lrt _build/*" + archiveArtifacts artifacts: "_build/**/*", allowEmptyArchive: true } } } From 57a66cfefc959736fdc5ca187739a98371fade0e Mon Sep 17 00:00:00 2001 From: Shuchita Khare Date: Fri, 22 Sep 2023 14:10:18 +0100 Subject: [PATCH 13/15] Jenkinsfile --- Jenkinsfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 13535d93..4528a37b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -103,7 +103,8 @@ pipeline { dir("doc/python") { sh "python -m doc_asrc.py" sh "ls -lrt _build/*" - archiveArtifacts artifacts: "_build/**/*", allowEmptyArchive: true + archiveArtifacts artifacts: "_build/output/*", allowEmptyArchive: true + archiveArtifacts artifacts: "_build/rst/*", allowEmptyArchive: true } } } From c539d5bbc5aea8912774af687744e02452dc115d Mon Sep 17 00:00:00 2001 From: Shuchita Khare Date: Thu, 28 Sep 2023 11:13:38 +0100 Subject: [PATCH 14/15] Archive snr plots as a zip --- Jenkinsfile | 5 ++--- doc/python/README.txt | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 4528a37b..e501e57e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -102,9 +102,8 @@ pipeline { withVenv { dir("doc/python") { sh "python -m doc_asrc.py" - sh "ls -lrt _build/*" - archiveArtifacts artifacts: "_build/output/*", allowEmptyArchive: true - archiveArtifacts artifacts: "_build/rst/*", allowEmptyArchive: true + sh "zip -r snr_build.zip _build" + archiveArtifacts artifacts: "snr_build.zip" } } } diff --git a/doc/python/README.txt b/doc/python/README.txt index e7ea1d1f..d425bebf 100644 --- a/doc/python/README.txt +++ b/doc/python/README.txt @@ -10,7 +10,7 @@ We scrape the logs to extract MIPS/Utilization where supported. Tests are run for all suported input and output sample freqencies, and for worst case frequency deviation. Note that not all modules support all input scenarios - these are ignored. -The result of this script is the generation an rst file which is inclorporated into the documentation, +The result of this script is the generation an rst file which is included into the documentation, incorporating a full set of charts and summary table. From 544d3c8444562f9319a7ded47f9975008fbf9046 Mon Sep 17 00:00:00 2001 From: Shuchita Khare Date: Thu, 28 Sep 2023 11:30:35 +0100 Subject: [PATCH 15/15] Cleanup --- doc/python/asrc_utils.py | 22 +++++++++++----------- doc/python/doc_asrc.py | 16 ++++++++-------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/doc/python/asrc_utils.py b/doc/python/asrc_utils.py index 388b5f14..5adcef85 100644 --- a/doc/python/asrc_utils.py +++ b/doc/python/asrc_utils.py @@ -39,16 +39,16 @@ def __init__(self, path, relative, fftPoints, ch0_bins, ch1_bins): # definitions of sample ratea self.allRates =["16", "32", "44", "48", "88", "96", "176", "192"] - self.srcRates =["44", "48", "88", "96", "176", "192"] # supported by the aasrc and ssrc + self.srcRates =["44", "48", "88", "96", "176", "192"] # supported by the asrc and ssrc self.factors = {"44":0, "48":1, "88":2, "96":3, "176":4, "192":5} # look up to convert rate to the filter bank id needed by the ASRC - self.sampleRates = {"16":16000, "32":32000, "44":44100, "48":48000, "88":88200, "96":96000, "176":176400, "192":192000} #convinience to save typing out sample rates in full + self.sampleRates = {"16":16000, "32":32000, "44":44100, "48":48000, "88":88200, "96":96000, "176":176400, "192":192000} #convenience to save typing out sample rates in full self.sigMax = {"16":7300, "32":14600, "44":20200, "48":21800, "88":40000, "96":47990, "176":80000, "192":85000} # upper limit on input freq for given sample rate - self.numSamples = {} #populated later based on oprate to ensure sufficient samples for fft + self.numSamples = {} #populated later based on op-rate to ensure sufficient samples for fft self.ignoreSamples = 2000 #worst case for high up-sampling self.fftPoints = fftPoints - self.sig = [ch0_bins, ch1_bins] # defines the test signals for each of 2 channels, each is a list so more than one tine can be genertaed and combined + self.sig = [ch0_bins, ch1_bins] # defines the test signals for each of 2 channels, each is a list so more than one tone can be generated and combined self.log=[] self.plots=[] mp.prec = 1024 @@ -77,7 +77,7 @@ def build_model_exe(self, target): # populates channel 1 with mutiple tones, spaced logrithmically, most dense at higher freq, and ch0 is always 10% in # set in 'sigMax' beyond which reflections degrade the SNR. def updateSig(self, ipRate, ch0_bins, ch1_bins, autoFill=False): - # this manipultaes the fft size to attempt to force any reflections around the ip sample rate nyquist also ending up + # this manipulates the fft size to attempt to force any reflections around the ip sample rate nyquist also ending up # in an integer fft bin self.fftPoints = int(10000 *(self.sampleRates[ipRate] * self.fDev) / self.sampleRates[self.opRate]) print(f"Over-wrote the fft size to {self.fftPoints} points. ipRate = {ipRate}") @@ -167,7 +167,7 @@ def listFactors(self, x): def makeSignal(self, fsamp, fsig, asig, l, ferr=1.0): # populates ipdata with sinewave - # fsamp: sample rate, as abbevaited string (e.g., "48", interpreted as 48,000Hz) + # fsamp: sample rate, as abbreviated string (e.g., "48", interpreted as 48,000Hz) # fsig: signal freq, in Hz # asig: amplitude, in range 0-1 # l: number of samples @@ -201,7 +201,7 @@ def makeInputFiles(self, test_rates, ferr=1.0): chan=[] chanSigData=[] i=0 - for ch in self.sig: #itterates over channels + for ch in self.sig: #iterates over channels sig=[] for s in ch: f = mp.fmul(realRate ,mp.fdiv(s, self.fftPoints)) @@ -226,7 +226,7 @@ def makeInputFiles(self, test_rates, ferr=1.0): def doFFT(self, data, window=False): - # convinient way to select between fft styles. Note that the periodic one will need a lot more samples, so + # convenient way to select between fft styles. Note that the periodic one will need a lot more samples, so # use window=True for debuging. if window: return self.winFFT(data) @@ -256,7 +256,7 @@ def rawFFT(self, data): def plotFFT(self, xydata, combine=False, title=None, subtitles=None, log=True, text=None): # Plot style setup for the FFT plots, labels x asis as KHz and y axis as dB. # xydata: an array of datasets, each dataset is a 3 element array containing the xdata array and ydata_dB array and ydata_lin array - # combine: (optional) forces all plots ontot the same chart, otherwise it will create a grid of plots + # combine: (optional) forces all plots onto the same chart, otherwise it will create a grid of plots # title: (optional) the chart title at the top, common to any subplots # subtitles: (optional) an array of subtitles, used for each subplot. # log: plots the dB data in the input @@ -299,12 +299,12 @@ def plotFFT(self, xydata, combine=False, title=None, subtitles=None, log=True, t def opFileName(self, fin, label, fDev, opRate): - #a simple utility to convert an input file pat+name to an output version, appending the frequency deviation + #a simple utility to convert an input file path+name to an output version, appending the frequency deviation newName = fin.replace(self.inputFolder, self.outputFolder).replace(".dat", "_fso{}_fdev{:f}_{}.dat".format(opRate, fDev, label)) return newName def exFileName(self, fin, label, fDev, opRate): - #a simple utility to convert an input file pat+name to an output version, appending the frequency deviation + #a simple utility to convert an input file path+name to an output version, appending the frequency deviation newName = fin.replace(self.inputFolder, self.expectedFolder).replace(".dat", "_fso{}_fdev{:f}_{}.dat".format(opRate, fDev, label)) return newName diff --git a/doc/python/doc_asrc.py b/doc/python/doc_asrc.py index 312b702d..bb6b564a 100644 --- a/doc/python/doc_asrc.py +++ b/doc/python/doc_asrc.py @@ -9,11 +9,11 @@ # OVERVIEW # # This script generates a set of test files for the supported input and output rates. -# It passes these through the golden reference "C" models and xsim, and -# Plots the FFTs, extracts the SNR, THD, and extracts MIPS estimate. -# All this info is annotated on the Plot which is saved to the otput folder. +# It passes these through the golden reference "C" models, and +# Plots the FFTs, and extracts the SNR and THD. +# All this info is annotated on the Plot which is saved to the output folder. # -# Note that both the C model is a dual-channel iplementations, so we pass them a pair of source files +# Note that both the C model is a dual-channel implementations, so we pass them a pair of source files # and since this is ASRC, we vary the frequency deviation parameter fDev. # But, OS3 and DS3 are single channel apps - which is dealt with! # @@ -42,7 +42,7 @@ no_results = True # the RST contains headings for all fDev, opRate and ipRate - so if this is an unsupported combination, add a warning to thr RST. for ipRate in U.allRates:# for each of the possible input sample rates # opportunity to choose different test freq, which also sets a safer fft size - U.updateSig(ipRate, [int(fftPoints/5)], [int(fftPoints/4),int(fftPoints/6)], True) # specified in temrs of FFT o/p bin, when flag set true it auto-fills a logrithmic range for ch1 and 5% in for ch0 + U.updateSig(ipRate, [int(fftPoints/5)], [int(fftPoints/4),int(fftPoints/6)], True) # specified in terms of FFT o/p bin, when flag set true it auto-fills a logarithmic range for ch1 and 5% in for ch0 # Make a set of input files based on range of sample rates supported ipFiles, sig = U.makeInputFiles([ipRate], FERR) # makes the signals, saves data as files and returns some info about them @@ -50,7 +50,7 @@ # Put the input data through the golden "C" emulators opFiles, simLog = U.run_c_model(ipFiles, opRate, 4, fDev) - # Itterate over all the input data files and channels + # Iterate over all the input data files and channels for channels in ipFiles: # For each input sata set there will be an output one for each channel for channel in channels[0:2]: print(channel) @@ -82,9 +82,9 @@ #Plot the results - plotFile = U.plotFFT(U.plot_data, combine=False, title=U.makePlotTitle(simLog, channel), subtitles=U.plot_label, log=True, text=U.plot_text) # plots a grid of charts, one per cimulation model + plotFile = U.plotFFT(U.plot_data, combine=False, title=U.makePlotTitle(simLog, channel), subtitles=U.plot_label, log=True, text=U.plot_text) # plots a grid of charts, one per simulation model U.makeRST(plotFile, ipRate, opRate, fDev, simLog[channel].keys()) # add this plot to a list to save as an RST file later - U.resetPlotInfo() # otherwise the next itteration of rates etc will add more plots to this instead of starting a new plot. + U.resetPlotInfo() # otherwise the next iteration of rates etc will add more plots to this instead of starting a new plot. if no_results: U.addRSTText("No SRC available for this scenario.")