## Friday, March 17, 2017

### An FPGA SDR HF Transceiver, Part 2 -- FPGA Modulation and Demodulation

In this second blog post in my FPGA SDR Transceiver series, I will discuss how CW, SSB, and AM modulation and demodulation are accomplished within the FPGA.

(Part 1 of this series is here:  Part 1)

But before I begin, let me again acknowledge Dick Benson, W1QG.  Dick is the father of this design, and although I've made some modifications to the FPGA logic, the underlying architecture and the vast majority of the Simulink implementation is Dick's.

Modulation:

This discussion will include some small amount of mathematical analysis to show how signals are transformed step-by-step through the modulation process.  I'm keeping the math simple for "demonstration" purposes.  For example, the Audio signal will be represented by a cosine signal at a single frequency, rather than a more complex waveform.

And so, signal transformations can be easily calculated using the familiar Trigonometric "product-to-sum" formulas which I am including below (from sosmath.com), just in case you would like to confirm my equations for yourself:

SSB Modulation:

SSB modulation utilizes Weaver Modulation, and thus (to efficiently utilize FPGA logic "fabric") it is this modulator's architecture which also underlies the FPGA implementation of CW and AM modulation.

Below is a block diagram of the basic Weaver Modulator (see here for additional background on Weaver Modulation).

(Click on image to enlarge)

And the block diagram below represents how the Weaver Modulator is actually implemented within the FPGA:

(Click on image to enlarge)

To better understand how the the various subsystem blocks transform the input Audio signal into the final RF output USB signal, I've annotated this Block Diagram (see below) to show the signal transformation through the Modulator's subsystem blocks.

But first, note that the output to the DAC is a twos-complement 14-bit word and it is represented within the Simulink model as a signed value of 14 bits whose decimal point is at bit 13.  In this representation its maximum negative value represents -1.0 and its maximum positive value represents +0.99993..., which, for simplicity, I will round to +1.0.  Thus, the amplitude of the output cosine signal in the diagram below (being equal to 1) is at the maximum DAC input of +/-1.0.

(Click on image to enlarge)

Notes on SSB Generation:
1. The Interpolation Filter doesn't really have a role to play in understanding the basic Weaver Modulator, but I've shown it in the block diagram above because it has a gain 2, which does affect signal amplitudes.
2. The maximum amplitude of the Audio into the Weaver Modulator itself is half of full-scale (as determined by the "x0.5" stage just prior to the "Low Frequency Local Oscillator" multipliers).  Thus, the signal amplitude into the Information Filter's I and Q channels is also half of full-scale,     and I believe Dick did this to ensure that there would be no overflows during the Information Filter's internal calculations.
3. The Audio samples at the Weaver Modulator's input are clocked at a sampling rate of 9,765.625 Hz (i.e. 80 MHz / 8192), while the RF samples at the Weaver Modulator's output are clocked at 80 MHz.
4. Note that the Information Filter's output is also demodulated, thus allowing the operator to monitor the effect of the TX EQ and Information Filter's bandwidth on their signal quality.
5. The above diagram shows the equations for USB generation.  LSB generation can be calculated in a similar fashion.

Now let's look at CW generation...

CW Modulation:

Basic CW modulation can be thought of as simply keying an RF oscillator on and off, as shown in the diagram, below:

(Click on image to enlarge)

To effectively utilize FPGA resources, this radio's CW modulator adapts the radio's underlying SSB Weaver Modulator architecture to CW Modulation, per the diagram, below:

(Click on image to enlarge)

As such, although this radio does key the RF oscillator (HFLO) on and off, its operation isn't quite as simple as that.

To explain, I am going to annotate the drawing above with the signal flow.  Note that signal amplitudes are set so that the amplitude of the output RF signal in the diagram below (in this case a cosine of amplitude 1) is at the maximum DAC input of +/-1.0.

(Click on image to enlarge)

