Skip to content

fix #472 - #481

Closed
tapyu wants to merge 87 commits into
TorchDSP:mainfrom
tapyu:fix/break-metadata
Closed

tapyu wants to merge 87 commits into
TorchDSP:mainfrom
tapyu:fix/break-metadata

Conversation

@tapyu

@tapyu tapyu commented Aug 25, 2026 •

Copy link
Copy Markdown

Problem

Signal metadata written to the HDF5 dataset (_lower_frequency/_upper_frequency) can end up mirrored to the wrong side of 0 Hz relative to the signal's actual center_freq — for a subset of signals, the bounding box lands on the opposite side of baseband from where the signal's energy actually is.

Reproduced with a minimal FM dataset (Impairments(level=0), 200 signals): 140/200 (70%) of emitter records had _lower_frequency/_upper_frequency values inconsistent with their own center_freq — specifically, avg(_lower_frequency, _upper_frequency) ≈ -center_freq.

Root cause

_lower_frequency/_upper_frequency are cached side effects of reading the upper_freq/lower_freq properties (signals/signal_types.py) — they get computed from center_freq/bandwidth and written back into the metadata dict at read time, but nothing invalidates that cache when center_freq/bandwidth are mutated afterward via direct item assignment.

In practice:

  • datasets/dataset_utils.py::frequency_shift_signal is the only call site that ever reads .upper_freq/.lower_freq (populating the cache), right after the initial random RF placement — before the anti-aliasing-filter branch, which can then overwrite center_freq/bandwidth again without refreshing the cache.
  • ChannelSwap, SpectralInversion, TimeReversal, and Doppler (transforms/transforms.py) all mutate signal["center_freq"] (and, for Doppler, bandwidth) directly — including on component_signals — without touching the cache either.
  • At write time, utils/file_handlers/hdf5.py::populate_hdf5_group_with_metadata serializes every raw key in the metadata dict verbatim, so whatever stale value happens to be cached is what lands on disk.

Fix

  • signals/signal_types.py: add SignalMetadataObject.sync_frequency_bounds(), which force-refreshes _lower_frequency/_upper_frequency from the current center_freq/bandwidth.
  • datasets/dataset_utils.py: call it in frequency_shift_signal after the anti-aliasing-filter branch.
  • transforms/transforms.py: call it in ChannelSwap, SpectralInversion, TimeReversal, and Doppler, for both the signal and each of its component_signals, right after they mutate center_freq/bandwidth.
  • utils/file_handlers/hdf5.py: as a backstop, force a refresh in populate_hdf5_group_with_metadata immediately before serializing, so the written values are correct regardless of which code path touched center_freq/bandwidth last.

Testing

Regenerated a 200-signal FM dataset (Impairments(level=0)) before and after the fix and checked every emitter's _lower_frequency/_upper_frequency against center_freq/bandwidth for consistency:

mismatched records
before 131 / 200 (70%)
after 0 / 200

Fixes #472

pvallance and others added 30 commits June 5, 2024 11:39
…obe levesl from nominally -60 dB to about -90 dB

