Electronics Foundations — Validating Sensor Inputs
Does Compensating a Probe Reveal the Original Circuit?
Goal
Verify separately that a compensated screen is accurate and that it barely changed the original circuit. Write a Python CLI that reads real ngspice files and reproduces the basis for choosing a probe.
Why it matters
Start after knowing the earlier filter-response unit and Python functions, files, complex numbers, lists, dicts, and exceptions. The lab is expected to take 120 minutes. You can extend with +time before the default 60 minutes ends, up to a maximum of 180 minutes. Files disappear when the session ends, so keep the code and report you need separately. This lab uses only an ideal 1V circuit simulation and does not handle real equipment, high voltage, or ground connections.
Environment and materials
The deliverable is /root/probe-loading/analyze.py. Python3 and ngspice42 are in the image, and no external downloads or added permissions are needed. If you add /opt/lab/fixtures/probe_loading to sys.path, you can import simulator. The provided capture(cfg,new_folder) actually runs the circuit and returns the Path of that folder. The parent folder must already exist, and an existing output folder is not overwritten. It leaves input.cir, config.json, solver.log, ac.tsv, and step.tsv, and does not delete new evidence from a failure either.
The circuit structure and values are the ideal model of the earlier reading. From the same ideal input, through a separate Rs, it drives the reference ref and the measured tip. The DUT at each of the two nodes is 100kΩ∥20pF. The probe attaches only on the tip side. unloaded has no extra load and meter=tip, one-x is 1MΩ∥100pF, and active is 1MΩ∥1pF with an ideal meter=tip. ten-x has 9MΩ∥comp_pf from tip to meter and 1MΩ∥90pF from meter to ground. The displayed value is meter×10 for ten-x and meter for the rest.
The AC input is an amplitude of ac_v and a phase of 17 degrees. The step is 0V from 0 to 1µs, then rises to 1V over 5ns and holds until 50µs. ac_v applies only to the AC analysis. The infinite bandwidth and ideal output of active are an educational model and not the specification of a commercial product.
Configuration contract
config accepts only a dict with exactly the 5 keys below. Numbers allow only int and float, and bool, strings, NaN, infinity, and exceeding the float conversion range are a ValueError. Numbers are returned as float, kind as str, and when there is no compensation, comp_pf as None. Do not modify the input.
| Key | Allowed values |
|---|---|
| kind | unloaded, one-x, ten-x, active |
| rs_ohm | 10000 or 50000 |
| comp_pf | for ten-x, 5, 10, 20; for the others only None |
| dt_s | 1.25e-9 or 6.25e-10 |
| ac_v | 0.5, 1, 2 |
AC input and response contract
read_ac accepts a regular file of at most 1MiB. The header is, separated by whitespace, frequency v(in) v(in) v(ref) v(ref) v(tip) v(tip) v(meter) v(meter). Each pair is in the order real part, imaginary part. Ignore blank lines and read exactly 241 rows of 9 finite floats. The absolute value of the voltage components must be 1e6 or less, and the complex magnitude of input, ref, and tip must be 1e-12 or more. meter may be 0. The frequencies must be positive and strictly increasing, and for each i=0..240 equal to 10*10**(i/40) within a relative 1e-10 or an absolute 1e-9. Check the whole interior grid from 10Hz to 10MHz as well. Content errors are a ValueError, and OS file errors may be passed through. The return is a list of 9-float tuples.
response validates the configuration and accepts rows validated by the contract above. It does not change the rows and returns a list of dicts in the same order. With R=Vref/Vin, T=Vtip/Vin, and D=ratio*Vmeter/Vin, each field is as follows.
- frequency_hz: the original frequency.
- reference, tip, display: lists of [real part, imaginary part] of R, T, and D.
- chain_error: abs(D/T−1). It is a complex error, not a simple magnitude difference.
- loading_error: abs(T/R−1).
- loading_gain: abs(T/R).
- loading_phase_deg: degrees(cmath.phase(T/R)). It is the principal phase.
Model and step observation contract
transfer checks the configuration with config and accepts a finite number 0≤freq≤1e9. bool, strings, and out of range are a ValueError. This frequency range is the calculation domain of the ideal formulas and does not mean a real 1GHz probe bandwidth. The grid of the simulation file is separately limited to 10Hz to 10MHz above. Distinguish the two contracts so that the model function does not reject a valid AC file even if the 10MHz endpoint becomes very slightly larger through output rounding.
The return is a complex dict keyed by reference, tip, and meter. s=2jπf, Ydut=1/100000+s20e-12, and R=1/(1+RsYdut). For unloaded all three values are R. For one-x and active, Yprobe=1/1e6+sCprobe, where Cprobe is 100e-12 and 1e-12 respectively. T=1/(1+Rs(Ydut+Yprobe)), M=T. For ten-x, Za=1/(1/9e6+scomp_pf1e-12), Zb=1/(1/1e6+s90e-12), T=1/(1+Rs(Ydut+1/(Za+Zb))), and M=T*Zb/(Za+Zb). The display ratio is not yet applied to meter.
The provided load_step(path) accepts a regular file of at most 16MiB and returns a list of 5-column tuples with the header time v(in) v(ref) v(tip) v(meter). It has 2 to 200000 rows, all finite numbers, voltage component absolute value ≤1e6, time 0 or greater and strictly increasing, the first time ≤1ns, and the last within 1e-12 seconds of 50µs. Content errors are a ValueError. You do not need to reimplement this loader.
observe checks the configuration and uses load_step to linearly interpolate ref, tip, and meter at t=1.005e-6+i1e-8 (i=0..1999). A row at exactly the same time is read as is, and extrapolation is a ValueError. The return is reference_v, tip_v, and display_v (each a list of 2000 voltages), grid_step_s=1e-8, observation_end_s=1.005e-6+19991e-8, chain_error_v=max|display−tip|, loading_error_v=max|tip−reference|, and display_error_v=max|display−reference|. Apply the display ratio only to display. This is a comparison on a finite observation grid and is not a proof of the continuous-time overall maximum.
Judgment and report contract
assess validates the configuration and accepts a normal response list and an observe report. You do not need to repeat the structure validation of the intermediate reports. The following requirements are the design contract declared in this lab and not an industry-standard pass line.
- max_model_error: the maximum magnitude of the complex difference between the reference, tip, and display of each of the 241 AC points and the reference, tip, and ratio*meter of transfer. The unit is gain.
- model_consistent: the error above ≤1e-8.
- chain_ok: chain_error of sweep[160], which is 100kHz, ≤.01.
- loading_ok: loading_error at the same point ≤.02.
- step_ok: display_error_v≤.03V.
- eligible: kind is not unloaded.
- all_pass: model_consistent, chain_ok, loading_ok, step_ok, and eligible are all True.
analyze(value,folder) connects config, read_ac(folder/ac.tsv), response, observe(folder/step.tsv), and assess. The return is schema=1, config, sweep, step, and assessment. Preserve the original configuration and files, and pass errors on.
Condition comparison and CLI contract
campaign(values,runner) first validates all of an exact list of 2 to 6 configurations. Every input dt_s must be the default 1.25e-9, and if even one is wrong, it is a ValueError before calling runner. For each condition, perform in turn runner(copy of the default settings)→analyze, then runner→analyze with new settings in which only dt_s is changed to 6.25e-10. Finish one condition and then move on to the next. Do not change the original input, and pass runner errors on.
The return is schema=1, runs, candidate_indices, and scope='ideal-probe-chain-and-loaded-circuit'. runs holds, in input order, coarse and fine (each an analyze report), difference_v (the maximum of the differences between the three voltage lists at the same times), and converged (difference_v≤1e-5V). Compare not only the display values but also reference_v and tip_v. candidate_indices is the list of 0-based indices of the conditions that converged and for which both the coarse and fine assessment.all_pass are True. An empty candidate list is a normal result and is different from an execution error.
main(argv) takes a settings-list JSON file and the path of an output folder that does not exist yet. After checking the number of arguments, all the settings, and the default dt, it creates a new folder. The parent must already exist, and an existing folder is rejected. In the order of runner calls, run the provided capture in run-00, run-01, .... Print the result to stdout as one line of valid JSON and return 0. For an argument, configuration, file, or execution Exception, return one line {"error":"analysis_failed"} and 2, and do not print a traceback. Even on failure, do not delete new evidence already made. On import the CLI is not run, and it exits with main's return value only when run directly.
Run the six conditions of the provided /opt/lab/fixtures/probe_loading/cases.json with a new name as below. Choose a path for the report redirect that does not overwrite an existing file either.
python3 /root/probe-loading/analyze.py /opt/lab/fixtures/probe_loading/cases.json /root/probe-loading/trial-01 > /root/probe-loading/report-01.json
Steps
- Validate the probe conditions before running: implement config(value). Check the configuration contract below and return a new dict with the numbers converted to float. Preserve the original input and reject invalid values with a ValueError.
- Read the complex voltages of the input and the three nodes: add read_ac(path). Check the 9-column, 241-row, frequency-grid contract below and return a list of float tuples.
- Separate the display error from the loading error: add response(value,rows). Return the reference, tip, and display normalized by the input, plus the complex ratio errors, magnitude, and phase, in the format below.
- Calculate the circuit with the probe attached: add transfer(value,freq). Return the reference, tip, and meter transfer functions as complex values from the loaded model in the reading.
- Compare the three waveforms at the same times: add observe(value,path). Read the waveform with the provided load_step and compute the three voltages at the specified 2000 times and the maximum differences.
- Distinguish model agreement from design suitability: add assess(value,sweep,step). Judge separately the full complex model verification, the chain and loading requirements at 100kHz, the step requirement, and the candidate eligibility.
- Connect the analysis report from the original files: add analyze(value,folder). Connect the configuration, AC, and step analyses and the judgment, and return a report that preserves even the intermediate results.
- Check the two precisions against the real runs: add campaign(values,runner), main(argv), and the CLI entry point. After validating all configurations first, choose the candidates that satisfy the three-node convergence of the default and fine calculations and the suitability of both.
Notes
Grading separates function checks that use independent formulas and synthetic files from real ngspice and separate-CLI checks. It does not change the student code and folders. If you hardcode the candidate indices of the default conditions, it fails for other source resistances, amplitudes, and compensation conditions. You cannot certify the safety rating, grounding, noise, bandwidth, or reliability across temperature of a real probe with this model.
Validate the probe conditions before running
Implement config(value). Check the configuration contract below and return a new dict with the numbers converted to float. Preserve the original input and reject invalid values with a ValueError.
bool is a subtype of int, but it is not a number under this contract. The compensation C exists only for ten-x.
Read the complex voltages of the input and the three nodes
Add read_ac(path). Check the 9-column, 241-row, frequency-grid contract below and return a list of float tuples.
The reference node and the tip are also denominators of the division. Check not only the header and the two ends but every interior frequency.
Separate the display error from the loading error
Add response(value,rows). Return the reference, tip, and display normalized by the input, plus the complex ratio errors, magnitude, and phase, in the format below.
Apply the 10x display only to meter. Even if the magnitude ratio is 1, if the phase differs, the complex error is not 0.
Calculate the circuit with the probe attached
Add transfer(value,freq). Return the reference, tip, and meter transfer functions as complex values from the loaded model in the reading.
Even if the compensation time constants are equal, the probe input impedance is not infinite. Calculate the probe load in parallel with the DUT.
Compare the three waveforms at the same times
Add observe(value,path). Read the waveform with the provided load_step and compute the three voltages at the specified 2000 times and the maximum differences.
Do not read the input voltage column as the reference voltage. If you build the original time list only once, the interpolation search gets faster.
Distinguish model agreement from design suitability
Add assess(value,sweep,step). Judge separately the full complex model verification, the chain and loading requirements at 100kHz, the step requirement, and the candidate eligibility.
unloaded is the comparison baseline. Even if its error is the smallest, it is not a probe candidate to buy or connect.
Connect the analysis report from the original files
Add analyze(value,folder). Connect the configuration, AC, and step analyses and the judgment, and return a report that preserves even the intermediate results.
If you change ten-x to unloaded and pass it to the step analysis, the screen ratio disappears. Pass the same configuration all the way through.
Check the two precisions against the real runs
Add campaign(values,runner), main(argv), and the CLI entry point. After validating all configurations first, choose the candidates that satisfy the three-node convergence of the default and fine calculations and the suitability of both.
Do not cover a result that dropped out in the fine calculation with the default calculation's success. Also check that the original material generated and the report agree.