Anybody know of a straightforward way to analyse the sound recordings from SAQ transmissions and plot how the frequency varies over time? π
Anybody know of a straightforward way to analyse the sound recordings from SAQ transmissions and plot how the frequency varies over time? π
@MW1CFN Are you wanting to do something like this: https://academo.org/demos/spectrum-analyzer/#google_vignette
?
@MW1CFN Got it. The link I shared demonstrated that, in a way. That piece of software took an audio file and did the spectral decomposition (FT-type processing), showing the spectrum in time.
Are you looking for more of a 'waterfall' type display where you can infer the carrier frequency and the bandwidth of the signal at any one time?
@MW1CFN So. you want to determine at each time (say, second) the carrier frequency. Then you want to plot those time-frequency measurements, expecting to see a graph that is approximately horizontal. Am I understanding you correctly?
If so, then this is beyond my easy reach. Someone with more signal processing experience could probably point you to a tool. Hopefully our back-and-forth here will be helpful to other helpful souls.
@MW1CFN If you email me a sample audio file, I might be able to find some time to chew on it this weekend. No promises, but I learn by doing and will be happy to share with you what I learn.
Can I βprivate mentionβ you my email? Or, if you think the file will be too big for email, we can find another way to transfer the data.
@jasonemiller That's very kind of you. I've uploaded the file to Soundcloud. The signal doesn't appear for a minute or two due to the need to get a noise reference beforehand:
SAQ VLF transmission, first of two on 30/06/2024
@MW1CFN OK. This is what I can up with after a morning and afternoon of trying to remember python.
Save this as a python script in the same directory as your recording (which is called 'recording.mp3' in this script) and run.
The standard caveats about libraries and stuff applies.
WARNING: this is a total hack. At the most best, it will suggest a way for you to get what you want.
========
```#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Jul 06 11:08am
@author: Jason Miller (KM6PSZ)
"""
# basic idea from https://www.kaggle.com/code/vishnurapps/dummies-guide-to-audio-analysis
import librosa
import matplotlib.pyplot as plt
import warnings
import numpy as np
warnings.filterwarnings('ignore')
# load audio file as data set
# NOTE: loads file as a time series
# here
# y is the frequency value
# sr is the sampling rate from which we can inferr the sample times
yd, sr = librosa.load("recording.mp3")
# timestep
deltat=1/sr
#number of samples in the audio file
L=len(yd)
# array of sampling times
time = [ deltat*i for i in range( L )]
# array of sampling times for the zero crossing vector
time2=[time[i] for i in range (L-1)]
# grab a portion of the audio file for analysis (to keep the size managable as I
# work to pull together some code)
#
# Here we take the portion of data starting from the 20,000 index and going to the 20400 index.
time1=np.split(time, [20000,20400],axis=0)
yd1=np.split(yd, [20000,20400],axis=0)
# Here we determine which of the entries are zero crossings
logicyd=librosa.zero_crossings(yd)
logicyd1=np.split(logicyd, [20000,20400],axis=0)
# here we get the indices of the zero-crossings
zeros=np.nonzero(logicyd)
times=[time[i] for i in zeros[0]]
dtimes=2*np.diff(times)
yds=[yd[i] for i in zeros[0]]
yds=[yds[i] for i in range(len(yds)-1)]
freq=[1/dtimes[i] for i in range( len(dtimes) )]
#freqdata=librosa.util.stack([time2, freq1], axis=-1)
print(freq)
#
# # visualize the data - note that all the values are binary (either 0 or 1)
# print(logicyd1)
#
# # what follow is from
# # https://librosa.org/doc/latest/generated/librosa.zero_crossings.html#librosa.zero_crossings
#
# # here is the time-amplitude data
# data=librosa.util.stack([time1, yd1], axis=-1)
# # here are the times of the zero crossings
# time2=time1[zeros1]
# # here are the time-amplitude pairs at the zero crossings
# yd2 = data[zeros1]
# # because time for one oscillation is twice the time between two zero crossings
# # the frequency is the inverse of this
# deltazc=2*diff(time2)
# freq1=[1/deltazc[i] for i in range( len(deltazc) )]
# freqdata=librosa.util.stack([time2, freq1], axis=-1)
#
#
plt.figure(figsize=(20, 4))
plt.plot(freq)
plt.grid()
plt.title('frequencies')
plt.xlabel('time')
plt.ylabel('text')
plt.show()
```
EDIT: cleaned up the script a little but, and included the modified script on my post (not in Mastodon).
@MW1CFN Wrote up a quick post on this 'shitty first draft' script. People can find it here and make their own improvements.
If an improved script is shared, I'll post it on my obscure site that nobody looks and an Google probably doesn't know how to index.
http://www.jasonemiller.org/2024/07/06/CW-and-zero-crossing.html