Inductors · Pulsed measurements · Current bias
The current through a coil rises more slowly as a series resistor takes a larger share of the supply voltage. The core can change the ramp too. Measure the actual voltage across a Coilcraft LPS4018-333MRC during one bounded pulse, then compare two sections of the ramp at different currents. The calculation separates the voltage lost in the resistor and the winding from the voltage that changes magnetic flux linkage.
What this experiment measures. Each result is an average incremental inductance over a specified current band in this fixture. It depends on the part, temperature, excitation and setup. The numbers in the worked example come from a synthetic trace. Record real results only from your own capture.
Use the Coilcraft LPS4018-333MRC, a two-terminal shielded surface-mount inductor roughly 4 × 4 mm and under 1.8 mm high. Use its manufacturer land pattern on a small board. The coil has no electrical polarity for this circuit. In the order code, M is ±20% inductance tolerance, R is the termination, and C identifies the listed reel packaging. Coilcraft specifies 33 µH at 100 kHz and 0.1 V root mean square with no direct-current bias. The winding direct-current resistance (DCR) is at most 0.42 Ω at 25 °C.
| Published item | LPS4018-333MRC value at 25 °C | How to use it here |
|---|---|---|
| Nominal inductance | 33 µH ±20% | Small-signal reference at the stated test conditions |
| Saturation current, Isat | 0.58 A for a 10% inductance drop, 0.64 A for 20%, 0.68 A for 30% | Manufacturer characterization points to compare with the experiment |
| Heating current, Irms | 0.55 A for a 20 °C rise and 0.80 A for a 40 °C rise | Separate thermal guidance tied to the manufacturer’s setup |
Magnetic saturation is gradual. A higher current can make an additional increment of current produce less added flux linkage. The listed percentage points do not tell you exactly what one microsecond-scale capture will show. The heating figures also depend on the board and airflow. Use short pulses with ample idle time and stop if any part heats appreciably or if the trace fails to return near zero before the next pulse.
Use an isolated low-voltage output from the SIGLENT SPD3303X, channel 1 only, with its enclosure intact. Set 5.00 V and a 0.70 A current limit. Join its negative output to one fixture return point. The scope ground references and the generator shield connect to that same return. Use no additional load, grounded power source, or external bus. Use a small board with short power and diode loops. Keep the high-current return copper separate from the short measurement branches up to the shunt pads.
With supply off and disconnected, measure the series resistances. Use four-wire measurements for the shunt and coil DCR if possible, or characterize lead and contact resistance separately. Measure DCR on the cold, electrically isolated coil with a meter method that resolves the fraction of an ohm. The analyzer requires a measured shunt value from 0.990 to 1.010 Ω and positive measured DCR no greater than 0.42 Ω at 25 °C. If a measurement is outside those screens, check temperature, lead error and the part before using this analyzer.
At the highest accepted measured rail of 5.10 V, the two lowest 1% resistance values are 6.732 Ω and 0.990 Ω. Even if the coil DCR and switch resistance were zero, an intact series path bounds the rising current at 5.10 V divided by 7.722 Ω, under 0.661 A. A 25 µs pulse at 100 Hz has a 0.25% commanded duty cycle. The current bound assumes correct wiring and a verified rail. It does not permit a held-on switch or operation with a shorted shunt. The supply limit is a separate backstop and can respond too slowly for an individual pulse.
At switch opening the inductor current retains its direction through the shunt. The return current enters D1’s anode and exits its cathode into T, so the CH1 trace goes below the reference. Keep the negative interval in the capture. It verifies the release path, while both inductance estimates use only the earlier rising portion.
Let VT and VS be both coil-terminal voltages relative to return, and let z be the pre-pulse median of the shunt channel. Use the same measured shunt resistance for both bands. The analyzer removes z from the shunt channel. Check the T channel zero separately with its tip on return before moving it back to T.
I = (VS − z) / RS
Vmag = VT − (VS − z) − I × DCR
The first two voltage terms recover the voltage across L1 itself. Subtracting I times winding DCR estimates the part of that voltage that changes flux linkage. Integrate the resulting voltage from one current crossing to the next, then divide by the current step.
Lband = ∫ Vmag(t) dt / (Ihigh − Ilow)
Volts times microseconds divided by amperes gives microhenries. Choose the 0.16–0.25 A early band and 0.52–0.58 A late band. The code interpolates boundary crossings and uses trapezoidal integration on actual timestamps. It excludes 0.60 µs next to either switching edge, and qualifies pulse length, current range, shunt zero, the negative clamp and return to near zero. Keep the scope probes compensated and check timing skew between channels with a common signal. Wiring, offsets and DCR uncertainty matter most when the corrected voltage is small late in the pulse. At 0.55 A, a 0.02 Ω DCR error shifts that voltage by 11 mV. Compare that with the actual corrected voltage in the fitted band before interpreting a small change.
| Synthetic result | Current band | Inductance |
|---|---|---|
| Early | 0.16–0.25 A | 33.00 µH |
| Late | 0.52–0.58 A | 28.00 µH |
The synthetic model gives a −15.15% change relative to the early band, peaks near 0.608 A and includes a −0.32 V clamp. For a real coil, run the same analysis on repeated qualified shots and compare their spread with the difference between the bands. A smaller inductance late in the pulse is consistent with a core that is losing incremental response. If the apparent difference changes markedly when you correct offsets, replace long probe clips or measure the true cold DCR, resolve those setup errors before drawing a conclusion.
Normalize your scope export to time_us,top_v,shunt_v,gate_v and keep all rows from one shot. This program uses the Python 3 standard library. The experiment archive contains the complete code, synthetic capture, exact JSON, editable figures and tests. Use the blank results worksheet for measurements and observations. For the example, run python3 pulse_inductance.py synthetic-pulse.csv --kind synthetic --rail-v 5 --shunt-ohm 1 --dcr-ohm 0.35. Substitute your capture and measured inputs and use --kind observed for a real shot.
A valid shot can report one estimated band and one unavailable band. A missing or unstable pulse, invalid sample timing, excess current, missing negative clamp or non-monotonic fitted ramp produces an error. The current-screen threshold of 0.67 A leaves a small allowance above the calculated physical ceiling for measurement, and calls for stopping and checking the circuit when exceeded. The bench procedure still requires a pre-check of the physical limiter and the rail. The supplied tests exercise the program on a computer.
"""Estimate band-averaged incremental inductance from one bounded pulse."""
import argparse
import csv
import json
import math
from collections import namedtuple
from pathlib import Path
from statistics import median
Sample = namedtuple("Sample", "time top shunt gate")
Point = namedtuple("Point", "time current inductive_v")
HEADER = ("time_us", "top_v", "shunt_v", "gate_v")
EDGE_GUARD_US = 0.60
def read_capture(path):
with path.open(encoding="utf-8-sig", newline="") as stream:
rows = csv.reader(stream, strict=True)
if tuple(next(rows, ())) != HEADER:
raise ValueError("expected header " + ",".join(HEADER))
data = []
for row in rows:
if len(row) != len(HEADER):
raise ValueError(f"line {rows.line_num}: expected four fields")
sample = Sample(*map(float, row))
if not all(math.isfinite(value) for value in sample):
raise ValueError(f"line {rows.line_num}: nonfinite sample")
if data and not 0 < sample.time - data[-1].time <= .100000001:
raise ValueError(f"line {rows.line_num}: sample gap or non-increasing time")
if not (-1.1 <= sample.top <= 5.3 and -.04 <= sample.shunt <= .72
and -.30 <= sample.gate <= 5.30):
raise ValueError(f"line {rows.line_num}: sample outside voltage envelope")
data.append(sample)
if not data:
raise ValueError("capture contains no samples")
return data
def gate_windows(samples, rail):
low = [i for i, s in enumerate(samples) if s.gate <= .35]
if not low or low[0] == 0 or low[-1] == len(samples) - 1:
raise ValueError("capture requires gate idle before and after one pulse")
if len(low) != low[-1] - low[0] + 1:
raise ValueError("more than one low pulse or unstable gate plateau")
start = samples[low[0]].time
end = samples[low[-1] + 1].time
if not 15.0 - 1e-9 <= end - start <= 25.0 + 1e-9:
raise ValueError("gate pulse width outside 15 to 25 µs")
if start - samples[0].time < 3.0 - 1e-9 or samples[-1].time - end < 75.0 - 1e-9:
raise ValueError("missing 3 µs before pulse or 75 µs after release")
idle = [s for s in samples if s.time <= start - EDGE_GUARD_US or s.time >= end + EDGE_GUARD_US]
if any(abs(s.gate - rail) > .20 + 1e-9 for s in idle):
raise ValueError("gate idle is not within 0.20 V of the measured rail")
baseline = [s.shunt for s in idle if s.time < start]
if abs(median(baseline)) > .01 + 1e-9 or max(baseline) - min(baseline) > .01 + 1e-9:
raise ValueError("shunt zero is unstable or not within 10 mV")
return start, end, median(baseline)
def crossing(points, level, start_index=0):
for index in range(start_index, len(points) - 1):
a, b = points[index:index+2]
if a.current < level <= b.current:
part = (level - a.current) / (b.current - a.current)
return index, Point(a.time + part*(b.time - a.time), level,
a.inductive_v + part*(b.inductive_v - a.inductive_v))
return None
def fit_band(points, low, high):
limits = {"current_range_a": [low, high]}
first = crossing(points, low)
if first is None:
status = "lower_crossing_before_guard" if points[0].current >= low else "lower_current_not_reached"
return {**limits, "status": status, "inductance_uh": None}
last = crossing(points, high, first[0])
if last is None:
return {**limits, "status": "upper_current_not_reached", "inductance_uh": None}
begin, finish = first[1], last[1]
use = [begin, *(x for x in points[first[0]+1:last[0]+1] if begin.time < x.time < finish.time), finish]
if len(use) < 5:
raise ValueError("current band is too short for the captured sample spacing")
peak = use[0].current
for point in use[1:]:
if point.current < peak - .003:
raise ValueError("non-monotonic current within a fitted band")
peak = max(peak, point.current)
if any(x.inductive_v <= 0 for x in use):
raise ValueError("nonpositive inductive voltage within a fitted band")
flux_v_us = sum((a.inductive_v+b.inductive_v)*.5*(b.time-a.time)
for a,b in zip(use,use[1:]))
return {**limits, "status": "estimated", "inductance_uh": flux_v_us/(high-low),
"start_us": begin.time, "end_us": finish.time, "integral_v_us": flux_v_us,
"interpolated_point_count": len(use)}
def analyze(samples, shunt, dcr, rail, kind):
start, end, zero = gate_windows(samples, rail)
current = [(s.shunt - zero)/shunt for s in samples]
peak = max(current)
if peak > .67 + 1e-9 or min(current) < -.02 - 1e-9:
raise ValueError("measured current outside the fixture screening envelope")
clamp = [s.top for s,i in zip(samples,current) if end+.60 <= s.time <= end+2 and i >= .05]
if len(clamp) < 2 or median(clamp) > -.10:
raise ValueError("missing negative top voltage during the clamp interval")
if any(abs(i) > .01 + 1e-9 for s,i in zip(samples,current) if s.time >= end+70):
raise ValueError("coil current did not return near zero before capture ended")
points = [Point(s.time, i, s.top - (s.shunt-zero) - i*dcr) for s,i in zip(samples,current)
if start+EDGE_GUARD_US <= s.time <= end-EDGE_GUARD_US]
early = fit_band(points, .16, .25)
late = fit_band(points, .52, .58)
change = None
if early["status"] == late["status"] == "estimated":
change = 100*(late["inductance_uh"]/early["inductance_uh"]-1)
return {"kind":kind, "rail_v":rail, "shunt_ohm":shunt, "dcr_ohm":dcr, "zero_shunt_v":zero,
"gate_low_start_us":start, "gate_low_duration_us":end-start, "peak_current_a":peak,
"clamp_min_v":min(clamp), "bands":{"early":early,"late":late},
"late_relative_change_pct":change}
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("capture", type=Path)
parser.add_argument("--kind", choices=("synthetic","observed"), required=True)
parser.add_argument("--rail-v", type=float, required=True)
parser.add_argument("--shunt-ohm", type=float, required=True)
parser.add_argument("--dcr-ohm", type=float, required=True)
args = parser.parse_args()
if not 4.90 <= args.rail_v <= 5.10:
parser.error("rail must be 4.90 to 5.10 V")
if not .990 <= args.shunt_ohm <= 1.010:
parser.error("shunt must be .990 to 1.010 Ω")
if not 0 < args.dcr_ohm <= .42:
parser.error("DCR must be positive and at most 0.42 Ω at 25 °C")
try:
data = read_capture(args.capture)
result = analyze(data, args.shunt_ohm, args.dcr_ohm, args.rail_v, args.kind)
except (OSError, UnicodeError, csv.Error, ValueError) as error:
parser.error(str(error))
print(json.dumps(result, indent=2, allow_nan=False))
if __name__ == "__main__":
main()
A measured ramp bends over near 0.55 A. You are tempted to call that part of the curve evidence for a changing inductance. What else do you need from the capture?
Recover both terminal voltages and subtract the winding loss. A fixed inductance also produces a bending current ramp when the limiter reduces the voltage across it. Calculate the average incremental inductance in the early and late bands using the actual corrected voltage. Check the gate, shunt zero and clamp, and compare repeatability before attributing a difference to the core.