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.

Read more…

12 Weeks of GSoC: A Lot Learned, A Lot More to Do

A Lot Learned, A Lot More to Do 🚀

And just like that, 12 weeks of GSoC are over!

The last two weeks have been quite different from the earlier ones. There was a lot of finishing up, reviewing, fixing things that broke along the way and making sure the bigger pieces of the project were coming together properly.

Here's what I've been up to.

Learning to Write Less Code 😅

One of the biggest lessons from these weeks actually came from a code review.

I had previously added some custom methods for getting time and frequency profiles from a spectrogram. The code worked, but there were some change of plans, during review we realized that ndcube already provided the functionality needed to achieve the same thing.

So instead of keeping the extra code, I removed it and focused on showing users how to use the existing ndcube methods.

It was a really good reminder that adding more code isn't always the right solution.

Sometimes the better approach is to make better use of what already exists.


Making the Examples More Useful 📚

After removing the custom wrappers, I wanted to make sure users could still easily understand how to perform these operations.

I worked on a Sphinx-Gallery example showing how to crop spectrograms and extract profiles using the ndcube API.

The example covers cropping by time, cropping by frequency, combining both and extracting 1D profiles.







I also changed the example based on mentor feedback so that the explanation, code and plots appear step by step instead of having all the plots at the end.

I think it makes the whole example much easier to follow.


Finally Finishing the Factory Migration 🔧

The biggest piece of work during these two weeks was completing the SpectrogramFactory migration.

I had previously migrated WAVES and e-CALLISTO and during these weeks I worked through the remaining instruments.

The idea behind the new design is to keep the factory simple:

Before:

Read everything → Find instrument → Create spectrogram

Now:

Read basic information → Find instrument → Let the instrument parse the data

The instrument-specific parsing now lives inside the individual instrument classes through from_raw() instead of having all of it inside the factory.

Getting all the instruments moved over was definitely one of the bigger milestones of my GSoC project.


The Final Few Fixes 🛠️

Of course, finishing a large refactor also meant dealing with a few unexpected things.

I worked through some documentation build issues, resolved merge conflicts while keeping ndcube-refactor up to date with main and fixed a few smaller issues that came up along the way.

These weren't necessarily the most exciting changes, but they were a good reminder that a project doesn't end when the main code is written. There's always testing, documentation, reviews and cleanup.


12 Weeks Done… But Not Finished Yet 🚀

And that's officially 12 weeks of GSoC done!

Looking back, it's honestly hard to believe how much I've learned in these three months. I started with a lot of questions about the codebase and now I'm working on things like the architecture of the SpectrogramFactory, ndcube integration, documentation, testing and all the little things that come with maintaining an open-source project.

Read more…

Learning to Speak C & Cython: My GSoC Summer with Astropy

The summer is officially over. I am staring at a remarkably clean Git branch, my laptop didn't literally take off into orbit (though the CPU fans certainly tried a few times during local CI builds), and I somehow know what git rebase -i does without having to Google it in a cold sweat.

If you'd asked me back in May what I was going to be doing, I would have confidently told you I was going to "write tests for Astropy's C extensions." It sounded so neat. So contained. But open source doesn't really work like that. I came in thinking I was just going to write tests, and somewhere along the way, I ended up learning how the actual machinery underneath the Python abstraction works, how maintainers think about architecture, and how to safely catch C-level memory panics without taking down the entire interpreter.

Read more…

Weeks 9 & 10: Slicing, Profiles and a New Factory

 Slicing, Profiles and a New Factory 🚀

The last two weeks have been a pretty interesting mix of working on spectrogram slicing and starting to look at one of the bigger architectural changes in radiospectra.

A lot of the work this time was about understanding how the different pieces fit together and figuring out how to make things easier for users and contributors.

Read more…

From Photons to Power Spectra: Journey with Stingray.jl

From Photons to Power Spectra: Journey with Stingray.jl

The final week of Google Summer of Code is here. As I sit back and look at the codebase, it feels surreal. Three months ago, I was an undergraduate with a deep fascination for black holes, neutron stars, and the extreme physics of the cosmos. I knew I wanted to write code that would help decode the universe, but I never imagined the ride would be this thrilling.

When I started my journey with Stingray.jl under the OpenAstronomy umbrella, the mission was clear: take the powerful spectral-timing capabilities of the Python Stingray library and bring them to the Julia ecosystem. Python is fantastic, but when you are dealing with millions of X-ray photons and computing averaged cross-spectra over thousands of segments, the blazing speed of Julia becomes a game-changer.

This is the story of how we built a high-performance spectral timing engine, the promises we kept, the fun we had, and where we go from here.


What was Promised vs. What was Delivered

At the start of the summer, my proposal outlined an ambitious set of goals: building high-level spectral types, implementing time lags, porting the Lomb-Scargle periodogram, and laying the foundation for a variability-vs-energy framework.

As we got into the weeds of the Fourier transforms and spectral analysis, here is what we successfully shipped into the core library:

High-Level Spectral Types: We implemented the Powerspectrum and Crossspectrum struct types from scratch, giving users a clean, type-safe API rather than dealing with raw DataFrames.

Mission-Specific FITS Support: We added telescope-specific event file readers for missions like NICER, NuSTAR, XMM-Newton, and RXTE.

Frequency Rebinning: Implemented both geometric (logarithmic) and linear frequency rebinning for proper broadband noise studies.

Robust Normalizations: Added support for Leahy, Fractional RMS, and Absolute RMS normalizations, rigorously tested against Poisson noise statistics.

Lomb-Scargle Periodograms: Ported the Lomb-Scargle algorithm to handle unevenly sampled astrophysical data.

Fourier Utilities: Successfully ported complex utility functions like shift_and_add for dynamic spectral analysis.

Codecov & Testing: Integrated Codecov and dramatically improved the test suite to ensure our Julia port produces scientifically valid results matching its Python sibling.


Code In Action: Power Spectra and Time Lags

To give you a taste of what the new Stingray.jl API feels like, let's look at a real-world workflow. Imagine you have raw photon event data from the NICER telescope and you are hunting for a Quasi-Periodic Oscillation (QPO) from an accreting black hole.

Before GSoC, this would have required manual DataFrame manipulation. Now? It’s beautifully concise.

Generating and Rebinning a Power Spectrum

julia

using Stingray

using CairoMakie

# 1. Load up mission data (e.g., NICER)

ev = readevents("nicer_observation.fits")

# 2. Generate an Averaged Power Spectrum

# (128s segments, Leahy normalization sets Poisson noise at power = 2)

ps = Powerspectrum(ev, segment_size=128.0, dt=1/4096, norm="leahy")

# 3. Logarithmic Rebinning for broadband noise studies

# (Bins get 3% wider at each step, perfect for log-log plots!)

ps_rebinned = rebin(ps, factor=1.03, method=:geometric)

With our plotting utilities and Makie integration, we can easily visualize what a QPO looks like on top of a red-noise continuum:


Read more…

Weeks 7 & 8: Making Spectrograms a Little Easier to Work With

Making Spectrograms a Little Easier to Work With

Another couple of weeks have gone by and it's been a mix of writing code, responding to reviews and learning more about the radiospectra codebase.

Compared to the first few weeks, I spent less time figuring out where things lived and more time thinking about how people actually use the library. That shift has been really fun because even small improvements can make a noticeable difference for users.

Read more…