utils/dsp.py convolve now discards transition regions from filter output
randomization rework: now allows parameters to be randomized internally per-process, therefore each signal generated is randomized rather than groups of signals having consistent values
better implementation of FSK modulation using polyphase filter bank
	new file:   image_datasets/__init__.py
	new file:   image_datasets/dataset_generation.py
	new file:   image_datasets/datasets/__init__.py
	new file:   image_datasets/datasets/composites.py
	new file:   image_datasets/datasets/dataset_generation.py
	new file:   image_datasets/datasets/file_loading_datasets.py
	new file:   image_datasets/datasets/synthetic_signals.py
	new file:   image_datasets/generate_dataset.py
	new file:   image_datasets/transforms/__init__.py
	new file:   image_datasets/transforms/impairments.py
	modified:   CONTRIBUTING.md
	modified:   DISCLAIMER.md
	modified:   Dockerfile
	modified:   LICENSE.md
	modified:   README.md
	modified:   docs/Makefile
	modified:   docs/README.md
	modified:   docs/conf.py
	modified:   docs/datasets.rst
	modified:   docs/docs-requirements.txt
	modified:   docs/index.rst
	modified:   docs/logo.png
	modified:   docs/models.rst
	modified:   docs/torchsig_logo_white_dodgerblue.png
	modified:   docs/transforms.rst
	modified:   docs/utils.rst
	modified:   examples/00_example_sig53_dataset.ipynb
	modified:   examples/01_example_modulations_dataset.ipynb
	modified:   examples/02_example_sig53_classifier.ipynb
	modified:   examples/03_example_widebandsig53_dataset.ipynb
	modified:   examples/04_example_wideband_modulations_dataset.ipynb
	modified:   examples/05_example_wideband_detector.ipynb
	modified:   examples/README.md
	modified:   examples/__init__.py
	modified:   examples/figures/.gitkeep
	modified:   examples/sig53/.gitkeep
	modified:   examples/wideband_sig53/.gitkeep
	modified:   pyproject.toml
	modified:   scripts/generate_sig53.py
	modified:   scripts/generate_wideband_sig53.py
	modified:   scripts/train_sig53.py
	modified:   setup.py
	modified:   tests/figures/.gitkeep
	modified:   tests/test_datasets_sig53.py
	modified:   tests/test_models.py
	modified:   tests/test_modulation_benchmark.py
	modified:   tests/test_modulation_figures.py
	modified:   tests/test_transforms.py
	modified:   tests/test_transforms_figures.py
	modified:   tests/test_wideband_benchmark.py
	modified:   tests/test_wideband_figures.py
	modified:   tests/test_writer.py
	modified:   torchsig/models/iq_models/efficientnet/LICENSE.md
	modified:   torchsig/models/iq_models/efficientnet/README.md
	modified:   torchsig/models/iq_models/xcit/LICENSE.md
	modified:   torchsig/models/iq_models/xcit/README.md
	modified:   torchsig/models/spectrogram_models/detr/LICENSE.md
	modified:   torchsig/models/spectrogram_models/detr/README.md
	new file:   generate_dataset_example.ipynb
	modified:   torchsig/__init__.py
	modified:   torchsig/datasets/sig53.py
	modified:   torchsig/datasets/wideband.py
	modified:   torchsig/image_datasets/datasets/composites.py
	modified:   torchsig/image_datasets/generate_dataset.py
	modified:   torchsig/image_datasets/generate_dataset_example.ipynb
	modified:   torchsig/utils/writer.py
	new file:   ../../docker-compose.yml
	new file:   iq_models/efficientnet/efficientnet1d.py
	new file:   iq_models/xcit/xcit1d.py
	new file:   model_utils/general_layers.py
	new file:   model_utils/layer_tools.py
	new file:   model_utils/model_utils_1d/conversions_to_1d.py
	new file:   model_utils/model_utils_1d/iq_sampling.py
	new file:   model_utils/model_utils_1d/layers_1d.py
	new file:   model_utils/simple_models.py
	new file:   efficientnet/__init__.py
	new file:   efficientnet/efficientnet2d.py
	modified:   torchsig/datasets/synthetic.py
	modified:   torchsig/datasets/wideband.py
	modified:   torchsig/models/model_utils/layer_tools.py
	modified:   torchsig/models/model_utils/model_utils_1d/conversions_to_1d.py
	modified:   torchsig/transforms/transforms.py
	modified:   torchsig/utils/dsp.py
	modified:   torchsig/utils/visualize.py
	modified:   torchsig/utils/writer.py
… pytorch and timm

	modified:   Dockerfile
	modified:   docker-compose.yml
	modified:   pyproject.toml
	modified:   Dockerfile
	modified:   scripts/generate_sig53.py
	modified:   torchsig/utils/writer.py
