Close
0%
0%

Silicon Photomultiplier Gamma Spectrometer

Gamma spectrometer using scintillation crystal, Silicon Photomultiplier (SiPM), and all-digital FPGA signal processing

Similar projects worth following
This device allows for the quantitative measurement of the amount of different isotopes in an analyzed sample.

It consists of a scintillator (a material that converts gamma particles into photons) and a photomultiplier that multiplies those photons and converts them into electric current pulses. By measuring the amplitude of these pulses, it is possible to calculate the original energy of the gamma ray that produced the pulse.

Sampling data over time and plotting its histogram reveals which radioactive isotopes are present in the measured sample and their respective ratios.

Design Philosophy


Silicon Photomultiplier vs Traditional PMT

Conventional gamma spectrometers commonly use vacuum photomultiplier tubes (PMTs). These require high-voltage supplies in the kilovolt range, are mechanically fragile, and are sensitive to magnetic fields.

In this project, a Silicon Photomultiplier (SiPM) is used instead. The SiPM operates at significantly lower bias voltage (tens of volts rather than kilovolts), is mechanically robust, compact, and insensitive to magnetic fields. These characteristics simplify the power supply design and mechanical integration.


Digital vs Analog Processing

Traditional spectrometers implement CR-RC shaping networks and peak detection entirely in analog hardware. This approach is cost-effective but offers limited flexibility, as parameter changes require hardware modification.

In this system, pulse shaping and peak detection are performed digitally after high-speed sampling. This increases flexibility and allows signal processing parameters to be modified in firmware without hardware changes. The trade-off is higher performance requirements for the ADC and the need for an FPGA rather than a microcontroller, which increases overall system cost.

Hardware

Architecture Overview

The hardware consists of four main parts:


Analog Processing

Handles low-level currents produced by the sensor (typically 1uA - 1mA pulses, 10mV - 1V after amplification):

  • NaI(Tl) Scintillator Crystal - Converts gamma rays into visible light pulses through scintillation
  • Silicon Photomultiplier: MICROFC-60035-SMT-TR1 SiPM - Converts light pulses from the scintillator crystal into electrical current pulses
  • Amplifier: OPA690 - can be configured either as a charge amplifier or as a transimpedance amplifier (TIA), depending on the selected feedback network. In the current design, the amplifier is configured as a charge amplifier.
    • Charge Amplifier Configuration. In this mode, the amplifier integrates the input current pulse generated by the SiPM and converts the total collected charge into a proportional output voltage. The output voltage is defined by the feedback capacitor: This can also be expressed as: Where: The total charge generated by the Silicon Photomultiplier (SiPM). The feedback capacitance of the integrator or transimpedance stage. The time-dependent input current pulse from the detector.
    • Transimpedance Amplifier (TIA) Configuration. In this configuration, the output voltage is proportional to instantaneous input current: Where: The feedback resistor that determines the gain of the transimpedance stage. The instantaneous input current flowing from the sensor into the amplifier.
  • Differential ADC Driver: AD8139 - Converts the single-ended amplifier output into a differential signal suitable for the ADC input. It also sets the required common-mode voltage (1.5 V) needed by ADC.
  • Bias Supply - Provides a stable and filtered 29 V bias voltage for the SiPM. The bias voltage is adjustable to compensate for temperature-dependent gain variation of the SiPM.

Note: Analog pulse shaping and peak detection are not used - raw signal is digitized directly and processed in FPGA.


Digital processing

Performs high-speed sampling and real-time pulse analysis:

  • ADC: AD9238 - 2-channel, 12-bit, 40 MSPS sampling rate. ch channel produces 480 Mbps (60 MB/s), resulting in 960 Mbps (120 MB/s) total sample data rate.
  • FPGA: iCE40HX4K - Real-time digital pulse processing (trapezoidal filtering, peak detection, histogram generation)
  • SPI Flash: MX25L1606EM1I-12G - Stores FPGA configuration bitstream
  • SPI Port - External header for FPGA programming
  • UART to USB Converter: CH340X - Communication interface to PC.


Clock Subsystem

to be refined


Power Distribution

to be refined

Tools: KiCad.

Software

The project is developed using a completely open-source FPGA toolchain. The RTL code is written in VHDL-2008, synthesized with Yosys + GHDL, and targets...