Notes on CW Modulation:
1. The Interpolation Filter doesn't really have a role to play in understanding the CW Modulation, but I've shown it in the block diagram above because it has a gain 2, which does affect signal amplitudes.
2. If the Information Filter's bandwidth is less than twice the LFLO's frequency, then the "Q" channel's signal is essentially non-existent, because the component at 1600 Hz is filtered out and the DC term, being the sine of 0 degrees, is 0.  In this case, the Weaver Modulator acts like a basic CW generator, with the I channel's DC signal effectively keying the HFLO signal on and off.
3. However, if the Information Filter's bandwidth is at least twice the LFLO's frequency, it is possible for this generator to simultaneously generate two CW signals, separated by twice the HFLO frequency.  This is where this implementation differs from a simple CW generator!  Note that the input into the Information Filter consists of two terms: a DC term and a cosine (or sine) term at twice the LFLO's frequency (the latter being 800 Hz in this design).   The Information Filter normally removes this cosine (or sine) "image" component, leaving just the DC, but, if the Information Filter's bandwidth is made too large (e.g. for an LFLO frequency of 800 Hz, the Low Pass bandwidth is 1600 Hz or more (corresponding to a signal bandwidth of 3.2 KHz or more)), this "image" will also appear as part of the transmitted signal.  Thus, there will be TWO CW signals, plus distortion products (the latter if TX Level is set to a value that causes the combined amplitude of these two signals to be greater than the FPGA's limit of +/- 1).
4. The Low Frequency Local Oscillator's frequency is set here to 800 Hz (but it could be programmed to be a different frequency, should the operator prefer).

And now let's look at AM generation...

AM Modulation:

Given an audio input signal x(t), AM modulation can be expressed mathematically as:

In Block Diagram form, this equation for y(t) is equivalent to:

To effectively utilize FPGA resources, this AM Modulator also adapts the underlying FPGA SSB Weaver Modulator architecture to AM modulation:

(Click on image to enlarge)

Again, as with the SSB and CW annotated diagrams describing signal flow, above, the amplitude of the output signal in the diagram below is at the maximum DAC input of +/-1.0.  (You can prove this to yourself by setting 'A' (the frequency of the modulating signal) to 0.  The output's equation reduces to cos(HF), whose amplitude is 1, thus matching the DAC's maximum input of +/-1.0.)

(Click on image to enlarge)

Notes on AM Generation:
1. The Interpolation Filter doesn't really have a role to play in understanding the AM Modulation process, but I've shown it in the block diagram above because it has a gain 2, which does affect signal amplitudes.
2. The AM Modulator includes an additional stage to add a DC offset to the incoming Audio, so that, assuming full-scale audio (amplitude of +/-1.0), when added to a DC offset of 1.0, results in a signal amplitude ranging between 0 to +2.0 and never going negative.   To bring the signal back into the +/-1 max amplitude range, this Adder is therefore immediately followed by a divide-by-two stage, before being applied to the following stages (stages common to CW and SSB modulation).
3. Note that only the I-channel contains the signal.  The Q channel has no signal -- it is zero.
4. If the DC Offset is less than the maximum amplitude of the incoming Audio signal, the signal will overmodulate.
5. The TX Level must be limited to 0.5, max, instead of 1.0.  Otherwise the RF output will be driven into limiting (and thus distort).
6. The Low Frequency Local Oscillator's frequency is set to 0 Hz.

Demodulation:

SSB/CW Reception:

SSB and CW reception is accomplished via SSB Modulation is accomplished via Weaver Demodulation.

(Click on image to enlarge)

Notes on SSB/CW Demodulation:
1. Not much to add that hasn't been explained here: Weaver Demodulation.

AM Reception:

AM reception uses the high-frequency section of the Weaver Demodulator to down-convert the received signal.  But, following this down-conversion and band-width filtering, the AM demodulation itself is strictly an envelope detector.

(Click on image to enlarge)

Notes on AM Demodulation:
1. The Weaver Demodulator's Low Frequency Local Oscillator and its associated mixers are not used.
2. Instead, AM is demodulated by first taking the absolute value of the Information Filter's complex (I and Q) output.
3. Following the absolute-value stage, the resultant signal, now being only positive, is passed through a high-pass filter to remove the low-frequencies (i.e. DC, representing the carrier), leaving the Audio signal.
4. Similarly, the absolute-value signal is low-pass filtered to derive the carrier.
5. Both the Audio and the Carrier are then applied to the AGC stage (AGC and the audio stages will be discussed in a later post).

That covers the basics of Modulation and Demodulation used within the FPGA.  Now let's look at the Simulink model, which defines the FPGA's logic.

First, the receiver and its Weaver Demodulator:

The topic of this discussion will only be the Demodulator section of the receiver.  I will leave discussion of the RX Audio chain, as well as discussion of the Decimation Filter, for later posts.

SSB/CW Demodulation:

Here is the basic block diagram of a Weaver Demodulator:

(Click on image to enlarge)

Within the FPGA, this demodulator is represented by the functional blocks outlined in red, below:

(Click on image to enlarge)

Let's first focus on the high-frequency down-conversion stage of the Weaver Demodulator.  This stage is clocked at the RF sampling rate of 80 MHz, and it consists of the High Frequency Local Oscillator (HFLO) and its two associated mixers.

Weaver Demodulator, High-Frequency section:

Note that this down-conversion stage common for SSB, CW, and AM reception.

(Click on image to enlarge)

In the Simulink Model, this function lies within the "Up/Down LO and Mixers" subsystem block circled in yellow, below:

(Click on image to enlarge)

Clicking on this subsystem block to open it, we would see the following logic model:

(Click on image to enlarge)

Note that some of this logic is used for both Transmit and Receive signal processing (thus you will see 2:1 multiplexers and other logic required for this multitasking).

Let's examine how the receive signal flows from the ADC input to this subsystem's I/Q outputs:

(Click on image to enlarge)

Now let's look inside the subsystem blocks shown within the subsystem, above.  First, let's look at the HFLO (labeled the "DDS_LO" and circled in yellow, below):

(Click on image to enlarge)

Here is the "DDS_LO" circuitry:

(Click on image to enlarge)

Notes on the DDS_LO circuitry:
1. The logic is clocked at 80 MHz.
2. Two different frequencies can be stored in this block -- an RX frequency and a TX frequency.
3. This data is written to this block from an external "User Interface" processor via the FPGA's "Command Interface".  (This Command Interface, and its FPGA circuitry, will be described in a later post).
4. The appropriate frequency, TX or RX, is loaded into the DDS upon a TX to RX transition or an RX to TX transition.
5. The DDS data-word is 26 bits wide.
6. The data-write interface into this block operates at 10 MHz.  The data, when written into the DDS block, is then up-converted to 80 MHz.
7. If we were to click on the "LO_DDS Compiler 4.0" block in the subsystem above, we would see the following.  (Note that Fs is defined to be 80 MHz):

(Click on image to enlarge)

A note regarding Frequency Resolution...the frequency resolution is defined to be Fs*2^-26.  Given an FS of 80 MHz, this would make the resolution 1.19 Hz.

Additional information of the Xilinx DDS 4.0 compiler can be found here. (It is a worthwhile read if you are not familiar with DDS).

Now let's examine the "Delay Allow T/R Relay Time" subsystem.  It is the subsystem circled below:

(Click on image to enlarge)

This block forces the ADC data to be zero for a small amount of time (128 clocks at the 9,765.625 Audio sample rate) after Transmit has finished (Dick, W1QG, added this circuit to prevent residual TX energy from adversely affecting the receive AGC when transitioning from Tx to Rx).

Clicking on it, we would see (note that I am also showing the One-Shot subsystem's logic):

(Click on image to enlarge)

And finally, let's look at the "Overload_Det" subsystem, circled below:

(Click on image to enlarge)

(Click on image to enlarge)

1. The 80 MHz sampled-RF input signal is first down-sampled to 1.25 MHz.
2. If the 4 MSB's of the 16-bit signal are either 0x0111 or 0x1000 (representing the two's complement signal levels at or near the max (+1) or or min (-1)), then an Overload condition is latched.  This bit can then illuminate an LED or be read by another processor via the FPGA's Command Interface.
3. The latched bit is reset by a free-running counter clocked at the Audio sample rate of 80 MHz/8192, or 9,765.625 KHz.  Note that because this counter is free-running, the Overload bit's ON period can be anywhere from 1 to 255 Audio-sample clocks long.

That covers the HFLO and its mixers.  Now let's now examine the remaining Weaver Demodulator functions, which consist of the Information Filter, the Low Frequency Local Oscillator (LFLO), and its two mixers.  These functions run at the Audio sampling rate of 9,765.625 Hz (80 MHz / 8192).

Weaver Demodulator, Low-Frequency Section:

In the FPGA these would be the functions within the red rectangle, below:

(Click on image to enlarge)

Examining the Simulink Model's Top Level, we would find these functions in the "SSB/AM Mod_Demod WCS" subsystem, which I've circled in yellow, below

(Click on image to enlarge)

Opening up the "SSB/AM Mod_Demod WCS" subsystem block:

(Click on image to enlarge)

Below is the signal flow, from the I/Q inputs to the Audio Output:

(Click on image to enlarge)

Now let's look at some of the subsystem blocks contained within the "SSB/AM Mod_Demod WCS" subsystem.

First, the LFLO block (shown below, circled in yellow).  This is the Low Frequency Local Oscillator:

(Click on image to enlarge)

Clicking on it, we would see:

(Click on image to enlarge)

Notes:
1. Data is written into this block from the Command Interface logic (to be described in a later post), operating at a 10 MHz rate.
2. Because the DDS block operates at the Audio sample-rate of 9,765.625 KHz (i.e. 80 MHz/8192), the incoming register-write data is down-sampled by a factor of 1024 (from 10 MHz to 9,765.625 KHz).
3. This block can contain both a TX HFLO frequency and a separate RX HFLO frequency.
4. The appropriate HFLO frequency is automatically loaded into the DDS at a TX-to-RX or an RX-to-TX transition.
5. The register-write data consists of 26 bits.   They are:
• Bits 0-12:  HFLO frequency
• Bits 13-20:  AM Carrier Level (sets the carrier level for the AM modulator, to be discussed further, below).
• Bit 21:  Identifies if the HFLO frequency being written is a TX frequency or an RX frequency.

And if we were to open its "BFO_DDS Compiler 4.0" block, we would see the following parameters:

(Click on image to enlarge)

Notes on the LFLO DDS block:
1. Regarding Dick's note in the "System Clock" entry, implying that the System Clock is 2x the Audio sampling rate -- he is doubtful that this is the actual case, and his best guess is that this parameter sets the timing constraints and that Xilinx did not expect anything to run this slow.  But as Dick says, "GUESS is the key word."
2. Daudio (used to define frequency resolution) is 8192.  Thus, the DDS frequency resolution is 2.38 Hz.

Next, let's examine the "Time_Share_MPY" block, highlighted in yellow, below:

(Click on image to enlarge)

Clicking on it, we would see:

(Click on image to enlarge)

Notes on the Time_Share_MPY:
1. Both the Time-Division-Multipler and the Time Division Demultiplexer blocks are rate-changing blocks.  And so, in the above circuit, given that there are four inputs and four outputs, I am assuming that they implicitly change the external clock rate of 9,765.625 KHz to an internal rate of 4 times that external rate, in order to complete all multiplications within one Audio clock cycle.
2. Per Dick, there is a latency of 4 clocks in the Multiplier "to maintain the ordering of inputs and outputs.  Otherwise, one would need to rearrange the inputs to the demux."

The last Weaver Demodulator block we will look at is the "128 Tap FIR with Writable Control Store" subsystem (circled below):

(Click on image to enlarge)

And here is its logic:

(Click on image to enlarge)

Notes on the "128 Tap FIR with Writable Control Store":
1. There is an FIR filter for the TX path and a separate FIR filter for the RX path.
2. The appropriate filter is automatically select when in RX or TX mode.
3. The write-data, clocked at 10 MHz, is down-sampled by 256 to about 39 KHz (4 times the Audio sample rate), in order to generate a short Reset pulse. (See next subsystem.)

You can see that there are some additional subsystem blocks within this block.  Let's first look "under the mask" of the "2 channel (IQ) SHARED MPY MAC with Writable Coefficient Store" (the yellow block, above).  We would see:

(Click on image to enlarge)

Notes on the "2 channel (IQ) SHARED MPY MAC":
1. This block represents the 128-tap FIR filter.
2. I/Q samples arrive at the Filter's input (and filtered samples are clocked out of the Filter) at the Audio sample rate of 9,765.625 Hz.
3. Although the filter consists of 128 taps, because the filter is symmetric, only 64 coefficients are needed.  Thus, two I samples (as well as two Q samples on a parallel path) are first added together (e.g. sample 0 and sample 127, or sample 1 and sample 126, etc.) and each two-sample sum then multiplied once by the appropriate FIR coefficient to produce a product.
4. This product is then summed into an accumulator with previous products until 64 products, representing the convolved 128 I-channel samples, has been accumulated.  This sum-of-products will be the filtered I-channel output sample.
5. A parallel accumulation process takes place for the Q-channel, with a second accumulator accumulating the 64 products representing the convolved 128 Q-channel samples.
6. The two 6-bit counters (on the left-hand side) are clocked at 64 times the Audio sample rate.

Another subsystem block within the  "128 Tap FIR with Writable Control Store" subsystem is the "Information_Filter_Transient_Suppressor" subsystem:

(Click on image to enlarge)
Notes on the "Information_Filter_Transient_Suppressor":
1. This block is operating at the Audio sampling rate of 9,765.625 Hz.
2. Because the Information Filter is shared between TX and RX, there will be 128 samples of erroneous data when switching from RX to TX or TX to RX.  For example, when switching from RX to TX, 128 samples of TX data must first be clocked out of the Information Filter before we start seeing RX data.
3. To keep this unwanted data from affecting processes downstream (e.g. AGC), it is forced to be zero (when appropriate).
4. In the design above, the data is suppressed for 130 clocks (13.3 ms).
5. Important note:  this suppression of the Filter's output at the start of a TX cycle means that, in CW mode, the first CW element's leading edge will be clipped by 13.3 ms, unless the CW characters are first delayed.  I will discuss circuitry to delay CW (to lessen, if not eliminate, clipping of the first symbol) in a later post.  (Note, at 60 words-per-minute a dit is only 20 ms long, but at 20 wpm it is 60 ms long.)

Note that I have not yet explained two other blocks in the "SSB/AM Mod_Demod WCS".  These two blocks are the "Sequential_Complex_abs()" and the "Fixed Point HPF/LPF" subsystems, and they are not part of the SSB/CWWeaver Demodulation process.  Instead, they form the AM Demodulator, and I will discuss them next...

AM Demodulation:

The AM Demodulator utilizes the first half of the Weaver Demodulator to down-convert the incoming RF.  This down-converted signal is then fed to the Information Filter (as it would be for SSB/CW demodulation), but the Information Filter's output is instead fed to the AM Demodulator function, rather than to the second half of the Weaver Demodulator.  I've illustrated this, below:

(Click on image to enlarge)

I've already discussed the HFLO and its mixers.  Let's instead focus on the second part of AM demodulation: the Information Filter and how its output is demodulated in AM Mode.

These functions are contained within the "SSB/AM Mod_Demod WCS" subsystem we examined earlier:

(Click on image to enlarge)

Below is the signal flow for AM Demodulation.  Note that these samples are clocked at the Audio sample rate of 9,765.625 Hz.

(Click on image to enlarge)

And now let's look inside the two remaining subsystem blocks that we have not yet examined.  First, the "Sequential_Complex_abs()" subsystem, circled in yellow, below:

(Click on image to enlarge)

This subsystem will take the absolute value of the complex-number represented by the I and Q samples at its input.

Opening this subsystem, we would see:

(Click on image to enlarge)

Again, there are additional subsystem blocks within this block.  Let's examine these blocks.  (Note:  I have not yet tried to understand Dick's AM Demodulator design, so I will simply present the Subsystem blocks without additional notes).

