Skip to content

Commit

Permalink
Merge pull request #45 from ed-xmos/feature/sdm
Browse files Browse the repository at this point in the history
Refactor python model and initial SDM model
  • Loading branch information
ed-xmos authored Nov 23, 2023
2 parents bda4a65 + b3baa28 commit 69e67bd
Show file tree
Hide file tree
Showing 12 changed files with 1,238 additions and 682 deletions.
61 changes: 43 additions & 18 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,54 @@ pipeline {
}

stages {
stage('ci') {
stage('Build and tests') {
agent {
label 'linux&&64'
}
steps {
sh 'mkdir lib_sw_pll'
// source checks require the directory
// name to be the same as the repo name
dir('lib_sw_pll') {
// checkout repo
checkout scm
installPipfile(false)
withVenv {
withTools(params.TOOLS_VERSION) {
sh './tools/ci/checkout-submodules.sh'
catchError {
sh './tools/ci/do-ci.sh'
stages{
stage('Checkout'){
steps {
sh 'mkdir lib_sw_pll'
// source checks require the directory
// name to be the same as the repo name
dir('lib_sw_pll') {
// checkout repo
checkout scm
installPipfile(false)
withVenv {
withTools(params.TOOLS_VERSION) {
sh './tools/ci/checkout-submodules.sh'
}
}
zip archive: true, zipFile: "build.zip", dir: "build"
zip archive: true, zipFile: "tests.zip", dir: "tests/bin"
archiveArtifacts artifacts: "tests/bin/timing-report.txt", allowEmptyArchive: false
}
}
}
stage('Build'){
steps {
dir('lib_sw_pll') {
withVenv {
withTools(params.TOOLS_VERSION) {
sh './tools/ci/do-ci-build.sh'
}
}
}
}
}
stage('Test'){
steps {
dir('lib_sw_pll') {
withVenv {
withTools(params.TOOLS_VERSION) {
catchError {
sh './tools/ci/do-ci-tests.sh'
}
zip archive: true, zipFile: "build.zip", dir: "build"
zip archive: true, zipFile: "tests.zip", dir: "tests/bin"
archiveArtifacts artifacts: "tests/bin/timing-report.txt", allowEmptyArchive: false

junit 'tests/results.xml'
junit 'tests/results.xml'
}
}
}
}
}
Expand Down
88 changes: 88 additions & 0 deletions python/sw_pll/analysis_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Copyright 2023 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.

import matplotlib.pyplot as plt
import numpy as np
import soundfile
from scipy.io import wavfile # soundfile has some issues writing high Fs files

class audio_modulator:
def __init__(self, duration_s, sample_rate=48000, test_tone_hz=1000):
self.sample_rate = sample_rate
self.test_tone_hz = test_tone_hz

self.modulator = np.full(int(duration_s * sample_rate), test_tone_hz, dtype=np.float64)

def apply_frequency_deviation(self, start_s, end_s, delta_freq):
start_idx = int(start_s * self.sample_rate)
end_idx = int(end_s * self.sample_rate)
self.modulator[start_idx:end_idx] += delta_freq


def modulate_waveform(self):
# Now create the frequency modulated waveform
# this is designed to accumulate the phase so doesn't see discontinuities
# https://dsp.stackexchange.com/questions/80768/fsk-modulation-with-python
delta_phi = self.modulator * np.pi / (self.sample_rate / 2.0)
phi = np.cumsum(delta_phi)
self.waveform = np.sin(phi)

def save_modulated_wav(self, filename):
integer_output = np.int16(self.waveform * 32767)
# soundfile.write(filename, integer_output, int(self.sample_rate)) # This struggles with >768ksps
wavfile.write(filename, int(self.sample_rate), integer_output)

def plot_modulated_fft(self, filename, skip_s=None):
start_x = 0 if skip_s is None else int(skip_s * self.sample_rate) // 2 * 2
waveform = self.waveform[start_x:]

xf = np.linspace(0.0, 1.0/(2.0/self.sample_rate), waveform.size // 2)
N = xf.size
window = np.kaiser(N*2, 14)
waveform = waveform * window
yf = np.fft.fft(waveform)
fig, ax = plt.subplots()

# Plot a zoom in on the test
tone_idx = int(self.test_tone_hz / (self.sample_rate / 2) * N)
num_side_bins = 50
yf = 20 * np.log10(np.abs(yf) / N)
# ax.plot(xf[tone_idx - num_side_bins:tone_idx + num_side_bins], yf[tone_idx - num_side_bins:tone_idx + num_side_bins], marker='.')

# Plot the whole frequncy range from DC to nyquist
ax.plot(xf[:N], yf[:N], marker='.')
ax.set_xscale("log")
plt.xlim((10**1, 10**5))
plt.ylim((-200, 0))
plt.savefig(filename, dpi=150)

def load_wav(self, filename):
"""
Used for testing only - load a wav into self.waveform
"""
self.waveform, self.sample_rate = soundfile.read(filename)


if __name__ == '__main__':
"""
This module is not intended to be run directly. This is here for internal testing only.
"""
if 0:
test_len = 10
audio = audio_modulator(test_len)
for time_s in range(test_len):
modulation_hz = 10 * (time_s - (test_len) / 2)
audio.apply_frequency_deviation(time_s, time_s + 1, modulation_hz)

audio.modulate_waveform()
audio.save_modulated_wav("modulated.wav")
audio.plot_modulated_fft("modulated_fft.png")

else:
audio = audio_modulator(1)
audio.load_wav("modulated_tone_1000Hz_sd_ds.wav")
# audio = audio_modulator(1, sample_rate=3072000)
# audio.modulate_waveform()
audio.plot_modulated_fft("modulated_tone_1000Hz_sd_ds.png")
# audio.save_modulated_wav("modulated.wav")

Loading

0 comments on commit 69e67bd

Please sign in to comment.