See my projects ↓

Technical skills

Select an underlined skill to highlight the projects where I used it.

RF, ANALOGUE & PCB

and simulation (ANSYS HFSS), (op-amps, instrumentation and charge amplifiers), impedance matching, schematic capture and PCB layout, .

DIGITAL & EMBEDDED

RTL design, (Intel Quartus, Questa, Cyclone V), , I2C/SPI/UART protocols.

SIMULATION & INSTRUMENTATION

, , , /LTSpice, plus oscilloscopes, signal generators, , .

PROGRAMMING

(NumPy, pandas, scikit-learn), /Simulink, .

Engineering projects 2023–2026

Select any project to read the problem, the method and the result.

SELECTED PROJECTS

Each of these received a first-class mark.

Fabricated capaciflector and flexible PCB, and the capaciflector integrated with the RFID tag

Wireless RFID E-Textile Breathing Sensor

First-class mark

Team of 5 · I led the sensor and antenna · 2025–26

ANSYS HFSSKiCadMultisimRFIDFlexible PCB

A tag stuck to a shirt that reads breathing with no battery, because the reader's radio signal powers it. I designed its sensor, a capaciflector, and its antenna. The chip's datasheet only described simple two-plate sensors, so I worked out how to drive a three-electrode sensor from a single pin. It picked up clear breathing from a metre away.

Read more

The brief was continuous breathing monitoring for patients outside intensive care, with nothing on the patient that needs a battery. Our team of five built a passive RFID tag that sticks to clothing. I led two parts of it, the sensor (a capaciflector) and the antenna, from the first simulations to the finished tags.

The first puzzle was the chip. It had a single sensing pin, and its datasheet only covered simple two-plate capacitors, but a capaciflector has three electrodes. Working from first principles, I routed the ground plane to the sensing pin so the chip drove both the reflector and the sensor electrode, and that went into the final design. I simulated the sensing principle in Multisim, and designed and simulated the antenna in ANSYS HFSS, including the effect of the wearer's body.

Then fabrication failed, twice. Our screen-printed sensors shorted because of air bubbles in the dielectric, in week 7 and again in week 8. On my supervisor's advice I switched to Kapton–copper clad and made it work reliably. I designed ten flexible-PCB layouts in KiCad, chose the four most promising to manufacture, fixed their manufacturing-rule errors and assembled the tags by hand. Testing raised two more problems. Long wires made the data noisy, and a more compact version lost read range because the sensor coupled to the antenna. A smaller sensor geometry fixed both.

In the end it read breathing clearly from a metre away. With breathing paced at 6 per minute by a metronome, we tested from 80 to 160 cm. The pattern was clear up to 100 cm, still recognisable at 120 cm and weaker beyond. 100 cm was the best balance between patient comfort and signal, with an average peak-to-peak swing of 75 raw counts. The reader picked up several tags at once, each with its own ID, so one reader could in principle watch a whole ward. We only had one working breathing sensor, though, so readings from several patients at once are still untested.

If I did it again, I'd test the fabrication route in week 1, not week 7.

The finished tag adhered to a shirt, worn to monitor breathing
The finished tag adhered to a shirt, worn to monitor breathing
Capaciflector raw output at 100 cm over about 2,000 samples, showing regular breathing cycles, with a 50-sample moving average drawn on top
Capaciflector raw output at 100 cm over about 2,000 samples, showing regular breathing cycles, with a 50-sample moving average drawn on top
NISC datapath, showing the program counter and 16-bit control word store, the control word split into seven fields, and the address register, waveform ROM, multiplier, bit slice, adder, mux and accumulator they drive

NISC Processor on FPGA

First-class mark

Solo project · 2026

SystemVerilogQuestaIntel QuartusCyclone V

A processor with no instruction decoder, where each instruction word is itself the set of control signals. I built one to run a Gaussian smoothing filter over a 256-sample waveform as cheaply as possible on an FPGA. It used just 63 of the FPGA's logic blocks and one multiplier, and ran correctly in simulation and on the board.

Read more

The task was to smooth a 256-sample waveform with a 5-tap Gaussian filter on a Cyclone V FPGA, scored on how little hardware the processor used. Most processors encode their instructions and then spend logic decoding them.

I went the other way. In a No-Instruction-Set Computer (NISC), each instruction word simply is the set of control signals, so there is no decoder at all. The program memory gets slightly wider, but removing the decoder saves more than that costs.