First, the "Time Shared Square Operator":

(Click on image to enlarge)

Next, the "Pre-scaler" subsystem:
(Click on image to enlarge)

Then, the "Iterative SQRT" subsystem:

(Click on image to enlarge)

And finally, the "Sequential Post Scale" subsystem:

(Click on image to enlarge)

The final subsystem block in the AM Demodulator is the "Fixed Point HPF/LPF" subsystem, circled below in yellow:

(Click on image to enlarge)

This block filters the absolute-value samples.  Low-pass filtering gives us the Carrier Level (i.e. DC), while High-pass filtering gives us the Audio, as shown below.

(Click on image to enlarge)

A note regarding Dick's IIR Lowpass filter in the above model.  For those of you who are more familiar with 1-pole IIR low-pass filters of the form that I will call "Form 1" (per drawing, below), note that Dick's LPF (in a form that I will call "Form 2") is equivalent.

OK, that covers the Simulink Models for SSB/CW and AM Demodulation.  Let's look at the Simulink Models for Modulation:

In this section I will examine only the modulation sections of the transmitter  (and, where appropriate, demodulation of the TX signal for operator monitoring), leaving the TX Audio chain and the Interpolation Filter for future posts.

SSB Modulation:

Weaver Modulator, Low-Frequency Section:

First, SSB Weaver Modulation.  Let's first examine the Low-Frequency functions (operating at the Audio Sample rate of 9,765.625 Hz) outlined in red, below:

(Click on image to enlarge)

At the Simulink Model's Top Level, these functions are found in the "SSB/AM Mod_Demod WCS" subsystem, circled in yellow, below:

(Click on image to enlarge)

Opening up this Subsystem:

(Click on image to enlarge)

Let's relate this Subsystem to the SSB Generation Block Diagram discussed earlier.  I'll highlight the appropriate blocks, as shown below:

(Click on image to enlarge)

The above highlighted blocks correspond to the following Subsystem blocks in the Simulink Model:

(Click on image to enlarge)

Let's look at the signal paths when this Subsystem acts as the SSB Weaver Modulator:

(Click on image to enlarge)

The majority of these functions have already been discussed, above, in the SSB Weaver Demodulator section.  The only function that has not yet been discussed is the circuitry which demodulates the TX signal for monitoring of audio quality by the operator.

In the drawing above, this demodulation occurs after the Information Filter (and, of course, after the TX EQ), and so it can provide the operator with a representation of how their audio should sound at a far-end receiver (were the receiver perfect).

In essence, this Demodulation is again the low-frequency half of a Weaver Demodulator, consisting of the LFLO and two additional mixers (a function that has already been described, earlier, and I will not spend any additional time on it).