Read more »

  • Reusable, testable blocks in LTspice

    Robert Gawronan hour ago 0 comments

    I'm testing the model of my analog frontend against requirements in LTspice, and in this post, I will share an LTspice hack I came up with for the problems below:

    • There are many configuration analysis models in LTspice that I need. If I kept everything in one file, I would need to comment/uncomment what I need for each simulation. This is annoying.
    • Depending on the tests, I need different models of the SiPM. Again, I could connect/disconnect what I need, but this is annoying and error-prone.
    • If I split these into different LTspice simulations and then change the analog frontend (for example, a resistor value), I need to manually update every single file to keep them coherent.

    The solution is to create a symbol for the part under test (the analog frontend) and reuse it in all my LTspice test benches. Since the symbol lives in two files (.asy and .sub), they just need to be regenerated from the main analog frontend model, and then all test models can use them.

    It looks simple and reasonable, but it didn't work well in LTspice at first..

    I made the tutorial below for myself and others on how to do it

    Step 1: pure model (.asc file)

    We create the model of the component under test (as an .asc file) without:

    • Any power sources
    • Any sensors
    • Any LTspice directives

    Just a pure circuit with no external components.

    Add labels to its inputs, outputs, and power nodes (GND doesn't need one). Anything that needs to be connected in the test bench models must be labeled; otherwise, those nets won't be visible in the subcircuit.

    Step 2: symbol of the model (.asy file)

    Now we create the .asy file (representing how the component looks on the schematic and defining its pin names and numbers):

    Click "Hierarchy" -> "Create a New Symbol from this Schematic", and click Yes.
    Now we have our symbol. Note that it is filled with a background color (grey).

    Go to "Attributes" -> "Edit Attributes".
    The Prefix must be "X". This is important because it tells LTspice to treat the component as a subcircuit (otherwise, it won't work).
    .asy file of the tested model should be in the same folder as its .asc file.

    Step 3: behavioural model (.sub file)

    We need a .sub file (which describes how the circuit behaves, this is the real model). The automatic generation never worked well for me, so I made a workaround.

    Open the .asy file in a text editor and find the entries like these:

    SYMATTR Value AFE
    
    ...
    
    PINATTR PinName SIPM
    PINATTR SpiceOrder 1
    PIN -112 -32 LEFT 8
    PINATTR PinName VCC_5V
    PINATTR SpiceOrder 2
    PIN -112 0 LEFT 8
    PINATTR PinName VREF
    

    We are interested in the names found after PinName, they will be different depending on what is on the model (for me they are as above).

    Next, create the unpopulated .sub file. The subcircuit file for the tested model should be in the same folder as the .asc and .asy files. They should all have the same filename, differing only by their extensions.

    Using a text editor, place the following dummy content into the .sub file:

    .SUBCKT AFE SIPM VCC_5V VREF CSA_OUT OUT_N OUT_P
    ** CIRCUIT_BODY_START **
    ** CIRCUIT_BODY_END **
    .ENDS AFE
    

    The line starting with .SUBCKT is important, based on your specific model:

    • First element: This is the name of your model (this must match the SYMATTR Value from the .asy file).
    • Then goes the PINS, list the pins exactly as they appeared in the .asy file (PINATTR PinName). The order matters!

    Place the Python script in the same folder as your files and run it.

    import re
    from pathlib import Path
    
    
    def extract_net_body(net_path: Path) -> str:
        """
        Extract all lines from .net except .end (case insensitive).
        """
        lines = net_path.read_text(encoding="utf-8").splitlines()
    
        filtered = []
        for line in lines:
            stripped = line.strip().lower()
            if stripped == ".end":
                continue
            filtered.append(line)
    
        return "\n".join(filtered)
    
    
    def replace_subckt_body(sub_path: Path, new_body: str) -> None:
        """
     Replace content between CIRCUIT_BODY_START and CIRCUIT_BODY_END
    ...
    Read more »

  • SiPM internal capacitance, transimpedance and charge amplifiers

    Robert Gawron04/18/2026 at 15:46 0 comments

    There has been a lot going on with the project lately :)

    The SiPM sensor I originally planned to use (MICROFC-60035-SMT-TR1) has 3.4nF internal capacitance, which is huge. Normally, to compensate for this in a transimpedance amplifier (TIA), a capacitor is connected between the inverting input and the output. Since the capacitance is so large, the circuit will effectively behave more like a charge amplifier rather than a true transimpedance amplifier.

    I think this may be a problem because a charge amplifier, by its nature, integrates the input signal. If we simplify the math: integrals are slow, derivatives are fast. There are gamma spectrometers with charge amplifier as first stage so the design is not wrong however. The pulses will be slower and wider, so the device will have lower time resolution. In that case, using a fast ADC and FPGA might be overkill, also fast ADC are expensive.

    My idea is to switch from the AD9226 (65 MSPS, single channel) to the AD9238 (pin-compatible variants: 20/40/65 MSPS, dual channel). I plan to use the 40 MSPS version for now and switch to a higher sampling rate later if needed. This already saves roughly 20 euros.

    Since the ADC has two channels, I could use two smaller SiPMs (3×3 mm) with two analog front-ends. Two channels require twice as many op-amps, so the savings form choosing different ADC are eaten, but I still think this is a better solution because:

    • The sensor capacitance is roughly x5 lower.
    • Having two channels gives the opportunity to perform some digital processing in the FPGA (I don’t have idea about digital processing yet, but I will look into it later).

    The downside is that the total sensor area will be twice smaller: 6x6 mm vs. two 3x3 mm sensors.

    In the image below, you can see how one channel currently looks. It includes a TIA and a differential amplifier. Due to the ADC change, the element values will change, but the general idea will remain the same I think.

  • Try to keep it easy to solder and cheap

    Robert Gawron03/25/2026 at 22:34 0 comments

    I think it's best to handle all the math is in the FPGA (but I don't know FPGA). That way, no need for a peak detector or RC circuits, and it’s a lot easier to fix code than it is to reroute a PCB.  Also I've tried to choose easy to solder chips:

    • MICROFC-60035-SMT-TR1 as a sensor
    •  LTC6268 as transimpedance amplifier
    •  LTC6363 differential amplifier
    •  AD9226 fast "and good?" ADC
    •  iCE40HX4K + SPI flash for data handling
    •  maybe some STM32 for communicating with outside world

    TBD:

    •  SW tools for iCE40HX4K 
    • divide project on what can be done in v1 vs what can be touched in v1, learned and finalized later.

  • LTspice+JupyterNotebook simulation of sensor biasing

    Robert Gawron03/21/2026 at 18:15 0 comments

    Most important, there's  a relatively cheap photomultiplier diode, looks perfect for this project: MICROFC-60035-SMT-TR1.

    I've decided to change plans and use a regular DC/DC converter plus a silent voltage regulator at the output; this way, the output voltage will be well-stabilized and can be adjusted for temperature changes. 

    If I made the math right, mentioned above diode needs a bias of 28.89V - 29.22V (why it's not constant later on in post) assuming the device will work in temperature range of 20C-35C (meaning 15C span)?

    I've made this simulation and it seems to work (but it's just a simulation):

    The silicon photomultiplier (SiPM) requires not only a stable bias voltage, but also temperature-dependent voltage adjustment. This is necessary to compensate for the parameter called "temperature dependence of Vbr" (breakdown voltage), specified in mV/°C. As the SiPM temperature increases, its breakdown voltage shifts, causing its detection characteristics to change. To maintain consistent meassurements, the bias voltage must be increased proportionally - by the specified mV/°C value for each degree of temperature rise.

    The TPS7A4901DGNR regulator uses a feedback (FB) pin to monitor its output via a resistor divider. By injecting additional voltage into this node, we trick the regulator into sensing a higher-than-actual output, causing it to reduce the real output voltage. This provides a mechanism for precise voltage adjustment through an external control signal directly from microcontroller or via DAC for better precision.

    I didn't simulate gathering the actual temperature, as this seems easy.

    The diagrams were made in LTspice, and then raw output files were parsed in Jupyter Notebook Python to produce the above diagrams; this way, the diagrams look much better, and it's possible to do extra math.

    Jupyter Notebook is a web server that acts as a nice wrapper for small scripts, etc.; it's not a full IDE, but it works great for small snippets of code. It's containerized and launched inside Docker, but the web port is exposed so that on the host it can be accessed via a web browser.

  • Choosing a Scintillator crystal, PIN photodiode, and its biasing

    Robert Gawron03/18/2026 at 19:08 0 comments

    Choosing detector elements

    This project will be much more expensive than what I was thinking - scintillator crystals and PIN diodes are expensive! I've found a shop online that has scintillator crystals, and it seems to me that this one will be good:

    Hopefully, the frontend is the most pricey part, I think.

    Choosing sensor biasing

    The PIN diode can be biased either negatively (pulses will be positive, meaning signal peaks when a particle is detected will be higher than the background) or positively (peaks will be lower). Negative biasing seems better, so I will stick to it.

    The S3590-08 can withstand 100V of reverse voltage, so I plan to bias it with -70V - that should be good enough and it gives margins (I want to be extra safe since it costs money!).

    The easiest and cleanest way, at least for a prototype, is to just use 12V batteries in series. I've already used this when I was building a semiconductor radioactivity detector (which is a much more simplified version of the gamma spectrometer being designed here). The problem with batteries is that they lose voltage over time (although the PIN diode takes almost no current) and they take up a lot of space on the PCB/device.

    I was thinking about a DC/DC converter to produce the needed -70V, but then I would need a coil or a transformer in it, and I don't want to mess up the small signal from the diode with EMI interference from a coil. Maybe that's not a problem at all; I don't know, as I haven't built such a thing before.

    I then moved toward a voltage multiplier and designed a Dickson charge pump using LTspice, which at least in theory would give me the needed -70V from a usual 5V. Here is the circuit and simulation:

    Will this circuit work in real conditions with all the parasitic capacitances, resistances, etc? I don't know, so I will put space on the PCB for both batteries in series and this charge pump. If the pump works, great, if not, I will use batteries in the first prototype.

View all 5 project logs

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates