My GSoC 2026 Journey (Final Report): 12 Weeks of Learning and Contributing
12 Weeks of Learning and Contributing to SunPy
I’m really grateful that I got the opportunity to work on radiospectra during GSoC. Over these 12 weeks, I got to work across several areas of the project - from the ndcube migration and metadata to plotting, testing, documentation, support for different data sources and eventually the SpectrogramFactory redesign.
What made the experience especially valuable was getting to learn from my mentors and the wider SunPy community along the way. Each review, discussion and challenge helped me understand the project better and become more thoughtful about how I approach a change.
Before GSoC: Getting Started with radiospectra
My GSoC work actually started before the official coding period.
I had already made a few contributions to radiospectra including restoring the WIND/WAVES client, fixing time and frequency handling in plotting and migrating network clients to newer SunPy APIs.
Some of my early contributions were:
- PR #142: Migrate network clients to the new unified Scraper format
- PR #144: Fix non-UTC time handling in spectrogram plotting
- PR #151: Fix mixed frequency units while plotting
- PR #158: Restore the WIND/WAVES Fido client
These contributions helped me become familiar with the codebase and made me understand the project better.
They also gave me an early idea of how different contributing to an existing open-source project is from building something on your own. You have to understand the existing architecture, conventions, tests and the reasoning behind previous decisions before making changes.
Moving GenericSpectrogram to ndcube
One of the main goals of my project was to move GenericSpectrogram towards ndcube.NDCube.
This was an important architectural change because NDCube already provides functionality that is very useful for spectrogram data, including coordinate-aware operations, WCS handling, slicing and unit-aware data handling.
Instead of maintaining similar functionality inside radiospectra, the idea was to build on top of the existing ndcube ecosystem.
My work included:
-
Making
GenericSpectrograminherit fromNDCube - Building the spectrogram WCS from its time and frequency axes
- Fixing coordinate ordering in the WCS
-
Updating plotting mixins to work with the new
NDCubestructure - Updating tests and instrument integrations affected by the change
PR #228: Initial GenericSpectrogram base using NDCube
Status: Merged
This became the foundation for several of the changes that followed.
Once GenericSpectrogram started using NDCube, it also changed how I approached some of the other features I had planned. In several cases, functionality I initially thought needed to be implemented in radiospectra was already available through ndcube.
That became particularly important when I started working on slicing and profiles.
Structuring Spectrogram Metadata
Another major part of the project was improving metadata handling.
Previously, spectrogram metadata was essentially represented as dictionaries. While this worked, it made it harder to clearly define which metadata fields were expected and how instrument-specific metadata should be handled.
I introduced a metadata hierarchy with a common SpectrogramMeta class and instrument-specific classes
PR #245: Add spectrogram metadata classes
Status: Merged
The goal was to give metadata a more structured interface while still allowing individual instruments to define their own requirements.
This also became useful later during the SpectrogramFactory migration, where the factory needed to work with lightweight metadata before handing the actual parsing over to the appropriate instrument class.
Testing, Plotting and Data Sources
Alongside the larger architectural work, I also worked on testing, plotting and adding support for additional data sources.
PR #202: Online integration tests
Status: Merged
I added integration tests using real online data sources. These were useful for catching problems that weren't always visible when working only with local test data.
PR #240: Figure testing for WAVES
Status: Merged
I added pytest-mpl based figure comparisons to help detect visual regressions in plotting.
This was particularly useful for a project where plotting is an important part of how users interact with spectrogram data.
PR #231: Nançay Decameter Array support
Status: Merged
This added support for Nançay Decameter Array observations.
PR #227: Example Gallery setup
Status: Merged
This introduced an Example Gallery to the documentation using sphinx-gallery following the same configuration and directory structure as SunPy.
These contributions were not all part of the refactor, but they helped strengthen the project around it, especially the testing and documentation infrastructure.
Adding CDAWeb Support for WIND and STEREO
Another significant part of the project was adding CDAWeb CDF support.
PR #254: Add CDAWeb CDF support for WIND and STEREO
Status: Under review
The goal was to allow radiospectra to read CDF data from NASA's CDAWeb and construct spectrograms from it.
The implementation involved handling things such as:
- CDF variables and metadata
- Time conversion
- Invalid/fill values
- Instrument identification
- Frequency axes
- Plotting the resulting spectrograms
The STEREO data also required some additional consideration because its LFR and HFR observations cover different frequency ranges and need to be represented appropriately.
A Lesson in Keeping the API Simple
One of the more interesting parts of the project started with me trying to make spectrogram slicing more convenient.
I initially added methods such as:
spec.crop_time(...)spec.crop_freq(...)spec.time_profile(...)spec.line_profile(...)
I also implemented internal helpers to convert physical values such as frequencies and times into the corresponding array indices.
The implementation worked and the tests were passing.
Then came the review.
The important point raised during review was that GenericSpectrogram already inherits from ndcube.NDCube, which provides coordinate-aware methods such as .crop() and .crop_by_values().
So although my implementation worked, it was effectively adding another layer around functionality that already existed.
PR #256: Add crop wrappers
Status: Merged
PR #262: Remove custom crop wrappers (to remove the work done PR #256)
Status: Merged
We removed the custom wrappers and instead decided to demonstrate the existing ndcube functionality properly.
This was probably one of the most useful lessons I learned during GSoC:
A working implementation is not necessarily the right implementation.
It is very easy to think about how to add a feature. It is much more important to first ask whether the project already has the right abstraction for it.
Turning the Slicing Work into Documentation
Removing the wrappers didn't mean that the original goal was lost.
I still wanted users to have an easy way to discover how to crop spectrograms and extract profiles.
So I moved the work into the example gallery.
PR #263: Add gallery examples for cropping and extracting profiles
Status: Merged
The example demonstrates how to use the native ndcube functionality for:
-
Cropping the time axis using
NDCube.crop() -
Cropping the frequency axis using
NDCube.crop_by_values() - Cropping both axes together
- Extracting a time profile
- Extracting a frequency profile
One particularly useful part is that setting the lower and upper bounds to the same coordinate can collapse a dimension and produce a 1D profile.
I also changed the example based on the review feedback, keeping the explanation, code and plots together step by step. This made it much easier to follow instead of having all the code first and the plots at the end.
It was also nice to see the slicing work come together this way. I started by trying to add new convenience methods but ended up showing users how to use the functionality that ndcube already provides.
The SpectrogramFactory Migration
The largest architectural part of my GSoC project was the SpectrogramFactory migration.
Previously, the factory would do most of the parsing work before determining which instrument class should handle the file.
The general flow was roughly:
This meant that the factory itself had to know quite a lot about the individual instruments and had to perform parsing before the correct instrument was even identified.
The new design changes this approach.
The factory is now responsible mainly for reading the file and routing it to the correct instrument, while the instrument class handles the actual parsing.
PR #259: Metadata factory migration
Status: Under review
I initially migrated:
- WAVES
- e-CALLISTO
and then continued the migration for:
- PSP RFS
- Solar Orbiter RPW
- SWAVES
- RSTN
- ILOFAR
- EOVSA
- NDA
Each instrument had its own details to deal with.
For example, some sources use CDF files, some use FITS, some use binary formats and some require reconstruction of indices or frequencies from instrument-specific information.
The common pattern was to move that instrument-specific logic out of the central factory and into:
InstrumentSpectrogram.from_raw(...)
This keeps the factory smaller and makes each instrument responsible for understanding its own raw data.
What I Learned
The technical side of the project was a big part of GSoC, but I’m equally grateful for everything I learned through the process of working with the project and the community.
One of the biggest things I learned was to look at what the project and its dependencies already provide before adding something new. The slicing work was a great example of this. What started as custom wrappers eventually became a much simpler solution using the functionality already available in ndcube.
I’m especially grateful for the reviews I received throughout the project. They often went beyond pointing out what needed to change and helped me understand why a particular approach was better. Some of my best learning came from those discussions.
The SpectrogramFactory migration taught me that architectural changes need a lot of thought. Understanding where each piece of logic belongs and how it affects the rest of the project is just as important as writing the code itself.
Working on the example gallery also showed me how important it is to make existing functionality easy to discover. A good feature is much more useful when users can easily understand how to use it.
Over these 12 weeks, I got to learn about testing, CI, reviews, releases, branches, communication and working with other contributors. I’m really grateful that GSoC gave me the chance to experience all of this as part of a real project.
12 Weeks Later
And now, the official 12 weeks of GSoC are over.
Looking back, I’m genuinely grateful for how much I got to experience during these few months. I started with contributions around plotting and data sources and eventually got to work on deeper architectural changes in radiospectra.
There are still PRs under review, ideas that didn't fit into the GSoC timeline, and parts of radiospectra that I would still like to understand and improve.
So while the GSoC coding period has ended, I don't really see this as the end of my journey with the project.
I’m really looking forward to continuing to contribute to radiospectra and the wider SunPy community.
Thank You
A huge thank you to Laura Hayes and Shane Maloney for all the guidance, reviews, discussions and patience throughout these twelve weeks.
I’m especially thankful for how willing they were to discuss ideas with me and help me understand the reasoning behind different design decisions. There were several times when I had a working implementation, but a review discussion helped me step back and find a better approach.
I’d also like to thank everyone in the SunPy community who reviewed my work, answered my questions, discussed ideas or helped me understand different parts of the project.
I’m truly grateful for the opportunity to spend these 12 weeks contributing to an open-source scientific Python project and learning from such a welcoming community. This experience has taught me a lot and I’m really happy that I got to be a part of it.
Looking Ahead
When I started GSoC, one of my main goals was to become better at writing code.
After these 12 weeks, I think I came away with something even more valuable: a better understanding of how to think about the code I write and the decisions behind it.
There is still a lot I want to learn and plenty that I want to contribute.
So rather than ending with “GSoC is over”, I think I'll end with:
12 weeks are over. The work isn't.
My GSoC Contributions
Here is a summary of the main work I contributed to during GSoC:
| PR | Contribution | Status |
|---|---|---|
| #202 | Added online integration tests for radiospectra data sources |
Merged |
| #227 | Added example gallery with WAVES plot | Merged |
| #228 | Initial GenericSpectrogram base using NDCube |
Merged |
| #231 | Added source for Nançay Decameter Array observations | Merged |
| #237 | Finalized SunPy>7.0 support | Merged |
| #240 | Added initial figure test support for WAVES | Merged |
| #242 | Fixed issue/PR links in the changelog | Merged |
| #245 | Added spectrogram metadata classes | Merged |
| #254 | Added CDAWeb CDF support for WIND and STEREO | Under Review |
| #256 | Added crop_time and crop_freq wrappers to GenericSpectrogram |
Merged |
| #259 | Metadata factory migration | Under Review |
| #262 | Removed custom crop wrappers from GenericSpectrogram |
Merged |
| #263 | Added gallery examples for cropping and extracting profiles | Merged |
| #267 | Updated ndcube-refactor with main |
Merged |








Comments
Post a Comment