…to avoid torch Dataloader from converting the labels to tensors.

	modified:   scripts/generate_sig53.py
	modified:   scripts/generate_wideband_sig53.py
	modified:   scripts/train_sig53.py
	modified:   torchsig/datasets/modulations.py
	modified:   torchsig/datasets/sig53.py
	modified:   torchsig/datasets/synthetic.py
	modified:   torchsig/datasets/wideband.py
	modified:   torchsig/datasets/wideband_sig53.py
	modified:   torchsig/transforms/target_transforms.py
	modified:   torchsig/transforms/transforms.py
	modified:   torchsig/utils/visualize.py
	modified:   torchsig/utils/writer.py
	modified:   ../Dockerfile
	modified:   ../README.md
	modified:   ../examples/00_example_sig53_dataset.ipynb
	modified:   ../examples/01_example_modulations_dataset.ipynb
	modified:   ../examples/02_example_sig53_classifier.ipynb
	modified:   ../examples/03_example_widebandsig53_dataset.ipynb
	modified:   ../examples/04_example_wideband_modulations_dataset.ipynb
	new file:   ../examples/05_example_wideband_yolo_to_disk.ipynb
	new file:   ../examples/06_example_wideband_yolo.ipynb
	new file:   ../examples/07_example_classify_yolo.ipynb
	modified:   ../examples/README.md
	new file:   ../examples/classify.yaml
	new file:   ../examples/wbdata.yaml
	new file:   ../examples/wbsig53.yaml
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/CMakeLists.txt
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/Dockerfile
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/MANIFEST.md
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/apps/CMakeLists.txt
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/build_docker.sh
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/cmake/Modules/CMakeParseArgumentsCopy.cmake
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/cmake/Modules/gnuradio-spectrumDetectConfig.cmake
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/cmake/Modules/targetConfig.cmake.in
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/cmake/cmake_uninstall.cmake.in
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/docs/CMakeLists.txt
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/docs/README.spectrumDetect
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/docs/doxygen/CMakeLists.txt
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/docs/doxygen/Doxyfile.in
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/docs/doxygen/doxyxml/__init__.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/docs/doxygen/doxyxml/base.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/docs/doxygen/doxyxml/doxyindex.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/docs/doxygen/doxyxml/generated/__init__.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/docs/doxygen/doxyxml/generated/compound.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/docs/doxygen/doxyxml/generated/compoundsuper.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/docs/doxygen/doxyxml/generated/index.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/docs/doxygen/doxyxml/generated/indexsuper.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/docs/doxygen/doxyxml/text.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/docs/doxygen/other/doxypy.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/docs/doxygen/other/group_defs.dox
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/docs/doxygen/other/main_page.dox
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/docs/doxygen/pydoc_macros.h
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/docs/doxygen/update_pydoc.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/examples/example.grc
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/examples/generate.sh
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/examples/generate_wideband_sig53.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/examples/make_wbsig53_yolo_dataset_impaired_train.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/examples/make_wbsig53_yolo_dataset_impaired_train_scipy.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/examples/make_wbsig53_yolo_dataset_impaired_val.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/examples/make_wbsig53_yolo_dataset_impaired_val_scipy.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/examples/make_yolo.sh
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/examples/train.sh
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/examples/verify_yolo_dataset_plot.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/examples/wbsig53.yaml
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/grc/CMakeLists.txt
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/grc/spectrumDetect_specDetect.block.yml
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/grc/spectrumDetect_spectrumPlot.block.yml
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/include/gnuradio/spectrumDetect/CMakeLists.txt
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/include/gnuradio/spectrumDetect/api.h
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/lib/CMakeLists.txt
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/python/spectrumDetect/.gitignore
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/python/spectrumDetect/CMakeLists.txt
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/python/spectrumDetect/__init__.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/python/spectrumDetect/bindings/CMakeLists.txt
	renamed:    ../tests/figures/.gitkeep -> ../gr-spectrumdetect/gr-spectrumdetect/python/spectrumDetect/bindings/README.md
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/python/spectrumDetect/bindings/bind_oot_file.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/python/spectrumDetect/bindings/docstrings/README.md
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/python/spectrumDetect/bindings/header_utils.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/python/spectrumDetect/bindings/python_bindings.cc
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/python/spectrumDetect/rxTime.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/python/spectrumDetect/specDetect.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/python/spectrumDetect/spectrumPlot.py
	new file:   ../gr-spectrumdetect/gr-spectrumdetect/run_docker.sh
	modified:   ../pyproject.toml
	modified:   ../scripts/generate_sig53.py
	modified:   ../scripts/generate_wideband_sig53.py
	new file:   ../scripts/test_generate_sig53_scripts.py
	new file:   ../scripts/test_generate_wideband_sig53_scripts.py
	deleted:    ../tests/test_datasets_sig53.py
	deleted:    ../tests/test_models.py
	deleted:    ../tests/test_modulation_benchmark.py
	deleted:    ../tests/test_modulation_figures.py
	deleted:    ../tests/test_transforms.py
	deleted:    ../tests/test_transforms_figures.py
	deleted:    ../tests/test_wideband_benchmark.py
	deleted:    ../tests/test_wideband_figures.py
	deleted:    ../tests/test_writer.py
	modified:   __init__.py
	modified:   datasets/conf.py
	new file:   datasets/datamodules.py
	modified:   datasets/modulations.py
	modified:   datasets/radioml.py
	modified:   datasets/sig53.py
	new file:   datasets/signal_classes.py
	modified:   datasets/synthetic.py
	modified:   datasets/wideband.py
	modified:   datasets/wideband_sig53.py
	modified:   image_datasets/__init__.py
	modified:   image_datasets/dataset_generation.py
	modified:   image_datasets/datasets/__init__.py
	modified:   image_datasets/datasets/composites.py
	modified:   image_datasets/datasets/file_loading_datasets.py
	new file:   image_datasets/datasets/protocols.py
	modified:   image_datasets/datasets/synthetic_signals.py
	new file:   image_datasets/datasets/yolo_datasets.py
	modified:   image_datasets/generate_dataset.py
	modified:   image_datasets/generate_dataset_example.ipynb
	new file:   image_datasets/plotting/__init__.py
	new file:   image_datasets/plotting/plotting.py
	modified:   image_datasets/transforms/__init__.py
	modified:   image_datasets/transforms/impairments.py
	modified:   models/__init__.py
	new file:   models/iq_models/densenet/__init__.py
	new file:   models/iq_models/densenet/densenet1d.py
	modified:   models/iq_models/efficientnet/efficientnet.py
	modified:   models/model_utils/model_utils_1d/conversions_to_1d.py
	modified:   models/spectrogram_models/detr/modules.py
	modified:   transforms/functional.py
	modified:   transforms/target_transforms.py
	modified:   transforms/transforms.py
	new file:   utils/classify_transforms.py
	modified:   utils/dataset.py
	modified:   utils/types.py
	modified:   utils/visualize.py
	modified:   utils/writer.py
	new file:   utils/yolo_classify.py
	new file:   utils/yolo_train.py
	new file:   utils/yolo_val.py
	new file:   utils/yolo_validator.py
	modified:   examples/00_example_sig53_dataset.ipynb
	modified:   examples/01_example_modulations_dataset.ipynb
	modified:   examples/02_example_sig53_classifier.ipynb
	modified:   examples/03_example_widebandsig53_dataset.ipynb
	modified:   examples/04_example_wideband_modulations_dataset.ipynb
	deleted:    examples/05_example_wideband_detector.ipynb
	modified:   examples/05_example_wideband_yolo_to_disk.ipynb
	modified:   examples/06_example_wideband_yolo.ipynb
	modified:   examples/07_example_classify_yolo.ipynb
	deleted:    examples/wideband_sig53/.gitkeep
	modified:   ../examples/00_example_sig53_dataset.ipynb
	modified:   ../examples/01_example_modulations_dataset.ipynb
	modified:   ../examples/02_example_sig53_classifier.ipynb
	modified:   ../examples/03_example_widebandsig53_dataset.ipynb
	modified:   ../examples/04_example_wideband_modulations_dataset.ipynb
	modified:   ../examples/05_example_wideband_yolo_to_disk.ipynb
	new file:   examples/detect.pt
	renamed:    gr-spectrumdetect/examples/example.grc -> examples/example.grc
	new file:   examples/generate.sh
	renamed:    gr-spectrumdetect/examples/generate_wideband_sig53.py -> examples/generate_wideband_sig53.py
	renamed:    gr-spectrumdetect/examples/make_wbsig53_yolo_dataset_impaired_train.py -> examples/make_wbsig53_yolo_dataset_impaired_train.py
	renamed:    gr-spectrumdetect/examples/make_wbsig53_yolo_dataset_impaired_train_scipy.py -> examples/make_wbsig53_yolo_dataset_impaired_train_scipy.py
	renamed:    gr-spectrumdetect/examples/make_wbsig53_yolo_dataset_impaired_val.py -> examples/make_wbsig53_yolo_dataset_impaired_val.py
	renamed:    gr-spectrumdetect/examples/make_wbsig53_yolo_dataset_impaired_val_scipy.py -> examples/make_wbsig53_yolo_dataset_impaired_val_scipy.py
	renamed:    gr-spectrumdetect/examples/make_yolo.sh -> examples/make_yolo.sh
	renamed:    gr-spectrumdetect/examples/train.sh -> examples/train.sh
	renamed:    gr-spectrumdetect/examples/verify_yolo_dataset_plot.py -> examples/verify_yolo_dataset_plot.py
	renamed:    gr-spectrumdetect/examples/wbsig53.yaml -> examples/wbsig53.yaml
	deleted:    gr-spectrumdetect/CMakeLists.txt
	deleted:    gr-spectrumdetect/Dockerfile
	deleted:    gr-spectrumdetect/MANIFEST.md
	deleted:    gr-spectrumdetect/apps/CMakeLists.txt
	deleted:    gr-spectrumdetect/build_docker.sh
	deleted:    gr-spectrumdetect/cmake/Modules/CMakeParseArgumentsCopy.cmake
	deleted:    gr-spectrumdetect/cmake/Modules/gnuradio-spectrumDetectConfig.cmake
	deleted:    gr-spectrumdetect/cmake/Modules/targetConfig.cmake.in
	deleted:    gr-spectrumdetect/cmake/cmake_uninstall.cmake.in
	deleted:    gr-spectrumdetect/docs/CMakeLists.txt
	deleted:    gr-spectrumdetect/docs/README.spectrumDetect
	deleted:    gr-spectrumdetect/docs/doxygen/CMakeLists.txt
	deleted:    gr-spectrumdetect/docs/doxygen/Doxyfile.in
	deleted:    gr-spectrumdetect/docs/doxygen/doxyxml/__init__.py
	deleted:    gr-spectrumdetect/docs/doxygen/doxyxml/base.py
	deleted:    gr-spectrumdetect/docs/doxygen/doxyxml/doxyindex.py
	deleted:    gr-spectrumdetect/docs/doxygen/doxyxml/generated/__init__.py
	deleted:    gr-spectrumdetect/docs/doxygen/doxyxml/generated/compound.py
	deleted:    gr-spectrumdetect/docs/doxygen/doxyxml/generated/compoundsuper.py
	deleted:    gr-spectrumdetect/docs/doxygen/doxyxml/generated/index.py
	deleted:    gr-spectrumdetect/docs/doxygen/doxyxml/generated/indexsuper.py
	deleted:    gr-spectrumdetect/docs/doxygen/doxyxml/text.py
	deleted:    gr-spectrumdetect/docs/doxygen/other/doxypy.py
	deleted:    gr-spectrumdetect/docs/doxygen/other/group_defs.dox
	deleted:    gr-spectrumdetect/docs/doxygen/other/main_page.dox
	deleted:    gr-spectrumdetect/docs/doxygen/pydoc_macros.h
	deleted:    gr-spectrumdetect/docs/doxygen/update_pydoc.py
	deleted:    gr-spectrumdetect/examples/generate.sh
	deleted:    gr-spectrumdetect/grc/CMakeLists.txt
	deleted:    gr-spectrumdetect/grc/spectrumDetect_specDetect.block.yml
	deleted:    gr-spectrumdetect/grc/spectrumDetect_spectrumPlot.block.yml
	deleted:    gr-spectrumdetect/include/gnuradio/spectrumDetect/CMakeLists.txt
	deleted:    gr-spectrumdetect/include/gnuradio/spectrumDetect/api.h
	deleted:    gr-spectrumdetect/lib/CMakeLists.txt
	deleted:    gr-spectrumdetect/python/spectrumDetect/.gitignore
	deleted:    gr-spectrumdetect/python/spectrumDetect/CMakeLists.txt
	deleted:    gr-spectrumdetect/python/spectrumDetect/__init__.py
	deleted:    gr-spectrumdetect/python/spectrumDetect/bindings/CMakeLists.txt
	deleted:    gr-spectrumdetect/python/spectrumDetect/bindings/README.md
	deleted:    gr-spectrumdetect/python/spectrumDetect/bindings/bind_oot_file.py
	deleted:    gr-spectrumdetect/python/spectrumDetect/bindings/docstrings/README.md
	deleted:    gr-spectrumdetect/python/spectrumDetect/bindings/header_utils.py
	deleted:    gr-spectrumdetect/python/spectrumDetect/bindings/python_bindings.cc
	deleted:    gr-spectrumdetect/python/spectrumDetect/rxTime.py
	deleted:    gr-spectrumdetect/python/spectrumDetect/specDetect.py
	deleted:    gr-spectrumdetect/python/spectrumDetect/spectrumPlot.py
	deleted:    gr-spectrumdetect/run_docker.sh
	modified:   ../scripts/generate_sig53.py
	modified:   ../scripts/generate_wideband_sig53.py
	modified:   ../scripts/test_generate_sig53_scripts.py
	modified:   ../scripts/test_generate_wideband_sig53_scripts.py
	modified:   ../torchsig/datasets/wideband.py
	modified:   ../torchsig/utils/writer.py
	new file:   CMakeLists.txt
	new file:   Dockerfile
	new file:   MANIFEST.md
	new file:   apps/CMakeLists.txt
	new file:   build_docker.sh
	new file:   cmake/Modules/CMakeParseArgumentsCopy.cmake
	new file:   cmake/Modules/gnuradio-spectrumDetectConfig.cmake
	new file:   cmake/Modules/targetConfig.cmake.in
	new file:   cmake/cmake_uninstall.cmake.in
	new file:   docs/CMakeLists.txt
	new file:   docs/README.spectrumDetect
	new file:   docs/doxygen/CMakeLists.txt
	new file:   docs/doxygen/Doxyfile.in
	new file:   docs/doxygen/doxyxml/__init__.py
	new file:   docs/doxygen/doxyxml/base.py
	new file:   docs/doxygen/doxyxml/doxyindex.py
	new file:   docs/doxygen/doxyxml/generated/__init__.py
	new file:   docs/doxygen/doxyxml/generated/compound.py
	new file:   docs/doxygen/doxyxml/generated/compoundsuper.py
	new file:   docs/doxygen/doxyxml/generated/index.py
	new file:   docs/doxygen/doxyxml/generated/indexsuper.py
	new file:   docs/doxygen/doxyxml/text.py
	new file:   docs/doxygen/other/doxypy.py
	new file:   docs/doxygen/other/group_defs.dox
	new file:   docs/doxygen/other/main_page.dox
	new file:   docs/doxygen/pydoc_macros.h
	new file:   docs/doxygen/update_pydoc.py
	new file:   grc/CMakeLists.txt
	new file:   grc/spectrumDetect_specDetect.block.yml
	new file:   grc/spectrumDetect_spectrumPlot.block.yml
	new file:   include/gnuradio/spectrumDetect/CMakeLists.txt
	new file:   include/gnuradio/spectrumDetect/api.h
	new file:   lib/CMakeLists.txt
	new file:   python/spectrumDetect/.gitignore
	new file:   python/spectrumDetect/CMakeLists.txt
	new file:   python/spectrumDetect/__init__.py
	new file:   python/spectrumDetect/bindings/CMakeLists.txt
	new file:   python/spectrumDetect/bindings/README.md
	new file:   python/spectrumDetect/bindings/bind_oot_file.py
	new file:   python/spectrumDetect/bindings/docstrings/README.md
	new file:   python/spectrumDetect/bindings/header_utils.py
	new file:   python/spectrumDetect/bindings/python_bindings.cc
	new file:   python/spectrumDetect/rxTime.py
	new file:   python/spectrumDetect/specDetect.py
	new file:   python/spectrumDetect/spectrumPlot.py
	new file:   run_docker.sh
Co-authored-by: TorchDSP <108295102+TorchDSP@users.noreply.github.com>
	new file:   .gitattributes
	modified:   gr-spectrumdetect/examples/detect.pt
	modified:   01_example_modulations_dataset.ipynb
	modified:   03_example_widebandsig53_dataset.ipynb
	modified:   06_example_wideband_yolo.ipynb
	modified:   07_example_classify_yolo.ipynb
	modified:   classify.yaml
	modified:   wbdata.yaml
	modified:   ../torchsig/transforms/target_transforms.py
	modified:   ../torchsig/utils/classify_transforms.py
	modified:   ../torchsig/utils/dsp.py
	modified:   ../torchsig/utils/visualize.py
	modified:   ../torchsig/utils/yolo_classify.py
	modified:   ../torchsig/utils/yolo_train.py
	modified:   examples/00_example_sig53_dataset.ipynb
	modified:   examples/01_example_modulations_dataset.ipynb
	modified:   examples/02_example_sig53_classifier.ipynb
	modified:   examples/03_example_widebandsig53_dataset.ipynb
	modified:   examples/04_example_wideband_modulations_dataset.ipynb
	modified:   examples/05_example_wideband_yolo_to_disk.ipynb
	modified:   examples/06_example_wideband_yolo.ipynb
	modified:   examples/07_example_classify_yolo.ipynb
	modified:   examples/classify.yaml