The whole filter came down to nine instructions. It produces one output sample every seven clock cycles, using 63 ALMs (the Cyclone V's basic logic blocks), one DSP multiplier and no block RAM. All eight test cases, covering positive, negative and boundary indices, passed in simulation and on the DE1-SoC board, each checked against a Python model of the exact hardware arithmetic. To push it further, I'd give the waveform memory a synchronous read so it could use block RAM and possibly free logic, and pipeline the multiply-accumulate for a faster clock.

Questa simulation, with the program counter stepping through the MAC instructions while the accumulator and LED output build the smoothed sample
Questa simulation, with the program counter stepping through the MAC instructions while the accumulator and LED output build the smoothed sample
The design running on the DE1-SoC board, result shown on the LEDs
The design running on the DE1-SoC board, result shown on the LEDs
Initial differential amplifier circuit in NI Multisim that exposed the loading problem

Accelerometer Signal Conditioning for Engine Vibration

First-class mark

Solo project · 2026

NI MultisimAnalogue designHand calculation

An engine controller needs a clean, correctly scaled vibration signal from an accelerometer, on a single 5 V supply. My first amplifier quietly lost most of the signal, 78% on paper and 60% in simulation. Bigger resistors still lost 13%, so I traced the cause to the sensor's output impedance and changed the circuit to an instrumentation amplifier. The loss fell to 0.35%, and the amplifier delivered 102.5 mV/g, inside its 100 ± 5 mV/g target.

Read more

An engine controller needed a clean vibration signal, scaled to 100 ± 5 mV/g, from a piezoresistive accelerometer, all on a single 5 V supply. I designed the whole chain, with the sensor modelled as a Wheatstone bridge, followed by an amplifier and cascaded second-order Butterworth filters passing 20–170 Hz, the vibration range of an engine running at 1,200–10,200 RPM.

My first amplifier quietly threw most of the signal away. A simple differential amplifier loaded the sensor bridge. I predicted 78% signal loss, and simulation showed 60%. It also couldn't swing negative on a single 5 V supply, so it clipped at +10 g. Raising its resistors tenfold still lost 26% on paper and 13% in simulation, more than the 10% a precision measurement can tolerate, so instead of retuning further I looked for the cause. The bridge's output impedance was too high for the amplifier's input impedance. A three-op-amp instrumentation amplifier, with its much higher input impedance, cut the loss to 0.35%, and biasing its output at 2.5 V kept the full ±10 g range inside the supply.

The amplifier delivered 102.5 mV/g, inside the 100 ± 5 mV/g target. With the filters added, sensitivity was 96 mV/g at 100 Hz and peaked at 110 mV/g at 50 Hz, 10% over target. Gain varied by 1.2 dB from 50 to 100 Hz, within the 1.5 dB flatness target. A final change cut the current draw by 7.1%, to 2.87 mA. I calculated every value by hand first, then checked it in Multisim. It has only been simulated so far, so building it on a protoboard is the obvious next step, to see real op-amp offsets.

The complete chain in Multisim, from the sensor bridge model through the three-op-amp instrumentation amplifier to the cascaded high-pass and low-pass filters
The complete chain in Multisim, from the sensor bridge model through the three-op-amp instrumentation amplifier to the cascaded high-pass and low-pass filters
Simulated frequency response of the complete chain, a band-pass response peaking near 55 Hz, inside the 20–170 Hz engine band
Simulated frequency response of the complete chain, a band-pass response peaking near 55 Hz, inside the 20–170 Hz engine band
COMSOL Euler-Bernoulli beam simulation showing the first bending mode of the NEM resonator

NEM Resonator Characterisation

First-class mark

Solo project · 2026

Lock-in amplifierPythonCOMSOL

A 2 µm silicon beam that vibrates about 98 million times a second. I measured how it rings with lock-in detection, fitted the response in Python, and built a COMSOL model that matched within 3%. Driving it harder pushed the resonance up by 3 MHz, because the beam stiffens as it swings further, like a string pulled tighter.

Read more

The device was a 2 µm silicon beam, a nanoelectromechanical (NEM) resonator, and the job was to find out how it really behaves. I measured it electrically with a lock-in amplifier, using a 1-ω mixing technique, while sweeping the back-gate bias from 0 to 2 V and the drive power from 0 to −8 dB. The lab's LabVIEW program logged each sweep, and fitting Lorentzian curves in Python gave the resonance frequency and quality factor.

It resonated near 98 MHz, with a quality factor of 270–400. To check that against physics, I built COMSOL eigenfrequency models of a pure silicon beam (99.13 MHz) and a silicon beam with an oxide shell (95.02 MHz). The measurement sat between the two, and the model matched within 3%.

Driven harder, the resonance climbed by 3 MHz. That is Duffing stiffening, where the beam's effective stiffness rises with the size of its swing. I also wrote a critical review of a published method for modelling this nonlinear behaviour on identical devices. Fitting a Duffing model directly, rather than tracking how the peak moves, would describe that shift more accurately.

Measured in-phase current at five drive powers, stacked. The fitted resonance moves from 97.3 MHz at −8 dB to 100.3 MHz at 0 dB, the Duffing stiffening signature
Measured in-phase current at five drive powers, stacked. The fitted resonance moves from 97.3 MHz at −8 dB to 100.3 MHz at 0 dB, the Duffing stiffening signature
27mm piezoelectric disc beside its 3D-printed housing

Piezoelectric Accelerometer Package

First-class mark

Solo project · 2026

SolidWorks3D printingNI MultisimLCR meter

I turned a bare piezoelectric disc into a working accelerometer, with a 3D-printed housing and a two-stage charge amplifier, and tested it on a shaker from 0.5 Hz to 5 kHz. The biggest peak came at 850 Hz, nowhere near the datasheet's 4.2 kHz. Rather than blame the sensor, I traced the likely cause to the shaker's own resonance. A second peak at 2.5–2.7 kHz, which appeared once mass was added, is most likely the disc's.

Read more

The goal was to turn a bare piezoelectric disc into a working accelerometer. I designed a 3D-printed housing and a two-stage charge amplifier with a gain of 20, and first checked the disc itself on an LCR meter, which measured 18.44 nF at 1 kHz, within the datasheet tolerance.

The biggest peak was in the wrong place. On a shaker, from 0.5 Hz to 5 kHz with no added mass, 0.7 g and 1.4 g, the biggest peak sat near 850 Hz, far from the datasheet's 4.2 kHz. Rather than blame the sensor, I attributed it to the shaker's own resonance, since lab shakers typically resonate at a few hundred hertz. With mass added, a second peak appeared at 2.5–2.7 kHz and moved down as the mass grew, most likely the disc's own resonance.

Simulation and measurement didn't match exactly, and I could explain why. The protoboard added parasitics, a chain of series resistors stood in for one 10 MΩ part, and the probe loaded the circuit. The lab had no 30 MΩ resistor, so the low-frequency cutoff came out at 1.6 Hz instead of the specified 0.5 Hz. A reference accelerometer on the shaker is the first thing I'd add, to separate its response from the sensor's.

SolidWorks model of the housing, with an internal ledge to seat the disc, a central hole for the shaker rod and side windows for the wires
SolidWorks model of the housing, with an internal ledge to seat the disc, a central hole for the shaker rod and side windows for the wires
The two-stage charge amplifier built on protoboard
The two-stage charge amplifier built on protoboard
Measured frequency response across three mass conditions, showing the resonance peak shift
Measured frequency response across three mass conditions, showing the resonance peak shift
Classification report showing precision, recall and F1 score for the SVC model

Fake News Detection with Machine Learning

First-class mark

Solo project · 2024

Pythonscikit-learnpandasNumPy

Can a model spot fake social-media posts from their wording alone? On posts from the MediaEval dataset, I compared a tuned Support Vector Classifier with the much simpler Naive Bayes. The simple model won, F1 0.86 against 0.83, with a smaller train–test gap, so it generalised better.

Read more

The question was whether a model can tell fake social-media posts from real ones using only their wording. I worked with posts from the MediaEval dataset, turned the text into TF-IDF word features, and compared single words (unigrams) with pairs of words (bigrams). Unigrams scored higher, because hashtags carry their meaning as single words.

The simpler model won. I tuned a Support Vector Classifier and a Multinomial Naive Bayes model with a grid search, then evaluated both on a held-out test set using micro-averaged F1 and confusion matrices. Naive Bayes scored 0.86 against the classifier's 0.83, and its smaller gap between training and test scores showed it generalised better.

Training versus test F1. The Support Vector Classifier drops from 0.98 to 0.83, while Naive Bayes holds closer, from 0.92 to 0.86
Training versus test F1. The Support Vector Classifier drops from 0.98 to 0.83, while Naive Bayes holds closer, from 0.92 to 0.86
SUBSTITUTION
frequencies alone ✗ → frequencies + word patterns ✓
REPEATING-KEY XOR (HEX FILE)
1-byte brute force ✗ → 2-byte key → solve each byte stream ✓
KEYED COLUMNAR TRANSPOSITION
5 other transpositions ruled out ✗ → column order by quadgram score ✓
Attack path for each cipher. Every answer was verified by re-encrypting it. Keys, ciphertexts and plaintexts are deliberately not shown.

Cryptanalysis of Classical Ciphers

First-class mark

Solo project · 2026

PythonFrequency analysisCryptanalysis

Three secret messages, three unknown ciphers, no keys. I broke a substitution cipher, a repeating-key XOR cipher hidden in a hex file, and a keyed columnar transposition, and proved each answer by re-encrypting it back to the exact ciphertext.

Read more

Three messages, three unknown ciphers, no keys. I wrote the attacks in Python, and every answer had to pass the same test. Re-encrypting the plaintext had to reproduce the original ciphertext exactly.

The substitution cipher beat pure statistics. Its letter frequencies still followed the shape of English, but mapping the most common letters straight onto e, t, a and so on produced nonsense. With only 269 letters the rankings were unreliable, and two letters were tied at 4.8%. Combining the frequencies with word patterns cracked it, and the finished key had a structure that explained why my first guess, a simple Caesar shift, had failed.

The hex file gave itself away by halves. A single-byte key, worked out from a hint about the first letter, turned every other character into English and left the rest garbled, and a brute force over all 256 single-byte keys confirmed none worked. The even and odd bytes also sat in different value ranges, which pointed to a 2-byte key, so I split the file into two streams and brute-forced each one separately.

The transposition took six attempts. The text's length, 84 letters, limited the possible grid sizes. Simple columnar, rail-fence, route, skip and row-rearrangement ciphers all failed, and I ruled out every rail-fence setting with a quadgram fitness score, a measure of how English-like text is. Then I brute-forced every column order of a keyed columnar transposition, scored each the same way, and one produced English.

Bar chart of the substitution cipher's letter frequencies, ranked from most to least common, following the English curve closely, with an index of coincidence of 0.067 against 0.065 for English and 0.038 for random letters
Substitution. The ranked letter frequencies follow English, and the index of coincidence (0.067) matches English (0.065), not random text (0.038)
Bar chart showing that a 1, 3, 5, 7 or 9-byte key decodes only about half of the 90-byte file into readable text, while every even length decodes all of it and 2 bytes is the shortest
XOR file. Odd key lengths decode only half the file and every even length decodes all of it, so the key is 2 bytes
Dot plot of quadgram fitness. Every rail-fence setting from 2 to 20 rails scores between −7.1 and −6.3, and keyed columnar widths 2 to 9 score similarly except width 6, which scores −4.1, inside the range of ordinary English
Transposition. No rail-fence setting comes close to English, but one columnar width does. Statistics recomputed for this page, with keys and text not shown

MORE PROJECTS

A design review, a power-systems study and a security lab series.

Photodiode Transimpedance Amplifier Design Review

Solo take-home challenge set by a photonics company · 2026

Analogue designNoise analysisLoop stabilityPCB review

Review a photodiode amplifier board (a transimpedance amplifier, TIA) and say what's wrong with it. I found a placeholder mistake, repeated across the board, that switched off the feedback and compensation networks, and a resistor in the wrong place on the sensitive input node. Then I wrote the bring-up and test plan for gain, bandwidth, noise and stability.

Read more

The challenge was to review a two-stage photodiode amplifier board across its schematic, layout and bring-up, and say what's wrong. Instead of checking each part alone, I treated input capacitance, feedback resistance, compensation, bandwidth and noise as one connected problem, and checked my calculations against manufacturer data.

The biggest find was a placeholder pattern, repeated across the board, that disabled the feedback and compensation networks. I also found a resistor on the summing node that most likely belonged on the guard buffer's output, too little phase margin in both stages (only 3–5° in the second), and no test points. To make the board testable, I proposed current-injection points and a step-by-step bring-up and validation plan covering gain, bandwidth, noise and stability.

The reviewers' feedback taught me how to report, not just what to find. Next time I'd lead with a one-page list of board-stopping issues before the secondary findings, state the assumptions behind any component recommendation, and confirm layout findings in the native PCB viewer before reporting them.

2000 MW HVDC Subsea Interconnector Design Study

Team of 6 · I owned power quality · 2026

MATLAB/SimulinkPythonPower electronicsHVDC

How do you join two power grids that don't run in sync, under 200 km of sea, carrying 2000 MW at ±525 kV? Our team chose the converters, ratings, cable and protection for a High-Voltage Direct Current (HVDC) link. I owned power quality, and showed that the Modular Multilevel Converter's (MMC) stepped output keeps distortion within the IEEE 519 limit without banks of tuned filters. In my own simulations of a line-commutated converter, the filters I designed cut harmonic distortion from 27% to 7%.

Read more

The brief was a 2000 MW link under 200 km of sea between two power grids that don't run in sync. Our team of six chose the converter type, ratings, cable and protection, basing each decision on case studies of links already in operation. We settled on a ±525 kV bipole using half-bridge Modular Multilevel Converters (MMC).

I owned power quality and filtering. The MMC builds its AC output as a fine staircase of small voltage steps, and my section showed that this keeps AC-side distortion within the IEEE 519 limit without banks of tuned filters. I also sized the phase reactor. The next step we identified was electromagnetic transient studies of resonance between converter and grid before finalising the reactors.

Separately, I simulated other converter types in MATLAB/Simulink. For a line-commutated converter (LCC), I modelled the output voltage, commutation overlap and harmonics, then designed tuned 5th and 7th and damped 12th AC filters that cut total harmonic distortion from 27.27% to 7.13%. I also simulated voltage-source converter power flow and a 3-level converter, and produced the plots in Python.

Bar chart of AC-side total harmonic distortion, 27.27% with no filter, 16.21% with a tuned 5th filter, 7.04% adding a tuned 7th, 7.13% adding a damped 12th
Bar chart of AC-side total harmonic distortion, 27.27% with no filter, 16.21% with a tuned 5th filter, 7.04% adding a tuned 7th, 7.13% adding a damped 12th

Hardware Security Labs

Lab series with one partner · 2025

Software-defined radioChipWhispererNFC/RFIDGSM

Four attacks on real hardware. We recovered the secret keys from a MIFARE Classic access card, recovered a password and an AES key from a microcontroller's power consumption, and replayed a 433.92 MHz remote-control signal to trigger a relay. We also captured and decoded a test GSM network's broadcasts.

Read more

Across a series of labs, my partner and I attacked real hardware. We started with an access card, using libnfc tools to recover the secret keys from a MIFARE Classic tag.

Then we read a chip through its power supply. With a ChipWhisperer, we matched a microcontroller's power traces to the loop iterations in its C code, recovered a password one character at a time from how the power changed, and extracted an AES key with correlation power analysis.

Our radio replay attack worked, with a catch. We recorded a 433.92 MHz remote-control signal and replayed it to trigger a relay. It worked 96% of the time, but only within 6 cm. Finally, we captured a test GSM network's broadcasts with the radio and decoded them in Wireshark, reading the temporary IDs phones are paged by and checking whether encryption was switched on.

EARLIER WORK

  • Embedded Networked Systems

    team of 5 · 2023–24 — wrote the data-link and physical layers in C on an AVR microcontroller with an RFM12 radio, including bit-stuffed framing, stop-and-wait retransmission with a 0.5 s timeout and CRC-16 error checking. My layers sent and acknowledged messages between two boards, but a radio-library build error stopped the team's layers being joined up before the demo. The lesson was to integrate early.
  • Transmission Line Equivalent Circuit

    2023 — modelled a coaxial line as a cascade of LC sections in Multisim from its geometry (Z₀ ≈ 74.9 Ω) and compared lossless and lossy behaviour under different loads.
  • Chaotic Oscillator

    lab, with a partner · 2023 — built an inductor–diode circuit and observed period-doubling bifurcation on its way to chaos.

Contact

Southampton, England, UK — open to relocation.
UK Graduate Route visa application in progress — unrestricted right to work, no sponsorship required.
Projects using