Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve spectrogram time alignment #227

Closed
wants to merge 1 commit into from

Conversation

daniestevez
Copy link
Contributor

Currently, SpectrogramPlot::getLine() uses the sample parameter to determine the index of the first sample that is used to calculate the FFT for a particular spectrogram line. Since the FFT is in some sense an average of fftSize samples, this causes features (such as the start and end of packet bursts) to appear somewhat sooner in the spectrogram compared to their actual locations in the IQ file.

This improves the time alignment of the spectrogram plot by making sample refer to the middle sample of the FFT (so the samples used to compute the FFT start at sample - fftSize / 2.

For the beginning of the file we need to make an exception, because if we try to fetch samples before the beginning of the file, then inputSource->getSamples() returns nullptr. SpectrogramPlot::getLine() handles this gracefully, but an ugly red bar appears at the beginning of the file when the FFT size and zoom are large. To solve this, we cheat and force the FFT to start at the beginning of the file. To be more precise we could pad the beginning with zeros instead.


To test this, I'm using the following script to generate a 100 ms SigMF file at 1 Msps with an AWGN noise floor and a chunk of much stronger AWGN between 10 and 11 ms (which simulates a digital communications packet). A SigMF annotation marks this stronger noise chunk.

#!/usr/bin/env python3

import datetime

import numpy as np
import sigmf
from sigmf import SigMFFile

fs = 1000000
x = np.zeros(int(100e-3 * fs), 'complex64')
a = int(10e-3*fs)
b = int(11e-3*fs)
x[:] = 0.0001 * (np.random.randn(x.size) + 1j * np.random.randn(x.size))
x[a:b] = 0.1 * (np.random.randn(b - a) + 1j * np.random.randn(b - a))
data_file = 'test-file.sigmf-data'
x.tofile(data_file)

meta = SigMFFile(
    data_file=data_file,
    global_info = {
        SigMFFile.DATATYPE_KEY: sigmf.utils.get_data_type_str(x),
        SigMFFile.SAMPLE_RATE_KEY: fs,
        SigMFFile.VERSION_KEY: '1.0.0',
    }
)

f0 = 100_000_000
meta.add_capture(0, metadata={
    SigMFFile.FREQUENCY_KEY: f0,
    SigMFFile.DATETIME_KEY: datetime.datetime.utcnow().isoformat()+'Z',
})

meta.add_annotation(a, b-a, metadata = {
    SigMFFile.FLO_KEY: f0 - fs / 2,
    SigMFFile.FHI_KEY: f0 + fs / 2,
    SigMFFile.COMMENT_KEY: 'signal',
})

meta.tofile('test-file.sigmf-meta')

The main branch shows the following. The packet appears to begin before it should.

inspectrum-main

This PR shows the following. The start and end of the packet "bleed over" outside of the annotation box, but this is inevitable when using FFTs. I think that this is a better representation of the signal.

inspectrum-pr

Currently, SpectrogramPlot::getLine() uses the sample parameter to
determine the index of the first sample that is used to calculate
the FFT for a particular spectrogram line. Since the FFT is in some
sense an average of fftSize samples, this causes features (such as
the start and end of packet bursts) to appear somewhat sooner in
the spectrogram compared to their actual locations in the IQ file.

This improves the time alignment of the spectrogram plot by making
sample refer to the middle sample of the FFT (so the samples used
to compute the FFT start at sample - fftSize / 2.

For the beginning of the file we need to make an exception, because
if we try to fetch samples before the beginning of the file, then
inputSource->getSamples() returns nullptr. SpectrogramPlot::getLine()
handles this gracefully, but an ugly red bar appears at the beginning
of the file when the FFT size and zoom are large. To solve this, we
cheat and force the FFT to start at the beginning of the file. To
be more precise we could pad the beginning with zeros instead.

Signed-off-by: Daniel Estévez <daniel@destevez.net>
@miek
Copy link
Owner

miek commented Oct 16, 2023

This is great, thanks! I've merged it manually with some whitespace fixes.

@miek miek closed this Oct 16, 2023
@jacobagilbert
Copy link

Already merged but wanted to say thanks @daniestevez 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants