How to automatically "normalise" a voltage signal in LTspice?

I would like to create an output signal that is normalised by the maximum value of a signal in LTspice. This is to produce a signal between [-1, 1] that is suitable for producing a wave file like found here i.e.

.wave "z:/home/runejuhl/out3.wav" 16 44100 out1

Signals will clip the wav file if they exceed a magnitude of 1.

Currently I am using a voltage controlled voltage source to reduce the overall level by a constant factor: This works ok, but doesn't maximally utilise the range of [-1, 1], and requires manual adjustment to adapt to changes in signal level.

I would like to set the gain factor such that it is equal to 1/max(Vout). Completing this in a single analysis command seems impossible as it would introduce a delay-free loop, the normalisation factor would be dependent upon something currently being calculated.

Is it possible to calculate a value from one simulation and use that in a following simulation? Preferably automatically without having to manually perform the calculation and place the result in the schematic.

• Using some external scripting, this link might be useful: electronics.stackexchange.com/questions/124666/… Feb 5 '19 at 12:45
• Connect Vout to a voltage source with output = {abs(vVout)} and connect that source with an ideal diode to a capacitor (with initial value 0V). File -> "Export data as text" this (trace) voltage of the capacitor (selecting only the last time stamp). Import this as a PWL file. Feb 5 '19 at 12:50
• Another idea is to append copy of the .wav file to the original one (so it contains the data twice and has twice the original length). Apply the same principe of voltage source, absolute value, diode, capacitor as above. And set the gain of your voltage controlled voltage to {1/V(capacitor) * u(time-12345)}. 12345 is the duration of the original .wav file Feb 5 '19 at 13:01
• @Huisman your ideas certainly seem plausible but it would require significant manual work, I was wondering if it could be automated. I will update the question to make this more obvious. Feb 5 '19 at 13:24
• Off-topic but it's sad that the support for floats in WAV is so lousy in pretty much every application. It would be perfectly feasible to just store simple floats, there's no need to clip anything in the actual file.
– pipe
Feb 6 '19 at 14:58

Avoiding the voltage drop from the diode in a peak detector circuit

An arbitrary behavioral source with delay(x,t[,tmax]) is the key.

1. add a spice directive .param samplerate=44kHz duration=1sec
2. set the simulation command to .tran 0 {2*duration} 0 {1/samplerate} (EDIT: original .tran 0 {2*duration} {duration} {1/samplerate} doesn't work)
3. add an independent voltage source with the .wav file as value and label its output "original".
4. add an arbitrary behavioral source, label its output "running_max" and set its value to V=max(delay(v(running_max),1/(bitrate)),abs(v(original)))
5. add another arbitrary behavioral source with value V=1/(1mV+v(running_max))*delay(v(original),filesize/bitrate) and label its output "result"
6. File -> Export data as text

When plotting "running_max", you'll see the signal is collapsing/dropping after each peak of "original". I've no idea why. This eventually makes "running_max" to become less than the maximum absolute value of "original". And so, the result will be exceeding [-1,1]. Decreasing the minimum step time, or increasing samplerate=10*44k, improves "running_max", but, at the cost of (much more) simulation time.

P.S. The solution with the diode won't work, because even the ideal diode seems to have a leakage current, which lowers the voltage on the capacitor.

EDIT: I used .tran 0 {2*duration} 0 {1/samplerate} all the time. Unfortunatelly, .tran 0 {2*duration} {duration} {1/samplerate} doesn't work, because the delay function won't work... You could work around this by opening another instance of LTSpice and repeat step 1 and 3 with .tran 0 {2*duration} {duration} {1/samplerate} and the new .wav file, and step 6.

So long for automation...

Automated method using a peak detector circuit

The problem with above method is that in introducing a delay() element the simulation takes a much longer time to complete. Because of this the use of a peak detector circuit may be an acceptable solution.

An ideal diode can be defined as noted in this question

.model Didl D(Ron=0.0001 Roff=100G Vfwd=0)

This can be rolled into a peak detector circuit using a capacitor and a voltage controlled voltage source to prevent loading the previous circuitry: which over time does decay due to the non-ideal behaviour of the diode, but this is an acceptable trade for my purpose. The effect will be a slight amplitude modulation resulting in some side-bands, but should be largely imperceptible.

Here is an example drop in voltage from the peak detector: The normalised output voltage can then be found from a behavioural voltage source bv: Two elements must be considered: first the peak voltage is exaggerated here to prevent any clipping, multiplying by 1.1. Second, the initial condition on the capacitor should be set to something reasonable, as if it is 0 initially the simulation will get some crazy voltages. This is achieved through setting SpiceLine: IC=1e-3 or some other similar value.

The final step not possible when using the delay operation is to run the simulation twice but only save data on the second run. The peak detector will have charged to the peak value already and therefore is already normalising the signal. This is an additional benefit as you can immediately save the wav file without having a first section that clips horribly.

I used .tran 0 {2*dur} {dur} {samplePeriod} where `.param samplePeriod=1/44.1e3' and 'dur=3'.

Testing this in my circuit for click-pop measurements, here are the un-normalised voltage in blue and the normalised voltage in green: • This must have taken so much work, thank you! I want to accept your answer because with your guidance I found the answer I was after, but hope you don't mind if I make an edit to it. Feb 6 '19 at 12:49
• What happens if there's another peak, greater than the first, further in the simulation, or even close to the end of it? It will mess up the normalizing. The only solution is to apply the peak detector, run the simulation for twice T seconds, where, after the first T, you stop everything else and simply use a delay(V(out),T)/V(pk). For the first T seconds there is no ouput for the delay while the rest of the circuit goes normally; for the 2nd half, the rest of the circuit stops except the delayed source, which is just starting. This also means the delay will not hinder the simulation. Feb 6 '19 at 14:46

If you don't mind two runs, you can add a .meas peak max abs(v(x)) directive (v(x) being the needed voltage, an example), then use the reverse of that measured value as the gain. Otherwise, you'll have to either provide an estimate gain, or export your trace and do the normalizing elsewhere (or even LTspice).

This is just a corollary for @Huisman's answer, to add a picture of what I meant in the comment: This is just for exemplification. I've let it run for 8s, but the useful signal goes from 0..4s, and the output (V(z)) starts at 4s. The simulation card can (should) be modified to start saving only after 4s.

• This might be the best solution. I've been investigating scripting this with LTspice and it seems totally impervious to automation on a mac. Feb 5 '19 at 15:03
• @loudnoises The .meas variable will not be available unless you read the log with some external program or read it yourself. That's the minor drawback, but not impossible. Otherwise, Huisman's solution (or anything similar) is valid, with the minor comment that a VCVS (or VCCS) will not allow a voltage in its expression as that is time dependent, but a behavioural source will. Also, you probably know, the .meas will be sensitive to the timestep and data compression. Feb 5 '19 at 16:54
• I think this would be a better solution should the signal/circuit response not be repeated if the simulation is extended to 2T. Feb 6 '19 at 16:03
• @loudnoises The benefit I see is that, if you can manage to stop the rest of the circuit, whether "orthodox"-like, as in the picture, or brute-force (switches to ground inputs, multiply by 0, etc), it has the advantage that it has, or should be of no burden after half the simulation, since it will simply be a source, repeating a signal. Lots of "if" though. Feb 6 '19 at 16:17