* fix for #254

* Update types.py

* Update types.py

* Update types.py

* Update types.py

* Remove accidental whitespace
@tapyu

tapyu commented Aug 25, 2026 •

Copy link
Copy Markdown
Author

Here is the code you can check this fix

import h5py

from torchsig.datasets.datasets import TorchSigIterableDataset
from torchsig.utils.data_loading import WorkerSeedingDataLoader
from torchsig.signals.builders.fm import FMSignalGenerator
from torchsig.utils.writer import DatasetCreator, default_collate_fn
from torchsig.transforms.impairments import Impairments

dataset_metadata = {
    "num_iq_samples_dataset": 108_032,
    "fft_size": 512,
    "num_signals_min": 1,
    "num_signals_max": 1,
    "sample_rate": 20e6,
    "noise_power_db": 0,
    "snr_db_min": 50,
    "snr_db_max": 50,
    "signal_duration_min": 0.00378112,
    "signal_duration_max": 0.00378112,
    "bandwidth_min": 500e3,
    "bandwidth_max": 1e6,
    "signal_duration_in_samples_min": 75622,
    "signal_duration_in_samples_max": 75622,
    "frequency_min": -10e6,
    "frequency_max": 10e6,
    "signal_center_freq_min": -9e6,
    "signal_center_freq_max": 9e6,
    "class_list": "fm",
    "class_distribution": "1",
    "fft_stride": 512,
    "cochannel_overlap_probability": 0,
}