Weaver Modulator, High-Frequency Section:

We've just examined the Low Frequency Weaver Modulator functions.  Now let's examine the Weaver Modulator's High Frequency functions (operating at the 80 MHz RF sample rate), shown below, outlined in red.

(Click on image to enlarge)

These functions are contained in the "Up/Down LO and Mixers" subsystem block circled in yellow, below:
(Click on image to enlarge)

You've seen this block before, in my description of the demodulator.  Let's look at it again.

(Click on image to enlarge)

And here is the high-frequency signal path.  This path and function is common for SSB, CW, and AM modulation:

(Click on image to enlarge)

CW Modulation:

As mentioned earlier above, CW Modulation utilizes the SSB Weaver Modulator, whose blocks I've outlined in red, below.

(Click on image to enlarge)

The Simulink models of these outlined blocks have already been discussed earlier in this post.  The other blocks, consisting of the Audio path (for the 800 Hz signal) and the Interpolation filter, will be discussed in future posts.

AM Modulation:

AM modulation also utilizes the SSB Weaver Modulator circuitry, but in this case, the frequency of the Weaver Modulator's HFLO is set to 0.

Note that the Weaver Modulator's HFLO and mixers have already been described earlier in this post.  Let's look at the following functions, circled in red, that are contained within the "SSB/AM Mod_Demod WCS" subsystem (previously discussed, above).

