Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
junction.hpp File Reference

The semiconductor junction law, once. More...

#include <algorithm>
#include <cmath>

Go to the source code of this file.

Classes

struct  pulp::signal::junction::JunctionPair
 One junction network: the exponential law over one or two antiparallel legs. More...

Namespaces

namespace  pulp
namespace  pulp::signal
namespace  pulp::signal::junction

Functions

double pulp::signal::junction::safe_exp (double x)
 exp(x) with the argument clamped into the safe range.
double pulp::signal::junction::exprel (double x)
 (exp(x) − 1) / x, with the removable singularity at 0 handled.

Variables

constexpr double pulp::signal::junction::kThermalVoltage = 0.02585
 Thermal voltage k·T/q at 300 K, in volts.
constexpr double pulp::signal::junction::kMaxExponent = 80.0
 Ceiling on an exponent argument, so a transient overshoot cannot produce inf.

Detailed Description

The semiconductor junction law, once.

The same exponential shows up everywhere in circuit-modelled audio — diode clippers, transistor fuzz pairs, optical and diode-bridge compressors, tape bias stages, tube approximations. Every one of them is

i(v) = Is·(exp(v/(n·V_T)) − 1)

with a different (Is, n) and a different circuit wrapped around it. Written per module it becomes several implementations of one physical constant and one transcendental, each with its own overflow clamp and its own idea of what V_T is. This header is the one implementation.

What it owns: the thermal voltage, the overflow-safe exponent, and the three closed forms every consumer needs — the current, its derivative (the incremental conductance a Newton solve wants), and its antiderivative (what antiderivative-antialiasing wants).

What it does NOT own: device families. A diode's silicon row and a transistor's silicon row are different numbers for different components, and collapsing them into one table would be a false economy — the consuming module declares its own [design parameter] rows and hands them here.

Two antiparallel legs

JunctionPair generalises to the antiparallel networks clippers use, and parameterises them by the RECIPROCAL series-diode count per leg:

i(v) = Is·( exp(a·v/θ) − exp(−b·v/θ) ) θ = n·V_T

Reciprocals rather than counts is what makes the parameter continuous: "this leg is absent" is b = 0, a finite value, where a count would have to be infinite. A single junction is the b = 0 case, so one type covers both.

The antiderivative's parity

F1 is even when the positive and negative legs match, because the current is then odd. An asymmetric pair is neither odd nor even, and neither is its antiderivative. Worth stating because silently forcing even parity would erase the even-harmonic behavior that set_symmetry exists to create.

It is computed through exprel rather than the algebraically obvious θ/b·(exp(−b·v/θ) − 1), which divides by a vanishing b as a leg is removed and produces a huge value that only cancels in a later subtraction — correct on paper, catastrophic in floating point.

RT contract: everything here is pure, stateless scalar arithmetic over POD. Nothing allocates, locks, or performs I/O, so all of it is safe per sample on the audio thread. Each call costs one or two exp evaluations.

Reference: Shockley (1949) for the diode law; Ebers & Moll, "Large-Signal Behavior of Junction Transistors", Proc. IRE 42(12):1761–1772, 1954, for the transistor forward-active term. They are the same equation.