def get_dataset():

    impairments = Impairments(level=0)  # noiseless channel

    iter_dataset = TorchSigIterableDataset(
        signal_generators=[FMSignalGenerator(**dataset_metadata)],
        metadata=dataset_metadata,
        transforms=[impairments.dataset_transforms],
        component_transforms=[impairments.signal_transforms],
        target_labels=['class_name', 'start_in_samples', 'duration_in_samples', 'lower_freq', 'upper_freq', 'snr_db'],
        seed=14,
    )

    dataloader = WorkerSeedingDataLoader(
        iter_dataset,
        seed=14,
        batch_size=8,
        num_workers=10,
        collate_fn=default_collate_fn,
    )

    dataset_creator = DatasetCreator(
        dataset_length=200,
        dataloader=dataloader,
        root='dataset',
        overwrite=True,
        multithreading=True,
    )
    dataset_creator.create()
    print(f"Dataset created at {dataset_creator.root}")


def count_mismatched_metadata(rel_tol: float = 1e-6) -> tuple[int, int]:
    """Checks every signal metadata record in the generated dataset for
    internal consistency between `center_freq`/`bandwidth` and the derived
    `_lower_frequency`/`_upper_frequency` fields.

    A record is "mismatched" when either:
      - avg(_lower_frequency, _upper_frequency) != center_freq, or
      - (_upper_frequency - _lower_frequency) != bandwidth

    beyond a small numerical tolerance. This is the bug signature: TorchSig
    caches `_lower_frequency`/`_upper_frequency` as a side effect of reading
    the `upper_freq`/`lower_freq` properties, but nothing refreshes that
    cache when `center_freq`/`bandwidth` are mutated afterward (e.g.
    anti-aliasing clipping, ChannelSwap, SpectralInversion, TimeReversal,
    Doppler) -- so the cached values can go stale and end up inconsistent,
    including fully mirrored to the opposite side of 0 Hz.

    Returns:
        tuple[int, int]: (number of mismatched records, total records checked)
    """
    n_records = 0
    n_mismatched = 0

    required_fields = ("center_freq", "bandwidth", "_lower_frequency", "_upper_frequency")

    with h5py.File("dataset/data.h5", "r") as f:
        metadata = f["metadata"]
        for key in metadata.keys():
            record = metadata[key]
            if not all(field in record for field in required_fields):
                continue

            center_freq = record["center_freq"][()]
            bandwidth = record["bandwidth"][()]
            lower_freq = record["_lower_frequency"][()]
            upper_freq = record["_upper_frequency"][()]

            n_records += 1

            tol = max(1.0, rel_tol * max(abs(bandwidth), abs(center_freq)))
            center_mismatch = abs((lower_freq + upper_freq) / 2 - center_freq) > tol
            bandwidth_mismatch = abs((upper_freq - lower_freq) - bandwidth) > tol

            if center_mismatch or bandwidth_mismatch:
                n_mismatched += 1

    print(f"Mismatched metadata records: {n_mismatched} / {n_records}")
    return n_mismatched, n_records


def main():
    get_dataset()
    count_mismatched_metadata()


if __name__ == "__main__":
    main()

@tapyu tapyu closed this by deleting the head repository Oct 13, 2026
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.

[Bug] Inverted lower/upper frequency signs in the generated HDF5 when Spectrogram is used as a transform

6 participants