(Click on image to enlarge)

And here they are in the "SSB/AM Mod_Demod WCS":

Items 2 and 3 have been described earlier in this post, so I will skip them.  Regarding item 1, the source of the AM Carrier level is shown in the "LFLO" subsystem, below.

(Click on image to enlarge)

I've added the above subsystem to identify the source of the AM Carrier level.  It is an 8-bit value written to the above register via the FPGA's Command Interface.  This Command Interface will be described in a later post (it is the interface that an outside processor uses to write control-data into the FPGA), and the addition of the Carrier Level to the Audio signal, being handled within the Audio chain, will also be described in a later post.

The drawing below highlights the AM signal's path in the low-frequency section of the Weaver Modulator circuitry:

(Click on image to enlarge)

Not shown as part of this discussion on AM Modulation is the AM Overmodulation Detector.  This function is part of the Audio chain, and will be described in a future post.

That's it for this post!

Background Notes:

Part 1: Overview
Part 2: FPGA Modulation and Demodulation
Part 3: Interpolation and Decimation Filters

Posts in this Series:

Part 1: Overview
Part 2: FPGA Modulation and Demodulation
Part 3: Interpolation and Decimation Filters
Part 5: Control Interface, Etc.

Resources:

Standard Caveat:

I or Dick might have made a mistake in our designs, equations, schematics, models, etc.  If anything looks confusing or wrong to you, please feel free to comment below or send me an email.

Also, I will note:

This design and any associated information is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.